diff --git a/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/FormRenderData.groovy b/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/FormRenderData.groovy index 7ef7ece..7d68ac1 100644 --- a/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/FormRenderData.groovy +++ b/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/FormRenderData.groovy @@ -1,12 +1,14 @@ package com.riege.scope.gradle.forms import com.riege.jasperservice.model.PDFRawData +import com.riege.jasperservice.model.TextRawData import java.nio.file.Path class FormRenderData { PDFRawData jasperServiceData + TextRawData textData File file String fileName Set dependencies = new HashSet<>() @@ -28,6 +30,9 @@ class FormRenderData { } String getOutputName() { + if (textData != null && jasperServiceData == null) { + return fileName.concat(".txt") + } return fileName.concat(".pdf") } diff --git a/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/FormRenderDataFactory.groovy b/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/FormRenderDataFactory.groovy index e5215b4..9237743 100644 --- a/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/FormRenderDataFactory.groovy +++ b/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/FormRenderDataFactory.groovy @@ -8,11 +8,13 @@ package com.riege.scope.gradle.forms import com.riege.jasperservice.LocalJasperService import com.riege.jasperservice.backend.FormsLoader import com.riege.jasperservice.backend.FormsLoader$ +import com.riege.jasperservice.model.DocumentContext import com.riege.jasperservice.model.Image import com.riege.jasperservice.model.Report import scala.Option$ import scala.collection.JavaConverters +import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -31,23 +33,51 @@ class FormRenderDataFactory { private FormRenderData loadJson(Path file) { LocalJasperService.startUp(formPath.out.toString()) - def data = LocalJasperService.instance().read(file.toString()) + DocumentContext context + String formName def result = new FormRenderData( - jasperServiceData: data, file: file.toFile(), fileName: dataDir.relativize(file), ) - def localeOption = Option$.MODULE$.apply(data.context().locale()) - def loader = new FormsLoader(data.context(), formPath.out.toString(), false) + + if (file.toString().endsWith(".text.json")) { + result.textData = LocalJasperService.instance().readText(file.toString()) + context = result.textData.context() + formName = result.textData.formName() + } else { + result.jasperServiceData = LocalJasperService.instance().read(file.toString()) + context = result.jasperServiceData.context() + formName = result.jasperServiceData.formName() + + if (PDFWithTextSupport.isFormSupported(formName)) { + def textFile = Paths.get(file.toString().replaceFirst("\\.json\$", ".text.json")) + if(Files.exists(textFile)) { + result.textData = LocalJasperService.instance().readText(textFile.toString()) + } + result.addDependency(textFile) + } + } + + def localeOption = Option$.MODULE$.apply(context.locale()) + def loader = new FormsLoader(context, formPath.out.toString(), false) def formDirUri = loader - .getForm(data.formName(), localeOption) + .getForm(formName, localeOption) .getProperty(FormsLoader$.MODULE$.PROPERTY_FORM_DIR()) def formDir = Paths.get(new URI(formDirUri).normalize()) - def formFile = loader.getFile(data.formName() + ".jasper", localeOption).get().toPath() result.addDependency(file) - result.addDependency(formFile) - addFileDependencies(result, formDir, new Form(getFormPath(), formFile), loader) - addParameterDependencies(result, JavaConverters.mapAsJavaMap(data.data()), loader) + + if (result.jasperServiceData != null) { + def formFile = loader.getFile(result.jasperServiceData.formName() + ".jasper", localeOption).get().toPath() + result.addDependency(formFile) + addFileDependencies(result, formDir, new Form(getFormPath(), formFile), loader) + addParameterDependencies(result, JavaConverters.mapAsJavaMap(result.jasperServiceData.data()), loader) + } + + if (result.textData != null) { + def formFile = loader.getFile(result.textData.formName() + ".jasper", localeOption).get().toPath() + result.addDependency(formFile) + } + return result } diff --git a/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/PDFWithTextSupport.groovy b/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/PDFWithTextSupport.groovy new file mode 100644 index 0000000..ac41399 --- /dev/null +++ b/buildSrc/src/main/groovy/com/riege/scope/gradle/forms/PDFWithTextSupport.groovy @@ -0,0 +1,46 @@ +package com.riege.scope.gradle.forms + +import com.riege.jasperservice.model.PDFRawData +import scala.collection.JavaConverters +import scala.collection.immutable.Map$ + +class PDFWithTextSupport { + + static boolean isFormSupported(String formName) { + return formName == "CmrWaybill" + } + + static PDFRawData replaceEmbeddedText(PDFRawData data, String renderedText) { + if (data.formName() == "CmrWaybill") { + return replaceParameter(data, "mainreport.dataSource", buildCmrTextLineRows(renderedText)) + } + return data + } + + private static PDFRawData replaceParameter(PDFRawData pdfRawData, String name, Object scalaLines) { + def updatedData = pdfRawData.data().updated(name, scalaLines) + return new PDFRawData( + pdfRawData.context(), + pdfRawData.encryptPDF(), + pdfRawData.pdfa(), + pdfRawData.formName(), + pdfRawData.dataSourceParameterName(), + pdfRawData.backgroundImage(), + updatedData + ) + } + + static def buildCmrTextLineRows(String text) { + def scalaLineMaps = (text?.readLines() ?: []).collect { line -> + createSingleEntryScalaMap("cmrTextLine", line ?: "") + } + JavaConverters.asScalaBuffer(scalaLineMaps).toList() + } + + static def createSingleEntryScalaMap(String key, String value) { + def tupleSeq = JavaConverters.asScalaBuffer([ + new scala.Tuple2(key, value) + ]).toSeq() + Map$.MODULE$.apply(tupleSeq) + } +} diff --git a/buildSrc/src/main/groovy/com/riege/scope/gradle/tasks/RenderFormsTask.groovy b/buildSrc/src/main/groovy/com/riege/scope/gradle/tasks/RenderFormsTask.groovy index 6ff922e..edb1484 100644 --- a/buildSrc/src/main/groovy/com/riege/scope/gradle/tasks/RenderFormsTask.groovy +++ b/buildSrc/src/main/groovy/com/riege/scope/gradle/tasks/RenderFormsTask.groovy @@ -11,6 +11,7 @@ import com.riege.scope.gradle.forms.FormPath import com.riege.scope.gradle.forms.FormRenderData import com.riege.scope.gradle.forms.FormRenderDataCache import com.riege.scope.gradle.forms.FormRenderDataFactory +import com.riege.scope.gradle.forms.PDFWithTextSupport import com.riege.scope.gradle.forms.PdfCreator import net.sf.jasperreports.engine.JasperReport import org.gradle.api.DefaultTask @@ -21,6 +22,7 @@ import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.incremental.IncrementalTaskInputs import org.gradle.api.tasks.incremental.InputFileDetails +import java.nio.charset.StandardCharsets import java.util.concurrent.TimeoutException class RenderFormsTask extends DefaultTask { @@ -95,8 +97,9 @@ class RenderFormsTask extends DefaultTask { rebuildSet.addAll(renderList.findAll { it.dependencies.contains(change.file) }) } removed.each { change -> + rebuildSet.addAll(rebuildEntriesForRemovedFile(renderList, change.file)) if (change.file.getCanonicalPath().startsWith(dataDir.getCanonicalPath())) { - def relativeFile = new File(outputDir, "${relativeToDataDir(change.file)}.pdf") + def relativeFile = new File(outputDir, removedOutputName(change.file)) println "Removing ${relativeFile.absolutePath}" relativeFile.delete() } @@ -108,10 +111,19 @@ class RenderFormsTask extends DefaultTask { rebuildSet.each { data -> println "Rebuilding ${data.file}" try { - byte[] pdf - pdf = LocalJasperService.instance().render(data.jasperServiceData) - pdf = PdfCreator.addTestOverlay(pdf) - writePdfToOutputDir(data.outputName, pdf) + byte[] renderedBytes + if (data.jasperServiceData != null) { + def renderData = data.jasperServiceData + if (data.textData != null) { + def generatedText = LocalJasperService.instance().render(data.textData) + renderData = PDFWithTextSupport.replaceEmbeddedText(data.jasperServiceData, new String(generatedText, StandardCharsets.UTF_8)) + } + renderedBytes = LocalJasperService.instance().render(renderData) + renderedBytes = PdfCreator.addTestOverlay(renderedBytes) + } else { + renderedBytes = LocalJasperService.instance().render(data.textData) + } + writePdfToOutputDir(data.outputName, renderedBytes) } catch (Exception e) { handleRendererException(data.file, e) } @@ -158,4 +170,19 @@ class RenderFormsTask extends DefaultTask { dataDir.toPath().relativize(dataFile.toPath()).toFile() } + Collection rebuildEntriesForRemovedFile(Collection renderList, File removedFile) { + if (!removedFile.name.endsWith(".text.json")) { + return [] + } + + def pdfDataFile = new File(removedFile.path.replaceFirst(/\.text\.json$/, '.json')).canonicalFile + return renderList.findAll { it.file.canonicalFile == pdfDataFile } + } + + String removedOutputName(File removedDataFile) { + def suffix = removedDataFile.name.endsWith(".text.json") ? ".txt" : ".pdf" + return "${relativeToDataDir(removedDataFile)}${suffix}" + + } + } diff --git a/buildSrc/src/main/scala/com/riege/jasperservice/LocalJasperService.scala b/buildSrc/src/main/scala/com/riege/jasperservice/LocalJasperService.scala index 7f49efd..061cfdf 100644 --- a/buildSrc/src/main/scala/com/riege/jasperservice/LocalJasperService.scala +++ b/buildSrc/src/main/scala/com/riege/jasperservice/LocalJasperService.scala @@ -15,9 +15,9 @@ import akka.util.Timeout import better.files.File import com.riege.jasperservice.backend._ import com.riege.jasperservice.frontend.JasperServiceProtocol -import com.riege.jasperservice.model.{PDFDocument, PDFRawData} -import spray.json._ +import com.riege.jasperservice.model.{PDFDocument, PDFRawData, TextDocument, TextRawData} +import spray.json._ import scala.concurrent.duration.SECONDS import scala.concurrent.{Await, Future} @@ -66,6 +66,7 @@ class LocalJasperService(val fileStore: String, val formsStore: String) extends // ) val pdfProducer = actorOf(PDFProducer(formsStore, fileStore), "pdf-producer") + val textProducer = actorOf(TextProducer(formsStore), "text-producer") // pageDimensionResolver = actorOf(PageDimensionResolver(formsStore), "page-dimension-resolver") // textProducer = actorOf(TextProducer(formsStore), "text-producer") @@ -83,6 +84,11 @@ class LocalJasperService(val fileStore: String, val formsStore: String) extends pdfRawDataFormat.read(jsonWithDefaults).copy(encryptPDF = false) } + def readText(dataFile: String): TextRawData = { + val json = File(dataFile).lines(UTF_8).mkString.parseJson + textRawDataFormat.read(json) + } + def render(data: PDFRawData): Array[Byte] = { val pdfFuture: Future[Any] = pdfProducer ? data val result = Await.result(pdfFuture, timeout.duration) @@ -92,4 +98,11 @@ class LocalJasperService(val fileStore: String, val formsStore: String) extends tmpFile.delete() bytes } + + def render(data: TextRawData): Array[Byte] = { + val textFuture: Future[Any] = textProducer ? data + val result = Await.result(textFuture, timeout.duration) + val text = result.asInstanceOf[TextDocument] + text.content.getBytes(UTF_8) + } } diff --git a/buildSrc/src/main/scala/com/riege/jasperservice/backend/TextProducer.scala b/buildSrc/src/main/scala/com/riege/jasperservice/backend/TextProducer.scala new file mode 100644 index 0000000..1e4a103 --- /dev/null +++ b/buildSrc/src/main/scala/com/riege/jasperservice/backend/TextProducer.scala @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Riege Software International. All rights reserved. + * Use is subject to license terms. + */ + +package com.riege.jasperservice.backend + +import java.lang.StringBuilder +import java.util + +import scala.util.control.NonFatal + +import akka.actor.{Actor, ActorLogging, Props, Status} + +import com.riege.jasperservice.backend.FormsLoader.PROPERTY_FORM_DIR +import com.riege.jasperservice.functions.JasperServiceFunctions +import com.riege.jasperservice.model.{TextDocument, TextRawData} + +import net.sf.jasperreports.engine.data.JRMapCollectionDataSource +import net.sf.jasperreports.engine.export.JRTextExporter +import net.sf.jasperreports.engine.{JRParameter, JasperFillManager, JasperPrint, JasperReport} +import net.sf.jasperreports.export.{SimpleExporterInput, SimpleTextExporterConfiguration, SimpleTextReportConfiguration, SimpleWriterExporterOutput} + +object TextProducer { + + def apply(formsStore: String): Props = + Props(new TextProducer(formsStore)) + +} + +/** + * @author Andrej Golovnin + */ +class TextProducer( + formsStore: String +) extends Actor with ActorLogging { + + override def receive: Receive = { + case data: TextRawData => + try { + val document = createDocument(data) + sender() ! document + } catch { + case NonFatal(e) => + import com.riege.jasperservice.frontend.JasperServiceProtocol._ + + log.error(e, "Failed to produce a text document for the data:\n{}\n", + textRawDataFormat.write(data).prettyPrint) + sender() ! Status.Failure(BackendException(e)) + } + } + + private def createDocument(data: TextRawData): TextDocument = { + val ctx = data.context + val formLoader = new FormsLoader(ctx, formsStore) + val form = formLoader.getForm(data.formName, Option(ctx.locale)) + val exporter = getJRExporter(data) + val printable = createPrintable(data, formLoader, form) + + val out = new StringBuilder() + exporter.setExporterInput(new SimpleExporterInput(printable)) + exporter.setExporterOutput(new SimpleWriterExporterOutput(out)) + exporter.exportReport() + + if (ctx.production) { + TextDocument(out.toString) + } else { + TextDocument(out.toString, Some(form.getProperty(FormsLoader.PROPERTY_FORM_FILE))) + } + } + + private def getJRExporter(data: TextRawData): JRTextExporter = { + val ec = new SimpleTextExporterConfiguration + ec.setLineSeparator(data.lineSeparator.getOrElse("\n")) + ec.setPageSeparator(data.pageSeparator.getOrElse("\n")) + val rc = new SimpleTextReportConfiguration + rc.setCharHeight(float2Float(data.charHeight.getOrElse(12.0f))) + rc.setPageWidthInChars(int2Integer(data.pageWidthInChars.getOrElse(74))) + rc.setPageHeightInChars(int2Integer(data.pageHeightInChars.getOrElse(64))) + val exporter = new JRTextExporter + exporter.setConfiguration(ec) + exporter.setConfiguration(rc) + exporter + } + + private def createPrintable( + data: TextRawData, + formLoader: FormsLoader, + form: JasperReport + ): JasperPrint = { + val locale = data.context.locale + val localeOpt = Option(locale) + val formDir = form.getProperty(PROPERTY_FORM_DIR) + val jasperData = formLoader.toJasperData(data.data) + val map: util.Map[String, Object] = jasperData.data + map.put(PROPERTY_FORM_DIR, formDir) + map.put(JRParameter.REPORT_LOCALE, locale) + JasperServiceFunctions.setLoadFormHandler( + formName => formLoader.getForm(formName, localeOpt) + ) + try { + var dataSource: JRMapCollectionDataSource = null + val dataSourceData: util.Collection[util.Map[String, _]] = + map.get(data.dataSourceParameterName) + .asInstanceOf[util.Collection[util.Map[String, _]]] + if (dataSourceData != null) { + dataSource = new JRMapCollectionDataSource(dataSourceData) + } + + val jasperPrint = JasperFillManager.fillReport(form, map, dataSource) + + data.pageHeight.foreach(jasperPrint.setPageHeight) + data.pageWidth.foreach(jasperPrint.setPageWidth) + + jasperPrint + } finally { + JasperServiceFunctions.remove() + } + } + +} diff --git a/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/FormRenderDataFactorySpec.groovy b/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/FormRenderDataFactorySpec.groovy index ea77908..21f3a31 100644 --- a/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/FormRenderDataFactorySpec.groovy +++ b/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/FormRenderDataFactorySpec.groovy @@ -5,7 +5,6 @@ package com.riege.scope.gradle.forms - import spock.lang.Specification import java.nio.file.Paths @@ -18,15 +17,16 @@ class FormRenderDataFactorySpec extends Specification { dataDir: testResources, formPath: formPath) def testJsonFile = factory.dataDir.resolve('rawData.json') + def testJsonWithTextFile = factory.dataDir.resolve('pdfWithText.json') + def testJsonTextFile = factory.dataDir.resolve('pdfWithText.text.json') - def "extract meta data from JSON document"() { + def "extract pdf meta data from JSON document"() { when: def result = factory.load(testJsonFile) then: result.fileName == "rawData.json" result.outputName == "rawData.json.pdf" result.hasDependency(factory.formPath.out.resolve("testformdir/EExpDatPosition.jasper")) - result.hasDependency(factory.formPath.out.resolve("testformdir/EExpDatPositionLoaded.jasper")) result.hasDependency(factory.formPath.out.resolve("testformdir/EExpDatPositionLoadedLocale_nl.jasper")) result.hasDependency(factory.formPath.out.resolve("testformdir/EExpDatPosition.png")) result.hasDependency(factory.formPath.out.resolve("testformdir/background_loaded.svg")) @@ -41,6 +41,29 @@ class FormRenderDataFactorySpec extends Specification { result.hasDependency(factory.formPath.out.resolve("footer.jasper")) } + def "extract pdf with embedded text meta data from JSON document"() { + when: + def result = factory.load(testJsonWithTextFile) + then: + result.fileName == "pdfWithText.json" + result.outputName == "pdfWithText.json.pdf" + result.hasDependency(factory.formPath.out.resolve("testformdir/EExpDatPosition.jasper")) + result.hasDependency(factory.formPath.out.resolve("testformdir/EExpDatPosition.png")) + result.hasDependency(factory.formPath.out.resolve("testformdir/background_loaded.svg")) + result.hasDependency(factory.formPath.out.resolve("background_loaded.png")) + result.hasDependency(factory.formPath.out.resolve("testformdir/background_loaded_locale_nl.svg")) + result.hasDependency(factory.formPath.out.resolve("testformdir/CmrWaybill.jasper")) + result.hasDependency(factory.formPath.out.resolve("testformdir/background.svg")) + result.hasDependency(factory.formPath.out.resolve("background.png")) + result.hasDependency(factory.formPath.out.resolve("optionalBackground.svg")) + result.hasDependency(factory.dataDir.resolve("pdfWithText.json")) + result.hasDependency(factory.formPath.out.resolve("header.jasper")) + result.hasDependency(factory.formPath.out.resolve("footer.jasper")) + + result.hasDependency(factory.dataDir.resolve("pdfWithText.text.json")) + result.hasDependency(factory.formPath.out.resolve("testformdir/CmrWaybillText.jasper")) + } + def "extract files referenced via \$P{formDir} expressions"() { setup: def testFile = ''' @@ -74,4 +97,15 @@ class FormRenderDataFactorySpec extends Specification { result.hasDependency(formPath.out.resolve("testformdir/EExpDat_circular.jasper")) } + def "extract text meta data from JSON document"() { + when: + def result = factory.load(testJsonTextFile) + then: + result.fileName == "pdfWithText.text.json" + result.outputName == "pdfWithText.text.json.txt" + result.textData != null + result.jasperServiceData == null + result.hasDependency(factory.dataDir.resolve("pdfWithText.text.json")) + result.hasDependency(factory.formPath.out.resolve("testformdir/CmrWaybillText.jasper")) + } } diff --git a/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/FormRenderDataSpec.groovy b/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/FormRenderDataSpec.groovy index 7acf1ea..3bd3a28 100644 --- a/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/FormRenderDataSpec.groovy +++ b/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/FormRenderDataSpec.groovy @@ -5,6 +5,8 @@ package com.riege.scope.gradle.forms +import com.riege.jasperservice.model.PDFRawData +import com.riege.jasperservice.model.TextRawData import spock.lang.Specification class FormRenderDataSpec extends Specification { @@ -42,4 +44,27 @@ class FormRenderDataSpec extends Specification { data.dependencies.every {it.isAbsolute()} } + def "Output name adds .txt for plain text"() { + data.fileName = INPUT_NAME + data.textData = new TextRawData(null, "testForm", null, null, null, null, null, null, null, null, null) + expect: + data.outputName == OUTPUT_NAME + where: + INPUT_NAME | OUTPUT_NAME + "test.html" | "test.html.txt" + "bla" | "bla.txt" + } + + def "Output name adds .pdf for PDF with embedded plain text"() { + data.fileName = INPUT_NAME + data.jasperServiceData = new PDFRawData(null, false, false, "testPDFForm", null, null, null) + data.textData = new TextRawData(null, "testForm", null, null, null, null, null, null, null, null, null) + expect: + data.outputName == OUTPUT_NAME + where: + INPUT_NAME | OUTPUT_NAME + "test.html" | "test.html.pdf" + "bla" | "bla.pdf" + } + } diff --git a/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/PDFWithTextSupportSpec.groovy b/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/PDFWithTextSupportSpec.groovy new file mode 100644 index 0000000..8a5a324 --- /dev/null +++ b/buildSrc/src/test/groovy/com/riege/scope/gradle/forms/PDFWithTextSupportSpec.groovy @@ -0,0 +1,101 @@ +package com.riege.scope.gradle.forms + +import com.riege.jasperservice.LocalJasperService +import scala.collection.JavaConverters +import spock.lang.Specification + +import java.nio.file.Paths + +class PDFWithTextSupportSpec extends Specification { + + static def testResources = Paths.get("src/test/resources").toAbsolutePath() + static def formPath = new FormPath(testResources, testResources) + static def factory = new FormRenderDataFactory( + dataDir: testResources, + formPath: formPath) + + static def testJsonFile = factory.dataDir.resolve('rawData.json') + static def testJsonWithTextFile = factory.dataDir.resolve('pdfWithText.json') + static def testJsonTextFile = factory.dataDir.resolve('pdfWithText.text.json') + + def "replaceEmbeddedText keeps PDFRawData metadata unchanged"() { + setup: + LocalJasperService.startUp(formPath.out.toString()) + def baseData = LocalJasperService.instance().read(FILE.toString()) + + when: + def replaced = PDFWithTextSupport.replaceEmbeddedText(baseData, "x") + + then: + replaced.context() == baseData.context() + replaced.encryptPDF() == baseData.encryptPDF() + replaced.pdfa() == baseData.pdfa() + replaced.formName() == baseData.formName() + replaced.dataSourceParameterName() == baseData.dataSourceParameterName() + replaced.backgroundImage() == baseData.backgroundImage() + + where: + FILE | MEH + testJsonFile | true + testJsonTextFile | true + testJsonWithTextFile | true + } + + def "replaceParameters does not replace anything for EExpDat"() { + setup: + LocalJasperService.startUp(formPath.out.toString()) + def baseData = LocalJasperService.instance().read(testJsonFile.toString()) + + when: + def replaced = PDFWithTextSupport.replaceEmbeddedText(baseData, "first\n\nthird") + + then: + replaced == baseData + } + + def "replaceParameters replaces mainreport datasource with expected lines for CmrWaybill"() { + setup: + LocalJasperService.startUp(formPath.out.toString()) + def baseData = LocalJasperService.instance().read(testJsonWithTextFile.toString()) + + when: + def replaced = PDFWithTextSupport.replaceEmbeddedText(baseData, "first\n\nthird") + def javaMap = JavaConverters.mapAsJavaMap(replaced.data()) + def scalaLines = javaMap.get("mainreport.dataSource") + def lines = JavaConverters.seqAsJavaList(scalaLines) + def row1 = JavaConverters.mapAsJavaMap(lines[0]) + def row2 = JavaConverters.mapAsJavaMap(lines[1]) + def row3 = JavaConverters.mapAsJavaMap(lines[2]) + + then: + lines.size() == 3 + row1.get("cmrTextLine") == "first" + row2.get("cmrTextLine") == "" + row3.get("cmrTextLine") == "third" + } + + def "createSingleEntryScalaMap creates scala map with one entry"() { + when: + def scalaMap = PDFWithTextSupport.createSingleEntryScalaMap("cmrTextLine", "abc") + def javaMap = JavaConverters.mapAsJavaMap(scalaMap) + + then: + javaMap.size() == 1 + javaMap.get("cmrTextLine") == "abc" + } + + def "buildCmrTextLineRows creates scala list of scala maps"() { + when: + def scalaLines = PDFWithTextSupport.buildCmrTextLineRows("a\n\nc") + def lines = JavaConverters.seqAsJavaList(scalaLines) + def row1 = JavaConverters.mapAsJavaMap(lines[0]) + def row2 = JavaConverters.mapAsJavaMap(lines[1]) + def row3 = JavaConverters.mapAsJavaMap(lines[2]) + + then: + lines.size() == 3 + row1.get("cmrTextLine") == "a" + row2.get("cmrTextLine") == "" + row3.get("cmrTextLine") == "c" + } +} diff --git a/buildSrc/src/test/groovy/com/riege/scope/gradle/tasks/RenderFormsTaskSpec.groovy b/buildSrc/src/test/groovy/com/riege/scope/gradle/tasks/RenderFormsTaskSpec.groovy index 1515dac..f68e8fb 100644 --- a/buildSrc/src/test/groovy/com/riege/scope/gradle/tasks/RenderFormsTaskSpec.groovy +++ b/buildSrc/src/test/groovy/com/riege/scope/gradle/tasks/RenderFormsTaskSpec.groovy @@ -5,7 +5,7 @@ package com.riege.scope.gradle.tasks -import org.gradle.api.internal.changedetection.changes.RebuildIncrementalTaskInputs +import org.gradle.api.tasks.incremental.InputFileDetails import org.gradle.testfixtures.ProjectBuilder import spock.lang.Specification @@ -59,4 +59,68 @@ class RenderFormsTaskSpec extends Specification { tempDir.deleteDir() } + def "removed text json deletes txt output"() { + setup: + File tempOutputDir = File.createTempDir("RenderFormsTaskSpec", "") + File tempDataDir = File.createTempDir("RenderFormsTaskSpecData", "") + def nested = new File(tempDataDir, "nested") + nested.mkdirs() + def removedInput = new File(nested, "document.text.json") + removedInput.text = "{}" + def outputFile = new File(tempOutputDir, "nested/document.text.json.txt") + outputFile.parentFile.mkdirs() + outputFile.text = "stale" + def project = ProjectBuilder.builder().build() + def task = project.task('testTaskDeleteTxt', type: RenderFormsTask) { + dataDir = tempDataDir + outputDir = tempOutputDir + localFormDir = dataDir + formSrcDir = dataDir + errorForm = new File("src/test/resources/ErrorPDF.jasper") + } + def removed = [Stub(InputFileDetails) { getFile() >> removedInput }] as ArrayList + + when: + task.calculateRebuildSet([], [] as ArrayList, removed) + + then: + !outputFile.exists() + + cleanup: + tempOutputDir.deleteDir() + tempDataDir.deleteDir() + } + + def "removed text json rebuilds sibling pdf input"() { + setup: + File tempOutputDir = File.createTempDir("RenderFormsTaskSpec", "") + File tempDataDir = File.createTempDir("RenderFormsTaskSpecData", "") + def nested = new File(tempDataDir, "nested") + nested.mkdirs() + def pdfInput = new File(nested, "document.json") + pdfInput.text = "{}" + def removedInput = new File(nested, "document.text.json") + removedInput.text = "{}" + def renderData = new com.riege.scope.gradle.forms.FormRenderData(file: pdfInput, fileName: "nested/document.json") + def project = ProjectBuilder.builder().build() + def task = project.task('testTaskRebuildSiblingPdf', type: RenderFormsTask) { + dataDir = tempDataDir + outputDir = tempOutputDir + localFormDir = dataDir + formSrcDir = dataDir + errorForm = new File("src/test/resources/ErrorPDF.jasper") + } + def removed = [Stub(InputFileDetails) { getFile() >> removedInput }] as ArrayList + + when: + def rebuildSet = task.calculateRebuildSet([renderData], [] as ArrayList, removed) + + then: + rebuildSet == [renderData] as Set + + cleanup: + tempOutputDir.deleteDir() + tempDataDir.deleteDir() + } + } diff --git a/buildSrc/src/test/resources/pdfWithText.json b/buildSrc/src/test/resources/pdfWithText.json new file mode 100644 index 0000000..4fa65c5 --- /dev/null +++ b/buildSrc/src/test/resources/pdfWithText.json @@ -0,0 +1,73 @@ +{ + "formName": "CmrWaybill", + "dataSourceParameterName": "custom.dataSource", + "context": { + "production": false, + "legalEntityCode": "NL", + "organizationOID": "4bf7716f-b56f-4523-93db-5484fea8bca0", + "systemID": "SCOPE-NLCUS", + "domain": "NLCUS", + "countryOID": "905ae091-2b9c-470d-8c89-38f8f7b79d00", + "branchCode": "ORGBRA", + "legalEntityOID": "0d3a7a45-1b11-4c21-8df1-b94d7400fa4c", + "locale": "nl-NL", + "mimeType": "x-scope/nl-customs-ags-dmsdoc", + "version": "10.1.940", + "organizationCode": "testFormDir", + "entityOID": "84e4305d-beca-441d-bf0e-9cbb928e19c0", + "countryCode": "NL", + "branchOID": "30880a0c-a34b-4305-8696-8a83db4f3def", + "userID": "testuser", + "mimeTypeDescription": "EExpDat Test" + }, + "data": { + "currentTime": "10:02", + "backgroundImage": { + "$otype": "IMAGE", + "name": "background", + "locale": "nl-NL" + }, + "formLanguage": { + "$otype": "LOCALE", + "locale": "nl-NL" + }, + "declarationVersionID": "1", + "currentDateTime": "16-2-2018 10:02", + "optionalBackgroundImage": { + "$otype": "IMAGE", + "name": "optionalBackground", + "ext": "svg", + "locale": "nl-NL" + }, + "rasterBackgroundImage": { + "$otype": "IMAGE", + "name": "background", + "ext": "png", + "locale": "nl-NL" + }, + "responsibleUser": "Test User", + "printTime": "10:02", + "scope.version.major": { + "$otype": "INT", + "value": 10 + }, + "header": { + "$otype": "REPORT", + "name": "header", + "language": "nl-NL" + }, + "scope.version.minor": { + "$otype": "INT", + "value": 1 + }, + "footer": { + "$otype": "REPORT", + "name": "footer", + "language": "nl-NL" + }, + "currentDate": "16-2-2018" + }, + "pdfa": false, + "large": false, + "encryptPDF": true +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/pdfWithText.text.json b/buildSrc/src/test/resources/pdfWithText.text.json new file mode 100644 index 0000000..9303982 --- /dev/null +++ b/buildSrc/src/test/resources/pdfWithText.text.json @@ -0,0 +1,73 @@ +{ + "formName": "CmrWaybillText", + "dataSourceParameterName": "custom.dataSource", + "context": { + "production": false, + "legalEntityCode": "NL", + "organizationOID": "4bf7716f-b56f-4523-93db-5484fea8bca0", + "systemID": "SCOPE-NLCUS", + "domain": "NLCUS", + "countryOID": "905ae091-2b9c-470d-8c89-38f8f7b79d00", + "branchCode": "ORGBRA", + "legalEntityOID": "0d3a7a45-1b11-4c21-8df1-b94d7400fa4c", + "locale": "nl-NL", + "mimeType": "x-scope/nl-customs-ags-dmsdoc", + "version": "10.1.940", + "organizationCode": "testFormDir", + "entityOID": "84e4305d-beca-441d-bf0e-9cbb928e19c0", + "countryCode": "NL", + "branchOID": "30880a0c-a34b-4305-8696-8a83db4f3def", + "userID": "testuser", + "mimeTypeDescription": "EExpDat Test" + }, + "data": { + "currentTime": "10:02", + "backgroundImage": { + "$otype": "IMAGE", + "name": "background", + "locale": "nl-NL" + }, + "formLanguage": { + "$otype": "LOCALE", + "locale": "nl-NL" + }, + "declarationVersionID": "1", + "currentDateTime": "16-2-2018 10:02", + "optionalBackgroundImage": { + "$otype": "IMAGE", + "name": "optionalBackground", + "ext": "svg", + "locale": "nl-NL" + }, + "rasterBackgroundImage": { + "$otype": "IMAGE", + "name": "background", + "ext": "png", + "locale": "nl-NL" + }, + "responsibleUser": "Test User", + "printTime": "10:02", + "scope.version.major": { + "$otype": "INT", + "value": 10 + }, + "header": { + "$otype": "REPORT", + "name": "header", + "language": "nl-NL" + }, + "scope.version.minor": { + "$otype": "INT", + "value": 1 + }, + "footer": { + "$otype": "REPORT", + "name": "footer", + "language": "nl-NL" + }, + "currentDate": "16-2-2018" + }, + "pdfa": false, + "large": false, + "encryptPDF": true +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/testformdir/CmrWaybill.jasper b/buildSrc/src/test/resources/testformdir/CmrWaybill.jasper new file mode 100644 index 0000000..5ec1b2b Binary files /dev/null and b/buildSrc/src/test/resources/testformdir/CmrWaybill.jasper differ diff --git a/buildSrc/src/test/resources/EExpDat.jrxml b/buildSrc/src/test/resources/testformdir/CmrWaybill.jrxml similarity index 100% rename from buildSrc/src/test/resources/EExpDat.jrxml rename to buildSrc/src/test/resources/testformdir/CmrWaybill.jrxml diff --git a/buildSrc/src/test/resources/testformdir/CmrWaybillText.jasper b/buildSrc/src/test/resources/testformdir/CmrWaybillText.jasper new file mode 100644 index 0000000..5ec1b2b Binary files /dev/null and b/buildSrc/src/test/resources/testformdir/CmrWaybillText.jasper differ diff --git a/tools/copy_source_from_jasper_service.sh b/tools/copy_source_from_jasper_service.sh index 2f93972..9dab41f 100755 --- a/tools/copy_source_from_jasper_service.sh +++ b/tools/copy_source_from_jasper_service.sh @@ -42,13 +42,13 @@ echo Written dependencies to "buildSrc/build.gradle" rm deps_for_gradle -copy_source_file lib copy_source_file src/main/scala/com/riege/jasperservice/frontend/JasperServiceProtocol.scala copy_source_file src/main/scala/com/riege/jasperservice/backend/BackendException.scala copy_source_file src/main/scala/com/riege/jasperservice/backend/PrintException.scala copy_source_file src/main/scala/com/riege/jasperservice/backend/FormsLoader.scala copy_source_file src/main/scala/com/riege/jasperservice/backend/PDFProducer.scala copy_source_file src/main/scala/com/riege/jasperservice/backend/PDFUtils.scala +copy_source_file src/main/scala/com/riege/jasperservice/backend/TextProducer.scala copy_source_file src/main/scala/com/riege/jasperservice/model/Report.scala copy_source_file src/main/scala/com/riege/jasperservice/model/Simulations.scala copy_source_file src/main/scala/com/riege/jasperservice/model/PageDimension.scala