Skip to content

Commit

Permalink
Merge branch 'master' of github.com:ZUGFeRD/mustangproject
Browse files Browse the repository at this point in the history
# Conflicts:
#	library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java
  • Loading branch information
jstaerk committed Dec 20, 2024
2 parents 3260a95 + 8ae1f99 commit 9b846ff
Show file tree
Hide file tree
Showing 17 changed files with 1,033 additions and 569 deletions.
19 changes: 16 additions & 3 deletions Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.mustangproject.commandline;

import org.apache.commons.cli.*;
import org.apache.commons.io.FilenameUtils;
import org.mustangproject.CII.CIIToUBL;
import org.mustangproject.EStandard;
import org.mustangproject.FileAttachment;
Expand Down Expand Up @@ -84,6 +85,7 @@ private static String getUsage() {
+ " [--logAppend <text>]: text to be added to log line\n"
+ " Additional parameters (optional - user will be prompted if not defined)\n"
+ " [--source <filename>]: input PDF or XML file\n"
+ " [--log-as-pdf]: save log output as pdf\n"
+ " --action validateExpectInvalid validate directory expecting negative results \n"
+ " [--no-notices]: refrain from reporting notices\n"
+ " Additional parameters (optional - user will be prompted if not defined)\n"
Expand Down Expand Up @@ -357,6 +359,7 @@ public static void main(String[] args) {
options.addOption(new Option("d", "directory", true, "which directory to operate on"));
options.addOption(new Option("i", "ignorefileextension", false, "ignore non-matching file extensions"));
options.addOption(new Option("l", "listfromstdin", false, "take list of files from commandline"));
options.addOption(new Option("log-as-pdf", "log-as-pdf", false, "saving log output to pdf file"));

boolean optionsRecognized = false;
String action = "";
Expand All @@ -380,6 +383,7 @@ public static void main(String[] args) {
String format = cmd.getOptionValue("format");
String lang = cmd.getOptionValue("language");
Boolean noNotices = cmd.hasOption("no-notices");
Boolean LogAsPDF = cmd.hasOption("log-as-pdf");

String zugferdVersion = cmd.getOptionValue("version");
String zugferdProfile = cmd.getOptionValue("profile");
Expand Down Expand Up @@ -427,7 +431,7 @@ public static void main(String[] args) {
performUBL(sourceName, outName);
optionsRecognized = true;
} else if ((action != null) && (action.equals("validate"))) {
optionsRecognized = performValidate(sourceName, noNotices, cmd.getOptionValue("logAppend"));
optionsRecognized = performValidate(sourceName, noNotices, cmd.getOptionValue("logAppend"), LogAsPDF);
} else if ((action != null) && (action.equals("validateExpectValid"))) {
optionsRecognized = performValidateExpect(true, directoryName);
} else if ((action != null) && (action.equals("validateExpectInvalid"))) {
Expand All @@ -454,7 +458,7 @@ public static void main(String[] args) {

}

private static boolean performValidate(String sourceName, boolean noNotices, String logAppend) {
private static boolean performValidate(String sourceName, boolean noNotices, String logAppend, boolean createLogAsPDF) {
boolean optionsRecognized;
if (sourceName == null) {
sourceName = getFilenameFromUser("Source PDF or XML", "invoice.pdf", "pdf|xml", true, false);
Expand All @@ -466,7 +470,16 @@ private static boolean performValidate(String sourceName, boolean noNotices, Str
if (noNotices) {
zfv.disableNotices();
}
System.out.println(zfv.validate(sourceName));

String validationResultXML = zfv.validate(sourceName);
System.out.println(validationResultXML);

if( createLogAsPDF) {
ValidationLogVisualizer vlvi = new ValidationLogVisualizer();
String fileBasename = FilenameUtils.getBaseName(sourceName);

vlvi.toPDF(validationResultXML, fileBasename + "_result.pdf");
}
optionsRecognized = !zfv.hasOptionsError();
if (!zfv.wasCompletelyValid()) {
System.exit(-1);
Expand Down
40 changes: 35 additions & 5 deletions library/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -200,21 +200,51 @@
</manifest>
<manifestSections>
<manifestSection>
<name>FreeSans.ttf</name>
<name>SourceSansPro-Regular.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>FreeSerif.ttf</name>
<name>SourceSansPro-It.ttf</name>
<manifestEntries>
<Content-Type>application/x-font</Content-Type>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSansPro-Bold.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSansPro-BoldIt.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>Times-Bold.ttf</name>
<name>SourceSerifPro-Regular.ttf</name>
<manifestEntries>
<Content-Type>application/x-font</Content-Type>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSerifPro-It.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSerifPro-Bold.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>SourceSerifPro-BoldIt.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
</manifestSections>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package org.mustangproject.ZUGFeRD;

import org.apache.fop.apps.*;
import org.apache.fop.apps.io.ResourceResolverFactory;
import org.apache.fop.configuration.Configuration;
import org.apache.fop.configuration.ConfigurationException;
import org.apache.fop.configuration.DefaultConfigurationBuilder;
import org.apache.xmlgraphics.util.MimeConstants;
import org.mustangproject.ClasspathResolverURIAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.nio.charset.StandardCharsets;

public class ValidationLogVisualizer {
public enum Language {
EN,
FR,
DE
}

static final ClassLoader CLASS_LOADER = ValidationLogVisualizer.class.getClassLoader();
private static final String RESOURCE_PATH = "";
private static final Logger LOGGER = LoggerFactory.getLogger(ValidationLogVisualizer.class);

private TransformerFactory mFactory = null;
private Templates mXsltPDFTemplate = null;


public ValidationLogVisualizer() {
mFactory = new net.sf.saxon.TransformerFactoryImpl();
// fact = TransformerFactory.newInstance();
mFactory.setURIResolver(new ValidationLogVisualizer.ClasspathResourceURIResolver());
}

protected void applyXSLTToPDF(final String xmlContent, final OutputStream PDFOutstream)
throws TransformerException {
Transformer transformer = mXsltPDFTemplate.newTransformer();

transformer.transform(new StreamSource(new StringReader(xmlContent)), new StreamResult(PDFOutstream));
}

protected String toFOP(final String xmlContent)
throws TransformerException {

try {
if (mXsltPDFTemplate == null) {
mXsltPDFTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/result-pdf.xsl")));
}
} catch (TransformerConfigurationException ex) {
LOGGER.error("Failed to init XSLT templates", ex);
}

ByteArrayOutputStream baos = new ByteArrayOutputStream();

try {

applyXSLTToPDF(xmlContent, baos);

} catch (Exception e1) {
LOGGER.error("Failed to create PDF", e1);
}

return baos.toString(StandardCharsets.UTF_8);
}

public void toPDF(String xmlLogfileContent, String pdfFilename) {

// the writing part

String result = null;

/* remove file endings so that tests can also pass after checking
out from git with arbitrary options (which may include CSRF changes)
*/
try {
result = this.toFOP(xmlLogfileContent);
} catch ( TransformerException e) {
LOGGER.error("Failed to apply FOP", e);
}
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();

Configuration cfg = null;
try {
cfg = cfgBuilder.build(CLASS_LOADER.getResourceAsStream("fop-config.xconf"));
} catch (ConfigurationException e) {
throw new RuntimeException(e);
}

FopFactoryBuilder builder = new FopFactoryBuilder(new File(".").toURI(), new ClasspathResolverURIAdapter()).setConfiguration(cfg);
// Step 1: Construct a FopFactory by specifying a reference to the configuration file
// (reuse if you plan to render multiple documents!)

FopFactory fopFactory = builder.build();

fopFactory.getFontManager().setResourceResolver(
ResourceResolverFactory.createInternalResourceResolver(
new File(".").toURI(),
new ClasspathResolverURIAdapter()));

FOUserAgent userAgent = fopFactory.newFOUserAgent();

userAgent.getRendererOptions().put("pdf-a-mode", "PDF/A-3b");

// Step 2: Set up output stream.
// Note: Using BufferedOutputStream for performance reasons (helpful with FileOutputStreams).

try (OutputStream out = new BufferedOutputStream(new FileOutputStream(pdfFilename))) {

// Step 3: Construct fop with desired output format
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out);

// Step 4: Setup JAXP using identity transformer
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(); // identity transformer

// Step 5: Setup input and output for XSLT transformation
// Setup input stream
Source src = new StreamSource(new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8)));

// Resulting SAX events (the generated FO) must be piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());

// Step 6: Start XSLT transformation and FOP processing
transformer.transform(src, res);

} catch (FOPException | IOException | TransformerException e) {
LOGGER.error("Failed to create PDF", e);
}
}

private static class ClasspathResourceURIResolver implements URIResolver {
ClasspathResourceURIResolver() {
// Do nothing, just prevents synthetic access warning.
}

@Override
public Source resolve(String href, String base) throws TransformerException {
return new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/" + href));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,7 @@ private PostalTradeAddress getAddressFromNodeList(NodeList nl) {
*
* @return a List of LineItem instances
*/
@Deprecated
public List<Item> getLineItemList() {
final List<Node> nodeList = getLineItemNodes();
final List<Item> lineItemList = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ public String getUTF8() {
* for PDF embedded files in FX use getFileAttachmentsPDF()
* @deprecated use invoice.getAdditionalReferencedDocuments
*/
@Deprecated
public List<FileAttachment> getFileAttachmentsXML() {
return new ArrayList<>(Arrays.asList(importedInvoice.getAdditionalReferencedDocuments()));
}
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
94 changes: 57 additions & 37 deletions library/src/main/resources/fop-config.xconf
Original file line number Diff line number Diff line change
@@ -1,37 +1,57 @@
<fop version="1.0">

<!-- Strict user configuration -->
<strict-configuration>true</strict-configuration>

<!-- Strict FO validation -->
<strict-validation>true</strict-validation>

<!-- Base URL for resolving relative URLs -->

<!-- Font Base URL for resolving relative font URLs -->

<!-- Source resolution in dpi (dots/pixels per inch) for determining the size of pixels in SVG and bitmap images, default: 72dpi -->
<source-resolution>96</source-resolution>
<!-- Target resolution in dpi (dots/pixels per inch) for specifying the target resolution for generated bitmaps, default: 72dpi -->
<target-resolution>96</target-resolution>

<!-- default page-height and page-width, in case
value is specified as auto -->
<default-page-settings height="297mm" width="210mm"/><!-- DIN A/4 -->

<!-- etc. etc..... -->

<renderers>
<renderer mime="application/pdf">
<fonts><!-- https://xmlgraphics.apache.org/fop/1.1/fonts.html auto-embed -->
<auto-detect/>
<font kerning="no" embed-url="classpath:FreeSans.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="normal" weight="normal" />
<font-triplet name="Times-Bold" style="normal" weight="normal" />
</font>
</fonts>
<output-profile>classpath:AdobeCompat-v2.icc</output-profile>
</renderer>
</renderers>

</fop>
<fop version="1.0">

<!-- Strict user configuration -->
<strict-configuration>true</strict-configuration>

<!-- Strict FO validation -->
<strict-validation>true</strict-validation>

<!-- Base URL for resolving relative URLs -->

<!-- Font Base URL for resolving relative font URLs -->

<!-- Source resolution in dpi (dots/pixels per inch) for determining the size of pixels in SVG and bitmap images, default: 72dpi -->
<source-resolution>96</source-resolution>
<!-- Target resolution in dpi (dots/pixels per inch) for specifying the target resolution for generated bitmaps, default: 72dpi -->
<target-resolution>96</target-resolution>

<!-- default page-height and page-width, in case
value is specified as auto -->
<default-page-settings height="297mm" width="210mm"/><!-- DIN A/4 -->

<!-- etc. etc..... -->

<renderers>
<renderer mime="application/pdf">
<fonts>
<!-- https://xmlgraphics.apache.org/fop/1.1/fonts.html auto-embed -->
<auto-detect/>
<font kerning="yes" embed-url="classpath:fonts/SourceSansPro-Regular.ttf" embedding-mode="subset">
<font-triplet name="SourceSansPro" style="normal" weight="400"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSansPro-It.ttf" embedding-mode="subset">
<font-triplet name="SourceSansPro" style="italic" weight="400"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSansPro-Bold.ttf" embedding-mode="subset">
<font-triplet name="SourceSansPro" style="normal" weight="700"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSansPro-BoldIt.ttf" embedding-mode="subset">
<font-triplet name="SourceSansPro" style="italic" weight="700"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSerifPro-Regular.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="normal" weight="400"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSerifPro-It.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="italic" weight="400"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSerifPro-Bold.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="normal" weight="700"/>
</font>
<font kerning="yes" embed-url="classpath:fonts/SourceSerifPro-BoldIt.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="italic" weight="700"/>
</font>
</fonts>
<output-profile>classpath:AdobeCompat-v2.icc</output-profile>
</renderer>
</renderers>
</fop>
Loading

0 comments on commit 9b846ff

Please sign in to comment.