static char *genImageFilename(const char *mediaDir, const char *extension, DFError **error) { const char **names = DFContentsOfDirectory(mediaDir,0,error); if (names == NULL) return NULL; DFHashTable *existingNames = DFHashTableNew((DFCopyFunction)strdup,free); for (int i = 0; names[i]; i++) { const char *filename = names[i]; char *lowerFilename = DFLowerCase(filename); char *noExtension = DFPathWithoutExtension(lowerFilename); DFHashTableAdd(existingNames,noExtension,noExtension); free(lowerFilename); free(noExtension); } int num = 1; char *candidate = NULL; do { free(candidate); candidate = DFFormatString("image%d",num); num++; } while (DFHashTableLookup(existingNames,candidate) != NULL); char *result = DFFormatString("%s.%s",candidate,extension); free(candidate); free(names); DFHashTableRelease(existingNames); return result; }
static OPCRelationship *addImageRelationship(WordConverter *converter, const char *src, DFError **error) { const char *mediaDir = DFAppendPathComponent(converter->concretePath,"word/media"); if (!DFFileExists(mediaDir) && !DFCreateDirectory(mediaDir,1,error)) return NULL; char *ext = DFPathExtension(src); char *filename = genImageFilename(mediaDir,ext,error); free(ext); if (filename == NULL) return NULL; char *abstractPathSlash = DFFormatString("%s/",converter->abstractPath); char *unescapedSrc = DFRemovePercentEncoding(src); char *srcPath = DFPathResolveAbsolute(abstractPathSlash,unescapedSrc); char *destPath = DFAppendPathComponent(mediaDir,filename); OPCRelationship *result = NULL; if (DFCopyFile(srcPath,destPath,error)) { OPCRelationshipSet *rels = converter->package->documentPart->relationships; char *relPath = DFFormatString("/word/media/%s",filename); result = OPCRelationshipSetAddType(rels,WORDREL_IMAGE,relPath,0); free(relPath); } free(filename); free(abstractPathSlash); free(unescapedSrc); free(srcPath); free(destPath); return result; }
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; }
DFHashTable *CSSSheetRules(CSSSheet *sheet) { DFHashTable *result = DFHashTableNew((DFCopyFunction)DFHashTableRetain,(DFFreeFunction)DFHashTableRelease); const char **allSelectors = CSSSheetCopySelectors(sheet); for (int selIndex = 0; allSelectors[selIndex]; selIndex++) { const char *selector = allSelectors[selIndex]; CSSStyle *origStyle = CSSSheetLookupSelector(sheet,selector,0,0); if ((origStyle == NULL) || origStyle->latent) continue; char *elementName = CSSSelectorCopyElementName(selector); char *className = CSSSelectorCopyClassName(selector); char *baseSelector; if (className != NULL) { char *escapedClassName = CSSEscapeIdent(className); baseSelector = DFFormatString("%s.%s",elementName,escapedClassName); free(escapedClassName); } else { baseSelector = xstrdup(elementName); } CSSStyle *flattenedStyle = CSSSheetFlattenedStyle(sheet,origStyle); const char **allSuffixes = CSSStyleCopySuffixes(flattenedStyle); for (int suffixIndex = 0; allSuffixes[suffixIndex]; suffixIndex++) { const char *suffix = allSuffixes[suffixIndex]; char *fullSelector = DFFormatString("%s%s",baseSelector,suffix); CSSProperties *properties = CSSStyleRuleForSuffix(flattenedStyle,suffix); DFHashTable *collapsed = CSSCollapseProperties(properties); if (!((DFHashTableCount(collapsed) == 0) && ((strlen(suffix) > 0) || CSSIsBuiltinSelector(selector)))) DFHashTableAdd(result,fullSelector,collapsed); free(fullSelector); DFHashTableRelease(collapsed); } free(allSuffixes); CSSStyleRelease(flattenedStyle); free(elementName); free(className); free(baseSelector); } free(allSelectors); return result; }
static char *computeDocumentRelsPath(const char *documentPath) { char *documentParent = DFPathDirName(documentPath); char *documentFilename = DFPathBaseName(documentPath); char *documentRelsPath = DFFormatString("%s/_rels/%s.rels",documentParent,documentFilename); free(documentParent); free(documentFilename); return documentRelsPath; }
char *Word_toPlain(DFStorage *rawStorage, DFHashTable *parts) { DFError *error = NULL; char *result = Word_toPlainFromDir(rawStorage,parts,&error); if (result == NULL) { result = DFFormatString("%s\n",DFErrorMessage(&error)); DFErrorRelease(error); } return result; }
static void updateFromRawCSSRules(CSSSheet *sheet, DFHashTable *rules) { // FIXME: Handle class names containing escape sequences DFHashTableRelease(sheet->_styles); sheet->_styles = DFHashTableNew((DFCopyFunction)CSSStyleRetain,(DFFreeFunction)CSSStyleRelease); const char **sortedSelectors = DFHashTableCopyKeys(rules); DFSortStringsCaseInsensitive(sortedSelectors); for (int selIndex = 0; sortedSelectors[selIndex]; selIndex++) { const char *constSelector = sortedSelectors[selIndex]; // Treat any selectors specifying the class name only as paragraph styles char *selector; if (!strncmp(constSelector,".",1)) selector = DFFormatString("p%s",constSelector); // FIXME: Not covered by tests else selector = xstrdup(constSelector); DFHashTable *raw = DFHashTableLookup(rules,constSelector); char *baseId = NULL; char *suffix = NULL; CSSParseSelector(selector,&baseId,&suffix); CSSStyle *style = CSSSheetLookupSelector(sheet,baseId,0,0); if (style == NULL) { style = CSSStyleNew(baseId); CSSSheetAddStyle(sheet,style); CSSStyleRelease(style); } CSSProperties *properties = CSSStyleRuleForSuffix(style,suffix); CSSProperties *expanded = CSSPropertiesNewWithRaw(raw); const char **allNames = CSSPropertiesCopyNames(expanded); for (int nameIndex = 0; allNames[nameIndex]; nameIndex++) { const char *name = allNames[nameIndex]; CSSPut(properties,name,CSSGet(expanded,name)); } free(allNames); if (!strcmp(suffix,"")) { const char *defaultVal = CSSGet(properties,"-uxwrite-default"); if ((defaultVal != NULL) && DFStringEqualsCI(defaultVal,"true")) CSSSheetSetDefaultStyle(sheet,style,StyleFamilyFromHTMLTag(style->tag)); } CSSPropertiesRelease(expanded); free(baseId); free(suffix); free(selector); } free(sortedSelectors); removeRedundantProperties(sheet); }
static int getImageFile(WordConverter *converter, const char *src, PixelSize *size) { size->widthPx = 0; size->heightPx = 0; char *unescapedSrc = DFRemovePercentEncoding(src); char *abstractPathSlash = DFFormatString("%s/",converter->abstractPath); char *newSrcPath = DFPathResolveAbsolute(abstractPathSlash,unescapedSrc); int ok = DFPlatformGetImageDimensions(newSrcPath,&size->widthPx,&size->heightPx); free(abstractPathSlash); free(unescapedSrc); free(newSrcPath); return ok; }
static void disableNumberingForStyle(CSSStyle *style, int explicitly) { CSSProperties *rule = CSSStyleRule(style); CSSProperties *before = CSSStyleBefore(style); if (explicitly) { // FIXME: Not covered by tests char *increment = DFFormatString("h%d 0",style->headingLevel); CSSPut(rule,"counter-increment",increment); CSSPut(rule,"counter-reset","null"); CSSPut(before,"content","\"\""); style->latent = 0; free(increment); } else { CSSPut(rule,"counter-increment",NULL); CSSPut(rule,"counter-reset",NULL); CSSPut(before,"content",NULL); } }
static void parseDocumentRels(const char *documentPath, DFDocument *relsDoc, DFHashTable *rels, DFError **error) { if (relsDoc == NULL) return;; const char *basePrefix = (documentPath[0] == '/') ? "" : "/"; char *basePath = DFFormatString("%s%s",basePrefix,documentPath); for (DFNode *child = relsDoc->root->first; child != NULL; child = child->next) { if (child->tag != REL_RELATIONSHIP) continue; const char *type = DFGetAttribute(child,NULL_Type); const char *target = DFGetAttribute(child,NULL_TARGET); if ((type == NULL) || (target == NULL)) continue; char *absTarget = DFPathResolveAbsolute(basePath,target); DFHashTableAdd(rels,type,absTarget); free(absTarget); } free(basePath); }
DFNode *WordDrawingGet(WordGetData *get, DFNode *concrete) { ImageInfo *info = getImageInfo(concrete); if (info == NULL) return createAbstractPlaceholder(get,"[Unsupported object]",concrete); int validFileType = 0; const char *filename = WordPackageTargetForDocumentRel(get->conv->package,info->rId); if (filename != NULL) { char *origExtension = DFPathExtension(filename); char *lowerExtension = DFLowerCase(origExtension); if (DFHashTableLookup(get->conv->supportedContentTypes,lowerExtension) != NULL) validFileType = 1; free(origExtension); free(lowerExtension); } DFNode *result = NULL; if (!validFileType) { if (info->progId != NULL) { char *message = DFFormatString("[Unsupported object: %s]",info->progId); DFNode *placeholder = createAbstractPlaceholder(get,message,concrete); free(message); result = placeholder; } else { result = createAbstractPlaceholder(get,"[Unsupported object]",concrete); } } if (result == NULL) result = imageWithFilename(get,filename,info->widthPts,concrete); ImageInfoFree(info); return result; }
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); }
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 int processParts(DFHashTable *parts, const char *documentPath, DFDocument *relsDoc, DFHashTable *documentRels, DFBuffer *output, DFStorage *storage, DFError **error) { int ok = 0; DFHashTable *includeTypes = DFHashTableNew((DFCopyFunction)xstrdup,free); DFHashTableAdd(includeTypes,WORDREL_HYPERLINK,""); DFHashTableAdd(includeTypes,WORDREL_IMAGE,""); if ((parts == NULL) || (DFHashTableLookup(parts,"document") != NULL)) { DFDocument *doc = DFParseXMLStorage(storage,documentPath,error); if (doc == NULL) goto end; addStrippedSerializedDoc(output,doc,"document.xml"); DFDocumentRelease(doc); } if ((parts == NULL) || (DFHashTableLookup(parts,"styles") != NULL)) { if (!addRelatedDoc(parts,documentRels,WORDREL_STYLES,"styles.xml",output,includeTypes,storage,error)) goto end; } if ((parts == NULL) || (DFHashTableLookup(parts,"numbering") != NULL)) { if (!addRelatedDoc(parts,documentRels,WORDREL_NUMBERING,"numbering.xml",output,includeTypes,storage,error)) goto end; } if ((parts == NULL) || (DFHashTableLookup(parts,"footnotes") != NULL)) { if (!addRelatedDoc(parts,documentRels,WORDREL_FOOTNOTES,"footnotes.xml",output,includeTypes,storage,error)) goto end; } if ((parts == NULL) || (DFHashTableLookup(parts,"endnotes") != NULL)) { if (!addRelatedDoc(parts,documentRels,WORDREL_ENDNOTES,"endnotes.xml",output,includeTypes,storage,error)) goto end; } if ((parts != NULL) && (DFHashTableLookup(parts,"settings") != NULL)) { if (!addRelatedDoc(parts,documentRels,WORDREL_SETTINGS,"settings.xml",output,includeTypes,storage,error)) goto end; } if ((parts != NULL) && (DFHashTableLookup(parts,"theme") != NULL)) { if (!addRelatedDoc(parts,documentRels,WORDREL_THEME,"theme.xml",output,includeTypes,storage,error)) goto end; } if ((DFHashTableLookup(documentRels,WORDREL_HYPERLINK) != NULL) || (DFHashTableLookup(documentRels,WORDREL_IMAGE) != NULL) || ((parts != NULL) && (DFHashTableLookup(parts,"documentRels") != NULL))) { if (relsDoc == NULL) { DFErrorFormat(error,"document.xml.rels does not exist"); goto end; } DFNode *next; for (DFNode *child = relsDoc->root->first; child != NULL; child = next) { next = child->next; if (child->tag != REL_RELATIONSHIP) continue; const char *type = DFGetAttribute(child,NULL_Type); if ((type != NULL) && (DFHashTableLookup(includeTypes,type) == NULL)) { DFRemoveNode(child); } } addSerializedDoc(output,relsDoc,"document.xml.rels"); } const char **entries = DFStorageList(storage,NULL); if (entries != NULL) { // FIXME: Should really report an error if this is not the case for (int i = 0; entries[i]; i++) { const char *filename = entries[i]; char *extension = DFPathExtension(filename); if (DFStringEqualsCI(extension,"png") || DFStringEqualsCI(extension,"jpg")) { char *absFilename; if (!DFStringHasSuffix(filename,"/")) absFilename = DFFormatString("/%s",filename); else absFilename = xstrdup(filename); DFBuffer *data = DFBufferReadFromStorage(storage,absFilename,NULL); addSerializedBinary(output,data,absFilename); DFBufferRelease(data); free(absFilename); } free(extension); } } free(entries); DFHashTableRelease(includeTypes); ok = 1; end: return ok; }
// FIXME: This won't work now for Word documents, where styles always have class names // FIXME: Not covered by tests void CSSSheetSetHeadingNumbering(CSSSheet *sheet, int enabled) { CSSStyle *defaultStyle = CSSSheetDefaultStyleForFamily(sheet,StyleFamilyParagraph); if (enabled) { DFHashTable *stylesByHeadingLevel = getStylesByHeadingLevel(sheet); for (int level = 1; level <= 6; level++) { StyleList *styles = DFHashTableLookupInt(stylesByHeadingLevel,level); int haveClassless = 0; for (StyleList *item = styles; item != NULL; item = item->next) { if (item->style->className == NULL) haveClassless = 1; } // If there exists a style at level n which has no class name, then that is the parent of all other // heading styles. Set the numbering on that. Alternatively, if there no styles exist at level n, then // create a new one with no class name, and set the numbering information on that. if ((styles == NULL) || haveClassless) { char *elementName = DFFormatString("h%d",level); CSSStyle *style = CSSSheetLookupElement(sheet,elementName,NULL,1,0); enableNumberingForStyle(style); free(elementName); } else { // There are multiple heading styles at level n, all of which have a class name associated with them. // Set the numbering information on those which either have no parentId, or whose parentId is the // default paragraph style. This caters for situations like the "HeadingXUnnumbered" styles we used // to have, which were based on the corresponding "HeadingX" style. for (StyleList *item = styles; item != NULL; item = item->next) { char *parentSelector = CSSStyleCopyParent(item->style); if ((parentSelector == NULL) || !strcmp(parentSelector,defaultStyle->selector)) { enableNumberingForStyle(item->style); } else { disableNumberingForStyle(item->style,1); } free(parentSelector); } } StyleList *next; for (; styles != NULL; styles = next) { next = styles->next; CSSStyleRelease(styles->style); free(styles); } } DFHashTableRelease(stylesByHeadingLevel); } else { // Clear all numbering information on all heading styles const char **allSelectors = CSSSheetCopySelectors(sheet); for (int i = 0; allSelectors[i]; i++) { CSSStyle *style = CSSSheetLookupSelector(sheet,allSelectors[i],0,0); if (style->headingLevel > 0) { CSSPut(CSSStyleRule(style),"counter-increment",NULL); CSSPut(CSSStyleRule(style),"counter-reset",NULL); CSSPut(CSSStyleBefore(style),"content",NULL); } } free(allSelectors); } }
static void WordGetStyle(DFNode *concrete, CSSStyle *style, WordConverter *converter) { WordSection *section = converter->mainSection; for (DFNode *child = concrete->first; child != NULL; child = child->next) { const char *styleId = NULL; switch (child->tag) { case WORD_NAME: CSSStyleSetDisplayName(style,DFGetAttribute(child,WORD_VAL)); break; case WORD_NEXT: { // FIXME: do he need to handle style types other than paragraph here? const char *nextName = DFGetAttribute(child,WORD_VAL); if (nextName == NULL) continue; WordStyle *nextStyle = WordSheetStyleForTypeId(converter->styles,"paragraph",nextName); if (nextStyle == NULL) continue; CSSStyleSetNext(style,nextStyle->selector); break; } case WORD_RPR: WordGetRPr(child,CSSStyleRule(style),&styleId,converter->theme); break; case WORD_PPR: { WordGetPPr(child,CSSStyleRule(style),&styleId,section); if (style->headingLevel > 0) { DFNode *numPr = DFChildWithTag(child,WORD_NUMPR); if (numPr != NULL) WordGetNumPrStyle(numPr,style,converter); } break; } case WORD_TBLPR: WordGetTblPr(child,CSSStyleRule(style),CSSStyleCell(style),section,&styleId); break; case WORD_TRPR: // FIXME break; case WORD_TCPR: WordGetTcPr(child,CSSStyleRule(style)); break; case WORD_TBLSTYLEPR: WordGetTblStylePr(child,style,section,converter->theme); break; } } // Special case: The ListParagraph style that word automatically adds specifies an indentation // of 36pt. We don't actually want this, because HTML automatically indents lists anyway. If // we see this, clear it, but keep the old value around for when we update the word document. StyleFamily family = WordStyleFamilyForSelector(style->selector); const char *name = WordSheetStyleIdForSelector(converter->styles,style->selector); if ((family == StyleFamilyParagraph) && DFStringEquals(name,"ListParagraph")) { CSSProperties *properties = CSSStyleRule(style); const char *wordMarginLeft = CSSGet(properties,"margin-left"); CSSPut(properties,"-word-margin-left",wordMarginLeft); CSSPut(properties,"margin-left",NULL); } DFNode *pPr = DFChildWithTag(concrete,WORD_PPR); DFNode *numPr = DFChildWithTag(pPr,WORD_NUMPR); const char *numId = DFGetChildAttribute(numPr,WORD_NUMID,WORD_VAL); if ((numId != NULL) && (atoi(numId) == 0)) { switch (style->tag) { case HTML_H1: case HTML_H2: case HTML_H3: case HTML_H4: case HTML_H5: case HTML_H6: case HTML_FIGURE: case HTML_TABLE: { char *counterIncrement = DFFormatString("%s 0",style->elementName); CSSPut(CSSStyleRule(style),"counter-reset","null"); CSSPut(CSSStyleRule(style),"counter-increment",counterIncrement); CSSPut(CSSStyleBefore(style),"content","none"); free(counterIncrement); } } } }
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); }
char *WordStyleIdForStyle(CSSStyle *style) { const char *selector = style->selector; char *resStyleId = NULL; if (!strcmp(selector,"table.Normal_Table")) return strdup("TableNormal"); if (!strcmp(selector,"table.Table_Grid")) return strdup("TableGrid"); if (!strcmp(selector,"span.Default_Paragraph_Font")) return strdup("DefaultParagraphFont"); if (!strcmp(selector,"p.List_Paragraph")) return strdup("ListParagraph"); int headingLevel = CSSSelectorHeadingLevel(selector); if (headingLevel != 0) { char *prefix = DFFormatString("heading_%d",headingLevel); if ((style->className != NULL) && DFStringHasPrefix(style->className,prefix)) { char *rest = DFSubstring(style->className,strlen(prefix),strlen(style->className)); char *result = DFFormatString("Heading%d%s",headingLevel,rest); free(rest); free(prefix); return result; } free(prefix); } if (!strcmp(selector,"span.Heading1Char")) return strdup("Heading1Char"); if (!strcmp(selector,"span.Heading2Char")) return strdup("Heading2Char"); if (!strcmp(selector,"span.Heading3Char")) return strdup("Heading3Char"); if (!strcmp(selector,"span.Heading4Char")) return strdup("Heading4Char"); if (!strcmp(selector,"span.Heading5Char")) return strdup("Heading5Char"); if (!strcmp(selector,"span.Heading6Char")) return strdup("Heading6Char"); if (!strcmp(selector,"span.Heading7Char")) return strdup("Heading7Char"); if (!strcmp(selector,"span.Heading8Char")) return strdup("Heading8Char"); if (!strcmp(selector,"span.Heading9Char")) return strdup("Heading9Char"); char *className = CSSSelectorCopyClassName(selector); switch (CSSSelectorGetTag(selector)) { case HTML_FIGURE: { resStyleId = DFStrDup("Figure"); break; } case HTML_CAPTION: { resStyleId = DFStrDup("Caption"); break; } case HTML_H1: case HTML_H2: case HTML_H3: case HTML_H4: case HTML_H5: case HTML_H6: { if ((className == NULL) || (strlen(className) == 0)) { int level = CSSSelectorHeadingLevel(selector); if ((level >= 1) && (level <= 6)) { // FIXME: we shouldn't rely on the specific word "Heading" here - instead using the localised name // FIXME: not covered by tests resStyleId = DFFormatString("Heading%d",level); } } else { resStyleId = DFStrDup(className); } break; } case HTML_P: resStyleId = DFStrDup(className); break; case HTML_SPAN: resStyleId = DFStrDup(className); break; case HTML_TABLE: resStyleId = DFStrDup(className); break; } free(className); if (resStyleId == NULL) { // Note: selector here may start with . (i.e. applies to all elements) // FIXME: not covered by tests resStyleId = strdup(selector); } return resStyleId; }