diff --git a/.gitignore b/.gitignore index 9964ce99..124b5118 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,7 @@ bin/ .DS_Store ### postgres dev container data ### -.pgdata/ \ No newline at end of file +.pgdata/ + +### AI +.claude/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f1e43962..9ffe7d7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) - Added sample input files for DocBook example to the resources folder - deployStyles now creates wfd as well as jld style definition for interactive - A warning will now be printed if an unsupported GA version of IPS is detected +- PDF tagging support and alternate text option for tables +- PDF tagging support for paragraph styles including support in export/import mapping scripts +- Alternate text option for images including support in export/import mapping scripts +- New Hyperlink text content ### Changed diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesExport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesExport.groovy index 0e16e4bf..0d6d84a5 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesExport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesExport.groovy @@ -27,7 +27,7 @@ static void run(Migration migration, Path imagesDstPath) { imagesDstPath.toFile().createParentDirectories() imagesDstPath.toFile().withWriter { writer -> - def headers = ["id", "name", "sourcePath", "imageType", "targetFolder", "status", "skip", "skipPlaceholder", "skipReason", Mapping.displayHeader("originalName", true), Mapping.displayHeader("originLocations", true)] + def headers = ["id", "name", "sourcePath", "imageType", "targetFolder", "alternateText", "status", "skip", "skipPlaceholder", "skipReason", Mapping.displayHeader("originalName", true), Mapping.displayHeader("originLocations", true)] writer.writeLine(headers.join(",")) images.each { obj -> def status = migration.statusTrackingRepository.findLastEventRelevantToOutput(obj.id, @@ -40,6 +40,7 @@ static void run(Migration migration, Path imagesDstPath) { builder.append("," + Csv.serialize(obj.sourcePath)) builder.append("," + Csv.serialize(obj.imageType)) builder.append("," + Csv.serialize(obj.targetFolder)) + builder.append("," + Csv.serialize(obj.alternateText)) builder.append("," + Csv.serialize(status.class.simpleName)) builder.append("," + Csv.serialize(obj.skip.skipped)) builder.append("," + Csv.serialize(obj.skip.placeholder)) diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesImport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesImport.groovy index 8ab90e9b..0afde167 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesImport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ImagesImport.groovy @@ -55,6 +55,9 @@ static void run(Migration migration, Path imagesFilePath) { def newTargetFolder = Csv.deserialize(values.get("targetFolder"), String.class) Mapping.mapProp(existingMapping, existingImage, "targetFolder", newTargetFolder) + def newAlternateText = Csv.deserialize(values.get("alternateText"), String.class) + Mapping.mapProp(existingMapping, existingImage, "alternateText", newAlternateText) + def csvStatus = values.get("status") if (status != null && csvStatus == "Active" && status.class.simpleName != "Active") { migration.statusTrackingRepository.active(existingImage.id, ResourceType.Image, [reason: "Manual"]) diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ParagraphStylesExport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ParagraphStylesExport.groovy index 0905fb40..cb15f802 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ParagraphStylesExport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ParagraphStylesExport.groovy @@ -27,7 +27,7 @@ static void run(Migration migration, Path exportFilePath) { def styles = migration.paragraphStyleRepository.listAll() file.withWriter { writer -> - def headers = ["id","name","targetId","leftIndent","rightIndent","defaultTabSize","spaceBefore","spaceAfter","alignment","firstLineIndent","keepWithNextParagraph","lineSpacingType","lineSpacingValue", Mapping.displayHeader("originLocations", true)] + def headers = ["id", "name", "targetId", "leftIndent", "rightIndent", "defaultTabSize", "spaceBefore", "spaceAfter", "alignment", "firstLineIndent", "keepWithNextParagraph", "lineSpacingType", "lineSpacingValue", "pdfTaggingRule", Mapping.displayHeader("originLocations", true)] writer.writeLine(headers.join(",")) styles.each { style -> @@ -64,7 +64,8 @@ static StringBuilder serializeDefinition(ParagraphStyleDefinition definition) { builder << "${Csv.serialize(definition?.firstLineIndent)}," builder << "${Csv.serialize(definition?.keepWithNextParagraph)}," // Linespacing serializes as two columns - builder << "${Csv.serialize(definition?.lineSpacing)}" + builder << "${Csv.serialize(definition?.lineSpacing)}," + builder << "${Csv.serialize(definition?.pdfTaggingRule)}" return builder } \ No newline at end of file diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ParagraphStylesImport.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ParagraphStylesImport.groovy index 57925342..7d08bd68 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ParagraphStylesImport.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/common/mapping/ParagraphStylesImport.groovy @@ -58,7 +58,7 @@ static void mapStyle(Migration migration, ParagraphStyle existingStyle, String s if (styleRefId.empty) { MappingItem.ParagraphStyle.Def mappingDefinition if (!(mapping.definition instanceof MappingItem.ParagraphStyle.Def)) { - mappingDefinition = new MappingItem.ParagraphStyle.Def(null, null, null, null, null, null, null, null, null, null) + mappingDefinition = new MappingItem.ParagraphStyle.Def(null, null, null, null, null, null, null, null, null, null, null) } else { mappingDefinition = mapping.definition as MappingItem.ParagraphStyle.Def } diff --git a/migration-examples/src/main/groovy/com/quadient/migration/example/example/Import.groovy b/migration-examples/src/main/groovy/com/quadient/migration/example/example/Import.groovy index 8e220cf8..1e31b6d1 100644 --- a/migration-examples/src/main/groovy/com/quadient/migration/example/example/Import.groovy +++ b/migration-examples/src/main/groovy/com/quadient/migration/example/example/Import.groovy @@ -23,6 +23,8 @@ import com.quadient.migration.shared.GroupOp import com.quadient.migration.shared.ImageOptions import com.quadient.migration.shared.ImageType import com.quadient.migration.shared.PageOptions +import com.quadient.migration.shared.ParagraphPdfTaggingRule +import com.quadient.migration.shared.TablePdfTaggingRule import com.quadient.migration.shared.Size import java.time.Instant @@ -119,6 +121,13 @@ def headingStyle = new TextStyleBuilder("headingStyle") } .build() +def headingParaStyle = new ParagraphStyleBuilder("headingParagraphStyle") + .definition { + it.spaceAfter(Size.ofMillimeters(3)) + it.pdfTaggingRule(ParagraphPdfTaggingRule.Heading1) + } + .build() + def paragraphStyle = new ParagraphStyleBuilder("paragraphStyle") .definition { it.firstLineIndent(Size.ofMillimeters(10)) @@ -137,12 +146,15 @@ def logo = new ImageBuilder("logo") .sourcePath(logoImageName) .imageType(ImageType.Png) .subject("Example logo") + .alternateText("Example logo image") .build() // Table containing some data with the first address row being optionally hidden // by using displayRuleRef to the display displayHeaderRule defined above. // The table also contains some merged cells and custom column widths. def table = table { + it.pdfTaggingRule(TablePdfTaggingRule.Table) + it.pdfAlternateText("Example key value table") it.addColumnWidth(Size.ofMillimeters(10), 10) it.addColumnWidth(Size.ofMillimeters(20), 20) it.addColumnWidth(Size.ofMillimeters(98), 70) @@ -229,6 +241,7 @@ def paragraph1 = new DocumentObjectBuilder("paragraph1", DocumentObjectType.Bloc it.styleRef(headingStyle.id) it.string("Lorem ipsum dolor sit amet\n") } + it.styleRef(headingParaStyle.id) } .paragraph { it.styleRef(paragraphStyle.id) @@ -365,6 +378,13 @@ def page = new DocumentObjectBuilder("page1", DocumentObjectType.Page) .documentObjectRef(conditionalParagraph.id) .documentObjectRef(firstMatchBlock.id) .documentObjectRef(selectByLanguageBlock.id) + .paragraph { + it.styleRef(paragraphStyle.id).text { + it.styleRef(normalStyle.id) + .string("For more information visit ") + .hyperlink("https://github.com/quadient/migration-stack", "Migration Stack GitHub", "Migration Stack GitHub URL link") + } + } } .area { it.position { @@ -397,7 +417,7 @@ for (item in [displayHeaderVariable, displayParagraphVariable, displayLastSenten for (item in [displayAddressRule, displayHeaderRule, displayParagraphRule, displayLastSentenceRule, displayRuleStateCzechia, displayRuleStateFrance]) { migration.displayRuleRepository.upsert(item) } -for (item in [paragraphStyle]) { +for (item in [paragraphStyle, headingParaStyle]) { migration.paragraphStyleRepository.upsert(item) } diff --git a/migration-examples/src/test/groovy/ImagesMappingExportTest.groovy b/migration-examples/src/test/groovy/ImagesMappingExportTest.groovy index 31244e96..481a55ab 100644 --- a/migration-examples/src/test/groovy/ImagesMappingExportTest.groovy +++ b/migration-examples/src/test/groovy/ImagesMappingExportTest.groovy @@ -25,10 +25,10 @@ class ImagesMappingExportTest { def migration = Utils.mockMigration() when(migration.imageRepository.listAll()).thenReturn([ - new Image("empty", null, [], new CustomFieldMap([:]), null, null, null, null, [:], emptySkipOptions()), - new Image("full", "full", ["foo", "bar"], new CustomFieldMap([:]), "sourcePath", null, ImageType.Jpeg, "targetDir", [:], new SkipOptions(true, "placeholder", "reason")), - new Image("overridden empty", null, [], new CustomFieldMap([:]), null, null, null, null, [:], emptySkipOptions()), - new Image("overridden full", "full", ["foo", "bar"], new CustomFieldMap(["originalName": "originalFull"]), "sourcePath", null, ImageType.Gif, "targetDir", [:], emptySkipOptions()), + new Image("empty", null, [], new CustomFieldMap([:]), null, null, null, null, [:], emptySkipOptions(), null), + new Image("full", "full", ["foo", "bar"], new CustomFieldMap([:]), "sourcePath", null, ImageType.Jpeg, "targetDir", [:], new SkipOptions(true, "placeholder", "reason"), "Alt text for full"), + new Image("overridden empty", null, [], new CustomFieldMap([:]), null, null, null, null, [:], emptySkipOptions(), null), + new Image("overridden full", "full", ["foo", "bar"], new CustomFieldMap(["originalName": "originalFull"]), "sourcePath", null, ImageType.Gif, "targetDir", [:], emptySkipOptions(), "Alt text for overridden"), ]) when(migration.statusTrackingRepository.findLastEventRelevantToOutput(any(), any(), any())).thenReturn(new Active()) @@ -36,11 +36,11 @@ class ImagesMappingExportTest { ImagesExport.run(migration, mappingFile) def expected = """\ - id,name,sourcePath,imageType,targetFolder,status,skip,skipPlaceholder,skipReason,originalName (read-only),originLocations (read-only) - empty,,,,,Active,false,,,,[] - full,full,sourcePath,Jpeg,targetDir,Active,true,placeholder,reason,,[foo; bar] - overridden empty,,,,,Active,false,,,,[] - overridden full,full,sourcePath,Gif,targetDir,Active,false,,,originalFull,[foo; bar] + id,name,sourcePath,imageType,targetFolder,alternateText,status,skip,skipPlaceholder,skipReason,originalName (read-only),originLocations (read-only) + empty,,,,,,Active,false,,,,[] + full,full,sourcePath,Jpeg,targetDir,Alt text for full,Active,true,placeholder,reason,,[foo; bar] + overridden empty,,,,,,Active,false,,,,[] + overridden full,full,sourcePath,Gif,targetDir,Alt text for overridden,Active,false,,,originalFull,[foo; bar] """.stripIndent() Assertions.assertEquals(expected, mappingFile.toFile().text.replaceAll("\\r\\n|\\r", "\n")) } diff --git a/migration-examples/src/test/groovy/ImagesMappingImportTest.groovy b/migration-examples/src/test/groovy/ImagesMappingImportTest.groovy index 187262d8..8e96f583 100644 --- a/migration-examples/src/test/groovy/ImagesMappingImportTest.groovy +++ b/migration-examples/src/test/groovy/ImagesMappingImportTest.groovy @@ -35,11 +35,11 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image("keptName", null, null, null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image("keptName", null, null, null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image("someName", null, null, null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image("someName", null, null, null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } @@ -63,11 +63,11 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, "keptFolder", null, null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, "keptFolder", null, null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, "overrideFolder", null, null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, "overrideFolder", null, null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } @@ -91,11 +91,11 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, "keptPath", null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, "keptPath", null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, "overridePath", null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, "overridePath", null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } @@ -119,11 +119,11 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, ImageType.Jpeg, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, ImageType.Jpeg, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, ImageType.Gif, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, ImageType.Gif, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } @@ -147,16 +147,44 @@ class ImagesMappingImportTest { ImagesImport.run(migration, mappingFile) - verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") - verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, ImageType.Jpeg, new SkipOptions(false, null, null))) + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, ImageType.Jpeg, new SkipOptions(false, null, null), null)) verify(migration.mappingRepository, times(1)).applyImageMapping("kept") - verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, ImageType.Gif, new SkipOptions(true, "placeholder", "reason"))) + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, ImageType.Gif, new SkipOptions(true, "placeholder", "reason"), null)) + verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") + } + + @Test + void overridesAlternateText() { + def migration = Utils.mockMigration() + Path mappingFile = Paths.get(dir.path, "testProject.csv") + def input = """\ + id,name,sourcePath,imageType,targetFolder,alternateText,status,originLocations + unchanged,,,,,,Active,[] + kept,,,,,keptAltText,Active,[] + overridden,,,,,overriddenAltText,Active,[] + """.stripIndent() + mappingFile.toFile().write(input) + givenExistingImage(migration, "unchanged", null, null, null, null) + givenExistingImageMapping(migration, "unchanged", null, null, null, null) + givenExistingImage(migration, "kept", null, null, null, null) + givenExistingImageMapping(migration, "kept", null, null, null, null) + givenExistingImage(migration, "overridden", null, null, null, null) + givenExistingImageMapping(migration, "overridden", null, null, null, null) + + ImagesImport.run(migration, mappingFile) + + verify(migration.mappingRepository, times(1)).upsert("unchanged", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), null)) + verify(migration.mappingRepository, times(1)).applyImageMapping("unchanged") + verify(migration.mappingRepository, times(1)).upsert("kept", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), "keptAltText")) + verify(migration.mappingRepository, times(1)).applyImageMapping("kept") + verify(migration.mappingRepository, times(1)).upsert("overridden", new MappingItem.Image(null, null, null, null, new SkipOptions(false, null, null), "overriddenAltText")) verify(migration.mappingRepository, times(1)).applyImageMapping("overridden") } static void givenExistingImage(Migration mig, String id, String name, String targetFolder, String sourcePath, ImageType imageType) { - when(mig.imageRepository.find(id)).thenReturn(new Image(id, name, [], new CustomFieldMap([:]), sourcePath, null, imageType, targetFolder, [:], new SkipOptions(false, null, null))) + when(mig.imageRepository.find(id)).thenReturn(new Image(id, name, [], new CustomFieldMap([:]), sourcePath, null, imageType, targetFolder, [:], new SkipOptions(false, null, null), null)) } static void givenExistingImageMapping(Migration mig, @@ -166,6 +194,6 @@ class ImagesMappingImportTest { String sourcePath, ImageType imageType) { when(mig.mappingRepository.getImageMapping(id)) - .thenReturn(new MappingItem.Image(name, targetFolder, sourcePath, imageType, null)) + .thenReturn(new MappingItem.Image(name, targetFolder, sourcePath, imageType, null, null)) } } diff --git a/migration-examples/src/test/groovy/ParagraphStylesMappingExportTest.groovy b/migration-examples/src/test/groovy/ParagraphStylesMappingExportTest.groovy index 64e063a9..6f78ef08 100644 --- a/migration-examples/src/test/groovy/ParagraphStylesMappingExportTest.groovy +++ b/migration-examples/src/test/groovy/ParagraphStylesMappingExportTest.groovy @@ -22,10 +22,10 @@ class ParagraphStylesMappingExportTest { void exportWorksCorrectlyForAllVariants() { Path mappingFile = Paths.get(dir.path, "testProject.csv") def migration = Utils.mockMigration() - def emptyDefinition = new ParagraphStyleDefinition(null, null, null, null, null, Alignment.Left, null, new LineSpacing.Additional(Size.ofInches(0)), null, null) - def fullDefinition = new ParagraphStyleDefinition(Size.ofInches(1), Size.ofInches(1), Size.ofInches(1), Size.ofInches(1), Size.ofInches(1), Alignment.Center, Size.ofInches(1), new LineSpacing.Additional(Size.ofInches(1)), true, new Tabs([new Tab(Size.ofInches(1), TabType.Right)], true)) + def emptyDefinition = new ParagraphStyleDefinition(null, null, null, null, null, Alignment.Left, null, new LineSpacing.Additional(Size.ofInches(0)), null, null, null) + def fullDefinition = new ParagraphStyleDefinition(Size.ofInches(1), Size.ofInches(1), Size.ofInches(1), Size.ofInches(1), Size.ofInches(1), Alignment.Center, Size.ofInches(1), new LineSpacing.Additional(Size.ofInches(1)), true, new Tabs([new Tab(Size.ofInches(1), TabType.Right)], true), null) - def overrideMappingDef = new MappingItem.ParagraphStyle("newName", new MappingItem.ParagraphStyle.Def(Size.ofInches(2), Size.ofInches(2), Size.ofInches(2), Size.ofInches(2), Size.ofInches(2), Alignment.Right, Size.ofInches(2), new LineSpacing.ExactFromPrevious(Size.ofInches(2)), true, new Tabs([new Tab(Size.ofInches(2), TabType.Right)], true))) + def overrideMappingDef = new MappingItem.ParagraphStyle("newName", new MappingItem.ParagraphStyle.Def(Size.ofInches(2), Size.ofInches(2), Size.ofInches(2), Size.ofInches(2), Size.ofInches(2), Alignment.Right, Size.ofInches(2), new LineSpacing.ExactFromPrevious(Size.ofInches(2)), true, new Tabs([new Tab(Size.ofInches(2), TabType.Right)], true), null)) def overrideMappingRef = new MappingItem.ParagraphStyle("newName", new MappingItem.ParagraphStyle.Ref("new other")) when(migration.paragraphStyleRepository.listAll()).thenReturn([ @@ -58,19 +58,19 @@ class ParagraphStylesMappingExportTest { "id,name,targetId,leftIndent,rightIndent,defaultTabSize,spaceBefore,spaceAfter,alignment,firstLineIndent,keepWithNextParagraph,lineSpacingType,lineSpacingValue,originLocations (read-only)" def expected = """\ - id,name,targetId,leftIndent,rightIndent,defaultTabSize,spaceBefore,spaceAfter,alignment,firstLineIndent,keepWithNextParagraph,lineSpacingType,lineSpacingValue,originLocations (read-only) - empty,,,,,,,,Left,,,Additional,0.0mm,[] - empty with targetId,,other,,,,,,,,,,[] - full,full,,25.4mm,25.4mm,25.4mm,25.4mm,25.4mm,Center,25.4mm,true,Additional,25.4mm,[foo; bar] - full with targetId,full,other,,,,,,,,,,[foo; bar] - empty overridden by def,,,,,,,,Left,,,Additional,0.0mm,[] - empty overridden by ref,,,,,,,,Left,,,Additional,0.0mm,[] - empty with targetId overridden by def,,other,,,,,,,,,,[] - empty with targetId overridden by ref,,other,,,,,,,,,,[] - full overridden by def,,,25.4mm,25.4mm,25.4mm,25.4mm,25.4mm,Center,25.4mm,true,Additional,25.4mm,[] - full overridden by ref,,other,,,,,,,,,,[] - full with targetId overridden by def,,other,,,,,,,,,,[] - full with targetId overridden by ref,,other,,,,,,,,,,[] + id,name,targetId,leftIndent,rightIndent,defaultTabSize,spaceBefore,spaceAfter,alignment,firstLineIndent,keepWithNextParagraph,lineSpacingType,lineSpacingValue,pdfTaggingRule,originLocations (read-only) + empty,,,,,,,,Left,,,Additional,0.0mm,,[] + empty with targetId,,other,,,,,,,,,,,[] + full,full,,25.4mm,25.4mm,25.4mm,25.4mm,25.4mm,Center,25.4mm,true,Additional,25.4mm,,[foo; bar] + full with targetId,full,other,,,,,,,,,,,[foo; bar] + empty overridden by def,,,,,,,,Left,,,Additional,0.0mm,,[] + empty overridden by ref,,,,,,,,Left,,,Additional,0.0mm,,[] + empty with targetId overridden by def,,other,,,,,,,,,,,[] + empty with targetId overridden by ref,,other,,,,,,,,,,,[] + full overridden by def,,,25.4mm,25.4mm,25.4mm,25.4mm,25.4mm,Center,25.4mm,true,Additional,25.4mm,,[] + full overridden by ref,,other,,,,,,,,,,,[] + full with targetId overridden by def,,other,,,,,,,,,,,[] + full with targetId overridden by ref,,other,,,,,,,,,,,[] """.stripIndent() Assertions.assertEquals(expected, mappingFile.toFile().text.replaceAll("\\r\\n|\\r", "\n")) } diff --git a/migration-examples/src/test/groovy/ParagraphStylesMappingImportTest.groovy b/migration-examples/src/test/groovy/ParagraphStylesMappingImportTest.groovy index 78d9c4d8..106f7500 100644 --- a/migration-examples/src/test/groovy/ParagraphStylesMappingImportTest.groovy +++ b/migration-examples/src/test/groovy/ParagraphStylesMappingImportTest.groovy @@ -45,6 +45,7 @@ class ParagraphStylesMappingImportTest { Size.ofMeters(1), new LineSpacing.Exact(null), true, + null, null))) } @@ -89,6 +90,7 @@ class ParagraphStylesMappingImportTest { Size.ofMeters(1), new LineSpacing.AtLeast(Size.ofMeters(1)), true, + null, null))) verify(migration.mappingRepository).applyParagraphStyleMapping("existing") } @@ -156,6 +158,7 @@ class ParagraphStylesMappingImportTest { Size.ofMeters(2), new LineSpacing.AtLeast(Size.ofMeters(2)), false, + null, null))) verify(migration.mappingRepository).applyParagraphStyleMapping("existing") } @@ -186,6 +189,7 @@ class ParagraphStylesMappingImportTest { firstLineIndentM != null ? Size.ofMeters(firstLineIndentM) : null, lineSpacing, keepWithNextParagraph, + null, null))) } @@ -219,6 +223,7 @@ class ParagraphStylesMappingImportTest { firstLineIndentM != null ? Size.ofMeters(firstLineIndentM) : null, lineSpacing, keepWithNextParagraph, + null, null))) } } diff --git a/migration-examples/src/test/groovy/Utils.groovy b/migration-examples/src/test/groovy/Utils.groovy index 03fb7b7c..fbb20097 100644 --- a/migration-examples/src/test/groovy/Utils.groovy +++ b/migration-examples/src/test/groovy/Utils.groovy @@ -16,8 +16,8 @@ static Migration mockMigration() { def migration = mock(Migration.class) def projectConfig = mock(ProjectConfig.class) - when(migration.projectConfig).thenReturn(projectConfig) - when(migration.projectConfig.name).thenReturn("testProject") + when(migration.getProjectConfig()).thenReturn(projectConfig) + when(projectConfig.getName()).thenReturn("testProject") def varRepo = mock(VariableRepository.class) def structureRepo = mock(VariableStructureRepository.class) @@ -28,14 +28,14 @@ static Migration mockMigration() { def textStyleRepo = mock(TextStyleRepository.class) def paraStyleRepo = mock(ParagraphStyleRepository.class) - when(migration.paragraphStyleRepository).thenReturn(paraStyleRepo) - when(migration.textStyleRepository).thenReturn(textStyleRepo) - when(migration.statusTrackingRepository).thenReturn(statusTrackingRepo) - when(migration.imageRepository).thenReturn(imageRepo) - when(migration.documentObjectRepository).thenReturn(docObjectRepo) - when(migration.variableRepository).thenReturn(varRepo) - when(migration.variableStructureRepository).thenReturn(structureRepo) - when(migration.mappingRepository).thenReturn(mappingRepo) + when(migration.getParagraphStyleRepository()).thenReturn(paraStyleRepo) + when(migration.getTextStyleRepository()).thenReturn(textStyleRepo) + when(migration.getStatusTrackingRepository()).thenReturn(statusTrackingRepo) + when(migration.getImageRepository()).thenReturn(imageRepo) + when(migration.getDocumentObjectRepository()).thenReturn(docObjectRepo) + when(migration.getVariableRepository()).thenReturn(varRepo) + when(migration.getVariableStructureRepository()).thenReturn(structureRepo) + when(migration.getMappingRepository()).thenReturn(mappingRepo) return migration } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Hyperlink.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Hyperlink.kt new file mode 100644 index 00000000..7fa33f34 --- /dev/null +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Hyperlink.kt @@ -0,0 +1,30 @@ +package com.quadient.migration.api.dto.migrationmodel + +import com.quadient.migration.data.HyperlinkModel +import com.quadient.migration.persistence.migrationmodel.HyperlinkEntity + +data class Hyperlink( + val url: String, + val displayText: String? = null, + val alternateText: String? = null +) : TextContent { + companion object { + fun fromModel(model: HyperlinkModel) = Hyperlink( + url = model.url, + displayText = model.displayText, + alternateText = model.alternateText + ) + } + + fun toModel() = HyperlinkModel( + url = url, + displayText = displayText, + alternateText = alternateText + ) + + fun toDb() = HyperlinkEntity( + url = url, + displayText = displayText, + alternateText = alternateText + ) +} diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Image.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Image.kt index f3fedde9..da9191f4 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Image.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Image.kt @@ -17,6 +17,7 @@ data class Image( var targetFolder: String?, val metadata: Map>, val skip: SkipOptions, + var alternateText: String? = null, ) : MigrationObject { companion object { fun fromModel(model: ImageModel): Image { @@ -31,6 +32,7 @@ data class Image( targetFolder = model.targetFolder?.toString(), metadata = model.metadata, skip = model.skip, + alternateText = model.alternateText, ) } } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Mapping.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Mapping.kt index 04ca894b..b7ac0e90 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Mapping.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Mapping.kt @@ -30,6 +30,7 @@ sealed class MappingItem { var sourcePath: String?, var imageType: ImageType?, var skip: SkipOptions? = null, + var alternateText: String? = null, ) : MappingItem() data class ParagraphStyle(override var name: String?, var definition: Definition?) : MappingItem() { @@ -46,6 +47,7 @@ sealed class MappingItem { var lineSpacing: LineSpacing?, var keepWithNextParagraph: Boolean?, var tabs: Tabs?, + var pdfTaggingRule: ParagraphPdfTaggingRule?, ) : Definition } @@ -99,6 +101,7 @@ sealed class MappingItem { sourcePath = this.sourcePath, imageType = this.imageType, skip = this.skip, + alternateText = this.alternateText, ) } @@ -120,7 +123,8 @@ sealed class MappingItem { firstLineIndent = def.firstLineIndent, lineSpacing = def.lineSpacing, keepWithNextParagraph = def.keepWithNextParagraph, - tabs = def.tabs?.toDb() + tabs = def.tabs?.toDb(), + pdfTaggingRule = def.pdfTaggingRule ) null -> null diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/ParagraphStyle.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/ParagraphStyle.kt index 01649211..6c75c33c 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/ParagraphStyle.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/ParagraphStyle.kt @@ -9,6 +9,7 @@ import com.quadient.migration.persistence.migrationmodel.TabEntity import com.quadient.migration.persistence.migrationmodel.TabsEntity import com.quadient.migration.shared.Alignment import com.quadient.migration.shared.LineSpacing +import com.quadient.migration.shared.ParagraphPdfTaggingRule import com.quadient.migration.shared.Size import com.quadient.migration.shared.TabType @@ -41,6 +42,7 @@ data class ParagraphStyleDefinition( var lineSpacing: LineSpacing = LineSpacing.Additional(null), var keepWithNextParagraph: Boolean?, var tabs: Tabs?, + var pdfTaggingRule: ParagraphPdfTaggingRule?, ) : ParagraphStyleDefOrRef { companion object { fun fromModel(model: ParagraphStyleDefinitionModel) = ParagraphStyleDefinition( @@ -54,6 +56,7 @@ data class ParagraphStyleDefinition( lineSpacing = model.lineSpacing, keepWithNextParagraph = model.keepWithNextParagraph, tabs = model.tabs?.let(Tabs::fromModel), + pdfTaggingRule = model.pdfTaggingRule, ) } @@ -68,6 +71,7 @@ data class ParagraphStyleDefinition( lineSpacing = lineSpacing, keepWithNextParagraph = keepWithNextParagraph, tabs = tabs?.toDb(), + pdfTaggingRule = pdfTaggingRule, ) override fun toModel() = ParagraphStyleDefinitionModel( @@ -81,6 +85,7 @@ data class ParagraphStyleDefinition( lineSpacing = lineSpacing, keepWithNextParagraph = keepWithNextParagraph, tabs = tabs?.toModel(), + pdfTaggingRule = pdfTaggingRule, ) } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Ref.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Ref.kt index c8e763aa..ead9b48b 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Ref.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/Ref.kt @@ -3,6 +3,7 @@ package com.quadient.migration.api.dto.migrationmodel import com.quadient.migration.data.DisplayRuleModelRef import com.quadient.migration.data.DocumentObjectModelRef import com.quadient.migration.data.FirstMatchModel +import com.quadient.migration.data.HyperlinkModel import com.quadient.migration.data.ImageModelRef import com.quadient.migration.data.ParagraphStyleDefOrRefModel import com.quadient.migration.data.ParagraphStyleDefinitionModel @@ -18,6 +19,7 @@ import com.quadient.migration.data.VariableModelRef import com.quadient.migration.data.VariableStructureModelRef import com.quadient.migration.persistence.migrationmodel.DisplayRuleEntityRef import com.quadient.migration.persistence.migrationmodel.DocumentObjectEntityRef +import com.quadient.migration.persistence.migrationmodel.HyperlinkEntity import com.quadient.migration.persistence.migrationmodel.ImageEntityRef import com.quadient.migration.persistence.migrationmodel.ParagraphStyleEntityRef import com.quadient.migration.persistence.migrationmodel.StringEntity @@ -50,6 +52,7 @@ sealed interface TextContent { is TableModel -> Table.fromModel(model) is VariableModelRef -> VariableRef.fromModel(model) is FirstMatchModel -> FirstMatch.fromModel(model) + is HyperlinkModel -> Hyperlink.fromModel(model) } } } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/DocumentContentBuilderBase.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/DocumentContentBuilderBase.kt index bf17efe9..fce261c7 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/DocumentContentBuilderBase.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/DocumentContentBuilderBase.kt @@ -74,6 +74,7 @@ interface DocumentContentBuilderBase { this.content.add(ImageRef(imageId)) } as T + /** * Adds a document object reference to the content. * @param documentObjectId The ID of the document object to reference. diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/ImageBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/ImageBuilder.kt index af07753c..45b00c17 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/ImageBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/ImageBuilder.kt @@ -15,6 +15,7 @@ class ImageBuilder(id: String) : DtoBuilderBase(id) { var skip = false var placeholder: String? = null var reason: String? = null + var alternateText: String? = null /** * Sets source path of the image. This path is relative to the storage root folder. @@ -76,6 +77,13 @@ class ImageBuilder(id: String) : DtoBuilderBase(id) { metadata("Subject") { string(subject) } } + /** + * Sets the alternate text for the image for accessibility purposes. + * @param alternateText the alternate text for the image + * @return the builder instance for chaining + */ + fun alternateText(alternateText: String) = apply { this.alternateText = alternateText } + /** * Builds the Image instance with the provided properties. * @return the built Image instance @@ -92,6 +100,7 @@ class ImageBuilder(id: String) : DtoBuilderBase(id) { targetFolder = targetFolder, metadata = metadata, skip = SkipOptions(skipped = skip, reason = reason, placeholder = placeholder), + alternateText = alternateText, ) } } \ No newline at end of file diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/ParagraphStyleBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/ParagraphStyleBuilder.kt index cdd028d8..2f2f5922 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/ParagraphStyleBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/ParagraphStyleBuilder.kt @@ -7,6 +7,7 @@ import com.quadient.migration.api.dto.migrationmodel.ParagraphStyleRef import com.quadient.migration.api.dto.migrationmodel.Tabs import com.quadient.migration.shared.Alignment import com.quadient.migration.shared.LineSpacing +import com.quadient.migration.shared.ParagraphPdfTaggingRule import com.quadient.migration.shared.Size class ParagraphStyleBuilder(id: String) : DtoBuilderBase(id) { @@ -43,6 +44,7 @@ class ParagraphStyleDefinitionBuilder { var lineSpacing: LineSpacing = LineSpacing.Additional(null) var keepWithNextParagraph: Boolean? = null var tabs: Tabs? = null + var pdfTaggingRule: ParagraphPdfTaggingRule? = null fun spaceBefore(spaceBefore: Size?) = apply { this.spaceBefore = spaceBefore } fun spaceAfter(spaceAfter: Size?) = apply { this.spaceAfter = spaceAfter } @@ -69,6 +71,8 @@ class ParagraphStyleDefinitionBuilder { fun tabs(tabs: Tabs?) = apply { this.tabs = tabs } + fun pdfTaggingRule(pdfTaggingRule: ParagraphPdfTaggingRule?) = apply { this.pdfTaggingRule = pdfTaggingRule } + fun build() = ParagraphStyleDefinition( leftIndent = leftIndent, rightIndent = rightIndent, @@ -80,5 +84,6 @@ class ParagraphStyleDefinitionBuilder { lineSpacing = lineSpacing, keepWithNextParagraph = keepWithNextParagraph, tabs = tabs, + pdfTaggingRule = pdfTaggingRule, ) } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/ParagraphBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/ParagraphBuilder.kt index d5b6138a..eb4eb93a 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/ParagraphBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/ParagraphBuilder.kt @@ -2,6 +2,7 @@ package com.quadient.migration.api.dto.migrationmodel.builder import com.quadient.migration.api.dto.migrationmodel.DisplayRuleRef import com.quadient.migration.api.dto.migrationmodel.DocumentObjectRef +import com.quadient.migration.api.dto.migrationmodel.Hyperlink import com.quadient.migration.api.dto.migrationmodel.ImageRef import com.quadient.migration.api.dto.migrationmodel.Paragraph import com.quadient.migration.api.dto.migrationmodel.ParagraphStyleRef @@ -273,6 +274,26 @@ class ParagraphBuilder { content.add(ref) } + /** + * Adds an inline hyperlink to the text content. + * @param url The URL to link to (mandatory). + * @param displayText The text to display (optional - if null, url will be displayed). + * @param alternateText The accessibility text for screen readers (optional). + * @return The current instance of [TextBuilder] for method chaining. + */ + fun hyperlink(url: String, displayText: String? = null, alternateText: String? = null) = apply { + content.add(Hyperlink(url, displayText, alternateText)) + } + + /** + * Adds a hyperlink to the text content. + * @param hyperlink The hyperlink to add. + * @return The current instance of [TextBuilder] for method chaining. + */ + fun hyperlink(hyperlink: Hyperlink) = apply { + content.add(hyperlink) + } + /** * Adds a table to the text content. * @param builder A builder function to configure the [TableBuilder]. diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/TableBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/TableBuilder.kt index d7ff22c8..00bef054 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/TableBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/TableBuilder.kt @@ -2,14 +2,18 @@ package com.quadient.migration.api.dto.migrationmodel.builder import com.quadient.migration.api.dto.migrationmodel.DisplayRuleRef import com.quadient.migration.api.dto.migrationmodel.DocumentContent -import com.quadient.migration.api.dto.migrationmodel.DocumentObjectRef import com.quadient.migration.api.dto.migrationmodel.Table -import com.quadient.migration.api.dto.migrationmodel.builder.documentcontent.SelectByLanguageBuilder import com.quadient.migration.shared.Size +import com.quadient.migration.shared.TablePdfTaggingRule class TableBuilder { private val rows = mutableListOf() private val columnWidths = mutableListOf() + private var pdfTaggingRule: TablePdfTaggingRule = TablePdfTaggingRule.Default + private var pdfAlternateText: String? = null + + fun pdfTaggingRule(rule: TablePdfTaggingRule) = apply { this.pdfTaggingRule = rule } + fun pdfAlternateText(text: String?) = apply { this.pdfAlternateText = text } /** * Add a row to the table. Rows are added in the order they are defined. @@ -44,7 +48,7 @@ class TableBuilder { minWidth = colWidth.minWidth, percentWidth = colWidth.percentWidth, ) - }) + }, pdfTaggingRule, pdfAlternateText) } class Row { diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/TableDsl.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/TableDsl.kt index 44a9a7c0..f9002542 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/TableDsl.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/builder/documentcontent/TableDsl.kt @@ -3,6 +3,7 @@ package com.quadient.migration.api.dto.migrationmodel.builder import com.quadient.migration.api.dto.migrationmodel.DisplayRuleRef import com.quadient.migration.api.dto.migrationmodel.DocumentContent import com.quadient.migration.api.dto.migrationmodel.Table +import com.quadient.migration.shared.TablePdfTaggingRule import com.quadient.migration.shared.Size object Dsl { @@ -21,7 +22,7 @@ object Dsl { minWidth = it.minWidth, percentWidth = it.percentWidth, ) - }) + }, pdfTaggingRule, pdfAlternateText) } } @@ -29,6 +30,11 @@ object Dsl { class TableDsl { val rows = mutableListOf() var columnWidths = mutableListOf() + var pdfTaggingRule: TablePdfTaggingRule = TablePdfTaggingRule.Default + var pdfAlternateText: String? = null + + fun pdfTaggingRule(rule: TablePdfTaggingRule) = apply { this.pdfTaggingRule = rule } + fun pdfAlternateText(text: String?) = apply { this.pdfAlternateText = text } /** * Add a row to the table. Rows are added in the order they are defined. diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/documentcontent/Paragraph.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/documentcontent/Paragraph.kt index e5dbd716..7a1b9c51 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/documentcontent/Paragraph.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/documentcontent/Paragraph.kt @@ -2,6 +2,7 @@ package com.quadient.migration.api.dto.migrationmodel import com.quadient.migration.data.DocumentObjectModelRef import com.quadient.migration.data.FirstMatchModel +import com.quadient.migration.data.HyperlinkModel import com.quadient.migration.data.ImageModelRef import com.quadient.migration.data.ParagraphModel import com.quadient.migration.data.StringModel @@ -31,6 +32,7 @@ data class Paragraph( is TableModel -> Table.fromModel(textContent) is ImageModelRef -> ImageRef.fromModel(textContent) is FirstMatchModel -> FirstMatch.fromModel(textContent) + is HyperlinkModel -> Hyperlink.fromModel(textContent) } }) }, @@ -75,6 +77,7 @@ data class Paragraph( is StringValue -> textContent.toDb() is VariableRef -> textContent.toDb() is FirstMatch -> textContent.toDb() + is Hyperlink -> textContent.toDb() } }.toMutableList(), it.styleRef?.toDb(), it.displayRuleRef?.toDb()) }.toMutableList(), diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/documentcontent/Table.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/documentcontent/Table.kt index ef3a7fb8..d2e5a2f4 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/documentcontent/Table.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/dto/migrationmodel/documentcontent/Table.kt @@ -4,33 +4,46 @@ import com.quadient.migration.data.TableModel import com.quadient.migration.persistence.migrationmodel.TableEntity import com.quadient.migration.persistence.migrationmodel.TableEntity.ColumnWidthEntity import com.quadient.migration.shared.Size +import com.quadient.migration.shared.TablePdfTaggingRule data class Table( val rows: List, val columnWidths: List, + val pdfTaggingRule: TablePdfTaggingRule = TablePdfTaggingRule.Default, + val pdfAlternateText: String? = null, ) : DocumentContent, TextContent { companion object { - fun fromModel(model: TableModel): Table = Table(rows = model.rows.map { row -> - Row(row.cells.map { cell -> - Cell( - cell.content.map { DocumentContent.fromModelContent(it) }, - cell.mergeLeft, - cell.mergeUp, - ) - }, displayRuleRef = row.displayRuleRef?.let { DisplayRuleRef.fromModel(it) }) - }, columnWidths = model.columnWidths.map { ColumnWidth(it.minWidth, it.percentWidth) }) + fun fromModel(model: TableModel): Table = Table( + rows = model.rows.map { row -> + Row(row.cells.map { cell -> + Cell( + cell.content.map { DocumentContent.fromModelContent(it) }, + cell.mergeLeft, + cell.mergeUp, + ) + }, displayRuleRef = row.displayRuleRef?.let { DisplayRuleRef.fromModel(it) }) + }, + columnWidths = model.columnWidths.map { ColumnWidth(it.minWidth, it.percentWidth) }, + pdfTaggingRule = model.pdfTaggingRule, + pdfAlternateText = model.pdfAlternateText + ) } fun toDb(): TableEntity { - return TableEntity(rows = rows.map { row -> - TableEntity.Row(row.cells.map { cell -> - TableEntity.Cell( - cell.content.toDb(), - cell.mergeLeft, - cell.mergeUp, - ) - }, displayRuleRef = row.displayRuleRef?.toDb()) - }, columnWidths = columnWidths.map { ColumnWidthEntity(it.minWidth, it.percentWidth) }) + return TableEntity( + rows = rows.map { row -> + TableEntity.Row(row.cells.map { cell -> + TableEntity.Cell( + cell.content.toDb(), + cell.mergeLeft, + cell.mergeUp, + ) + }, displayRuleRef = row.displayRuleRef?.toDb()) + }, + columnWidths = columnWidths.map { ColumnWidthEntity(it.minWidth, it.percentWidth) }, + pdfTaggingRule = pdfTaggingRule, + pdfAlternateText = pdfAlternateText + ) } data class Row(val cells: List, val displayRuleRef: DisplayRuleRef? = null) diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/repository/ImageRepository.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/repository/ImageRepository.kt index 56ceac8d..8b1db9d4 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/repository/ImageRepository.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/repository/ImageRepository.kt @@ -7,6 +7,7 @@ import com.quadient.migration.api.dto.migrationmodel.MigrationObject import com.quadient.migration.data.ImageModel import com.quadient.migration.persistence.repository.ImageInternalRepository import com.quadient.migration.persistence.table.DocumentObjectTable +import com.quadient.migration.persistence.table.ImageTable.alternateText import com.quadient.migration.persistence.table.ImageTable.imageType import com.quadient.migration.persistence.table.ImageTable.metadata import com.quadient.migration.persistence.table.ImageTable.options @@ -75,6 +76,7 @@ class ImageRepository(internalRepository: ImageInternalRepository) : Repository< it[targetFolder] = dto.targetFolder it[metadata] = dto.metadata it[skip] = dto.skip + it[alternateText] = dto.alternateText }.first() }) } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/api/repository/MappingRepository.kt b/migration-library/src/main/kotlin/com/quadient/migration/api/repository/MappingRepository.kt index a5737037..a7ab3ded 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/api/repository/MappingRepository.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/api/repository/MappingRepository.kt @@ -88,6 +88,7 @@ class MappingRepository( sourcePath = null, imageType = null, skip = null, + alternateText = null, )).toDto() as MappingItem.Image } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/data/HyperlinkModel.kt b/migration-library/src/main/kotlin/com/quadient/migration/data/HyperlinkModel.kt new file mode 100644 index 00000000..12df79bf --- /dev/null +++ b/migration-library/src/main/kotlin/com/quadient/migration/data/HyperlinkModel.kt @@ -0,0 +1,17 @@ +package com.quadient.migration.data + +import com.quadient.migration.persistence.migrationmodel.HyperlinkEntity + +data class HyperlinkModel( + val url: String, + val displayText: String? = null, + val alternateText: String? = null +) : TextContentModel { + companion object { + fun fromDb(entity: HyperlinkEntity) = HyperlinkModel( + url = entity.url, + displayText = entity.displayText, + alternateText = entity.alternateText + ) + } +} diff --git a/migration-library/src/main/kotlin/com/quadient/migration/data/ImageModel.kt b/migration-library/src/main/kotlin/com/quadient/migration/data/ImageModel.kt index 4ae660e1..3c85e8f1 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/data/ImageModel.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/data/ImageModel.kt @@ -20,6 +20,7 @@ data class ImageModel( val targetFolder: IcmPath?, val metadata: Map>, val skip: SkipOptions, + val alternateText: String? = null, ) : RefValidatable, MigrationObjectModel { override fun collectRefs(): List { return emptyList() diff --git a/migration-library/src/main/kotlin/com/quadient/migration/data/ParagraphStyleModel.kt b/migration-library/src/main/kotlin/com/quadient/migration/data/ParagraphStyleModel.kt index 6aab8cd0..583cb85a 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/data/ParagraphStyleModel.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/data/ParagraphStyleModel.kt @@ -38,6 +38,7 @@ data class ParagraphStyleDefinitionModel( val lineSpacing: LineSpacing, val keepWithNextParagraph: Boolean?, val tabs: TabsModel?, + val pdfTaggingRule: com.quadient.migration.shared.ParagraphPdfTaggingRule?, ) : ParagraphStyleDefOrRefModel { companion object { fun fromDb(entity: ParagraphStyleDefinitionEntity) = ParagraphStyleDefinitionModel( @@ -50,7 +51,8 @@ data class ParagraphStyleDefinitionModel( firstLineIndent = entity.firstLineIndent, lineSpacing = entity.lineSpacing, keepWithNextParagraph = entity.keepWithNextParagraph, - tabs = entity.tabs?.let { TabsModel.fromDb(it) } + tabs = entity.tabs?.let { TabsModel.fromDb(it) }, + pdfTaggingRule = entity.pdfTaggingRule ) } } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/data/RefModel.kt b/migration-library/src/main/kotlin/com/quadient/migration/data/RefModel.kt index cb220c72..dd285f51 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/data/RefModel.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/data/RefModel.kt @@ -3,6 +3,7 @@ package com.quadient.migration.data import com.quadient.migration.persistence.migrationmodel.DisplayRuleEntityRef import com.quadient.migration.persistence.migrationmodel.DocumentObjectEntityRef import com.quadient.migration.persistence.migrationmodel.FirstMatchEntity +import com.quadient.migration.persistence.migrationmodel.HyperlinkEntity import com.quadient.migration.persistence.migrationmodel.ImageEntityRef import com.quadient.migration.persistence.migrationmodel.ParagraphStyleDefOrRefEntity import com.quadient.migration.persistence.migrationmodel.ParagraphStyleDefinitionEntity @@ -29,6 +30,7 @@ sealed interface TextContentModel { is DocumentObjectEntityRef -> DocumentObjectModelRef.fromDb(entity) is ImageEntityRef -> ImageModelRef.fromDb(entity) is FirstMatchEntity -> FirstMatchModel.fromDb(entity) + is HyperlinkEntity -> HyperlinkModel.fromDb(entity) } } } @@ -107,5 +109,3 @@ data class StringModel(val value: String) : TextContentModel { fun fromDb(entity: StringEntity) = StringModel(entity.value) } } - - diff --git a/migration-library/src/main/kotlin/com/quadient/migration/data/documentcontent/ParagraphModel.kt b/migration-library/src/main/kotlin/com/quadient/migration/data/documentcontent/ParagraphModel.kt index 059873b7..2304d782 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/data/documentcontent/ParagraphModel.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/data/documentcontent/ParagraphModel.kt @@ -42,6 +42,7 @@ data class ParagraphModel( when (it) { is VariableModelRef -> listOf(it) is StringModel -> null + is HyperlinkModel -> null is TableModel -> it.collectRefs() is DocumentObjectModelRef -> listOf(it) is ImageModelRef -> listOf(it) diff --git a/migration-library/src/main/kotlin/com/quadient/migration/data/documentcontent/TableModel.kt b/migration-library/src/main/kotlin/com/quadient/migration/data/documentcontent/TableModel.kt index 57c3637f..b12ba615 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/data/documentcontent/TableModel.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/data/documentcontent/TableModel.kt @@ -1,24 +1,32 @@ package com.quadient.migration.data import com.quadient.migration.persistence.migrationmodel.TableEntity +import com.quadient.migration.shared.TablePdfTaggingRule import com.quadient.migration.service.RefValidatable import com.quadient.migration.shared.Size data class TableModel( val rows: List, val columnWidths: List, + val pdfTaggingRule: TablePdfTaggingRule = TablePdfTaggingRule.Default, + val pdfAlternateText: String? = null, ) : DocumentContentModel, TextContentModel, RefValidatable { companion object { - fun fromDb(table: TableEntity): TableModel = TableModel(rows = table.rows.map { row -> - RowModel(row.cells.map { cell -> - CellModel( - cell.content.map { DocumentContentModel.fromDbContent(it) }, - cell.mergeLeft, - cell.mergeUp, - ) - }, displayRuleRef = row.displayRuleRef?.let { DisplayRuleModelRef.fromDb(it) }) - }, columnWidths = table.columnWidths.map { ColumnWidthModel(it.minWidth, it.percentWidth) }) + fun fromDb(table: TableEntity): TableModel = TableModel( + rows = table.rows.map { row -> + RowModel(row.cells.map { cell -> + CellModel( + cell.content.map { DocumentContentModel.fromDbContent(it) }, + cell.mergeLeft, + cell.mergeUp, + ) + }, displayRuleRef = row.displayRuleRef?.let { DisplayRuleModelRef.fromDb(it) }) + }, + columnWidths = table.columnWidths.map { ColumnWidthModel(it.minWidth, it.percentWidth) }, + pdfTaggingRule = table.pdfTaggingRule, + pdfAlternateText = table.pdfAlternateText + ) } override fun collectRefs(): List { diff --git a/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/DocumentContentEntity.kt b/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/DocumentContentEntity.kt index f72f3673..05dcc2b3 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/DocumentContentEntity.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/DocumentContentEntity.kt @@ -3,6 +3,7 @@ package com.quadient.migration.persistence.migrationmodel import com.quadient.migration.shared.Position import com.quadient.migration.shared.Size import kotlinx.serialization.Serializable +import com.quadient.migration.shared.TablePdfTaggingRule @Serializable sealed interface DocumentContentEntity @@ -11,6 +12,8 @@ sealed interface DocumentContentEntity data class TableEntity( val rows: List, val columnWidths: List, + val pdfTaggingRule: TablePdfTaggingRule = TablePdfTaggingRule.Default, + val pdfAlternateText: String? = null, ) : DocumentContentEntity, TextContentEntity { @Serializable data class Row(val cells: List, val displayRuleRef: DisplayRuleEntityRef?) diff --git a/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/HyperlinkEntity.kt b/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/HyperlinkEntity.kt new file mode 100644 index 00000000..78a2faca --- /dev/null +++ b/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/HyperlinkEntity.kt @@ -0,0 +1,10 @@ +package com.quadient.migration.persistence.migrationmodel + +import kotlinx.serialization.Serializable + +@Serializable +data class HyperlinkEntity( + val url: String, + val displayText: String? = null, + val alternateText: String? = null +) : TextContentEntity diff --git a/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/MappingEntity.kt b/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/MappingEntity.kt index e550e2c2..483a3604 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/MappingEntity.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/MappingEntity.kt @@ -91,6 +91,7 @@ sealed class MappingItemEntity { val sourcePath: String?, val imageType: ImageType? = null, var skip: SkipOptions? = null, + val alternateText: String? = null, ) : MappingItemEntity() { fun apply(item: ImageDto): ImageDto { return item.copy( @@ -99,6 +100,7 @@ sealed class MappingItemEntity { sourcePath = sourcePath, imageType = imageType, skip = skip ?: SkipOptions(false, null, null), + alternateText = alternateText, ) } } @@ -122,7 +124,8 @@ sealed class MappingItemEntity { var firstLineIndent: Size?, var lineSpacing: LineSpacing?, var keepWithNextParagraph: Boolean?, - var tabs: TabsEntity? + var tabs: TabsEntity?, + var pdfTaggingRule: ParagraphPdfTaggingRule? ) : Definition fun apply(item: ParagraphStyleDto): ParagraphStyleDto { @@ -148,7 +151,8 @@ sealed class MappingItemEntity { firstLineIndent = definition.firstLineIndent, lineSpacing = definition.lineSpacing ?: Additional(null), keepWithNextParagraph = definition.keepWithNextParagraph, - tabs = definition.tabs?.let(TabsModel::fromDb)?.let(Tabs::fromModel) + tabs = definition.tabs?.let(TabsModel::fromDb)?.let(Tabs::fromModel), + pdfTaggingRule = definition.pdfTaggingRule ) ) } @@ -165,7 +169,8 @@ sealed class MappingItemEntity { firstLineIndent = definition.firstLineIndent, lineSpacing = definition.lineSpacing ?: Additional(null), keepWithNextParagraph = definition.keepWithNextParagraph ?: false, - tabs = definition.tabs?.let(TabsModel::fromDb)?.let(Tabs::fromModel) + tabs = definition.tabs?.let(TabsModel::fromDb)?.let(Tabs::fromModel), + pdfTaggingRule = definition.pdfTaggingRule ) ) } @@ -298,7 +303,8 @@ sealed class MappingItemEntity { targetFolder = this.targetFolder, sourcePath = this.sourcePath, imageType = this.imageType, - skip = this.skip + skip = this.skip, + alternateText = this.alternateText, ) } @@ -316,7 +322,8 @@ sealed class MappingItemEntity { firstLineIndent = definition.firstLineIndent, lineSpacing = definition.lineSpacing, keepWithNextParagraph = definition.keepWithNextParagraph, - tabs = definition.tabs?.let(TabsModel::fromDb)?.let(Tabs::fromModel) + tabs = definition.tabs?.let(TabsModel::fromDb)?.let(Tabs::fromModel), + pdfTaggingRule = definition.pdfTaggingRule ) } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/ParagraphStyleEntity.kt b/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/ParagraphStyleEntity.kt index f8155a1a..8f14f626 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/ParagraphStyleEntity.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/persistence/migrationmodel/ParagraphStyleEntity.kt @@ -2,6 +2,7 @@ package com.quadient.migration.persistence.migrationmodel import com.quadient.migration.shared.Alignment import com.quadient.migration.shared.LineSpacing +import com.quadient.migration.shared.ParagraphPdfTaggingRule import com.quadient.migration.shared.Size import com.quadient.migration.shared.TabType import kotlinx.serialization.Serializable @@ -18,6 +19,7 @@ data class ParagraphStyleDefinitionEntity( val lineSpacing: LineSpacing, val keepWithNextParagraph: Boolean?, val tabs: TabsEntity?, + val pdfTaggingRule: ParagraphPdfTaggingRule?, ): ParagraphStyleDefOrRefEntity @Serializable diff --git a/migration-library/src/main/kotlin/com/quadient/migration/persistence/repository/ImageInternalRepository.kt b/migration-library/src/main/kotlin/com/quadient/migration/persistence/repository/ImageInternalRepository.kt index dc8f90d0..9e733cf6 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/persistence/repository/ImageInternalRepository.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/persistence/repository/ImageInternalRepository.kt @@ -23,6 +23,7 @@ class ImageInternalRepository( targetFolder = row[ImageTable.targetFolder]?.let(IcmPath::from), metadata = row[ImageTable.metadata], skip = row[ImageTable.skip], + alternateText = row[ImageTable.alternateText], ) } } \ No newline at end of file diff --git a/migration-library/src/main/kotlin/com/quadient/migration/persistence/table/ImageTable.kt b/migration-library/src/main/kotlin/com/quadient/migration/persistence/table/ImageTable.kt index 14df2c43..3ae4f2cd 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/persistence/table/ImageTable.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/persistence/table/ImageTable.kt @@ -13,4 +13,5 @@ object ImageTable : MigrationObjectTable("image") { val targetFolder = varchar("target_folder", 255).nullable() val metadata = jsonb>>("metadata", Json) val skip = jsonb("skip", Json) + val alternateText = varchar("alternate_text", 255).nullable() } \ No newline at end of file diff --git a/migration-library/src/main/kotlin/com/quadient/migration/persistence/upgrade/V9__image_alternate_text.kt b/migration-library/src/main/kotlin/com/quadient/migration/persistence/upgrade/V9__image_alternate_text.kt new file mode 100644 index 00000000..5c15e8a5 --- /dev/null +++ b/migration-library/src/main/kotlin/com/quadient/migration/persistence/upgrade/V9__image_alternate_text.kt @@ -0,0 +1,17 @@ +package com.quadient.migration.persistence.upgrade + +import org.flywaydb.core.api.migration.BaseJavaMigration +import org.flywaydb.core.api.migration.Context +import java.sql.Connection + +class V9__image_alternate_text : BaseJavaMigration() { + override fun migrate(context: Context) { + val connection: Connection = context.connection + + // Add alternate_text column to image table + val imageStmt = connection.prepareStatement("ALTER TABLE image ADD COLUMN IF NOT EXISTS alternate_text varchar(255)") + imageStmt.executeUpdate() + imageStmt.close() + } +} + diff --git a/migration-library/src/main/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClient.kt b/migration-library/src/main/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClient.kt index f3c2cce5..7cbbb368 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClient.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClient.kt @@ -167,7 +167,7 @@ class InteractiveDeployClient( val outputPathJld = documentObjectBuilder.getStyleDefinitionPath(extension = "jld") val xml2wfdResult = ipsService.xml2wfd( - documentObjectBuilder.buildStyles(emptyList(), emptyList(), withDeltaStyles = true), + documentObjectBuilder.buildStyles(emptyList(), emptyList()), outputPathWfd ) diff --git a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/DesignerDocumentObjectBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/DesignerDocumentObjectBuilder.kt index 13ec2004..2b52fabc 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/DesignerDocumentObjectBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/DesignerDocumentObjectBuilder.kt @@ -30,6 +30,7 @@ import com.quadient.migration.shared.millimeters import com.quadient.wfdxml.WfdXmlBuilder import com.quadient.wfdxml.api.layoutnodes.Flow import com.quadient.wfdxml.api.layoutnodes.FlowArea +import com.quadient.wfdxml.api.layoutnodes.Image import com.quadient.wfdxml.api.layoutnodes.Page import com.quadient.wfdxml.api.layoutnodes.Pages import com.quadient.wfdxml.api.layoutnodes.tables.GeneralRowSet @@ -126,6 +127,10 @@ class DesignerDocumentObjectBuilder( return IcmPath.root().join(fontConfigPath).toString() } + override fun applyImageAlternateText(layout: Layout, image: Image, alternateText: String) { + image.setAlternateText(alternateText) + } + override fun buildDocumentObject(documentObject: DocumentObjectModel, styleDefinitionPath: String?): String { val builder = WfdXmlBuilder() val layout = builder.addLayout() @@ -296,14 +301,15 @@ class DesignerDocumentObjectBuilder( val content = areaModel.content if (content.size == 1 && content.first() is ImageModelRef) { - val imageModel = imageRepository.findModelOrFail((content.first() as ImageModelRef).id) + val imageRef = content.first() as ImageModelRef + val imageModel = imageRepository.findModelOrFail(imageRef.id) when (val imagePlaceholder = getImagePlaceholder(imageModel)) { is ImagePlaceholderResult.Skip -> return is ImagePlaceholderResult.RenderAsNormal -> { page.addImageArea().setPosX(position.x.toMeters()).setPosY(position.y.toMeters()) .setWidth(position.width.toMeters()).setHeight(position.height.toMeters()) - .setImage(getOrBuildImage(layout, imageModel)) + .setImage(getOrBuildImage(layout, imageModel, imageModel.alternateText)) } is ImagePlaceholderResult.Placeholder -> { val flow = layout.addFlow() diff --git a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtils.kt b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtils.kt index 8e12e54b..e365d90a 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtils.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtils.kt @@ -90,6 +90,31 @@ fun getFontByName(layout: Layout, fontName: String): Font? { return fontGroup.children.find { (it as FontImpl).name == fontName } as? Font } +fun getTextStyleByName(layout: Layout, styleName: String): com.quadient.wfdxml.api.layoutnodes.TextStyle? { + val textStyleGroup = (layout as LayoutImpl).children.find { it.name == "TextStyles" } as Group + return textStyleGroup.children.find { (it as com.quadient.wfdxml.internal.layoutnodes.TextStyleImpl).name == styleName } as? com.quadient.wfdxml.api.layoutnodes.TextStyle +} + +fun getColorByRGB(layout: Layout, r: Int, g: Int, b: Int): com.quadient.wfdxml.api.layoutnodes.Color? { + val colorGroup = (layout as LayoutImpl).children.find { it.name == "Colors" } as Group + val targetRed = r / 255.0 + val targetGreen = g / 255.0 + val targetBlue = b / 255.0 + return colorGroup.children.find { + val color = it as com.quadient.wfdxml.internal.layoutnodes.ColorImpl + val colorRgb = color.colorRgb + colorRgb.red == targetRed && colorRgb.green == targetGreen && colorRgb.blue == targetBlue + } as? com.quadient.wfdxml.api.layoutnodes.Color +} + +fun getFillStyleByColor(layout: Layout, color: com.quadient.wfdxml.api.layoutnodes.Color): com.quadient.wfdxml.api.layoutnodes.FillStyle? { + val fillStyleGroup = (layout as LayoutImpl).children.find { it.name == "FillStyles" } as Group + return fillStyleGroup.children.find { + val fillStyle = it as com.quadient.wfdxml.internal.layoutnodes.FillStyleImpl + fillStyle.color == color + } as? com.quadient.wfdxml.api.layoutnodes.FillStyle +} + fun getVariable(data: DataImpl, name: String, parentPath: String): Variable? { return (data.children).find { val variable = it as VariableImpl diff --git a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt index f1f4c84c..86aeab77 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilder.kt @@ -8,6 +8,7 @@ import com.quadient.migration.data.DocumentObjectModel import com.quadient.migration.data.DocumentObjectModelRef import com.quadient.migration.data.FirstMatchModel import com.quadient.migration.data.AreaModel +import com.quadient.migration.data.HyperlinkModel import com.quadient.migration.data.ImageModel import com.quadient.migration.data.ImageModelRef import com.quadient.migration.data.ParagraphModel @@ -48,11 +49,10 @@ import com.quadient.migration.shared.LineSpacing import com.quadient.migration.shared.Literal import com.quadient.migration.shared.LiteralDataType import com.quadient.migration.shared.LiteralOrFunctionCall +import com.quadient.migration.shared.ParagraphPdfTaggingRule as ParagraphPdfTaggingRuleModel import com.quadient.migration.shared.SuperOrSubscript import com.quadient.migration.shared.TabType import com.quadient.wfdxml.WfdXmlBuilder -import com.quadient.wfdxml.api.layoutnodes.Color -import com.quadient.wfdxml.api.layoutnodes.FillStyle import com.quadient.wfdxml.api.layoutnodes.Flow import com.quadient.wfdxml.api.layoutnodes.Font import com.quadient.wfdxml.api.layoutnodes.Image @@ -70,8 +70,15 @@ import com.quadient.wfdxml.api.layoutnodes.font.SubFont import com.quadient.wfdxml.api.layoutnodes.tables.GeneralRowSet import com.quadient.wfdxml.api.layoutnodes.tables.RowSet import com.quadient.wfdxml.api.layoutnodes.tables.Table +import com.quadient.migration.shared.TablePdfTaggingRule +import com.quadient.wfdxml.api.layoutnodes.ParagraphStyle.ParagraphPdfTaggingRule +import com.quadient.wfdxml.api.layoutnodes.TextStyle +import com.quadient.wfdxml.api.layoutnodes.TextStyleInheritFlag +import com.quadient.wfdxml.api.layoutnodes.TextStyleType +import com.quadient.wfdxml.api.layoutnodes.flow.Paragraph import com.quadient.wfdxml.api.module.Layout import com.quadient.wfdxml.internal.data.WorkFlowTreeDefinition +import com.quadient.wfdxml.internal.layoutnodes.TextStyleImpl import com.quadient.wfdxml.internal.layoutnodes.data.DataImpl import com.quadient.wfdxml.internal.layoutnodes.data.WorkFlowTreeEnums.NodeOptionality import com.quadient.wfdxml.internal.layoutnodes.data.WorkFlowTreeEnums.NodeType.SUB_TREE @@ -222,7 +229,6 @@ abstract class InspireDocumentObjectBuilder( fun buildStyles( textStyles: List, paragraphStyles: List, - withDeltaStyles: Boolean = false ): String { logger.debug("Starting to build style definition.") @@ -244,7 +250,7 @@ abstract class InspireDocumentObjectBuilder( buildParagraphStyles(layout, paragraphStyles) logger.debug("Successfully built style definition.") - return builder.build(withDeltaStyles) + return builder.build() } fun buildDocumentContentAsFlows( @@ -430,57 +436,44 @@ abstract class InspireDocumentObjectBuilder( arialFont.setName("Arial").setFontName("Arial") upsertSubFont(arialFont, isBold = false, isItalic = false) - val usedFonts = mutableMapOf("Arial" to arialFont) - val usedColorFillStyles = mutableMapOf>() - textStyleModels.forEach { styleModel -> val definition = styleModel.resolve() - val textStyle = layout.addTextStyle().setName(styleModel.nameOrId()) + applyTextStyleProperties(layout, textStyle, definition) + } + } - if (!definition.fontFamily.isNullOrBlank()) { - val usedFont = usedFonts[definition.fontFamily] - val font = if (usedFont != null) usedFont else { - val newFont = layout.addFont().setName(definition.fontFamily).setFontName(definition.fontFamily) - usedFonts[definition.fontFamily] = newFont - newFont - } - textStyle.setFont(font) + private fun applyTextStyleProperties(layout: Layout, textStyle: TextStyle, definition: TextStyleDefinitionModel) { + val fontFamily = definition.fontFamily ?: "Arial" - val subFont = upsertSubFont(font, definition.bold, definition.italic) - if (subFont != null) { - textStyle.setSubFont(subFont) - } - } + val font = getFontByName(layout, fontFamily) ?: layout.addFont().setName(fontFamily).setFontName(fontFamily) + textStyle.setFont(font) - if (definition.foregroundColor != null) { - val colorId = definition.foregroundColor.toString() - val usedColorFillStyle = usedColorFillStyles[colorId] - if (usedColorFillStyle != null) { - textStyle.setFillStyle(usedColorFillStyle.second) - } else { - val newColor = layout.addColor().setRGB( - definition.foregroundColor.red(), - definition.foregroundColor.green(), - definition.foregroundColor.blue() - ) - val newFillStyle = layout.addFillStyle().setColor(newColor) - usedColorFillStyles[colorId] = Pair(newColor, newFillStyle) - textStyle.setFillStyle(newFillStyle) - } - } + val subFont = upsertSubFont(font, definition.bold, definition.italic) + if (subFont != null) { + textStyle.setSubFont(subFont) + } - definition.size?.let { textStyle.setFontSizeInMeters(definition.size.toMeters()) } - textStyle.setBold(definition.bold) - textStyle.seItalic(definition.italic) - textStyle.setUnderline(definition.underline) - textStyle.setStrikeThrough(definition.strikethrough) - when (definition.superOrSubscript) { - SuperOrSubscript.Subscript -> textStyle.setSubScript(true).setSuperScript(false) - SuperOrSubscript.Superscript -> textStyle.setSubScript(false).setSuperScript(true) - SuperOrSubscript.None -> textStyle.setSubScript(false).setSuperScript(false) - } - definition.interspacing?.let { textStyle.setInterSpacing(it.toMeters()) } + textStyle.setBold(definition.bold) + textStyle.seItalic(definition.italic) + textStyle.setUnderline(definition.underline) + textStyle.setStrikeThrough(definition.strikethrough) + + definition.size?.let { textStyle.setFontSizeInMeters(it.toMeters()) } + definition.interspacing?.let { textStyle.setInterSpacing(it.toMeters()) } + + when (definition.superOrSubscript) { + SuperOrSubscript.Subscript -> textStyle.setSubScript(true).setSuperScript(false) + SuperOrSubscript.Superscript -> textStyle.setSubScript(false).setSuperScript(true) + SuperOrSubscript.None -> textStyle.setSubScript(false).setSuperScript(false) + } + + definition.foregroundColor?.let { colorModel -> + val layoutColor = getColorByRGB(layout, colorModel.red(), colorModel.green(), colorModel.blue()) + ?: layout.addColor().setRGB(colorModel.red(), colorModel.green(), colorModel.blue()) + val fillStyle = getFillStyleByColor(layout, layoutColor) + ?: layout.addFillStyle().setColor(layoutColor) + textStyle.setFillStyle(fillStyle) } } @@ -543,6 +536,20 @@ abstract class InspireDocumentObjectBuilder( paragraphStyle.addTabulator(tabModel.position.toMeters(), tabType) } } + + definition.pdfTaggingRule?.let { pdfTaggingRule -> + val rule = when (pdfTaggingRule) { + ParagraphPdfTaggingRuleModel.Paragraph -> ParagraphPdfTaggingRule.PARAGRAPH + ParagraphPdfTaggingRuleModel.Heading -> ParagraphPdfTaggingRule.HEADING + ParagraphPdfTaggingRuleModel.Heading1 -> ParagraphPdfTaggingRule.HEADING_1 + ParagraphPdfTaggingRuleModel.Heading2 -> ParagraphPdfTaggingRule.HEADING_2 + ParagraphPdfTaggingRuleModel.Heading3 -> ParagraphPdfTaggingRule.HEADING_3 + ParagraphPdfTaggingRuleModel.Heading4 -> ParagraphPdfTaggingRule.HEADING_4 + ParagraphPdfTaggingRuleModel.Heading5 -> ParagraphPdfTaggingRule.HEADING_5 + ParagraphPdfTaggingRuleModel.Heading6 -> ParagraphPdfTaggingRule.HEADING_6 + } + paragraphStyle.setPdfTaggingRule(rule) + } } } @@ -572,7 +579,7 @@ abstract class InspireDocumentObjectBuilder( return ImagePlaceholderResult.RenderAsNormal } - protected fun getOrBuildImage(layout: Layout, imageModel: ImageModel): Image { + protected fun getOrBuildImage(layout: Layout, imageModel: ImageModel, alternateText: String? = null): Image { val image = getImageByName(layout, imageModel.nameOrId()) ?: layout.addImage().setName(imageModel.nameOrId()) .setImageLocation(getImagePath(imageModel), LocationType.ICM) @@ -581,9 +588,15 @@ abstract class InspireDocumentObjectBuilder( imageModel.options.resizeHeight?.let { image.setResizeHeight(it.toMeters()) } } + if (!alternateText.isNullOrBlank()) { + applyImageAlternateText(layout, image, alternateText) + } + return image } + protected abstract fun applyImageAlternateText(layout: Layout, image: Image, alternateText: String) + private fun buildCompositeFlow( layout: Layout, variableStructure: VariableStructureModel, @@ -692,7 +705,7 @@ abstract class InspireDocumentObjectBuilder( } paragraphModel.content.forEach { textModel -> - val text = if (textModel.displayRuleRef == null) { + val baseText = if (textModel.displayRuleRef == null) { paragraph.addText() } else { buildSuccessFlowWrappedInInlineConditionFlow( @@ -702,21 +715,25 @@ abstract class InspireDocumentObjectBuilder( }.addText() } - findTextStyle(textModel)?.also { text.setExistingTextStyle("TextStyles.${it.nameOrId()}") } + val baseTextStyleModel = findTextStyle(textModel) + baseTextStyleModel?.also { baseText.setExistingTextStyle("TextStyles.${it.nameOrId()}") } + + var currentText = baseText textModel.content.forEach { when (it) { - is StringModel -> text.appendText(it.value) - is VariableModelRef -> text.appendVariable(it, layout, variableStructure) - is TableModel -> text.appendTable(buildTable(layout, variableStructure, it, languages)) + is StringModel -> currentText.appendText(it.value) + is VariableModelRef -> currentText.appendVariable(it, layout, variableStructure) + is TableModel -> currentText.appendTable(buildTable(layout, variableStructure, it, languages)) is DocumentObjectModelRef -> buildDocumentObjectRef( layout, variableStructure, it, languages )?.also { flow -> - text.appendFlow(flow) + currentText.appendFlow(flow) } - is ImageModelRef -> buildAndAppendImage(layout, text, it) - is FirstMatchModel -> text.appendFlow( + is ImageModelRef -> buildAndAppendImage(layout, currentText, it) + is HyperlinkModel -> currentText = buildAndAppendHyperlink(layout, paragraph, baseTextStyleModel, it) + is FirstMatchModel -> currentText.appendFlow( buildFirstMatch(layout, variableStructure, it, true, null, languages) ) } @@ -724,6 +741,48 @@ abstract class InspireDocumentObjectBuilder( } } + private fun createHyperlinkTextStyle( + layout: Layout, baseTextStyleModel: TextStyleModel?, hyperlinkModel: HyperlinkModel + ): TextStyle { + val baseStyleName = baseTextStyleModel?.nameOrId() ?: "text" + val hyperlinkName = generateUniqueHyperlinkStyleName(layout, baseStyleName) + + val urlVariable = createUrlVariable(layout, hyperlinkName, hyperlinkModel.url) + + val hyperlinkStyle = layout.addTextStyle() + .setName(hyperlinkName) + .setUrlTarget(urlVariable) + .setType(TextStyleType.DELTA) + .setUrlAlternateText(hyperlinkModel.alternateText) + .addInheritFlags( + *TextStyleInheritFlag.entries + .filter { it != TextStyleInheritFlag.UNDERLINE && it != TextStyleInheritFlag.FILL_STYLE } + .toTypedArray()) + (hyperlinkStyle as TextStyleImpl).setAncestorId("Def.TextStyleHyperlink") + + if (baseTextStyleModel != null) { + val definition = baseTextStyleModel.resolve() + applyTextStyleProperties(layout, hyperlinkStyle, definition) + } + + return hyperlinkStyle + } + + private fun generateUniqueHyperlinkStyleName(layout: Layout, baseStyleName: String): String { + var counter = 1 + var candidateName = "${baseStyleName}_url_${counter}" + while (getTextStyleByName(layout, candidateName) != null) { + counter++ + candidateName = "${baseStyleName}_url_${counter}" + } + return candidateName + } + + private fun createUrlVariable(layout: Layout, variableName: String, url: String): Variable { + return layout.data.addVariable().setName(variableName).setKind(VariableKind.CONSTANT) + .setDataType(DataType.STRING).setValue(url) + } + private fun buildAndAppendImage(layout: Layout, text: Text, ref: ImageModelRef) { val imageModel = imageRepository.findModelOrFail(ref.id) @@ -736,7 +795,20 @@ abstract class InspireDocumentObjectBuilder( is ImagePlaceholderResult.Skip -> return } - text.appendImage(getOrBuildImage(layout, imageModel)) + text.appendImage(getOrBuildImage(layout, imageModel, imageModel.alternateText)) + } + + private fun buildAndAppendHyperlink( + layout: Layout, paragraph: Paragraph, baseTextStyleModel: TextStyleModel?, hyperlinkModel: HyperlinkModel + ): Text { + val hyperlinkText = paragraph.addText() + val hyperlinkStyle = createHyperlinkTextStyle(layout, baseTextStyleModel, hyperlinkModel) + hyperlinkText.setTextStyle(hyperlinkStyle) + hyperlinkText.appendText(hyperlinkModel.displayText ?: hyperlinkModel.url) + + val newText = paragraph.addText() + baseTextStyleModel?.also { newText.setExistingTextStyle("TextStyles.${it.nameOrId()}") } + return newText } private fun Text.appendVariable( @@ -839,6 +911,13 @@ abstract class InspireDocumentObjectBuilder( } } + when (model.pdfTaggingRule) { + TablePdfTaggingRule.None -> table.setTablePdfTaggingRule(Table.TablePdfTaggingRule.NONE) + TablePdfTaggingRule.Default -> table.setTablePdfTaggingRule(Table.TablePdfTaggingRule.DEFAULT) + TablePdfTaggingRule.Table -> table.setTablePdfTaggingRule(Table.TablePdfTaggingRule.TABLE) + } + table.setTablePdfAlternateText(model.pdfAlternateText) + return table } diff --git a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilder.kt b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilder.kt index 4d0e06b2..e56dc300 100644 --- a/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilder.kt +++ b/migration-library/src/main/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilder.kt @@ -25,6 +25,10 @@ import com.quadient.migration.shared.ImageType import com.quadient.migration.shared.orDefault import com.quadient.wfdxml.WfdXmlBuilder import com.quadient.wfdxml.api.layoutnodes.Flow +import com.quadient.wfdxml.api.layoutnodes.Image +import com.quadient.wfdxml.api.layoutnodes.data.DataType +import com.quadient.wfdxml.api.layoutnodes.data.VariableKind +import com.quadient.wfdxml.api.module.Layout import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -125,6 +129,20 @@ class InteractiveDocumentObjectBuilder( .join(fontConfigPath.orDefault("Resources/Fonts")).toString() } + override fun applyImageAlternateText(layout: Layout, image: Image, alternateText: String) { + val escapedText = alternateText.replace("\"", "\\\"") + val variable = layout.data.addVariable() + .setName("Alternate text variable for ${image.name}") + .setKind(VariableKind.CALCULATED) + .setDataType(DataType.STRING) + .setScript("return '$escapedText';") + .addCustomProperty("ValueWrapperVariable", true) + val layoutRoot = layout.root ?: layout.addRoot() + layoutRoot.addLockedWebNode(variable) + + image.setAlternateTextVariable(variable) + } + override fun buildDocumentObject(documentObject: DocumentObjectModel, styleDefinitionPath: String?): String { logger.debug("Starting to build document object '${documentObject.nameOrId()}'.") diff --git a/migration-library/src/main/kotlin/com/quadient/migration/shared/PdfTaggingRule.kt b/migration-library/src/main/kotlin/com/quadient/migration/shared/PdfTaggingRule.kt new file mode 100644 index 00000000..dd6553a8 --- /dev/null +++ b/migration-library/src/main/kotlin/com/quadient/migration/shared/PdfTaggingRule.kt @@ -0,0 +1,23 @@ +package com.quadient.migration.shared + +import kotlinx.serialization.Serializable + +@Serializable +enum class TablePdfTaggingRule { + None, + Default, + Table +} + +@Serializable +enum class ParagraphPdfTaggingRule { + Paragraph, + Heading, + Heading1, + Heading2, + Heading3, + Heading4, + Heading5, + Heading6 +} + diff --git a/migration-library/src/test/kotlin/com/quadient/migration/persistence/DocumentObjectRepositoryTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/persistence/DocumentObjectRepositoryTest.kt index 7e9a5ef4..ff14ba69 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/persistence/DocumentObjectRepositoryTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/persistence/DocumentObjectRepositoryTest.kt @@ -32,6 +32,8 @@ class DocumentObjectRepositoryTest { @Test fun `roundtrip is correct`() { val table = table { + pdfTaggingRule(com.quadient.migration.shared.TablePdfTaggingRule.Table) + pdfAlternateText("Table alt text") row { displayRuleRef("someref") cell { } @@ -167,4 +169,3 @@ class DocumentObjectRepositoryTest { result.first().id.shouldBeEqualTo("parablock") } } - diff --git a/migration-library/src/test/kotlin/com/quadient/migration/persistence/migrationmodel/MappingEntityTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/persistence/migrationmodel/MappingEntityTest.kt index 757f1014..8f7773d0 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/persistence/migrationmodel/MappingEntityTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/persistence/migrationmodel/MappingEntityTest.kt @@ -14,6 +14,7 @@ import com.quadient.migration.shared.DataType import com.quadient.migration.shared.DocumentObjectType import com.quadient.migration.shared.ImageType import com.quadient.migration.shared.LineSpacing +import com.quadient.migration.shared.ParagraphPdfTaggingRule import com.quadient.migration.shared.SkipOptions import com.quadient.migration.shared.SuperOrSubscript import com.quadient.migration.shared.millimeters @@ -28,6 +29,7 @@ import com.quadient.migration.tools.model.aImage import com.quadient.migration.tools.model.aParagraph import com.quadient.migration.tools.model.anArea import com.quadient.migration.tools.shouldBeEqualTo +import com.quadient.wfdxml.api.layoutnodes.ParagraphStyle import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -262,7 +264,8 @@ class MappingEntityTest { firstLineIndent = 3.0.millimeters(), lineSpacing = LineSpacing.ExactFromPrevious(14.0.millimeters()), keepWithNextParagraph = false, - tabs = TabsEntity(tabs = emptyList(), useOutsideTabs = true) + tabs = TabsEntity(tabs = emptyList(), useOutsideTabs = true), + pdfTaggingRule = ParagraphPdfTaggingRule.Heading1 ) ) val dto = aParagraphStyle( @@ -275,7 +278,9 @@ class MappingEntityTest { alignment = Alignment.JustifyLeft, firstLineIndent = 2.0.millimeters(), lineSpacing = LineSpacing.ExactFromPrevious(12.0.millimeters()), - keepWithNextParagraph = true + keepWithNextParagraph = true, + tabs = null, + pdfTaggingRule = null, ) ) @@ -312,7 +317,8 @@ class MappingEntityTest { firstLineIndent = 3.0.millimeters(), lineSpacing = LineSpacing.ExactFromPrevious(14.0.millimeters()), keepWithNextParagraph = false, - tabs = TabsEntity(tabs = emptyList(), useOutsideTabs = true) + tabs = TabsEntity(tabs = emptyList(), useOutsideTabs = true), + pdfTaggingRule = ParagraphPdfTaggingRule.Paragraph ) ) val dto = aParagraphStyle("para1", name = "para1", definition = ParagraphStyleRef("other")) @@ -335,6 +341,7 @@ class MappingEntityTest { assertEquals(resultDef.keepWithNextParagraph, false) assertEquals(resultDef.tabs?.tabs?.size, 0) assertEquals(resultDef.tabs?.useOutsideTabs, true) + assertEquals(resultDef.pdfTaggingRule, ParagraphPdfTaggingRule.Paragraph) } } diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/DesignerDeployClientTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/DesignerDeployClientTest.kt index 8afb709b..e4acc9ea 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/DesignerDeployClientTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/DesignerDeployClientTest.kt @@ -488,7 +488,7 @@ class DesignerDeployClientTest { @Test fun `deployStyles creates style definition and sets production approval state`() { // given - every { documentObjectBuilder.buildStyles(any(), any(), any()) } returns "" + every { documentObjectBuilder.buildStyles(any(), any()) } returns "" every { documentObjectBuilder.buildStyleLayoutDelta(any(), any()) } returns "" every { statusTrackingRepository.findLastEventRelevantToOutput(any(), any(), any()) } returns Active() @@ -511,7 +511,7 @@ class DesignerDeployClientTest { @Test fun `deployStyles does not continue when wfd creation fails`() { // given - every { documentObjectBuilder.buildStyles(any(), any(), any()) } returns "" + every { documentObjectBuilder.buildStyles(any(), any()) } returns "" every { statusTrackingRepository.findLastEventRelevantToOutput(any(), any(), any()) } returns Active() every { statusTrackingRepository.deployed(any(), any(), any(), any(), any(), any()) } returns aDeployedStatus("id") diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClientTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClientTest.kt index dc4d18b1..0c95fce5 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClientTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/deploy/InteractiveDeployClientTest.kt @@ -325,7 +325,7 @@ class InteractiveDeployClientTest { @Test fun `deployStyles creates style definition and sets production approval state`() { // given - every { documentObjectBuilder.buildStyles(any(), any(), any()) } returns "" + every { documentObjectBuilder.buildStyles(any(), any()) } returns "" every { documentObjectBuilder.buildStyleLayoutDelta(any(), any()) } returns "" every { statusTrackingRepository.findLastEventRelevantToOutput(any(), any(), any()) } returns Active() @@ -356,7 +356,7 @@ class InteractiveDeployClientTest { @Test fun `deployStyles does not continue when wfd creation fails`() { // given - every { documentObjectBuilder.buildStyles(any(), any(), any()) } returns "" + every { documentObjectBuilder.buildStyles(any(), any()) } returns "" every { statusTrackingRepository.findLastEventRelevantToOutput(any(), any(), any()) } returns Active() every { statusTrackingRepository.deployed(any(), any(), any(), any(), any(), any()) } returns aDeployedStatus("id") diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/DesignerDocumentObjectBuilderTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/DesignerDocumentObjectBuilderTest.kt index 53c03534..9541f25d 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/DesignerDocumentObjectBuilderTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/DesignerDocumentObjectBuilderTest.kt @@ -54,7 +54,6 @@ import com.quadient.migration.tools.model.aVariableStructureModel import com.quadient.migration.tools.model.anArea import com.quadient.migration.tools.shouldBeEqualTo import com.quadient.migration.tools.shouldNotBeEmpty -import com.quadient.migration.tools.shouldNotBeEqualTo import com.quadient.migration.tools.shouldNotBeNull import io.mockk.every import io.mockk.mockk @@ -229,6 +228,35 @@ class DesignerDocumentObjectBuilderTest { .shouldBeEqualTo("VCSLocation,icm://${config.defaultTargetFolder}/${imageModel.nameOrId()}.jpg") } + @Test + fun `buildDocumentObject creates image with alternate text from ImageModel`() { + // given + val imageModel = mockImg(aImage("Img_1", alternateText = "Description of the image")) + val page = mockObj( + aDocObj( + "P_1", Page, listOf( + anArea( + listOf( + ImageModelRef(imageModel.id) + ), Position(60.millimeters(), 120.millimeters(), 20.centimeters(), 10.centimeters()) + ), + ) + ) + ) + val template = mockObj(aDocObj("T_1", Template, listOf(aDocumentObjectRef(page.id)))) + + // when + val result = + subject.buildDocumentObject(template, null).let { xmlMapper.readTree(it.trimIndent()) }["Layout"]["Layout"] + + // then + val imageObject = result["ImageObject"].last() + val imageId = imageObject["ImageId"].textValue() + + val image = result["Image"].last { it["Id"].textValue() == imageId } + image["PDFAdvanced"]["Tagging"]["AlternateText"].textValue().shouldBeEqualTo("Description of the image") + } + @Test fun `buildDocumentObject uses inline condition flow when block is used with display rule`() { // given @@ -283,7 +311,7 @@ class DesignerDocumentObjectBuilderTest { aCell(aParagraph(aText(StringModel("D")))) ), rule.id ) - ), listOf() + ), listOf(), pdfTaggingRule = com.quadient.migration.shared.TablePdfTaggingRule.Table, pdfAlternateText = "Table alt text" ) ) ) @@ -300,6 +328,10 @@ class DesignerDocumentObjectBuilderTest { val secondRow = result["RowSet"].last { it["Id"].textValue() == rowIds[1].textValue() } secondRow["RowSetType"].textValue().shouldBeEqualTo("InlCond") secondRow["RowSetCondition"][0]["Condition"].textValue().shouldBeEqualTo("return (String('A')==String('B'));") + val pdfAdvanced = result["Table"].last()["PDFAdvanced"] + pdfAdvanced.shouldNotBeNull() + pdfAdvanced["Tagging"]["Rule"].textValue().shouldBeEqualTo("Table") + pdfAdvanced["Tagging"]["AlternateText"].textValue().shouldBeEqualTo("Table alt text") } @Test diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtils.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtils.kt deleted file mode 100644 index caa66bb0..00000000 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtils.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.quadient.migration.service.inspirebuilder - -import com.quadient.migration.tools.shouldBeEqualTo -import org.junit.jupiter.api.Test - -class InspireBuilderUtils { - @Test - fun `sanitizeVariablePart basic input`() { - val result = sanitizeVariablePart("Name-1") - - result.shouldBeEqualTo("Name_1") - } - - @Test - fun `sanitizeVariablePart sanitizes more complex input correctly`() { - val result = sanitizeVariablePart("My Special Name-1(Joe).:?!") - - result.shouldBeEqualTo("My__Special_Name_1_Joe_____") - } -} \ No newline at end of file diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtilsTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtilsTest.kt new file mode 100644 index 00000000..e32eac50 --- /dev/null +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireBuilderUtilsTest.kt @@ -0,0 +1,185 @@ +package com.quadient.migration.service.inspirebuilder + +import com.quadient.wfdxml.WfdXmlBuilder +import com.quadient.migration.tools.shouldBeEqualTo +import com.quadient.migration.tools.shouldBeNull +import com.quadient.migration.tools.shouldNotBeNull +import org.junit.jupiter.api.Test + +class InspireBuilderUtilsTest { + @Test + fun `getTextStyleByName returns null for non-existent style`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + layout.addTextStyle().setName("ExistingStyle") + + // when + val result = getTextStyleByName(layout, "NonExistentStyle") + + // then + result.shouldBeNull() + } + + @Test + fun `getTextStyleByName finds correct style among multiple`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + layout.addTextStyle().setName("Style1") + val targetStyle = layout.addTextStyle().setName("Style2") + layout.addTextStyle().setName("Style3") + + // when + val result = getTextStyleByName(layout, "Style2") + + // then + result.shouldNotBeNull() + result.shouldBeEqualTo(targetStyle) + } + + @Test + fun `getColorByRGB returns null for non-existent color`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + layout.addColor().setRGB(255, 0, 0) // Red + + // when + val result = getColorByRGB(layout, 0, 255, 0) // Looking for green + + // then + result.shouldBeNull() + } + + @Test + fun `getColorByRGB finds correct color among multiple`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + layout.addColor().setRGB(255, 0, 0) // Red + val targetColor = layout.addColor().setRGB(0, 255, 0) // Green + layout.addColor().setRGB(0, 0, 255) // Blue + + // when + val result = getColorByRGB(layout, 0, 255, 0) + + // then + result.shouldNotBeNull() + result.shouldBeEqualTo(targetColor) + } + + @Test + fun `getFillStyleByColor returns null for non-existent fill style`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + val color1 = layout.addColor().setRGB(255, 0, 0) + layout.addFillStyle().setColor(color1) + + val color2 = layout.addColor().setRGB(0, 255, 0) + + // when + val result = getFillStyleByColor(layout, color2) + + // then + result.shouldBeNull() + } + + @Test + fun `getFillStyleByColor finds correct fill style among multiple`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + val color1 = layout.addColor().setRGB(255, 0, 0) + val color2 = layout.addColor().setRGB(0, 255, 0) + val color3 = layout.addColor().setRGB(0, 0, 255) + + layout.addFillStyle().setColor(color1) + val targetFillStyle = layout.addFillStyle().setColor(color2) + layout.addFillStyle().setColor(color3) + + // when + val result = getFillStyleByColor(layout, color2) + + // then + result.shouldNotBeNull() + result.shouldBeEqualTo(targetFillStyle) + } + + @Test + fun `getFontByName returns null for non-existent font`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + layout.addFont().setName("Arial").setFontName("Arial") + + // when + val result = getFontByName(layout, "TimesNewRoman") + + // then + result.shouldBeNull() + } + + @Test + fun `getFontByName finds correct font among multiple`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + layout.addFont().setName("Arial").setFontName("Arial") + val targetFont = layout.addFont().setName("Helvetica").setFontName("Helvetica") + layout.addFont().setName("Courier").setFontName("Courier") + + // when + val result = getFontByName(layout, "Helvetica") + + // then + result.shouldNotBeNull() + result.shouldBeEqualTo(targetFont) + } + + @Test + fun `getImageByName returns null for non-existent image`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + layout.addImage().setName("ExistingImage") + + // when + val result = getImageByName(layout, "NonExistentImage") + + // then + result.shouldBeNull() + } + + @Test + fun `getImageByName finds correct image among multiple`() { + // given + val builder = WfdXmlBuilder() + val layout = builder.addLayout() + layout.addImage().setName("Image1") + val targetImage = layout.addImage().setName("Image2") + layout.addImage().setName("Image3") + + // when + val result = getImageByName(layout, "Image2") + + // then + result.shouldNotBeNull() + result.shouldBeEqualTo(targetImage) + } + + @Test + fun `sanitizeVariablePart basic input`() { + val result = sanitizeVariablePart("Name-1") + + result.shouldBeEqualTo("Name_1") + } + + @Test + fun `sanitizeVariablePart sanitizes more complex input correctly`() { + val result = sanitizeVariablePart("My Special Name-1(Joe).:?!") + + result.shouldBeEqualTo("My__Special_Name_1_Joe_____") + } +} diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilderTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilderTest.kt index d4ea265f..df1c21d8 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilderTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InspireDocumentObjectBuilderTest.kt @@ -1,18 +1,168 @@ package com.quadient.migration.service.inspirebuilder +import com.fasterxml.jackson.dataformat.xml.XmlMapper +import com.quadient.migration.api.InspireOutput import com.quadient.migration.data.DisplayRuleModel +import com.quadient.migration.data.DocumentObjectModel +import com.quadient.migration.data.HyperlinkModel +import com.quadient.migration.data.StringModel +import com.quadient.migration.data.TextStyleModel +import com.quadient.migration.data.TextStyleModelRef +import com.quadient.migration.persistence.repository.* +import com.quadient.migration.service.ipsclient.IpsService import com.quadient.migration.shared.BinOp import com.quadient.migration.shared.Function import com.quadient.migration.shared.Literal import com.quadient.migration.shared.LiteralDataType -import com.quadient.migration.tools.model.aVariable -import com.quadient.migration.tools.model.aDisplayRule -import com.quadient.migration.tools.model.aVariableStructureModel +import com.quadient.migration.shared.Size +import com.quadient.migration.tools.aProjectConfig +import com.quadient.migration.tools.model.* import com.quadient.migration.tools.shouldBeEqualTo -import org.junit.jupiter.api.Test +import com.quadient.wfdxml.api.layoutnodes.TextStyleInheritFlag import com.quadient.wfdxml.internal.module.layout.LayoutImpl +import io.mockk.every +import io.mockk.mockk +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test class InspireDocumentObjectBuilderTest { + private val documentObjectRepository = mockk() + private val textStyleRepository = mockk() + private val paragraphStyleRepository = mockk() + private val variableRepository = mockk() + private val variableStructureRepository = mockk() + private val displayRuleRepository = mockk() + private val imageRepository = mockk() + private val ipsService = mockk() + + private val xmlMapper = XmlMapper().also { it.findAndRegisterModules() } + + private var subject = DesignerDocumentObjectBuilder( + documentObjectRepository, + textStyleRepository, + paragraphStyleRepository, + variableRepository, + variableStructureRepository, + displayRuleRepository, + imageRepository, + aProjectConfig(output = InspireOutput.Designer), + ipsService, + ) + + @BeforeEach + fun setUp() { + every { variableStructureRepository.listAllModel() } returns emptyList() + every { textStyleRepository.listAllModel() } returns emptyList() + every { paragraphStyleRepository.listAllModel() } returns emptyList() + every { ipsService.gatherFontData(any()) } returns "Arial,Regular,icm://Fonts/arial.ttf;" + every { ipsService.fileExists(any()) } returns true + } + + @Test + fun `buildStyles creates ParagraphStyle with PdfTaggingRule Paragraph`() { + // given + val paragraphStyle = aParaStyle( + "PS_Para", + definition = aParaDef( + leftIndent = Size.ofMillimeters(0), + pdfTaggingRule = com.quadient.migration.shared.ParagraphPdfTaggingRule.Paragraph + ) + ) + every { paragraphStyleRepository.listAllModel() } returns listOf(paragraphStyle) + + // when + val result = subject.buildStyles(emptyList(), listOf(paragraphStyle)) + .let { xmlMapper.readTree(it.trimIndent()) } + + // then + val paraStyles = result["Layout"]["Layout"]["ParaStyle"] + val paraStyleId = paraStyles.first { it["Name"]?.textValue() == paragraphStyle.name }["Id"].textValue() + val paraStyleContent = paraStyles.last { it["Id"].textValue() == paraStyleId } + + paraStyleContent["PDFAdvanced"]["Tagging"]["Rule"].textValue().shouldBeEqualTo("P") + } + + @Test + fun `buildDocumentObject with hyperlink creates text style with URL variable and alternate text`() { + // given + val hyperlink = HyperlinkModel("https://www.example.com", "Click here", "Link to example website") + val textContent = listOf(StringModel("Visit our site: "), hyperlink, StringModel(" for more info")) + val block = mockObj(aBlock("B_1", listOf(aParagraph(aText(textContent))))) + + // when + val result = subject.buildDocumentObject(block, null).let { xmlMapper.readTree(it.trimIndent()) }["Layout"]["Layout"] + + // then + val hyperlinkStyleId = result["TextStyle"].first { it["Name"]?.textValue() == "text_url_1" }["Id"].textValue() + val hyperlinkStyleContent = result["TextStyle"].last { it["Id"].textValue() == hyperlinkStyleId } + + val urlVarId = hyperlinkStyleContent["URLLink"].textValue() + hyperlinkStyleContent["URLAlternateText"].textValue().shouldBeEqualTo("Link to example website") + + result["Variable"].first { it["Name"].textValue() == "text_url_1" } + val urlVariableContent = result["Variable"].last { it["Id"].textValue() == urlVarId } + urlVariableContent["Type"].textValue().shouldBeEqualTo("Constant") + urlVariableContent["VarType"].textValue().shouldBeEqualTo("String") + urlVariableContent["Content"].textValue().shouldBeEqualTo("https://www.example.com") + } + + @Test + fun `buildDocumentObject with hyperlink inherits from base text style correctly`() { + // given + val textStyle = mockTextStyle( + aTextStyle( + "TS_Base", definition = aTextDef( + fontFamily = "Calibri", size = Size.ofPoints(14), bold = true, italic = true + ) + ) + ) + + val block = mockObj( + aBlock( + "B_1", listOf( + aParagraph( + aText( + listOf( + StringModel("Some text before link. "), + HyperlinkModel("https://www.example.com", "Link text"), + StringModel(" Some text after link.") + ), TextStyleModelRef(textStyle.id) + ) + ) + ) + ) + ) + + // when + val result = + subject.buildDocumentObject(block, null).let { xmlMapper.readTree(it.trimIndent()) }["Layout"]["Layout"] + + // then + val hyperlinkStyleId = + result["TextStyle"].first { it["Name"]?.textValue() == "${textStyle.name}_url_1" }["Id"].textValue() + val hyperlinkStyleContent = result["TextStyle"].last { it["Id"].textValue() == hyperlinkStyleId } + + hyperlinkStyleContent["Type"].textValue().shouldBeEqualTo("Delta") + hyperlinkStyleContent["AncestorId"].textValue().shouldBeEqualTo("Def.TextStyleHyperlink") + val inheritFlags = hyperlinkStyleContent["InheritFlag"] + + inheritFlags.size().shouldBeEqualTo(TextStyleInheritFlag.entries.size - 2) + + assert(inheritFlags.none { it.textValue() == "Underline" }) + assert(inheritFlags.none { it.textValue() == "FillStyle" }) + } + + private fun mockObj(documentObject: DocumentObjectModel): DocumentObjectModel { + every { documentObjectRepository.findModelOrFail(documentObject.id) } returns documentObject + return documentObject + } + + private fun mockTextStyle(textStyle: TextStyleModel): TextStyleModel { + every { textStyleRepository.firstWithDefinitionModel(textStyle.id) } returns textStyle + val currentAllStyles = textStyleRepository.listAllModel() + every { textStyleRepository.listAllModel() } returns currentAllStyles + textStyle + return textStyle + } @Test fun `simple display rule with single expression`() { @@ -84,4 +234,4 @@ class InspireDocumentObjectBuilderTest { findVar = { aVariable(it) } ) ?: error("No definition") } -} \ No newline at end of file +} diff --git a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt index 3fd26fce..b034c2d0 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/service/inspirebuilder/InteractiveDocumentObjectBuilderTest.kt @@ -67,6 +67,7 @@ import com.quadient.migration.tools.shouldBeEqualTo import com.quadient.migration.tools.shouldBeNull import com.quadient.migration.tools.shouldNotBeEmpty import com.quadient.migration.tools.shouldNotBeEqualTo +import com.quadient.migration.tools.shouldNotBeNull import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -739,6 +740,40 @@ class InteractiveDocumentObjectBuilderTest { .shouldBeEqualTo(image.options?.resizeHeight?.toMeters().toString()) } + @Test + fun `build block with image uses alternateText from ImageModel`() { + // given + val image = aImage("Dog", alternateText = "A cute dog picture") + val block = aBlock("1", listOf(ImageModelRef(image.id))) + + every { imageRepository.findModelOrFail(image.id) } returns image + + // when + val result = subject.buildDocumentObject(block, null).let { xmlMapper.readTree(it.trimIndent()) } + + // then + result["Image"].first()["Name"].textValue().shouldBeEqualTo("Image_Dog") + val imageContent = result["Image"].last() + val varId = imageContent["AlternativeTextVar"].textValue() + varId.shouldNotBeNull() + + val tagging = imageContent["PDFAdvanced"]["Tagging"] + tagging["AlternateTextType"].textValue().shouldBeEqualTo("2") + tagging["AlternateTextNodeId"].textValue().shouldBeEqualTo(varId) + tagging["AlternateText"].textValue().shouldBeEqualTo("") + tagging["Rule"].textValue().shouldBeEqualTo("Figure") + + val varData = result["Variable"].first { it["Id"].textValue() == varId } + varData["Name"].textValue().shouldBeEqualTo("Alternate text variable for Image_Dog") + varData["CustomProperty"].textValue().shouldBeEqualTo("{\"ValueWrapperVariable\":true}") + + val varContent = result["Variable"].last { it["Id"].textValue() == varId } + varContent["Type"].textValue().shouldBeEqualTo("Calculated") + varContent["Script"].textValue().shouldBeEqualTo("return 'A cute dog picture';") + + result["Root"]["LockedWebNodes"]["LockedWebNode"].textValue().shouldBeEqualTo(varId) + } + @Test fun `build template with two blocks referencing same image results in single image definition`() { // given diff --git a/migration-library/src/test/kotlin/com/quadient/migration/tools/TestObjectBuilders.kt b/migration-library/src/test/kotlin/com/quadient/migration/tools/TestObjectBuilders.kt index 40097ee0..a29fd8b3 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/tools/TestObjectBuilders.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/tools/TestObjectBuilders.kt @@ -51,6 +51,7 @@ import com.quadient.migration.shared.DocumentObjectOptions import com.quadient.migration.shared.DocumentObjectType import com.quadient.migration.shared.IcmPath import com.quadient.migration.shared.LineSpacing +import com.quadient.migration.shared.ParagraphPdfTaggingRule import com.quadient.migration.shared.Size import com.quadient.migration.shared.SkipOptions import com.quadient.migration.shared.SuperOrSubscript @@ -250,7 +251,8 @@ fun aParagraphStyleDefinition( firstLineIndent: Size? = null, lineSpacing: LineSpacing = LineSpacing.Additional(null), keepWithNextParagraph: Boolean? = null, - tabs: Tabs? = null + tabs: Tabs? = null, + pdfTaggingRule: ParagraphPdfTaggingRule? = null, ): ParagraphStyleDefinition { return ParagraphStyleDefinition( leftIndent = leftIndent, @@ -262,7 +264,8 @@ fun aParagraphStyleDefinition( firstLineIndent = firstLineIndent, lineSpacing = lineSpacing, keepWithNextParagraph = keepWithNextParagraph, - tabs = tabs + tabs = tabs, + pdfTaggingRule = pdfTaggingRule, ) } diff --git a/migration-library/src/test/kotlin/com/quadient/migration/tools/model/TestModelObjectBuilders.kt b/migration-library/src/test/kotlin/com/quadient/migration/tools/model/TestModelObjectBuilders.kt index b0279db8..94b7c535 100644 --- a/migration-library/src/test/kotlin/com/quadient/migration/tools/model/TestModelObjectBuilders.kt +++ b/migration-library/src/test/kotlin/com/quadient/migration/tools/model/TestModelObjectBuilders.kt @@ -56,6 +56,7 @@ import com.quadient.migration.shared.ImageType import com.quadient.migration.shared.LineSpacing import com.quadient.migration.shared.LiteralOrFunctionCall import com.quadient.migration.shared.MetadataPrimitive +import com.quadient.migration.shared.ParagraphPdfTaggingRule import com.quadient.migration.shared.Position import com.quadient.migration.shared.Size import com.quadient.migration.shared.SkipOptions @@ -214,7 +215,8 @@ fun aParaDef( firstLineIndent: Size? = null, lineSpacing: LineSpacing = LineSpacing.Additional(null), keepWithNextParagraph: Boolean? = null, - tabs: TabsModel? = null + tabs: TabsModel? = null, + pdfTaggingRule: ParagraphPdfTaggingRule? = null, ): ParagraphStyleDefinitionModel { return ParagraphStyleDefinitionModel( leftIndent = leftIndent, @@ -226,7 +228,8 @@ fun aParaDef( firstLineIndent = firstLineIndent, lineSpacing = lineSpacing, keepWithNextParagraph = keepWithNextParagraph, - tabs = tabs + tabs = tabs, + pdfTaggingRule = pdfTaggingRule, ) } @@ -381,6 +384,7 @@ fun aImage( targetFolder: String? = null, metadata : Map> = emptyMap(), skip : SkipOptions = SkipOptions(false, null, null), + alternateText: String? = null, ): ImageModel { return ImageModel( id = id, @@ -394,6 +398,7 @@ fun aImage( targetFolder = targetFolder?.let(IcmPath::from), metadata = metadata, skip = skip, + alternateText = alternateText, ) } diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/Node.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/Node.java index abb39f56..931faa31 100644 --- a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/Node.java +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/Node.java @@ -1,5 +1,7 @@ package com.quadient.wfdxml.api; +import java.util.Map; + public interface Node> { String getName(); @@ -16,4 +18,8 @@ public interface Node> { String getDisplayName(); S setDisplayName(String displayName); + + Map getCustomProperties(); + + S addCustomProperty(String key, Object value); } diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Image.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Image.java index 1fd90e97..30cb1e81 100644 --- a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Image.java +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Image.java @@ -1,6 +1,7 @@ package com.quadient.wfdxml.api.layoutnodes; import com.quadient.wfdxml.api.Node; +import com.quadient.wfdxml.api.layoutnodes.data.Variable; public interface Image extends Node { @@ -23,4 +24,8 @@ public interface Image extends Node { Image setTransparentG(int min, int max); Image setTransparentB(int min, int max); + + Image setAlternateText(String alternateText); + + Image setAlternateTextVariable(Variable variable); } \ No newline at end of file diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/ParagraphStyle.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/ParagraphStyle.java index b4f67e9f..96c444a5 100644 --- a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/ParagraphStyle.java +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/ParagraphStyle.java @@ -58,6 +58,8 @@ public interface ParagraphStyle extends Node { ParagraphStyle setHyphenate(boolean hyphenate); + ParagraphStyle setPdfTaggingRule(ParagraphPdfTaggingRule rule); + enum AlignType { LEFT, RIGHT, @@ -86,4 +88,15 @@ enum NumberingType { RESET, NONE } + + enum ParagraphPdfTaggingRule { + PARAGRAPH, + HEADING, + HEADING_1, + HEADING_2, + HEADING_3, + HEADING_4, + HEADING_5, + HEADING_6 + } } \ No newline at end of file diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Root.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Root.java index 9c1c1d27..4acca4f2 100644 --- a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Root.java +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/Root.java @@ -1,7 +1,14 @@ package com.quadient.wfdxml.api.layoutnodes; +import com.quadient.wfdxml.api.Node; + +import java.util.List; + public interface Root { Root setAllowRuntimeModifications(boolean allowRuntimeModifications); Root setExternalStylesLayout(String vcsLocation); Root setSubject(String subject); + + Root addLockedWebNode(Node node); + List> getLockedWebNodes(); } \ No newline at end of file diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyle.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyle.java index c1a63408..91c28ed2 100644 --- a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyle.java +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyle.java @@ -52,6 +52,8 @@ public interface TextStyle extends Node { TextStyle setUrlTarget(Variable variable); + TextStyle setUrlAlternateText(String alternateText); + TextStyle setUnderlineStyle(LineStyle lineStyle); TextStyle setStrikeThroughStyle(LineStyle lineStyle); @@ -61,4 +63,10 @@ public interface TextStyle extends Node { TextStyle setConnectBorders(boolean connectBorders); TextStyle setWithLineGap(boolean withLineGap); + + TextStyle setType(TextStyleType type); + + TextStyle addInheritFlag(TextStyleInheritFlag flag); + + TextStyle addInheritFlags(TextStyleInheritFlag... flags); } diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyleInheritFlag.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyleInheritFlag.java new file mode 100644 index 00000000..89509957 --- /dev/null +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyleInheritFlag.java @@ -0,0 +1,47 @@ +package com.quadient.wfdxml.api.layoutnodes; + +public enum TextStyleInheritFlag { + FONT("Font"), + FONT_SIZE("FontSize"), + BASELINE_SHIFT("BaselineShift"), + INTER_CHARACTER_SPACING("InterCharacterSpacing"), + BOLD("Bold"), + ITALIC("Italic"), + UNDERLINE("Underline"), + STRIKETHROUGH("Strikethrough"), + KERNING("Kerning"), + LINE_WIDTH("LineWidth"), + MITER_LIMIT("MiterLimit"), + CAP_TYPE("CapType"), + JOIN_TYPE("JoinType"), + FILL_STYLE("FillStyle"), + OUTLINE_STYLE("OutlineStyle"), + BORDER_STYLE("BorderStyle"), + CONNECT_BORDERS("ConnectBorders"), + WITH_LINE_GAP("WithLineGap"), + LANGUAGE("Language"), + SMALL_CAP("SmallCap"), + SUPER_SUB_SCRIPT("SuperSubScript"), + SUPER_SUB_SCRIPT_PROP("SuperSubScriptProp"), + CUSTOM_UNDER_STRIKE("CustomUnderStrike"), + URL_LINK("URLLink"), + URL_BASE_TEXT_STYLE("URLBaseTextStyle"), + URL_POST_CLICK_FILL_STYLE("URLPostClickFillStyle"), + HORIZONTAL_SCALE("HorizontalScale"), + WRAPPING_RULE("WrappingRule"), + SUB_FONT("SubFont"), + STRIKETHROUGH_LINE_STYLE("StrikethroughLineStyle"), + UNDERLINE_LINE_STYLE("UnderlineLineStyle"), + CSS_FILE("CssFile"), + URL_ALTERNATE_TEXT("URLAlternateText"); + + private final String xmlElementName; + + TextStyleInheritFlag(String xmlElementName) { + this.xmlElementName = xmlElementName; + } + + public String getXmlElementName() { + return xmlElementName; + } +} diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyleType.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyleType.java new file mode 100644 index 00000000..5f4a1532 --- /dev/null +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/TextStyleType.java @@ -0,0 +1,16 @@ +package com.quadient.wfdxml.api.layoutnodes; + +public enum TextStyleType { + SIMPLE("Simple"), + DELTA("Delta"); + + private final String xmlValue; + + TextStyleType(String xmlValue) { + this.xmlValue = xmlValue; + } + + public String getXmlValue() { + return xmlValue; + } +} diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/tables/Table.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/tables/Table.java index 51f47ccd..e4d05375 100644 --- a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/tables/Table.java +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/layoutnodes/tables/Table.java @@ -53,6 +53,15 @@ public interface Table extends Node { Table setEditability(EditabilityType editability); + Table setTablePdfTaggingRule(TablePdfTaggingRule rule); + + Table setTablePdfAlternateText(String alternateText); + + enum TablePdfTaggingRule { + NONE, + DEFAULT, + TABLE + } enum EditabilityType { LABEL_AND_LOCK, diff --git a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/module/Layout.java b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/module/Layout.java index 98bd57e0..bb121199 100644 --- a/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/module/Layout.java +++ b/wfd-xml/api/src/main/java/com/quadient/wfdxml/api/module/Layout.java @@ -64,4 +64,6 @@ public interface Layout extends WorkFlowModule { Element addElement(); Root addRoot(); + + Root getRoot(); } \ No newline at end of file diff --git a/wfd-xml/gradle/libs.versions.toml b/wfd-xml/gradle/libs.versions.toml index 11ccec1c..3659a642 100644 --- a/wfd-xml/gradle/libs.versions.toml +++ b/wfd-xml/gradle/libs.versions.toml @@ -6,6 +6,7 @@ xmlunit = "2.10.0" cglib = "3.3.0" mockito = "5.13.0" logback = "1.5.18" +jackson = "2.20.1" [libraries] groovy-xml = { group = "org.apache.groovy", name = "groovy-xml", version.ref = "groovyXml" } @@ -14,4 +15,5 @@ testcointainer-spock = { group = "org.testcontainers", name = "spock", version.r xmlunit-core = { group = "org.xmlunit", name = "xmlunit-core", version.ref = "xmlunit" } cglib = { group = "cglib", name = "cglib", version.ref = "cglib" } mockito-core = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" } -logback = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" } \ No newline at end of file +logback = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" } +jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" } \ No newline at end of file diff --git a/wfd-xml/impl/build.gradle.kts b/wfd-xml/impl/build.gradle.kts index 8beb9326..1f39e7e8 100644 --- a/wfd-xml/impl/build.gradle.kts +++ b/wfd-xml/impl/build.gradle.kts @@ -10,6 +10,7 @@ plugins { dependencies { implementation(project(":${rootProject.name}-api")) implementation(libs.logback) + implementation(libs.jackson.databind) testImplementation(libs.testcointainer.spock) testImplementation(libs.xmlunit.core) testImplementation(libs.cglib) diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/WfdXmlBuilder.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/WfdXmlBuilder.java index e12ee52d..a2da9cce 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/WfdXmlBuilder.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/WfdXmlBuilder.java @@ -47,20 +47,14 @@ private void setPosition(WorkFlowModule module) { } public String build() { - return this.build(false); - } - - public String build(boolean withDeltaStyles) { XmlExporter exporter = new XmlExporter(); exporter.declaration("1.0", "UTF-8"); exporter.beginElement("WorkFlow"); - if (withDeltaStyles) { - exporter.beginElement("Property"); - exporter.addElementWithStringData("Name", "DeltaStyles"); - exporter.addElementWithIntData("Value", 1); - exporter.endElement(); - } + exporter.beginElement("Property"); + exporter.addElementWithStringData("Name", "DeltaStyles"); + exporter.addElementWithIntData("Value", 1); + exporter.endElement(); for (WorkFlowModuleImpl module : modules) { module.export(exporter); diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/NodeImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/NodeImpl.java index e9ed7b8e..62433906 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/NodeImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/NodeImpl.java @@ -3,12 +3,16 @@ import com.quadient.wfdxml.api.Node; import com.quadient.wfdxml.internal.xml.export.XmlExportable; +import java.util.HashMap; +import java.util.Map; + public abstract class NodeImpl> implements Node, XmlExportable { private String name; private String comment; private String id; private String displayName; + private final Map customProperties = new HashMap<>(); @Override public String getName() { @@ -58,6 +62,18 @@ public S setDisplayName(String displayName) { return (S) this; } + @Override + public Map getCustomProperties() { + return customProperties; + } + + @Override + @SuppressWarnings("unchecked") + public S addCustomProperty(String key, Object value) { + customProperties.put(key, value); + return (S) this; + } + public String getXmlElementName() { throw new UnsupportedOperationException("Not supported operation on class '" + this.getClass().getName() + "'"); } diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/ImageImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/ImageImpl.java index 39413d39..4e2fa274 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/ImageImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/ImageImpl.java @@ -2,6 +2,7 @@ import com.quadient.wfdxml.api.layoutnodes.Image; import com.quadient.wfdxml.api.layoutnodes.LocationType; +import com.quadient.wfdxml.api.layoutnodes.data.Variable; import com.quadient.wfdxml.internal.NodeImpl; import com.quadient.wfdxml.internal.xml.export.XmlExporter; @@ -28,6 +29,9 @@ public class ImageImpl extends NodeImpl implements Image { private String htmlImageWidthValue; private String htmlImageHeightValue; + private String alternateText; + private Variable alternateTextVariable; + public String getImageType() { return imageType; } @@ -185,6 +189,25 @@ public ImageImpl setHtmlImageHeightValue(String htmlImageHeightValue) { return this; } + public String getAlternateText() { + return alternateText; + } + + public ImageImpl setAlternateText(String alternateText) { + this.alternateText = alternateText; + return this; + } + + public Variable getAlternateTextVariable() { + return alternateTextVariable; + } + + @Override + public ImageImpl setAlternateTextVariable(Variable variable) { + this.alternateTextVariable = variable; + return this; + } + @Override public ImageImpl setImageDiskLocation(String imageLocation) { this.imageLocation = LocationType.DISK.getXmlValue() + "," + imageLocation; @@ -287,5 +310,28 @@ public void export(XmlExporter exporter) { .addElementWithBoolData("UseDifferentImageSizeForHtml", useDifferentImageSizeForHtml) .addElementWithStringData("HtmlImageWidthValue", htmlImageWidthValue) .addElementWithStringData("HtmlImageHeightValue", htmlImageHeightValue); + + // Export alternate text - variable takes priority over string + boolean hasVariable = alternateTextVariable != null; + boolean hasString = alternateText != null && !alternateText.isEmpty(); + + exporter.addElementWithIface("AlternativeTextVar", alternateTextVariable); + exporter.beginElement("PDFAdvanced"); + exporter.beginElement("Tagging"); + exporter.addElementWithStringData("Rule", "Figure"); + if (hasVariable) { + exporter.addElementWithStringData("AlternateText", ""); + } else if (hasString) { + exporter.addElementWithStringData("AlternateText", alternateText); + } else { + exporter.addElementWithStringData("AlternateText", ""); + } + exporter.beginElement("Attributes"); + exporter.addStringAttribute("Type", "Array"); + exporter.endElement(); + exporter.addElementWithIface("AlternateTextNodeId", alternateTextVariable); + exporter.addElementWithIntData("AlternateTextType", hasVariable ? 2 : 1); + exporter.endElement(); + exporter.endElement(); } } \ No newline at end of file diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/ParagraphStyleImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/ParagraphStyleImpl.java index 09f9575a..37b457dd 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/ParagraphStyleImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/ParagraphStyleImpl.java @@ -37,6 +37,8 @@ public class ParagraphStyleImpl extends NodeImpl implements Para private boolean hyphenate = false; private int numberingFrom = 1; + + private ParagraphPdfTaggingRule pdfTaggingRule = null; private double leftIndent = 0; private double rightIndent = 0; @@ -210,6 +212,15 @@ public void export(XmlExporter exporter) { .addElementWithDoubleData("LineSpacing", lineSpacingValue) .addElementWithStringData("LineSpacingType", convertLineSpacingTypeToXmlName(lineSpacingType)) .addElementWithStringData("Type", type); + + if (pdfTaggingRule != null) { + exporter.beginElement("PDFAdvanced"); + exporter.addElementWithBoolData("LinkedToParent", false); + exporter.beginElement("Tagging"); + exporter.addElementWithStringData("Rule", convertParagraphPdfTaggingRuleToXmlName(pdfTaggingRule)); + exporter.endElement(); + exporter.endElement(); + } } public String getAncestorId() { @@ -424,4 +435,28 @@ public ParagraphStyleImpl setConnectBorders(boolean connectBorders) { public boolean isWithLineGab() { return withLineGab; } + + public ParagraphPdfTaggingRule getPdfTaggingRule() { + return pdfTaggingRule; + } + + @Override + public ParagraphStyleImpl setPdfTaggingRule(ParagraphPdfTaggingRule rule) { + this.pdfTaggingRule = rule; + return this; + } + + public static String convertParagraphPdfTaggingRuleToXmlName(ParagraphPdfTaggingRule rule) { + if (rule == null) return null; + return switch (rule) { + case PARAGRAPH -> "P"; + case HEADING -> "H"; + case HEADING_1 -> "H1"; + case HEADING_2 -> "H2"; + case HEADING_3 -> "H3"; + case HEADING_4 -> "H4"; + case HEADING_5 -> "H5"; + case HEADING_6 -> "H6"; + }; + } } \ No newline at end of file diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/RootImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/RootImpl.java index 6b9197ea..57bb7ac9 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/RootImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/RootImpl.java @@ -1,14 +1,19 @@ package com.quadient.wfdxml.internal.layoutnodes; +import com.quadient.wfdxml.api.Node; import com.quadient.wfdxml.api.layoutnodes.Root; import com.quadient.wfdxml.internal.xml.export.XmlExportable; import com.quadient.wfdxml.internal.xml.export.XmlExporter; +import java.util.ArrayList; +import java.util.List; + public class RootImpl implements Root, XmlExportable { private boolean allowRuntimeModifications = false; private String externalStylesLayoutVcsLocation = null; private String subject = null; + private final List> lockedWebNodes = new ArrayList<>(); @Override public Root setAllowRuntimeModifications(boolean allowRuntimeModifications) { @@ -25,7 +30,18 @@ public Root setExternalStylesLayout(String vcsLocation) { @Override public Root setSubject(String subject) { this.subject = subject; - return null; + return this; + } + + @Override + public Root addLockedWebNode(Node node) { + lockedWebNodes.add(node); + return this; + } + + @Override + public List> getLockedWebNodes() { + return lockedWebNodes; } @Override @@ -39,5 +55,12 @@ public void export(XmlExporter exporter) { if (subject != null) { exporter.addElementWithStringData("Subject", subject); } + if (!lockedWebNodes.isEmpty()) { + exporter.beginElement("LockedWebNodes"); + for (Node node : lockedWebNodes) { + exporter.addElementWithIface("LockedWebNode", node); + } + exporter.endElement(); + } } } \ No newline at end of file diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/TextStyleImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/TextStyleImpl.java index 5dde74d3..bd19d9f9 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/TextStyleImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/TextStyleImpl.java @@ -4,20 +4,25 @@ import com.quadient.wfdxml.api.layoutnodes.Font; import com.quadient.wfdxml.api.layoutnodes.LineStyle; import com.quadient.wfdxml.api.layoutnodes.TextStyle; +import com.quadient.wfdxml.api.layoutnodes.TextStyleInheritFlag; +import com.quadient.wfdxml.api.layoutnodes.TextStyleType; import com.quadient.wfdxml.api.layoutnodes.data.Variable; import com.quadient.wfdxml.api.layoutnodes.font.SubFont; import com.quadient.wfdxml.api.layoutnodes.tables.BorderStyle; import com.quadient.wfdxml.internal.NodeImpl; -import com.quadient.wfdxml.internal.layoutnodes.font.SubFontImpl; import com.quadient.wfdxml.internal.xml.export.XmlExporter; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + public class TextStyleImpl extends NodeImpl implements TextStyle { /** * Inspire Designer #define DPI_TS (72.0*100.0/2.54) */ private static final double DPI_TS = 72.0d * 100.0 / 2.54; - private final String type = "Simple"; + private TextStyleType type = TextStyleType.SIMPLE; private final boolean isVisible = true; private FillStyle fillStyle; private Font font; @@ -46,6 +51,8 @@ public class TextStyleImpl extends NodeImpl implements TextStyle { private LineStyle underlineStyleId; private LineStyle strikeThroughStyleId; + private String urlAlternateText; + private final Set inheritFlags = new LinkedHashSet<>(); public TextStyleImpl() { } @@ -122,12 +129,21 @@ public void export(XmlExporter exporter) { .addElementWithBoolData("SuperScript", isSuperScript) .addElementWithBoolData("SubScript", isSubScript) .addElementWithIface("URLLink", urlLink) + .addElementWithStringData("URLAlternateText", urlAlternateText) .addElementWithDoubleData("HorizontalScale", horizontalScale * 100.0d) - .addElementWithStringData("Type", type) + .addElementWithStringData("Type", type.getXmlValue()) .addElementWithIface("StrikethroughLineStyleId", strikeThroughStyleId) .addElementWithIface("UnderlineLineStyleId", underlineStyleId) .addElementWithBoolData("IsFixedWidth", isFixedWidth) .addElementWithDoubleData("FixedWidth", fixedWidth); + + if (type == TextStyleType.DELTA) { + exporter.beginElement("InheritFlag"); + for (TextStyleInheritFlag flag : inheritFlags) { + exporter.addElement(flag.getXmlElementName()); + } + exporter.endElement(); + } } public FillStyle getFillStyle() { @@ -164,10 +180,16 @@ public void setAncestorId(String ancestorId) { this.ancestorId = ancestorId; } - public String getType() { + public TextStyleType getType() { return type; } + @Override + public TextStyleImpl setType(TextStyleType type) { + this.type = type; + return this; + } + public String getLanguage() { return language; } @@ -305,6 +327,16 @@ public TextStyleImpl setUrlLink(Variable urlLink) { return this; } + public String getUrlAlternateText() { + return urlAlternateText; + } + + @Override + public TextStyleImpl setUrlAlternateText(String urlAlternateText) { + this.urlAlternateText = urlAlternateText; + return this; + } + public double getBaselineShift() { return baselineShift; } @@ -366,4 +398,16 @@ public LineStyle getUnderlineStyleId() { public LineStyle getStrikeThroughStyleId() { return strikeThroughStyleId; } + + @Override + public TextStyleImpl addInheritFlag(TextStyleInheritFlag flag) { + this.inheritFlags.add(flag); + return this; + } + + @Override + public TextStyleImpl addInheritFlags(TextStyleInheritFlag... flags) { + Collections.addAll(this.inheritFlags, flags); + return this; + } } diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/tables/TableImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/tables/TableImpl.java index 527ec0b8..6efa9f92 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/tables/TableImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/layoutnodes/tables/TableImpl.java @@ -36,6 +36,8 @@ public class TableImpl extends NodeImpl
implements Table { private boolean displayAsImage = true; private boolean htmlFormatting = false; private boolean responsiveHtml = false; + private TablePdfTaggingRule tablePdfTaggingRule = TablePdfTaggingRule.DEFAULT; + private String tablePdfAlternateText = null; public RowSet getRowSet() { return rowSet; @@ -289,6 +291,26 @@ public TableImpl setResponsiveHtml(boolean responsiveHtml) { return this; } + public TablePdfTaggingRule getTablePdfTaggingRule() { + return tablePdfTaggingRule; + } + + @Override + public TableImpl setTablePdfTaggingRule(TablePdfTaggingRule rule) { + this.tablePdfTaggingRule = rule; + return this; + } + + public String getTablePdfAlternateText() { + return tablePdfAlternateText; + } + + @Override + public TableImpl setTablePdfAlternateText(String alternateText) { + this.tablePdfAlternateText = alternateText; + return this; + } + @Override public String getXmlElementName() { @@ -352,6 +374,56 @@ public void export(XmlExporter exporter) { } exporter.addElementWithBoolData("ResponsiveHtml", responsiveHtml); + switch (tablePdfTaggingRule) { + case NONE: + exporter.beginElement("PDFAdvanced"); + exporter.beginElement("Tagging"); + exporter.addElementWithStringData("Rule", "None"); + exporter.addElementWithStringData("AlternateText", ""); + exporter.beginElement("Attributes"); + exporter.addStringAttribute("Type", "Array"); + exporter.endElement(); + exporter.addElementWithStringData("AlternateTextNodeId", ""); + exporter.addElementWithIntData("AlternateTextType", 1); + exporter.endElement(); + exporter.endElement(); + break; + case DEFAULT: + if (tablePdfAlternateText != null && !tablePdfAlternateText.isEmpty()) { + exporter.beginElement("PDFAdvanced"); + exporter.beginElement("Tagging"); + exporter.addElementWithStringData("Rule", "Default"); + exporter.addElementWithStringData("AlternateText", tablePdfAlternateText); + exporter.beginElement("Attributes"); + exporter.addStringAttribute("Type", "Array"); + exporter.endElement(); + exporter.addElementWithStringData("AlternateTextNodeId", ""); + exporter.addElementWithIntData("AlternateTextType", 1); + exporter.endElement(); + exporter.endElement(); + } + break; + case TABLE: + exporter.beginElement("PDFAdvanced"); + exporter.beginElement("Tagging"); + exporter.addElementWithStringData("Rule", "Table"); + exporter.beginElement("AlternateText"); + if (tablePdfAlternateText != null && !tablePdfAlternateText.isEmpty()) { + exporter.addPCData(tablePdfAlternateText); + } + exporter.endElement(); + exporter.beginElement("Attributes"); + exporter.addStringAttribute("Type", "Array"); + exporter.endElement(); + exporter.addElementWithStringData("AlternateTextNodeId", ""); + exporter.addElementWithIntData("AlternateTextType", 1); + exporter.endElement(); + exporter.endElement(); + break; + default: + throw new IllegalStateException(tablePdfTaggingRule.toString()); + } + { switch (editabilityType) { case LABEL_AND_LOCK: diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/ForwardReferencesExporter.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/ForwardReferencesExporter.java index 814781e9..36778062 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/ForwardReferencesExporter.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/ForwardReferencesExporter.java @@ -1,5 +1,6 @@ package com.quadient.wfdxml.internal.module.layout; +import com.fasterxml.jackson.databind.ObjectMapper; import com.quadient.wfdxml.api.layoutnodes.data.Variable; import com.quadient.wfdxml.internal.DefaultNodeType; import com.quadient.wfdxml.internal.NodeImpl; @@ -7,7 +8,6 @@ import com.quadient.wfdxml.internal.xml.export.XmlExporter; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -18,7 +18,7 @@ public class ForwardReferencesExporter { private final XmlExporter exporter; private Set rootDefNodes; - + private final ObjectMapper objectMapper = new ObjectMapper(); public ForwardReferencesExporter(LayoutImpl layout, Map defNodes, XmlExporter exporter) { this.layout = layout; @@ -64,7 +64,17 @@ private void writeForwardReferenceToExporter(NodeImpl node, Tree parent, Boolean } if (node.getDisplayName() != null && !node.getDisplayName().isBlank()) { - exporter.addElementWithStringData("CustomProperty", "{\"DisplayName\":\"" + node.getDisplayName() + "\"}"); + node.addCustomProperty("DisplayName", node.getDisplayName()); + } + + var customProperties = node.getCustomProperties(); + if (customProperties != null && !customProperties.isEmpty()) { + try { + String customPropsJson = objectMapper.writeValueAsString(customProperties); + exporter.addElementWithStringData("CustomProperty", customPropsJson); + } catch (Exception e) { + throw new RuntimeException("Failed to serialize customProperties to JSON", e); + } } var forwardElement = exporter.beginElement("Forward"); diff --git a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/LayoutImpl.java b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/LayoutImpl.java index 75f0dc11..071fc2ce 100644 --- a/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/LayoutImpl.java +++ b/wfd-xml/impl/src/main/java/com/quadient/wfdxml/internal/module/layout/LayoutImpl.java @@ -71,8 +71,8 @@ public class LayoutImpl extends WorkFlowModuleImpl implements Layout { private DataImpl data; private PagesImpl pages; - private final List layoutDeltaAllowedGroups = List.of("Flows", "Tables", "RowSets", "Cells", "Data", "Images"); - private final List styleLayoutDeltaAllowedGroups = List.of("TextStyles", "FillStyles", "ParagraphStyles", "Colors"); + private final List layoutDeltaAllowedGroups = List.of("Flows", "Tables", "RowSets", "Cells", "Data", "Images", "TextStyles"); + private final List styleLayoutDeltaAllowedGroups = List.of("TextStyles", "FillStyles", "ParagraphStyles", "Colors", "Fonts"); public LayoutImpl() { initializeDefaultNodes(); @@ -221,6 +221,11 @@ public Root addRoot() { return root; } + @Override + public Root getRoot() { + return root; + } + @Override public ParagraphStyleImpl addBulletParagraph(TextStyle textStyle, String bullet) { return addParagraphStyle().setBulletsNumberingFlow(addBulletFlow(textStyle, bullet)); @@ -355,15 +360,21 @@ public void exportLayoutDelta(XmlExporter exporter) { dataNode.children = dataNode.children.stream().filter(dataChild -> dataChild.getName() == null || !dataChild.getName().equals("SystemVariable")).toList(); return dataNode; } + if (child.getName().equals("TextStyles") && child instanceof Tree textStylesNode) { + textStylesNode.children = textStylesNode.children.stream().filter(textStyleChild -> !textStyleChild.getName().equals("Normal")).toList(); + return textStylesNode; + } return child; }).collect(Collectors.toList()); + + new ForwardReferencesExporter(this, defNodes, exporter).exportForwardReferences(true); + if (root != null) { exporter.beginElement("Root"); root.export(exporter); exporter.endElement(); } - new ForwardReferencesExporter(this, defNodes, exporter).exportForwardReferences(true); exportNodes(exporter); exporter.endElement(); diff --git a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/WfdXmlBuilderTest.groovy b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/WfdXmlBuilderTest.groovy index df61d6ed..326e17a3 100644 --- a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/WfdXmlBuilderTest.groovy +++ b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/WfdXmlBuilderTest.groovy @@ -16,7 +16,7 @@ class WfdXmlBuilderTest extends Specification { String result = emptyBuilder.build() then: - String expected = '' + String expected = 'DeltaStyles1' assert result == expected } diff --git a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/ImageImplTest.groovy b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/ImageImplTest.groovy index 616fc91a..c98cfab8 100644 --- a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/ImageImplTest.groovy +++ b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/ImageImplTest.groovy @@ -2,6 +2,8 @@ package com.quadient.wfdxml.internal.layoutnodes import com.quadient.wfdxml.api.layoutnodes.Image import com.quadient.wfdxml.api.layoutnodes.LocationType +import com.quadient.wfdxml.api.layoutnodes.data.Variable +import com.quadient.wfdxml.internal.layoutnodes.data.VariableImpl import com.quadient.wfdxml.internal.xml.export.XmlExporter import spock.lang.Specification @@ -31,6 +33,16 @@ class ImageImplTest extends Specification { False + + + + Figure + + + + 1 + + """) } @@ -64,7 +76,17 @@ class ImageImplTest extends Specification { True 500px - 600px + 600px + + + + Figure + + + + 1 + + """) } @@ -89,7 +111,125 @@ class ImageImplTest extends Specification { - False + False + + + + Figure + + + + 1 + + + """) + } + + def "export image with string alternate text"() { + given: + ImageImpl image = new ImageImpl().setAlternateText("Some Alternate text example") + + when: + image.export(exporter) + + then: + assertXmlEqualsWrapRoot(exporter.buildString(), """ + Simple + 0.0 + 0.0 + False + 0.0 + False + 0.0 + False + + + + False + + + + Figure + Some Alternate text example + + + 1 + + + """) + } + + def "export image with variable alternate text"() { + given: + Variable variable = new VariableImpl() + String variableId = exporter.idRegister.getOrCreateId(variable) + ImageImpl image = new ImageImpl().setAlternateTextVariable(variable) + + when: + image.export(exporter) + + then: + assertXmlEqualsWrapRoot(exporter.buildString(), """ + Simple + 0.0 + 0.0 + False + 0.0 + False + 0.0 + False + + + + False + $variableId + + + Figure + + + $variableId + 2 + + + """) + } + + def "export image with both string and variable alternate text - variable takes priority"() { + given: + Variable variable = new VariableImpl() + String variableId = exporter.idRegister.getOrCreateId(variable) + ImageImpl image = new ImageImpl() + .setAlternateText("Some Alternate text example") + .setAlternateTextVariable(variable) + + when: + image.export(exporter) + + then: + assertXmlEqualsWrapRoot(exporter.buildString(), """ + Simple + 0.0 + 0.0 + False + 0.0 + False + 0.0 + False + + + + False + $variableId + + + Figure + + + $variableId + 2 + + """) } } \ No newline at end of file diff --git a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/ParagraphStyleImplTest.groovy b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/ParagraphStyleImplTest.groovy index 5100eea0..a8bf6f3b 100644 --- a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/ParagraphStyleImplTest.groovy +++ b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/ParagraphStyleImplTest.groovy @@ -9,6 +9,7 @@ import spock.lang.Specification import static com.quadient.wfdxml.api.layoutnodes.ParagraphStyle.AlignType import static com.quadient.wfdxml.api.layoutnodes.ParagraphStyle.LineSpacingType import static com.quadient.wfdxml.api.layoutnodes.ParagraphStyle.NumberingType +import static com.quadient.wfdxml.api.layoutnodes.ParagraphStyle.ParagraphPdfTaggingRule import static com.quadient.wfdxml.api.layoutnodes.TabulatorType.LEFT import static com.quadient.wfdxml.utils.AssertXml.assertXmlEqualsWrapRoot @@ -290,4 +291,63 @@ class ParagraphStyleImplTest extends Specification { result.contains("0.005") result.contains("0.007") } + + def "paraStyle with PDF tagging rule PARAGRAPH"() { + given: + ParagraphStyleImpl paraStyle = new ParagraphStyleImpl() + .setPdfTaggingRule(ParagraphPdfTaggingRule.PARAGRAPH) + + when: + paraStyle.export(exporter) + + then: + assertXmlEqualsWrapRoot(exporter.buildString(), """ + Def.ParaStyle + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + Left + + True + False + False + + False + + 0.0125 + False + + + False + + Increment + + False + False + False + 0.0 + Additional + Simple + + False + + P + + """) + } + + def "paraStyle without PDF tagging rule"() { + given: + ParagraphStyleImpl paraStyle = new ParagraphStyleImpl() + + when: + paraStyle.export(exporter) + + then: + String result = exporter.buildString() + !result.contains("") + !result.contains("") + } } \ No newline at end of file diff --git a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/TextStyleImplTest.groovy b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/TextStyleImplTest.groovy index 52c05156..65f124a5 100644 --- a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/TextStyleImplTest.groovy +++ b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/TextStyleImplTest.groovy @@ -2,6 +2,8 @@ package com.quadient.wfdxml.internal.layoutnodes import com.quadient.wfdxml.api.layoutnodes.LineStyle import com.quadient.wfdxml.api.layoutnodes.TextStyle +import com.quadient.wfdxml.api.layoutnodes.TextStyleInheritFlag +import com.quadient.wfdxml.api.layoutnodes.TextStyleType import com.quadient.wfdxml.api.layoutnodes.data.Variable import com.quadient.wfdxml.internal.layoutnodes.data.VariableImpl import com.quadient.wfdxml.internal.xml.export.XmlExporter @@ -165,4 +167,39 @@ class TextStyleImplTest extends Specification { then: assert textStyle.getLanguage() == "en" } + + def "textStyle with URL target and alternate text"() { + given: + Variable urlVariable = new VariableImpl() + TextStyle textStyle = new TextStyleImpl() + .setUrlTarget(urlVariable) + .setUrlAlternateText("Link to external resource") + + when: + (textStyle as TextStyleImpl).export(exporter) + + then: + String result = exporter.buildString() + result.contains("SR_1") + result.contains("Link to external resource") + } + + def "textStyle with type Delta and inherit flags"() { + given: + TextStyle textStyle = new TextStyleImpl() + .setType(TextStyleType.DELTA) + .addInheritFlags(TextStyleInheritFlag.FONT, + TextStyleInheritFlag.FONT_SIZE, + TextStyleInheritFlag.BOLD, + TextStyleInheritFlag.ITALIC, + TextStyleInheritFlag.UNDERLINE) + + when: + (textStyle as TextStyleImpl).export(exporter) + + then: + String result = exporter.buildString() + result.contains("Delta") + result.contains("") + } } diff --git a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/tables/TableImplTest.groovy b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/tables/TableImplTest.groovy index 7dd6bc79..1adb8da5 100644 --- a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/tables/TableImplTest.groovy +++ b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/layoutnodes/tables/TableImplTest.groovy @@ -8,6 +8,7 @@ import spock.lang.Specification import static com.quadient.wfdxml.api.layoutnodes.tables.RowSet.Type.MULTIPLE_ROWS import static com.quadient.wfdxml.api.layoutnodes.tables.Table.BordersType.SIMPLE import static com.quadient.wfdxml.api.layoutnodes.tables.Table.EditabilityType.LOCK +import static com.quadient.wfdxml.api.layoutnodes.tables.Table.TablePdfTaggingRule.* import static com.quadient.wfdxml.api.layoutnodes.tables.Table.TableAlignment.CENTER import static com.quadient.wfdxml.utils.AssertXml.assertXmlEqualsWrapRoot @@ -170,4 +171,56 @@ class TableImplTest extends Specification { result.contains("0.0") result.contains("-1.0") } + + def "export table pdf tagging none rule emits block"() { + given: + Table table = new TableImpl().setTablePdfTaggingRule(NONE).setTablePdfAlternateText("ignored text") + + when: + table.export(exporter) + + then: + String xml = exporter.buildString() + xml.contains("") + xml.contains("") + xml.contains("None") + xml.contains("") + } + + def "export table pdf tagging default without alt text emits nothing"() { + given: + Table table = new TableImpl().setTablePdfTaggingRule(DEFAULT) + + when: + table.export(exporter) + + then: + !exporter.buildString().contains("PDFAdvanced") + } + + def "export table pdf tagging default with alt text emits block with text"() { + given: + Table table = new TableImpl().setTablePdfTaggingRule(DEFAULT).setTablePdfAlternateText("Default with alt text") + + when: + table.export(exporter) + + then: + String xml = exporter.buildString() + xml.contains("Default") + xml.contains("Default with alt text") + } + + def "export table pdf tagging table rule emits block"() { + given: + Table table = new TableImpl().setTablePdfTaggingRule(TABLE).setTablePdfAlternateText("Table alt text") + + when: + table.export(exporter) + + then: + String xml = exporter.buildString() + xml.contains("Table") + xml.contains("Table alt text") + } } \ No newline at end of file diff --git a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/module/LayoutImplTest.groovy b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/module/LayoutImplTest.groovy index 9bb3d65e..a8fa4fda 100644 --- a/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/module/LayoutImplTest.groovy +++ b/wfd-xml/impl/src/test/groovy/com/quadient/wfdxml/internal/module/LayoutImplTest.groovy @@ -184,4 +184,17 @@ class LayoutImplTest extends Specification { then: assert result.contains("SR_1Def.FlowGroup{"DisplayName":"Custom flow name"}") } + + def "node with display name and additional custom property"() { + given: + Layout layout = new LayoutImpl() + layout.addFlow().setType(Flow.Type.SIMPLE).setDisplayName("Flow with multiple properties").addCustomProperty("ValueWrapperVariable", true).addCustomProperty("Version", 2) + + when: + layout.exportLayoutDelta(exporter) + String result = exporter.buildString() + + then: + assert result.contains("SR_1Def.FlowGroup{"ValueWrapperVariable":true,"Version":2,"DisplayName":"Flow with multiple properties"}SR_1SimpleFalseDef.Data") + } } \ No newline at end of file diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/AllInOneUsageExample.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/AllInOneUsageExample.xml index b6df9d59..fe10fd94 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/AllInOneUsageExample.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/AllInOneUsageExample.xml @@ -1,5 +1,9 @@ + + DeltaStyles + 1 + SR_1 CreatedByScript @@ -2985,6 +2989,16 @@ False + + + + Figure + + + + 1 + + Def.BorderStyle diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ConditionUsageExample.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ConditionUsageExample.xml index c700bcd3..12b35361 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ConditionUsageExample.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ConditionUsageExample.xml @@ -1,5 +1,9 @@ + + DeltaStyles + 1 + SR_1 MyLayout diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ImageAndLineUsageExample.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ImageAndLineUsageExample.xml index f7984c5f..bdb7b8f1 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ImageAndLineUsageExample.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ImageAndLineUsageExample.xml @@ -1,5 +1,9 @@ + + DeltaStyles + 1 + SR_1 Layout @@ -463,6 +467,16 @@ False + + + + Figure + + + + 1 + + Def.BorderStyle diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ListsUsageExample.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ListsUsageExample.xml index 008cb00f..9aee67f0 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ListsUsageExample.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ListsUsageExample.xml @@ -1,5 +1,9 @@ + + DeltaStyles + 1 + SR_1 Layout diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/OverflowUsageExample.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/OverflowUsageExample.xml index d9790b91..02aae27a 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/OverflowUsageExample.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/OverflowUsageExample.xml @@ -1,5 +1,9 @@ + + DeltaStyles + 1 + SR_1 MyXmlDataInput diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ParagraphUsageExample.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ParagraphUsageExample.xml index 81e832f2..255b244b 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ParagraphUsageExample.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/ParagraphUsageExample.xml @@ -1,5 +1,9 @@ + + DeltaStyles + 1 + SR_1 diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/SimpleDeltaLayoutWithImage.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/SimpleDeltaLayoutWithImage.xml index 2825659a..29f64556 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/SimpleDeltaLayoutWithImage.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/SimpleDeltaLayoutWithImage.xml @@ -35,6 +35,16 @@ False + + + + Figure + + + + 1 + + Def.Data diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/SimpleLayout.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/SimpleLayout.xml index 6d9fb686..688fbcc3 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/SimpleLayout.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/SimpleLayout.xml @@ -1,5 +1,9 @@ + + DeltaStyles + 1 + SR_1 diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/TableUsageExample.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/TableUsageExample.xml index ae763a04..ed19967d 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/TableUsageExample.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/TableUsageExample.xml @@ -1,5 +1,9 @@ + + DeltaStyles + 1 + SR_1 Layout diff --git a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/VariableUsageExample.xml b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/VariableUsageExample.xml index 23f10ba0..c2fcc027 100644 --- a/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/VariableUsageExample.xml +++ b/wfd-xml/impl/src/test/resources/com/quadient/wfdxml/workflow/VariableUsageExample.xml @@ -1,5 +1,9 @@ + + DeltaStyles + 1 + SR_1 CreatedByScript