static void WordPutNumPr(DFNode *concrete, CSSProperties *newp) { DFNode *children[PREDEFINED_TAG_COUNT]; childrenToArray(concrete,children); CSSProperties *oldp = CSSPropertiesNew(); WordGetNumPr(concrete,oldp); if (!DFStringEquals(CSSGet(oldp,"-word-numId"),CSSGet(newp,"-word-numId")) || !DFStringEquals(CSSGet(oldp,"-word-ilvl"),CSSGet(newp,"-word-ilvl"))) { if (CSSGet(newp,"-word-numId") != NULL) { children[WORD_NUMID] = DFCreateElement(concrete->doc,WORD_NUMID); DFSetAttribute(children[WORD_NUMID],WORD_VAL,CSSGet(newp,"-word-numId")); if (CSSGet(newp,"-word-ilvl") != NULL) { children[WORD_ILVL] = DFCreateElement(concrete->doc,WORD_ILVL); DFSetAttribute(children[WORD_ILVL],WORD_VAL,CSSGet(newp,"-word-ilvl")); } else { children[WORD_ILVL] = NULL; } } else { children[WORD_NUMID] = NULL; children[WORD_ILVL] = NULL; } } replaceChildrenFromArray(concrete,children,WordNumPr_Children); CSSPropertiesRelease(oldp); }
static void expandRecursive(DFNode *node) { DFNode *next; for (DFNode *child = node->first; child != NULL; child = next) { next = child->next; expandRecursive(child); } if (node->tag == WORD_BOOKMARK) { const char *bookmarkId = DFGetAttribute(node,WORD_ID); const char *bookmarkName = DFGetAttribute(node,WORD_NAME); if (bookmarkId == NULL) bookmarkId = ""; if (bookmarkName == NULL) bookmarkName = ""; DFNode *startElem = DFCreateElement(node->doc,WORD_BOOKMARKSTART); DFNode *endElem = DFCreateElement(node->doc,WORD_BOOKMARKEND); DFSetAttribute(startElem,WORD_ID,bookmarkId); DFSetAttribute(startElem,WORD_NAME,bookmarkName); DFSetAttribute(endElem,WORD_ID,bookmarkId); DFInsertBefore(node->parent,startElem,node); DFInsertBefore(node->parent,endElem,node->next); DFRemoveNodeButKeepChildren(node); } }
static WordBookmark *createBookmark(WordConverter *converter) { WordBookmark *bookmark = WordObjectsAddBookmark(converter->objects); DFNode *bookmarkSpan = DFCreateElement(converter->package->document,HTML_SPAN); DFSetAttribute(bookmarkSpan,HTML_CLASS,DFBookmarkClass); DFSetAttribute(bookmarkSpan,WORD_NAME,bookmark->bookmarkName); DFSetAttribute(bookmarkSpan,WORD_ID,bookmark->bookmarkId); bookmark->element = bookmarkSpan; return bookmark; }
static WordBookmark *createBookmark(WordPutData *put) { WordBookmark *bookmark = WordObjectsAddBookmark(put->conv->objects); DFNode *bookmarkSpan = DFCreateElement(put->conv->html,HTML_SPAN); DFSetAttribute(bookmarkSpan,HTML_CLASS,DFBookmarkClass); DFSetAttribute(bookmarkSpan,WORD_NAME,bookmark->bookmarkName); DFSetAttribute(bookmarkSpan,WORD_ID,bookmark->bookmarkId); bookmark->element = bookmarkSpan; return bookmark; }
static DFNode *WordParagraphContentCreate(WordPutData *put, DFNode *abstract) { switch (abstract->tag) { case HTML_SPAN: { const char *spanClass = DFGetAttribute(abstract,HTML_CLASS); if (DFStringEquals(spanClass,DFFieldClass)) { DFNode *concrete = DFCreateElement(put->contentDoc,WORD_FLDSIMPLE); char *nodeText = DFNodeTextToString(abstract); DFSetAttribute(concrete,WORD_INSTR,nodeText); free(nodeText); put->conv->haveFields = 1; return concrete; } else if (DFStringEquals(spanClass,DFBookmarkClass)) { const char *bookmarkId = DFGetAttribute(abstract,WORD_ID); const char *bookmarkName = DFGetAttribute(abstract,WORD_NAME); if ((bookmarkId == NULL) || (bookmarkName == NULL)) return NULL;; WordBookmark *bookmark = WordObjectsBookmarkWithName(put->conv->objects,bookmarkName); if (bookmark == NULL) return NULL;; DFNode *concrete = DFCreateElement(put->contentDoc,WORD_BOOKMARK); DFSetAttribute(concrete,WORD_ID,bookmarkId); DFSetAttribute(concrete,WORD_NAME,bookmarkName); bookmark->element = concrete; WordParagraphContentPut(put,abstract,concrete); return concrete; } else { DFNode *concrete = DFCreateElement(put->contentDoc,WORD_R); WordParagraphContentPut(put,abstract,concrete); return concrete; } } case HTML_INS: case HTML_DEL: return WordChangeLens.create(put,abstract); case HTML_A: { const char *href = DFGetAttribute(abstract,HTML_HREF); if (href != NULL) { if (HTML_nodeIsHyperlink(abstract)) return WordHyperlinkLens.create(put,abstract); else return WordFieldLens.create(put,abstract); } return NULL; } default: return NULL; } }
void ODFManifestAddEntry(ODFManifest *manifest, const char *path, const char *mediaType, const char *version) { DFNode *entry = DFHashTableLookup(manifest->entriesByPath,path); if (entry == NULL) { entry = DFCreateChildElement(manifest->doc->root,MF_FILE_ENTRY); DFHashTableAdd(manifest->entriesByPath,path,entry); } DFSetAttribute(entry,MF_FULL_PATH,path); DFSetAttribute(entry,MF_MEDIA_TYPE,mediaType); DFSetAttribute(entry,MF_VERSION,version); if (!strcmp(path,"") && (version != NULL)) DFSetAttribute(manifest->doc->root,MF_VERSION,version); }
static DFNode *createAbstractPlaceholder(WordGetData *get, const char *placeholderText, DFNode *concrete) { DFNode *span = WordConverterCreateAbstract(get,HTML_SPAN,concrete); DFSetAttribute(span,HTML_CLASS,DFPlaceholderClass); DFNode *text = DFCreateTextNode(get->conv->html,placeholderText); DFAppendChild(span,text); return span; }
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); }
static DFNode *WordBookmarkGet(WordGetData *get, DFNode *concrete) { if (!WordBookmarkIsVisible2(concrete)) return NULL; DFNode *abstract = WordConverterCreateAbstract(get,HTML_SPAN,concrete); DFSetAttribute(abstract,HTML_CLASS,DFBookmarkClass); WordContainerGet(get,&WordParagraphContentLens,abstract,concrete); return abstract; }
static DFNode *WordFieldCreate(WordPutData *put, DFNode *abstract) { DFNode *concrete = DFCreateElement(put->contentDoc,WORD_FLDSIMPLE); // fldSimple elements are required to have an instr attribute (even if it's empty), so set // it here in case update doesn't change it for some reason DFSetAttribute(concrete,WORD_INSTR,""); WordFieldPut(put,abstract,concrete); put->conv->haveFields = 1; return concrete; }
static DFNode *WordRunContentGet(WordGetData *get, DFNode *concrete) { switch (concrete->tag) { case WORD_T: case WORD_DELTEXT: { DFBuffer *buf = DFBufferNew(); DFNodeTextToBuffer(concrete,buf); DFNode *abstract = DFCreateTextNode(get->conv->html,buf->data); DFBufferRelease(buf); return abstract; } case WORD_DRAWING: case WORD_OBJECT: case WORD_PICT: return WordDrawingGet(get,concrete); case WORD_TAB: { DFNode *span = WordConverterCreateAbstract(get,HTML_SPAN,concrete); DFSetAttribute(span,HTML_CLASS,DFTabClass); return span; } case WORD_BR: { const char *type = DFGetAttribute(concrete,WORD_TYPE); if (DFStringEquals(type,"column")) { DFNode *span = WordConverterCreateAbstract(get,HTML_SPAN,concrete); DFSetAttribute(span,HTML_CLASS,DFPlaceholderClass); DFCreateChildTextNode(span,"[Column break]"); return span; } else if (DFStringEquals(type,"page")) { DFNode *span = WordConverterCreateAbstract(get,HTML_SPAN,concrete); DFSetAttribute(span,HTML_CLASS,DFPlaceholderClass); DFCreateChildTextNode(span,"[Page break]"); return span; } else { return WordConverterCreateAbstract(get,HTML_BR,concrete); } } default: return NULL; } }
static void Word_addContentParts(DFNode *child, const char *content, WordCaption *caption) { if (content == NULL) return; DFNode *nextSibling = child->first; DFArray *parts = CSSParseContent(content); for (int i = 0; i < DFArrayCount(parts); i++) { ContentPart *part = DFArrayItemAt(parts,i); switch (part->type) { case ContentPartString: { DFNode *text = DFCreateTextNode(child->doc,part->value); if (strlen(part->value) > 0) { DFNode *span = DFCreateElement(child->doc,HTML_SPAN); DFAppendChild(span,text); DFInsertBefore(child,span,nextSibling); } break; } case ContentPartCounter: { if (DFStringEquals(part->value,"figure")) { DFNode *span = DFCreateElement(child->doc,HTML_SPAN); DFSetAttribute(span,HTML_CLASS,DFFieldClass); DFCreateChildTextNode(span," SEQ Figure \\* ARABIC "); DFInsertBefore(child,span,nextSibling); caption->number = span; } else if (DFStringEquals(part->value,"table")) { DFNode *span = DFCreateElement(child->doc,HTML_SPAN); DFSetAttribute(span,HTML_CLASS,DFFieldClass); DFCreateChildTextNode(span," SEQ Table \\* ARABIC "); DFInsertBefore(child,span,nextSibling); caption->number = span; } break; default: break; } } } DFArrayRelease(parts); }
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); }
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; }
DFNode *WordConverterCreateAbstract(WordGetData *get, Tag tag, DFNode *concrete) { DFNode *element = DFCreateElement(get->conv->html,tag); if (concrete != NULL) { char *idStr; if (concrete->doc == get->conv->package->document) idStr = DFFormatString("%s%u",get->conv->idPrefix,concrete->seqNo); else idStr = DFFormatString("%s%u-%s",get->conv->idPrefix,concrete->seqNo,DFNodeName(concrete->doc->root)); DFSetAttribute(element,HTML_ID,idStr); free(idStr); } return element; }
static DFNode *WordRunGet(WordGetData *get, DFNode *concrete) { assert(concrete->tag == WORD_R); // First check the run to see if it's a footnote or endnote reference. These need to be handled specially, // as we place the actual content of the footnote or endnote in-line in the HTML output. DFNode *note = WordRunGetNote(get,concrete); if (note != NULL) return note; // If we didn't find a footnote or endnote reference, treat it as a normal content run assert(concrete->tag == WORD_R); DFNode *rPr = DFChildWithTag(concrete,WORD_RPR); CSSProperties *properties = CSSPropertiesNew(); const char *styleId = NULL; if (rPr != NULL) WordGetRPr(rPr,properties,&styleId,get->conv->theme);; const char *selector = WordSheetSelectorForStyleId(get->conv->styles,"character",styleId); Tag elementName = (selector == NULL) ? HTML_SPAN : CSSSelectorGetTag(selector); char *className = (selector == NULL) ? NULL : CSSSelectorCopyClassName(selector); DFNode *abstract = WordConverterCreateAbstract(get,elementName,concrete); DFSetAttribute(abstract,HTML_CLASS,className); free(className); DFHashTable *collapsed = CSSCollapseProperties(properties); char *styleValue = CSSSerializeProperties(collapsed); DFHashTableRelease(collapsed); if (strlen(styleValue) > 0) DFSetAttribute(abstract,HTML_STYLE,styleValue); free(styleValue); CSSPropertiesRelease(properties); WordContainerGet(get,&WordRunContentLens,abstract,concrete); return abstract; }
DFNode *fromTidyNode(DFDocument *htmlDoc, TidyDoc tdoc, TidyNode tnode) { switch (tidyNodeGetType(tnode)) { case TidyNode_Text: { char *value = copyTidyNodeValue(tnode,tdoc); DFNode *result = DFCreateTextNode(htmlDoc,value); free(value); return result; } case TidyNode_CDATA: break; case TidyNode_Comment: break; case TidyNode_Root: printf("Have root\n"); break; default: { const char *name = tidyNodeGetName(tnode); if (name == NULL) { printf("NULL name for %p, type %d\n",tnode,tidyNodeGetType(tnode)); return NULL; } const NamespaceDecl *namespaceDecl = DFNameMapNamespaceForID(htmlDoc->map,NAMESPACE_HTML); Tag tag = DFNameMapTagForName(htmlDoc->map,namespaceDecl->namespaceURI,name); DFNode *element = DFCreateElement(htmlDoc,tag); for (TidyAttr tattr = tidyAttrFirst(tnode); tattr != NULL; tattr = tidyAttrNext(tattr)) { const char *name = tidyAttrName(tattr); const char *value = tidyAttrValue(tattr); if (value == NULL) // Can happen in case of the empty string value = "";; Tag attrTag = DFNameMapTagForName(htmlDoc->map,namespaceDecl->namespaceURI,name); DFSetAttribute(element,attrTag,value); } for (TidyNode tchild = tidyGetChild(tnode); tchild != NULL; tchild = tidyGetNext(tchild)) { DFNode *child = fromTidyNode(htmlDoc,tdoc,tchild); if (child != NULL) DFAppendChild(element,child); } return element; } } return NULL; }
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 DFNode *WordTcCreateAbstractNode(WordGetData *get, DFNode *concrete) { DFNode *td = WordConverterCreateAbstract(get,HTML_TD,concrete); CSSProperties *properties = CSSPropertiesNew(); DFNode *tcPr = DFChildWithTag(concrete,WORD_TCPR); if (tcPr != NULL) WordGetTcPr(tcPr,properties);; DFHashTable *collapsed = CSSCollapseProperties(properties); char *styleValue = CSSSerializeProperties(collapsed); DFHashTableRelease(collapsed); if (strlen(styleValue) > 0) DFSetAttribute(td,HTML_STYLE,styleValue); free(styleValue); CSSPropertiesRelease(properties); return td; }
static DFNode *WordDocumentGet(WordGetData *get, DFNode *concrete) { if (concrete->tag != WORD_DOCUMENT) return NULL; DFNode *html = WordConverterCreateAbstract(get,HTML_HTML,concrete); DFNode *head = WordConverterCreateAbstract(get,HTML_HEAD,NULL); DFAppendChild(html,head); DFNode *meta = WordConverterCreateAbstract(get,HTML_META,NULL); DFAppendChild(head,meta); DFSetAttribute(meta,HTML_CHARSET,"utf-8"); DFNode *wordBody = DFChildWithTag(concrete,WORD_BODY); if (wordBody != NULL) { DFNode *htmlBody = WordBodyLens.get(get,wordBody); DFAppendChild(html,htmlBody); } return html; }
static void SAXStartElement(void *ctx, const xmlChar *fullname, const xmlChar **atts) { DFSAXParser *parser = (DFSAXParser *)ctx; const NamespaceDecl *namespaceDecl = DFNameMapNamespaceForID(parser->document->map,NAMESPACE_HTML); Tag tag = DFNameMapTagForName(parser->document->map,namespaceDecl->namespaceURI,(const char *)fullname); DFNode *element = DFCreateElement(parser->document,tag); if (atts != NULL) { for (int i = 0; atts[i] != NULL; i += 2) { const xmlChar *name = atts[i]; const xmlChar *value = atts[i+1]; Tag attrTag = DFNameMapTagForName(parser->document->map,namespaceDecl->namespaceURI,(const char *)name); DFSetAttribute(element,attrTag,(const char *)value); } } DFAppendChild(parser->parent,element); parser->parent = element; if (parser->document->root == NULL) parser->document->root = element; }
static void replaceField(WordSimplification *simp) { assert(simp->instrText != NULL); assert(simp->beginNode != NULL); assert(simp->endNode != NULL); if ((simp->beginNode->parent->tag == WORD_R) && (simp->endNode->parent->tag == WORD_R)) { DFNode *beginRun = simp->beginNode->parent; DFNode *simple = DFCreateElement(simp->beginNode->doc,WORD_FLDSIMPLE); DFSetAttribute(simple,WORD_INSTR,simp->instrText->data); DFInsertBefore(beginRun->parent,simple,beginRun); removeNodes(simp->beginNode,simp->endNode); } DFBufferRelease(simp->instrText); simp->instrText = NULL; simp->beginNode = NULL; simp->endNode = NULL; simp->haveFields = 1; }
static DFNode *WordRunContentCreate(WordPutData *put, DFNode *abstract) { switch (abstract->tag) { case DOM_TEXT: { DFNode *text = DFCreateTextNode(put->contentDoc,abstract->value); // Text inside a <w:del> element must be stored in a <w:delText> element // Text *not* inside a <w:del> element is stored in a <w:t> element Tag tag = WORD_T; for (DFNode *a = abstract->parent; a != NULL; a = a->parent) { if (a->tag == HTML_DEL) tag = WORD_DELTEXT; } DFNode *t = DFCreateElement(put->contentDoc,tag); DFAppendChild(t,text); char *trimmed = DFStringTrimWhitespace(abstract->value); if (!DFStringEquals(trimmed,abstract->value)) DFSetAttribute(t,XML_SPACE,"preserve"); free(trimmed); return t; } case HTML_IMG: return WordDrawingCreate(put,abstract); case HTML_BR: return DFCreateElement(put->contentDoc,WORD_BR); case HTML_SPAN: { const char *className = DFGetAttribute(abstract,HTML_CLASS); if (DFStringEquals(className,DFTabClass)) return DFCreateElement(put->contentDoc,WORD_TAB); return NULL; } default: return NULL; } }
static void adjustMarginLeft(DFNode *element, double adjustPct, int noTextIndent) { if ((element->tag != HTML_TABLE) && !HTML_isParagraphTag(element->tag)) return;; const char *cssText = DFGetAttribute(element,HTML_STYLE); CSSProperties *properties = CSSPropertiesNewWithString(cssText); double oldMarginLeft = 0; if (CSSGet(properties,"margin-left") != NULL) { CSSLength length = CSSLengthFromString(CSSGet(properties,"margin-left")); if (CSSLengthIsValid(length) && (length.units == UnitsPct)) oldMarginLeft = length.value; if (CSSGet(properties,"width") != NULL) { CSSLength length = CSSLengthFromString(CSSGet(properties,"width")); if (CSSLengthIsValid(length) && (length.units == UnitsPct)) { double oldWidth = length.value; double newWidth = oldWidth + oldMarginLeft; char buf[100]; CSSPut(properties,"width",DFFormatDoublePct(buf,100,newWidth)); } } } double oldTextIndent = 0; if (CSSGet(properties,"text-indent") != NULL) { CSSLength length = CSSLengthFromString(CSSGet(properties,"text-indent")); if (CSSLengthIsValid(length) && (length.units == UnitsPct)) oldTextIndent = length.value; } double newMarginLeft = oldMarginLeft + adjustPct; double newTextIndent = oldTextIndent; if (newMarginLeft < 0) newMarginLeft = 0; if (fabs(newMarginLeft) >= 0.01) { char buf[100]; CSSPut(properties,"margin-left",DFFormatDoublePct(buf,100,newMarginLeft)); } else { CSSPut(properties,"margin-left",NULL); } if (noTextIndent) { CSSPut(properties,"text-indent",NULL); } else if (newTextIndent < -newMarginLeft) { // Don't allow negative text-indent newTextIndent = -newMarginLeft; if (fabs(newTextIndent) >= 0.01) { char buf[100]; CSSPut(properties,"text-indent",DFFormatDoublePct(buf,100,newTextIndent)); } else { CSSPut(properties,"text-indent",NULL); } } char *propertiesText = CSSPropertiesCopyDescription(properties); if (strlen(propertiesText) == 0) DFRemoveAttribute(element,HTML_STYLE); else DFSetAttribute(element,HTML_STYLE,propertiesText); free(propertiesText); CSSPropertiesRelease(properties); }
void DFVFormatAttribute(DFNode *element, Tag tag, const char *format, va_list ap) { char *value = DFVFormatString(format,ap); DFSetAttribute(element,tag,value); free(value); }
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); }
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); }
static void SAXStartElementNS(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes) { DFSAXParser *parser = (DFSAXParser *)ctx; if (parser->ignoreDepth > 0) { parser->ignoreDepth++; return; } for (int i = 0; i < nb_namespaces; i++) { const xmlChar *nsPrefix = namespaces[i*2]; const xmlChar *nsURI = namespaces[i*2+1]; DFNameMapFoundNamespace(parser->document->map,(const char *)nsURI,(const char *)nsPrefix); } Tag tag = DFNameMapTagForName(parser->document->map,(const char *)URI,(const char *)localname); if (parser->compatibility != NULL) { const TagDecl *tagDecl = DFNameMapNameForTag(parser->document->map,tag); MCAction action = DFMarkupCompatibilityLookup(parser->compatibility,tagDecl->namespaceID,tag,1); if (action == MCActionIgnore) { parser->ignoreDepth++; return; } } if (parser->compatibility != NULL) { DFMarkupCompatibilityPush(parser->compatibility,nb_namespaces,(const char **)namespaces,parser->document->map); } DFNode *element = DFCreateElement(parser->document,tag); for (int i = 0; i < nb_attributes; i++) { const xmlChar *attrLocalName = attributes[i*5+0]; const xmlChar *attrURI = attributes[i*5+2]; const xmlChar *attrValueStart = attributes[i*5+3]; const xmlChar *attrValueEnd = attributes[i*5+4]; unsigned long attrValueLen = (unsigned long)(attrValueEnd - attrValueStart); Tag attrTag = DFNameMapTagForName(parser->document->map,(const char *)attrURI,(const char *)attrLocalName); const TagDecl *attrTagDecl = DFNameMapNameForTag(parser->document->map,attrTag); char *attrValue = (char *)xmalloc(attrValueLen+1); memcpy(attrValue,attrValueStart,attrValueLen); attrValue[attrValueLen] = '\0'; if (parser->compatibility != NULL) { switch (attrTag) { case MC_IGNORABLE: case MC_PROCESSCONTENT: case MC_MUSTUNDERSTAND: DFMarkupCompatibilityProcessAttr(parser->compatibility,attrTag,attrValue,parser->document->map); break; default: { MCAction action = DFMarkupCompatibilityLookup(parser->compatibility,attrTagDecl->namespaceID,0,0); if (action != MCActionIgnore) DFSetAttribute(element,attrTag,attrValue); break; } } } else { DFSetAttribute(element,attrTag,attrValue); } free(attrValue); } DFAppendChild(parser->parent,element); parser->parent = element; if (parser->document->root == NULL) parser->document->root = element; }
static void fixWordLists(DFNode *node, WordConverter *conv) { for (DFNode *child = node->first; child != NULL; child = child->next) fixWordLists(child,conv); int haveParagraphs = 0; for (DFNode *child = node->first; child != NULL; child = child->next) { if (child->tag == WORD_P) { haveParagraphs = 1; break; } } if (!haveParagraphs) return; int createdHashTables = 0; DFHashTable *replacementNumIds = NULL; DFHashTable *itemNoByListKey = NULL; DFHashTable *lastNumIdByIlvl = NULL; DFHashTable *itemNoByIlvl = NULL; int maxIlvl = -1; for (DFNode *child = node->first; child != NULL; child = child->next) { if (child->tag != WORD_P) continue; DFNode *pPrElem = DFChildWithTag(child,WORD_PPR); DFNode *numPrElem = DFChildWithTag(pPrElem,WORD_NUMPR); DFNode *numIdElem = DFChildWithTag(numPrElem,WORD_NUMID); DFNode *ilvlElem = DFChildWithTag(numPrElem,WORD_ILVL); const char *numId = DFGetAttribute(numIdElem,WORD_VAL); const char *ilvl = DFGetAttribute(ilvlElem,WORD_VAL); if ((numId == NULL) || (atoi(numId) == 0)) continue; if (!createdHashTables) { replacementNumIds = DFHashTableNew((DFCopyFunction)xstrdup,free); itemNoByListKey = DFHashTableNew((DFCopyFunction)xstrdup,free); lastNumIdByIlvl = DFHashTableNew((DFCopyFunction)xstrdup,free); itemNoByIlvl = DFHashTableNew((DFCopyFunction)xstrdup,free); createdHashTables = 1; } if (ilvl == NULL) ilvl = "0";; WordConcreteNum *concreteNum = WordNumberingConcreteWithId(conv->numbering,numId); // may be NULL WordNumLevel *numLevel = WordConcreteNumGetLevel(concreteNum,atoi(ilvl)); // may be NULL const char *levelStart = NULL; if (numLevel != NULL) { for (DFNode *lvlChild = numLevel->element->first; lvlChild != NULL; lvlChild = lvlChild->next) { switch (lvlChild->tag) { case WORD_START: levelStart = DFGetAttribute(lvlChild,WORD_VAL); break; } } } char *listKey = DFFormatString("%s:%s",numId,ilvl); char *itemNo = DFStrDup(DFHashTableLookup(itemNoByListKey,listKey)); if (itemNo == NULL) { itemNo = xstrdup("1"); if ((levelStart != NULL) && (atoi(levelStart) > 1) && (atoi(ilvl) <= maxIlvl)) { const char *prevNumId = DFHashTableLookup(lastNumIdByIlvl,ilvl); const char *prevItemNo = DFHashTableLookup(itemNoByIlvl,ilvl); if ((prevNumId != NULL) && (prevItemNo != NULL)) { DFHashTableAdd(replacementNumIds,numId,prevNumId); free(itemNo); itemNo = DFFormatString("%d",atoi(prevItemNo)+1); } } } else { int intValue = atoi(itemNo); free(itemNo); itemNo = DFFormatString("%d",intValue+1); } DFHashTableAdd(itemNoByListKey,listKey,itemNo); const char *replNumId = DFHashTableLookup(replacementNumIds,numId); if (replNumId != NULL) { numId = replNumId; DFSetAttribute(numIdElem,WORD_VAL,numId); } DFHashTableAdd(lastNumIdByIlvl,ilvl,numId); DFHashTableAdd(itemNoByIlvl,ilvl,itemNo); maxIlvl = atoi(ilvl); free(listKey); free(itemNo); } DFHashTableRelease(replacementNumIds); DFHashTableRelease(itemNoByListKey); DFHashTableRelease(lastNumIdByIlvl); DFHashTableRelease(itemNoByIlvl); }
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); }