| // 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; |
| } |