blob: 8c9231c0cf30cd83a805916e526eb01127b89f9d [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "WordSheet.h"
#include "DFDOM.h"
#include "CSSSelector.h"
#include "DFString.h"
#include "Word.h"
#include "DFCommon.h"
#include "DFPlatform.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
static char *WordSheetIdentForType(const char *type, const char *styleId)
{
return DFFormatString("%s/%s",type,styleId);
}
char *WordStyleNameToClassName(const char *name)
{
char *className = xstrdup(name);
for (char *c = className; *c != '\0'; c++) {
if (*c == ' ')
*c = '_';
}
return className;
}
char *WordStyleNameFromClassName(const char *name)
{
char *className = xstrdup(name);
for (char *c = className; *c != '\0'; c++) {
if (*c == '_')
*c = ' ';
}
return className;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// WordStyle //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
static WordStyle *WordStyleNew(DFNode *element, const char *type, const char *styleId, const char *name)
{
assert(element != NULL);
assert(type != NULL);
assert(styleId != NULL);
assert(element->tag == WORD_STYLE);
WordStyle *style = (WordStyle *)xcalloc(1,sizeof(WordStyle));
style->retainCount = 1;
style->element = element;
style->type = (type != NULL) ? xstrdup(type) : NULL;
style->styleId = (styleId != NULL) ? xstrdup(styleId) : NULL;
style->ident = WordSheetIdentForType(style->type,style->styleId);
style->basedOn = DFStrDup(DFGetChildAttribute(style->element,WORD_BASEDON,WORD_VAL));
DFNode *pPr = DFChildWithTag(style->element,WORD_PPR);
style->outlineLvl = DFStrDup(DFGetChildAttribute(pPr,WORD_OUTLINELVL,WORD_VAL));
style->name = xstrdup(name);
return style;
}
static WordStyle *WordStyleRetain(WordStyle *style)
{
if (style != NULL)
style->retainCount++;
return style;
}
static void WordStyleRelease(WordStyle *style)
{
if ((style == NULL) || (--style->retainCount > 0))
return;
free(style->type);
free(style->styleId);
free(style->selector);
free(style->ident);
free(style->basedOn);
free(style->outlineLvl);
free(style->name);
free(style);
}
int WordStyleIsProtected(WordStyle *style)
{
return (!strcmp(style->name,WordStyleNameFootnoteReference) ||
!strcmp(style->name,WordStyleNameFootnoteText) ||
!strcmp(style->name,WordStyleNameEndnoteReference) ||
!strcmp(style->name,WordStyleNameEndnoteText));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// WordSheet //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
struct WordSheet {
DFHashTable *stylesByIdent;
DFHashTable *stylesByName;
DFHashTable *stylesBySelector;
DFDocument *doc;
};
static void determineSelectors(WordSheet *sheet);
WordSheet *WordSheetNew(DFDocument *doc)
{
WordSheet *sheet = (WordSheet *)xcalloc(1,sizeof(WordSheet));
sheet->stylesByIdent = DFHashTableNew((DFCopyFunction)WordStyleRetain,(DFFreeFunction)WordStyleRelease);
sheet->stylesByName = DFHashTableNew((DFCopyFunction)WordStyleRetain,(DFFreeFunction)WordStyleRelease);
sheet->stylesBySelector = DFHashTableNew((DFCopyFunction)WordStyleRetain,(DFFreeFunction)WordStyleRelease);
sheet->doc = DFDocumentRetain(doc);
if (sheet->doc == NULL)
sheet->doc = DFDocumentNewWithRoot(WORD_STYLES);;
DFNode *root = sheet->doc->root;
for (DFNode *child = root->first; child != NULL; child = child->next) {
if (child->tag == WORD_STYLE) {
const char *type = DFGetAttribute(child,WORD_TYPE);
const char *styleId = DFGetAttribute(child,WORD_STYLEID);
const char *name = DFGetChildAttribute(child,WORD_NAME,WORD_VAL);
if ((type != NULL) && (styleId != NULL) && (name != NULL)) {
WordStyle *style = WordStyleNew(child,type,styleId,name);
DFHashTableAdd(sheet->stylesByIdent,style->ident,style);
DFHashTableAdd(sheet->stylesByName,style->name,style);
WordStyleRelease(style);
}
}
}
determineSelectors(sheet);
return sheet;
}
void WordSheetFree(WordSheet *sheet)
{
DFHashTableRelease(sheet->stylesByIdent);
DFHashTableRelease(sheet->stylesByName);
DFHashTableRelease(sheet->stylesBySelector);
DFDocumentRelease(sheet->doc);
free(sheet);
}
const char **WordSheetCopyIdents(WordSheet *sheet)
{
return DFHashTableCopyKeys(sheet->stylesByIdent);
}
WordStyle *WordSheetStyleForIdent(WordSheet *sheet, const char *ident)
{
return DFHashTableLookup(sheet->stylesByIdent,ident);
}
WordStyle *WordSheetStyleForTypeId(WordSheet *sheet, const char *type, const char *styleId)
{
if ((type == NULL) || (styleId == NULL))
return NULL;
char *ident = WordSheetIdentForType(type,styleId);
WordStyle *style = WordSheetStyleForIdent(sheet,ident);
free(ident);
return style;
}
WordStyle *WordSheetStyleForName(WordSheet *sheet, const char *name)
{
if (name == NULL)
return NULL;
return DFHashTableLookup(sheet->stylesByName,name);
}
WordStyle *WordSheetStyleForSelector(WordSheet *sheet, const char *selector)
{
if (selector == NULL)
return NULL;
return DFHashTableLookup(sheet->stylesBySelector,selector);
}
WordStyle *WordSheetAddStyle(WordSheet *sheet, const char *type, const char *styleId, const char *name, const char *selector)
{
assert(type != NULL);
assert(styleId != NULL);
assert(name != NULL);
assert(selector != NULL);
DFNode *element = DFCreateChildElement(sheet->doc->root,WORD_STYLE);
DFSetAttribute(element,WORD_STYLEID,styleId);
DFSetAttribute(element,WORD_TYPE,type);
DFAppendChild(sheet->doc->root,element);
DFNode *nameNode = DFCreateChildElement(element,WORD_NAME);
DFSetAttribute(nameNode,WORD_VAL,name);
WordStyle *style = WordStyleNew(element,type,styleId,name);
style->selector = xstrdup(selector);
DFHashTableAdd(sheet->stylesByIdent,style->ident,style);
DFHashTableAdd(sheet->stylesByName,style->name,style);
DFHashTableAdd(sheet->stylesBySelector,style->selector,style);
WordStyleRelease(style);
return style;
}
void WordSheetRemoveStyle(WordSheet *sheet, WordStyle *style)
{
WordStyleRetain(style);
DFRemoveNode(style->element);
DFHashTableRemove(sheet->stylesByIdent,style->ident);
DFHashTableRemove(sheet->stylesByName,style->name);
if (style->selector != NULL)
DFHashTableRemove(sheet->stylesBySelector,style->selector);
WordStyleRelease(style);
}
const char *WordSheetNameForStyleId(WordSheet *sheet, const char *type, const char *styleId)
{
WordStyle *style = WordSheetStyleForTypeId(sheet,type,styleId);
return (style != NULL) ? style->name : NULL;
}
const char *WordSheetSelectorForStyleId(WordSheet *sheet, const char *type, const char *styleId)
{
if ((type == NULL) || (styleId == NULL))
return NULL;;
WordStyle *style = WordSheetStyleForTypeId(sheet,type,styleId);
if (style == NULL)
return NULL;
return style->selector;
}
const char *WordSheetStyleIdForSelector(WordSheet *sheet, const char *selector)
{
if (selector == NULL)
return NULL;;
WordStyle *style = WordSheetStyleForSelector(sheet,selector);
if (style == NULL)
return NULL;
return style->styleId;
}
static void determineSelectors(WordSheet *sheet)
{
const char **allIdents = DFHashTableCopyKeys(sheet->stylesByIdent);
for (int i = 0; allIdents[i]; i++) {
const char *ident = allIdents[i];
WordStyle *style = DFHashTableLookup(sheet->stylesByIdent,ident);
const char *outlineLvl = NULL;
// Compute inherited properties
WordStyle *ancestor = style;
DFHashTable *visited = DFHashTableNew((DFCopyFunction)xstrdup,free);
while ((ancestor != NULL) && (DFHashTableLookup(visited,ancestor->ident) == NULL)) {
DFHashTableAdd(visited,ancestor->ident,"");
if (outlineLvl == NULL)
outlineLvl = ancestor->outlineLvl;
if (ancestor->basedOn != NULL)
ancestor = WordSheetStyleForTypeId(sheet,ancestor->type,ancestor->basedOn);
else
ancestor = NULL;
}
DFHashTableRelease(visited);
char *name = (style->name != NULL) ? WordStyleNameToClassName(style->name) : WordStyleNameToClassName(style->styleId);
if (DFStringEquals(style->type,"paragraph")) {
if ((outlineLvl != NULL) && (atoi(outlineLvl) >= 0) && (atoi(outlineLvl) <= 5)) {
int headingLevel = atoi(outlineLvl) + 1;
switch (headingLevel) {
case 1: style->selector = CSSMakeSelector("h1",name); break;
case 2: style->selector = CSSMakeSelector("h2",name); break;
case 3: style->selector = CSSMakeSelector("h3",name); break;
case 4: style->selector = CSSMakeSelector("h4",name); break;
case 5: style->selector = CSSMakeSelector("h5",name); break;
case 6: style->selector = CSSMakeSelector("h6",name); break;
default: style->selector = CSSMakeSelector("p",name); break;
}
}
else if (DFStringEquals(style->styleId,"Caption")) {
style->selector = CSSMakeSelector("caption",NULL);
}
else if (DFStringEquals(style->styleId,"Figure")) {
style->selector = CSSMakeSelector("figure",NULL);
}
else {
style->selector = CSSMakeSelector("p",name);
}
}
else if (DFStringEquals(style->type,"character")) {
style->selector = CSSMakeSelector("span",name);
}
else if (DFStringEquals(style->type,"table")) {
style->selector = CSSMakeSelector("table",name);
}
if (style->selector != NULL)
DFHashTableAdd(sheet->stylesBySelector,style->selector,style);
free(name);
}
free(allIdents);
}
static void setupNoteReferenceStyle(WordStyle *style)
{
free(style->selector);
style->selector = CSSMakeSelector("span",style->styleId);
// FIXME: Set basedOn
DFNode *rPr = DFCreateChildElement(style->element,WORD_RPR);
DFNode *vertAlign = DFCreateChildElement(rPr,WORD_VERTALIGN);
DFSetAttribute(vertAlign,WORD_VAL,"superscript");
}
static void setupNoteTextStyle(WordStyle *style)
{
free(style->selector);
style->selector = CSSMakeSelector("p",style->styleId);
// FIXME: Set basedOn
}
WordStyle *WordSheetFootnoteReference(WordSheet *sheet)
{
WordStyle *style = WordSheetStyleForName(sheet,WordStyleNameFootnoteReference);
if (style != NULL)
return style;
style = WordSheetAddStyle(sheet,"character","FootnoteReference",WordStyleNameFootnoteReference,"span.FootnoteReference");
setupNoteReferenceStyle(style);
return style;
}
WordStyle *WordSheetFootnoteText(WordSheet *sheet)
{
WordStyle *style = WordSheetStyleForName(sheet,WordStyleNameFootnoteText);
if (style != NULL)
return style;
style = WordSheetAddStyle(sheet,"paragraph","FootnoteText",WordStyleNameFootnoteText,"p.FootnoteText");
setupNoteTextStyle(style);
return style;
}
WordStyle *WordSheetEndnoteReference(WordSheet *sheet)
{
WordStyle *style = WordSheetStyleForName(sheet,WordStyleNameEndnoteReference);
if (style != NULL)
return style;
style = WordSheetAddStyle(sheet,"character","EndnoteReference",WordStyleNameEndnoteReference,"span.EndnoteReference");
setupNoteReferenceStyle(style);
return style;
}
WordStyle *WordSheetEndnoteText(WordSheet *sheet)
{
WordStyle *style = WordSheetStyleForName(sheet,WordStyleNameEndnoteText);
if (style != NULL)
return style;
style = WordSheetAddStyle(sheet,"paragraph","EndnoteText",WordStyleNameEndnoteText,"p.EndnoteText");
setupNoteTextStyle(style);
return style;
}