Beispiel #1
0
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;
}