static void WordGetSectPr(DFNode *concrete, CSSProperties *body, CSSProperties *page, WordSection *section) { DFNode *pgSz = NULL; DFNode *pgMar = NULL; for (DFNode *child = concrete->first; child != NULL; child = child->next) { switch (child->tag) { case WORD_PGSZ: pgSz = child; break; case WORD_PGMAR: pgMar = child; break; } } if ((pgSz != NULL) && (pgMar != NULL)) { const char *widthStr = DFGetAttribute(pgSz,WORD_W); const char *heightStr = DFGetAttribute(pgSz,WORD_H); if (widthStr != NULL) section->pageWidth = atoi(widthStr); if (heightStr != NULL) section->pageHeight = atoi(heightStr); if ((widthStr != NULL) && (heightStr != NULL)) { const char *leftStr = DFGetAttribute(pgMar,WORD_LEFT); const char *rightStr = DFGetAttribute(pgMar,WORD_RIGHT); const char *topStr = DFGetAttribute(pgMar,WORD_TOP); const char *bottomStr = DFGetAttribute(pgMar,WORD_BOTTOM); // In CSS, margins (both horizontal and vertical) are measured relative to the width of // the containing block, when expressed as percentages double width = atof(widthStr); if (leftStr != NULL) { double leftPct = 100.0*atof(leftStr)/width; char buf[100]; CSSPut(body,"margin-left",DFFormatDoublePct(buf,100,leftPct)); section->leftMargin = atoi(leftStr); } if (rightStr != NULL) { double rightPct = 100.0*atof(rightStr)/width; char buf[100]; CSSPut(body,"margin-right",DFFormatDoublePct(buf,100,rightPct)); section->rightMargin = atoi(rightStr); } if (topStr != NULL) { double topPct = 100.0*atof(topStr)/width; char buf[100]; CSSPut(body,"margin-top",DFFormatDoublePct(buf,100,topPct)); section->topMargin = atoi(topStr); } if (bottomStr != NULL) { double bottomPct = 100.0*atof(bottomStr)/width; char buf[100]; CSSPut(body,"margin-bottom",DFFormatDoublePct(buf,100,bottomPct)); section->bottomMargin = atoi(bottomStr); } // A "twip" is a twentieth of a point int widthTwips = atoi(widthStr); int heightTwips = atoi(heightStr); if ((widthTwips == A4_WIDTH_TWIPS) && (heightTwips == A4_HEIGHT_TWIPS)) CSSPut(page,"size","A4 portrait"); else if ((widthTwips == A4_HEIGHT_TWIPS) && (heightTwips == A4_WIDTH_TWIPS)) CSSPut(page,"size","A4 landscape"); else if ((widthTwips == LETTER_WIDTH_TWIPS) && (heightTwips == LETTER_HEIGHT_TWIPS)) CSSPut(page,"size","letter portrait"); else if ((widthTwips == LETTER_HEIGHT_TWIPS) && (heightTwips == LETTER_WIDTH_TWIPS)) CSSPut(page,"size","letter landscape"); } } }
static void WordGetStyle(DFNode *concrete, CSSStyle *style, WordConverter *converter) { WordSection *section = converter->mainSection; for (DFNode *child = concrete->first; child != NULL; child = child->next) { const char *styleId = NULL; switch (child->tag) { case WORD_NAME: CSSStyleSetDisplayName(style,DFGetAttribute(child,WORD_VAL)); break; case WORD_NEXT: { // FIXME: do he need to handle style types other than paragraph here? const char *nextName = DFGetAttribute(child,WORD_VAL); if (nextName == NULL) continue; WordStyle *nextStyle = WordSheetStyleForTypeId(converter->styles,"paragraph",nextName); if (nextStyle == NULL) continue; CSSStyleSetNext(style,nextStyle->selector); break; } case WORD_RPR: WordGetRPr(child,CSSStyleRule(style),&styleId,converter->theme); break; case WORD_PPR: { WordGetPPr(child,CSSStyleRule(style),&styleId,section); if (style->headingLevel > 0) { DFNode *numPr = DFChildWithTag(child,WORD_NUMPR); if (numPr != NULL) WordGetNumPrStyle(numPr,style,converter); } break; } case WORD_TBLPR: WordGetTblPr(child,CSSStyleRule(style),CSSStyleCell(style),section,&styleId); break; case WORD_TRPR: // FIXME break; case WORD_TCPR: WordGetTcPr(child,CSSStyleRule(style)); break; case WORD_TBLSTYLEPR: WordGetTblStylePr(child,style,section,converter->theme); break; } } // Special case: The ListParagraph style that word automatically adds specifies an indentation // of 36pt. We don't actually want this, because HTML automatically indents lists anyway. If // we see this, clear it, but keep the old value around for when we update the word document. StyleFamily family = WordStyleFamilyForSelector(style->selector); const char *name = WordSheetStyleIdForSelector(converter->styles,style->selector); if ((family == StyleFamilyParagraph) && DFStringEquals(name,"ListParagraph")) { CSSProperties *properties = CSSStyleRule(style); const char *wordMarginLeft = CSSGet(properties,"margin-left"); CSSPut(properties,"-word-margin-left",wordMarginLeft); CSSPut(properties,"margin-left",NULL); } DFNode *pPr = DFChildWithTag(concrete,WORD_PPR); DFNode *numPr = DFChildWithTag(pPr,WORD_NUMPR); const char *numId = DFGetChildAttribute(numPr,WORD_NUMID,WORD_VAL); if ((numId != NULL) && (atoi(numId) == 0)) { switch (style->tag) { case HTML_H1: case HTML_H2: case HTML_H3: case HTML_H4: case HTML_H5: case HTML_H6: case HTML_FIGURE: case HTML_TABLE: { char *counterIncrement = DFFormatString("%s 0",style->elementName); CSSPut(CSSStyleRule(style),"counter-reset","null"); CSSPut(CSSStyleRule(style),"counter-increment",counterIncrement); CSSPut(CSSStyleBefore(style),"content","none"); free(counterIncrement); } } } }
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); }
int WordConverterUpdateFromHTML(WordConverter *converter, DFError **error) { if (converter->package->document == NULL) { DFErrorFormat(error,"document.xml not found"); return 0; } DFNode *wordDocument = DFChildWithTag(converter->package->document->docNode,WORD_DOCUMENT); if (wordDocument == NULL) { DFErrorFormat(error,"word:document not found"); return 0; } // FIXME: Need a more reliable way of telling whether this is a new document or not - it could be that the // document already existed (with styles set up) but did not have any content DFNode *wordBody = DFChildWithTag(wordDocument,WORD_BODY); int creating = ((wordBody == NULL) || (wordBody->first == NULL)); converter->haveFields = Word_simplifyFields(converter->package); Word_mergeRuns(converter->package); assert(converter->package->styles); CSSSheetRelease(converter->styleSheet); converter->styleSheet = CSSSheetNew(); char *cssText = HTMLCopyCSSText(converter->html); CSSSheetUpdateFromCSSText(converter->styleSheet,cssText); free(cssText); addMissingDefaultStyles(converter); CSSEnsureReferencedStylesPresent(converter->html,converter->styleSheet); if (creating) CSSSetHTMLDefaults(converter->styleSheet); CSSEnsureUnique(converter->styleSheet,converter->html,creating); CSSStyle *pageStyle = CSSSheetLookupElement(converter->styleSheet,"@page",NULL,0,0); CSSStyle *bodyStyle = CSSSheetLookupElement(converter->styleSheet,"body",NULL,1,0); CSSProperties *page = (pageStyle != NULL) ? CSSPropertiesRetain(CSSStyleRule(pageStyle)) : CSSPropertiesNew(); CSSProperties *body = (bodyStyle != NULL) ? CSSPropertiesRetain(CSSStyleRule(bodyStyle)) : CSSPropertiesNew(); if (CSSGet(body,"margin-left") == NULL) CSSPut(body,"margin-left","10%"); if (CSSGet(body,"margin-right") == NULL) CSSPut(body,"margin-right","10%"); if (CSSGet(body,"margin-top") == NULL) CSSPut(body,"margin-top","10%"); if (CSSGet(body,"margin-bottom") == NULL) CSSPut(body,"margin-bottom","10%"); WordSectionUpdateFromCSSPage(converter->mainSection,page,body); WordPutData put; put.conv = converter; put.numIdByHtmlId = DFHashTableNew((DFCopyFunction)strdup,free); put.htmlIdByNumId = DFHashTableNew((DFCopyFunction)strdup,free); // Make sure we update styles.xml from the CSS stylesheet *before* doing any conversion of the content, // since the latter requires a full mapping of CSS selectors to styleIds to be in place. WordUpdateStyles(converter,converter->styleSheet); Word_preProcessHTMLDoc(converter,converter->html); buildListMapFromHTML(&put,converter->html->docNode); updateListTypes(&put); WordBookmarks_removeCaptionBookmarks(converter->package->document); WordObjectsCollapseBookmarks(converter->objects); WordObjectsScan(converter->objects); Word_setupBookmarkLinks(&put); WordObjectsAnalyzeBookmarks(converter->objects,converter->styles); WordDocumentLens.put(&put,converter->html->root,wordDocument); WordObjectsExpandBookmarks(converter->objects); WordRemoveNbsps(converter->package->document); // Make sure the updateFields flag is set Word_updateSettings(converter->package,converter->haveFields); // Remove any abstract numbering definitions that are no longer referenced from concrete // numbering definitions WordNumberingRemoveUnusedAbstractNums(converter->numbering); // Remove any relationships and images that have been removed from the HTML file and no longer // have any other references pointing to them WordGarbageCollect(converter->package); CSSPropertiesRelease(page); CSSPropertiesRelease(body); DFHashTableRelease(put.numIdByHtmlId); DFHashTableRelease(put.htmlIdByNumId); return 1; }
static void adjustMarginLeft(DFNode *element, double adjustPct, int noTextIndent) { if ((element->tag != HTML_TABLE) && !HTML_isParagraphTag(element->tag)) return;; const char *cssText = DFGetAttribute(element,HTML_STYLE); CSSProperties *properties = CSSPropertiesNewWithString(cssText); double oldMarginLeft = 0; if (CSSGet(properties,"margin-left") != NULL) { CSSLength length = CSSLengthFromString(CSSGet(properties,"margin-left")); if (CSSLengthIsValid(length) && (length.units == UnitsPct)) oldMarginLeft = length.value; if (CSSGet(properties,"width") != NULL) { CSSLength length = CSSLengthFromString(CSSGet(properties,"width")); if (CSSLengthIsValid(length) && (length.units == UnitsPct)) { double oldWidth = length.value; double newWidth = oldWidth + oldMarginLeft; char buf[100]; CSSPut(properties,"width",DFFormatDoublePct(buf,100,newWidth)); } } } double oldTextIndent = 0; if (CSSGet(properties,"text-indent") != NULL) { CSSLength length = CSSLengthFromString(CSSGet(properties,"text-indent")); if (CSSLengthIsValid(length) && (length.units == UnitsPct)) oldTextIndent = length.value; } double newMarginLeft = oldMarginLeft + adjustPct; double newTextIndent = oldTextIndent; if (newMarginLeft < 0) newMarginLeft = 0; if (fabs(newMarginLeft) >= 0.01) { char buf[100]; CSSPut(properties,"margin-left",DFFormatDoublePct(buf,100,newMarginLeft)); } else { CSSPut(properties,"margin-left",NULL); } if (noTextIndent) { CSSPut(properties,"text-indent",NULL); } else if (newTextIndent < -newMarginLeft) { // Don't allow negative text-indent newTextIndent = -newMarginLeft; if (fabs(newTextIndent) >= 0.01) { char buf[100]; CSSPut(properties,"text-indent",DFFormatDoublePct(buf,100,newTextIndent)); } else { CSSPut(properties,"text-indent",NULL); } } char *propertiesText = CSSPropertiesCopyDescription(properties); if (strlen(propertiesText) == 0) DFRemoveAttribute(element,HTML_STYLE); else DFSetAttribute(element,HTML_STYLE,propertiesText); free(propertiesText); CSSPropertiesRelease(properties); }
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 = strdup(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 Word_postProcessHTML(WordConverter *conv, DFNode *node) { DFNode *next; for (DFNode *child = node->first; child != NULL; child = next) { next = child->next; switch (child->tag) { case HTML_SPAN: { const char *className = DFGetAttribute(child,HTML_CLASS); if (DFStringEquals(className,DFBookmarkClass)) { if (child->first != NULL) next = child->first; DFRemoveNodeButKeepChildren(child); } break; } case HTML_CAPTION: { const char *counterName = NULL; if ((child->prev != NULL) && (child->prev->tag == HTML_FIGURE) && (DFChildWithTag(child->prev,HTML_FIGCAPTION) == NULL)) { child->tag = HTML_FIGCAPTION; counterName = "figure"; DFAppendChild(child->prev,child); } else if ((child->prev != NULL) && (child->prev->tag == HTML_TABLE) && (DFChildWithTag(child->prev,HTML_CAPTION) == NULL)) { counterName = "table"; DFInsertBefore(child->prev,child,child->prev->first); } else if ((child->next != NULL) && (child->next->tag == HTML_FIGURE) && (DFChildWithTag(child->next,HTML_FIGCAPTION) == NULL)) { child->tag = HTML_FIGCAPTION; counterName = "figure"; DFInsertBefore(child->next,child,child->next->first); } else if ((child->next != NULL) && (child->next->tag == HTML_TABLE) && (DFChildWithTag(child->next,HTML_CAPTION) == NULL)) { counterName = "table"; DFSetAttribute(child,HTML_STYLE,"caption-side: top"); DFInsertBefore(child->next,child,child->next->first); } if (counterName != NULL) { char *beforeText = extractPrefix(child,counterName); if (beforeText != NULL) { CSSStyle *style = CSSSheetLookupElement(conv->styleSheet,DFNodeName(child),NULL,1,0); if (CSSGet(CSSStyleBefore(style),"content") == NULL) { CSSPut(CSSStyleRule(style),"counter-increment",counterName); CSSPut(CSSStyleBefore(style),"content",beforeText); } } free(beforeText); } break; } case HTML_NAV: { if (HTML_isParagraphTag(node->tag)) { if (child->prev != NULL) { DFNode *beforeP = DFCreateElement(conv->package->document,node->tag); while (child->prev != NULL) DFInsertBefore(beforeP,child->prev,beforeP->first); DFInsertBefore(node->parent,beforeP,node); } DFInsertBefore(node->parent,child,node); if ((node->first == NULL) || ((node->first->tag == HTML_BR) && (node->first->next == NULL))) { DFRemoveNode(node); return; } next = NULL; } break; } } } for (DFNode *child = node->first; child != NULL; child = next) { next = child->next; Word_postProcessHTML(conv,child); } }
// FIXME: This won't work now for Word documents, where styles always have class names // FIXME: Not covered by tests void CSSSheetSetHeadingNumbering(CSSSheet *sheet, int enabled) { CSSStyle *defaultStyle = CSSSheetDefaultStyleForFamily(sheet,StyleFamilyParagraph); if (enabled) { DFHashTable *stylesByHeadingLevel = getStylesByHeadingLevel(sheet); for (int level = 1; level <= 6; level++) { StyleList *styles = DFHashTableLookupInt(stylesByHeadingLevel,level); int haveClassless = 0; for (StyleList *item = styles; item != NULL; item = item->next) { if (item->style->className == NULL) haveClassless = 1; } // If there exists a style at level n which has no class name, then that is the parent of all other // heading styles. Set the numbering on that. Alternatively, if there no styles exist at level n, then // create a new one with no class name, and set the numbering information on that. if ((styles == NULL) || haveClassless) { char *elementName = DFFormatString("h%d",level); CSSStyle *style = CSSSheetLookupElement(sheet,elementName,NULL,1,0); enableNumberingForStyle(style); free(elementName); } else { // There are multiple heading styles at level n, all of which have a class name associated with them. // Set the numbering information on those which either have no parentId, or whose parentId is the // default paragraph style. This caters for situations like the "HeadingXUnnumbered" styles we used // to have, which were based on the corresponding "HeadingX" style. for (StyleList *item = styles; item != NULL; item = item->next) { char *parentSelector = CSSStyleCopyParent(item->style); if ((parentSelector == NULL) || !strcmp(parentSelector,defaultStyle->selector)) { enableNumberingForStyle(item->style); } else { disableNumberingForStyle(item->style,1); } free(parentSelector); } } StyleList *next; for (; styles != NULL; styles = next) { next = styles->next; CSSStyleRelease(styles->style); free(styles); } } DFHashTableRelease(stylesByHeadingLevel); } else { // Clear all numbering information on all heading styles const char **allSelectors = CSSSheetCopySelectors(sheet); for (int i = 0; allSelectors[i]; i++) { CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[i],0,0); if (style->headingLevel > 0) { CSSPut(CSSStyleRule(style),"counter-increment",NULL); CSSPut(CSSStyleRule(style),"counter-reset",NULL); CSSPut(CSSStyleBefore(style),"content",NULL); } } free(allSelectors); } }
void WordGetPPr(DFNode *pPr, CSSProperties *properties, const char **styleId, WordSection *section) { assert(pPr->tag == WORD_PPR); if (styleId != NULL) *styleId = NULL; for (DFNode *child = pPr->first; child != NULL; child = child->next) { switch (child->tag) { case WORD_PSTYLE: if (styleId != NULL) *styleId = DFGetAttribute(child,WORD_VAL); break; case WORD_PBDR: WordGetPBdr(child,properties); break; case WORD_SHD: WordGetShd(child,properties); break; case WORD_JC: { const char *val = DFGetAttribute(child,WORD_VAL); if (val != NULL) { if (!strcmp(val,"left") || !strcmp(val,"start")) CSSPut(properties,"text-align","left"); else if (!strcmp(val,"right") || !strcmp(val,"end")) CSSPut(properties,"text-align","right"); else if (!strcmp(val,"center")) CSSPut(properties,"text-align","center"); else if (!strcmp(val,"both")) CSSPut(properties,"text-align","justify"); } break; } case WORD_IND: { if ((section == NULL) || (WordSectionContentWidth(section) <= 0)) break; // Units: 1/20th of a point const char *firstLine = DFGetAttribute(child,WORD_FIRSTLINE); const char *hanging = DFGetAttribute(child,WORD_HANGING); const char *left = DFGetAttribute(child,WORD_START); if (left == NULL) left = DFGetAttribute(child,WORD_LEFT);; const char *right = DFGetAttribute(child,WORD_END); if (right == NULL) right = DFGetAttribute(child,WORD_RIGHT); if (left != NULL) { double leftPct = 100.0*atoi(left)/(double)WordSectionContentWidth(section); char buf[100]; CSSPut(properties,"margin-left",DFFormatDoublePct(buf,100,leftPct)); } if (right != NULL) { double rightPct = 100.0*atoi(right)/(double)WordSectionContentWidth(section); char buf[100]; CSSPut(properties,"margin-right",DFFormatDoublePct(buf,100,rightPct)); } // hanging and firstLine attributes are mutually exclusive. If both are specified, // hanging takes precedence if (hanging != NULL) { double indentPct = -100.0*atoi(hanging)/(double)WordSectionContentWidth(section); char buf[100]; CSSPut(properties,"text-indent",DFFormatDoublePct(buf,100,indentPct)); } else if (firstLine != NULL) { double indentPct = 100.0*atoi(firstLine)/(double)WordSectionContentWidth(section); char buf[100]; CSSPut(properties,"text-indent",DFFormatDoublePct(buf,100,indentPct)); } break; } case WORD_SPACING: { const char *before = DFGetAttribute(child,WORD_BEFORE); if (before != NULL) { // units: 1/20th of a point char buf[100]; CSSPut(properties,"margin-top",DFFormatDoublePt(buf,100,atoi(before)/20.0)); } const char *after = DFGetAttribute(child,WORD_AFTER); if (after != NULL) { // units: 1/20th of a point char buf[100]; CSSPut(properties,"margin-bottom",DFFormatDoublePt(buf,100,atoi(after)/20.0)); } const char *line = DFGetAttribute(child,WORD_LINE); const char *lineRule = DFGetAttribute(child,WORD_LINERULE); if ((lineRule == NULL) || !strcmp(lineRule,"auto") || !strcmp(lineRule,"exact")) { // Only other alternative for lineRule is "atLeast", which we ignore if (line != NULL) { // units: 1/2.4th of a percent char buf[100]; CSSPut(properties,"line-height",DFFormatDoublePct(buf,100,atoi(line)/2.4)); } } break; } } } }
static DFNode *WordTblGet(WordGetData *get, DFNode *concrete) { if (concrete->tag != WORD_TBL) return NULL;; DFNode *table = WordConverterCreateAbstract(get,HTML_TABLE,concrete); ConcreteInfo *cinfo = getConcreteInfo(get->conv,concrete); calcTotals(get,cinfo); const char *cellWidthType = cellWidthTypeForTable(concrete); int autoWidth = DFStringEquals(cellWidthType,"auto"); if ((CSSGet(cinfo->tableProperties,"width") == NULL) && autoWidth) { CSSPut(cinfo->tableProperties,"width",NULL); } else { // Determine column widths and table width if (cinfo->totalWidthPts > 0) { DFNode *colgroup = HTML_createColgroup(get->conv->html,cinfo->structure); DFAppendChild(table,colgroup); double tableWidthPct = 100.0; if (WordSectionContentWidth(get->conv->mainSection) > 0) { double contentWidthPts = WordSectionContentWidth(get->conv->mainSection)/20.0; tableWidthPct = 100.0*cinfo->totalWidthPts/contentWidthPts; if (CSSGet(cinfo->tableProperties,"width") == NULL) { char buf[100]; CSSPut(cinfo->tableProperties,"width",DFFormatDoublePct(buf,100,tableWidthPct)); } } } if (CSSGet(cinfo->tableProperties,"width") == NULL) CSSPut(cinfo->tableProperties,"width","100%"); } DFHashTable *collapsed = CSSCollapseProperties(cinfo->tableProperties); char *styleValue = CSSSerializeProperties(collapsed); DFHashTableRelease(collapsed); if (strlen(styleValue) > 0) DFSetAttribute(table,HTML_STYLE,styleValue); free(styleValue); if ((cinfo->style != NULL) && (cinfo->style->selector != NULL)) { char *className = CSSSelectorCopyClassName(cinfo->style->selector); DFSetAttribute(table,HTML_CLASS,className); free(className); } else { CSSStyle *defaultStyle = CSSSheetDefaultStyleForFamily(get->conv->styleSheet,StyleFamilyTable); if (defaultStyle != NULL) DFSetAttribute(table,HTML_CLASS,defaultStyle->className); } // Create rows and cells int row = 0; for (DFNode *tblChild = concrete->first; tblChild != NULL; tblChild = tblChild->next) { if (tblChild->tag != WORD_TR) continue; DFNode *tr = WordConverterCreateAbstract(get,HTML_TR,tblChild); DFAppendChild(table,tr); unsigned int col = 0; while (col < cinfo->structure->cols) { DFCell *cell = DFTableGetCell(cinfo->structure,row,col); if (cell == NULL) { DFNode *td = DFCreateElement(get->conv->html,HTML_TD); DFAppendChild(tr,td); col++; continue; } if (row == cell->row) { DFNode *td = WordTcGet(get,cell->element); DFAppendChild(tr,td); if (cell->colSpan != 1) DFFormatAttribute(td,HTML_COLSPAN,"%d",cell->colSpan); if (cell->rowSpan != 1) DFFormatAttribute(td,HTML_ROWSPAN,"%d",cell->rowSpan); } col += cell->colSpan; } row++; } ConcreteInfoFree(cinfo); return table; }
static void WordTblPut(WordPutData *put, DFNode *abstract, DFNode *concrete) { if ((abstract->tag != HTML_TABLE) || (concrete->tag != WORD_TBL)) return;; DFTable *abstractStructure = HTML_tableStructure(abstract); const char *inlineCSSText = DFGetAttribute(abstract,HTML_STYLE); CSSProperties *tableProperties = CSSPropertiesNewWithString(inlineCSSText); CSSProperties *cellProperties = CSSPropertiesNew(); const char *className = DFGetAttribute(abstract,HTML_CLASS); char *selector = CSSMakeSelector("table",className); WordStyle *style = WordSheetStyleForSelector(put->conv->styles,selector); CellPadding padding = getPadding(put->conv->styleSheet,style,cellProperties); DFNode *tblPr = DFChildWithTag(concrete,WORD_TBLPR); if (tblPr == NULL) tblPr = DFCreateElement(concrete->doc,WORD_TBLPR);; DFNode *tblGrid = DFChildWithTag(concrete,WORD_TBLGRID); if (tblGrid == NULL) tblGrid = DFCreateElement(concrete->doc,WORD_TBLGRID); while (concrete->first != NULL) DFRemoveNode(concrete->first); const char *oldJc = DFGetChildAttribute(tblPr,WORD_JC,WORD_VAL); WordPutTblPr(tblPr,tableProperties,NULL,put->conv->mainSection,style != NULL ? style->styleId : NULL); const char *newJc = DFGetChildAttribute(tblPr,WORD_JC,WORD_VAL); double tableWidthPct = 100; if (CSSGet(tableProperties,"width") != NULL) { CSSLength length = CSSLengthFromString(CSSGet(tableProperties,"width")); if (CSSLengthIsValid(length) && (length.units == UnitsPct)) tableWidthPct = length.value; } double contentWidthPts = WordSectionContentWidthPts(put->conv->mainSection); double totalWidthPts = (contentWidthPts+padding.leftPts+padding.rightPts)*(tableWidthPct/100.0); while (tblGrid->first != NULL) DFRemoveNode(tblGrid->first); for (unsigned int i = 0; i < abstractStructure->cols; i++) { DFNode *gridCol = DFCreateChildElement(tblGrid,WORD_GRIDCOL); double colWidthPct = DFTablePctWidthForCol(abstractStructure,i); double colWidthPts = totalWidthPts*colWidthPct/100.0; int colWidthTwips = (int)round(colWidthPts*20); DFFormatAttribute(gridCol,WORD_W,"%d",colWidthTwips); } DFAppendChild(concrete,tblPr); DFAppendChild(concrete,tblGrid); for (unsigned int row = 0; row < abstractStructure->rows; row++) { DFNode *htmlTr = DFTableGetRowElement(abstractStructure,row); DFNode *wordTr = concreteRowForAbstractRow(put,htmlTr); updateTrJc(wordTr,oldJc,newJc); DFAppendChild(concrete,wordTr); unsigned int col = 0; while (col < abstractStructure->cols) { DFCell *cell = DFTableGetCell(abstractStructure,row,col); assert(cell != NULL); DFNode *tc = WordConverterGetConcrete(put,cell->element); if ((tc == NULL) || (row != cell->row)) tc = DFCreateElement(concrete->doc,WORD_TC); DFAppendChild(wordTr,tc); if (cell->row == row) WordTcPut(put,cell->element,tc);; const char *vMerge = NULL; if (cell->rowSpan > 1) { if (row == cell->row) vMerge = "restart"; else vMerge = "continue"; } DFNode *tcPr = DFChildWithTag(tc,WORD_TCPR); if (tcPr == NULL) tcPr = DFCreateElement(concrete->doc,WORD_TCPR); // Make sure tcPr comes first DFInsertBefore(tc,tcPr,tc->first); WordPutTcPr2(tcPr,cell->colSpan,vMerge); const char *inlineCSSText = DFGetAttribute(cell->element,HTML_STYLE); CSSProperties *innerCellProperties = CSSPropertiesNewWithString(inlineCSSText); if ((row == cell->row) && (totalWidthPts > 0)) { double spannedWidthPct = 0; for (unsigned int c = col; c < col + cell->colSpan; c++) spannedWidthPct += DFTablePctWidthForCol(abstractStructure,c); char buf[100]; CSSPut(innerCellProperties,"width",DFFormatDoublePct(buf,100,spannedWidthPct)); } WordPutTcPr1(tcPr,innerCellProperties); int haveBlockLevelElement = 0; for (DFNode *tcChild = tc->first; tcChild != NULL; tcChild = tcChild->next) { if (WordBlockLevelLens.isVisible(put,tcChild)) haveBlockLevelElement = 1; } // Every cell must contain at least one block-level element if (!haveBlockLevelElement) { DFNode *p = DFCreateElement(concrete->doc,WORD_P); DFAppendChild(tc,p); } col += cell->colSpan; CSSPropertiesRelease(innerCellProperties); } } free(selector); DFTableRelease(abstractStructure); CSSPropertiesRelease(tableProperties); CSSPropertiesRelease(cellProperties); }
void CSSSetDefault(CSSProperties *properties, int value) { CSSPut(properties,"-uxwrite-default",value ? "true" : NULL); }
void CSSSetOverline(CSSProperties *properties, int value) { CSSPut(properties,"text-decoration-overline",value ? "overline" : NULL); }
void CSSSetLinethrough(CSSProperties *properties, int value) { CSSPut(properties,"text-decoration-line-through",value ? "line-through" : NULL); }
void CSSSetItalic(CSSProperties *properties, int value) { CSSPut(properties,"font-style",value ? "italic" : NULL); }
void CSSSetBold(CSSProperties *properties, int value) { CSSPut(properties,"font-weight",value ? "bold" : NULL); }