void parseContent(const char *content) { DFArray *parts = CSSParseContent(content); printf("parts.count = %zu\n",DFArrayCount(parts)); for (size_t i = 0; i < DFArrayCount(parts); i++) { ContentPart *part = DFArrayItemAt(parts,i); char *quotedValue = DFQuote(part->value); printf("%s %s\n",ContentPartTypeString(part->type),quotedValue); free(quotedValue); } DFArrayRelease(parts); }
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); }
void CSSSheetUpdateFromCSSText(CSSSheet *sheet, const char *cssText) { DFHashTable *rules = DFHashTableNew((DFCopyFunction)DFHashTableRetain,(DFFreeFunction)DFHashTableRelease); CSSParser *parser = CSSParserNew(cssText); DFHashTable *top = CSSParserRules(parser); CSSParserFree(parser); const char **allSelectorsText = DFHashTableCopyKeys(top); for (int i = 0; allSelectorsText[i]; i++) { const char *selectorsText = allSelectorsText[i]; const char *propertiesText = DFHashTableLookup(top,selectorsText); parser = CSSParserNew(selectorsText); DFArray *selectors = CSSParserSelectors(parser); CSSParserFree(parser); if (selectors == NULL) continue; parser = CSSParserNew(propertiesText); DFHashTable *properties = CSSParserProperties(parser); CSSParserFree(parser); if (properties == NULL) { DFArrayRelease(selectors); continue; } for (size_t selIndex = 0; selIndex < DFArrayCount(selectors); selIndex++) { const char *selector = DFArrayItemAt(selectors,selIndex); DFHashTableAdd(rules,selector,properties); } DFHashTableRelease(properties); DFArrayRelease(selectors); } updateFromRawCSSRules(sheet,rules); free(allSelectorsText); DFHashTableRelease(top); DFHashTableRelease(rules); }
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; }
CaptionParts WordBookmarkGetCaptionParts(WordBookmark *bookmark) { CaptionParts parts; parts.beforeNum = 0; parts.num = 0; parts.afterNum = 0; // FIXME: Check if the following line is still relevant with the new bookmarks model if (bookmark->element == NULL) return parts; DFArray *nodes = DFArrayNew(NULL,NULL); findAllNodes(bookmark->element,nodes); for (size_t i = 0; i < DFArrayCount(nodes); i++) { DFNode *node = DFArrayItemAt(nodes,i); if (node->tag == WORD_FLDSIMPLE) { const char *instr = DFGetAttribute(node,WORD_INSTR); if (instr != NULL) { const char **args = Word_parseField(instr); if ((args[0] != NULL) && !strcmp(args[0],"SEQ")) parts.num = 1; free(args); } } else if (node->tag != WORD_BOOKMARK) { if (!parts.num) parts.beforeNum = 1; else parts.afterNum = 1; } } DFArrayRelease(nodes); return parts; }
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 collapseRecursive(DFNode *node, DFHashTable *bookmarksById) { DFNode *next; for (DFNode *child = node->first; child != NULL; child = next) { next = child->next; switch (child->tag) { case WORD_BOOKMARKSTART: case WORD_BOOKMARKEND: { DFArray *startElements = DFArrayNew(NULL,NULL); DFArray *endElements = DFArrayNew(NULL,NULL); DFHashTable *startIds = DFHashTableNew((DFCopyFunction)strdup,(DFFreeFunction)free); DFHashTable *endIds = DFHashTableNew((DFCopyFunction)strdup,(DFFreeFunction)free); DFNode *n; for (n = child; (n != NULL) && ((n->tag == WORD_BOOKMARKSTART) || (n->tag == WORD_BOOKMARKEND)); n = n->next) { if (n->tag == WORD_BOOKMARKSTART) { const char *idValue = DFGetAttribute(n,WORD_ID); if (idValue == NULL) idValue = ""; DFHashTableAdd(startIds,idValue,idValue); DFArrayAppend(startElements,n); } else { const char *idValue = DFGetAttribute(n,WORD_ID); if (idValue == NULL) idValue = ""; DFHashTableAdd(endIds,idValue,idValue); DFArrayAppend(endElements,n); } } next = n; DFArraySort(startElements,bookmarksById,compareStartElements); for (size_t endIndex = 0; endIndex < DFArrayCount(endElements); endIndex++) { DFNode *elem = DFArrayItemAt(endElements,endIndex); const char *endId = DFGetAttribute(elem,WORD_ID); int found = 0; DFNode *ancestor; for (ancestor = elem->parent; (ancestor != NULL) && !found; ancestor = ancestor->parent) { if ((ancestor->tag == WORD_BOOKMARK) && DFStringEquals(DFGetAttribute(ancestor,WORD_ID),endId)) { found = 1; break; } } if (found) { DFNode *before = ancestor->next; DFNode *nnext; for (DFNode *n = child; n != NULL; n = nnext) { nnext = n->next; DFInsertBefore(ancestor->parent,n,before); } } } size_t x = 0; while (x < DFArrayCount(startElements)) { DFNode *element = DFArrayItemAt(startElements,x); const char *bookmarkId = DFGetAttribute(element,WORD_ID); if (bookmarkId == NULL) bookmarkId = ""; if (DFHashTableLookup(endIds,bookmarkId) != NULL) { element->tag = WORD_BOOKMARK; DFArrayRemove(startElements,x); } else { x++; } } if (DFArrayCount(startElements) > 0) { for (size_t i = 1; i < DFArrayCount(startElements); i++) { DFNode *tempParent = DFArrayItemAt(startElements,i-1); DFNode *tempChild = DFArrayItemAt(startElements,i); DFAppendChild(tempParent,tempChild); } DFNode *last = DFArrayItemAt(startElements,DFArrayCount(startElements)-1); while (next != NULL) { DFNode *tempChild = next; next = next->next; DFAppendChild(last,tempChild); } } for (size_t eIndex = 0; eIndex < DFArrayCount(startElements); eIndex++) { DFNode *e = DFArrayItemAt(startElements,eIndex); e->tag = WORD_BOOKMARK; } for (size_t eIndex = 0; eIndex < DFArrayCount(endElements); eIndex++) { DFNode *e = DFArrayItemAt(endElements,eIndex); DFRemoveNode(e); } if (DFArrayCount(startElements) > 0) { DFNode *last = DFArrayItemAt(startElements,DFArrayCount(startElements)-1); collapseRecursive(last,bookmarksById); } DFArrayRelease(startElements); DFArrayRelease(endElements); DFHashTableRelease(startIds); DFHashTableRelease(endIds); break; } default: collapseRecursive(child,bookmarksById); break; } } }
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); }