static void updateDefault(CSSStyle *style, DFNode *element, CSSSheet *styleSheet, WordConverter *converter)
{
    StyleFamily family = WordStyleFamilyForSelector(style->selector);
    if (CSSSheetDefaultStyleForFamily(styleSheet,family) == style)
        DFSetAttribute(element,WORD_DEFAULT,"1");
    else
        DFRemoveAttribute(element,WORD_DEFAULT);
}
Beispiel #2
0
static void addMissingDefaultStyles(WordConverter *converter)
{
    if (CSSSheetDefaultStyleForFamily(converter->styleSheet,StyleFamilyParagraph) == NULL) {
        CSSStyle *style = CSSSheetLookupElement(converter->styleSheet,"p","Normal",1,0);
        CSSSheetSetDefaultStyle(converter->styleSheet,style,StyleFamilyParagraph);
    }
    if (CSSSheetDefaultStyleForFamily(converter->styleSheet,StyleFamilyCharacter) == NULL) {
        CSSStyle *style = CSSSheetLookupElement(converter->styleSheet,"span","DefaultParagraphFont",1,0);
        CSSStyleSetDisplayName(style,"Default Paragraph Font");
        CSSSheetSetDefaultStyle(converter->styleSheet,style,StyleFamilyCharacter);
    }
    if (CSSSheetDefaultStyleForFamily(converter->styleSheet,StyleFamilyTable) == NULL) {
        CSSStyle *style = CSSSheetLookupElement(converter->styleSheet,"table","Normal_Table",1,0);
        CSSStyleSetDisplayName(style,"Normal Table");
        CSSPut(CSSStyleCell(style),"padding-left","5.4pt");
        CSSPut(CSSStyleCell(style),"padding-right","5.4pt");
        CSSPut(CSSStyleCell(style),"padding-top","0pt");
        CSSPut(CSSStyleCell(style),"padding-bottom","0pt");
        CSSSheetSetDefaultStyle(converter->styleSheet,style,StyleFamilyTable);
    }
}
static CellPadding getPadding(CSSSheet *styleSheet, WordStyle *wordStyle, CSSProperties *cellProperties)
{
    const char *paddingLeftStr = NULL;
    const char *paddingRightStr = NULL;

    CSSStyle *style;
    if ((wordStyle != NULL) && (wordStyle->selector != NULL)) {
        style = CSSSheetLookupSelector(styleSheet,wordStyle->selector,0,0);
    }
    else {
        style = CSSSheetDefaultStyleForFamily(styleSheet,StyleFamilyTable);
    }

    if (style != NULL) {
        paddingLeftStr = CSSGet(CSSStyleCell(style),"padding-left");
        paddingRightStr = CSSGet(CSSStyleCell(style),"padding-right");
    }

    if (CSSGet(cellProperties,"padding-left") != NULL)
        paddingLeftStr = CSSGet(cellProperties,"padding-left");
    if (CSSGet(cellProperties,"padding-right") != NULL)
        paddingRightStr = CSSGet(cellProperties,"padding-right");;

    CellPadding padding;
    padding.leftPts = 0;
    padding.rightPts = 0;

    CSSLength paddingLeftLength = CSSLengthFromString(paddingLeftStr);
    if (CSSLengthIsValid(paddingLeftLength) && (paddingLeftLength.units == UnitsPt))
        padding.leftPts = paddingLeftLength.value;;

    CSSLength paddingRightLength = CSSLengthFromString(paddingRightStr);
    if (CSSLengthIsValid(paddingRightLength) && (paddingRightLength.units == UnitsPt))
        padding.rightPts = paddingRightLength.value;;

    return padding;
}
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 = xstrdup(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);
}
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);
}
Beispiel #6
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);
    }
}
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;
}