static void ConcreteInfoFree(ConcreteInfo *info) { DFTableRelease(info->structure); CSSPropertiesRelease(info->tableProperties); CSSPropertiesRelease(info->cellProperties); free(info); }
static ImageInfo *getImageInfoObject(DFNode *concrete) { DFNode *shape = DFChildWithTag(concrete,VML_SHAPE); DFNode *imageData = DFChildWithTag(shape,VML_IMAGEDATA); const char *cssText = DFGetAttribute(shape,NULL_STYLE); const char *rId = DFGetAttribute(imageData,OREL_ID); if ((shape == NULL) || (imageData == NULL) || (cssText == NULL) || (rId == NULL)) return NULL; CSSProperties *imgProperties = CSSPropertiesNewWithString(cssText); CSSLength width = CSSLengthFromString(CSSGet(imgProperties,"width")); CSSLength height = CSSLengthFromString(CSSGet(imgProperties,"height")); CSSPropertiesRelease(imgProperties); if (!CSSLengthIsValid(width) || !CSSLengthIsAbsolute(width)) return NULL; if (!CSSLengthIsValid(height) || !CSSLengthIsAbsolute(height)) return NULL; double widthPts = convertBetweenUnits(width.value,width.units,UnitsPt); double heightPts = convertBetweenUnits(height.value,height.units,UnitsPt); ImageInfo *info = ImageInfoNew(rId,widthPts,heightPts); DFNode *oleObject = DFChildWithTag(concrete,MSOFFICE_OLEOBJECT); info->progId = DFGetAttribute(oleObject,NULL_PROGID); return info; }
static void WordRunPut(WordPutData *put, DFNode *abstract, DFNode *concrete) { if ((abstract->tag != HTML_SPAN) || (concrete->tag != WORD_R)) return; if (WordRunPutNote(put,abstract,concrete)) return; WordContainerPut(put,&WordRunContentLens,abstract,concrete); DFNode *rPr = DFChildWithTag(concrete,WORD_RPR); if (rPr == NULL) rPr = DFCreateElement(put->contentDoc,WORD_RPR); DFInsertBefore(concrete,rPr,concrete->first); // Ensure first, in case [super put] moved it char *selector = CSSMakeNodeSelector(abstract); const char *styleId = WordSheetStyleIdForSelector(put->conv->styles,selector); const char *inlineCSSText = DFGetAttribute(abstract,HTML_STYLE); CSSProperties *properties = CSSPropertiesNewWithString(inlineCSSText); WordPutRPr(rPr,properties,styleId,put->conv->theme); CSSPropertiesRelease(properties); if (rPr->first == NULL) DFRemoveNode(rPr); free(selector); }
static void WordPutNumPr(DFNode *concrete, CSSProperties *newp) { DFNode *children[PREDEFINED_TAG_COUNT]; childrenToArray(concrete,children); CSSProperties *oldp = CSSPropertiesNew(); WordGetNumPr(concrete,oldp); if (!DFStringEquals(CSSGet(oldp,"-word-numId"),CSSGet(newp,"-word-numId")) || !DFStringEquals(CSSGet(oldp,"-word-ilvl"),CSSGet(newp,"-word-ilvl"))) { if (CSSGet(newp,"-word-numId") != NULL) { children[WORD_NUMID] = DFCreateElement(concrete->doc,WORD_NUMID); DFSetAttribute(children[WORD_NUMID],WORD_VAL,CSSGet(newp,"-word-numId")); if (CSSGet(newp,"-word-ilvl") != NULL) { children[WORD_ILVL] = DFCreateElement(concrete->doc,WORD_ILVL); DFSetAttribute(children[WORD_ILVL],WORD_VAL,CSSGet(newp,"-word-ilvl")); } else { children[WORD_ILVL] = NULL; } } else { children[WORD_NUMID] = NULL; children[WORD_ILVL] = NULL; } } replaceChildrenFromArray(concrete,children,WordNumPr_Children); CSSPropertiesRelease(oldp); }
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 ListDimensions listIndent(WordConverter *conv, const char *numId, const char *ilvl) { CSSProperties *properties = CSSPropertiesNew(); listProperties(conv,numId,ilvl,properties); ListDimensions result = cssPropertiesIndent(properties); CSSPropertiesRelease(properties); return result; }
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); } }
static ListDimensions paragraphIndent(DFNode *p) { if (p->tag < MIN_ELEMENT_TAG) return ListDimensionsZero;; const char *cssText = DFGetAttribute(p,HTML_STYLE); CSSProperties *properties = CSSPropertiesNewWithString(cssText); ListDimensions result = cssPropertiesIndent(properties); CSSPropertiesRelease(properties); return result; }
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); }
static DFNode *imageWithFilename(WordGetData *get, const char *filename, double widthPts, DFNode *concrete) { const char *concretePath = get->conv->concretePath; const char *abstractPath = get->conv->abstractPath; char *abstractImagesPath = DFAppendPathComponent(abstractPath,"images"); char *lastComponent = DFPathBaseName(filename); char *srcImagePath = DFAppendPathComponent(concretePath,filename); char *dstImagePath = DFAppendPathComponent(abstractImagesPath,lastComponent); if (DFFileExists(dstImagePath)) DFDeleteFile(dstImagePath,NULL); DFError *error = NULL; DFNode *imageNode = NULL; if (!DFFileExists(abstractImagesPath) && !DFCreateDirectory(abstractImagesPath,1,&error)) { WordConverterWarning(get->conv,"Create %s: %s",abstractImagesPath,DFErrorMessage(&error)); DFErrorRelease(error); imageNode = createAbstractPlaceholder(get,"[Error reading image]",concrete); } else if (!DFCopyFile(srcImagePath,dstImagePath,&error)) { WordConverterWarning(get->conv,"Copy %s to %s: %s",srcImagePath,dstImagePath,DFErrorMessage(&error)); DFErrorRelease(error); imageNode = createAbstractPlaceholder(get,"[Error reading image]",concrete); } else { imageNode = WordConverterCreateAbstract(get,HTML_IMG,concrete); DFFormatAttribute(imageNode,HTML_SRC,"images/%s",lastComponent); double contentWidthPts = WordSectionContentWidthPts(get->conv->mainSection); if (contentWidthPts > 0) { double widthPct = widthPts/contentWidthPts*100.0; CSSProperties *properties = CSSPropertiesNew(); char buf[100]; CSSPut(properties,"width",DFFormatDoublePct(buf,100,widthPct)); char *propertiesText = CSSPropertiesCopyDescription(properties); DFSetAttribute(imageNode,HTML_STYLE,propertiesText); free(propertiesText); CSSPropertiesRelease(properties); } } free(abstractImagesPath); free(lastComponent); free(srcImagePath); free(dstImagePath); return imageNode; }
// FIXME: make private to this file double listDesiredIndent(WordConverter *conv, const char *numId, const char *ilvl) { CSSProperties *properties = CSSPropertiesNew(); listProperties(conv,numId,ilvl,properties); double result = 0.0; if (CSSGet(properties,"margin-left") != NULL) { CSSLength length = CSSLengthFromString(CSSGet(properties,"margin-left")); if (CSSLengthIsValid(length) && (length.units == UnitsPct)) result += length.value; } CSSPropertiesRelease(properties); 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 void Word_flattenList(WordConverter *word, DFNode *list, int ilvl, DFNode *parent, DFNode *nextSibling) { const char *type = (list->tag == HTML_OL) ? "decimal" : "disc"; const char *cssText = DFGetAttribute(list,HTML_STYLE); CSSProperties *properties = CSSPropertiesNewWithString(cssText); if (CSSGet(properties,"list-style-type") != NULL) type = CSSGet(properties,"list-style-type"); DFFormatAttribute(list,CONV_LISTNUM,"%u",list->seqNo); DFFormatAttribute(list,CONV_ILVL,"%d",ilvl); DFSetAttribute(list,CONV_LISTTYPE,type); for (DFNode *li = list->first; li != NULL; li = li->next) { DFNode *first = li->first; DFNode *liChildNext; for (DFNode *liChild = li->first; liChild != NULL; liChild = liChildNext) { liChildNext = liChild->next; if ((liChild->tag == HTML_UL) || (liChild->tag == HTML_OL)) { Word_flattenList(word,liChild,ilvl+1,parent,nextSibling); } else { if (liChild->tag == HTML_P) { DFFormatAttribute(liChild,CONV_LISTNUM,"%u",list->seqNo); DFFormatAttribute(liChild,CONV_ILVL,"%d",ilvl); DFSetAttribute(liChild,CONV_LISTTYPE,type); if (liChild == first) DFSetAttribute(liChild,CONV_LISTITEM,"true"); } DFInsertBefore(parent,liChild,nextSibling); Word_preProcessLists(word,liChild,ilvl); } } } DFRemoveNode(list); CSSPropertiesRelease(properties); }
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; }
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); }
static void WordPutSectPr(DFNode *concrete, CSSSheet *styleSheet, WordSection *section) { // Note: The sectPr element can potentially contain one or more headerReference or // footerReference elements at the start, before the elements in WordSectPr_Children. // So the straight childrenToArray/replaceChildrenFromArray method used elsewhere doesn't // work here - we need to make sure these other elements are maintained DFNode *children[PREDEFINED_TAG_COUNT]; childrenToArray(concrete,children); CSSProperties *oldBody = CSSPropertiesNew(); CSSProperties *oldPage = CSSPropertiesNew(); WordGetSectPr(concrete,oldBody,oldPage,section); CSSProperties *newBody = CSSSheetBodyProperties(styleSheet); CSSProperties *newPage = CSSSheetPageProperties(styleSheet); // Page size if (children[WORD_PGSZ] == NULL) children[WORD_PGSZ] = DFCreateElement(concrete->doc,WORD_PGSZ); int updatePageSize = 0; const char *widthStr = DFGetAttribute(children[WORD_PGSZ],WORD_W); const char *heightStr = DFGetAttribute(children[WORD_PGSZ],WORD_H); int widthTwips; int heightTwips; if ((widthStr == NULL) || (atoi(widthStr) <= 0) || (heightStr == NULL) || (atoi(heightStr) <= 0)) { // Invalid or missing page size: Set to A4 portrait widthTwips = A4_WIDTH_TWIPS; heightTwips = A4_HEIGHT_TWIPS; updatePageSize = 1; } else { widthTwips = atoi(widthStr); heightTwips = atoi(heightStr); } if (!DFStringEquals(CSSGet(oldPage,"size"),CSSGet(newPage,"size"))) { const char *newSize = CSSGet(newPage,"size"); if (DFStringEqualsCI(newSize,"A4 portrait")) { widthTwips = A4_WIDTH_TWIPS; heightTwips = A4_HEIGHT_TWIPS; } else if (DFStringEqualsCI(newSize,"A4 landscape")) { widthTwips = A4_HEIGHT_TWIPS; heightTwips = A4_WIDTH_TWIPS; } else if (DFStringEqualsCI(newSize,"letter portrait")) { widthTwips = LETTER_WIDTH_TWIPS; heightTwips = LETTER_HEIGHT_TWIPS; } else if (DFStringEqualsCI(newSize,"letter landscape")) { widthTwips = LETTER_HEIGHT_TWIPS; heightTwips = LETTER_WIDTH_TWIPS; } else { widthTwips = A4_WIDTH_TWIPS; heightTwips = A4_HEIGHT_TWIPS; } updatePageSize = 1; } if (updatePageSize) { DFFormatAttribute(children[WORD_PGSZ],WORD_W,"%d",widthTwips); DFFormatAttribute(children[WORD_PGSZ],WORD_H,"%d",heightTwips); if (widthTwips > heightTwips) DFSetAttribute(children[WORD_PGSZ],WORD_ORIENT,"landscape"); else DFRemoveAttribute(children[WORD_PGSZ],WORD_ORIENT); } if (children[WORD_PGMAR] == NULL) children[WORD_PGMAR] = DFCreateElement(concrete->doc,WORD_PGMAR); // Page margins if (!DFStringEquals(CSSGet(oldBody,"margin-left"),CSSGet(newBody,"margin-left")) || updatePageSize) updateTwipsFromLength(children[WORD_PGMAR],WORD_LEFT,CSSGet(newBody,"margin-left"),widthTwips); if (!DFStringEquals(CSSGet(oldBody,"margin-right"),CSSGet(newBody,"margin-right")) || updatePageSize) updateTwipsFromLength(children[WORD_PGMAR],WORD_RIGHT,CSSGet(newBody,"margin-right"),widthTwips); if (!DFStringEquals(CSSGet(oldBody,"margin-top"),CSSGet(newBody,"margin-top")) || updatePageSize) updateTwipsFromLength(children[WORD_PGMAR],WORD_TOP,CSSGet(newBody,"margin-top"),widthTwips); if (!DFStringEquals(CSSGet(oldBody,"margin-bottom"),CSSGet(newBody,"margin-bottom")) || updatePageSize) updateTwipsFromLength(children[WORD_PGMAR],WORD_BOTTOM,CSSGet(newBody,"margin-bottom"),widthTwips); if (children[WORD_PGMAR]->attrsCount == 0) children[WORD_PGMAR] = NULL;; DFArray *extra = DFArrayNew(NULL,NULL); for (DFNode *child = concrete->first; child != NULL; child = child->next) { switch (child->tag) { case WORD_HEADERREFERENCE: case WORD_FOOTERREFERENCE: DFArrayAppend(extra,child); break; } } replaceChildrenFromArray(concrete,children,WordSectPr_Children); for (long i = (long)(DFArrayCount(extra)-1); i >= 0; i--) { DFNode *child = DFArrayItemAt(extra,i); DFInsertBefore(concrete,child,concrete->first); } DFArrayRelease(extra); CSSPropertiesRelease(oldBody); CSSPropertiesRelease(oldPage); }
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 WordPutPPr(DFNode *pPr, CSSProperties *properties, const char *styleId, WordSection *section, int outlineLvl) { assert(pPr->tag == WORD_PPR); // The child elements of pPr have to be in a specific order. So we build up a structure based // on the existing elements (updated as appropriate from newProperties), and then rebuild // from that CSSProperties *oldp = CSSPropertiesNew(); CSSProperties *newp = properties; const char *oldStyleId = NULL; const char *newStyleId = styleId; WordGetPPr(pPr,oldp,&oldStyleId,section); { DFNode *children[PREDEFINED_TAG_COUNT]; childrenToArray(pPr,children); int existingOutlineLvl = -1; if (children[WORD_OUTLINELVL] != NULL) { const char *value = DFGetAttribute(children[WORD_OUTLINELVL],WORD_VAL); if (value != NULL) existingOutlineLvl = atoi(value); } // Style name if (!DFStringEquals(oldStyleId,newStyleId)) { if (newStyleId != NULL) { children[WORD_PSTYLE] = DFCreateElement(pPr->doc,WORD_PSTYLE); DFSetAttribute(children[WORD_PSTYLE],WORD_VAL,newStyleId); } else { children[WORD_PSTYLE] = NULL; } } // Paragraph border (pBdr) if (children[WORD_PBDR] == NULL) children[WORD_PBDR] = DFCreateElement(pPr->doc,WORD_PBDR); WordPutPBdr(children[WORD_PBDR],oldp,newp); if (children[WORD_PBDR]->first == NULL) children[WORD_PBDR] = NULL; // Numbering and outline level // Don't change these properties for styles with outline level >= 6, as these can't be // represented with the standard HTML heading elements, which only go from h1 - h6 // (outline levels 0 - 5) if (existingOutlineLvl < 6) { if (children[WORD_NUMPR] == NULL) children[WORD_NUMPR] = DFCreateElement(pPr->doc,WORD_NUMPR); WordPutNumPr(children[WORD_NUMPR],newp); if (children[WORD_NUMPR]->first == NULL) children[WORD_NUMPR] = NULL; if ((outlineLvl >= 0) && (outlineLvl <= 5)) { if (children[WORD_OUTLINELVL] == NULL) children[WORD_OUTLINELVL] = DFCreateElement(pPr->doc,WORD_OUTLINELVL); DFFormatAttribute(children[WORD_OUTLINELVL],WORD_VAL,"%d",outlineLvl); } else { children[WORD_OUTLINELVL] = NULL; } } // background-color char *oldBackgroundColor = CSSHexColor(CSSGet(oldp,"background-color"),0); char *newBackgroundColor = CSSHexColor(CSSGet(newp,"background-color"),0); if (!DFStringEquals(oldBackgroundColor,newBackgroundColor)) WordPutShd(pPr->doc,&children[WORD_SHD],newBackgroundColor); free(oldBackgroundColor); free(newBackgroundColor); // text-align if (!DFStringEquals(CSSGet(oldp,"text-align"),CSSGet(newp,"text-align"))) { const char *newTextAlign = CSSGet(newp,"text-align"); if (newTextAlign != NULL) { const char *val = NULL; if (!strcmp(newTextAlign,"left")) val = "left"; else if (!strcmp(newTextAlign,"right")) val = "right"; else if (!strcmp(newTextAlign,"center")) val = "center"; else if (!strcmp(newTextAlign,"justify")) val = "both"; if (val != NULL) { children[WORD_JC] = DFCreateElement(pPr->doc,WORD_JC); DFSetAttribute(children[WORD_JC],WORD_VAL,val); } } else { children[WORD_JC] = NULL; } } if ((section != NULL) && (WordSectionContentWidth(section) >= 0)) { const char *oldMarginLeft = CSSGet(oldp,"margin-left"); const char *oldMarginRight = CSSGet(oldp,"margin-right"); const char *oldTextIndent = CSSGet(oldp,"text-indent"); const char *newMarginLeft = CSSGet(newp,"margin-left"); const char *newMarginRight = CSSGet(newp,"margin-right"); const char *newTextIndent = CSSGet(newp,"text-indent"); // Special case of the List_Paragraph style, which is used by Word to ensure lists are indented. We // don't set this property for HTML, because it automatically indents lists. However we need to ensure // that it remains unchanged when updating the word document const char *newWordMarginLeft = CSSGet(newp,"-word-margin-left"); if (newMarginLeft == NULL) newMarginLeft = newWordMarginLeft; if ((newMarginLeft == NULL) && (newMarginRight == NULL) && (newTextIndent == NULL)) { children[WORD_IND] = NULL; } else { if (children[WORD_IND] == NULL) children[WORD_IND] = DFCreateElement(pPr->doc,WORD_IND); if (!DFStringEquals(oldMarginLeft,newMarginLeft)) { if (newMarginLeft != NULL) updateTwipsFromLength(children[WORD_IND],WORD_LEFT,newMarginLeft,WordSectionContentWidth(section)); else DFRemoveAttribute(children[WORD_IND],WORD_LEFT); DFRemoveAttribute(children[WORD_IND],WORD_START); } if (!DFStringEquals(oldMarginRight,newMarginRight)) { if (newMarginRight != NULL) updateTwipsFromLength(children[WORD_IND],WORD_RIGHT,newMarginRight,WordSectionContentWidth(section)); else DFRemoveAttribute(children[WORD_IND],WORD_RIGHT); DFRemoveAttribute(children[WORD_IND],WORD_END); } if (!DFStringEquals(oldTextIndent,newTextIndent)) { if (newTextIndent != NULL) { CSSLength length = CSSLengthFromString(newTextIndent); if (CSSLengthIsValid(length)) { double pts = CSSLengthToPts(length,WordSectionContentWidthPts(section)); int twips = (int)round(pts*20); if (twips >= 0) { DFFormatAttribute(children[WORD_IND],WORD_FIRSTLINE,"%d",twips); DFRemoveAttribute(children[WORD_IND],WORD_HANGING); } else { DFFormatAttribute(children[WORD_IND],WORD_HANGING,"%d",-twips); DFRemoveAttribute(children[WORD_IND],WORD_FIRSTLINE); } } } else { DFRemoveAttribute(children[WORD_IND],WORD_FIRSTLINE); DFRemoveAttribute(children[WORD_IND],WORD_HANGING); } } } } if (!DFStringEquals(CSSGet(oldp,"margin-top"),CSSGet(newp,"margin-top")) || !DFStringEquals(CSSGet(oldp,"margin-bottom"),CSSGet(newp,"margin-bottom")) || !DFStringEquals(CSSGet(oldp,"line-height"),CSSGet(newp,"line-height"))) { if ((CSSGet(newp,"margin-top") == NULL) && (CSSGet(newp,"margin-bottom") == NULL) && (CSSGet(newp,"line-height") == NULL)) { children[WORD_SPACING] = NULL; } else { children[WORD_SPACING] = DFCreateElement(pPr->doc,WORD_SPACING); if (DFStringEquals(CSSGet(newp,"margin-top"),"-word-auto")) { DFSetAttribute(children[WORD_SPACING],WORD_BEFORE,"100"); DFSetAttribute(children[WORD_SPACING],WORD_BEFOREAUTOSPACING,"1"); } else { char *before = twipsFromCSS(CSSGet(newp,"margin-top"),WordSectionContentWidth(section)); DFSetAttribute(children[WORD_SPACING],WORD_BEFORE,before); DFSetAttribute(children[WORD_SPACING],WORD_BEFOREAUTOSPACING,NULL); free(before); } if (DFStringEquals(CSSGet(newp,"margin-bottom"),"-word-auto")) { DFSetAttribute(children[WORD_SPACING],WORD_AFTER,"100"); DFSetAttribute(children[WORD_SPACING],WORD_AFTERAUTOSPACING,"1"); } else { char *after = twipsFromCSS(CSSGet(newp,"margin-bottom"),WordSectionContentWidth(section)); DFSetAttribute(children[WORD_SPACING],WORD_AFTER,after); DFSetAttribute(children[WORD_SPACING],WORD_AFTERAUTOSPACING,NULL); free(after); } CSSLength lineHeight = CSSLengthFromString(CSSGet(newp,"line-height")); if (CSSLengthIsValid(lineHeight) && (lineHeight.units == UnitsPct)) { int value = (int)round(lineHeight.value*2.4); DFFormatAttribute(children[WORD_SPACING],WORD_LINE,"%d",value); } if (children[WORD_SPACING]->attrsCount == 0) children[WORD_SPACING] = NULL; } } replaceChildrenFromArray(pPr,children,WordPPR_Children); } CSSPropertiesRelease(oldp); }
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; }