Repository navigation
v1.9.0
Added
-
Knuth–Plass line breaking in layout, opt in per paragraph and as a document default.
LineBreakMode(Greedy, the default, andKnuthPlass),KnuthPlassOptions(tolerance, pre-tolerance, line, hyphen and explicit-hyphen penalties, consecutive-hyphen, final-hyphen and fitness demerits, looseness, emergency stretch),TextParagraph.LineBreakMode/KnuthPlass,Text.LineBreakMode/KnuthPlass,LayoutOptions.LineBreakMode/KnuthPlass,TextDescriptor.OptimalLineBreaks/GreedyLineBreaksandDocumentComposer.LineBreaking. After Knuth and Plass, 1981: the breaks of a whole paragraph are chosen together, with hyphenation points, mixed fonts and sizes and bidi text; a paragraph with no setting in tolerance is retried with emergency stretch and then accepts any line, and one beyond its search bounds is broken greedily with a warning. Greedy output is unchanged. Seedocs/layout-text.md. -
RecursiveAI.OnePdf.Hyphenation, a new package:PatternHyphenator(Liang's pattern algorithm with exception lists, minimum word length and left/right minimums) andHyphenators(For,Auto,Resolve,IsSupported,Languages), with bundled patterns for 26 language variants loaded lazily from compressed resources: English (US, GB), German (1996, 1901, Swiss 1901), French, Spanish, Italian, Dutch, Portuguese, Polish, Danish, Norwegian (no, nb, nn), Bulgarian, Slovak, Slovenian, Lithuanian, Estonian, Ukrainian, Galician, Greek (monotonic), Latin, Welsh and Irish. Plug it into layout withTextDescriptor.Hyphenator(Hyphenators.Auto()); layout itself does not depend on it and hyphenation stays opt-in. Pattern data is generated bytools/hyphenation/generate.pyfrom pinned upstream files, each under its own permissive licence (notices in the package). Swedish, Finnish, Icelandic, Catalan, Croatian, Russian, Turkish, Esperanto, Czech, Hungarian, Latvian and Romanian are not bundled because their pattern files are copyleft, LPPL-only or have unclear terms. Seedocs/layout-text.md. -
Gradient backgrounds in layout:
LayoutGradient(Linearat any angle,Horizontal,Vertical,Circlewith a centre andRadialExtent) andLayoutGradientStop, taken bySlot.Background,Box.BackgroundGradient, table rows and cells (TableRowDescriptor.Background,CellBackground,TableRow.BackgroundGradient,TableCell.BackgroundGradient) and page backgrounds. Clipped to rounded corners, faded by opacity, carried by transforms, drawn as artifacts. A box split over pages paints the whole gradient in each piece. Seedocs/layout.md. -
Multi-column flow in layout:
MultiColumn(element) andSlot.MultiColumn(cols => ...)pour paragraphs, lists, tables and pictures through 1 to 32 equal columns, down each column, then across, then onto the next page. Column count, gap, an optional rule between columns, balanced (default) or sequential fill (ColumnFill),Spacingbetween items, andColumnBreak. Content breaks between columns as it breaks between pages: widows and orphans, keep-together, keep-with-next, ensure-space, headings never ending a column, table rows splitting with repeated header rows. Balancing the last region is a bisection of at most 12 trial layouts, so it is bounded whatever the content, and it never breaks a rule to even the columns out. Right-to-left documents fill columns right to left; the structure tree and text extraction follow the flow. Seedocs/layout.md. -
Content builder:
PdfGradient(Axial,Radial),PdfGradientStop,PdfGradientColor(DeviceGray, DeviceRGB, DeviceCMYK),PdfContentBuilder.PaintGradientandFillRectangle. Written as axial and radial shadings with exponential and stitching functions. Seedocs/content-creation.md. -
Generated table of contents in layout:
TableOfContents(levels, leader, indent, gap, spacing, per-levelTocLevelStyle),TocEntryfor explicit entries,Heading.InTocandHeading.TocTitle,LayoutOptions.TableOfContents, and the fluentSlot.TableOfContents,Slot.TocEntry,Slot.Heading(level, inToc, tocTitle)andTocDescriptor. Entries show the page label of their target (roman and prefixed numbering included), wrap long titles with the number on the last line, link to their heading and are taggedTOC/TOCI/Reference/Link. Page numbers are settled by laying the document out without drawing until the entries stop changing, then once more for real; documents without a table are unaffected. Seedocs/layout.md.
Changed
- Faster first document in a managed process (JIT included): about 16 ms less for the layout invoice (about 130 to 114 ms on the benchmark host).
PdfDocument.CreateEmpty(and with it everyPdfDocumentWriter) builds its cross-reference data directly instead of reading a generated file back, and the first document no longer compiles struct-keyed concurrent and sorted collections: the editable document's object table, the layout font-chain and shaping-option caches, the writer's information, destination and role maps, and the TrueType subsetter's table set and composite check now use plain dictionaries, arrays and snapshots. Output is byte-identical. Peak memory of the first document is about 1 MB lower. - Much less memory when recording large pages into a
PdfDisplayList. The recording now grows in fixed-size chunks instead of doubling and copying its arrays, stores each distinct graphics state once, and shares the outline of a glyph between all its uses instead of copying it for every occurrence (PdfPath.ToOwnedreturns a library-owned, never-modified path as it is). Replay output and the text format are unchanged. On two large real-world files (open, extract text, interpret every page): allocation 2.3 GiB to 0.9 GiB and peak working set about 1.07 GiB to 0.43 GiB for a 137-page, 135,000-item-per-page file; 3.3 GiB to 1.5 GiB and 1.0 GiB to 0.47 GiB for a 154-page file with 300,000 items on a page. A text-heavy page into a display list: 15 ms to 2.2 ms and 32 MB to 4.8 MB allocated. - PDF/A saves embed the standard-14 substitutes (Liberation) as subsets of the characters the document actually shows, not of the whole encoding. The pages, form XObjects, patterns, soft-mask groups, Type 3 glyph procedures and annotation appearances are read to find the codes in use; a font that is never shown, is listed in the form's default resources, or sits where the scan cannot follow keeps its whole encoding. A two-font PDF/A-2b document went from 63,030 to 14,856 bytes.
- CFF subsets (
AddFontwith an OpenType-CFF or bare CFF font) no longer copy the source String INDEX; the subset's own two ROS strings are all it holds. - A glyph that ends in the old accented-character form of
endchar(adx ady bchar achar, Type 2 charstring appendix C) can now be subset. Its composed outline and width are written as a plain path charstring, because a CID-keyed subset has no glyph names to resolve the two StandardEncoding parts with, so the base and accent glyphs are not needed in the subset. A base or accent that is itself composed is still refused with aFontProgramException. - Signing: a time-stamp token returned with indefinite lengths or constructed OCTET STRINGs is re-encoded to DER before it is embedded (ETSI EN 319 142-1 wants a DER SignedData in
/Contents). Only the length framing changes; certificates, signed attributes and every value are copied byte for byte.
Fixed
- Flattening a form in a tagged document left
OBJRentries in the structure tree pointing at the removed widgets, and their entries in the parent tree. Both are removed now, and a structure element that held nothing else (aFormelement, say) goes with them, so the tree stays valid and PDF/UA-1 output stays compliant. The baked-in appearance is marked as an artifact in a tagged document. - Overlays and stamps count the page's open graphics states with the content-stream lexer instead of a scan of the raw bytes, so
qandQinside strings, comments, names, dictionaries and inline image data are no longer counted, and a damaged or unbalanced stream cannot throw the count off. - Text stamps (and any text) set on a diagonal came out of extraction in reversed letter order, and at 135 degrees as single letters. Text rotated by more than 12 degrees from its quantized reading direction is now measured along its own baseline, so its glyphs share one line. Axis-aligned text is unchanged.