All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.docx4j.toc.TocEntry Maven / Gradle / Ivy
/*
* Copyright 2013-2016, Plutext Pty Ltd.
*
* This file is part of docx4j.
docx4j is licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.docx4j.toc;
import java.io.StringWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBElement;
import org.apache.commons.lang3.StringUtils;
import org.docx4j.TextUtils;
import org.docx4j.XmlUtils;
import org.docx4j.model.PropertyResolver;
import org.docx4j.model.listnumbering.Emulator.ResultTriple;
import org.docx4j.model.properties.paragraph.Indent;
import org.docx4j.model.structure.PageDimensions;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.model.styles.StyleUtil;
import org.docx4j.wml.BooleanDefaultTrue;
import org.docx4j.wml.CTTabStop;
import org.docx4j.wml.FldChar;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.P.Hyperlink;
import org.docx4j.wml.PPr;
import org.docx4j.wml.PPrBase;
import org.docx4j.wml.PPrBase.Ind;
import org.docx4j.wml.ParaRPr;
import org.docx4j.wml.R;
import org.docx4j.wml.RPr;
import org.docx4j.wml.RStyle;
import org.docx4j.wml.STFldCharType;
import org.docx4j.wml.STTabJc;
import org.docx4j.wml.STTabTlc;
import org.docx4j.wml.Tabs;
import org.docx4j.wml.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TocEntry {
private static Logger log = LoggerFactory.getLogger(TocEntry.class);
private TocEntry() {}
public TocEntry(PropertyResolver propertyResolver, PageDimensions pageDimensions, STTabTlc leader) {
this.propertyResolver = propertyResolver;
this.writableWidthTwips = pageDimensions.getWritableWidthTwips();
this.leader = leader;
}
private PropertyResolver propertyResolver;
private static final String PRESERVE = "preserve";
private static final String PAGEREF_MASK = "PAGEREF %s \\h";
private static final String HYPERLINK = "Hyperlink";
//private static final int DOTS_POSITION = 9345;
/**
* right tab pos= pg width – (gutter + left + right margin)
best to put that explicitly in the ToC p, rather than the toc styles (which we just override)
Word doesn’t update the right tab pos (where its in a ToC style) in response to a change in margin!
@since 3.2.0
*/
private int writableWidthTwips;
private STTabTlc leader;
// private String entryValue;
private List entryValues = new ArrayList();
private String anchorValue;
private String number = "";
private int entryLevel = -1;
private boolean hyperlink = false;
private boolean pageNumber = true;
/**
* If this ToC entry has a paragraph number, we'll need to
* allow for that in our tabs definition
*/
private boolean isNumbered = false;
private P entryP; // be careful; this is only set very late in the process
private Text pageNumberText;
ObjectFactory wmlObjectFactory = new ObjectFactory();
// public void setEntryValue(String entryValue) {
// this.entryValue = entryValue;
// }
public List getEntryValue() {
return entryValues;
}
public void setAnchorValue(String anchorValue) {
this.anchorValue = anchorValue;
}
public String getAnchorValue() {
return anchorValue;
}
public void makeHyperlink(boolean hyperlink) {
this.hyperlink = hyperlink;
}
public void addPageNumber(boolean pageNumber) {
this.pageNumber = pageNumber;
}
public boolean isPageNumber() {
return pageNumber;
}
public int getEntryLevel() {
return entryLevel;
}
public void setEntryLevel(int entryLevel) {
this.entryLevel = entryLevel;
}
public P getEntryParagraph(TocStyles tocStyles){
if(entryP == null){
entryP = generateTocEntry(tocStyles);
}
return entryP;
}
public Text getEntryPageNumberText(){
if(pageNumberText == null && entryP != null){
// Get the last empty w:t
List texts = TocHelper.getAllElementsFromObject(entryP, Text.class);
Text t;
for(Object o: texts){
t = (Text)o;
if(t.getValue().isEmpty()){
pageNumberText = t;
}
}
}
return pageNumberText;
}
private P generateTocEntry(TocStyles tocStyles){
// Create object for p
P p3 = wmlObjectFactory.createP();
p3.setPPr(generateTocEntryPPr(tocStyles));
if(hyperlink){
p3.getContent().add(generateTocEntryHyperlink());
} else {
p3.getContent().addAll(generateTocEntryContent());
}
return p3;
}
private Map styleIndent = new HashMap();
private Ind getInd(String styleId) {
if (styleId==null) return null;
Ind ind = styleIndent.get(styleId);
if (ind == null) {
PPr ppr = propertyResolver.getEffectivePPr(styleId);
if (ppr==null) {
return null;
} else {
ind = ppr.getInd();
styleIndent.put(styleId, ind);
}
}
return ind;
}
private PPr generateTocEntryPPr(TocStyles tocStyles){
String styleId = tocStyles.getStyleIdForName(String.format(TocStyles.TOC_STYLE_MASK, entryLevel+1));
// Create object for pPr
PPr ppr3 = wmlObjectFactory.createPPr();
// Create object for rPr
ParaRPr pararpr2 = wmlObjectFactory.createParaRPr();
ppr3.setRPr(pararpr2);
// Create object for noProof
BooleanDefaultTrue booleandefaulttrue20 = wmlObjectFactory.createBooleanDefaultTrue();
pararpr2.setNoProof(booleandefaulttrue20);
// Create object for tabs
Tabs tabs2 = wmlObjectFactory.createTabs();
ppr3.setTabs(tabs2);
if (isNumbered) {
// add a simple tab definition, as our first tab
CTTabStop tabstop = wmlObjectFactory.createCTTabStop();
tabs2.getTab().add(tabstop);
tabstop.setVal(org.docx4j.wml.STTabJc.LEFT);
// Work out how much indentation is required for our tab stop
Ind ind = getInd( styleId);
// take width of number into account - it could be something like 'Appendix 1'!
int numWidth = 110 * numChars; // crude, similar to XsltFOFunctions
// .. ok for TNR 12
// KNOWN ISSUE: numbering ticking over from 9 to 10, or i to ii to iii
// will increase the width of the field. Need LNE to tell us a numChars value which takes that into account
int paddedWidth = 330 + numWidth;
if (ind!=null
&& ind.getLeft()!=null) {
tabstop.setPos( BigInteger.valueOf( ind.getLeft().intValue() + paddedWidth) );
} else {
tabstop.setPos( BigInteger.valueOf( paddedWidth) );
}
}
// Create object for tab
CTTabStop tabstop2 = wmlObjectFactory.createCTTabStop();
tabs2.getTab().add(tabstop2);
tabstop2.setVal(STTabJc.RIGHT);
tabstop2.setPos(BigInteger.valueOf(writableWidthTwips) );
tabstop2.setLeader(leader);
// Create object for pStyle
if (styleId==null) {
log.warn("No style found for " + String.format(TocStyles.TOC_STYLE_MASK, entryLevel+1) );
} else {
PPrBase.PStyle pprbasepstyle3 = wmlObjectFactory.createPPrBasePStyle();
ppr3.setPStyle(pprbasepstyle3);
pprbasepstyle3.setVal(styleId);
}
return ppr3;
}
private JAXBElement generateTocEntryHyperlink(){
// Create object for hyperlink (wrapped in JAXBElement)
Hyperlink phyperlink2 = wmlObjectFactory.createPHyperlink();
JAXBElement phyperlinkWrapped2 = wmlObjectFactory.createPHyperlink(phyperlink2);
phyperlink2.setAnchor(anchorValue);
phyperlink2.getContent().addAll(generateTocEntryContent());
return phyperlinkWrapped2;
}
private List generateTocEntryContent(){
// List rList = new ArrayList();
//
// // Create object for r
// R r13 = wmlObjectFactory.createR();
// rList.add(r13);
// // Create object for rPr
// RPr rpr11 = wmlObjectFactory.createRPr();
// r13.setRPr(rpr11);
// if(hyperlink){
// // Create object for rStyle
// RStyle rstyle2 = wmlObjectFactory.createRStyle();
// rpr11.setRStyle(rstyle2);
// rstyle2.setVal(HYPERLINK);
// }
// // Create object for noProof
// BooleanDefaultTrue booleandefaulttrue21 = wmlObjectFactory.createBooleanDefaultTrue();
// rpr11.setNoProof(booleandefaulttrue21);
//
// // Create object for t (wrapped in JAXBElement)
// Text text6 = wmlObjectFactory.createText();
// JAXBElement textWrapped6 = wmlObjectFactory.createRT(text6);
// r13.getContent().add(textWrapped6);
// text6.setValue(entryValue);
List rList = entryValues;
generateTocEntryPageNumber(rList);
return rList;
}
private void generateTocEntryPageNumber(List rList){
// Create object for r
R r14 = wmlObjectFactory.createR();
rList.add(r14);
// Create object for rPr
RPr rpr12 = wmlObjectFactory.createRPr();
r14.setRPr(rpr12);
// Create object for noProof
BooleanDefaultTrue booleandefaulttrue22 = wmlObjectFactory.createBooleanDefaultTrue();
rpr12.setNoProof(booleandefaulttrue22);
// Create object for webHidden
BooleanDefaultTrue booleandefaulttrue23 = wmlObjectFactory.createBooleanDefaultTrue();
rpr12.setWebHidden(booleandefaulttrue23);
if(pageNumber){
// Create object for tab (wrapped in JAXBElement)
R.Tab rtab2 = wmlObjectFactory.createRTab();
JAXBElement rtabWrapped2 = wmlObjectFactory.createRTab(rtab2);
r14.getContent().add(rtabWrapped2);
}
// Create object for r
R r15 = wmlObjectFactory.createR();
rList.add(r15);
// Create object for rPr
RPr rpr13 = wmlObjectFactory.createRPr();
r15.setRPr(rpr13);
// Create object for noProof
BooleanDefaultTrue booleandefaulttrue24 = wmlObjectFactory.createBooleanDefaultTrue();
rpr13.setNoProof(booleandefaulttrue24);
// Create object for webHidden
BooleanDefaultTrue booleandefaulttrue25 = wmlObjectFactory.createBooleanDefaultTrue();
rpr13.setWebHidden(booleandefaulttrue25);
// Create object for fldChar (wrapped in JAXBElement)
FldChar fldchar6 = wmlObjectFactory.createFldChar();
JAXBElement fldcharWrapped6 = wmlObjectFactory.createRFldChar(fldchar6);
r15.getContent().add( fldcharWrapped6);
fldchar6.setFldCharType(STFldCharType.BEGIN);
// Create object for r
R r16 = wmlObjectFactory.createR();
rList.add(r16);
// Create object for rPr
RPr rpr14 = wmlObjectFactory.createRPr();
r16.setRPr(rpr14);
// Create object for noProof
BooleanDefaultTrue booleandefaulttrue26 = wmlObjectFactory.createBooleanDefaultTrue();
rpr14.setNoProof(booleandefaulttrue26);
// Create object for webHidden
BooleanDefaultTrue booleandefaulttrue27 = wmlObjectFactory.createBooleanDefaultTrue();
rpr14.setWebHidden(booleandefaulttrue27);
// Create object for instrText (wrapped in JAXBElement)
Text text7 = wmlObjectFactory.createText();
JAXBElement textWrapped7 = wmlObjectFactory.createRInstrText(text7);
r16.getContent().add(textWrapped7);
text7.setValue(String.format(PAGEREF_MASK, anchorValue));
text7.setSpace(PRESERVE);
// Create object for r
R r17 = wmlObjectFactory.createR();
rList.add(r17);
// Create object for rPr
RPr rpr15 = wmlObjectFactory.createRPr();
r17.setRPr(rpr15);
// Create object for noProof
BooleanDefaultTrue booleandefaulttrue28 = wmlObjectFactory.createBooleanDefaultTrue();
rpr15.setNoProof(booleandefaulttrue28);
// Create object for webHidden
BooleanDefaultTrue booleandefaulttrue29 = wmlObjectFactory.createBooleanDefaultTrue();
rpr15.setWebHidden(booleandefaulttrue29);
// Create object for r
R r18 = wmlObjectFactory.createR();
rList.add(r18);
// Create object for rPr
RPr rpr16 = wmlObjectFactory.createRPr();
r18.setRPr(rpr16);
// Create object for noProof
BooleanDefaultTrue booleandefaulttrue30 = wmlObjectFactory.createBooleanDefaultTrue();
rpr16.setNoProof(booleandefaulttrue30);
// Create object for webHidden
BooleanDefaultTrue booleandefaulttrue31 = wmlObjectFactory.createBooleanDefaultTrue();
rpr16.setWebHidden(booleandefaulttrue31);
// Create object for fldChar (wrapped in JAXBElement)
FldChar fldchar7 = wmlObjectFactory.createFldChar();
JAXBElement fldcharWrapped7 = wmlObjectFactory.createRFldChar(fldchar7);
r18.getContent().add(fldcharWrapped7);
fldchar7.setFldCharType(STFldCharType.SEPARATE);
// Create object for r
R r19 = wmlObjectFactory.createR();
rList.add(r19);
// Create object for rPr
RPr rpr17 = wmlObjectFactory.createRPr();
r19.setRPr(rpr17);
// Create object for noProof
BooleanDefaultTrue booleandefaulttrue32 = wmlObjectFactory.createBooleanDefaultTrue();
rpr17.setNoProof(booleandefaulttrue32);
// Create object for webHidden
BooleanDefaultTrue booleandefaulttrue33 = wmlObjectFactory.createBooleanDefaultTrue();
rpr17.setWebHidden(booleandefaulttrue33);
// Create object for t (wrapped in JAXBElement)
Text text8 = wmlObjectFactory.createText();
JAXBElement textWrapped8 = wmlObjectFactory.createRT(text8);
r19.getContent().add(textWrapped8);
text8.setValue(number);
// Create object for r
R r20 = wmlObjectFactory.createR();
rList.add(r20);
// Create object for rPr
RPr rpr18 = wmlObjectFactory.createRPr();
r20.setRPr(rpr18);
// Create object for noProof
BooleanDefaultTrue booleandefaulttrue34 = wmlObjectFactory.createBooleanDefaultTrue();
rpr18.setNoProof(booleandefaulttrue34);
// Create object for webHidden
BooleanDefaultTrue booleandefaulttrue35 = wmlObjectFactory.createBooleanDefaultTrue();
rpr18.setWebHidden(booleandefaulttrue35);
// Create object for fldChar (wrapped in JAXBElement)
FldChar fldchar8 = wmlObjectFactory.createFldChar();
JAXBElement fldcharWrapped8 = wmlObjectFactory.createRFldChar(fldchar8);
r20.getContent().add(fldcharWrapped8);
fldchar8.setFldCharType(STFldCharType.END);
}
// public void setEntryValue(P sourceP) {
//
// StringWriter sw = new StringWriter();
// try {
// TextUtils.extractText(sourceP, sw);
// } catch (Exception e) {
// log.error(e.getMessage(), e);
// }
// String entryValue = sw.toString();
// this.setEntryValue(entryValue);
// }
// since 3.1.0.5
public void setEntryValue(P sourceP) {
/*
Certain Run formatting on entries is re-used, including:
• font face
• italic
• text highlight
• hidden (honoured)
• small caps
but not
• font size
• font color
• underline
*/
// Step 1: create a clone of the P
P clonedP = (P)XmlUtils.deepCopy(sourceP);
// Step 2: make a List, comprising the w:r/w:t contents,
// with styles resolved
List runsFound = TocHelper.getAllElementsFromObject(clonedP, R.class);
R lastRun = null;
for( Object o : runsFound) {
R r = (R)o;
List textsFound = TocHelper.getAllElementsFromObject(r, Text.class);
if (textsFound.size()>0) {
R newR = new R();
if (r.getRPr()==null) {
newR.setRPr(wmlObjectFactory.createRPr());
} else {
// Resolve the formatting
newR.setRPr(
getEffectiveRPr(r.getRPr()));
// newR.setRPr(
// r.getRPr());
// Step 3: strip/filter unwanted run formatting
nullify(newR.getRPr());
if (newR.getRPr()==null) {
newR.setRPr(wmlObjectFactory.createRPr());
}
}
// apply hyperlink if appropriate
if (hyperlink) {
RStyle rstyle = wmlObjectFactory.createRStyle();
newR.getRPr().setRStyle(rstyle);
rstyle.setVal(HYPERLINK);
}
newR.getContent().addAll(textsFound);
entryValues.add(newR);
lastRun = newR;
}
}
// drop any trailing space off last run (Word does this)
if (lastRun!=null) {
int size = lastRun.getContent().size();
if (size>0) {
Text lastText = (Text)lastRun.getContent().get(size-1);
String val = lastText.getValue();
if (val!=null) {
lastText.setValue(StringUtils.stripEnd(val, null));
}
}
}
// drop leading space off the first run
R firstRun = null;
if (runsFound.size()>0) {
firstRun = (R)runsFound.get(0);
List textsFound = TocHelper.getAllElementsFromObject(firstRun, Text.class);
int size = textsFound.size();
if (size>0) {
Text firstText = (Text)textsFound.get(0);
String val = firstText.getValue();
if (val!=null) {
firstText.setValue(StringUtils.stripStart(val, null));
}
}
}
}
private RPr getEffectiveRPr(RPr expressRPr) {
RPr resolvedRPr = null;
if (expressRPr != null && expressRPr.getRStyle() != null ) {
String runStyleId = expressRPr.getRStyle().getVal();
resolvedRPr = propertyResolver.getEffectiveRPr(runStyleId);
// remove the style, so it is not set by apply below
expressRPr.setRStyle(null);
}
return StyleUtil.apply(expressRPr, resolvedRPr);
}
private void nullify(RPr destination) {
if (destination==null) return;
// The following ARE used/preserved in the ToC
// setRFonts(null);
// setB(null);
// setBCs(null);
// setICs(null);
// setI(null);
// setVanish(null);
// setCaps(null);
// setSmallCaps(null);
// setHighlight(null);
// The following are definitely dropped
destination.setRStyle(null);
destination.setSz(null);
destination.setSzCs(null);
destination.setColor(null);
destination.setU(null);
// The following are unknown, so assume dropped
destination.setLang(null);
destination.setStrike(null);
destination.setDstrike(null);
destination.setOutline(null);
destination.setShadow(null);
destination.setEmboss(null);
destination.setImprint(null);
destination.setSnapToGrid(null);
destination.setSpacing(null);
destination.setW(null);
destination.setKern(null);
destination.setPosition(null);
destination.setEffect(null);
destination.setBdr(null);
destination.setShd(null);
destination.setVertAlign(null);
destination.setRtl(null);
destination.setCs(null);
destination.setEm(null);
destination.setSpecVanish(null);
destination.setOMath(null);
}
/**
* Number this entry, if necessary
* @param numberTriple
*/
public void numberEntry(ResultTriple numberTriple) {
if (numberTriple!=null && numberTriple.getNumString()!=null) {
isNumbered = true; //signal that we need to define the tab setting
// it depends on the width of the number
if (numberTriple.getBullet()!=null ) {
numChars=1;
} else if (numberTriple.getNumString()==null) {
numChars=0;
} else {
numChars = numberTriple.getNumString().length();
}
R r = wmlObjectFactory.createR();
RPr rpr = wmlObjectFactory.createRPr();
r.setRPr(rpr);
if(hyperlink){
// Create object for rStyle
RStyle rstyle2 = wmlObjectFactory.createRStyle();
rpr.setRStyle(rstyle2);
rstyle2.setVal(HYPERLINK);
}
// Create object for t (wrapped in JAXBElement)
Text t = wmlObjectFactory.createText();
JAXBElement textWrapped6 = wmlObjectFactory.createRT(t);
r.getContent().add(textWrapped6);
t.setSpace("preserve");
this.entryValues.add(0, r);
/* Add a tab, but in a new run (since the tab isn't to be underlined),
* unless w:lvl has
or
*/
if (numberTriple.getLvl().getSuff()==null) {
t.setValue(numberTriple.getNumString() );
this.entryValues.add(1, tabAfterPNumber());
} else if ( numberTriple.getLvl().getSuff().getVal().equals("space")) {
t.setValue(numberTriple.getNumString() + " ");
} else if ( numberTriple.getLvl().getSuff().getVal().equals("nothing")) {
t.setValue(numberTriple.getNumString());
} else {
// for anything else, ignore, and use a tab (mimicking Word 2010)
t.setValue(numberTriple.getNumString() );
this.entryValues.add(1, tabAfterPNumber());
}
}
}
/** Add a tab, but in a new run (since the tab isn't to be underlined),
* unless w:lvl has
or
*/
private R tabAfterPNumber() {
R r = wmlObjectFactory.createR();
R.Tab rtab = wmlObjectFactory.createRTab();
JAXBElement rtabWrapped = wmlObjectFactory.createRTab(rtab);
r.getContent().add(rtabWrapped);
return r;
}
int numChars=1;
}