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; }
char *CSSSheetCopyText(CSSSheet *sheet) { DFBuffer *result = DFBufferNew(); const char **allSelectors = CSSSheetCopySelectors(sheet); DFSortStringsCaseInsensitive(allSelectors); for (int selIndex = 0; allSelectors[selIndex]; selIndex++) { CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[selIndex],0,0); DFBufferFormat(result,"%s\n",style->selector); const char **sortedSuffixes = CSSStyleCopySuffixes(style); DFSortStringsCaseInsensitive(sortedSuffixes); for (int suffixIndex = 0; sortedSuffixes[suffixIndex]; suffixIndex++) { const char *suffix = sortedSuffixes[suffixIndex]; char *quotedSuffix = DFQuote(suffix); DFBufferFormat(result," %s\n",quotedSuffix); free(quotedSuffix); CSSProperties *properties = CSSStyleRuleForSuffix(style,suffix); const char **sortedNames = CSSPropertiesCopyNames(properties); DFSortStringsCaseInsensitive(sortedNames); for (int nameIndex = 0; sortedNames[nameIndex]; nameIndex++) { const char *name = sortedNames[nameIndex]; const char *value = CSSGet(properties,name); DFBufferFormat(result," %s = %s\n",name,value); } free(sortedNames); } free(sortedSuffixes); } free(allSelectors); char *str = xstrdup(result->data); DFBufferRelease(result); return str; }
static void removeRedundantProperties(CSSSheet *sheet) { // Remove any properties set on a style that have the same value as the corresponding property // on the parent style. This is necessary because CSS doesn't support style inheritance (in // the sense of Word & ODF's styles), so when we save out a HTML file, every style has all // properties of its ancestors. After reading in a HTML file for the purposes of updating the // original Word or ODF style, we don't want these extra property settings to remain, so that // we can avoid adding spurious extra redundant property settings to the original file. breakCycles(sheet); const char **sortedSelectors = reverseTopologicalSortedSelectors(sheet); for (size_t selIndex = 0; sortedSelectors[selIndex]; selIndex++) { const char *selector = sortedSelectors[selIndex]; CSSStyle *child = CSSSheetLookupSelector(sheet,selector,0,0); CSSStyle *parent = CSSSheetGetStyleParent(sheet,child); if (parent == NULL) continue; const char **allSuffixes = CSSStyleCopySuffixes(child); for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) { const char *suffix = allSuffixes[suffixIndex]; int isCell = !strcmp(suffix," > * > tr > td"); CSSProperties *childProperties = CSSStyleRuleForSuffix(child,suffix); CSSProperties *parentProperties = CSSStyleRuleForSuffix(parent,suffix); const char **allNames = CSSPropertiesCopyNames(childProperties); for (int nameIndex = 0; allNames[nameIndex]; nameIndex++) { const char *name = allNames[nameIndex]; // In docx's styles.xml, the tblCellMar values in table styles are not inherited // (this seems like a bug in word, as isn't inconsistent with all other properties) // So keep these ones. if (isCell && DFStringHasPrefix(name,"padding-")) continue; const char *childVal = CSSGet(childProperties,name); const char *parentVal = CSSGet(parentProperties,name); if ((childVal != NULL) && (parentVal != NULL) && DFStringEquals(childVal,parentVal)) CSSPut(childProperties,name,NULL); } free(allNames); } free(allSuffixes); } free(sortedSelectors); }
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 *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 void WordPutStyle(DFNode *concrete, CSSStyle *style, WordConverter *converter) { // If we find a style with a counter-increment value of "<elementname> 0", this means that it's // an explicitly unnumbered paragraph. So we set the numbering id to 0, so word doesn't increment // the corresponding counter when encountering this style if (CSSGet(CSSStyleRule(style),"counter-increment") != NULL) { char *zeroIncrement = DFFormatString("%s 0",style->elementName); if (DFStringEquals(CSSGet(CSSStyleRule(style),"counter-increment"),zeroIncrement)) CSSPut(CSSStyleRule(style),"-word-numId","0"); free(zeroIncrement); } int outlineLvl = -1; if ((style->headingLevel >= 1) && (style->headingLevel <= 6)) { outlineLvl = style->headingLevel-1; char *nextSelector = CSSStyleCopyNext(style); if (nextSelector == NULL) { CSSStyle *def = CSSSheetDefaultStyleForFamily(converter->styleSheet,StyleFamilyParagraph); if (def != NULL) CSSStyleSetNext(style,def->selector); } free(nextSelector); } DFNode *children[PREDEFINED_TAG_COUNT]; childrenToArray(concrete,children); char *parentSelector = CSSStyleCopyParent(style); if (parentSelector == NULL) { if (style->className != NULL) { if ((style->tag != HTML_P) && (style->tag != HTML_SPAN) && (style->tag != HTML_TABLE)) { CSSStyle *parentStyle = CSSSheetLookupElement(converter->styleSheet,style->elementName,NULL,0,0); if ((parentStyle != NULL) && !parentStyle->latent) parentSelector = xstrdup(style->elementName); } } } // Based on const char *basedOn = WordSheetStyleIdForSelector(converter->styles,parentSelector); if (basedOn == NULL) { children[WORD_BASEDON] = NULL; } else { children[WORD_BASEDON] = DFCreateElement(concrete->doc,WORD_BASEDON); DFSetAttribute(children[WORD_BASEDON],WORD_VAL,basedOn); } // Style for next paragraph children[WORD_NEXT] = NULL; char *nextSelector = CSSStyleCopyNext(style); if (nextSelector != NULL) { StyleFamily thisFamily = WordStyleFamilyForSelector(style->selector); StyleFamily nextFamily = WordStyleFamilyForSelector(nextSelector); if (nextFamily == thisFamily) { children[WORD_NEXT] = DFCreateElement(concrete->doc,WORD_NEXT); const char *nextStyleId = WordSheetStyleIdForSelector(converter->styles,nextSelector); DFSetAttribute(children[WORD_NEXT],WORD_VAL,nextStyleId); } } if (WordStyleFamilyForSelector(style->selector) == StyleFamilyTable) { // Table properties if (children[WORD_TBLPR] == NULL) children[WORD_TBLPR] = DFCreateElement(concrete->doc,WORD_TBLPR);; const char *oldJc = DFGetChildAttribute(children[WORD_TBLPR],WORD_JC,WORD_VAL); CSSProperties *wholeTable = CSSStyleRuleForSuffix(style,DFTableSuffixWholeTable); WordPutTblPr(children[WORD_TBLPR],wholeTable,CSSStyleCell(style),converter->mainSection,NULL); const char *newJc = DFGetChildAttribute(children[WORD_TBLPR],WORD_JC,WORD_VAL); if (children[WORD_TBLPR]->first == NULL) children[WORD_TBLPR] = NULL; if (children[WORD_TRPR] == NULL) children[WORD_TRPR] = DFCreateElement(concrete->doc,WORD_TRPR); WordPutTrPr(children[WORD_TRPR],oldJc,newJc); if (children[WORD_TRPR]->first == NULL) children[WORD_TRPR] = NULL; } else { // Paragraph properties if (children[WORD_PPR] == NULL) children[WORD_PPR] = DFCreateElement(concrete->doc,WORD_PPR); WordPutPPr(children[WORD_PPR],CSSStyleRule(style),NULL,converter->mainSection,outlineLvl); if (children[WORD_PPR]->first == NULL) children[WORD_PPR] = NULL; // Run properties if (children[WORD_RPR] == NULL) children[WORD_RPR] = DFCreateElement(concrete->doc,WORD_RPR); WordPutRPr(children[WORD_RPR],CSSStyleRule(style),NULL,converter->theme); if (children[WORD_RPR]->first == NULL) children[WORD_RPR] = NULL; } replaceChildrenFromArray(concrete,children,WordStyle_Children); free(parentSelector); free(nextSelector); }