예제 #1
0
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");
        }
    }
}
예제 #2
0
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);
            }
        }
    }
}
예제 #3
0
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);
}
예제 #4
0
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;
}
예제 #5
0
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);
}
예제 #6
0
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);
}
예제 #7
0
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);
    }
}
예제 #8
0
// 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);
    }
}
예제 #9
0
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;
            }
        }
    }
}
예제 #10
0
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;
}
예제 #11
0
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);
}
예제 #12
0
void CSSSetDefault(CSSProperties *properties, int value)
{
    CSSPut(properties,"-uxwrite-default",value ? "true" : NULL);
}
예제 #13
0
void CSSSetOverline(CSSProperties *properties, int value)
{
    CSSPut(properties,"text-decoration-overline",value ? "overline" : NULL);
}
예제 #14
0
void CSSSetLinethrough(CSSProperties *properties, int value)
{
    CSSPut(properties,"text-decoration-line-through",value ? "line-through" : NULL);
}
예제 #15
0
void CSSSetItalic(CSSProperties *properties, int value)
{
    CSSPut(properties,"font-style",value ? "italic" : NULL);
}
예제 #16
0
void CSSSetBold(CSSProperties *properties, int value)
{
    CSSPut(properties,"font-weight",value ? "bold" : NULL);
}