1- package org.ods.doc.gen.html .conversor
1+ package org.ods.doc.gen.pdf .conversor
22
33import com.github.jknack.handlebars.Handlebars
44import com.github.jknack.handlebars.io.FileTemplateLoader
5+ import groovy.util.logging.Slf4j
6+ import org.apache.commons.io.output.TeeOutputStream
57import org.apache.pdfbox.pdmodel.PDDocument
68import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo
79import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink
@@ -11,77 +13,71 @@ import org.springframework.stereotype.Service
1113import java.nio.file.Files
1214import java.nio.file.Path
1315
16+ @Slf4j
1417@Service
1518class HtmlToPDFService {
16- // Execute a document template with the necessary data
19+
1720 String executeTemplate (Path path , Object data ) {
18- // TODO: throw if template variables are not provided
1921 def loader = new FileTemplateLoader (" " , " " )
2022 return new Handlebars (loader). compile(path. toString()). apply(data)
2123 }
2224
2325 Path convert (Path documentHtmlFile , Map data = null ) {
2426 Path documentPDFFile = Files . createTempFile(" document" , " .pdf" )
27+ List cmd = generateCmd(data, documentHtmlFile, documentPDFFile)
28+ executeCmd(documentHtmlFile, cmd)
29+ fixDestinations(documentPDFFile. toFile())
30+ return documentPDFFile
31+ }
2532
33+ private List<String > generateCmd (Map data , Path documentHtmlFile , Path documentPDFFile ) {
2634 def cmd = [" wkhtmltopdf" , " --encoding" , " UTF-8" , " --no-outline" , " --print-media-type" ]
2735 cmd << " --enable-local-file-access"
2836 cmd. addAll([" -T" , " 40" , " -R" , " 25" , " -B" , " 25" , " -L" , " 25" ])
37+ addHeader(data, cmd)
38+ cmd. addAll([" --footer-center" , " 'Page [page] of [topage]'" , " --footer-font-size" , " 10" ])
39+ setOrientation(data, cmd)
40+ cmd << documentHtmlFile. toFile(). absolutePath
41+ cmd << documentPDFFile. toFile(). absolutePath
42+ return cmd
43+ }
2944
45+ private void setOrientation (Map data , ArrayList<String > cmd ) {
46+ if (data?. metadata?. orientation) {
47+ cmd. addAll([" --orientation" , data. metadata. orientation])
48+ }
49+ }
50+
51+ private void addHeader (Map data , ArrayList<String > cmd ) {
3052 if (data?. metadata?. header) {
3153 if (data. metadata. header. size() > 1 ) {
3254 cmd. addAll([" --header-center" , """ ${ data.metadata.header[0]}
3355${ data.metadata.header[1]} """ ])
3456 } else {
3557 cmd. addAll([" --header-center" , data. metadata. header[0 ]])
3658 }
37-
3859 cmd. addAll([" --header-font-size" , " 10" , " --header-spacing" , " 10" ])
3960 }
40-
41- cmd. addAll([" --footer-center" , " 'Page [page] of [topage]'" , " --footer-font-size" , " 10" ])
42-
43- if (data?. metadata?. orientation) {
44- cmd. addAll([" --orientation" , data. metadata. orientation])
45- }
46-
47- cmd << documentHtmlFile. toFile(). absolutePath
48- cmd << documentPDFFile. toFile(). absolutePath
49-
50- println " [INFO]: executing cmd: ${ cmd} "
51- def result = shell(cmd)
52- if (result. rc != 0 ) {
53- println " [ERROR]: ${ cmd} has exited with code ${ result.rc} "
54- println " [ERROR]: ${ result.stderr} "
55- throw new IllegalStateException (
56- " PDF Creation of ${ documentHtmlFile} failed!\r :${ result.stderr} \r :Error code:${ result.rc} " )
57- }
58-
59- fixDestinations(documentPDFFile. toFile())
60-
61- return documentPDFFile
6261 }
6362
64- // Execute a command in the shell
65- private def Map shell ( List< String > cmd ) {
63+ private void executeCmd ( documentHtmlFile , List< String > cmd ) {
64+ log . info " executing cmd: ${ cmd } "
6665 def proc = cmd. execute()
67- // for some VERY complex docs - the process implementation hangs on wait() ...
68- // switching to the below - seem to work but gets a weird NPE...
69- // java.lang.NullPointerException: Cannot invoke method call() on null object
70- // at org.ods.doc.gen.services.PdfGenerationService$Util.shell(PdfGenerationService.groovy:193)
71- ByteArrayOutputStream bosOut = new ByteArrayOutputStream ()
72- ByteArrayOutputStream bosErr = new ByteArrayOutputStream ()
73- try
74- {
75- proc. waitForProcessOutput(bosOut, bosErr)
76- } catch (NullPointerException wtfEx) {
77- //
66+ Path tempFilePath = Files . createTempFile(" shell" , " .bin" )
67+ File tempFile = tempFilePath. toFile()
68+ FileOutputStream tempFileOutputStream = new FileOutputStream (tempFile)
69+ def errOutputStream = new TeeOutputStream (tempFileOutputStream, System . err)
70+ try {
71+ proc. waitForProcessOutput(System . out, errOutputStream)
72+ } finally {
73+ tempFileOutputStream. close()
7874 }
7975
80- return [
81- rc : proc. exitValue(),
82- stderr : bosErr . toString(),
83- stdout : bosOut . toString( )
84- ]
76+ if (proc . exitValue() != 0 ) {
77+ String errorDesc = " ${ documentHtmlFile } failed: code: ${ proc.exitValue()} \r Description: ${ tempFile.text } "
78+ log . error errorDesc
79+ throw new IllegalStateException (errorDesc )
80+ }
8581 }
8682
8783 /**
@@ -99,6 +95,7 @@ ${data.metadata.header[1]}"""])
9995 def doc = PDDocument . load(file)
10096 fixDestinations(doc)
10197 doc. save(file)
98+ doc. close()
10299 }
103100
104101 /**
0 commit comments