static void WordFieldPut(WordPutData *put, DFNode *abstract, DFNode *concrete)
{
    switch (abstract->tag) {
        case HTML_SPAN: {
            const char *className = DFGetAttribute(abstract,HTML_CLASS);
            if (!DFStringEquals(className,DFFieldClass))
                return;
            char *text = DFNodeTextToString(abstract);
            DFSetAttribute(concrete,WORD_INSTR,text);
            free(text);
            break;
        }
        case HTML_A: {
            const char *href = DFGetAttribute(abstract,HTML_HREF);
            if ((href == NULL) || (href[0] != '#'))
                return;;

            const char *targetId = &href[1];
            const char *className = DFGetAttribute(abstract,HTML_CLASS);
            if (className == NULL)
                className = "";;
            const char *bookmarkName = bookmarkNameForHtmlId(put->conv,targetId,className);
            if (bookmarkName == NULL)
                return;;

            DFNode *htmlElem = DFElementForIdAttr(put->conv->html,targetId);
            if ((htmlElem != NULL) && ((htmlElem->tag == HTML_TABLE) || (htmlElem->tag == HTML_FIGURE))) {
                if (!DFStringEquals(className,DFRefTextClass) &&
                    !DFStringEquals(className,DFRefLabelNumClass) &&
                    !DFStringEquals(className,DFRefCaptionTextClass))
                    className = DFRefTextClass;
            }

            if (DFStringEquals(className,DFRefTextClass) ||
                DFStringEquals(className,DFRefLabelNumClass) ||
                DFStringEquals(className,DFRefCaptionTextClass))
                DFFormatAttribute(concrete,WORD_INSTR," REF %s \\h ",bookmarkName);
            else if (DFStringEquals(className,DFRefDirectionClass))
                DFFormatAttribute(concrete,WORD_INSTR," REF %s \\p \\h ",bookmarkName);
            else
                DFFormatAttribute(concrete,WORD_INSTR," REF %s \\r \\h ",bookmarkName);
            break;
        }
    }
}
static void Word_flattenList(WordConverter *word, DFNode *list, int ilvl, DFNode *parent, DFNode *nextSibling)
{
    const char *type = (list->tag == HTML_OL) ? "decimal" : "disc";
    const char *cssText = DFGetAttribute(list,HTML_STYLE);
    CSSProperties *properties = CSSPropertiesNewWithString(cssText);
    if (CSSGet(properties,"list-style-type") != NULL)
        type = CSSGet(properties,"list-style-type");

    DFFormatAttribute(list,CONV_LISTNUM,"%u",list->seqNo);
    DFFormatAttribute(list,CONV_ILVL,"%d",ilvl);
    DFSetAttribute(list,CONV_LISTTYPE,type);

    for (DFNode *li = list->first; li != NULL; li = li->next) {

        DFNode *first = li->first;
        DFNode *liChildNext;
        for (DFNode *liChild = li->first; liChild != NULL; liChild = liChildNext) {
            liChildNext = liChild->next;

            if ((liChild->tag == HTML_UL) || (liChild->tag == HTML_OL)) {
                Word_flattenList(word,liChild,ilvl+1,parent,nextSibling);
            }
            else {
                if (liChild->tag == HTML_P) {
                    DFFormatAttribute(liChild,CONV_LISTNUM,"%u",list->seqNo);
                    DFFormatAttribute(liChild,CONV_ILVL,"%d",ilvl);
                    DFSetAttribute(liChild,CONV_LISTTYPE,type);
                    if (liChild == first)
                        DFSetAttribute(liChild,CONV_LISTITEM,"true");
                }
                DFInsertBefore(parent,liChild,nextSibling);
                Word_preProcessLists(word,liChild,ilvl);
            }
        }
    }

    DFRemoveNode(list);
    CSSPropertiesRelease(properties);
}
示例#3
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 WordPutSectPr(DFNode *concrete, CSSSheet *styleSheet, WordSection *section)
{
    // Note: The sectPr element can potentially contain one or more headerReference or
    // footerReference elements at the start, before the elements in WordSectPr_Children.
    // So the straight childrenToArray/replaceChildrenFromArray method used elsewhere doesn't
    // work here - we need to make sure these other elements are maintained

    DFNode *children[PREDEFINED_TAG_COUNT];
    childrenToArray(concrete,children);



    CSSProperties *oldBody = CSSPropertiesNew();
    CSSProperties *oldPage = CSSPropertiesNew();
    WordGetSectPr(concrete,oldBody,oldPage,section);

    CSSProperties *newBody = CSSSheetBodyProperties(styleSheet);
    CSSProperties *newPage = CSSSheetPageProperties(styleSheet);


    // Page size
    if (children[WORD_PGSZ] == NULL)
        children[WORD_PGSZ] = DFCreateElement(concrete->doc,WORD_PGSZ);

    int updatePageSize = 0;
    const char *widthStr = DFGetAttribute(children[WORD_PGSZ],WORD_W);
    const char *heightStr = DFGetAttribute(children[WORD_PGSZ],WORD_H);
    int widthTwips;
    int heightTwips;
    if ((widthStr == NULL) || (atoi(widthStr) <= 0) ||
        (heightStr == NULL) || (atoi(heightStr) <= 0)) {
        // Invalid or missing page size: Set to A4 portrait
        widthTwips = A4_WIDTH_TWIPS;
        heightTwips = A4_HEIGHT_TWIPS;
        updatePageSize = 1;
    }
    else {
        widthTwips = atoi(widthStr);
        heightTwips = atoi(heightStr);
    }

    if (!DFStringEquals(CSSGet(oldPage,"size"),CSSGet(newPage,"size"))) {
        const char *newSize = CSSGet(newPage,"size");
        if (DFStringEqualsCI(newSize,"A4 portrait")) {
            widthTwips = A4_WIDTH_TWIPS;
            heightTwips = A4_HEIGHT_TWIPS;
        }
        else if (DFStringEqualsCI(newSize,"A4 landscape")) {
            widthTwips = A4_HEIGHT_TWIPS;
            heightTwips = A4_WIDTH_TWIPS;
        }
        else if (DFStringEqualsCI(newSize,"letter portrait")) {
            widthTwips = LETTER_WIDTH_TWIPS;
            heightTwips = LETTER_HEIGHT_TWIPS;
        }
        else if (DFStringEqualsCI(newSize,"letter landscape")) {
            widthTwips = LETTER_HEIGHT_TWIPS;
            heightTwips = LETTER_WIDTH_TWIPS;
        }
        else {
            widthTwips = A4_WIDTH_TWIPS;
            heightTwips = A4_HEIGHT_TWIPS;
        }
        updatePageSize = 1;
    }

    if (updatePageSize) {
        DFFormatAttribute(children[WORD_PGSZ],WORD_W,"%d",widthTwips);
        DFFormatAttribute(children[WORD_PGSZ],WORD_H,"%d",heightTwips);

        if (widthTwips > heightTwips)
            DFSetAttribute(children[WORD_PGSZ],WORD_ORIENT,"landscape");
        else
            DFRemoveAttribute(children[WORD_PGSZ],WORD_ORIENT);
    }

    if (children[WORD_PGMAR] == NULL)
        children[WORD_PGMAR] = DFCreateElement(concrete->doc,WORD_PGMAR);

    // Page margins
    if (!DFStringEquals(CSSGet(oldBody,"margin-left"),CSSGet(newBody,"margin-left")) || updatePageSize)
        updateTwipsFromLength(children[WORD_PGMAR],WORD_LEFT,CSSGet(newBody,"margin-left"),widthTwips);

    if (!DFStringEquals(CSSGet(oldBody,"margin-right"),CSSGet(newBody,"margin-right")) || updatePageSize)
        updateTwipsFromLength(children[WORD_PGMAR],WORD_RIGHT,CSSGet(newBody,"margin-right"),widthTwips);

    if (!DFStringEquals(CSSGet(oldBody,"margin-top"),CSSGet(newBody,"margin-top")) || updatePageSize)
        updateTwipsFromLength(children[WORD_PGMAR],WORD_TOP,CSSGet(newBody,"margin-top"),widthTwips);

    if (!DFStringEquals(CSSGet(oldBody,"margin-bottom"),CSSGet(newBody,"margin-bottom")) || updatePageSize)
        updateTwipsFromLength(children[WORD_PGMAR],WORD_BOTTOM,CSSGet(newBody,"margin-bottom"),widthTwips);

    if (children[WORD_PGMAR]->attrsCount == 0)
        children[WORD_PGMAR] = NULL;;

    DFArray *extra = DFArrayNew(NULL,NULL);
    for (DFNode *child = concrete->first; child != NULL; child = child->next) {
        switch (child->tag) {
            case WORD_HEADERREFERENCE:
            case WORD_FOOTERREFERENCE:
                DFArrayAppend(extra,child);
                break;
        }
    }
    replaceChildrenFromArray(concrete,children,WordSectPr_Children);
    for (long i = (long)(DFArrayCount(extra)-1); i >= 0; i--) {
        DFNode *child = DFArrayItemAt(extra,i);
        DFInsertBefore(concrete,child,concrete->first);
    }

    DFArrayRelease(extra);
    CSSPropertiesRelease(oldBody);
    CSSPropertiesRelease(oldPage);
}
示例#5
0
void Word_setupBookmarkLinks(WordPutData *put)
{
    DFHashTable *referencesById = findReferences(put->conv->html);
    const char **sortedIds = DFHashTableCopyKeys(referencesById);
    DFSortStringsCaseSensitive(sortedIds);
    for (int idIndex = 0; sortedIds[idIndex]; idIndex++) {
        const char *targetId = sortedIds[idIndex];
        DFArray *references = DFHashTableLookup(referencesById,targetId);
        DFNode *targetElem = DFElementForIdAttr(put->conv->html,targetId);
        if (targetElem == NULL)
            continue;

        // The following is only relevant for figures and tables
        int refText = 0;
        int refLabelNum = 0;
        int refCaptionText = 0;

        for (int refIndex = 0; refIndex < DFArrayCount(references); refIndex++) {
            DFNode *a = DFArrayItemAt(references,refIndex);
            const char *className = DFGetAttribute(a,HTML_CLASS);
            if (DFStringEquals(className,DFRefTextClass))
                refText = 1;
            else if (DFStringEquals(className,DFRefLabelNumClass))
                refLabelNum = 1;
            else if (DFStringEquals(className,DFRefCaptionTextClass))
                refCaptionText = 1;
        }

        DFNode *concrete = WordConverterGetConcrete(put,targetElem);
        switch (targetElem->tag) {
            case HTML_H1:
            case HTML_H2:
            case HTML_H3:
            case HTML_H4:
            case HTML_H5:
            case HTML_H6: {
                const char *bookmarkId = NULL;
                const char *bookmarkName = NULL;
                DFNode *bookmarkElem = NULL;
                if ((concrete != NULL) && (concrete->tag == WORD_P)) {

                    // FIXME: We only want to consider the bookmark to be the headings "correct"
                    // bookmark in the case where it contains all of the heading's content, though
                    // excluding other bookmarks that might come before or after it.

                    // If you have the cursor inside a heading bookmark when you save the document,
                    // word puts a bookmark called _GoBack there, and we of course don't want to
                    // confuse that with the actual heading's bookmark (if any).

                    // For now as a temporary hack we just explicitly filter out _GoBack; but there
                    // needs to be a more general fix, as there may be other bookmarks that end up
                    // in the heading.

                    for (DFNode *child = concrete->first; child != NULL; child = child->next) {
                        if ((child->tag == WORD_BOOKMARK) &&
                            !DFStringEquals(DFGetAttribute(child,WORD_NAME),"_GoBack")) {
                            bookmarkElem = child;
                            bookmarkId = DFGetAttribute(bookmarkElem,WORD_ID);
                            bookmarkName = DFGetAttribute(bookmarkElem,WORD_NAME);
                            break;
                        }
                    }
                }

                if ((bookmarkElem == NULL) || (bookmarkId == NULL) || (bookmarkName == NULL)) {
                    // New bookmark
                    WordBookmark *bookmark = WordObjectsAddBookmark(put->conv->objects);
                    bookmarkId =bookmark->bookmarkId;
                    bookmarkName = bookmark->bookmarkName;
                }

                DFNode *bookmarkSpan = DFCreateElement(put->conv->package->document,HTML_SPAN);
                DFSetAttribute(bookmarkSpan,HTML_CLASS,DFBookmarkClass);

                if (bookmarkElem != NULL) {
                    // FIXME: Not covered by tests
                    DFFormatAttribute(bookmarkSpan,HTML_ID,"%s%u",put->conv->idPrefix,bookmarkElem->seqNo);
                }

                DFSetAttribute(bookmarkSpan,WORD_NAME,bookmarkName);
                DFSetAttribute(bookmarkSpan,WORD_ID,bookmarkId);

                while (targetElem->first != NULL)
                    DFAppendChild(bookmarkSpan,targetElem->first);
                DFAppendChild(targetElem,bookmarkSpan);

                break;
            }
            case HTML_TABLE:
            case HTML_FIGURE: {
                WordCaption *caption = WordObjectsCaptionForTarget(put->conv->objects,targetElem);
                if (caption == NULL)
                    break;

                assert(caption->element != NULL);
                assert((caption->number == NULL) || (caption->number->parent == caption->element));
                assert((caption->contentStart == NULL) || (caption->contentStart->parent == caption->element));

                // Note: caption.number may be null (i.e. if the caption is unnumbered)
                //       caption.contentStart may be null (if there is no text in the caption)

                WordBookmark *captionTextBookmark = NULL;
                WordBookmark *labelNumBookmark = NULL;
                WordBookmark *textBookmark = NULL;

                if (!refCaptionText && !refLabelNum && !refText)
                    refText = 1;

                if (refCaptionText) {
                    captionTextBookmark = createBookmark(put->conv);
                    DFNode *nnext;
                    for (DFNode *n = caption->contentStart; n != NULL; n = nnext) {
                        nnext = n->next;
                        DFAppendChild(captionTextBookmark->element,n);
                    }
                    DFAppendChild(caption->element,captionTextBookmark->element);
                }
                if (refLabelNum && (caption->number != NULL)) {
                    labelNumBookmark = createBookmark(put->conv);
                    DFNode *numberNext = caption->number->next;
                    DFNode *nnext;
                    for (DFNode *n = caption->element->first; (n != NULL) && (n != numberNext); n = nnext) {
                        nnext = n->next;
                        DFAppendChild(labelNumBookmark->element,n);
                    }
                    DFInsertBefore(caption->element,labelNumBookmark->element,caption->element->first);
                }
                if (refText) {
                    textBookmark = createBookmark(put->conv);
                    DFNode *nnext;
                    for (DFNode *n = caption->element->first; n != NULL; n = nnext) {
                        nnext = n->next;
                        DFAppendChild(textBookmark->element,n);
                    }
                    DFAppendChild(caption->element,textBookmark->element);
                }

                caption->captionTextBookmark = captionTextBookmark;
                caption->labelNumBookmark = labelNumBookmark;
                caption->textBookmark = textBookmark;

                break;
            }
        }
    }
    free(sortedIds);
    DFHashTableRelease(referencesById);
}
static void Word_fixListSingle(WordConverter *conv, DFNode *node)
{
    ListStack stack;
    bzero(&stack,sizeof(ListStack));

    DFNode *next;
    for (DFNode *child = node->first; child != NULL; child = next) {
        next = child->next;

        int isListItem = 0;

        if (child->tag == HTML_P) {
            DFNode *elem = child;

            const char *numIdStr = DFGetAttribute(elem,WORD_NUMID);
            const char *ilvlStr = DFGetAttribute(elem,WORD_ILVL);
            DFRemoveAttribute(elem,WORD_NUMID);
            DFRemoveAttribute(elem,WORD_ILVL);

            // A numId of 0 means that there is no numbering applied to this paragraph
            if ((numIdStr != NULL) && (atoi(numIdStr) == 0)) {
                numIdStr = NULL;
                ilvlStr = NULL;
            }

            if ((numIdStr != NULL) && (ilvlStr != NULL)) {
                isListItem = 1;
                int numId = atoi(numIdStr);
                int ilvl = atoi(ilvlStr);
                ListDimensions dimensions = listIndent(conv,numIdStr,ilvlStr);

                // Find the list at the same ilvl, and check if it has the same numId. If not, we're
                // starting a new list.

                ListFrame *sameLevelFrame = NULL;
                for (ListFrame *frame = stack.top; frame != NULL; frame = frame->parent) {
                    if (frame->ilvl == ilvl)
                        sameLevelFrame = frame;
                }

                if ((sameLevelFrame != NULL) && (sameLevelFrame->numId != numId))
                    fixTrailingParagraphs(&stack,ilvl);
                else
                    fixTrailingParagraphs(&stack,ilvl+1);

                if ((stack.top != NULL) && (stack.top->numId != numId))
                    ListStackPopToAboveIlvl(&stack,ilvl);
                else if ((stack.top != NULL) && (stack.top->ilvl > ilvl))
                    ListStackPopToAboveIlvl(&stack,ilvl+1);

                if ((stack.top == NULL) || (stack.top->numId != numId) || (stack.top->ilvl < ilvl)) {
                    WordConcreteNum *num = WordNumberingConcreteWithId(conv->numbering,numIdStr); // may be NULL
                    WordNumLevel *level = WordConcreteNumGetLevel(num,ilvl); // may be NULL

                    const char *type = WordNumLevelToListStyleType(level); // may be NULL
                    Tag tag;
                    if (DFStringEquals(type,"disc") ||
                        DFStringEquals(type,"circle") ||
                        DFStringEquals(type,"square"))
                        tag = HTML_UL;
                    else
                        tag = HTML_OL;

                    DFNode *element = DFCreateElement(conv->html,tag);

                    if (type != NULL)
                        DFFormatAttribute(element,HTML_STYLE,"list-style-type: %s",type);

                    if (stack.top != NULL) {
                        DFNode *li;
                        if (stack.top->element->last != NULL)
                            li = stack.top->element->last;
                        else
                            li = DFCreateChildElement(stack.top->element,HTML_LI);
                        DFAppendChild(li,element);
                    }
                    else {
                        DFInsertBefore(node,element,child);
                    }
                    ListStackPushFrame(&stack,element,numId,ilvl,dimensions);
                }
            }
        }

        if (stack.top != NULL) {
            DFNode *li;
            if ((stack.top->element->last != NULL) && !isListItem)
                li = stack.top->element->last;
            else
                li = DFCreateChildElement(stack.top->element,HTML_LI);
            DFAppendChild(li,child);
        }
    }
    fixTrailingParagraphs(&stack,-1);
    while (stack.top != NULL)
        ListStackPop(&stack);
}
示例#7
0
static void populateDrawingElement(WordConverter *converter, DFNode *root, double widthPts,
                                   double heightPts, const char *drawingId, const char *rId)
{
    EMUSize size;
    size.widthEmu = round(widthPts*EMUS_PER_POINT);
    size.heightEmu = round(heightPts*EMUS_PER_POINT);

    root->tag = WORD_DRAWING;
    DFRemoveAllAttributes(root);

    while (root->first != NULL)
        DFRemoveNode(root->first);

    char *docName = DFFormatString("Picture %s",drawingId);
    const char *imgName = "image";
    const char *imgId = "0";
    DFNode *inlin = DFCreateChildElement(root,DML_WP_INLINE);

    DFNode *extent = DFCreateChildElement(inlin,DML_WP_EXTENT); // a_CT_PositiveSize2D
    DFFormatAttribute(extent,NULL_CX,"%llu",size.widthEmu);
    DFFormatAttribute(extent,NULL_CY,"%llu",size.heightEmu);
    DFNode *docPr = DFCreateChildElement(inlin,DML_WP_DOCPR); // a_CT_NonVisualDrawingProps
    DFSetAttribute(docPr,NULL_ID,drawingId);
    DFSetAttribute(docPr,NULL_NAME,docName);
    DFNode *cNvGraphicFramePr = DFCreateChildElement(inlin,DML_WP_CNVGRAPHICFRAMEPR);
    DFNode *graphicFrameLocks = DFCreateChildElement(cNvGraphicFramePr,DML_MAIN_GRAPHICFRAMELOCKS);
    DFSetAttribute(graphicFrameLocks,NULL_NOCHANGEASPECT,"1");

    DFNode *graphic = DFCreateChildElement(inlin,DML_MAIN_GRAPHIC);
    DFNode *graphicData = DFCreateChildElement(graphic,DML_MAIN_GRAPHICDATA);
    DFSetAttribute(graphicData,NULL_URI,"http://schemas.openxmlformats.org/drawingml/2006/picture");

    DFNode *pic = DFCreateChildElement(graphicData,DML_PICTURE_PIC);
    DFNode *nvPicPr = DFCreateChildElement(pic,DML_PICTURE_NVPICPR);
    DFNode *blipFill = DFCreateChildElement(pic,DML_PICTURE_BLIPFILL); // a_CT_BlipFillProperties
    DFNode *spPr = DFCreateChildElement(pic,DML_PICTURE_SPPR); // a_CT_ShapeProperties

    // Graphic - Non-visual properties
    DFNode *cNvPr = DFCreateChildElement(nvPicPr,DML_PICTURE_CNVPR);
    DFNode *cNvPicPr = DFCreateChildElement(nvPicPr,DML_PICTURE_CNVPICPR);
    DFSetAttribute(cNvPr,NULL_ID,imgId);
    DFSetAttribute(cNvPr,NULL_NAME,imgName);

    // Blip
    DFNode *blip = DFCreateChildElement(blipFill,DML_MAIN_BLIP);
    DFSetAttribute(blip,OREL_EMBED,rId);
    DFNode *stretch = DFCreateChildElement(blipFill,DML_MAIN_STRETCH);
    DFNode *fillRect = DFCreateChildElement(stretch,DML_MAIN_FILLRECT);

    // Shape properties
    DFNode *xfrm = DFCreateChildElement(spPr,DML_MAIN_XFRM);
    DFNode *off = DFCreateChildElement(xfrm,DML_MAIN_OFF);
    DFSetAttribute(off,NULL_X,"0");
    DFSetAttribute(off,NULL_Y,"0");
    DFNode *ext = DFCreateChildElement(xfrm,DML_MAIN_EXT);
    DFFormatAttribute(ext,NULL_CX,"%llu",size.widthEmu);
    DFFormatAttribute(ext,NULL_CY,"%llu",size.heightEmu);
    DFNode *prstGeom = DFCreateChildElement(spPr,DML_MAIN_PRSTGEOM);
    DFSetAttribute(prstGeom,NULL_PRST,"rect");


    (void)cNvPicPr;
    (void)fillRect;
    free(docName);
}
static DFNode *WordFieldGet(WordGetData *get, DFNode *concrete)
{
    if (concrete->tag != WORD_FLDSIMPLE)
        return NULL;;

    const char *instr = DFGetAttribute(concrete,WORD_INSTR);
    if (instr != NULL) {
        const char **args = Word_parseField(instr);
        size_t argCount = DFStringArrayCount(args);

        if ((argCount >= 2) && !strcmp(args[0],"REF")) {
            WordBookmark *bookmark = WordObjectsBookmarkWithName(get->conv->objects,args[1]);
            if ((bookmark != NULL) && (bookmark->target != NULL)) {

                WordRefType type = WordRefTypeGet(args,bookmark);

                DFNode *a = WordConverterCreateAbstract(get,HTML_A,concrete);
                DFFormatAttribute(a,HTML_HREF,"#%s%u",get->conv->idPrefix,bookmark->target->seqNo);
                DFSetAttribute(a,HTML_CLASS,WordRefTypeClassName(type));

                free(args);
                return a;
            }
        }
        else if ((argCount >= 1) && !strcmp(args[0],"TOC")) {

            if ((argCount >= 2) && !strcmp(args[1],"\\o")) {
                DFNode *nav = WordConverterCreateAbstract(get,HTML_NAV,concrete);
                DFSetAttribute(nav,HTML_CLASS,DFTableOfContentsClass);
                free(args);
                return nav;
            }
            else if ((argCount >= 3) && !strcmp(args[1],"\\c")) {
                // FIXME: The names "Figure" and "Table" here will be different if the document
                // was created in a language other than English. We need to look through the
                // document to figure out which counter names are used in captions adjacent to
                // figures and tables to know what the counter names used in the document
                // actually are.

                // Another option might be just to collect a static list of names used in all the
                // major languages and base the detection on that. These would need to be checked
                // with multiple versions of word, as the names used could in theory change
                // between releases.

                // We should keep track of a set of "document parameters", which record the names
                // used for figure and table counters, as well as the prefixes used on numbered
                // figures and tables. The latter would correspond to the content property of the
                // caption::before and figcaption::before CSS rules.

                if (!strcmp(args[2],"Figure")) {
                    DFNode *nav = WordConverterCreateAbstract(get,HTML_NAV,concrete);
                    DFSetAttribute(nav,HTML_CLASS,DFListOfFiguresClass);
                    free(args);
                    return nav;
                }
                else if (!strcmp(args[2],"Table")) {
                    DFNode *nav = WordConverterCreateAbstract(get,HTML_NAV,concrete);
                    DFSetAttribute(nav,HTML_CLASS,DFListOfTablesClass);
                    free(args);
                    return nav;
                }
            }
        }

        DFNode *span = WordConverterCreateAbstract(get,HTML_SPAN,concrete);
        DFSetAttribute(span,HTML_CLASS,DFFieldClass);
        DFNode *text = DFCreateTextNode(get->conv->html,instr);
        DFAppendChild(span,text);
        free(args);
        return span;
    }
    return NULL;
}
示例#9
0
void WordPutPPr(DFNode *pPr, CSSProperties *properties, const char *styleId, WordSection *section, int outlineLvl)
{
    assert(pPr->tag == WORD_PPR);

    // The child elements of pPr have to be in a specific order. So we build up a structure based
    // on the existing elements (updated as appropriate from newProperties), and then rebuild
    // from that

    CSSProperties *oldp = CSSPropertiesNew();
    CSSProperties *newp = properties;
    const char *oldStyleId = NULL;
    const char *newStyleId = styleId;
    WordGetPPr(pPr,oldp,&oldStyleId,section);

    {
        DFNode *children[PREDEFINED_TAG_COUNT];
        childrenToArray(pPr,children);

        int existingOutlineLvl = -1;
        if (children[WORD_OUTLINELVL] != NULL) {
            const char *value = DFGetAttribute(children[WORD_OUTLINELVL],WORD_VAL);
            if (value != NULL)
                existingOutlineLvl = atoi(value);
        }

        // Style name
        if (!DFStringEquals(oldStyleId,newStyleId)) {
            if (newStyleId != NULL) {
                children[WORD_PSTYLE] = DFCreateElement(pPr->doc,WORD_PSTYLE);
                DFSetAttribute(children[WORD_PSTYLE],WORD_VAL,newStyleId);
            }
            else {
                children[WORD_PSTYLE] = NULL;
            }
        }

        // Paragraph border (pBdr)

        if (children[WORD_PBDR] == NULL)
            children[WORD_PBDR] = DFCreateElement(pPr->doc,WORD_PBDR);

        WordPutPBdr(children[WORD_PBDR],oldp,newp);

        if (children[WORD_PBDR]->first == NULL)
            children[WORD_PBDR] = NULL;

        // Numbering and outline level
        // Don't change these properties for styles with outline level >= 6, as these can't be
        // represented with the standard HTML heading elements, which only go from h1 - h6
        // (outline levels 0 - 5)
        if (existingOutlineLvl < 6) {
            if (children[WORD_NUMPR] == NULL)
                children[WORD_NUMPR] = DFCreateElement(pPr->doc,WORD_NUMPR);

            WordPutNumPr(children[WORD_NUMPR],newp);

            if (children[WORD_NUMPR]->first == NULL)
                children[WORD_NUMPR] = NULL;

            if ((outlineLvl >= 0) && (outlineLvl <= 5)) {
                if (children[WORD_OUTLINELVL] == NULL)
                    children[WORD_OUTLINELVL] = DFCreateElement(pPr->doc,WORD_OUTLINELVL);
                DFFormatAttribute(children[WORD_OUTLINELVL],WORD_VAL,"%d",outlineLvl);
            }
            else {
                children[WORD_OUTLINELVL] = NULL;
            }
        }

        // background-color

        char *oldBackgroundColor = CSSHexColor(CSSGet(oldp,"background-color"),0);
        char *newBackgroundColor = CSSHexColor(CSSGet(newp,"background-color"),0);
        if (!DFStringEquals(oldBackgroundColor,newBackgroundColor))
            WordPutShd(pPr->doc,&children[WORD_SHD],newBackgroundColor);
        free(oldBackgroundColor);
        free(newBackgroundColor);

        // text-align

        if (!DFStringEquals(CSSGet(oldp,"text-align"),CSSGet(newp,"text-align"))) {
            const char *newTextAlign = CSSGet(newp,"text-align");
            if (newTextAlign != NULL) {
                const char *val = NULL;
                if (!strcmp(newTextAlign,"left"))
                    val = "left";
                else if (!strcmp(newTextAlign,"right"))
                    val = "right";
                else if (!strcmp(newTextAlign,"center"))
                    val = "center";
                else if (!strcmp(newTextAlign,"justify"))
                    val = "both";
                if (val != NULL) {
                    children[WORD_JC] = DFCreateElement(pPr->doc,WORD_JC);
                    DFSetAttribute(children[WORD_JC],WORD_VAL,val);
                }
            }
            else {
                children[WORD_JC] = NULL;
            }
        }

        if ((section != NULL) && (WordSectionContentWidth(section) >= 0)) {
            const char *oldMarginLeft = CSSGet(oldp,"margin-left");
            const char *oldMarginRight = CSSGet(oldp,"margin-right");
            const char *oldTextIndent = CSSGet(oldp,"text-indent");
            const char *newMarginLeft = CSSGet(newp,"margin-left");
            const char *newMarginRight = CSSGet(newp,"margin-right");
            const char *newTextIndent = CSSGet(newp,"text-indent");

            // Special case of the List_Paragraph style, which is used by Word to ensure lists are indented. We
            // don't set this property for HTML, because it automatically indents lists. However we need to ensure
            // that it remains unchanged when updating the word document
            const char *newWordMarginLeft = CSSGet(newp,"-word-margin-left");
            if (newMarginLeft == NULL)
                newMarginLeft = newWordMarginLeft;

            if ((newMarginLeft == NULL) && (newMarginRight == NULL) && (newTextIndent == NULL)) {
                children[WORD_IND] = NULL;
            }
            else {
                if (children[WORD_IND] == NULL)
                    children[WORD_IND] = DFCreateElement(pPr->doc,WORD_IND);

                if (!DFStringEquals(oldMarginLeft,newMarginLeft)) {
                    if (newMarginLeft != NULL)
                        updateTwipsFromLength(children[WORD_IND],WORD_LEFT,newMarginLeft,WordSectionContentWidth(section));
                    else
                        DFRemoveAttribute(children[WORD_IND],WORD_LEFT);
                    DFRemoveAttribute(children[WORD_IND],WORD_START);
                }

                if (!DFStringEquals(oldMarginRight,newMarginRight)) {
                    if (newMarginRight != NULL)
                        updateTwipsFromLength(children[WORD_IND],WORD_RIGHT,newMarginRight,WordSectionContentWidth(section));
                    else
                        DFRemoveAttribute(children[WORD_IND],WORD_RIGHT);
                    DFRemoveAttribute(children[WORD_IND],WORD_END);
                }

                if (!DFStringEquals(oldTextIndent,newTextIndent)) {
                    if (newTextIndent != NULL) {
                        CSSLength length = CSSLengthFromString(newTextIndent);

                        if (CSSLengthIsValid(length)) {
                            double pts = CSSLengthToPts(length,WordSectionContentWidthPts(section));
                            int twips = (int)round(pts*20);

                            if (twips >= 0) {
                                DFFormatAttribute(children[WORD_IND],WORD_FIRSTLINE,"%d",twips);
                                DFRemoveAttribute(children[WORD_IND],WORD_HANGING);
                            }
                            else {
                                DFFormatAttribute(children[WORD_IND],WORD_HANGING,"%d",-twips);
                                DFRemoveAttribute(children[WORD_IND],WORD_FIRSTLINE);
                            }
                        }
                    }
                    else {
                        DFRemoveAttribute(children[WORD_IND],WORD_FIRSTLINE);
                        DFRemoveAttribute(children[WORD_IND],WORD_HANGING);
                    }
                }

            }
        }

        if (!DFStringEquals(CSSGet(oldp,"margin-top"),CSSGet(newp,"margin-top")) ||
            !DFStringEquals(CSSGet(oldp,"margin-bottom"),CSSGet(newp,"margin-bottom")) ||
            !DFStringEquals(CSSGet(oldp,"line-height"),CSSGet(newp,"line-height"))) {

            if ((CSSGet(newp,"margin-top") == NULL) &&
                (CSSGet(newp,"margin-bottom") == NULL) &&
                (CSSGet(newp,"line-height") == NULL)) {
                children[WORD_SPACING] = NULL;
            }
            else {
                children[WORD_SPACING] = DFCreateElement(pPr->doc,WORD_SPACING);

                if (DFStringEquals(CSSGet(newp,"margin-top"),"-word-auto")) {
                    DFSetAttribute(children[WORD_SPACING],WORD_BEFORE,"100");
                    DFSetAttribute(children[WORD_SPACING],WORD_BEFOREAUTOSPACING,"1");
                }
                else {
                    char *before = twipsFromCSS(CSSGet(newp,"margin-top"),WordSectionContentWidth(section));
                    DFSetAttribute(children[WORD_SPACING],WORD_BEFORE,before);
                    DFSetAttribute(children[WORD_SPACING],WORD_BEFOREAUTOSPACING,NULL);
                    free(before);
                }

                if (DFStringEquals(CSSGet(newp,"margin-bottom"),"-word-auto")) {
                    DFSetAttribute(children[WORD_SPACING],WORD_AFTER,"100");
                    DFSetAttribute(children[WORD_SPACING],WORD_AFTERAUTOSPACING,"1");
                }
                else {
                    char *after = twipsFromCSS(CSSGet(newp,"margin-bottom"),WordSectionContentWidth(section));
                    DFSetAttribute(children[WORD_SPACING],WORD_AFTER,after);
                    DFSetAttribute(children[WORD_SPACING],WORD_AFTERAUTOSPACING,NULL);
                    free(after);
                }

                CSSLength lineHeight = CSSLengthFromString(CSSGet(newp,"line-height"));
                if (CSSLengthIsValid(lineHeight) && (lineHeight.units == UnitsPct)) {
                    int value = (int)round(lineHeight.value*2.4);
                    DFFormatAttribute(children[WORD_SPACING],WORD_LINE,"%d",value);
                }
                
                if (children[WORD_SPACING]->attrsCount == 0)
                    children[WORD_SPACING] = NULL;
            }
        }
        
        replaceChildrenFromArray(pPr,children,WordPPR_Children);
    }
    CSSPropertiesRelease(oldp);
}
示例#10
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);
}
示例#11
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;
}