void DFNameMapFree(DFNameMap *map) { DFHashTableRelease(map->namespacesByID); DFHashTableRelease(map->namespacesByURI); DFHashTableRelease(map->tagsByID); DFNameHashTableFree(map->localTagsByNameURI); free(map); }
void CSSSheetRelease(CSSSheet *sheet) { if ((sheet == NULL) || (--sheet->retainCount > 0)) return; DFHashTableRelease(sheet->_styles); DFHashTableRelease(sheet->_defaultStyles); free(sheet); }
char *CSSPropertiesCopyDescription(CSSProperties *properties) { DFHashTable *collapsed = CSSCollapseProperties(properties); char *result = CSSSerializeProperties(collapsed); DFHashTableRelease(collapsed); return result; }
CSSProperties *CSSPropertiesNewWithString(const char *string) { DFHashTable *raw = CSSParseProperties(string); CSSProperties *result = CSSPropertiesNewWithRaw(raw); DFHashTableRelease(raw); return result; }
CSSStyle *WordSetupTableGridStyle(CSSSheet *styleSheet, int *changed) { CSSStyle *style = CSSSheetLookupElement(styleSheet,"table","Table_Grid",0,0); if (style == NULL) { style = CSSSheetLookupElement(styleSheet,"table","Table_Grid",1,0); CSSProperties *border = CSSPropertiesNewWithString("border: 1px solid black"); DFHashTable *collapsed = CSSCollapseProperties(border); CSSPropertiesUpdateFromRaw(CSSStyleRule(style),collapsed); CSSPropertiesUpdateFromRaw(CSSStyleCell(style),collapsed); DFHashTableRelease(collapsed); CSSPut(CSSStyleRule(style),"margin-left","auto"); CSSPut(CSSStyleRule(style),"margin-right","auto"); // These must be set last, as updateRaw clears them when modifying rule CSSStyleSetParent(style,"table.Normal_Table"); CSSStyleSetDisplayName(style,"Table Grid"); if (changed != NULL) *changed = 1; CSSPropertiesRelease(border); } return style; }
static char *genImageFilename(const char *mediaDir, const char *extension, DFError **error) { const char **names = DFContentsOfDirectory(mediaDir,0,error); if (names == NULL) return NULL; DFHashTable *existingNames = DFHashTableNew((DFCopyFunction)strdup,free); for (int i = 0; names[i]; i++) { const char *filename = names[i]; char *lowerFilename = DFLowerCase(filename); char *noExtension = DFPathWithoutExtension(lowerFilename); DFHashTableAdd(existingNames,noExtension,noExtension); free(lowerFilename); free(noExtension); } int num = 1; char *candidate = NULL; do { free(candidate); candidate = DFFormatString("image%d",num); num++; } while (DFHashTableLookup(existingNames,candidate) != NULL); char *result = DFFormatString("%s.%s",candidate,extension); free(candidate); free(names); DFHashTableRelease(existingNames); return result; }
CSSProperties *CSSPropertiesNewWithExtra(CSSProperties *orig, const char *string) { DFHashTable *extra = CSSParseProperties(string); CSSExpandProperties(extra); CSSProperties *result = (CSSProperties *)calloc(1,sizeof(CSSProperties)); result->retainCount = 1; result->hashTable = DFHashTableNew((DFCopyFunction)strdup,free); const char **names = DFHashTableCopyKeys(orig->hashTable); for (int i = 0; names[i]; i++) { const char *value = DFHashTableLookup(orig->hashTable,names[i]); DFHashTableAdd(result->hashTable,names[i],(void *)value); } free(names); const char **keys = DFHashTableCopyKeys(extra); for (int i = 0; keys[i]; i++) { const char *key = keys[i]; const char *value = DFHashTableLookup(extra,key); DFHashTableAdd(result->hashTable,key,value); } free(keys); DFHashTableRelease(extra); return result; }
char *CSSSheetCopyCSSText(CSSSheet *sheet) { DFHashTable *hashRules = CSSSheetRules(sheet); char *cssText = CSSCopyStylesheetTextFromRules(hashRules); DFHashTableRelease(hashRules); return cssText; }
CSSStyle *CSSSheetFlattenedStyle(CSSSheet *sheet, CSSStyle *orig) { // FIXME: Need tests for parent cycles CSSStyle *ancestor = orig; CSSStyle *result = CSSStyleNew("temp"); DFHashTable *visited = DFHashTableNew((DFCopyFunction)xstrdup,(DFFreeFunction)free); const char **allSuffixes = NULL; while (1) { free(allSuffixes); allSuffixes = CSSStyleCopySuffixes(ancestor); for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) { const char *suffix = allSuffixes[suffixIndex]; CSSProperties *origProperties = CSSStyleRuleForSuffix(ancestor,suffix); CSSProperties *collapsedProperties = CSSStyleRuleForSuffix(result,suffix); const char **allNames = CSSPropertiesCopyNames(origProperties); for (int nameIndex = 0; allNames[nameIndex]; nameIndex++) { const char *name = allNames[nameIndex]; if (!strcmp(name,"-uxwrite-default") && (ancestor != orig)) continue; if (CSSGet(collapsedProperties,name) == NULL) CSSPut(collapsedProperties,name,CSSGet(origProperties,name)); } free(allNames); } DFHashTableAdd(visited,ancestor->selector,""); ancestor = CSSSheetGetStyleParent(sheet,ancestor); if ((ancestor == NULL) || (DFHashTableLookup(visited,ancestor->selector) != NULL)) break; } free(allSuffixes); DFHashTableRelease(visited); return result; }
void WordGarbageCollect(WordPackage *package) { assert(package->documentPart != NULL); assert(package->document != NULL); DFHashTable *referencedIds = DFHashTableNew(NULL,NULL); // used as a set collect(package,referencedIds); DFHashTableRelease(referencedIds); }
void WordBookmarks_collapseNew(DFDocument *doc) { putInParagraphsRecursive(doc->docNode); DFHashTable *bookmarksById = DFHashTableNew(NULL,(DFFreeFunction)WordRawBookmarkFree); int offset = 0; findBookmarkSizes(doc->root,bookmarksById,&offset); collapseRecursive(doc->root,bookmarksById); DFHashTableRelease(bookmarksById); }
static void updateDefaults(WordConverter *converter, CSSSheet *styleSheet) { DFNode *root = converter->package->styles->root; CSSStyle *bodyStyle = CSSSheetLookupElement(converter->styleSheet,"body",NULL,0,0); if (bodyStyle != NULL) { // Remove margin properties DFHashTable *collapsed = CSSCollapseProperties(CSSStyleRule(bodyStyle)); CSSProperties *copy = CSSPropertiesNewWithRaw(collapsed); DFHashTableRelease(collapsed); CSSPut(copy,"margin-top",NULL); CSSPut(copy,"margin-bottom",NULL); CSSPut(copy,"margin-left",NULL); CSSPut(copy,"margin-right",NULL); DFNode *docDefaults = DFChildWithTag(root,WORD_DOCDEFAULTS); DFNode *rPrDefault = DFChildWithTag(docDefaults,WORD_RPRDEFAULT); DFNode *pPrDefault = DFChildWithTag(docDefaults,WORD_PPRDEFAULT); DFNode *rPr = DFChildWithTag(rPrDefault,WORD_RPR); DFNode *pPr = DFChildWithTag(pPrDefault,WORD_PPR); int hadEmptyRPrDefault = ((rPrDefault != NULL) && (rPrDefault->first == NULL)); int hadEmptyPPrDefault = ((pPrDefault != NULL) && (pPrDefault->first == NULL)); if (docDefaults == NULL) docDefaults = DFCreateElement(converter->package->styles,WORD_DOCDEFAULTS); if (rPrDefault == NULL) rPrDefault = DFCreateElement(converter->package->styles,WORD_RPRDEFAULT); if (pPrDefault == NULL) pPrDefault = DFCreateElement(converter->package->styles,WORD_PPRDEFAULT); if (rPr == NULL) rPr = DFCreateChildElement(rPrDefault,WORD_RPR); if (pPr == NULL) pPr = DFCreateChildElement(pPrDefault,WORD_PPR); DFAppendChild(docDefaults,rPrDefault); DFAppendChild(docDefaults,pPrDefault); DFInsertBefore(root,docDefaults,root->first); WordPutPPr(pPr,copy,NULL,converter->mainSection,-1); if (rPr->first == NULL) DFRemoveNode(rPr); if (pPr->first == NULL) DFRemoveNode(pPr); if ((rPrDefault->first == NULL) && !hadEmptyRPrDefault) DFRemoveNode(rPrDefault); if ((pPrDefault->first == NULL) && !hadEmptyPPrDefault) DFRemoveNode(pPrDefault); if (docDefaults->first == NULL) DFRemoveNode(docDefaults); CSSPropertiesRelease(copy); } }
void ODFManifestRelease(ODFManifest *manifest) { if ((manifest == NULL) || (--manifest->retainCount > 0)) return; DFDocumentRelease(manifest->doc); DFHashTableRelease(manifest->entriesByPath); free(manifest); }
void CSSPropertiesUpdateFromRaw(CSSProperties *properties, DFHashTable *raw) { DFHashTableRelease(properties->hashTable); properties->hashTable = DFHashTableCopy(raw); CSSExpandProperties(properties->hashTable); if (!properties->dirty) // Minimise KVO notifications properties->dirty = 1; DFCallbackInvoke(properties->changeCallbacks,properties,NULL); }
void DFMarkupCompatibilityPop(DFMarkupCompatibility *mc) { assert(mc->depth > 0); if (mc->depth < MAX_DEPTH) { MCRecord *record = &mc->records[mc->depth-1]; free(record->decls); DFHashTableRelease(record->namespaces); } mc->depth--; }
void CSSPropertiesRelease(CSSProperties *properties) { assert((properties == NULL) || (properties->retainCount > 0)); if ((properties == NULL) || (--properties->retainCount > 0)) return; assert(properties->changeCallbacks == NULL); DFHashTableRelease(properties->hashTable); free(properties); }
void TextPackageRelease(TextPackage *package) { if ((package == NULL) || (--package->retainCount > 0)) return; for (int i = 0; i < package->nkeys; i++) free(package->keys[i]); free(package->keys); DFHashTableRelease(package->items); free(package); }
void CSSSheetUpdateFromCSSText(CSSSheet *sheet, const char *cssText) { DFHashTable *rules = DFHashTableNew((DFCopyFunction)DFHashTableRetain,(DFFreeFunction)DFHashTableRelease); CSSParser *parser = CSSParserNew(cssText); DFHashTable *top = CSSParserRules(parser); CSSParserFree(parser); const char **allSelectorsText = DFHashTableCopyKeys(top); for (int i = 0; allSelectorsText[i]; i++) { const char *selectorsText = allSelectorsText[i]; const char *propertiesText = DFHashTableLookup(top,selectorsText); parser = CSSParserNew(selectorsText); DFArray *selectors = CSSParserSelectors(parser); CSSParserFree(parser); if (selectors == NULL) continue; parser = CSSParserNew(propertiesText); DFHashTable *properties = CSSParserProperties(parser); CSSParserFree(parser); if (properties == NULL) { DFArrayRelease(selectors); continue; } for (size_t selIndex = 0; selIndex < DFArrayCount(selectors); selIndex++) { const char *selector = DFArrayItemAt(selectors,selIndex); DFHashTableAdd(rules,selector,properties); } DFHashTableRelease(properties); DFArrayRelease(selectors); } updateFromRawCSSRules(sheet,rules); free(allSelectorsText); DFHashTableRelease(top); DFHashTableRelease(rules); }
static void updateFromRawCSSRules(CSSSheet *sheet, DFHashTable *rules) { // FIXME: Handle class names containing escape sequences DFHashTableRelease(sheet->_styles); sheet->_styles = DFHashTableNew((DFCopyFunction)CSSStyleRetain,(DFFreeFunction)CSSStyleRelease); const char **sortedSelectors = DFHashTableCopyKeys(rules); DFSortStringsCaseInsensitive(sortedSelectors); for (int selIndex = 0; sortedSelectors[selIndex]; selIndex++) { const char *constSelector = sortedSelectors[selIndex]; // Treat any selectors specifying the class name only as paragraph styles char *selector; if (!strncmp(constSelector,".",1)) selector = DFFormatString("p%s",constSelector); // FIXME: Not covered by tests else selector = xstrdup(constSelector); DFHashTable *raw = DFHashTableLookup(rules,constSelector); char *baseId = NULL; char *suffix = NULL; CSSParseSelector(selector,&baseId,&suffix); CSSStyle *style = CSSSheetLookupSelector(sheet,baseId,0,0); if (style == NULL) { style = CSSStyleNew(baseId); CSSSheetAddStyle(sheet,style); CSSStyleRelease(style); } CSSProperties *properties = CSSStyleRuleForSuffix(style,suffix); CSSProperties *expanded = CSSPropertiesNewWithRaw(raw); const char **allNames = CSSPropertiesCopyNames(expanded); for (int nameIndex = 0; allNames[nameIndex]; nameIndex++) { const char *name = allNames[nameIndex]; CSSPut(properties,name,CSSGet(expanded,name)); } free(allNames); if (!strcmp(suffix,"")) { const char *defaultVal = CSSGet(properties,"-uxwrite-default"); if ((defaultVal != NULL) && DFStringEqualsCI(defaultVal,"true")) CSSSheetSetDefaultStyle(sheet,style,StyleFamilyFromHTMLTag(style->tag)); } CSSPropertiesRelease(expanded); free(baseId); free(suffix); free(selector); } free(sortedSelectors); removeRedundantProperties(sheet); }
DFHashTable *CSSParserProperties(CSSParser *p) { DFHashTable *result = DFHashTableNew((DFCopyFunction)xstrdup,(DFFreeFunction)free); while (p->pos < p->length) { size_t start = p->pos; int invalid = 0; if (!matchBefore(p,':',&invalid)) { DFHashTableRelease(result); return NULL; } char *name = trimmedSubstring(p,start,p->pos); if (strlen(name) == 0) { free(name); break; } if (!match(p,':')) { CSSParserSetError(p,"Missing :"); DFHashTableRelease(result); free(name); return NULL; } start = p->pos; if (!matchBefore(p,';',&invalid)) { DFHashTableRelease(result); free(name); return NULL; } if (!invalid) { char *value = trimmedSubstring(p,start,p->pos); DFHashTableAdd(result,name,value); free(value); } match(p,';'); free(name); } return result; }
void DFDocumentRelease(DFDocument *doc) { if (doc == NULL) return; assert(doc->retainCount > 0); doc->retainCount--; if (doc->retainCount == 0) { DFHashTableRelease(doc->nodesByIdAttr); DFClearSeqNoHash(doc); for (size_t i = 0; i < doc->nodesCount; i++) free(doc->nodes[i]->attrs); free(doc->nodes); DFNameMapFree(doc->map); DFAllocatorFree(doc->allocator); free(doc); } }
DFHashTable *CSSSheetRules(CSSSheet *sheet) { DFHashTable *result = DFHashTableNew((DFCopyFunction)DFHashTableRetain,(DFFreeFunction)DFHashTableRelease); const char **allSelectors = CSSSheetCopySelectors(sheet); for (int selIndex = 0; allSelectors[selIndex]; selIndex++) { const char *selector = allSelectors[selIndex]; CSSStyle *origStyle = CSSSheetLookupSelector(sheet,selector,0,0); if ((origStyle == NULL) || origStyle->latent) continue; char *elementName = CSSSelectorCopyElementName(selector); char *className = CSSSelectorCopyClassName(selector); char *baseSelector; if (className != NULL) { char *escapedClassName = CSSEscapeIdent(className); baseSelector = DFFormatString("%s.%s",elementName,escapedClassName); free(escapedClassName); } else { baseSelector = xstrdup(elementName); } CSSStyle *flattenedStyle = CSSSheetFlattenedStyle(sheet,origStyle); const char **allSuffixes = CSSStyleCopySuffixes(flattenedStyle); for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) { const char *suffix = allSuffixes[suffixIndex]; char *fullSelector = DFFormatString("%s%s",baseSelector,suffix); CSSProperties *properties = CSSStyleRuleForSuffix(flattenedStyle,suffix); DFHashTable *collapsed = CSSCollapseProperties(properties); if (!((DFHashTableCount(collapsed) == 0) && ((strlen(suffix) > 0) || CSSIsBuiltinSelector(selector)))) DFHashTableAdd(result,fullSelector,collapsed); free(fullSelector); DFHashTableRelease(collapsed); } free(allSuffixes); CSSStyleRelease(flattenedStyle); free(elementName); free(className); free(baseSelector); } free(allSelectors); return result; }
static DFNode *WordTcCreateAbstractNode(WordGetData *get, DFNode *concrete) { DFNode *td = WordConverterCreateAbstract(get,HTML_TD,concrete); CSSProperties *properties = CSSPropertiesNew(); DFNode *tcPr = DFChildWithTag(concrete,WORD_TCPR); if (tcPr != NULL) WordGetTcPr(tcPr,properties);; DFHashTable *collapsed = CSSCollapseProperties(properties); char *styleValue = CSSSerializeProperties(collapsed); DFHashTableRelease(collapsed); if (strlen(styleValue) > 0) DFSetAttribute(td,HTML_STYLE,styleValue); free(styleValue); CSSPropertiesRelease(properties); return td; }
static char *Word_toPlainFromDir(DFStorage *storage, DFHashTable *parts, DFError **error) { char *documentPath = NULL; DFHashTable *rels = DFHashTableNew((DFCopyFunction)xstrdup,(DFFreeFunction)free); DFBuffer *output = DFBufferNew(); char *relsPathRel = NULL; DFDocument *relsDoc = NULL; int ok = 0; documentPath = findDocumentPath(storage,error); if (documentPath == NULL) { DFErrorFormat(error,"findDocumentPath: %s",DFErrorMessage(error)); goto end; } relsPathRel = computeDocumentRelsPath(documentPath); if (DFStorageExists(storage,relsPathRel) && ((relsDoc = DFParseXMLStorage(storage,relsPathRel,error)) == NULL)) { DFErrorFormat(error,"%s: %s",relsPathRel,DFErrorMessage(error)); goto end; } parseDocumentRels(documentPath,relsDoc,rels,error); if (!processParts(parts,documentPath,relsDoc,rels,output,storage,error)) goto end; ok = 1; end: free(relsPathRel); free(documentPath); DFHashTableRelease(rels); DFDocumentRelease(relsDoc); if (!ok) { DFBufferRelease(output); return NULL; } else { char *result = xstrdup(output->data); DFBufferRelease(output); return result; } }
void WordConverterFree(WordConverter *converter) { DFDocumentRelease(converter->html); free(converter->abstractPath); free(converter->concretePath); free(converter->idPrefix); WordSheetFree(converter->styles); WordNumberingFree(converter->numbering); WordThemeFree(converter->theme); WordSectionFree(converter->mainSection); WordObjectsFree(converter->objects); WordNoteGroupRelease(converter->footnotes); WordNoteGroupRelease(converter->endnotes); DFHashTableRelease(converter->supportedContentTypes); DFBufferRelease(converter->warnings); CSSSheetRelease(converter->styleSheet); WordPackageRelease(converter->package); free(converter); }
static void breakCycles(CSSSheet *sheet) { // FIXME: Not covered by tests const char **allSelectors = CSSSheetCopySelectors(sheet); for (int i = 0; allSelectors[i]; i++) { const char *selector = allSelectors[i]; CSSStyle *style = CSSSheetLookupSelector(sheet,selector,0,0); DFHashTable *visited = DFHashTableNew((DFCopyFunction)xstrdup,(DFFreeFunction)free); int depth = 0; while (style != NULL) { if (DFHashTableLookup(visited,style->selector) != NULL) { CSSStyleSetParent(style,NULL); break; } DFHashTableAdd(visited,style->selector,""); style = CSSSheetGetStyleParent(sheet,style); depth++; } DFHashTableRelease(visited); } free(allSelectors); }
static DFNode *WordRunGet(WordGetData *get, DFNode *concrete) { assert(concrete->tag == WORD_R); // First check the run to see if it's a footnote or endnote reference. These need to be handled specially, // as we place the actual content of the footnote or endnote in-line in the HTML output. DFNode *note = WordRunGetNote(get,concrete); if (note != NULL) return note; // If we didn't find a footnote or endnote reference, treat it as a normal content run assert(concrete->tag == WORD_R); DFNode *rPr = DFChildWithTag(concrete,WORD_RPR); CSSProperties *properties = CSSPropertiesNew(); const char *styleId = NULL; if (rPr != NULL) WordGetRPr(rPr,properties,&styleId,get->conv->theme);; const char *selector = WordSheetSelectorForStyleId(get->conv->styles,"character",styleId); Tag elementName = (selector == NULL) ? HTML_SPAN : CSSSelectorGetTag(selector); char *className = (selector == NULL) ? NULL : CSSSelectorCopyClassName(selector); DFNode *abstract = WordConverterCreateAbstract(get,elementName,concrete); DFSetAttribute(abstract,HTML_CLASS,className); free(className); DFHashTable *collapsed = CSSCollapseProperties(properties); char *styleValue = CSSSerializeProperties(collapsed); DFHashTableRelease(collapsed); if (strlen(styleValue) > 0) DFSetAttribute(abstract,HTML_STYLE,styleValue); free(styleValue); CSSPropertiesRelease(properties); WordContainerGet(get,&WordRunContentLens,abstract,concrete); return abstract; }
void WordUpdateStyles(WordConverter *converter, CSSSheet *styleSheet) { CSSStyle *paraDefault = CSSSheetDefaultStyleForFamily(styleSheet,StyleFamilyParagraph); if (CSSGet(CSSStyleRule(paraDefault),"margin-top") == NULL) CSSPut(CSSStyleRule(paraDefault),"margin-top","-word-auto"); if (CSSGet(CSSStyleRule(paraDefault),"margin-bottom") == NULL) CSSPut(CSSStyleRule(paraDefault),"margin-bottom","-word-auto"); if (converter->package->styles == NULL) // FIXME: create this document return;; DFNode *root = converter->package->styles->root; if ((root == NULL) || (root->tag != WORD_STYLES)) return;; DFHashTable *remainingSelectors = DFHashTableNew(NULL,NULL); // Used as a set const char **allSelectors = CSSSheetCopySelectors(styleSheet); for (int i = 0; allSelectors[i]; i++) { const char *selector = allSelectors[i]; DFHashTableAdd(remainingSelectors,selector,""); } free(allSelectors); WordSheet *sheet = converter->styles; DFHashTable *oldConcreteNumIds = WordSheetFindUsedConcreteNumIds(sheet); updateNumbering(converter,styleSheet); // Update or remove existing styles const char **allIdents = WordSheetCopyIdents(sheet); for (int i = 0; allIdents[i]; i++) { WordStyle *wordStyle = WordSheetStyleForIdent(sheet,allIdents[i]); DFNode *element = wordStyle->element; if (WordStyleIsProtected(wordStyle)) { DFHashTableRemove(remainingSelectors,wordStyle->selector); continue; } if (!DFStringEquals(wordStyle->type,"paragraph") && !DFStringEquals(wordStyle->type,"character") && !DFStringEquals(wordStyle->type,"table")) continue; CSSStyle *cssStyle = CSSSheetLookupSelector(styleSheet,wordStyle->selector,0,0); if (cssStyle == NULL) { // Remove style WordSheetRemoveStyle(sheet,wordStyle); continue; } // Update style WordPutStyle(element,cssStyle,converter); updateDefault(cssStyle,element,styleSheet,converter); DFHashTableRemove(remainingSelectors,wordStyle->selector); } free(allIdents); // Sort the list of new styles, so that test output is deterministic const char **sortedSelectors = DFHashTableCopyKeys(remainingSelectors); DFSortStringsCaseInsensitive(sortedSelectors); // Add new styles. We do this in two stages - first creating the styles, and then setting their properties. // This is because the second stage depends on referenced styles (e.g. based on and next) to be already // present. for (int selIndex = 0; sortedSelectors[selIndex]; selIndex++) { const char *selector = sortedSelectors[selIndex]; CSSStyle *style = CSSSheetLookupSelector(styleSheet,selector,0,0); const char *familyStr = NULL; StyleFamily family = WordStyleFamilyForSelector(selector); if (family == StyleFamilyParagraph) familyStr = "paragraph"; else if (family == StyleFamilyCharacter) familyStr = "character"; else if (family == StyleFamilyTable) familyStr = "table"; else continue; char *styleId = WordStyleIdForStyle(style); char *name = WordStyleNameForStyle(style); if (name == NULL) name = xstrdup(styleId);; WordStyle *wordStyle = WordSheetAddStyle(sheet,familyStr,styleId,name,selector); DFCreateChildElement(wordStyle->element,WORD_QFORMAT); free(styleId); free(name); } for (int selIndex = 0; sortedSelectors[selIndex]; selIndex++) { const char *selector = sortedSelectors[selIndex]; StyleFamily family = WordStyleFamilyForSelector(selector); if ((family != StyleFamilyParagraph) && (family != StyleFamilyCharacter) && (family != StyleFamilyTable)) continue; CSSStyle *style = CSSSheetLookupSelector(styleSheet,selector,0,0); WordStyle *wordStyle = WordSheetStyleForSelector(converter->styles,selector); assert(wordStyle != NULL); CSSStyleAddDefaultHTMLProperties(style); // FIXME: language // FIXME: not covered by tests if ((style->headingLevel >= 1) && (style->headingLevel <= 6)) CSSStyleSetNext(style,"p.Normal"); WordPutStyle(wordStyle->element,style,converter); updateDefault(style,wordStyle->element,styleSheet,converter); } free(sortedSelectors); // Update body style (document defaults) updateDefaults(converter,styleSheet); updateBody(converter,styleSheet); DFHashTable *newConcreteNumIds = WordSheetFindUsedConcreteNumIds(sheet); const char **oldKeys = DFHashTableCopyKeys(oldConcreteNumIds); for (int oldIndex = 0; oldKeys[oldIndex]; oldIndex++) { const char *numId = oldKeys[oldIndex]; if (DFHashTableLookup(newConcreteNumIds,numId) == NULL) { WordConcreteNum *concreteNum = WordNumberingConcreteWithId(converter->numbering,numId); if (concreteNum != NULL) WordNumberingRemoveConcrete(converter->numbering,concreteNum); } } free(oldKeys); DFHashTableRelease(remainingSelectors); DFHashTableRelease(oldConcreteNumIds); DFHashTableRelease(newConcreteNumIds); }
static void collapseRecursive(DFNode *node, DFHashTable *bookmarksById) { DFNode *next; for (DFNode *child = node->first; child != NULL; child = next) { next = child->next; switch (child->tag) { case WORD_BOOKMARKSTART: case WORD_BOOKMARKEND: { DFArray *startElements = DFArrayNew(NULL,NULL); DFArray *endElements = DFArrayNew(NULL,NULL); DFHashTable *startIds = DFHashTableNew((DFCopyFunction)strdup,(DFFreeFunction)free); DFHashTable *endIds = DFHashTableNew((DFCopyFunction)strdup,(DFFreeFunction)free); DFNode *n; for (n = child; (n != NULL) && ((n->tag == WORD_BOOKMARKSTART) || (n->tag == WORD_BOOKMARKEND)); n = n->next) { if (n->tag == WORD_BOOKMARKSTART) { const char *idValue = DFGetAttribute(n,WORD_ID); if (idValue == NULL) idValue = ""; DFHashTableAdd(startIds,idValue,idValue); DFArrayAppend(startElements,n); } else { const char *idValue = DFGetAttribute(n,WORD_ID); if (idValue == NULL) idValue = ""; DFHashTableAdd(endIds,idValue,idValue); DFArrayAppend(endElements,n); } } next = n; DFArraySort(startElements,bookmarksById,compareStartElements); for (size_t endIndex = 0; endIndex < DFArrayCount(endElements); endIndex++) { DFNode *elem = DFArrayItemAt(endElements,endIndex); const char *endId = DFGetAttribute(elem,WORD_ID); int found = 0; DFNode *ancestor; for (ancestor = elem->parent; (ancestor != NULL) && !found; ancestor = ancestor->parent) { if ((ancestor->tag == WORD_BOOKMARK) && DFStringEquals(DFGetAttribute(ancestor,WORD_ID),endId)) { found = 1; break; } } if (found) { DFNode *before = ancestor->next; DFNode *nnext; for (DFNode *n = child; n != NULL; n = nnext) { nnext = n->next; DFInsertBefore(ancestor->parent,n,before); } } } size_t x = 0; while (x < DFArrayCount(startElements)) { DFNode *element = DFArrayItemAt(startElements,x); const char *bookmarkId = DFGetAttribute(element,WORD_ID); if (bookmarkId == NULL) bookmarkId = ""; if (DFHashTableLookup(endIds,bookmarkId) != NULL) { element->tag = WORD_BOOKMARK; DFArrayRemove(startElements,x); } else { x++; } } if (DFArrayCount(startElements) > 0) { for (size_t i = 1; i < DFArrayCount(startElements); i++) { DFNode *tempParent = DFArrayItemAt(startElements,i-1); DFNode *tempChild = DFArrayItemAt(startElements,i); DFAppendChild(tempParent,tempChild); } DFNode *last = DFArrayItemAt(startElements,DFArrayCount(startElements)-1); while (next != NULL) { DFNode *tempChild = next; next = next->next; DFAppendChild(last,tempChild); } } for (size_t eIndex = 0; eIndex < DFArrayCount(startElements); eIndex++) { DFNode *e = DFArrayItemAt(startElements,eIndex); e->tag = WORD_BOOKMARK; } for (size_t eIndex = 0; eIndex < DFArrayCount(endElements); eIndex++) { DFNode *e = DFArrayItemAt(endElements,eIndex); DFRemoveNode(e); } if (DFArrayCount(startElements) > 0) { DFNode *last = DFArrayItemAt(startElements,DFArrayCount(startElements)-1); collapseRecursive(last,bookmarksById); } DFArrayRelease(startElements); DFArrayRelease(endElements); DFHashTableRelease(startIds); DFHashTableRelease(endIds); break; } default: collapseRecursive(child,bookmarksById); break; } } }
void Word_setupBookmarkLinks(WordPutData *put) { DFHashTable *referencesById = findReferences(put->conv->html); const char **sortedIds = DFHashTableCopyKeys(referencesById); DFSortStringsCaseSensitive(sortedIds); for (int idIndex = 0; sortedIds[idIndex]; idIndex++) { const char *targetId = sortedIds[idIndex]; DFArray *references = DFHashTableLookup(referencesById,targetId); DFNode *targetElem = DFElementForIdAttr(put->conv->html,targetId); if (targetElem == NULL) continue; // The following is only relevant for figures and tables int refText = 0; int refLabelNum = 0; int refCaptionText = 0; for (int refIndex = 0; refIndex < DFArrayCount(references); refIndex++) { DFNode *a = DFArrayItemAt(references,refIndex); const char *className = DFGetAttribute(a,HTML_CLASS); if (DFStringEquals(className,DFRefTextClass)) refText = 1; else if (DFStringEquals(className,DFRefLabelNumClass)) refLabelNum = 1; else if (DFStringEquals(className,DFRefCaptionTextClass)) refCaptionText = 1; } DFNode *concrete = WordConverterGetConcrete(put,targetElem); switch (targetElem->tag) { case HTML_H1: case HTML_H2: case HTML_H3: case HTML_H4: case HTML_H5: case HTML_H6: { const char *bookmarkId = NULL; const char *bookmarkName = NULL; DFNode *bookmarkElem = NULL; if ((concrete != NULL) && (concrete->tag == WORD_P)) { // FIXME: We only want to consider the bookmark to be the headings "correct" // bookmark in the case where it contains all of the heading's content, though // excluding other bookmarks that might come before or after it. // If you have the cursor inside a heading bookmark when you save the document, // word puts a bookmark called _GoBack there, and we of course don't want to // confuse that with the actual heading's bookmark (if any). // For now as a temporary hack we just explicitly filter out _GoBack; but there // needs to be a more general fix, as there may be other bookmarks that end up // in the heading. for (DFNode *child = concrete->first; child != NULL; child = child->next) { if ((child->tag == WORD_BOOKMARK) && !DFStringEquals(DFGetAttribute(child,WORD_NAME),"_GoBack")) { bookmarkElem = child; bookmarkId = DFGetAttribute(bookmarkElem,WORD_ID); bookmarkName = DFGetAttribute(bookmarkElem,WORD_NAME); break; } } } if ((bookmarkElem == NULL) || (bookmarkId == NULL) || (bookmarkName == NULL)) { // New bookmark WordBookmark *bookmark = WordObjectsAddBookmark(put->conv->objects); bookmarkId =bookmark->bookmarkId; bookmarkName = bookmark->bookmarkName; } DFNode *bookmarkSpan = DFCreateElement(put->conv->package->document,HTML_SPAN); DFSetAttribute(bookmarkSpan,HTML_CLASS,DFBookmarkClass); if (bookmarkElem != NULL) { // FIXME: Not covered by tests DFFormatAttribute(bookmarkSpan,HTML_ID,"%s%u",put->conv->idPrefix,bookmarkElem->seqNo); } DFSetAttribute(bookmarkSpan,WORD_NAME,bookmarkName); DFSetAttribute(bookmarkSpan,WORD_ID,bookmarkId); while (targetElem->first != NULL) DFAppendChild(bookmarkSpan,targetElem->first); DFAppendChild(targetElem,bookmarkSpan); break; } case HTML_TABLE: case HTML_FIGURE: { WordCaption *caption = WordObjectsCaptionForTarget(put->conv->objects,targetElem); if (caption == NULL) break; assert(caption->element != NULL); assert((caption->number == NULL) || (caption->number->parent == caption->element)); assert((caption->contentStart == NULL) || (caption->contentStart->parent == caption->element)); // Note: caption.number may be null (i.e. if the caption is unnumbered) // caption.contentStart may be null (if there is no text in the caption) WordBookmark *captionTextBookmark = NULL; WordBookmark *labelNumBookmark = NULL; WordBookmark *textBookmark = NULL; if (!refCaptionText && !refLabelNum && !refText) refText = 1; if (refCaptionText) { captionTextBookmark = createBookmark(put->conv); DFNode *nnext; for (DFNode *n = caption->contentStart; n != NULL; n = nnext) { nnext = n->next; DFAppendChild(captionTextBookmark->element,n); } DFAppendChild(caption->element,captionTextBookmark->element); } if (refLabelNum && (caption->number != NULL)) { labelNumBookmark = createBookmark(put->conv); DFNode *numberNext = caption->number->next; DFNode *nnext; for (DFNode *n = caption->element->first; (n != NULL) && (n != numberNext); n = nnext) { nnext = n->next; DFAppendChild(labelNumBookmark->element,n); } DFInsertBefore(caption->element,labelNumBookmark->element,caption->element->first); } if (refText) { textBookmark = createBookmark(put->conv); DFNode *nnext; for (DFNode *n = caption->element->first; n != NULL; n = nnext) { nnext = n->next; DFAppendChild(textBookmark->element,n); } DFAppendChild(caption->element,textBookmark->element); } caption->captionTextBookmark = captionTextBookmark; caption->labelNumBookmark = labelNumBookmark; caption->textBookmark = textBookmark; break; } } } free(sortedIds); DFHashTableRelease(referencesById); }