/** * Creates document footer including page number * * @param doc WordprocessingMLPackage for the document. * @throws InvalidFormatException */ private void createFooter(WordprocessingMLPackage doc) throws InvalidFormatException { MainDocumentPart content = doc.getMainDocumentPart(); // Create footer FooterPart footer = new FooterPart(); Ftr ftr = objectFactory.createFtr(); P footerParagraph = objectFactory.createP(); setStyle(footerParagraph, "footer"); PPr parProps = objectFactory.createPPr(); Jc al = objectFactory.createJc(); al.setVal(JcEnumeration.RIGHT); parProps.setJc(al); footerParagraph.setPPr(parProps); // Add field start R run = objectFactory.createR(); FldChar fldChar = objectFactory.createFldChar(); fldChar.setFldCharType(STFldCharType.BEGIN); run.getContent().add(fldChar); footerParagraph.getContent().add(run); // Add pageNumber field run = objectFactory.createR(); Text txt = objectFactory.createText(); txt.setSpace("preserve"); txt.setValue(" PAGE \\* MERGEFORMAT "); run.getContent().add(objectFactory.createRInstrText(txt)); footerParagraph.getContent().add(run); // Add field end run = objectFactory.createR(); fldChar = objectFactory.createFldChar(); fldChar.setFldCharType(STFldCharType.END); run.getContent().add(fldChar); footerParagraph.getContent().add(run); ftr.getContent().add(footerParagraph); footer.setJaxbElement(ftr); Relationship rel = content.addTargetPart(footer); // Relate footer to document List<SectionWrapper> sections = doc.getDocumentModel().getSections(); SectPr sectPr = sections.get(sections.size() - 1).getSectPr(); if (null == sectPr) { sectPr = objectFactory.createSectPr(); content.addObject(sectPr); sections.get(sections.size() - 1).setSectPr(sectPr); } FooterReference footerReference = objectFactory.createFooterReference(); footerReference.setId(rel.getId()); footerReference.setType(HdrFtrRef.DEFAULT); sectPr.getEGHdrFtrReferences().add(footerReference); }
/** * Sets style of paragraph * * @param paragraph Paragraph to style * @param styleName Style name */ private void setStyle(ContentAccessor element, String styleName) { if (element instanceof P) { PPrBase.PStyle style = objectFactory.createPPrBasePStyle(); style.setVal(styleName); PPr ppr = objectFactory.createPPr(); ppr.setPStyle(style); ((P) element).setPPr(ppr); } else if (element instanceof Tbl) { TblPr.TblStyle style = objectFactory.createCTTblPrBaseTblStyle(); style.setVal(styleName); TblPr tpr = objectFactory.createTblPr(); CTTblLook tblLook = objectFactory.createCTTblLook(); tblLook.setFirstColumn(STOnOff.FALSE); tblLook.setFirstRow(STOnOff.TRUE); tblLook.setLastColumn(STOnOff.FALSE); tblLook.setLastRow(STOnOff.FALSE); tblLook.setNoHBand(STOnOff.FALSE); tblLook.setNoVBand(STOnOff.TRUE); tpr.setTblLook(tblLook); tpr.setTblStyle(style); ((Tbl) element).setTblPr(tpr); } }
// create paraghraph with no space after private P createParagraph() { P paragraph = factory.createP(); PPr pPr = factory.createPPr(); Spacing spacing = new Spacing(); spacing.setAfter(BigInteger.ZERO); pPr.setSpacing(spacing); paragraph.setPPr(pPr); return paragraph; }
/** * Fija la alineación de un párrafo * * @param paragraph Párrafo a alinear * @param alignment Valor de alineamiento */ private void alignParagraph(P paragraph, JcEnumeration alignment) { PPr parProps = paragraph.getPPr(); if (null == parProps) parProps = objectFactory.createPPr(); Jc al = objectFactory.createJc(); al.setVal(alignment); parProps.setJc(al); paragraph.setPPr(parProps); }
/** * Follow the resolution rules to return the paragraph properties which actually apply, given this * pPr element (on a w:p). * * <p>Note 1: the properties are not the definition of any style name returned. Note 2: run * properties are not resolved or returned by this method. * * <p>What is returned is a live object. If you want to change it, you should clone it first! * * @param expressPPr * @return */ public PPr getEffectivePPr(PPr expressPPr) { PPr effectivePPr = null; // First, the document defaults are applied // Done elsewhere // PPr effectivePPr = (PPr)XmlUtils.deepCopy(documentDefaultPPr); // Next, the table style properties are applied to each table in the document, // following the conditional formatting inclusions and exclusions specified // per table. // TODO - if the paragraph is in a table? // Next, numbered item and paragraph properties are applied to each paragraph // formatted with a *numbering *style**. // TODO - who uses numbering styles (as opposed to numbering // via a paragraph style or direct formatting)? // Next, paragraph and run properties are // applied to each paragraph as defined by the paragraph style. PPr resolvedPPr = null; String styleId; if (expressPPr == null || expressPPr.getPStyle() == null) { // styleId = "Normal"; styleId = defaultParagraphStyleId; } else { styleId = expressPPr.getPStyle().getVal(); } resolvedPPr = getEffectivePPr(styleId); // Next, run properties are applied to each run with a specific character style // applied. // Not for pPr // Finally, we apply direct formatting (paragraph or run properties not from // styles). if (hasDirectPPrFormatting(expressPPr)) { if (resolvedPPr == null) { log.warn("resolvedPPr was null. Look into this?"); effectivePPr = Context.getWmlObjectFactory().createPPr(); } else { effectivePPr = (PPr) XmlUtils.deepCopy(resolvedPPr); } applyPPr(expressPPr, effectivePPr); return effectivePPr; } else { return resolvedPPr; } }
private void setHorizontalAlignment(P paragraph, JcEnumeration hAlign) { if (hAlign != null) { PPr pprop = paragraph.getPPr(); if (pprop == null) { pprop = new PPr(); paragraph.setPPr(pprop); } Jc align = new Jc(); align.setVal(hAlign); pprop.setJc(align); paragraph.setPPr(pprop); } }
private Lvl createLevel(int level, Map<String, CSSValue> cssMap) { if (level > 8) level = 8; // Word can't open a document with Ilvl>8 // Create object for lvl Lvl lvl = wmlObjectFactory.createLvl(); lvl.setIlvl(BigInteger.valueOf(level)); // // Create object for pStyle // Lvl.PStyle lvlpstyle = wmlObjectFactory.createLvlPStyle(); // lvl.setPStyle(lvlpstyle); // lvlpstyle.setVal( "Heading1"); // Create object for pPr PPr ppr = wmlObjectFactory.createPPr(); lvl.setPPr(ppr); ppr.setInd(getInd(getAncestorIndentation())); // Create object for numFmt NumFmt numfmt = wmlObjectFactory.createNumFmt(); lvl.setNumFmt(numfmt); numfmt.setVal(getNumberFormatFromCSSListStyleType(cssMap.get("list-style-type").getCssText())); // Create object for lvlText Lvl.LvlText lvllvltext = wmlObjectFactory.createLvlLvlText(); lvl.setLvlText(lvllvltext); lvllvltext.setVal( getLvlTextFromCSSListStyleType(cssMap.get("list-style-type").getCssText(), level + 1)); // Bullets have an associated font RFonts rfonts = geRFontsForCSSListStyleType(cssMap.get("list-style-type").getCssText()); if (rfonts != null) { RPr rpr = wmlObjectFactory.createRPr(); rpr.setRFonts(rfonts); lvl.setRPr(rpr); } // Create object for lvlJc Jc jc = wmlObjectFactory.createJc(); lvl.setLvlJc(jc); jc.setVal(org.docx4j.wml.JcEnumeration.LEFT); // Create object for start Lvl.Start lvlstart = wmlObjectFactory.createLvlStart(); lvl.setStart(lvlstart); lvlstart.setVal(BigInteger.valueOf(1)); return lvl; }
/** * Create a paragraph containing the string simpleText, styled using the specified style (up to * user to ensure it is a paragraph style) without adding it to the document. * * @param styleId * @param text * @return */ public org.docx4j.wml.P createStyledParagraphOfText(String styleId, String text) { org.docx4j.wml.P p = createParagraphOfText(text); StyleDefinitionsPart styleDefinitionsPart = this.getStyleDefinitionsPart(); if (getPropertyResolver().activateStyle(styleId)) { // Style is available org.docx4j.wml.ObjectFactory factory = Context.getWmlObjectFactory(); org.docx4j.wml.PPr pPr = factory.createPPr(); p.setPPr(pPr); org.docx4j.wml.PPrBase.PStyle pStyle = factory.createPPrBasePStyle(); pPr.setPStyle(pStyle); pStyle.setVal(styleId); } return p; }
public void setParagraphProperties(ParagraphPropertiesML pPr) { if (pPr != null && pPr.getParent() != null) { throw new IllegalArgumentException("Not an orphan."); } this.pPr = pPr; if (this.docxObject instanceof org.docx4j.wml.P) { org.docx4j.wml.PPr newDocxPPr = null; if (pPr != null) { pPr.setParent(ParagraphML.this); newDocxPPr = (org.docx4j.wml.PPr) pPr.getDocxObject(); } ((org.docx4j.wml.P) this.docxObject).setPPr(newDocxPPr); if (newDocxPPr != null) { newDocxPPr.setParent(this.docxObject); } } }
private P createPageNumParagraph() { CTSimpleField pgnum = factory.createCTSimpleField(); pgnum.setInstr(" PAGE \\* MERGEFORMAT "); RPr RPr = factory.createRPr(); RPr.setNoProof(new BooleanDefaultTrue()); PPr ppr = factory.createPPr(); Jc jc = factory.createJc(); jc.setVal(JcEnumeration.CENTER); ppr.setJc(jc); PPrBase.Spacing pprbase = factory.createPPrBaseSpacing(); pprbase.setBefore(BigInteger.valueOf(240)); pprbase.setAfter(BigInteger.valueOf(0)); ppr.setSpacing(pprbase); R run = factory.createR(); run.getContent().add(RPr); pgnum.getContent().add(run); JAXBElement<CTSimpleField> fldSimple = factory.createPFldSimple(pgnum); P para = createParagraph(); para.getContent().add(fldSimple); para.setPPr(ppr); return para; }
protected void setNumbering(PPr pPr, BigInteger numId) { // Create and add <w:numPr> NumPr numPr = Context.getWmlObjectFactory().createPPrBaseNumPr(); pPr.setNumPr(numPr); // The <w:numId> element NumId numIdElement = Context.getWmlObjectFactory().createPPrBaseNumPrNumId(); numPr.setNumId(numIdElement); numIdElement.setVal(numId); // point to the correct list // The <w:ilvl> element Ilvl ilvlElement = Context.getWmlObjectFactory().createPPrBaseNumPrIlvl(); numPr.setIlvl(ilvlElement); ilvlElement.setVal(BigInteger.valueOf(this.listStack.size() - 1)); // TMP: don't let this override our numbering // p.getPPr().setInd(null); }
/** * @param expressRPr * @param pPr - * @return */ public RPr getEffectiveRPr(RPr expressRPr, PPr pPr) { // NB Currently used in PDF viaXSLFO only log.debug("in getEffectiveRPr"); // Idea is that you pass pPr if you are using this for XSL FO, // since we need to take account of rPr in paragraph styles // (but not the rPr in a pPr direct formatting, since // that only applies to the paragraph mark). // * For HTML/CSS, this would be null (since the pPr level rPr // * is made into a separate style applied via a second value in // * the class attribute). But, in the CSS case, this // function is not used - since the rPr is made into a style as well. // First, the document defaults are applied RPr effectiveRPr = (RPr) XmlUtils.deepCopy(documentDefaultRPr); // Apply DefaultParagraphFont. We only do it explicitly // here as per conditions, because if there is a run style, // walking the hierarchy will include this if it is needed if (expressRPr == null || expressRPr.getRStyle() == null) { applyRPr(resolvedStyleRPrComponent.get(defaultCharacterStyleId), effectiveRPr); } // Next, the table style properties are applied to each table in the document, // following the conditional formatting inclusions and exclusions specified // per table. // TODO - if the paragraph is in a table? // Next, numbered item and paragraph properties are applied to each paragraph // formatted with a *numbering *style**. // TODO - who uses numbering styles (as opposed to numbering // via a paragraph style or direct formatting)? // Next, paragraph and run properties are // applied to each paragraph as defined by the paragraph style // (this includes run properties defined in a paragraph style, // but not run properties directly included in a pPr in the // document (those only apply to a paragraph mark). if (pPr == null) { log.debug("pPr was null"); } else { // At the pPr level, what rPr do we have? // .. ascend the paragraph style tree if (pPr.getPStyle() == null) { // log.warn("No pstyle:"); // log.debug(XmlUtils.marshaltoString(pPr, true, true)); } else { log.debug("pstyle:" + pPr.getPStyle().getVal()); RPr pPrLevelRunStyle = getEffectiveRPr(pPr.getPStyle().getVal()); // .. and apply those applyRPr(pPrLevelRunStyle, effectiveRPr); } // Check Paragraph rPr (our special hack of using ParaRPr // to format a fo:block) if ((expressRPr == null) && (pPr.getRPr() != null) && (hasDirectRPrFormatting(pPr.getRPr()))) { applyRPr(pPr.getRPr(), effectiveRPr); } } // Next, run properties are applied to each run with a specific character style // applied. RPr resolvedRPr = null; String runStyleId; if (expressRPr != null && expressRPr.getRStyle() != null) { runStyleId = expressRPr.getRStyle().getVal(); resolvedRPr = getEffectiveRPr(runStyleId); applyRPr(resolvedRPr, effectiveRPr); } // Finally, we apply direct formatting (run properties not from // styles). if (hasDirectRPrFormatting(expressRPr)) { // effectiveRPr = (RPr)XmlUtils.deepCopy(effectiveRPr); applyRPr(expressRPr, effectiveRPr); } return effectiveRPr; }
@Override public void set(PPr pPr) { pPr.setShd((CTShd) this.getObject()); }
@Override public List<Object> apply(Object o) { if (o instanceof org.docx4j.wml.P) { pPr = ((P) o).getPPr(); if (stylesInUse != null) { // do the styles boolean customPStyle = false; if (pPr != null) { if (pPr.getPStyle() != null) { // Note this paragraph style // log.debug("put style " + pPr.getPStyle().getVal()); customPStyle = true; stylesInUse.add(pPr.getPStyle().getVal()); } if ((pPr.getRPr() != null) && (pPr.getRPr().getRStyle() != null)) { // Note this run style // log.debug("put style " + pPr.getRPr().getRStyle().getVal() ); stylesInUse.add(pPr.getRPr().getRStyle().getVal()); } } defaultParagraphStyleUsed = defaultParagraphStyleUsed || (!customPStyle); } } else if (o instanceof org.docx4j.wml.R) { rPr = ((R) o).getRPr(); if (stylesInUse != null) { if (rPr != null) { if (rPr.getRStyle() == null) { defaultCharacterStyleUsed = true; } else { stylesInUse.add(rPr.getRStyle().getVal()); } } } } else if (o instanceof org.docx4j.wml.Text) { if (runFontSelector != null) { // discover the fonts which apply to this text log.debug(((Text) o).getValue()); runFontSelector.fontSelector(pPr, rPr, ((Text) o)); } } else if (o instanceof org.docx4j.wml.R.Sym) { if (fontsDiscovered != null) { org.docx4j.wml.R.Sym sym = (org.docx4j.wml.R.Sym) o; fontsDiscovered.add(sym.getFont()); } } else if (o instanceof org.docx4j.wml.Tbl) { // The table could have a table style; // Tables created in Word 2007 default to table style "TableGrid", // which is based on "TableNormal". org.docx4j.wml.Tbl tbl = (org.docx4j.wml.Tbl) o; if (stylesInUse != null && tbl.getTblPr() != null && tbl.getTblPr().getTblStyle() != null) { // log.debug("Adding table style: " + tbl.getTblPr().getTblStyle().getVal() ); stylesInUse.add(tbl.getTblPr().getTblStyle().getVal()); } // There is no such thing as a tr or a tc style, // so we don't need to look for them, // but since a tc can contain w:p or nested table, // we still need to recurse } return null; }
private boolean hasDirectPPrFormatting(PPr pPrToApply) { // NB, any rPr is intentionally ignored, // since pPr/rPr is not applicable to anything // except the paragraph mark if (pPrToApply == null) { return false; } // Here is where we do the real work. // There are a lot of paragraph properties // The below list is taken directly from PPrBase. // PPrBase.PStyle pStyle; // Ignore // BooleanDefaultTrue keepNext; if (pPrToApply.getKeepNext() != null) { return true; } // BooleanDefaultTrue keepLines; if (pPrToApply.getKeepLines() != null) { return true; } // BooleanDefaultTrue pageBreakBefore; if (pPrToApply.getPageBreakBefore() != null) { return true; } // CTFramePr framePr; // BooleanDefaultTrue widowControl; if (pPrToApply.getWidowControl() != null) { return true; } // PPrBase.NumPr numPr; // NumPr numPr; if (pPrToApply.getNumPr() != null) { return true; } // BooleanDefaultTrue suppressLineNumbers; if (pPrToApply.getSuppressLineNumbers() != null) { return true; } // PBdr pBdr; if (pPrToApply.getPBdr() != null) { return true; } // CTShd shd; if (pPrToApply.getShd() != null) { return true; } // Tabs tabs; if (pPrToApply.getTabs() != null) { return true; } // BooleanDefaultTrue suppressAutoHyphens; // BooleanDefaultTrue kinsoku; // BooleanDefaultTrue wordWrap; // BooleanDefaultTrue overflowPunct; // BooleanDefaultTrue topLinePunct; // BooleanDefaultTrue autoSpaceDE; // BooleanDefaultTrue autoSpaceDN; // BooleanDefaultTrue bidi; // BooleanDefaultTrue adjustRightInd; // BooleanDefaultTrue snapToGrid; // PPrBase.Spacing spacing; if (pPrToApply.getSpacing() != null) { return true; } // PPrBase.Ind ind; if (pPrToApply.getInd() != null) { return true; } // BooleanDefaultTrue contextualSpacing; // BooleanDefaultTrue mirrorIndents; // BooleanDefaultTrue suppressOverlap; // Jc jc; if (pPrToApply.getJc() != null) { return true; } // TextDirection textDirection; // PPrBase.TextAlignment textAlignment; if (pPrToApply.getTextAlignment() != null) { return true; } // CTTextboxTightWrap textboxTightWrap; // PPrBase.OutlineLvl outlineLvl; if (pPrToApply.getOutlineLvl() != null) { return true; } // PPrBase.DivId divId; // CTCnf cnfStyle; return false; }
protected static List<ConversionSectionWrapper> processComplete( WordprocessingMLPackage wmlPackage, Document document, RelationshipsPart rels, BooleanDefaultTrue evenAndOddHeaders, boolean dummyPageNumbering) { List<ConversionSectionWrapper> conversionSections = new ArrayList<ConversionSectionWrapper>(); List<Object> sectionContent = new ArrayList<Object>(); ConversionSectionWrapper currentSectionWrapper = null; HeaderFooterPolicy previousHF = null; int conversionSectionIndex = 0; // According to the ECMA-376 2ed, if type is not specified, read it as next page // However Word 2007 sometimes treats it as continuous, and sometimes doesn't?? // 20130216 Review above comment: ! In the Word UI, the Word "continuous" is shown where it is // effective. // In the XML, it is stored in the next following sectPr. // First, remove content controls, // since the P could be in a content control. // (It is easier to remove content controls, than // to make the code below TraversalUtil based) // RemovalHandler is an XSLT-based way of doing this, // but here we avoid introducing a dependency on // XSLT (Xalan) for PDF output. SdtBlockFinder sbr = new SdtBlockFinder(); new TraversalUtil(document.getContent(), sbr); for (int i = sbr.sdtBlocks.size() - 1; i >= 0; i--) { // Have to process in reverse order // so that parentList is correct for nested sdt SdtBlock sdtBlock = sbr.sdtBlocks.get(i); List<Object> parentList = null; if (sdtBlock.getParent() instanceof ArrayList) { parentList = (ArrayList) sdtBlock.getParent(); } else { log.error("Handle " + sdtBlock.getParent().getClass().getName()); } int index = parentList.indexOf(sdtBlock); parentList.remove(index); parentList.addAll(index, sdtBlock.getSdtContent().getContent()); } // if (log.isDebugEnabled()) { // log.debug(XmlUtils.marshaltoString(document, true, true)); // } // Make a list, so it is easy to look at the following sectPr, // which we need to do to handle continuous sections properly List<SectPr> sectPrs = new ArrayList<SectPr>(); for (Object o : document.getBody().getContent()) { if (o instanceof org.docx4j.wml.P) { if (((org.docx4j.wml.P) o).getPPr() != null) { org.docx4j.wml.PPr ppr = ((org.docx4j.wml.P) o).getPPr(); if (ppr.getSectPr() != null) { sectPrs.add(ppr.getSectPr()); } } } } if (document.getBody().getSectPr() != null) { // usual case sectPrs.add(document.getBody().getSectPr()); } else { log.debug("No body level sectPr in document"); // OK if the last object is w:p and it contains a sectPr. List<Object> all = document.getBody().getContent(); Object last = all.get(all.size() - 1); if (last instanceof P && ((P) last).getPPr() != null && ((P) last).getPPr().getSectPr() != null) { // ok log.debug( ".. but last p contains sectPr .. move it"); // so our assumption later about there // being a following section is correct SectPr thisSectPr = ((P) last).getPPr().getSectPr(); document.getBody().setSectPr(thisSectPr); ((P) last).getPPr().setSectPr(null); sectPrs.remove(thisSectPr); } else { document.getBody().setSectPr(Context.getWmlObjectFactory().createSectPr()); sectPrs.add(document.getBody().getSectPr()); } } int sectPrIndex = 0; // includes continuous ones for (Object o : document.getBody().getContent()) { if (o instanceof org.docx4j.wml.P) { if (((org.docx4j.wml.P) o).getPPr() != null) { org.docx4j.wml.PPr ppr = ((org.docx4j.wml.P) o).getPPr(); if (ppr.getSectPr() != null) { // If the *following* section is continuous, don't add *this* section boolean ignoreThisSection = false; SectPr followingSectPr = sectPrs.get(++sectPrIndex); if (followingSectPr.getType() != null && followingSectPr.getType().getVal().equals("continuous")) { ignoreThisSection = true; // If the w:pgSz on the two sections differs, // then Word inserts a page break (ie doesn't treat it as continuous). // If no w:pgSz element is present, then Word defaults // (presumably to Legal? TODO CHECK. There is no default setting in the docx). // Word always inserts a w:pgSz element? PgSz pgSzThis = ppr.getSectPr().getPgSz(); PgSz pgSzNext = followingSectPr.getPgSz(); if (pgSzThis != null && pgSzNext != null) { if (pgSzThis.getH().compareTo(pgSzNext.getH()) != 0) { ignoreThisSection = false; } if (pgSzThis.getW().compareTo(pgSzNext.getW()) != 0) { ignoreThisSection = false; } // Orientation:default is portrait boolean portraitThis = true; if (pgSzThis.getOrient() != null) { portraitThis = pgSzThis.getOrient().equals(STPageOrientation.PORTRAIT); } boolean portraitNext = true; if (pgSzNext.getOrient() != null) { portraitNext = pgSzNext.getOrient().equals(STPageOrientation.PORTRAIT); } if (portraitThis != portraitNext) { ignoreThisSection = false; } } // TODO: handle cases where one or both pgSz elements are missing, // or H or W is missing. // Treat pgSz element missing as Legal size? } if (ignoreThisSection) { // In case there are some headers/footers that get inherited by the next section previousHF = new HeaderFooterPolicy(ppr.getSectPr(), previousHF, rels, evenAndOddHeaders); } else { currentSectionWrapper = createSectionWrapper( ppr.getSectPr(), previousHF, rels, evenAndOddHeaders, ++conversionSectionIndex, sectionContent, dummyPageNumbering); conversionSections.add(currentSectionWrapper); previousHF = currentSectionWrapper.getHeaderFooterPolicy(); sectionContent = new ArrayList<Object>(); } } } } sectionContent.add(o); // System.out.println(XmlUtils.marshaltoString(o, true)); } currentSectionWrapper = createSectionWrapper( document.getBody().getSectPr(), previousHF, rels, evenAndOddHeaders, ++conversionSectionIndex, sectionContent, dummyPageNumbering); conversionSections.add(currentSectionWrapper); return conversionSections; }
@Override public void set(PPr pPr) { pPr.setInd((Ind) this.getObject()); }
private WordprocessingMLPackage fetchComponents( WordprocessingMLPackage srcPackage, ContentAccessor contentAccessor) throws Docx4JException { // convert components to altChunk Map<Integer, CTAltChunk> replacements = new HashMap<Integer, CTAltChunk>(); Integer index = 0; justGotAComponent = false; LinkedList<Integer> continuousBeforeIndex = new LinkedList<Integer>(); List<Boolean> continuousBefore = new ArrayList<Boolean>(); List<Boolean> continuousAfter = new ArrayList<Boolean>(); for (Object block : contentAccessor.getContent()) { // Object ublock = XmlUtils.unwrap(block); if (block instanceof org.docx4j.wml.SdtBlock) { org.docx4j.wml.SdtBlock sdt = (org.docx4j.wml.SdtBlock) block; Tag tag = getSdtPr(sdt).getTag(); if (tag == null) { List<Object> newContent = new ArrayList<Object>(); newContent.add(sdt); continue; } log.info(tag.getVal()); HashMap<String, String> map = QueryString.parseQueryString(tag.getVal(), true); String componentId = map.get(BINDING_ROLE_COMPONENT); if (componentId == null) continue; // Convert the sdt to a w:altChunk // .. get the IRI String iri = ComponentsPart.getComponentById(components, componentId).getIri(); log.debug("Fetching " + iri); if (docxFetcher == null) { log.error("You need a docxFetcher (and the MergeDocx extension) to fetch components"); return srcPackage; } // .. create the part AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart( getNewPartName( "/chunk", ".docx", srcPackage.getMainDocumentPart().getRelationshipsPart())); afiPart.setBinaryData(docxFetcher.getDocxFromIRI(iri)); afiPart.setContentType( new ContentType( "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")); // docx Relationship altChunkRel = srcPackage.getMainDocumentPart().addTargetPart(afiPart); CTAltChunk ac = Context.getWmlObjectFactory().createCTAltChunk(); ac.setId(altChunkRel.getId()); replacements.put(index, ac); /* * 2011 12 11 TODO. Rethink support for * od:continuousBefore and od:continuousAfter. */ // This is handled in this class if (map.get(BINDING_ROLE_COMPONENT_BEFORE) != null && map.get(BINDING_ROLE_COMPONENT_BEFORE).equals("true")) { continuousBefore.add(Boolean.TRUE); continuousBeforeIndex.addFirst(index); log.info("ctsBefore index: " + index); } else { continuousBefore.add(Boolean.FALSE); continuousBeforeIndex.addFirst(index); } // The following is handled in ProcessAltChunk if (map.get(BINDING_ROLE_COMPONENT_AFTER) != null && map.get(BINDING_ROLE_COMPONENT_AFTER).equals("true")) { continuousAfter.add(Boolean.TRUE); } else { continuousAfter.add(Boolean.TRUE); } justGotAComponent = true; } index++; } if (!justGotAComponent) { return srcPackage; } // Now replace in list for (Integer key : replacements.keySet()) { contentAccessor.getContent().set(key, replacements.get(key)); } // Go through docx in reverse order List<Object> bodyChildren = contentAccessor.getContent(); int i = 0; for (Integer indexIntoBody : continuousBeforeIndex) { if (continuousBefore.get(i)) { // Element before the w:altChunk if (indexIntoBody == 0) { // // Insert a sectPr right at the beginning of the docx? // // TODO check this isn't necessary // SectPr newSectPr = // Context.getWmlObjectFactory().createSectPr(); // SectPr.Type type = // Context.getWmlObjectFactory().createSectPrType(); // type.setVal("continuous"); // newSectPr.setType( type ); // // bodyChildren.add(0, newSectPr); } else { Object block = bodyChildren.get(indexIntoBody.intValue() - 1); if (block instanceof P && ((P) block).getPPr() != null && ((P) block).getPPr().getSectPr() != null) { makeContinuous(((P) block).getPPr().getSectPr()); } else if (block instanceof P) { // More likely PPr ppr = ((P) block).getPPr(); if (ppr == null) { ppr = Context.getWmlObjectFactory().createPPr(); ((P) block).setPPr(ppr); } SectPr newSectPr = Context.getWmlObjectFactory().createSectPr(); SectPr.Type type = Context.getWmlObjectFactory().createSectPrType(); type.setVal("continuous"); newSectPr.setType(type); ppr.setSectPr(newSectPr); } else { // Equally likely - its a table or something, so add a p P newP = Context.getWmlObjectFactory().createP(); PPr ppr = Context.getWmlObjectFactory().createPPr(); newP.setPPr(ppr); SectPr newSectPr = Context.getWmlObjectFactory().createSectPr(); SectPr.Type type = Context.getWmlObjectFactory().createSectPrType(); type.setVal("continuous"); newSectPr.setType(type); ppr.setSectPr(newSectPr); bodyChildren.add(indexIntoBody.intValue(), newP); // add // before // altChunk } } } // else nothing specified, so go with normal MergeDocx behaviour i++; } // process altChunk try { // Use reflection, so docx4j can be built // by users who don't have the MergeDocx utility Class<?> documentBuilder = Class.forName("com.plutext.merge.ProcessAltChunk"); // Method method = documentBuilder.getMethod("merge", // wmlPkgList.getClass()); Method[] methods = documentBuilder.getMethods(); Method processMethod = null; for (int j = 0; j < methods.length; j++) { log.debug(methods[j].getName()); if (methods[j].getName().equals("process")) { processMethod = methods[j]; } } if (processMethod == null) throw new NoSuchMethodException(); return (WordprocessingMLPackage) processMethod.invoke(null, srcPackage); } catch (ClassNotFoundException e) { extensionMissing(e); justGotAComponent = false; return srcPackage; // throw new Docx4JException("Problem processing w:altChunk", e); } catch (NoSuchMethodException e) { // Degrade gracefully extensionMissing(e); justGotAComponent = false; return srcPackage; // throw new Docx4JException("Problem processing w:altChunk", e); } catch (Exception e) { throw new Docx4JException("Problem processing w:altChunk", e); } }
@Override public List<Object> apply(Object o) { // NB: the tests in this method have to be comprehensive, // so if support for glow etc is introduced, tests for those // will need to be added if (o instanceof org.docx4j.wml.P) { P p = (P) o; // W14? if (p.getParaId() != null) { needW14 = true; } // W15? if (!needW15) { PPr ppr = p.getPPr(); if (ppr != null) { if (ppr.getCollapsed() != null) { needW15 = true; } if (ppr.getSectPr() != null && ppr.getSectPr().getFootnoteColumns() != null) { needW15 = true; } } } } else if (o instanceof SdtElement) { SdtPr sdtPr = ((SdtElement) o).getSdtPr(); if (sdtPr != null) { if (contains( sdtPr.getRPrOrAliasOrLock(), "http://schemas.microsoft.com/office/word/2010/wordml", w14SdtPrNames)) { needW14 = true; } if (contains( sdtPr.getRPrOrAliasOrLock(), "http://schemas.microsoft.com/office/word/2012/wordml", w15SdtPrNames)) { needW15 = true; } } } else if (o instanceof Tr) { // TODO does this need to be unwrapped? if (((Tr) o).getParaId() != null) { needW14 = true; } } else if (o instanceof CTObject) { if (((CTObject) o).getAnchorId() != null) { needW14 = true; } } else if (o instanceof Pict) { if (((Pict) o).getAnchorId() != null) { needW14 = true; } } return null; }