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; }
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 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 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 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 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; }