tclpdf

Timeline
Login

Timeline

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

44 check-ins

2026-08-17
09:41
PDF/A colour spaces held against the output intent, the striped head back to blue, 02.09 on three pages with right-to-left everyday lines and eight bars of Beethoven, 05.05 in Italian, equal columns for the standard-14 table

- pdfa checks colour like fonts: the document records which device colour spaces it uses and where - fills and strokes through the one gate ColourUsed, text, SVG, pictures at placement, shadings - and at write time every space that the output intent does not admit is refused in one message naming call and page (ISO 19005-2, 6.2.4.3: grey always, RGB only under an RGB intent, CMYK only under a CMYK one) with the two ways out; measured against veraPDF on fifty probes, refused equals failed. The old separation check was wrong under a CMYK profile - it refused the valid CMYK alternate and named sRGB - and is folded into this one; GraphicsStyle translates colours after the other values so a refused width leaves no record
- Table themes: the greys stay DeviceGray, the striped head keeps its blue - the one colour a theme brings, right under sRGB and in every plain PDF, overridden with -headStyle under a CMYK or grey intent, as the manual now says
- mkjoining merges adjacent ranges of one type like mkbidi does; joiningData.tcl regenerated, byte-identical under 8.6 and 9.0
- Example 02.09 has three pages: the faces, then the right-to-left page - the DejaVu lines, everyday Hebrew and Arabic invoice lines each with its meaning beside it, and an Arabic invoice table with the description column right-to-left and the amount column left-to-right at the left - then the first eight bars of Beethoven op. 2 no. 1 set by hand: heads, clef, key, time, dots, accidentals, rests, staccato, fermata, arpeggio and dynamics as Noto Music glyphs, staff, stems, beams, slurs and the hairpin as graphics, every position from a table in the script, and the text says there is no engraving engine and none is planned; the example footer shortens to a count when the list of faces would run off the page
- Example 05.05 speaks Italian, with language it-IT
- Manual: the standard-14 table asks for five equal columns and the HTML template makes tables with column widths fixed-layout, so browsers keep the 20 percent each that pandoc already wrote; the man page stays content-wide (mandoc treats w as a minimum); doc/tclpdf.html now depends on the template as well; pdfa, Colour and -theme describe the intent check
- Tests: 1148 under 8.6 and 9.0, make check 26 passed leaf check-in: f9438beb07 user: alex tags: trunk

08:07
Grey areas closed: hhea for variable instances, one matrix check, version ceiling for PDF/A and PDF/UA-1, shared UCD generator base, 16-bit colour key as soft mask, form groups without a colour space, Unicode spaces as break points, a CMYK output intent, themes and achromatic names in DeviceGray

- Variable fonts: the four hhea summary values of an instance (advanceWidthMax, minLeftSideBearing, minRightSideBearing, xMaxExtent) are computed from the moved glyphs; a face embedded without axes stays byte-identical
- geometry check is the one matrix check - six numbers, finite, non-singular - used by transform, pattern create and shading; shading pattern used to write five numbers into /Matrix and leave function and shading objects behind on a refused call
- Writer: limit / LimitVersion cap the version - configure -version 2.0 after pdfa is refused naming the claim (veraPDF fails such a file on 6.1.2-1), and a PDF/UA-1 claim on a file raised to 2.0 is refused at write time so a document may still move on to part 2; the ua/pdfa conflict check runs before ua raises the version
- tools/ucd.tcl carries what mkjoining.tcl and mkbidi.tcl shared - reading and edition check, line and @missing parsing, sorting and merging, the header - both generators reproduce their data files byte for byte and refuse the wrong UCD file instead of writing an empty table; joiningData.tcl regenerated with the unified header
- PNG: a 16-bit greyscale or truecolor file with a tRNS chunk gets an /SMask computed from the key while the picture passes through at 16 bits, since poppler and CoreGraphics both ignore a 16-bit /Mask; 8-bit files keep the /Mask pass-through; new asset sample-keyed16.png
- Forms: the transparency group names no colour space - /CS /DeviceRGB was a PDF/A failure under a CMYK or grey output intent (6.2.4.3-2), without it every intent validates and pdfa may come before or after form create
- Line breaking: the spaces of UAX #14 class BA (U+1680, U+2000-2006, U+2008-200A, U+205F, U+3000) are break points that vanish at a line end and are set unstretched inside it; U+00A0, U+2007, U+202F stay glue; the zero width space U+200B breaks and is stripped before any font like the soft hyphen; example 01.10 shows no-break and thin space side by side
- Colour: a name or hex triplet with equal components (black, white, gray, silver) is DeviceGray, not DeviceRGB, and a grey shading stop is promoted to the space of the coloured stops; the table themes are greys in DeviceGray, the striped head dark grey instead of blue - a theme in RGB was the one failed check of a CMYK document
- icc/ISOcoated_v2_bas.ICC ships as a CMYK output intent (basICColor, FOGRA39, zlib licence in LICENSE-ZLIB-bICC beside it, byte-identical to the Debian source), icc/README.md records origin, hash and why not ECI, Ghostscript or Idealliance; new example 05.09-pdfa-cmyk writes a PDF/A-3U print order in process colours with a second page imposing the A5 job on A4 - trim and bleed boxes, trim and registration marks, control strip and grey wedge in French; the example footer takes a colour so it can paint in ink
- Tests: 1136 under 8.6 and 9.0, make check 26 passed (05.09 validates against 3u); manual, README, ANNOUNCE and tools/BUILD-PACKAGE.md follow
- KNOWN: an unknown UCD generator merge is deliberately not applied to mkjoining (13 pairs would collapse and change the data file); a BA space before a word is a word character, not a break; the striped theme changed its look check-in: 58d8995772 user: alex tags: trunk

07:14
Round-2 grey areas built, plus what fell out of them: bidi classes from the UCD, no-break space, PNG colour key, CID-keyed CFF refused, page box limits, form transparency groups, PDF version gate, pattern matrix, variable-font boxes and bearings, subset checksum

- Bidi: the number classes EN/AN/ES/CS/ET come from DerivedBidiClass.txt (Unicode 17) through tools/mkbidi.tcl into the new module bidiData.tcl instead of a hand list - degree, pound and per-mille signs used to come out reversed beside a digit in a right-to-left line; three package lists carry the module
- No-break space: tools/mkafm.tcl gives the WinAnsi positions 0xA0 and 0xAD the Annex D aliases space and hyphen, afmData.tcl regenerated, so U+00A0 is set and measured in the standard fourteen and written as byte 0xA0; the line breaker breaks at ASCII white space only, never at a no-break space, and justification stretches U+0020 alone
- PNG: a tRNS chunk of a greyscale or truecolor file becomes a /Mask colour-key array on the pass-through, at the file's bit depth; a wrong tRNS length is refused at embed time; new example asset sample-keyed.png
- Fonts: a CID-keyed CFF face (ROS in the Top DICT) is refused at font embed, and an OTTO file without a CFF table is refused; head.checkSumAdjustment is now computed in Assemble - the field shipped as 0 in every subset while a comment claimed otherwise; a variable instance rebuilds the bounding box and bearing of every composite from its moved components, and the head box and /FontBBox of an instance are the union of the moved glyphs; faces without axes are byte-identical
- Page boxes: crop, bleed, trim and art must lie inside the media box, corners must be in order, the media box measures 3 to 14400 pt a side and cannot be shrunk under a box already set; -format is held to the same limit
- Forms: every form XObject is an isolated transparency group from PDF 1.4 on, so -opacity fades a placement as one object and an opacity set inside the form no longer replaces the placement's; image place, svg and form place check -opacity before writing q
- Version gate: writer require / RequireVersion refuses a feature the document's -version does not have - FlateDecode 1.2 at the one place streams are written, Type0 and separation and patterns 1.2, shading and colour key and attachments and page labels and bleed/trim/art boxes 1.3, opacity, blend, tagged, language and metadata 1.4, 16-bit images 1.5, CFF faces 1.6, viewer preference keys by key; pdfa and ua -part 1 lift the file to 1.7 through configure and it cannot be lowered again; configure passes -version to the writer only when it changes, which fixes configure -unit resetting a raised writer to 1.4
- Patterns: pattern create takes -matrix (six numbers, non-singular) or -origin (a point in document coordinates), not both; the matrix belongs to the pattern object, pattern size stays unscaled
- Tests: 1101 under 8.6 and 9.0 (new tests/pdfVersion.test, PNG builder moved to tests/helper.tcl), make check 25 passed; examples 01.03, 01.05, 02.09, 03.01 show the new behaviour
- Manual: the standard-14 file table is one table headed PDF standard 14 / macOS (the macOS PostScript names are the standard ones, ZapfDingbats excepted), hard-wrapped paragraphs joined, version notes per feature, new paragraphs on line breaking, colour key, page boxes, form groups, pattern space and variable-font boxes
- KNOWN: hhea summary values of a variable instance keep the file's numbers; the six-number matrix check is inline in transform and pattern and missing in shading pattern; pdfa followed by configure -version 2.0 is not refused; poppler renders a 16-bit colour key opaque (reader defect); the form group names DeviceRGB as blend space check-in: 5c200fdea5 user: alex tags: trunk

2026-08-16
21:28
Second norm review: separation colour, forward links, font subsets and descriptors, refuse-before-write, layout fixes, manual gaps

- A separation colour writes its colour space: the Separation array with the alternate space and a type 2 tint function, entered under Resources /ColorSpace and shared by fill, stroke and text. It used to be a bare name the reader could not resolve, so example 01.02 painted the swatch black. Under PDF/A a CMYK alternate is refused, since the shipped intent is sRGB
- link -page and bookmark -page may point at a page added later: the destination becomes an indirect object filled at write time, and a page that never comes is reported naming the link or the bookmark. Bookmark /Count counts the visible descendants, a /URI is 7-bit ASCII with percent-encoding, and example 05.03 counts pages from 0 as the API does - its bookmarks used to land one page late
- Fonts: the subset tag comes from the glyphs, coordinates and name, so two subsets of one face get two tags, and a variable instance is named after its fvar entry; an instance with -subset 0 is instantiated with every glyph instead of embedding the whole variable file; a PFA program is turned into the binary eexec form; ItalicAngle, CapHeight and StemV come from post, OS/2 and the weight; empty glyphs take part in instantiation, so the space width follows the weight; and the font objects are reserved once, which makes a second write byte-identical with embedded faces
- Shapes, shadings, SVG paint and textPath check their geometry before a byte reaches the stream, so an error no longer leaves q, BDC or an open path behind; dash arrays, line widths, miter limits, pattern steps, shading stops and radii, transform matrices, JPEG precision and opacity sides are checked against their range
- Layout: -avoid measures against the first baseline under -anchor top, keeps -indent and -firstIndent, and skips a line where the obstacle leaves no room instead of drawing single letters through it; the -height rest is a suffix of the original text; textLines and textHeight take the options of text; -anchor, -size, -stretch and -width are checked; leader -spacing keeps its dots off the right text; a column set that leaves no room and a percent width are refused with a message
- Manual: table style, column, cell and hook keys, colour forms, shape values, alternative option spellings and the values of -tag Artifact are documented; the Writing heading renders as a heading; README, ANNOUNCE and the wiki page name what the package does today
- 991 tests under 8.6 and 9.0, make check 25 passed
- KNOWN: HVAR is not read (instantiation gives the same widths), rlig chaining lookups and GPOS mark placement are not built, no bidi for mixed lines check-in: a82405ca21 user: alex tags: trunk

19:52
Right-to-left text: -direction rtl, Arabic contextual forms, digit islands, and two fixes found on the way

- text takes -direction rtl: the glyph run stays logical for ligatures and kerning and is reversed when written, -align is mirrored, and Hebrew, Thaana and Samaritan no longer need -unshaped 1. The kerning adjustments move by one on reversal because each closes the gap to the next glyph
- Arabic contextual forms out of GSUB: ccmp, then exactly one of isol/fina/medi/init per glyph from the joining analysis, then rlig where it is a type 1 or 2 lookup. New modules joining (Unicode 9.2, table generated by tools/mkjoining.tcl from tools/ucd/DerivedJoiningType.txt), joiningData, gsubApply (lookup types 1/2/4, shared with liga, which shrinks to its own topic) and forms. Verified glyph for glyph against hb-shape on DejaVu Sans
- A face that writes its letters as a skeleton plus separate mark glyphs is refused under rtl, with the reason and both ways out: without GPOS mark attachment the dots would sit beside the wrong letter. Noto Naskh is such a face; DejaVu Sans is not and is the demonstration
- Digit runs keep their own order inside a reversed line, paired brackets are mirrored and carry their character as ActualText, and a line that mixes strong left-to-right text into an rtl line is refused instead of coming out with the Latin words backwards. New module bidi holds the direction classes and the six mirrored pairs
- textPath, leader, pageNumbers and the table take -direction; direction is a table style key and a cell key, a decimal column does not mirror
- The standard fourteen and an embedded Type 1 face refuse -direction rtl at the call: WinAnsiEncoding has no right-to-left letter, and the option used to be accepted and do nothing
- Fonts are written on the resources event rather than beforeWrite: pageNumbers draws its labels on beforeWrite, and a face used by nothing else had its object reserved and never written
- Example 02.09 sets a Hebrew line and two Arabic lines in DejaVu Sans, one with an invoice number, an amount and a percentage in brackets; the manual's Writing systems section says what is set and what is refused
- 917 tests under 8.6 and 9.0, make check 25 passed
- KNOWN: no bidi algorithm for mixed lines (TODO 6), no rlig chaining lookups, no GPOS mark placement, no locl check-in: b2769b021a user: alex tags: trunk

2026-08-15
19:11
Split structure.tcl by topic, keep the shipped profile's IEC name, complete the module list

- structure.tcl keeps building the tree while the document is drawn; three new modules take what was a topic of its own: structureWrite (the tree as objects at write time, with StructurePage and StructureFirstPosition), structureReport (facts about the finished tree for the UA checks) and structureDest (the GoTo action a link or a bookmark points at an element with). Pure move, no method body changed
- The shipped sRGB profile is named sRGB IEC61966-2.1 as it always was, the colour space it implements; any other profile still takes the name from its desc tag
- tests/all.tcl requires every module: structure, pageNumber, textAvoid and textPath were missing, the three new ones are in
- 831 tests under 8.6 and 9.0, make check 25 passed check-in: 42bd780172 user: alex tags: trunk

17:43
Norm review: XMP encoding, output intent, structure destinations, PDF/UA checks and 27 manual gaps

- The XMP packet is stored as UTF-8 bytes: a title with an umlaut wrote Latin-1 under a UTF-8 BOM, one with a euro sign made the document unwritable. It is rebuilt on every write from the current title, language and declarations unless the caller set it, dc:language is written, and xmp:CreateDate and CreationDate come from one clock reading
- Every PDF/A file carries an output intent: the shipped sRGB profile is the default for -profile, a missing one is an error, and the output condition identifier is read from the profile's desc tag (-identifier overrides). KNOWN: the shipped profile calls itself sRGB, so every PDF/A document changes 26 bytes in the intent dictionary
- pdfa refuses a 2.0 file and a ua -part 2 declaration, in both orders; an ICC profile outside GRAY/RGB/CMYK and a NUL in a name are refused with a message
- zugferd derives -relationship from the profile: MINIMUM and BASIC WL take Data, the rest Alternative; an explicit value still wins
- Structure destinations need a 2.0 file and are written as a GoTo action with /SD and a /D page destination at the element's first content (/XYZ, /Fit when no position is known); bookmark -structure reserved its object too late and never worked
- Structure tree: OBJR carries /Pg, the artifact vocabulary is checked against Tables 330/331, the generic H stays in the 1.7 namespace, headings are reported to H10, Lbl is allowed in Note and FENote, a Caption has to be the first child (last as well in 2.0), structure and text take -expansion (/E), and structureReport answers about lists
- image place, image draw, svg and form place take -artifact 1 as the explicit way to say decoration; in a tagged document a graphic without -alt and without -artifact is remembered, and one GraphicMark helper replaces three copies of the same lines
- ua checks undescribed graphics, lists in both directions (numbered without Lbl, Lbl without -numbering), a four-digit -revision, and DisplayDocTitle staying on
- New module pageLabel: pageLabels -from -style -prefix -start writes /PageLabels with a leading unstyled range when index 0 is not covered
- Manual: 27 options that existed only in the code (shading -from -to -center -radius -innerRadius -focus -stops -extend -matrix, zugferd -name -type -compress -relationship -description, image place -size -scale -dpi, attach -data, transform -skew -matrix, table -alternateFill -minRowHeight, pattern -step -unit, form -unit, bookmark -open, link -zoom, -miter); page box is corner points, image info differs by format, headings run to H10, text without -width returns nothing, twelve 1.7-only types; new sections for page labels and the enlarged UA check list
- Examples: 05.01 attaches with Data, 05.03 shows page labels, 05.04 has an umlaut in its title, 05.06 marks its decorative form, 05.07 carries -expansion and the list rules
- 830 tests under 8.6 and 9.0, make check 25 passed
- KNOWN: structure.tcl and ua.tcl are past the module size limit; tests/all.tcl does not list structure, pageNumber, textAvoid and textPath check-in: e8a2a51faf user: alex tags: trunk

13:54
Structure destinations, break hyphens, and the 2.0 date spelling

- structure takes -name, link and bookmark take -structure: the destination names the element rather than a place on a page, which is what PDF/UA-2 asks for on internal targets. The destination object is indirect, so a reference may point forward at a section further down
- A hyphen the line breaker inserted at a soft-hyphen offer is drawn inside a Span with an empty ActualText, so extracting the line gives the word back whole; a hyphen the text brought with it stays untouched. Tagged documents only
- image place with -alt writes the BBox of the Figure it creates
- A 2.0 file spells the zone offset of a date without the trailing apostrophe, and the file version decides it
- A running head and a running foot name their artifact subtype: pageNumbers derives it from where the number sits, and -tag takes the kind as a list
- The fourteen modules changed by this work go up by 0.1, in the file and in pkgIndex.tcl.in; the three new ones stay at 1.0
- Manual: tzint under SEE ALSO with the reason it is no dependency, and the PDF/UA, viewer preference and structure attribute sections
- Example 5.8 is in English, and its document language moves with it
- 739 tests, make check 25 passed
- KNOWN: form place and svg derive no BBox, and -actualText is not refused on Figure, TH and TD, where common screen readers replace the whole cell check-in: 90d42eb5ca user: alex tags: trunk

13:03
Build PDF/UA-1 and PDF/UA-2, with WTPDF on top

- New ua.tcl - $doc ua declares PDF/UA and refuses the claim when the document does not keep it: title, language, every font embedded including the standard 14, headings without a gap, regular tables, a description on every link - all reported at once, each naming the call to change
- New viewerPreferences.tcl - the 12.2 dictionary, which UA-1 made due through DisplayDocTitle
- New xmp.tcl - the metadata packet now has one owner, because PDF/A and PDF/UA both write into it; pdfa.tcl contributes its two values and keeps its extension mechanism
- XML is built with tdom instead of appended as strings - escaping and well-formedness become the parser's business, and a contributed extension schema is parsed on the way in, so a broken one fails at the call. tdom is a runtime requirement from here on
- KNOWN: the XMP timestamp was written without the colon in its zone offset by every version of this package - string replace with last before first returns the string unchanged
- KNOWN: a link annotation's StructParent key was derived from the page count while pages were still being added, and collided with a page's own entry; annotations now count from zero and the pages follow
- KNOWN: a P refused inline markup although Annex L forbids only block elements there, which left no way to mark up emphasis
- KNOWN: PDF/A needs an extension schema description for pdfuaid, or it rejects a document that also claims PDF/UA - found by the example, not by a test
- structure gains scope, numbering, bbox, colSpan and rowSpan, the 2.0 type vocabulary, and artifacts that name their kind; tables write Scope and their spans by themselves
- Links join the tree through an OBJR with StructParent, and a page with annotations gets Tabs /S
- font.tcl gains fontsWithoutProgram, because PDF/A and PDF/UA judge the same fact differently
- check.sh holds every document to the accessibility profile it claims - ua1, ua2, wt1a, wt1r - instead of reporting ua1 as a note
- Two examples, 05.07 accessible and 05.08 ua2-wtpdf, three test files, 729 tests, make check 25/0/0
- OPEN: WTPDF 1.0 spells its identifier declarations/wtpdf/#accessibility1.0 and veraPDF 1.30.2 tests for the form without the slash - both are written until that is decided check-in: d2bfd39082 user: alex tags: trunk

2026-08-14
23:03
Take image data from memory, not only from a file

- image embed and image draw accept -data with the bytes themselves, the way svg has taken markup from the start; the name stays first and positional and -data replaces the path behind it
- The format was always decided by the leading bytes rather than by the file name, so passing none costs nothing - image info then reports an empty path
- image draw keys its cache on the bytes where there is no path, so the same picture drawn twice still travels once
- Module image 1.2
- 03.01-images gains a second page: the same JPEG embedded both ways side by side, and which format a browser should be asked for
- KNOWN: a PNG carrying an alpha channel is still the one path that has to be computed rather than passed through, and -data does not change that check-in: 7c9e169c36 user: alex tags: trunk

22:50
Instance variable fonts when embedding them

- font embed takes -axes {wght 620} and -instance "Bold": the outlines are computed for that point on the axes and embedded as a fixed instance, PDF having nowhere to put an axis value. The two combine, with -instance setting the starting point
- New glyfOutline.tcl reads and writes the points of a glyph - glyf was only ever walked before, and moving a point means rebuilding the whole packed representation, composites included
- varFont gained gvar, the per-region scalar, IUP, and the advance widths, which come from the phantom points rather than from HVAR
- The advances of an instance reach textWidth, the line breaker and the /W array from one source, so measuring and drawing can no longer disagree - they did while the instancing sat in the subsetter
- sfnt reaches any name record by id, which is how named instances are addressed; the platform decoding moved into one place
- Modules font 1.5, sfnt 1.3, subset 1.1, varFont 1.1
- New example 02.10-variable-fonts, and tests for the outline round trip, the interpolation and both spellings
- KNOWN: every point on the axes is a separate embedded font, so nine weights on a page are nine subsets. HVAR, MVAR, cvar and CFF2 are not read check-in: 43d9aace1f user: alex tags: trunk

22:01
Fix three table defects: continuation position, valign, and cells read as dictionaries

- Table -top says where a breaking table resumes on the pages after the first, taken from the page height like -bottom instead of from where -at started it; the frame of "-border outer" follows the table up the page
- A cell was read as a dictionary whenever "text" fell among its words, so "Medium length text here" drew "here" alone - the first word decides now
- valign written on a cell was merged in and read by nobody; it reaches the style like align does
- Unknown keys in a cell, a style or a column description are refused rather than ignored, through the new option keys - the counterpart of option parse for dictionary arguments
- BEHAVIOUR CHANGE: a mistyped key raises an error where it used to produce a document, and a table starting low on the page now takes fewer pages
- KNOWN: a plain string that begins with a cell key and has an even word count is still read as a dictionary - it names the offending key instead of keeping a fragment
- Modules option 1.1, table 1.3, tableLayout 1.1
- 04.01-table-basics gains a third page for the wrapping cell, ruled and plain; 04.02-table-paging shows -top under a running head check-in: 969ddbfc17 user: alex tags: trunk

21:15
Add shaping.tcl, missing from check-in e54da6fe

- New shaping.tcl and tests/shaping.test - e54da6fe carried their pkgIndex.tcl.in and tests/all.tcl entries without the files, so the test suite cannot load at that check-in
- The refusal in FontRun and -unshaped as a text state option in text.tcl
- BEHAVIOUR CHANGE takes effect with this check-in rather than the previous one, since font.tcl is here
- Manual section Writing systems check-in: f21d4d3128 user: alex tags: trunk

21:11
Refuse text from writing systems that need shaping instead of drawing it wrong

- New shaping.tcl - answers whether a string holds characters from a system needing contextual forms or reordering; the check sits in FontRun, so text, tables, SVG text and textPath all pass through it
- BEHAVIOUR CHANGE: Arabic, Hebrew, Syriac, Thaana, NKo, Mandaic, Samaritan, the Indic scripts, Myanmar, Khmer, Thai and Lao now raise an error where they previously produced a document with wrong text
- New text state option -unshaped draws such text as isolated glyphs in logical order, alongside -kerning and -ligatures
- New varFont.tcl - fvar axes, named instances, normalisation and avar warping
- KNOWN: varFont is half built - gvar, IUP and HVAR are missing, so an embedded variable face still gives its default instance
- KNOWN: no shaper and no bidi; Tibetan, CJK, Cuneiform and Egyptian Hieroglyphs are deliberately not on the list, they come out right as they are
- New example 02.09-writing-systems and a manual section Writing systems check-in: e54da6fec4 user: alex tags: trunk

16:20
Add the font material under examples/assets/fonts

- Ten Noto faces and a brush face with their licences: Japanese, Tibetan, two symbol sets, emoji, cuneiform, Egyptian hieroglyphs, Arabic and music notation
- Roboto and Bitcount Prop Single as variable fonts, as the material the instancing work is measured against
- Roboto and Bitcount keep their licence files from earlier; the Noto ones and Permanent Marker bring theirs
- KNOWN: Noto Color Emoji is left out on purpose - its glyphs carry no outlines and subsetting keeps neither the COLR layers nor the palette, so it embeds cleanly and draws nothing. Chinese and Korean are left out for their size check-in: 3f1a22c1df user: alex tags: trunk

12:17
Say what the missing mark attachment means for a caller

- doc/tclpdf.md: composed text is unaffected, which covers every European language; decomposed text and combinations Unicode has no composed form for come out with displaced accents, and normalising to NFC beforehand is the answer
- The paragraph above it already said the feature is absent - what was missing was who it affects check-in: 77ad5fcbfc user: alex tags: trunk

11:57
Embed Type 1 and OpenType/CFF faces

- type1.tcl: new, reads both containers of a Type 1 program - a .pfb with segment markers and a raw .pfa or .t1 - and the AFM beside it; the widths are addressed by glyph name, and which name a face carries is decided per face against its metrics rather than by taking the first the Adobe list offers
- sfnt.tcl: an OTTO file is parsed rather than refused, and marks its outlines as CFF; loca and glyf are skipped, being what TrueType has and CFF has not
- font.tcl: a CFF face goes in as FontFile3 with Subtype OpenType and a CIDFontType0 descendant, without a CIDToGIDMap; a Type 1 face as FontFile with the three lengths it already consists of. Neither is subsetted - that needs the outlines - and neither carries a subset prefix
- afm.tcl: the WinAnsi encoder takes a width list instead of looking one up, so an embedded single-byte face goes through it rather than through a second copy
- text.tcl: measuring and showing know a third kind of face beside TrueType and the standard fourteen
- tests/type1.test is new, tests/font.test gained the CFF cases; both compare a face against the same face in the other format, which is what this tree can offer and a font parser normally cannot
- examples/02.07-type1.tcl and examples/02.08-opentype.tcl: new, both PDF/A-3u
- tests/helper.tcl, examples/common.tcl: the write-and-check and the archival ending each existed twice
- afm, font, sfnt and text each raised a version, in pkgIndex.tcl.in as well; type1 starts at 1.0
- KNOWN: a Type 1 face gets no kerning, and the kern pairs an AFM lists are not read; the reach of both new roads is WinAnsi, so a document needing more characters wants TrueType check-in: 43caddce6b user: alex tags: trunk

10:47
Text in an SVG drawing goes through the same fonts as text does

- svgElement.tcl, text.tcl: a drawing measures and writes through TextPoints and TextShow instead of the AFM path, so font-family reaches an embedded face by its alias and kerning and ligatures apply - new TextPoints holds the one branch between an embedded face and the standard fourteen, and textWidth uses it too
- svg.tcl: a drawing that stops halfway unwinds its own q brackets and leaves no font resource behind - two older defects, either of which turned everything drawn afterwards wrong with no tool objecting
- sfnt.tcl: platform 0 names are decoded as UTF-16BE, which is what they are - a font leading with one produced a BaseFont with NUL bytes in it that qpdf rejects
- tests/svg.test, tests/font.test: nine tests; the platform 0 case is built from DejaVu rather than checked in - none of the fonts that show it may be redistributed
- examples/03.05-svg-text.tcl: new, PDF/A-3u - place names outside WinAnsi, an axis of labels measured by their own width, and a barcode caption in the face the encoder names
- examples/03.04-barcodes.tcl, doc/tclpdf.md: both said an SVG caption can only use the standard fourteen; it can come from an embedded face now
- text, svg, svgElement and sfnt each raised a version, in pkgIndex.tcl.in as well
- examples/assets/fonts/urw-core35-fonts/NOTICE.md: says why the fonts come from upstream and not from the distribution - the Debian package carries the AGPL alone where upstream states a choice of three licences
- KNOWN: text.tcl stays above the 500 line mark, and no example covers the platform 0 fix - that would need one of those fonts in the tree check-in: ae8529390d user: alex tags: trunk

09:22
Keep the URW faces in all four formats

- examples/assets/fonts/urw-core35-fonts: the metrics and the Type 1 form of the fourteen faces, taken from the same upstream archive the TrueType and OpenType files came from - all 28 of those compare byte for byte against what was already here
- NOTICE.md: the section explaining why two formats and not four now explains why four. The Type 1 files were dropped once because nothing was going to read them; that changed, and these are better test material than anything else in the tree, since the same face sits beside them as TrueType and as metrics
- KNOWN: only the TrueType form embeds today, and two of the fourteen not even then - StandardSymbolsPS and D050000L carry a symbol cmap and no Unicode one, which font embed refuses. That is unchanged and stated in NOTICE.md check-in: 046960d737 user: alex tags: trunk

08:56
Carry OCR A and OCR B for the clear text line under a barcode

- examples/assets/fonts: OCRA and OCRB in four forms each - TrueType, OpenType, Type 1 and the metrics. The TrueType ones embed and pass PDF/A-3u; the digits under an EAN can now be set in the face the standard asks for instead of in Helvetica
- examples/assets/fonts/licenses/OCR-LICENSE.txt: new - the three licence statements quoted in full rather than summarised, because each author is careful about what they do and do not say, and a paraphrase would turn that into a promise. It also lists what was left out of the two source packages and why
- KNOWN: only the TrueType form can be embedded today. The OpenType ones are CFF, the Type 1 ones are not read at all - both are kept as the material a reader would be built and tested against check-in: 704cf1792c user: alex tags: trunk

05:34
Draw barcodes through tzint, and add PDF417

- examples/03.04-barcodes.tcl: new - Code 128, EAN-13, QR, DataMatrix, PDF417 and a GiroCode, each from markup in a variable rather than a file; PDF417 is sized by width because a stacked symbol comes out roughly 3.6 to 1. tclpdf carries no encoder and the example skips itself where tzint is absent, so make examples is unaffected
- tests/barcode.test: new - as many modules leave as arrived for a linear, a matrix and a stacked symbol, a symbol of circles keeps its curves, an EAN keeps its digits as text, and the two encoder traps: status 1 to 4 is a warning with a usable symbol, and from 5 up the target variable still holds the previous one
- doc/tclpdf.md: a Barcodes section under SVG - tzint is not a dependency, the status is three-valued, and an EPC dataset needs -eci 26 to be encoded as it says it is
- examples/01.10-hyphen-and-leader.tcl: headings and explanations in English like every other example. The broken sentence stays German - it is what the page demonstrates, and the introduction now says so
- KNOWN: nothing here decodes. No barcode reader is installed on this machine, so no test claims a symbol scans - what they claim is that as many shapes come out as went in check-in: b0c904f977 user: alex tags: trunk

2026-08-13
23:20
Document svg -data, and the options the signature was missing

- doc/tclpdf.md: svg takes its markup from a variable as well as from a file, and -size, -scale and -opacity existed without being written down; svg size takes a file name only, which the entry now says check-in: 6b01065f68 user: alex tags: trunk

22:59
Check the manual is no older than what it is made from

- tools/check.sh: doc/tclpdf.n and doc/tclpdf.html are compared against doc/tclpdf.md by timestamp - neither is under version control, both travel in the source archive, and a check-in of the source alone looks complete either way check-in: 2431c117e1 user: alex tags: trunk

22:40
Honour soft hyphens, and set leader rows

- afm.tcl, font.tcl: U+00AD never reaches a font any more - a standard face refused the whole string over it, and an embedded one set a visible hyphen in the middle of an unbroken word
- textBlock.tcl: the breaker takes the offer where the line needs it and sets a real hyphen there; closing the line and trying again without a prefix is the next round of the same loop rather than a case of its own
- leader.tcl: new - a row with two ends and as many whole copies of the fill between them as fit, keeping -gap clear of each end; in a tagged document the row is one element and the fill an artifact
- option.tcl, text.tcl: the -at check lives in one place now that leader is its second caller
- document.tcl, configure.ac, pkgIndex.tcl.in, tests/all.tcl: leader in the four lists a module has to appear in
- examples/01.10-hyphen-and-leader.tcl: new - the same paragraph with and without the marks side by side, a table of contents, a total, and what the fill may be
- doc/tclpdf.md: leader, and what a soft hyphen does to a paragraph and to a standard face
- tests/leader.test: new; textFlow.test gained the soft hyphen cases
- KNOWN: a line broken at a soft hyphen extracts with the hyphen in it. PDF/UA wants an ActualText around the break and that is not written - TODO item 12 carries it, where it used to stand as not applicable check-in: f0d0a1ba2b user: alex tags: trunk

21:59
Read lookupFlag and GDEF, and add a target for the full acceptance run

- gdef.tcl: new - the glyph classes, mark attachment classes and mark glyph sets a lookup flag filters by, and the question it asks of them
- otLayout.tcl: lookupFlag and markFilteringSet out of the lookup header; the error boundary and the lookup walk moved here from the copy each that kernGpos.tcl and liga.tcl carried
- kern.tcl, font.tcl: a whole glyph run rather than a pair goes down to the kerning, and an amount lands in the gap before the second glyph - A + U+0301 + V is kerned as the pair A V again, as the ignore bits of most faces ask for
- kern.tcl: the old kern table is expressed as one unfiltered lookup with a pairs subtable, so both sources are evaluated by one piece of code; kernGpos.tcl reads and no longer evaluates
- liga.tcl: a lookup matches over the positions it sees, and a skipped glyph keeps its own characters and follows the ligature it stood inside
- examples/02.05-kerning.tcl, doc/tclpdf.md: what an accent between two letters does, and what is still not placed
- tests/gdef.test: new; kern.test and liga.test gained the cases for the two walks
- Makefile.in, tools/check.sh: new target make check - the suite under every interpreter present, all examples, qpdf on each document, veraPDF against the profile each document claims in its own XMP, poppler as a reader; a missing tool counts as skipped and the summary says so a second time
- Makefile.in: the example count in the comment matched neither the scripts nor the documents
- KNOWN: no face here sets an ignore bit on liga, so the three GSUB filter tests carry rules and a filter written by hand instead of read from a font
- KNOWN: GPOS mark attachment stays unread. A combining mark is drawn at the pen position rather than placed against its base, which is visible at large sizes; the manual says so and TODO item 14 carries the decision check-in: 9725c6302b user: alex tags: trunk

21:04
Fix three findings from the review

- text.tcl: the width of an empty string is no longer negative - an empty table cell was asking for less room than none
- textPath.tcl: -spacing now reaches the cursor as well as the measurement, so an aligned string on a path sits where it was measured; letterspacing on a path stays available, which forcing the option off would have cost
- liga.tcl, kernGpos.tcl: the arrays beside a coverage table are read by coverage index rather than by a running count, as ISO/IEC 14496-22 requires
- text 1.7, textPath 1.3, liga 1.1, kernGpos 1.2
- KNOWN: the coverage change has no test. A conforming font cannot show the difference, because its format 2 ranges are in glyph id order and the two numbers coincide - proving it would need a font built to break that rule. What is covered is the other direction: kerning pairs and ligatures come out unchanged for the faces the suite uses check-in: 8619947b1c user: alex tags: trunk

20:46
Write tagged PDF, and with it PDF/A level A

- structure.tcl: new - the logical tree beside the page: tagged as the switch, structure with a script block for the grouping a writer cannot infer, and the Annex L nesting rules checked where the call is written rather than in a validator later
- text.tcl, table.tcl, image.tcl: what can be derived is derived - text becomes a P, a table its Table with TR and TH and TD, a picture a Figure when -alt describes it and an artifact otherwise; -tag says what no writer can know, and -tag Artifact takes something out of the tree
- graphics.tcl, shading.tcl, textPath.tcl, svg.tcl: shapes, gradients, text on a path and drawings are marked content too - part of an open element where there is one, artifacts otherwise
- xObject.tcl, pattern.tcl, pageNumber.tcl: a form, a pattern and a page number are content streams of their own, so nothing inside them is marked and the invocation carries the marking instead - a mark number is unique per stream, not per page
- xObject.tcl, pattern.tcl, pageNumber.tcl: each of those streams now has a Resources dictionary of its own, which PDF/A 6.2.2 requires and which no example had ever combined with a form before
- textBlock.tcl: a wrapped line keeps the space the breaker consumed, or a reader that follows the tree hears the last word of one line and the first of the next as one
- pdfa.tcl: level A is accepted for a tagged document and names the missing call otherwise; the font check moved to the catalogue event, so declaring pdfa before the first font embed no longer refuses a document whose fonts are all embedded
- examples: new 05.05-tagged.tcl and 05.06-lists-and-artifacts.tcl, the second one about lists and about what stays out of the tree
- tests: new tests/structure.test; tests/helper.tcl gained writeTwice, which pageNumber.test now uses as well
- doc/tclpdf.md: a section on structure and accessibility, -alt on form place and svg, -tag on textPath
- document 1.3, output 1.2, text 1.6, textBlock 1.4, table 1.2, tableDraw 1.2, image 1.1, graphics 1.2, shading 1.1, pattern 1.1, svg 1.1, textPath 1.2, xObject 1.1, pageNumber 1.1, pdfa 1.3; structure starts at 1.0
- KNOWN: this is stage one. PDF/UA-1 additionally wants the pdfuaid schema, ViewerPreferences with DisplayDocTitle, a Link element with Contents on the annotation, Scope on TH, a subtype on pagination artifacts, and heading levels without gaps - none of which is written yet. colSpan and rowSpan make a table irregular in the UA sense, which is a property of the feature check-in: 46b2a38daf user: alex tags: trunk

18:22
Carry the font substitutes and the core metrics in the tree

- examples/assets/fonts/adobe-afm: new - the fourteen Adobe core font metrics afmData.tcl is generated from, with the LICENSE.txt Adobe's terms forbid separating from them; the generator over this directory reproduces the committed afmData.tcl byte for byte under Tcl 8.6 and 9.0
- examples/assets/fonts/urw-core35-fonts: new - the fourteen URW faces that stand in for the standard 14 where a document has to embed them, TrueType and CFF, under the SIL Open Font License 1.1; their advances were measured against the metrics this package ships and match throughout
- tools/archive.sh: the Adobe originals stay out of the source archive - they are here for internal measurement only and every one of them says All Rights Reserved
- doc/tclpdf.md: font embed now says which file to embed for each of the fourteen, and which of the columns can be embedded as it stands
- tools/BUILD-PACKAGE.md: the AFM directory is named instead of assumed - it used to say "the Adobe AFM files" and by the time anyone looked they were gone from the build machine
- examples/assets/README.md: adobe-afm named as the one thing under assets that the build depends on rather than the examples
- KNOWN: Symbol and ZapfDingbats have no embeddable substitute - the URW files carry a symbol cmap and no Unicode one, so font embed refuses them; the twelve text faces cover Helvetica, Times and Courier completely check-in: 9d3975722a user: alex tags: trunk

15:15
Read standard ligatures, and make the glyph run the single source of truth

- liga.tcl: new - the liga feature of GSUB, lookup type 4, extension type 7; dlig, hlig and rlig are not read
- otLayout.tcl: new - the walk both tables share, lifted out of kernGpos.tcl now that a second reader wants it: script and language system, feature to lookups, lookup to subtables, coverage and class definitions
- font.tcl: FontRun turns a string into the glyphs that get drawn and carries with each of them the characters it stands for, so encoding, measuring and kerning can no longer disagree; FontWidth is gone, its one caller holds the run itself
- font.tcl: ToUnicode writes a multi-character destination for a ligature, and the longer decomposition wins where one glyph is reachable two ways - drawing "office" before U+FB03 used to leave the word unsearchable
- text.tcl: new -ligatures option, on by default; the TJ array is split at glyph gaps, and -spacing counts glyphs rather than characters
- text.tcl: textWidth of a string with line breaks returns the widest line - it used to add them up, and lately threw, which stopped a table cell with a newline from working at all
- text.tcl: TextEncode carries the standard-face case only; the embedded branch had been unreachable since the run arrived and would have dropped the ligature flag
- textPath.tcl: kerning and ligatures forced off - one glyph is placed at a time and the advances added up, so the whole-string measurement was an alignment that never gets drawn
- liga.tcl, kernGpos.tcl: the error boundary sits per lookup and absorbs only TCLPDF LAYOUT, so a damaged lookup costs itself alone and a mistake in the package no longer looks like a font without ligatures
- tools/mkafm.tcl: the glyph list comes from tools/agl/glyphlist.txt instead of a pdf4tcl installation, and the generator writes the package provide line it always omitted - the output is byte-identical to the committed afmData.tcl
- doc/tclpdf.md: inside a form or a pattern the origin is the TOP left corner, not the bottom left; svg info takes no path; configure returns nothing; 148 colour names; -compress, table themes, pdfa state, pdfa extension and zugferd profile documented
- examples: new 02.06-ligatures.tcl; 01.05-forms.tcl carried the same wrong claim about the origin in its header
- README: tools/agl/glyphlist.txt named as third-party material under Adobe's BSD licence, build-time only, never installed
- font 1.3, text 1.5, textPath 1.1, kernGpos 1.1; the two new modules start at 1.0
- KNOWN: lookupFlag is not read, so kerning is lost across a combining mark in decomposed text; the coverage index is taken as a running count, which a conforming font does not notice; textPath still measures a -spacing it does not draw; textWidth of an empty string with -wordSpacing returns a negative width check-in: 1f8f601125 user: alex tags: trunk

09:23
Apply pair kerning, on by default

- kern.tcl and kernGpos.tcl: new - the amounts come from GPOS when the resolved language system has kerning lookups and from the kern table otherwise, the order of ISO/IEC 14496-22 8.16
- kernGpos.tcl: class based subtables stay coverage, class definitions and matrix and are asked per pair instead of being expanded into glyph pairs
- text.tcl: new -kerning option, on by default; TextShow no longer splits at spaces but writes an adjustment after any character that needs one, so word spacing and kerning share one TJ array
- font.tcl: FontWidth takes the flag and FontKern hands out one adjustment per gap, so the line breaker, the column widths and the text on a path measure what is drawn
- neither kern nor GPOS goes into the subset - the amounts are in the content stream
- doc/tclpdf.md: -kerning documented
- tests/kern.test: new, over both sources, the precedence and the stream
- examples: new 02.05-kerning.tcl; 04.01-table-basics.tcl gains a second page with a comma as decimal separator, and thousands separators on both pages
- text 1.4, font 1.2; the two new modules start at 1.0
- KNOWN: script latn is resolved before DFLT, so text in another script is kerned with the Latin lookups; ligatures over GSUB are still not read; the fourteen standard faces carry no pairs and -kerning does nothing for them
- Documents with an embedded face are now set differently than under 1.0; -kerning 0 restores the earlier setting check-in: 495518c1a0 user: alex tags: trunk

2026-08-12
18:27
Build roadmap items 1 to 5: PDF/A-3u, page numbers, text flow, text on a path and blend modes

- pdfa.tcl: level U is documented and taken; level A is now refused, because it needs a structure tree this writer has no way to produce and veraPDF rejected the result at clauses 6.7.2.2 and 6.7.3.3
- pageNumber.tcl: new, for "page 3 of 7" - the wish is recorded at the call and drawn during write, when the total is known, as one form XObject per page kept at a reserved number so a second write stays byte-identical
- textBlock.tcl: the line breaker now asks per line how wide that line may be and where it starts, which is what carries indents and the flow around shapes alike; -height draws what fits and hands back the rest as text, and -indent, -indentRight, -firstIndent and -paragraphSpacing shape the block
- textBlock.tcl: the last line of every paragraph stays flush left, not only the last line of the block - a paragraph ending mid-column came out stretched across the full width, which the rendered examples showed and no test did
- textAvoid.tcl: new, for -avoid and -avoidMargin - a rectangle blocks its own width, a circle the chord its band cuts, and each shape may carry a margin of its own so the text does not touch the picture
- textPath.tcl: new, for a line of text along a path - curves flattened once, the arc length walked, each glyph placed at its own middle and turned by the tangent there; glyphs past the end of the path are dropped rather than piled up on it
- graphics.tcl: the sixteen blend modes as ExtGState, per call through -blend and as state through the new method; Compatible is refused as deprecated, and mode and alpha stay one resource each because gs is cumulative
- tests/helper.tcl: reading the line positions out of a stream now lives here, for Td and Tm alike, instead of once per test file
- examples: new 01.08-textflow.tcl and 01.09-text-on-path.tcl; 01.02-colour.tcl gains a blend mode table, 04.02-table-paging.tcl real page numbers, 05.04-pdfa.tcl level U, and 05.03-navigation.tcl the line continuations its text bodies always needed
- document 1.2, graphics 1.1, pdfa 1.2, shape 1.1, text 1.3, textBlock 1.3; the three new modules start at 1.0 check-in: 95cb0ffc0c user: alex tags: trunk

12:22
Show decimal commas in the MINIMUM invoice and give the SVG page room

- examples/05.01-zugferd-minimum.tcl: the printed totals use a comma and the column lines up on it through -decimal, while the attached XML keeps the point EN 16931 prescribes
- examples/03.03-svg.tcl: the second half of the page moves down so the size label no longer touches the heading below it
- examples/03.03-svg.tcl: the inline chart draws its bars lower, leaving the caption above the tallest one legible check-in: 1cede32892 user: alex tags: trunk

09:28
Fix justified, rotated and anchored text, the subset prefix, and close the test and example gaps

- text.tcl: word spacing for embedded faces now comes from a TJ array instead of Tw, which has no effect under Identity-H - justified text came out flush left in every document with an embedded font, and PDF/A requires embedding
- text.tcl, textBlock.tcl: offsets across the baseline - the ascender of -anchor top and the line advance of a paragraph - are applied in the text matrix and therefore turn with -rotate; a rotated paragraph drew all of its lines on top of one another
- font.tcl: a face embedded whole through "font embed -subset 0" no longer carries a six-letter subset prefix, and the name is derived once for the descriptor, the CID font and the Type0 font
- doc/tclpdf.md: the table hooks are named as they are - didParseCell exists and was undocumented, willDrawPage never existed and was
- doc/PLUGINS.md: private catalog keys carry the XX prefix Annex E requires, the oo::define example requires tclpdf::document without which it fails, and both examples were run verbatim again
- tests/page.test: new, for coords, distance, extent, fitExtent and the page content buffer - the measuring surface every module draws through, and none of it had a test of its own
- tests: the documented methods and options that nothing had ever exercised, across attach, font, graphics, image, svg, table, text and zugferd
- examples: new 01.07-extension.tcl for the plugin surface and 05.04-pdfa.tcl for archival documents without an invoice; 01.06-stamp.tcl now centres the word across its baseline too; the others gained the options they never showed
- font raised to 1.1, text and textBlock to 1.2, in the modules and in pkgIndex.tcl.in check-in: 455676147d user: alex tags: trunk

2026-08-11
22:15
Fix rotated aligned text and add a diagonal stamp example

- The alignment shift of -align center and right now runs along the baseline: text.tcl passes it into TextRun, where it enters the Tm matrix before the rotation; a centred word rotated with -rotate drifted off towards the page corner instead of staying centred on -at
- textBlock.tcl passes the per-line shift the same way for rotated paragraphs
- Two tests in tests/text.test: the exact matrix at 90 degrees, and the unrotated case as a regression guard
- New example examples/01.06-stamp.tcl: DRAFT and PAID set diagonally across the page, sized from a single width measurement, over a readable sheet on A4 and A5 landscape
- text and textBlock raised to 1.1, in the module and in pkgIndex.tcl.in check-in: a9941b2d1f user: alex tags: trunk

21:52
Document the extension points in doc/PLUGINS.md

- New doc/PLUGINS.md: the event bus with its six events, the idempotency and error contracts, the six supported methods, and two worked examples covering a beforeWrite subscriber and an oo::define method package
- Both examples were run as written; the subscriber example produces byte-identical output across two writes
- doc/tclpdf.md: the Events section names pageAdded and points to doc/PLUGINS.md check-in: fbb63a2a64 user: alex tags: trunk

20:54
Give SVG gradient patterns unique names instead of the SVG id

- SvgGradient in svgPaint.tcl names patterns from a document-wide counter; naming them after the SVG id made a second drawing of the same file collide and lose its fills, and a second file sharing an id lost its own
- Reuse key is the gradient plus every pattern argument, so a userSpaceOnUse gradient filling several shapes stays one object while objectBoundingBox gives each shape its own geometry
- The catch around the registration is removed - a failure there is a real defect and no longer turns into a silently unfilled shape
- Three tests in tests/svg.test: the same drawing twice keeps its colour, each shape gets the gradient over its own box, one user-space gradient stays one object
- svgPaint raised to 1.1, in the module and in pkgIndex.tcl.in check-in: 0458721c7d user: alex tags: trunk

20:42
Wire up -border outer for tables

- New TableDrawFrame in tableDraw.tcl, called from TableRun: one frame per page around the block instead of a rule per cell
- TableDrawBorder takes outer as a known value and draws nothing per cell for it
- examples/04.01-table-basics.tcl shows it below the three themes, on the same rows as -theme grid
- doc/tclpdf.md names the border values: none, all, horizontal, vertical, outer
- Three tests in tests/table.test: the single frame and its measurements, one frame per page across a break, the error on an unknown value
- cleanupTests moved to the end of tests/table.test and tests/document.test - it sat ahead of the last tests, so a file run on its own left them out of its tally and exited 0 even with one failing
- table and tableDraw raised to 1.1, in the module and in pkgIndex.tcl.in check-in: 6e54f16f6c user: alex tags: trunk

2026-08-10
19:47
Make writing idempotent - a second write no longer duplicates the objects

- New method reservation in output.tcl hands out writer object numbers that survive a rebuild, so a builder writes over its objects instead of allocating new ones
- Core builders use it: catalog, page tree, resources, per-page content streams, metadata and info; WriteInfo keeps the generated CreationDate so an unchanged document comes out byte-identical
- streamObject in document.tcl takes an optional object number and writes over it
- attach, pdfa and outline hold their numbers the same way - the attachment bytes, the ICC profile and the bookmark tree are no longer embedded once per write
- event.tcl states the contract: write-time events fire on every write, so a subscriber that creates objects must be idempotent and take its numbers from reservation
- doc/tclpdf.md drops the write-once restriction and documents that write and writeChannel may be combined
- Three regression tests: byte-identical rewrite with constant object count, additions between two writes still arrive, attachments embedded once
- attach, document, event, outline, output and pdfa raised to 1.1 in the module and in pkgIndex.tcl.in check-in: 1a34ff2d40 user: alex tags: trunk

14:32
Add the functional code of tclpdf 1.0 - stages 1 to 6

- 42 Tcl modules implementing document core, TrueType embedding and subsetting, JPEG and PNG images, tables, SVG and ZUGFeRD as PDF/A-3B, loaded lazily through a topic table in document.tcl
- 20 test files covering every module, run in a single interpreter by tests/all.tcl, green under Tcl 8.6.18 and Tcl 9.0.4
- 18 examples in examples plus a shared common.tcl, writing 20 documents
- tools: coverage.tcl reports which document methods nothing calls, svgcheck.sh compares SVG output against rsvg-convert, mkafm.tcl generates afmData.tcl from the Adobe metrics
- configure.ac and pkgIndex.tcl.in list every module, each with its own version and open-ended dependencies
- doc/tclpdf.md rewritten as the full manual, one paragraph per line, covering all public commands
- tools/archive.sh excludes docs from the source archive
- uv/template-download.html lists only tdom as an optional package
- README states the scope, the licence and why no font is part of the package check-in: e1795e6562 user: alex tags: trunk

07:50
Example assets and a make target to build them

- Roboto, Bitcount Prop Single and Niconne added, one face each, next to the two DejaVu faces
- examples/assets/fonts flattened, licence texts collected in fonts/licenses and renamed after the family they cover, replacing the bare AUTHORS and LICENSE
- Eleven SVG test drawings added under examples/assets/images
- New target "make examples" runs every example into examples/out, and "make clean" removes that directory again
- tools/archive.sh: examples/out excluded from the source archive, examples/assets/xml no longer excluded
- examples/assets/README rewritten - per-face inventory with glyph and character counts, and a section on what does and does not ship with the package check-in: c0c89a09b5 user: alex tags: trunk

2026-08-08
22:01
Install and ship the sRGB output intent profile

- tools/install.tcl accepts directory arguments and copies them recursively, keeping the directory name
- make install now installs icc/ into the package directory
- tools/archive.sh copies icc/ into the binary archive as well check-in: c4e49c409e user: alex tags: trunk

21:53
Add example assets and the sRGB output intent profile

- icc/sRGB.icc as a runtime part of the package, installed and shipped in the binary archive, with a README recording its identity and how to verify it
- DejaVu Sans regular and bold with LICENSE and AUTHORS, reduced from the upstream release to the two faces the examples use
- Raster images covering the JPEG grey, JPEG colour, PNG palette and PNG alpha paths, plus an SVG for the deferred stage 6
- ZUGFeRD invoice XML in the MINIMUM and EN16931 profiles, renamed after their profile identifier
- Both READMEs record origin, licence and the measured properties of every file, and the rule for adding one
- The two XML files come with no licence in the FeRD distribution and are kept out of the release archive until that is settled check-in: b82768e52e user: alex tags: trunk

19:31
Initial project scaffolding for tclpdf 1.0

- TEA build adapted for this package: configure.ac, Makefile.in, aclocal.m4, tclconfig, pkgIndex.tcl.in
- tclpdf.tcl as the package stub with namespace, a Tcl 8.6.11 and up requirement and a check for the built-in zlib command
- tests/all.tcl and tests/version.test - the version test keeps AC_INIT and package provide in agreement
- tools with install.tcl for make install, plus archive.sh, mkversion.tcl and BUILD-PACKAGE.md
- README, doc/tclpdf.md with its pandoc template, bilingual ANNOUNCE and uv/template-download.html
- license.terms set to MIT
- No functional code yet check-in: a4f1e086dd user: alex tags: trunk

07:45
initial empty check-in check-in: 87e8fd0694 user: alex tags: trunk