Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
efc347d
Add support for table pdf tags to wfd-xml
Svit93 Jan 13, 2026
86ae06b
promote the table pdf tagging to library and migration model example
Svit93 Jan 14, 2026
ee70eb8
changelog for table pdf tagging
Svit93 Jan 14, 2026
a4622a5
Merge branch 'main' into feature/MIG-386-Support-accessibility-tagging
Svit93 Jan 14, 2026
cf9316c
More robust font handling in styles build
Svit93 Jan 14, 2026
fe84a99
Support for custom properties in wfd-xml
Svit93 Jan 15, 2026
5f84772
Image wfd-xml extension for pdf tag (alternate text) support
Svit93 Jan 15, 2026
f33469b
image alternate text support in model and inspirebuilders
Svit93 Jan 15, 2026
d942c58
Making image alternate text work for Interactive
Svit93 Jan 16, 2026
c7ff585
Update image alternate text to be on object level, implement to mappi…
Svit93 Jan 16, 2026
b6ce520
basic implementation for hyperlink
Svit93 Jan 19, 2026
50423ff
Basic impl of wfd-xml hyperlink creation and its inspire builder coun…
Svit93 Jan 21, 2026
c03b0c1
refactor hyperlink placement and update changelog
Svit93 Jan 22, 2026
61eb711
TextStyleType.java
Svit93 Jan 22, 2026
1d1970a
Refactor hyperlink handling in InspireDocumentObjectBuilder.kt
Svit93 Jan 22, 2026
a307335
Merge branch 'main' into feature/MIG-386-Support-accessibility-tagging
Svit93 Jan 22, 2026
9961f87
Text style font always default to Arial
Svit93 Jan 22, 2026
09db6c0
After merge fixes
Svit93 Jan 22, 2026
37c7d73
Allow text styles in layout delta because of hyperlinks
Svit93 Jan 22, 2026
823cd4b
Add support for inheritFlags to more precisely create delta styles
Svit93 Jan 22, 2026
ce1a4c7
paragraph pdf tagging style support - basic implementation, tests and…
Svit93 Jan 22, 2026
3742412
Merge remote-tracking branch 'origin/feature/MIG-386-Support-accessib…
Svit93 Jan 23, 2026
efad559
fix wfd-xml tests
Svit93 Jan 23, 2026
75f3f16
update changelog
Svit93 Jan 23, 2026
c4e89d2
Unit tests for inspire builders using hyperlink and paragraph (style)…
Svit93 Jan 23, 2026
b3db770
fix examples unit tests
Svit93 Jan 23, 2026
6291238
add missing unit tests for wfdxml text and paragraph styles
Svit93 Jan 23, 2026
4568c05
fix return type in wfdxml root builder
Svit93 Jan 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ bin/
.DS_Store

### postgres dev container data ###
.pgdata/
.pgdata/

### AI
.claude/
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Expand Down Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ 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())

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"))
}
Expand Down
62 changes: 45 additions & 17 deletions migration-examples/src/test/groovy/ImagesMappingImportTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

Expand All @@ -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")
}

Expand All @@ -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")
}

Expand All @@ -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")
}

Expand All @@ -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,
Expand All @@ -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))
}
}
Loading