示例#1
0
char *CSSSheetCopyText(CSSSheet *sheet)
{
    DFBuffer *result = DFBufferNew();
    const char **allSelectors = CSSSheetCopySelectors(sheet);
    DFSortStringsCaseInsensitive(allSelectors);
    for (int selIndex = 0; allSelectors[selIndex]; selIndex++) {
        CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[selIndex],0,0);
        DFBufferFormat(result,"%s\n",style->selector);

        const char **sortedSuffixes = CSSStyleCopySuffixes(style);
        DFSortStringsCaseInsensitive(sortedSuffixes);
        for (int suffixIndex = 0; sortedSuffixes[suffixIndex]; suffixIndex++) {
            const char *suffix = sortedSuffixes[suffixIndex];
            char *quotedSuffix = DFQuote(suffix);
            DFBufferFormat(result,"    %s\n",quotedSuffix);
            free(quotedSuffix);
            CSSProperties *properties = CSSStyleRuleForSuffix(style,suffix);

            const char **sortedNames = CSSPropertiesCopyNames(properties);
            DFSortStringsCaseInsensitive(sortedNames);
            for (int nameIndex = 0; sortedNames[nameIndex]; nameIndex++) {
                const char *name = sortedNames[nameIndex];
                const char *value = CSSGet(properties,name);
                DFBufferFormat(result,"        %s = %s\n",name,value);
            }
            free(sortedNames);
        }
        free(sortedSuffixes);
    }
    free(allSelectors);
    char *str = xstrdup(result->data);
    DFBufferRelease(result);
    return str;
}
示例#2
0
CSSStyle *CSSSheetLookupElement(CSSSheet *sheet, const char *elementName, const char *className, int add, int latent)
{
    char *selector = CSSMakeSelector(elementName,className);
    CSSStyle *result = CSSSheetLookupSelector(sheet,selector,add,latent);
    free(selector);
    return result;
}
示例#3
0
CSSStyle *CSSSheetGetStyleParent(CSSSheet *sheet, CSSStyle *style)
{
    char *parentSelector = CSSStyleCopyParent(style);
    if (parentSelector == NULL)
        return NULL;;

    CSSStyle *parent = CSSSheetLookupSelector(sheet,parentSelector,0,0);
    free(parentSelector);
    return parent;
}
CSSSheet *WordParseStyles(WordConverter *converter)
{
    CSSSheet *styleSheet = CSSSheetNew();
    CSSStyle *bodyStyle = CSSSheetLookupElement(styleSheet,"body",NULL,1,0);
    CSSPut(CSSStyleRule(bodyStyle),"counter-reset","h1 h2 h3 h4 h5 h6 figure table");
    parseBody(converter,styleSheet);
    if (converter->package->styles == NULL)
        return styleSheet;;
    DFNode *root = converter->package->styles->root;
    if (root == NULL)
        return styleSheet;
    if (root->tag != WORD_STYLES)
        return styleSheet;;

    WordSheet *sheet = converter->styles;

    const char **allIdents = WordSheetCopyIdents(sheet);
    for (int i = 0; allIdents[i]; i++) {
        WordStyle *wordStyle = WordSheetStyleForIdent(sheet,allIdents[i]);
        if ((wordStyle->selector == NULL) || WordStyleIsProtected(wordStyle))
            continue;
        CSSStyle *style = CSSSheetLookupSelector(styleSheet,wordStyle->selector,1,0);
        styleParse(wordStyle,converter,style);
        const char *defaultVal = DFGetAttribute(wordStyle->element,WORD_DEFAULT);
        if ((defaultVal != NULL) && Word_parseOnOff(defaultVal)) {
            StyleFamily family = WordStyleFamilyForSelector(style->selector);
            CSSSheetSetDefaultStyle(styleSheet,style,family);
            CSSSetDefault(CSSStyleRule(style),1);

            if (family == StyleFamilyParagraph)
                fixParagraphSpacing(style,wordStyle->element);
        }
    }
    free(allIdents);

    DFNode *docDefaults = DFChildWithTag(root,WORD_DOCDEFAULTS);
    DFNode *pPrDefault = DFChildWithTag(docDefaults,WORD_PPRDEFAULT);
    DFNode *pPr = DFChildWithTag(pPrDefault,WORD_PPR);
    if (pPr != NULL) {
        CSSStyle *body = CSSSheetLookupElement(styleSheet,"body",NULL,1,0);
        const char *styleId = NULL;
        WordGetPPr(pPr,CSSStyleRule(body),&styleId,converter->mainSection);
    }

    // Special case for figure style: set left and right margin to auto, if not already set
    CSSStyle *figure = CSSSheetLookupElement(styleSheet,"figure",NULL,0,0);
    if (figure != NULL) {
        if (CSSGet(CSSStyleRule(figure),"margin-left") == NULL)
            CSSPut(CSSStyleRule(figure),"margin-left","auto");
        if (CSSGet(CSSStyleRule(figure),"margin-right") == NULL)
            CSSPut(CSSStyleRule(figure),"margin-right","auto");
    }

    return styleSheet;
}
示例#5
0
static void updateFromRawCSSRules(CSSSheet *sheet, DFHashTable *rules)
{
    // FIXME: Handle class names containing escape sequences
    DFHashTableRelease(sheet->_styles);
    sheet->_styles = DFHashTableNew((DFCopyFunction)CSSStyleRetain,(DFFreeFunction)CSSStyleRelease);

    const char **sortedSelectors = DFHashTableCopyKeys(rules);
    DFSortStringsCaseInsensitive(sortedSelectors);
    for (int selIndex = 0; sortedSelectors[selIndex]; selIndex++) {
        const char *constSelector = sortedSelectors[selIndex];

        // Treat any selectors specifying the class name only as paragraph styles
        char *selector;
        if (!strncmp(constSelector,".",1))
            selector = DFFormatString("p%s",constSelector); // FIXME: Not covered by tests
        else
            selector = xstrdup(constSelector);

        DFHashTable *raw = DFHashTableLookup(rules,constSelector);
        char *baseId = NULL;
        char *suffix = NULL;
        CSSParseSelector(selector,&baseId,&suffix);

        CSSStyle *style = CSSSheetLookupSelector(sheet,baseId,0,0);
        if (style == NULL) {
            style = CSSStyleNew(baseId);
            CSSSheetAddStyle(sheet,style);
            CSSStyleRelease(style);
        }

        CSSProperties *properties = CSSStyleRuleForSuffix(style,suffix);
        CSSProperties *expanded = CSSPropertiesNewWithRaw(raw);

        const char **allNames = CSSPropertiesCopyNames(expanded);
        for (int nameIndex = 0; allNames[nameIndex]; nameIndex++) {
            const char *name = allNames[nameIndex];
            CSSPut(properties,name,CSSGet(expanded,name));
        }
        free(allNames);

        if (!strcmp(suffix,"")) {
            const char *defaultVal = CSSGet(properties,"-uxwrite-default");
            if ((defaultVal != NULL) && DFStringEqualsCI(defaultVal,"true"))
                CSSSheetSetDefaultStyle(sheet,style,StyleFamilyFromHTMLTag(style->tag));
        }
        CSSPropertiesRelease(expanded);
        free(baseId);
        free(suffix);
        free(selector);
    }
    free(sortedSelectors);
    removeRedundantProperties(sheet);
}
示例#6
0
static DFHashTable *getStylesByHeadingLevel(CSSSheet *sheet)
{
    DFHashTable *stylesByHeadingLevel = DFHashTableNew(NULL,NULL);
    const char **allSelectors = CSSSheetCopySelectors(sheet);
    for (int i = 0; allSelectors[i]; i++) {
        CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[i],0,0);
        if (style->headingLevel > 0) {
            int headingLevel = style->headingLevel;
            StyleList *item = (StyleList *)xcalloc(1,sizeof(StyleList));
            item->style = CSSStyleRetain(style);
            item->next = DFHashTableLookupInt(stylesByHeadingLevel,headingLevel);
            DFHashTableAddInt(stylesByHeadingLevel,headingLevel,item);
        }
    }
    free(allSelectors);
    return stylesByHeadingLevel;
}
示例#7
0
static void removeRedundantProperties(CSSSheet *sheet)
{
    // Remove any properties set on a style that have the same value as the corresponding property
    // on the parent style. This is necessary because CSS doesn't support style inheritance (in
    // the sense of Word & ODF's styles), so when we save out a HTML file, every style has all
    // properties of its ancestors. After reading in a HTML file for the purposes of updating the
    // original Word or ODF style, we don't want these extra property settings to remain, so that
    // we can avoid adding spurious extra redundant property settings to the original file.

    breakCycles(sheet);
    const char **sortedSelectors = reverseTopologicalSortedSelectors(sheet);

    for (size_t selIndex = 0; sortedSelectors[selIndex]; selIndex++) {
        const char *selector = sortedSelectors[selIndex];
        CSSStyle *child = CSSSheetLookupSelector(sheet,selector,0,0);
        CSSStyle *parent = CSSSheetGetStyleParent(sheet,child);
        if (parent == NULL)
            continue;
        const char **allSuffixes = CSSStyleCopySuffixes(child);
        for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) {
            const char *suffix = allSuffixes[suffixIndex];
            int isCell = !strcmp(suffix," > * > tr > td");
            CSSProperties *childProperties = CSSStyleRuleForSuffix(child,suffix);
            CSSProperties *parentProperties = CSSStyleRuleForSuffix(parent,suffix);

            const char **allNames = CSSPropertiesCopyNames(childProperties);
            for (int nameIndex = 0; allNames[nameIndex]; nameIndex++) {
                const char *name = allNames[nameIndex];

                // In docx's styles.xml, the tblCellMar values in table styles are not inherited
                // (this seems like a bug in word, as isn't inconsistent with all other properties)
                // So keep these ones.
                if (isCell && DFStringHasPrefix(name,"padding-"))
                    continue;

                const char *childVal = CSSGet(childProperties,name);
                const char *parentVal = CSSGet(parentProperties,name);
                if ((childVal != NULL) && (parentVal != NULL) && DFStringEquals(childVal,parentVal))
                    CSSPut(childProperties,name,NULL);
            }
            free(allNames);
        }
        free(allSuffixes);
    }
    free(sortedSelectors);
}
示例#8
0
DFHashTable *CSSSheetRules(CSSSheet *sheet)
{
    DFHashTable *result = DFHashTableNew((DFCopyFunction)DFHashTableRetain,(DFFreeFunction)DFHashTableRelease);
    const char **allSelectors = CSSSheetCopySelectors(sheet);
    for (int selIndex = 0; allSelectors[selIndex]; selIndex++) {
        const char *selector = allSelectors[selIndex];
        CSSStyle *origStyle = CSSSheetLookupSelector(sheet,selector,0,0);
        if ((origStyle == NULL) || origStyle->latent)
            continue;

        char *elementName = CSSSelectorCopyElementName(selector);
        char *className = CSSSelectorCopyClassName(selector);

        char *baseSelector;
        if (className != NULL) {
            char *escapedClassName = CSSEscapeIdent(className);
            baseSelector = DFFormatString("%s.%s",elementName,escapedClassName);
            free(escapedClassName);
        }
        else {
            baseSelector = xstrdup(elementName);
        }

        CSSStyle *flattenedStyle = CSSSheetFlattenedStyle(sheet,origStyle);
        const char **allSuffixes = CSSStyleCopySuffixes(flattenedStyle);
        for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) {
            const char *suffix = allSuffixes[suffixIndex];
            char *fullSelector = DFFormatString("%s%s",baseSelector,suffix);
            CSSProperties *properties = CSSStyleRuleForSuffix(flattenedStyle,suffix);
            DFHashTable *collapsed = CSSCollapseProperties(properties);
            if (!((DFHashTableCount(collapsed) == 0) && ((strlen(suffix) > 0) || CSSIsBuiltinSelector(selector))))
                DFHashTableAdd(result,fullSelector,collapsed);
            free(fullSelector);
            DFHashTableRelease(collapsed);
        }
        free(allSuffixes);

        CSSStyleRelease(flattenedStyle);
        free(elementName);
        free(className);
        free(baseSelector);
    }
    free(allSelectors);
    return result;
}
示例#9
0
CSSStyle *CSSSheetParentOfStyle(CSSSheet *sheet, CSSStyle *style)
{
    // FIXME: Not covered by tests
    if (style == NULL)
        return NULL;;

    CSSStyle *parent = CSSSheetGetStyleParent(sheet,style);
    if (parent != NULL)
        return parent;

    if (CSSSelectorHasClassName(style->selector)) {
        char *elementName = CSSSelectorCopyElementName(style->selector);
        parent = CSSSheetLookupSelector(sheet,elementName,0,0);
        free(elementName);
        if (parent != NULL)
            return parent;
    }

    return NULL;
}
示例#10
0
int CSSSheetHeadingNumbering(CSSSheet *sheet)
{
    const char **allSelectors = CSSSheetCopySelectors(sheet);
    for (int i = 0; allSelectors[i]; i++) {
        CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[i],0,0);
        if (style->headingLevel == 0)
            continue;
        if (CSSGet(CSSStyleBefore(style),"content") == NULL)
            continue;
        DFArray *contentParts = CSSParseContent(CSSGet(CSSStyleBefore(style),"content"));
        for (size_t partIndex = 0; partIndex < DFArrayCount(contentParts); partIndex++) {
            ContentPart *part = DFArrayItemAt(contentParts,partIndex);
            if (part->type == ContentPartCounter) {
                free(allSelectors);
                DFArrayRelease(contentParts);
                return 1;
            }
        }
        DFArrayRelease(contentParts);
    }
    free(allSelectors);
    return 0;
}
示例#11
0
static void breakCycles(CSSSheet *sheet)
{
    // FIXME: Not covered by tests
    const char **allSelectors = CSSSheetCopySelectors(sheet);
    for (int i = 0; allSelectors[i]; i++) {
        const char *selector = allSelectors[i];
        CSSStyle *style = CSSSheetLookupSelector(sheet,selector,0,0);
        DFHashTable *visited = DFHashTableNew((DFCopyFunction)xstrdup,(DFFreeFunction)free);
        int depth = 0;

        while (style != NULL) {
            if (DFHashTableLookup(visited,style->selector) != NULL) {
                CSSStyleSetParent(style,NULL);
                break;
            }
            DFHashTableAdd(visited,style->selector,"");
            style = CSSSheetGetStyleParent(sheet,style);
            depth++;
        }
        DFHashTableRelease(visited);
    }
    free(allSelectors);
}
示例#12
0
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;
}
示例#13
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 = 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);
}
示例#14
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);
    }
}