Skip to content

Commit

Permalink
Merge pull request #243 from metanorma/table_autolayout_refactoring
Browse files Browse the repository at this point in the history
Table autolayout refactoring
  • Loading branch information
Intelligent2013 authored Apr 3, 2024
2 parents d9101ef + 8dd3123 commit 74d2f32
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 44 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ SHELL ?= /bin/bash
endif

#JAR_VERSION := $(shell mvn -q -Dexec.executable="echo" -Dexec.args='$${project.version}' --non-recursive exec:exec -DforceStdout)
JAR_VERSION := 1.86
JAR_VERSION := 1.87
JAR_FILE := mn2pdf-$(JAR_VERSION).jar

all: target/$(JAR_FILE)
Expand Down
10 changes: 5 additions & 5 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ You will need the Java Development Kit (JDK) version 8, Update 241 (8u241) or hi

[source,sh]
----
java -Xss5m -Xmx2048m -jar target/mn2pdf-1.86.jar --xml-file <XML-FileName> --xsl-file <XSLT-FileName> --pdf-file <Output-PDF-FileName> [--syntax-highlight]
java -Xss5m -Xmx2048m -jar target/mn2pdf-1.87.jar --xml-file <XML-FileName> --xsl-file <XSLT-FileName> --pdf-file <Output-PDF-FileName> [--syntax-highlight]
----

e.g.

[source,sh]
----
java -Xss5m -Xmx2048m -jar target/mn2pdf-1.86.jar --xml-file tests/G.191.xml --xsl-file tests/itu.recommendation.xsl --pdf-file tests/G.191.pdf
java -Xss5m -Xmx2048m -jar target/mn2pdf-1.87.jar --xml-file tests/G.191.xml --xsl-file tests/itu.recommendation.xsl --pdf-file tests/G.191.pdf
----

=== PDF encryption features
Expand Down Expand Up @@ -100,7 +100,7 @@ Update version in `pom.xml`, e.g.:
----
<groupId>org.metanorma.fop</groupId>
<artifactId>mn2pdf</artifactId>
<version>1.86</version>
<version>1.87</version>
<name>Metanorma XML to PDF converter</name>
----

Expand All @@ -111,8 +111,8 @@ Tag the same version in Git:

[source,xml]
----
git tag v1.86
git push origin v1.86
git tag v1.87
git push origin v1.87
----

Then the corresponding GitHub release will be automatically created at:
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.metanorma.fop</groupId>
<artifactId>mn2pdf</artifactId>
<version>1.86</version>
<version>1.87</version>
<name>Metanorma XML to PDF converter</name>
<packaging>jar</packaging>
<url>https://www.metanorma.org</url>
Expand Down
98 changes: 82 additions & 16 deletions src/main/java/org/metanorma/fop/PDFGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.*;
Expand Down Expand Up @@ -1193,24 +1189,70 @@ private void setTablesWidths(fontConfig fontcfg, XSLTconverter xsltConverter, Fi

SourceXMLDocument sourceXMLDocumentTablesOnly = new SourceXMLDocument(xmlTablesOnly);

// transform XML to XSL-FO (XML .fo file)
xsltConverter.transform(sourceXMLDocumentTablesOnly, false);
int countTableCells = sourceXMLDocumentTablesOnly.getCountTableCells();
if (countTableCells < 30000) {
// transform XML to XSL-FO (XML .fo file)
xsltConverter.transform(sourceXMLDocumentTablesOnly, false);

String xmlFO = sourceXMLDocumentTablesOnly.getXMLFO();
String xmlFO = sourceXMLDocumentTablesOnly.getXMLFO();

//debug
debugSaveXML(xmlFO, pdf.getAbsolutePath() + ".fo.tables.xml");
//debug
debugSaveXML(xmlFO, pdf.getAbsolutePath() + ".fo.tables.xml");

fontcfg.outputFontManifestLog(Paths.get(pdf.getAbsolutePath() + ".tables.fontmanifest.log.txt"));
fontcfg.outputFontManifestLog(Paths.get(pdf.getAbsolutePath() + ".tables.fontmanifest.log.txt"));

fontcfg.setSourceDocumentFontList(sourceXMLDocumentTablesOnly.getDocumentFonts());
fontcfg.setSourceDocumentFontList(sourceXMLDocumentTablesOnly.getDocumentFonts());

Source sourceFO = new StreamSource(new StringReader(xmlFO));
Source sourceFO = new StreamSource(new StringReader(xmlFO));

logger.info("[INFO] Generation of Intermediate Format with information about the table's widths ...");
String xmlIF = generateFOPIntermediateFormat(sourceFO, fontcfg.getConfig(), pdf, true, ".tables");
logger.info("[INFO] Generation of Intermediate Format with information about the table's widths ...");
String xmlIF = generateFOPIntermediateFormat(sourceFO, fontcfg.getConfig(), pdf, true, ".tables");

xmlTableIF = createTableIF(xmlIF);
xmlTableIF = createTableIF(xmlIF);

} else { // for large tables, or large number of tables

List<String> tablesIds = sourceXMLDocumentTablesOnly.readElementsIds("//*[local-name() = 'table' or local-name() = 'dl']");

List<String> xmlTablesIF = new ArrayList<>();
// process each table separatery for memory consumption optimization
int tableCounter = 0;
int tableCount = tablesIds.size();
for (String tableId : tablesIds) {
tableCounter++;
logger.info("[INFO] Generation of XSL-FO (" + tableCounter + "/" + tableCount + ") with information about the table widths with id='" + tableId + "'...");

// process table with id=tableId only
xsltConverter.setParam("table_only_with_id", tableId);

// transform XML to XSL-FO (XML .fo file)
xsltConverter.transform(sourceXMLDocumentTablesOnly, false);

String xmlFO = sourceXMLDocumentTablesOnly.getXMLFO();

//debug
debugSaveXML(xmlFO, pdf.getAbsolutePath() + "." + tableId + ".fo.tables.xml");

fontcfg.outputFontManifestLog(Paths.get(pdf.getAbsolutePath() + "." + tableId + ".tables.fontmanifest.log.txt"));

fontcfg.setSourceDocumentFontList(sourceXMLDocumentTablesOnly.getDocumentFonts());

Source sourceFO = new StreamSource(new StringReader(xmlFO));

logger.info("[INFO] Generation of Intermediate Format (" + tableCounter + "/" + tableCount + ") with information about the table's widths with id='" + tableId + "'...");
String xmlIF = generateFOPIntermediateFormat(sourceFO, fontcfg.getConfig(), pdf, true, "." + tableId + ".tables");

xmlTableIF = createTableIF(xmlIF);

debugSaveXML(xmlTableIF, pdf.getAbsolutePath() + "." + tableId + ".tables.xml");

xmlTableIF = tableWidthsCleanup(xmlTableIF);

xmlTablesIF.add(xmlTableIF);
}
xmlTableIF = tablesWidthsUnion(xmlTablesIF);
xsltConverter.setParam("table_only_with_id", ""); // further process all tables
}

debugSaveXML(xmlTableIF, pdf.getAbsolutePath() + ".tables.xml");

Expand Down Expand Up @@ -1261,6 +1303,7 @@ private void debugSaveXML(String xmlString, String pathTo) {
}
}


private int getIFPageCount(String xmlIF) {
int pagecount = 0;
if (xmlIF != null) {
Expand All @@ -1280,4 +1323,27 @@ private void saveDebugFO(String debugXSLFO) {
}
}

private String tableWidthsCleanup(String table) {
int startPos = table.indexOf("<table ");
int endPos = table.indexOf("</tables>");
table = table.substring(startPos, endPos);
int startPosTbody = table.indexOf("<tbody>");
table = table.substring(0,startPosTbody) + "</table>";
return table;
}

private String tablesWidthsUnion(List<String> tables) {
StringBuilder sbTablesIF = new StringBuilder();
if (!tables.isEmpty()) {
sbTablesIF.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><tables>");
}
for (String itemTableIF: tables) {
sbTablesIF.append(itemTableIF);
}
if (!tables.isEmpty()) {
sbTablesIF.append("</tables>");
}
return sbTablesIF.toString();
}

}
59 changes: 50 additions & 9 deletions src/main/java/org/metanorma/fop/SourceXMLDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,13 @@ public SourceXMLDocument(File fXML) {
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
InputStream xmlstream = new FileInputStream(fXML);
sourceXML = dBuilder.parse(xmlstream);

String element_review = readValue("//*[local-name() = 'review'][1]");
this.hasAnnotations = element_review.length() != 0;
String element_table = readValue("//*[local-name() = 'table' or local-name() = 'dl'][1]");
this.hasTables = element_table.length() != 0;
String element_math = readValue("//*[local-name() = 'math'][1]");
this.hasMath = element_math.length() != 0;
readMetaInformation();
} catch (Exception ex) {
logger.severe("Can't read source XML.");
ex.printStackTrace();
}
}

public SourceXMLDocument(String strXML) {
try {
this.sourceXMLstr = strXML;
Expand All @@ -100,7 +94,17 @@ public SourceXMLDocument(String strXML) {
ex.printStackTrace();
}
}


private void readMetaInformation() {
String element_review = readValue("//*[local-name() = 'review'][1]");
this.hasAnnotations = element_review.length() != 0;
String element_table = readValue("//*[local-name() = 'table' or local-name() = 'dl'][1]");
this.hasTables = element_table.length() != 0;
String element_math = readValue("//*[local-name() = 'math'][1]");
this.hasMath = element_math.length() != 0;
}


public StreamSource getStreamSource() {
if (sourceXMLstr.isEmpty()) {
try {
Expand Down Expand Up @@ -410,6 +414,38 @@ private String readValue(String xpath) {
return value;
}

private int readTableCellsCount(){
int count = 0;
try {
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression query = xPath.compile("//*[local-name() = 'td' or local-name() = 'th' or local-name() = 'dt' or local-name() = 'dd']");
NodeList nodes = (NodeList)query.evaluate(sourceXML, XPathConstants.NODESET);
count = nodes.getLength();
} catch (Exception ex) {
logger.severe(ex.toString());
}
return count;
}

public List<String> readElementsIds(String xpath) {
List<String> values = new ArrayList<>();
try {
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression query = xPath.compile(xpath);
NodeList nodes = (NodeList)query.evaluate(sourceXML, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
Node node_id = nodes.item(i).getAttributes().getNamedItem("id");
if (node_id != null) {
String id = node_id.getTextContent();
values.add(id);
}
}
} catch (Exception ex) {
logger.severe(ex.toString());
}
return values;
}

public boolean hasAnnotations() {
return hasAnnotations;
}
Expand All @@ -423,6 +459,11 @@ public boolean hasMath() {
return hasMath;
}

public int getCountTableCells() {
int countTableCells = readTableCellsCount();
return countTableCells;
}

public void flushResources() {
sourceXML = null;
sourceXMLstr = "";
Expand Down
30 changes: 20 additions & 10 deletions src/main/java/org/metanorma/fop/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,7 @@
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
Expand Down Expand Up @@ -61,7 +52,12 @@
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
Expand Down Expand Up @@ -794,4 +790,18 @@ public static Node parseCSS(String cssString) {
}
return node;
}

private static String nodeToString(Node node) {
StringWriter sw = new StringWriter();
try {
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(node), new StreamResult(sw));
} catch (TransformerException e) {
System.out.println("nodeToString Transformer Exception: " + e.toString());
}
return sw.toString();
}

}
9 changes: 7 additions & 2 deletions src/main/resources/table_if.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
xmlns:xalan="http://xml.apache.org/xalan"
xmlns:java="http://xml.apache.org/xalan/java"
xmlns:str="http://exslt.org/strings"
exclude-result-prefixes="java str"
exclude-result-prefixes="fo if xalan java str"
version="1.0">

<xsl:output method="xml" encoding="UTF-8" indent="no"/>
Expand Down Expand Up @@ -54,7 +54,7 @@
</xsl:variable>

<xsl:variable name="ids" select="xalan:nodeset($ids_)"/>

<!-- <xsl:copy-of select="$ids"/> -->
<tables>
<!-- <xsl:apply-templates select="//if:id[starts-with(@name,$table_if_start_prefix)]"> -->
<xsl:apply-templates select="$ids//if:id[starts-with(@name,$table_if_start_prefix)]">
Expand Down Expand Up @@ -136,6 +136,8 @@

<xsl:variable name="cells" select="xalan:nodeset($cells_)"/>

<!-- <xsl:copy-of select="$cells"/> -->

<xsl:variable name="table_body_">
<tbody>
<xsl:for-each select="$cells/cell[generate-id(.) = generate-id(key('kRow', @row)[1])]">
Expand Down Expand Up @@ -164,6 +166,7 @@
<xsl:for-each select="$cells">
<xsl:for-each select="key('kRowCell', concat($row, ' ', $col))"> <!-- select all 'cell' relate to one source table cell -->
<!-- <divide><xsl:value-of select="@divide"/></divide> -->
<!-- <length><xsl:value-of select="@length"/></length> -->
<xsl:choose>
<xsl:when test="@type = 'p'">
<p_len><xsl:value-of select="round(@length div @divide)"/></p_len>
Expand All @@ -177,6 +180,8 @@
</xsl:variable>
<xsl:variable name="lengths" select="xalan:nodeset($lengths_)"/>

<!-- <xsl:copy-of select="$lengths"/> -->

<xsl:for-each select="$lengths/*">
<xsl:copy>
<xsl:choose>
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/tables_only.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@

<xsl:template match="*[local-name() = 'table'][@type = 'sourcecode']" priority="2"/>

<xsl:template match="*[local-name() = 'image'][not(ancestor::*[local-name() = 'table']) and not(ancestor::*[local-name() = 'dl'])]" priority="2"/>

<xsl:template match="@*|node()" mode="simple_td">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="simple_td"/>
Expand Down

0 comments on commit 74d2f32

Please sign in to comment.