pdfa
TOC »
Description
Writes PDF/A-1b archival documents with the pdf egg.
PDF/A (ISO 19005) is the PDF format for documents that must be kept for a long time. A PDF/A file contains everything needed to draw it the same way on any viewer, now or decades from now: its fonts, a color profile that says what its colors mean, and metadata that describes it. pdfa supports conformance level B ("basic") of PDF/A-1, which guarantees that pages look the same everywhere. Level A, which also needs a tagged logical structure, is not supported.
pdfa adds three things to a pdf document:
- the metadata PDF/A requires: an /Info dictionary, a matching XMP metadata stream, a trailer /ID and a binary header comment;
- an output intent: an embedded ICC color profile that defines the document's colors;
- conformance checks, run before the file is written, which reject a document that breaks a PDF/A-1b rule and list every problem found.
Fonts must be embedded, using pdf-truetype; the standard fonts from pdf's build-font are rejected.
Requirements
pdf, pdf-truetype, srfi-1, srfi-13, message-digest, md5, sxml-transforms, datatype
Documentation
Overview
A PDF/A document is a record of type pdfa-document. It wraps an ordinary pdf document (a doc, at PDF version 1.4) together with its metadata and output intent. Pages, fonts and drawing use the wrapped doc and the usual pdf and pdf-truetype procedures; metadata and the output intent use the pdfa-document.
A typical document:
- creates a pdfa-document, usually with with-pdfa-document-to-file;
- records its metadata with set-document-info!;
- sets its output intent with set-output-intent!;
- embeds its fonts with build-embedded-font from pdf-truetype;
- draws its pages with with-page, drawing text with draw-encoded-text;
- is checked and written by write-pdfa-document, which with-pdfa-document-to-file calls for you.
Each document has a conformance mode. With 'pdfa-1b (the default), the finished document is checked before it is written; if it breaks a rule, an error lists every problem and no file is written, and if it passes, its metadata records the claim that it conforms to PDF/A-1b. With #f, the same metadata is written with no checks and no claim.
What a conforming document may contain
The checks accept a document that uses only:
- fonts embedded with build-embedded-font;
- DeviceRGB color (set-rgb-fill, set-rgb-stroke) with an RGB output intent, or DeviceCMYK color (set-cmyk-fill, set-cmyk-stroke) with a CMYK one; DeviceGray color (set-gray-fill, set-gray-stroke) with either;
- vector graphics, clipping and graphics states, without transparency: an ExtGState from build-ext-gstate must keep both alphas at 1.0 and the blend mode "/Normal";
- numbers, strings and q nesting within the PDF/A-1 limits (real numbers up to 32767 in size, strings up to 65535 bytes, at most 28 nested save-graphics-state calls).
Building and writing documents
build-pdfa-document
- build-pdfa-document #!optional COMPRESS-STREAMS? CONFORMANCEprocedure
Creates and returns a new, empty PDF/A document, wrapping a new PDF 1.4 pdf document. COMPRESS-STREAMS? (default #t) sets whether streams are compressed; the XMP metadata stream is always stored uncompressed, as PDF/A requires. CONFORMANCE is 'pdfa-1b (the default) to check the document and claim PDF/A-1b, or #f for metadata only; any other value is an error.
with-pdfa-document-to-file
- (with-pdfa-document-to-file (PDFA-DOC DOC [COMPRESS-STREAMS? [CONFORMANCE]]) FILENAME BODY ...)syntax
Creates a PDF/A document (as build-pdfa-document), binds PDFA-DOC to it and DOC to its wrapped pdf document, runs BODY, then writes the finished document to FILENAME with write-pdfa-document. Use PDFA-DOC for metadata and the output intent, and DOC for fonts and pages.
write-pdfa-document
- write-pdfa-document PDFA-DOC FILENAMEprocedure
Finishes PDFA-DOC with finalize-document-metadata! and writes it to FILENAME, with the binary header comment and the trailer /Info and /ID entries. In conformance mode, a document that fails the checks raises an error and no file is written. Call it once, after all pages have been added.
pdfa-document?
- pdfa-document? Xprocedure
Tests whether X is a PDF/A document.
pdfa-document-doc
- pdfa-document-doc PDFA-DOCprocedure
Returns the pdf document wrapped by PDFA-DOC.
pdfa-document-conformance
- pdfa-document-conformance PDFA-DOCprocedure
Returns PDFA-DOC's conformance mode: 'pdfa-1b or #f.
Metadata and output intent
set-document-info!
- set-document-info! PDFA-DOC #!key TITLE AUTHOR SUBJECT KEYWORDS CREATORprocedure
Records the document's descriptive properties, all strings. CREATOR is the name of the application that produced the content; the producer is always recorded as this egg. The values are written twice, as the /Info dictionary and as the XMP packet, and PDF/A requires the two copies to agree, so:
- fields given as #f or "" are left out of both;
- non-ASCII text is allowed: /Info values are written as UTF-16 and the XMP packet as UTF-8;
- a value that is not a string, or that contains a control character other than tab, line feed or carriage return, is an error, since XML cannot represent such characters.
The creation date is set automatically when the document is written.
set-output-intent!
- set-output-intent! PDFA-DOC ICC-PATH #!key (N 3) (CONDITION-IDENTIFIER sRGB) (INFO sRGB IEC61966-2.1)procedure
Sets the document's output intent: the ICC color profile in the file ICC-PATH, which defines what the document's colors mean. N is the profile's number of color components (3 for RGB, 4 for CMYK, 1 for gray). CONDITION-IDENTIFIER names the output condition and INFO describes it.
PDF/A-1 needs a monitor or printer profile, in RGB, CMYK or gray, with a version below 4. Most Linux systems have a suitable sRGB profile at /usr/share/color/icc/sRGB.icc. In conformance mode the profile's header is checked straight away (see icc-profile-problems), and an unsuitable profile raises an error here.
icc-profile-problems
- icc-profile-problems BYTEVECTOR Nprocedure
Checks the header of the ICC profile in BYTEVECTOR for use as a PDF/A-1 output intent with N color components. Returns a list of strings describing each problem, or '() if there are none. The checks are: the declared size matches the data; the device class is a monitor or printer profile; the color space is RGB, CMYK or gray, and matches N; and the major version is below 4.
Conformance checks
check-pdfa-conformance
- check-pdfa-conformance PDFA-DOCprocedure
Checks PDFA-DOC against the PDF/A-1b rules a document built with pdf can break, and returns the list of violations found, or '() if it conforms. It checks the output intent, every font, every ExtGState resource, and every page's content stream. It does not change the document, so it can be called at any time, for example to report problems without raising an error.
write-pdfa-document calls it automatically in conformance mode.
pdfa-violation?
- pdfa-violation? Xprocedure
Tests whether X is a violation returned by check-pdfa-conformance.
pdfa-violation-kind
- pdfa-violation-kind VIOLATIONprocedure
Returns the kind of VIOLATION, as one of these symbols (clause numbers refer to ISO 19005-1:2005):
- missing-output-intent
- the document has no output intent (6.2.2)
- icc-profile-invalid
- the output intent's ICC profile is unsuitable (6.2.2)
- unembedded-font
- a font is not embedded, such as one from build-font (6.3.4)
- restricted-font-license
- an embedded font's licence (OS/2 fsType) does not allow embedding (6.3.4)
- colour-space-mismatch
- a page uses a device color space that does not match the output intent, or uses device color with no output intent (6.2.3.3)
- transparency
- an ExtGState sets an alpha below 1, a blend mode other than normal, or a soft mask (6.4)
- limit-exceeded
- a number, string, name or q nesting exceeds a PDF/A-1 implementation limit (6.1.12)
Each kind is reported at most once per value; for example, using set-cmyk-fill many times under an RGB intent gives one violation.
pdfa-violation->string
- pdfa-violation->string VIOLATIONprocedure
Describes VIOLATION in one line of text, naming the font, operator or resource involved and the clause it breaks.
Lower-level procedures
These are used by write-pdfa-document, and are exported for programs that assemble the parts themselves.
finalize-document-metadata!
- finalize-document-metadata! PDFA-DOCprocedure
Adds the /Info dictionary, XMP metadata stream and output intent to PDFA-DOC's wrapped document, and computes its /ID. In conformance mode it first runs check-pdfa-conformance, and raises an error listing every violation before changing anything. Call it once, after all pages are added and before writing.
info-trailer-fragment
- info-trailer-fragment PDFA-DOCprocedure
Returns the /Info and /ID trailer entries for a finalized PDFA-DOC, as text to pass to pdf's write-document as its TRAILER-EXTRA argument.
pdf-text-string
- pdf-text-string STRINGprocedure
Encodes STRING as a PDF text string: printable ASCII text as an escaped literal string such as "(A Report)", and any other text as a UTF-16BE hex string with a byte-order mark, such as "<FEFF...>".
build-xmp-packet
- build-xmp-packet #!key TITLE AUTHOR SUBJECT KEYWORDS PRODUCER CREATOR-TOOL CREATE-DATE PDFA-PART PDFA-CONFORMANCEprocedure
Returns an XMP metadata packet as a string. Fields given as #f or "" are left out. When PDFA-PART and PDFA-CONFORMANCE are both given (for PDF/A-1b, "1" and "B"), the packet includes the PDF/A identification fields, which claim conformance.
pdf-date-string
- pdf-date-string SECONDSprocedure
- xmp-date-string SECONDSprocedure
Format a Unix time (as from current-seconds) in UTC, as a PDF date string ("D:20261018120000Z") or an XMP date string ("2026-10-18T12:00:00Z").
md5-hex
- md5-hex STRINGprocedure
Returns the MD5 digest of STRING as 32 lowercase hex digits; used for the trailer /ID.
Validating the result
The conformance checks cover what a document built with pdf can do wrong, but they are not a full PDF/A validator. To confirm that a file conforms, check it with veraPDF, the reference PDF/A validator:
verapdf --flavour 1b document.pdf
The egg's source includes tests/verapdf-check.sh, which writes a set of test documents and validates each one with veraPDF.
Examples
A one-page report. The source distribution's pdfa-examples.scm has a longer version, with wrapped text and a bar chart.
(import scheme (chicken base) pdf pdfa pdf-truetype) (define font-file "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf") (define icc-profile "/usr/share/color/icc/sRGB.icc") (with-pdfa-document-to-file (pdfa-doc doc) "report.pdf" ;; metadata, stored as both /Info and XMP (set-document-info! pdfa-doc title: "Quarterly report \x2014; caf\xe9; division" author: "Example Author") ;; the colour profile every color in the file refers to (set-output-intent! pdfa-doc icc-profile) ;; fonts must be embedded (let ((font (build-embedded-font doc font-file))) (with-page doc (page) (set-gray-fill page 0) (in-text-mode page (set-font page (embedded-font-name font) 24) (move-text page 72 700) (draw-encoded-text page font "Quarterly report")) (in-text-mode page (set-font page (embedded-font-name font) 12) (move-text page 72 670) (draw-encoded-text page font "Sales rose to \x20ac;1.2 million.")) ;; RGB color is allowed, because the output intent is RGB (set-rgb-fill page 0.16 0.38 0.62) (basic-rect page 72 500 300 100) (fill-path page))))
Finding out why a document does not conform:
(import scheme (chicken base) pdf pdfa) (let* ((pdfa-doc (build-pdfa-document)) (doc (pdfa-document-doc pdfa-doc)) (helvetica (build-font doc "Helvetica"))) ; not embedded ;; no output intent (with-page doc (page) (set-cmyk-fill page 0 0.8 0.8 0) ; device color (in-text-mode page (set-font page (font-name helvetica) 12) (move-text page 72 700) (draw-text page "Not PDF/A"))) (for-each (lambda (v) (print (pdfa-violation->string v))) (check-pdfa-conformance pdfa-doc)))
This prints:
no output intent: call set-output-intent! with an ICC profile (6.2.2) font /CLF101 (/Helvetica) is not embedded; use build-embedded-font from pdf-truetype (6.3.4) content stream operator k sets a device color, which needs an output intent (6.2.3.3)
Author
License
Copyright (c) 2026, Ivan Raikov
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Repository
https://github.com/iraikov/chicken-pdfa
Version history
- 1.0
- Initial release: PDF/A-1b metadata, output intents and conformance checks