#1470·pdfcpu

`FreeText` `/CL`: relaxed validation lowers the bar to V1.4, but real-world files declare `%PDF-1.3`

Author: motchieCreated Aug 27, 2026Updated Aug 29, 2026

Problem

In ValidationRelaxed, validateAnnotationDictFreeTextPart1 lowers the version requirement for /CL from V1.6 to V1.4 (pkg/pdfcpu/validate/annotation.go, v0.15.0):

go
// CL, optional, number array, since V1.6, len: 4 or 6
validateCL := func(a types.Array) bool { return len(a) == 4 || len(a) == 6 }
sinceVersion = model.V16
if xRefTable.ValidationMode == model.ValidationRelaxed {
    sinceVersion = model.V14
    validateCL = nil
}

Files that declare %PDF-1.3 in the header therefore still fail, even in relaxed mode. The immediately preceding entry in the same function relaxes to V1.3:

go
// DS, optional, text string, since V1.5
sinceVersion = model.V15
if xRefTable.ValidationMode == model.ValidationRelaxed {
    sinceVersion = model.V13
}

So the relaxed floors for two adjacent optional entries differ by one version, and %PDF-1.3 happens to fall in between.

Reproducer

491 bytes, ASCII only — header says %PDF-1.3, one FreeText annotation with /CL:

%PDF-1.3
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Annots [4 0 R] >>
endobj
4 0 obj
<< /Type /Annot /Subtype /FreeText /Rect [10 10 60 60] /F 4 /DA (0 g) /Contents (x) /CL [10 10 30 30 60 60] >>
endobj
xref
0 5
0000000000 65535 f 
0000000009 00000 n 
0000000058 00000 n 
0000000115 00000 n 
0000000202 00000 n 
trailer
<< /Size 5 /Root 1 0 R >>
startxref
328
%%EOF

v0.15.0, default configuration (ValidationRelaxed):

prepare PDF context: validation error (obj#:4): document catalog: page annotations:
page annotations: page 1: page annotation obj#4: validate:
dict=FreeText entry=CL: unsupported in version 1.3

How we ran into it

We are migrating a document pipeline from pdf-lib (JavaScript) to Go. We scanned our whole corpus — 6,254 distinct PDFs produced by many tools over many years — and pdfcpu rejected 5 files. 4 of them are this case: all produced by Adobe PDF Library 15.0, all declaring %PDF-1.3 while carrying a 1.6 annotation entry. The documents render fine everywhere else we tried.

Proposal

Smallest fix: lower the relaxed floor for /CL to model.V13, matching /DS just above it.

Broader (entirely your call): this is the seventh-or-so report of the shape dict=X entry=Y: unsupported in version Z (#93, #839, #903, #1058, #1100, #1230, #1417), each resolved by adjusting one entry. Understated header versions seem common enough that treating the check as advisory in ValidationRelaxed — rather than tuning per-entry floors — might retire the whole category. There is related discussion in #1223 about returning a typed error from XRefTable.ValidateVersion so callers can distinguish this from real structural damage; that would work for us too.

Happy to send a PR for whichever direction you prefer.