Apache fop -- welche Möglichkeit gibt es um den Fortschritt anzuzeigen (Progressbar)

Hein_nieH

Bekanntes Mitglied
Hallo,

ich nutze die Bibliothek Apache fop um aus einer großen xml-Datei via xslt eine pdf-Datei zu erzeugen.
Das funktioniert jetzt auch alles wie es soll. Leider dauert es einige Sekunden bis die pdf-Datei generiert wurde.
Gerne würde ich den aktuellen Fortschritt in einem Progressbar oder Progressmonitor sichtbar machen.

Leider finde ich keinen Ansatz dazu. Ich habe mich an die Codebeispiele in der fop-Bibliothek gehalten.
Hat jemand einen Lösungsansatz?

Gruss Hein_nieH
 
Wie viele Seiten PDF werden eigentlich erzeugt? Selbst bei 1500-1800 Seiten zeige ich nur an: "Bitte haben Sie ein paar Sekunden Geduld, Ihr Dokument wird gerade erstellt." - mit so einem "loading gears"-Gif daunter. Ich denke das dauert nicht länger als 10-15 Sekunden. Darüber bekommt der Nutzer dann eine Mail mit Download-Link für die nächsten 48 Stunden oder auch direkt einen Slot in der Druckerei. Sehr lange darf das aber auch nicht dauern. Stell dir mal vor du hast gerade 10 Nutzer, die so große PDF's erstellen wollen. Dann ist es notwendig so schnell wie möglich den Speicher wieder frei zu bekommen. Auch die Druckerei kostet ganz schön viel Geld, das will man ja nicht für Wartezeit ausgeben.
 
Hallo

es sind etwa 970 - 1000 Seiten.
Das dauert auf meinem Rechner etwa 15 Sekunden.
Die Motivation war es eine perfekte Lösung zu schaffen.

Gruss Hein_nieH
 
Mich hat das Thema selbst mal interessiert und ich habe mal etwas herumgespielt. Das Ergebnis will ich hier mal kurz zeigen. Es ist noch nicht perfekt, mehr so ein proof of concept. Die volle Länge würde den Rahmen hier sprengen, also etwas gekürzt. Der Fortschritt wird über eine eigene FOP-Extension: fox:progress getrackt. Das Element wird nicht in PDF umgewandelt, sondern dafür verwendet ein Event für den Fortschritt auszulösen. Der Fortschritt kann dann per Eventlistener abgeholt werden. Ich hoffe der Code + XML/XSL ist weitgehend selbsterklärend. Feedback willkommen.

XML:
        <xsl:element name="fox:progress">
            <xsl:attribute name="value">
                <xsl:value-of select="$count_factor *  position()" />
            </xsl:attribute>
        </xsl:element>
Java:
        final EventBroadcaster eventBroadcaster = foUserAgent.getEventBroadcaster();
        eventBroadcaster.addEventListener(new ProgressEventListener());
        fopFactory.addElementMapping(new ProgressElementMapping(eventBroadcaster));


[CODE lang="xml" title="test.xml"]<?xml version="1.0"?>

<employees>
<companyname>FOP Inc.</companyname>

<employee>
<id>101</id>
<name>Jürgen</name>
<designation>Manager</designation>
</employee>

<employee>
<id>102</id>
<name>Emil</name>
<designation>Executive</designation>
</employee>

<employee>
<id>103</id>
<name>Klaus</name>
<designation>Executive</designation>
</employee>

</employees>
[/CODE]
[CODE lang="xml" title="test.xsl"]<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"
exclude-result-prefixes="fo">

<xsl:variable name="count_factor" select="100 div count(/employees/employee)" />

<xsl:template match="employees">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="simpleA4"
page-height="29.7cm" page-width="21cm" margin-top="2cm"
margin-bottom="2cm" margin-left="2cm" margin-right="2cm">
<fo:region-body />
</fo:simple-page-master>
</fo:layout-master-set>
<fo😛age-sequence master-reference="simpleA4">
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="16pt" font-weight="bold"
space-after="5mm">
Company Name:
<xsl:value-of select="companyname" />
</fo:block>
<fo:block font-size="10pt">
<fo:table table-layout="fixed" width="100%"
border-collapse="separate">
<fo:table-column column-width="4cm" />
<fo:table-column column-width="4cm" />
<fo:table-column column-width="5cm" />
<fo:table-body>
<xsl:apply-templates select="employee" />
</fo:table-body>
</fo:table>
</fo:block>
</fo:flow>
</fo😛age-sequence>
</fo:root>
</xsl:template>
<xsl:template match="employee">
<fo:table-row>
<xsl:if test="designation = 'Manager'">
<xsl:attribute name="font-weight">bold</xsl:attribute>
</xsl:if>
<fo:table-cell>
<fo:block>
<xsl:value-of select="id" />
</fo:block>
</fo:table-cell>

<fo:table-cell>
<fo:block>
<xsl:value-of select="name" />
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>
<xsl:value-of select="designation" />
</fo:block>
</fo:table-cell>
</fo:table-row>
<xsl:element name="fox😛rogress">
<xsl:attribute name="value">
<xsl:value-of select="$count_factor * position()" />
</xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>[/CODE]
[CODE lang="java" title="FopProgressTest.java"]public class FopProgressTest {

private static final class Progress extends FONode {

private String elementName;
private final EventBroadcaster eventBroadcaster;

Progress(final FONode parent, final EventBroadcaster eventBroadcaster) {
super(parent);
this.eventBroadcaster = eventBroadcaster;
}

@Override
public void processNode(final String elementName, final Locator locator,
final Attributes attlist, final PropertyList propertyList) throws FOPException {
setLocator(locator);
this.elementName = elementName;
final Event event = new Event(this, this.elementName, EventSeverity.INFO,
Event.paramsBuilder().param("value", attlist.getValue("value")).build());
this.eventBroadcaster.broadcastEvent(event);
}

@Override
public String getLocalName() {
return this.elementName;
}

@Override
public String getNamespaceURI() {
return ExtensionElementMapping.URI;
}

@Override
public String getNormalNamespacePrefix() {
return null;
}

}

private static final class ProgressElementMapping extends ElementMapping {

private static class ProgressMaker extends ElementMapping.Maker {

private final EventBroadcaster eventBroadcaster;

public ProgressMaker(final EventBroadcaster eventBroadcaster) {
this.eventBroadcaster = eventBroadcaster;
}

@Override
public FONode make(final FONode parent) {
return new Progress(parent, this.eventBroadcaster);
}
}

private final EventBroadcaster eventBroadcaster;

public ProgressElementMapping(final EventBroadcaster eventBroadcaster) {
this.eventBroadcaster = eventBroadcaster;
this.namespaceURI = ExtensionElementMapping.URI;
}

@Override
protected void initialize() {
if (this.foObjs == null) {
this.foObjs = new HashMap<>();
this.foObjs.put("progress", new ProgressMaker(this.eventBroadcaster));
}
}

}

private static final class ProgressEventListener implements EventListener {

private static final StringBuilder SB = new StringBuilder();
private static final char[] chars = new char[106];
static {
Arrays.fill(chars, '\b');
}

@Override
public void processEvent(final Event event) {
if ("progress".equals(event.getEventID())) {
final String s = (String) event.getParams().get("value");
final int p = Integer.parseInt(s.split("\\.")[0]);
SB.setLength(0);
for (int i = 0; i < 100; i++) {
SB.append(i < p ? '█' : '░');
}
System.out.print(chars);
System.out.print("[" + String.format("%-100s", SB.toString()) + "] " + p + "%");
}
}

}

public static void main(final String[] args) {
final FopProgressTest fopTest = new FopProgressTest();
try {
//fopTest.transform("test.xsl", "test.xml", "test.fo");
fopTest.convertToPDF();
} catch (final FOPException | IOException | TransformerException e) {
e.printStackTrace();
}
System.out.println();
}

private void convertToPDF() throws IOException, FOPException, TransformerException {
final File xsltFile = new File("test.xsl");
final StreamSource xmlSource = new StreamSource(new File("test.xml"));
final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());
final FOUserAgent foUserAgent = fopFactory.newFOUserAgent();

final EventBroadcaster eventBroadcaster = foUserAgent.getEventBroadcaster();
eventBroadcaster.addEventListener(new ProgressEventListener());
fopFactory.addElementMapping(new ProgressElementMapping(eventBroadcaster));

try (OutputStream out = new java.io.FileOutputStream("test.pdf")) {
final Fop fop = fopFactory.newFop(org.apache.xmlgraphics.util.MimeConstants.MIME_PDF, foUserAgent, out);
final TransformerFactory factory = TransformerFactory.newInstance();
final Transformer transformer = factory.newTransformer(new StreamSource(xsltFile));
final Result res = new SAXResult(fop.getDefaultHandler());
transformer.transform(xmlSource, res);
}
}

public void transform(final String inputXSL, final String inputXML, final String outfile)
throws TransformerException {
final Result out = new StreamResult(outfile);
final StreamSource xml = new StreamSource(inputXML);
final StreamSource xsl = new StreamSource(inputXSL);
final Transformer transformer = TransformerFactory.newInstance().newTransformer(xsl);
transformer.transform(xml, out);
}

}[/CODE]
 

Zurück
Oben