static void removeCaptionBookmarksRecursive(DFNode *node, int inCaption) { if (node->tag == WORD_P) { DFNode *pPr = DFChildWithTag(node,WORD_PPR); const char *styleId = DFGetChildAttribute(pPr,WORD_PSTYLE,WORD_VAL); if (DFStringEquals(styleId,"Caption")) inCaption = 1; } DFNode *next; for (DFNode *child = node->first; child != NULL; child = next) { next = child->next; removeCaptionBookmarksRecursive(child,inCaption); } if (inCaption) { switch (node->tag) { case WORD_BOOKMARKSTART: case WORD_BOOKMARKEND: case WORD_BOOKMARK: DFRemoveNodeButKeepChildren(node); break; } } }
WordTheme *WordThemeNew(WordPackage *package) { WordTheme *theme = (WordTheme *)calloc(1,sizeof(WordTheme)); DFDocument *doc = package->theme; if (doc == NULL) return theme; assert(doc->root != NULL); if (doc->root->tag != DML_MAIN_THEME) return theme; DFNode *themeElementsElem = DFChildWithTag(doc->root,DML_MAIN_THEMEELEMENTS); DFNode *fontSchemeElem = DFChildWithTag(themeElementsElem,DML_MAIN_FONTSCHEME); DFNode *majorFontElem = DFChildWithTag(fontSchemeElem,DML_MAIN_MAJORFONT); DFNode *minorFontElem = DFChildWithTag(fontSchemeElem,DML_MAIN_MINORFONT); const char *majorFont = DFGetChildAttribute(majorFontElem,DML_MAIN_LATIN,NULL_TYPEFACE); const char *minorFont = DFGetChildAttribute(minorFontElem,DML_MAIN_LATIN,NULL_TYPEFACE); theme->majorFont = (majorFont != NULL) ? strdup(majorFont) : NULL; theme->minorFont = (minorFont != NULL) ? strdup(minorFont) : NULL; return theme; }
static DFHashTable *WordSheetFindUsedConcreteNumIds(WordSheet *sheet) { DFHashTable *concreteNumIds = DFHashTableNew(NULL,NULL); // Used as a set const char **allIdents = WordSheetCopyIdents(sheet); for (int i = 0; allIdents[i]; i++) { const char *ident = allIdents[i]; WordStyle *style = WordSheetStyleForIdent(sheet,ident); DFNode *pPr = DFChildWithTag(style->element,WORD_PPR); DFNode *numPr = DFChildWithTag(pPr,WORD_NUMPR); const char *numId = DFGetChildAttribute(numPr,WORD_NUMID,WORD_VAL); if (numId != NULL) DFHashTableAdd(concreteNumIds,numId,""); } free(allIdents); return concreteNumIds; }
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); }
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 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); }