Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple ordered lists #125

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ public final class DocxConstants {
public static final QName QNAME_VERTICAL_ALIGNMENT_ATT = new QName("", "vertical-alignment");
public static final QName QNAME_WIDTH_ATT = new QName("", "width");
public static final QName QNAME_XSLT_FORMAT_ATT = new QName("", "xslt-format");
public static final QName QNAME_NUMID_ATT = new QName("", "numId");


// Elements:
public static final QName QNAME_COLS_ELEM = new QName(SIMPLE_WP_NS, "cols");
Expand Down
79 changes: 67 additions & 12 deletions src/main/java/org/wordinator/xml2docx/generator/DocxGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.imageio.ImageIO;
import javax.xml.namespace.QName;
Expand All @@ -46,6 +49,7 @@
import org.apache.poi.xwpf.usermodel.XWPFNum;
import org.apache.poi.xwpf.usermodel.XWPFNumbering;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRelation;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFStyle;
import org.apache.poi.xwpf.usermodel.XWPFStyles;
Expand All @@ -71,6 +75,7 @@
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHyperlink;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTMarkupRange;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTNumPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTOnOff;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar;
Expand Down Expand Up @@ -390,6 +395,8 @@ public boolean hasBorders() {
private boolean isFirstParagraphStyleWarning = true;
private boolean isFirstCharacterStyleWarning = true;
private boolean isFirstTableStyleWarning = true;
private int maxNum;
private Map<String, List<XWPFParagraph>> numParas = new LinkedHashMap<>();

/**
*
Expand All @@ -416,8 +423,8 @@ public void generate(XmlObject xml) throws DocxGenerationException, XmlException
setupNumbering(doc, this.templateDoc);
setupStyles(doc, this.templateDoc);
constructDoc(doc, xml);

FileOutputStream out = new FileOutputStream(outFile);
setNumbering(doc);
FileOutputStream out = new FileOutputStream(this.outFile);
doc.write(out);
doc.close();
}
Expand Down Expand Up @@ -1230,6 +1237,7 @@ private XWPFParagraph makeParagraph(
cursor.push();
String styleName = cursor.getAttributeText(DocxConstants.QNAME_STYLE_ATT);
String styleId = cursor.getAttributeText(DocxConstants.QNAME_STYLEID_ATT);
String numId = cursor.getAttributeText(DocxConstants.QNAME_NUMID_ATT);

if (null != styleName && null == styleId) {
// Look up the style by name:
Expand Down Expand Up @@ -1328,6 +1336,13 @@ private XWPFParagraph makeParagraph(
} while(cursor.toNextSibling());
}
cursor.pop();

if (numId != null && !numId.isEmpty()) {
// Store Numbering paragraph
List<XWPFParagraph> paras = this.numParas.computeIfAbsent(numId, k -> new ArrayList<>());
paras.add(para);
}

return para;
}

Expand Down Expand Up @@ -2154,23 +2169,39 @@ private void makeHyperlink(XWPFParagraph para, XmlCursor cursor) throws DocxGene

// Set the appropriate target:

XWPFHyperlinkRun hyperlinkRun;

if (href.startsWith("#")) {
// Just a fragment ID, must be to a bookmark
String bookmarkName = href.substring(1);
hyperlink.setAnchor(bookmarkName);
cursor.push();
hyperlinkRun = makeHyperlinkRun(hyperlink, cursor, para);
cursor.pop();
} else {
// Create a relationship that targets the href and use the
// relationship's ID on the hyperlink
// It's not yet clear from the POI API how to create a new relationship for
// use by an external hyperlink.
// throw new NotImplementedException("Links to external resources not yet implemented.");
}
// Add the link as External relationship
String id = para.getDocument().getPackagePart().addExternalRelationship(href, XWPFRelation.HYPERLINK.getRelation()).getId();

cursor.push();
XWPFHyperlinkRun hyperlinkRun = makeHyperlinkRun(hyperlink, cursor, para);
cursor.pop();
para.addRun(hyperlinkRun);
// Append the link and bind it to the relationship
hyperlink.setId(id);

// Create the linked text
String linkedText = cursor.getTextValue();
CTText ctText = CTText.Factory.newInstance();
ctText.setStringValue(linkedText);
CTR ctr = CTR.Factory.newInstance();
ctr.setTArray(new CTText[]{ctText});

// Create the formatting
CTRPr rpr = ctr.addNewRPr();
rpr.addNewRStyle().setVal("Hyperlink");

// Insert the linked text into the link
hyperlink.setRArray(new CTR[]{ctr});

hyperlinkRun = new XWPFHyperlinkRun(hyperlink, CTR.Factory.newInstance(), para);
}
para.addRun(hyperlinkRun);
}

/**
Expand Down Expand Up @@ -2992,6 +3023,26 @@ private void setupStyles(XWPFDocument doc, XWPFDocument templateDoc) throws Docx

}

private void setNumbering(XWPFDocument doc) {
int i = 1;

for (List<XWPFParagraph> paras : this.numParas.values()) {

int newNum = this.maxNum + i;
BigInteger bgiNumId = BigInteger.valueOf(newNum);
for (XWPFParagraph numPara : paras) {
CTPPr ctpPr = numPara.getCTPPr();
CTNumPr ctNumPr = ctpPr.addNewNumPr();
CTDecimalNumber decNumId = CTDecimalNumber.Factory.newInstance();
decNumId.setVal(bgiNumId);
ctNumPr.setNumId(decNumId);
}
doc.getNumbering().addNum(bgiNumId);

i++;
}
}

private void setupNumbering(XWPFDocument doc, XWPFDocument templateDoc) throws DocxGenerationException {
// Load the template's numbering definitions to the new document

Expand Down Expand Up @@ -3028,6 +3079,10 @@ private void setupNumbering(XWPFDocument doc, XWPFDocument templateDoc) throws D
} while (num != null);


// Calculate max numbering number
Optional<XWPFNum> maxXwpfNum = numbering.getNums().stream().max(Comparator.comparing(o -> o.getCTNum().getNumId()));
this.maxNum = maxXwpfNum.map(xwpfNum -> xwpfNum.getCTNum().getNumId().intValue()).orElse(0);

} catch (Exception e) {
new DocxGenerationException(e.getClass().getSimpleName() + " Copying numbering definitions from template doc: " + e.getMessage(), e);
}
Expand Down
6 changes: 2 additions & 4 deletions src/main/xsl/html2docx/baseProcessing.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,8 @@

<xsl:template match="xhtml:a[@href] | a[@href]">
<xsl:param name="doDebug" as="xs:boolean" tunnel="yes" select="false()"/>

<xsl:variable name="targetId" as="xs:string?" select="tokenize(@href, '#')[last()]"/>

<wp:hyperlink href="#{$targetId}">
<xsl:variable name="targetId" as="xs:string?" select="@href"/>
<wp:hyperlink href="{$targetId}">
<xsl:apply-templates>
<xsl:with-param name="doDebug" as="xs:boolean" tunnel="yes" select="$doDebug"/>
</xsl:apply-templates>
Expand Down
9 changes: 8 additions & 1 deletion src/main/xsl/html2docx/get-style-name.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,14 @@
<xsl:variable name="level" as="xs:string"
select="if ($level eq '1') then '' else $level"
/>
<xsl:sequence select="'List ' || $level"/>
<xsl:choose>
<xsl:when test="$list-type = 'ol'">
<xsl:sequence select="'List Number ' || $level"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="'List ' || $level"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template mode="get-style-name" match="*" priority="-1" as="xs:string?">
Expand Down