Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ISO 32000 conformance

pdfboss keeps a ledger of ISO 32000 clauses in iso32000/Iso32000/Catalogue/, one row per clause with the status pdfboss claims: implemented, incomplete, not implemented or out of scope. Clause numbers follow ISO 32000-1:2008; where ISO 32000-2:2020 numbers a clause differently the row carries both.

The ledger is checked by Lean 4 on every push (make iso32000-gate). Six theorems in iso32000/Iso32000/Gate.lean establish that every row is unique, that every clause of chapters 7 to 14 and the normative annexes has a row speaking to it, that every implemented row is cited by both implementation code and a test in the source tree, that every incomplete row is cited by implementation code and explains what is missing, that every other row explains itself, and that no first-edition citation names a clause the standard does not have. They are decided by Lean's compiled evaluator (native_decide), so they trust the Lean compiler as well as its kernel; the decode-after-encode theorems of the reference filter decoders in iso32000/Iso32000/Reference/, whose vectors the Rust tests replay, are checked by the kernel alone. Citation counts below are the number of source comments naming the clause or a sub-clause of it, in implementation code and in tests.

This page is generated by make iso32000-report. Edit the ledger, not the page.

StatusRows
implemented277
incomplete81
not implemented42
out of scope74

7 Syntax

ClauseFeatureStatusCode citationsTest citationsNote
7.2.2Character Setimplemented72Table 1 white-space bytes, Table 2 delimiters and regular bytes classify every token; the writer escapes delimiter bytes in names (crates/pdfboss-write/src/ser.rs write_name).
7.2.3Commentsimplemented12A percent sign skips to CR or LF everywhere tokens are read; the header and startxref comments are read by their own scanners (document.rs, xref.rs).
7.3.2Boolean Objectsimplemented22The true and false keywords parse to Object::Bool; the writer serializes them (crates/pdfboss-write/src/ser.rs, test ser.rs scalars_serialize_exactly).
7.3.3Numeric Objectsimplemented24Integers read as i64 and reals as f64 with sign and leading-dot forms, malformed runs cleaned leniently; the writer emits plain decimals without exponents (crates/pdfboss-write/src/ser.rs, test ser.rs real_pins_exact_bytes).
7.3.4String Objectsimplemented59Both string forms decode to raw bytes in Object::String; the writer picks the literal or hex form from the bytes (crates/pdfboss-write/src/ser.rs write_string, tests ser.rs strings_use_literal_form_with_escapes and ser.rs strings_fall_back_to_hex_form).
7.3.4.2Literal Stringsimplemented14Balanced parentheses, all Table 3 escapes, one to three digit octal codes, backslash line continuation and EOL normalization to LF are covered, and an unknown escape drops the backslash as the clause requires.
7.3.4.3Hexadecimal Stringsimplemented22White space inside the brackets is skipped and an odd final digit is read as the high nibble; non-hex bytes are skipped leniently (the implementation comment at lexer.rs cites the clause, the tests do not).
7.3.5Name Objectsimplemented11Number-sign escapes decode, the empty name is accepted and a bad escape is kept literally; the writer escapes bytes outside 0x21-0x7E and rejects NUL (crates/pdfboss-write/src/ser.rs and ser.rs, tests ser.rs names_escape_irregular_bytes and ser.rs names_with_nul_bytes_are_rejected).
7.3.6Array Objectsimplemented13Heterogeneous nested arrays parse with a nesting cap and an unterminated array ends leniently at end of input; the writer separates items with single spaces (crates/pdfboss-write/src/ser.rs arrays_separate_items_with_single_spaces).
7.3.7Dictionary Objectsimplemented23Name keys with any value parse, a non-name key is dropped and a value-less key maps to null; a null-valued entry is stored rather than treated as absent and a repeated key keeps the last value, while the writer sorts keys bytewise (crates/pdfboss-write/src/ser.rs dicts_sort_keys_bytewise).
7.3.8Stream Objectsincomplete1111The dictionary plus stream/endstream body, /Length, /Filter and /DecodeParms are handled, but the external-file entries (/F, /FFilter, /FDecodeParms) and /DL are never read; the writer refuses a stream nested inside another object (crates/pdfboss-write/src/error.rs, test ser.rs nested_streams_are_errors).
7.3.8.1Generalincomplete54Of the Table 5 entries only /Length, /Filter and /DecodeParms are consumed, so stream data kept in an external file (/F with /FFilter and /FDecodeParms) is never fetched and /DL is ignored.
7.3.8.2Stream Extentimplemented15The EOL after the stream keyword (CRLF or LF, a lone CR tolerated) is skipped, a direct or indirect /Length is verified against the following endstream, and a wrong or missing length falls back to a scan for endstream that trims one trailing EOL.
7.3.9Null Objectimplemented23The null keyword parses to Object::Null and a reference to a missing object resolves to null; a null /Filter reads as no filter.
7.3.10Indirect Objectsimplemented48Object headers, endobj (lenient when missing), the N G R lookahead and the rule that a reference to a missing object is null are all covered, with generation mismatches tolerated; the writer emits N G R and numbers objects densely (crates/pdfboss-write/src/ser.rs refs_serialize_as_num_gen_r).
7.4.2ASCIIHexDecode Filterimplemented37Hex pairs with white space skipped, the > EOD marker and an odd final digit padded with zero decode, and the AHx abbreviation is accepted.
7.4.3ASCII85Decode Filterimplemented37Five-character groups, the z shorthand, the > EOD marker, partial final groups and interior white space decode, with a leading < tolerated.
7.4.4LZWDecode and FlateDecode Filtersimplemented1329Both filters decode with the Table 8 parameters and the PNG and TIFF predictors at every bit depth (7.4.4.1 to 7.4.4.4); the writer compresses streams with Flate (crates/pdfboss-write/src/writer.rs compress_into, test writer.rs compressed_stream_round_trips).
7.4.4.1Generalimplemented25FlateDecode accepts zlib (RFC 1950) and raw deflate data, returns the decoded prefix of a truncated stream and caps output size; LZWDecode is assessed under 7.4.4.2.
7.4.4.2Details of LZW Encodingimplemented25Codes grow from 9 to 12 bits, clear-table (256) and EOD (257) codes, the KwKwK case and both EarlyChange width schedules decode; corrupt or truncated data keeps the decoded prefix.
7.4.4.3LZWDecode and FlateDecode Parametersimplemented27Table 8 parameters (/Predictor, /Colors, /BitsPerComponent, /Columns, /EarlyChange) are read with their defaults, /DecodeParms arrays stay aligned with the /Filter array and indirect values resolve.
7.4.4.4LZW and Flate Predictor Functionsimplemented39PNG predictors 10 to 15 (per-row filter byte, all five filter types, sub-byte samples) and TIFF predictor 2 at 1, 2, 4, 8 and 16 bits per component decode (crates/pdfboss-core/src/filters/predictor.rs tiff_horizontal: bytes in place, big-endian 16-bit components, packed sub-byte components per colour with a row's padding bits left as stored). Tests: predictor.rs tiff_horizontal_diff_round_trips, tiff_16bit_samples_add_big_endian, tiff_4bit_samples_add_within_packed_bytes, tiff_2bit_samples_add_per_colour, tiff_1bit_samples_add_modulo_two, png_every_row_type_round_trips and png_row_length_rounds_up_for_sub_byte_samples.
7.4.5RunLengthDecode Filterimplemented26Literal runs (0-127), repeat runs (129-255) and the 128 EOD byte decode, a missing EOD is tolerated and output size is capped.
7.4.6CCITTFaxDecode Filterimplemented1017Group 4 (K<0), pure 1-D Group 3 (K=0) and mixed Group 3 (K>0) decode to packed 1-bit samples, reading /K, /Columns, /Rows, /EncodedByteAlign, /BlackIs1 and /EndOfLine with Table 11 defaults; /EndOfBlock and /DamagedRowsBeforeError are described in comments but never read, the row count being inferred from the data instead.
7.4.7JBIG2Decode Filterimplemented28The PDF embedded-stream organization with /JBIG2Globals decodes through a cleanroom codec (generic, refinement, symbol/text, pattern/halftone regions and MMR) and 1 bits are inverted to DeviceGray ink as the clause requires; the filter must be last in the chain.
7.4.8DCTDecode Filterimplemented611The filter chain passes JPEG bytes through (crates/pdfboss-core/src/filters/mod.rs decode_stream) and the image layer decodes them with the external jpeg-decoder 0.3 crate (crates/pdfboss-render/src/image.rs decode_jpeg: gray, RGB and CMYK layouts, baseline and progressive). The colour transform follows Table 13: the Adobe APP14 flag when present (adobe_transform), else /ColorTransform from the trailing filter's decode parameters (dct_color_transform), else the crate's default of YCbCr for three components and none for four (jpeg_color_transform). Four-component samples are ink values as stored, the crate's 255 - ink output undone, and /Decode applies per component. Two-component JPEGs, which the clause allows, are refused by the crate; and the image's /ColorSpace is not consulted for JPEG data, which takes the device conversion for its component count (a limitation of the colour space rows under 8.6, not of the filter). Tests: image.rs dct_color_transform_zero_keeps_three_components_as_stored, dct_adobe_marker_overrides_color_transform, dct_color_transform_one_reads_four_components_as_ycck, dct_cmyk_samples_are_direct_ink_values.
7.4.9JPXDecode Filterimplemented1615A cleanroom JPEG 2000 decoder handles the codestream, the colour space comes from the codestream when the dictionary has none, /Decode is ignored except for image masks, /SMaskInData 1 and 2 (image.rs) and Indexed over JPX are honoured, and the filter must be last in the chain.
7.4.10Crypt Filterimplemented66The decryptor reads a stream's own /Crypt filter when it loads the object (crates/pdfboss-core/src/crypt.rs stream_crypt_filter): /Name Identity, or no /Name, leaves the data as stored, a name from /CF decrypts with that filter's method and the file key as is, as the clause asks, and a name matching no /CF entry leaves the data as stored; decode_stream then treats Crypt as a no-op (crates/pdfboss-core/src/filters/mod.rs). Tests: crypt.rs a_stream_crypt_filter_naming_identity_is_left_as_stored, a_stream_crypt_filter_without_a_name_means_identity and a_stream_crypt_filter_naming_a_cf_entry_uses_the_file_key_as_is; filters/mod.rs crypt_filter_leaves_data_as_stored and crypt_filter_first_in_a_chain_is_a_no_op.
7.5.2File Headerimplemented22The %PDF-M.m header is searched in the first 1 KiB and defaults to 1.4 when absent, without shifting xref offsets for a header not at byte 0 (the recovery scan covers that case); the writer emits the header and a binary comment line (crates/pdfboss-write/src/writer.rs write_header, test writer.rs table_mode_minimal_document_loads).
7.5.3File Bodyimplemented25Indirect objects are read at their cross-reference offsets, and when the table is unusable a whole-file scan for N G obj headers rebuilds it with the last occurrence winning.
7.5.4Cross-Reference Tableimplemented54Subsection headers and n/f entries are read token-wise so 19 and 21 byte lines load as well as 20 byte ones, though the free-entry linked list is not tracked; the writer emits exact 20-byte entries (crates/pdfboss-write/src/writer.rs emit_table and writer.rs table_offset, test writer.rs table_offsets_past_ten_digits_are_rejected).
7.5.5File Trailerimplemented35The startxref offset is found in the last 1 KiB widening to 64 KiB and the trailer's /Size, /Root, /Info, /ID and /Encrypt are consumed, with %%EOF itself not required; the writer emits the trailer, startxref and %%EOF (crates/pdfboss-write/src/writer.rs trailer_dict).
7.5.6Incremental Updatesimplemented43The newest section wins and /Prev is followed with a visited-offset loop guard; the writer appends update sections chained by /Prev (crates/pdfboss-write/src/update.rs cites the clause; tests crates/pdfboss-write/tests/update_append.rs two_appends_chain and tests/test_update.py test_update_appends_and_preserves_bytes).
7.5.7Object Streamsimplemented63The /N and /First header pairs are parsed once and members are addressed by index and never decrypted separately, while /Extends is not needed for reading and not read; the writer packs objects into streams (crates/pdfboss-write/src/writer.rs build_objstm, test writer.rs object_streams_pack_and_resolve).
7.5.8Cross-Reference Streamsimplemented911Cross-reference streams are decoded through the ordinary filter chain and their dictionary serves as the trailer; the writer emits them by default (crates/pdfboss-write/src/writer.rs emit_stream, test writer.rs stream_mode_minimal_document_loads).
7.5.8.2Cross-Reference Stream Dictionaryimplemented12/W field widths of 0 to 8 bytes, /Size, /Index (defaulting to [0 Size]) and /Prev are read; /Type /XRef is not checked.
7.5.8.3Cross-Reference Stream Dataimplemented23Type 0, 1 and 2 entries are decoded as big-endian fields per /W, a zero-width type field defaults to type 1, unknown types read as free and truncated data ends the table leniently.
7.5.8.4Compatibility with Applications That Do Not Support Compressed Reference Streamsimplemented43A classic trailer's /XRefStm section merges ahead of its own table and before /Prev is followed (the fixture crates/pdfboss-testkit/src/lib.rs hybrid_doc documents the clause); the writer appends a classic table to a hybrid base (crates/pdfboss-write/tests/update_append.rs hybrid_base_appends_a_classic_table).
7.6.2General Encryption Algorithmimplemented46Algorithm 1 per-object keys (with the sAlT suffix for AES-128) and the direct AES-256 file key decrypt strings and stream bodies, object-stream members are left alone and /Encrypt and /ID are never decrypted; the writer encrypts the same way with AES-256 (crates/pdfboss-write/src/writer.rs new_encrypted; tests crates/pdfboss-write/tests/encrypt.rs encrypt_dict_strings_are_never_encrypted and encrypt.rs xref_stream_is_never_encrypted).
7.6.3Standard Security Handlerincomplete1525V1/V2 RC4, V4 with the V2 or AESV2 crypt filter and V5 R5/R6 AES-256 files open under user or owner passwords, but a V4 file whose stream crypt filter is /Identity is refused (see 7.6.3.2 and 7.6.5); the writer produces AES-256 R6 files (crates/pdfboss-core/src/crypt.rs aes256_encrypt_dict).
7.6.3.2Standard Encryption Dictionaryimplemented1110/Filter /Standard, /V, /R, /Length, /P, /O, /U, /OE, /UE, /Perms, /EncryptMetadata, /CF, /StmF, /StrF and /EFF are read (crates/pdfboss-core/src/crypt.rs from_standard_with_password and CryptFilters::parse; tests crypt.rs string_filter_is_chosen_independently_of_the_stream_filter and embedded_file_filter_applies_to_embedded_file_streams); /SubFilter names the formats of public-key handlers (adbe.pkcs7.*), so a reader with only the Standard handler has nothing to read there.
7.6.3.3Encryption Key Algorithmimplemented115Algorithm 2 with the 32-byte pad, the 50-round MD5 for R3 and up and the R4 EncryptMetadata-false suffix derives the RC4/AES-128 key, and Algorithms 2.A and 2.B recover the AES-256 key for R5 and R6; every RC4 fixture is /V 2 /R 3 or /V 4 /R 4, so the R2 40-bit branch (crypt.rs) has no test.
7.6.3.4Password Algorithmsimplemented96The user password is verified through /U (Algorithms 4 and 5), the owner password recovers the padded user password from /O (Algorithm 7) and the R5/R6 checks use /O, /U, /OE, /UE and /Perms, while Algorithm 3 (computing /O) lives only in fixtures (crates/pdfboss-testkit/src/crypt.rs) and the R2 branch (crypt.rs) is untested; the writer computes /O, /U, /OE, /UE and /Perms for R6 (crates/pdfboss-core/src/crypt.rs, test crates/pdfboss-write/tests/encrypt.rs owner_password_also_opens).
7.6.4Public-Key Security Handlersout of scope120Only /Filter /Standard is accepted (crypt.rs) and every other handler fails with Error::Encrypted, because certificate and private-key handling is deliberately left out of pdfboss.
7.6.5Crypt Filtersimplemented713The /CF filters are read by name with /CFM V2, AESV2 or AESV3, /StmF, /StrF and /EFF pick among them with the standard Identity filter as the default and as a pass-through (crates/pdfboss-core/src/crypt.rs CryptFilters::parse), the crypt filter /Length sets the V4 key size (v4_key_length), /CFM None or an unknown method refuses the file as Error::Encrypted, and a stream's own /Crypt filter overrides /StmF (see 7.4.10); /EFF is recognised through /Type /EmbeddedFile, which Table 45 makes optional, so an untyped embedded file stream takes /StmF. /AuthEvent is not consulted: the password arrives when the document opens, Table 25 says readers ignore the entry for the /StmF and /StrF filters, and the Standard handler has the one file key for /EFF too. Tests: crypt.rs identity_stream_filter_leaves_streams_as_stored, string_filter_is_chosen_independently_of_the_stream_filter, embedded_file_filter_applies_to_embedded_file_streams, crypt_filter_length_sets_the_key_size and aesv3_identity_string_filter_leaves_strings_as_stored.
7.7.2Document Catalogincomplete63/Pages, /OCProperties, /StructTreeRoot, /Names (crates/pdfboss-core/src/names.rs), /Dests (destination.rs), /Outlines (outline.rs) and /PageLabels (page_label.rs) are read from the catalog; /Version never overrides the header and /Metadata, /MarkInfo, /Lang, /AcroForm, /PageMode and /PageLayout are not consumed, while the writer emits /Pages, /Outlines, /Metadata, /Names, /PageLabels and viewer entries (crates/pdfboss-write/src/pdf.rs and pdf.rs).
7.7.3Page Treeincomplete1918The tree walk, inheritance and page count are handled, with page-level gaps listed under 7.7.3.3; the writer builds page trees (crates/pdfboss-write/src/pdf.rs, crates/pdfboss-write/src/assemble.rs cites the clause).
7.7.3.2Page Tree Nodesimplemented26/Kids are walked depth-first in document order, /Count is used when plausible, /Type /Page ends descent and cycles and depth are capped; /Parent is not needed for reading.
7.7.3.3Page Objectsincomplete96/Contents (single stream or array joined by newlines with nulls skipped), the five boxes with Table 30 defaults, /Rotate, /Resources, /Annots and /StructParents are consumed; /UserUnit and the page-level /Group transparency dictionary are never read, and the viewer entries (/Dur, /Trans, /AA, /Tabs) are ignored.
7.7.3.4Inheritance of Page Attributesimplemented54/Resources, /MediaBox, /CropBox and /Rotate flow down the /Pages nodes with the nearest ancestor winning and the non-inheritable boxes staying on the leaf (the fixture at parity.rs cites the clause, the test does not).
7.7.4Name Dictionaryimplemented73crates/pdfboss-core/src/names.rs: NameTree names the ten trees of Table 31 by their dictionary key, name_tree_root_with reads the catalog /Names entry and the tree's root (direct or indirect, anything that is not a dictionary reads as absent), named_with resolves the object one name designates through tree.rs lookup, names_with lists a tree's pairs; Document::named and Document::names on the sync side, AsyncDocument::named and AsyncDocument::names on the async side (tests names.rs named_objects_come_from_the_catalog_name_dictionary, documents_without_a_name_dictionary_have_no_names, every_table_31_tree_has_its_dictionary_key; crates/pdfboss-aio/tests/parity.rs names fixture). The writer emits /Names /EmbeddedFiles (crates/pdfboss-write/src/pdf.rs, test crates/pdfboss-write/tests/roundtrip.rs attachments_sort_by_name_and_round_trip_bytes). Consumers of the individual trees (destinations 12.3.2.3, embedded files 7.11.4) are their own rows.
7.8.2Content Streamsimplemented25Operands followed by an operator are tokenized with unknown operators skipped, inline images with their abbreviations are parsed, and a /Contents array is concatenated with a newline between parts; the writer produces content streams (crates/pdfboss-write/src/canvas.rs and crates/pdfboss-write/src/content.rs).
7.8.3Resource Dictionariesimplemented34Category/name lookups walk the resource chain innermost first (form, then caller, then page, with a page missing /Resources inheriting from /Pages), and text extraction and rendering apply the same rule.
7.9.2String Object Typesimplemented912Text strings decode from UTF-16BE, UTF-8 or PDFDocEncoding by their byte-order mark (crates/pdfboss-core/src/object.rs decode_text_string) and byte strings stay raw; the writer encodes text strings as ASCII or UTF-16BE (crates/pdfboss-write/src/pdf.rs text_string cites 7.9.2.2).
7.9.2.2Text String Typeimplemented33UTF-16BE with a BOM and the PDF 2.0 UTF-8 BOM form decode with lone surrogates and odd trailing bytes replaced by U+FFFD, while language escape sequences inside UTF-16 text are kept unchanged; the writer writes ASCII plainly and everything else as UTF-16BE with a BOM (crates/pdfboss-write/src/pdf.rs).
7.9.2.3PDFDocEncoded String Typeimplemented25A string without a BOM decodes through the PDFDocEncoding table (crates/pdfboss-core/src/object.rs pdf_doc_char): the accents at 0x18-0x1F, the punctuation and ligatures at 0x80-0x9E and the Euro at 0xA0 map to their characters, the undefined codes become U+FFFD, and every other code agrees with Latin-1 (tests object.rs decode_pdf_doc_encoding_accents_at_0x18, decode_pdf_doc_encoding_punctuation_at_0x80, decode_pdf_doc_encoding_undefined_codes_are_replaced).
7.9.2.4Byte String Typeimplemented22Strings are kept as raw bytes in Object::String and exposed through as_str_bytes with no character interpretation, which is what /ID, /O and /U consumers rely on.
7.9.3Text Streamsnot implemented00No consumer reads a text stream (used by JavaScript and similar viewer features); decode_text_string is only applied to string objects.
7.9.4Datesimplemented118One Date type in the core crate (crates/pdfboss-core/src/date.rs, re-exported by pdfboss-write) parses the D:YYYYMMDDHHmmSSOHH'mm form with the clause's defaults and field ranges, taking the deviations producers write (a trailing apostrophe, Z followed by offset digits, an offset without apostrophes, no D: prefix) and refusing out-of-range fields; it formats back to the D: form and to ISO 8601. Metadata keeps /CreationDate and /ModDate as written and reads them through creation_date_parsed and mod_date_parsed (document.rs); the CLI info command prints a parsed date as ISO 8601. The Python metadata dict still carries the strings as written. Tests: date.rs parse_pdf_the_clause_example, parse_pdf_partial_fields_take_their_defaults, parse_pdf_accepts_the_common_deviations, parse_pdf_refuses_out_of_range_fields; document.rs metadata_dates_parse_per_the_date_clause.
7.9.5Rectanglesimplemented23Four-number arrays with indirect elements resolved are normalized so that any two opposite corners define the rectangle.
7.9.6Name Treesimplemented54crates/pdfboss-core/src/tree.rs lookup and entries walk a tree of any depth: TreeKey for byte-string keys (Vec, so keys compare byte by byte with shorter keys first, whatever their encoding), /Kids descended by /Limits with a node without readable limits searched regardless, /Names read as key-value pairs with values left unresolved so streams stay indirect, malformed nodes and pairs skipped, a visited set and a 4096-node budget ending cycles (tests tree.rs name_lookup_descends_kids_by_limits, name_keys_compare_bytewise_shorter_first, entries_list_every_leaf_in_order, malformed_nodes_and_cycles_end_the_walk). No catalog /Names tree is consumed yet (7.7.4); the writer emits a single flat /Names leaf without /Kids or /Limits, which the clause allows for a root (crates/pdfboss-write/src/pdf.rs).
7.9.7Number Treesimplemented53crates/pdfboss-core/src/tree.rs lookup and entries with integer keys (TreeKey for i64): /Nums leaves, /Kids subtrees and /Limits under a visited set and a node budget, used by the structure /ParentTree lookup (structure.rs parent_array; tests tree.rs number_lookup_reads_nums_leaves, structure.rs parent_tree_kids_and_limits_are_descended); the writer emits a /PageLabels /Nums tree (crates/pdfboss-write/src/pdf.rs, test crates/pdfboss-write/tests/roundtrip.rs page_labels_resolve_roman_front_matter_and_offset_decimal).
7.10.2Type 0 (Sampled) Functionsimplemented27/Size, /BitsPerSample of 1 to 32, /Encode and /Decode with their defaults and multilinear interpolation between the 2^m nearest samples are covered, and /Order 3 is not honoured (the clause permits linear interpolation).
7.10.3Type 2 (Exponential Interpolation) Functionsimplemented23C0 + x^N (C1 - C0) with the C0, C1 and N defaults evaluates for shadings and tint transforms, the first input only as the clause defines.
7.10.4Type 3 (Stitching) Functionsimplemented22/Functions, /Bounds (k-1 of them, checked) and /Encode with its default remap the input into the selected subfunction, with subfunctions loaded under a MAX_FUNCTIONS budget.
7.10.5Type 4 (PostScript Calculator) Functionsimplemented411The full Table 42 operator set (arithmetic, relational, boolean, bitwise, stack and if/ifelse compiled to jumps) is parsed from the brace-delimited program with comments skipped, and the result feeds shadings and Separation/DeviceN tint transforms.
7.10.5.2Errors in Type 4 Functionsimplemented24Syntax errors reject the function at load time, and runtime failures (stack underflow or overflow, type mismatch, division by zero and other domain errors, runaway programs) clamp every output to the bottom of /Range rather than aborting the paint.
7.11.2File Specification Stringsimplemented32crates/pdfboss-core/src/embedded_file.rs spec_components splits a file specification string on every solidus not preceded by a reverse solidus and removes the reverse solidi that escape a solidus or each other, an absolute specification starting with an empty component (test embedded_file.rs file_specification_strings_split_on_unescaped_solidus); FileSpec keeps /F as stored and /UF decoded, and names the file from /UF then /F. The components are handed on as bytes without conversion to a platform path, as 7.11.2.1 asks; the writer stores the attachment name as both /F and /UF (crates/pdfboss-write/src/pdf.rs embedded_files_dict).
7.11.3File Specification Dictionariesimplemented42crates/pdfboss-core/src/embedded_file.rs file_spec_with reads a /Type /Filespec dictionary (/F, /UF, /Desc, /FS, /V, and the /EF stream chosen from /UF, /F or the obsolescent /DOS, /Mac and /Unix) or a bare string as the /F of a specification; /ID, /RF and /CI are not read (tests embedded_file.rs a_file_specification_names_its_file_from_uf_then_f_then_the_legacy_keys, embedded_files_come_from_the_name_tree). The writer emits /Type /Filespec with /F, /UF, /Desc and /EF (crates/pdfboss-write/src/pdf.rs, test crates/pdfboss-write/tests/roundtrip.rs attachments_sort_by_name_and_round_trip_bytes).
7.11.4Embedded File Streamsimplemented83crates/pdfboss-core/src/embedded_file.rs embedded_files_with lists the catalog's /EmbeddedFiles name tree in tree order through names.rs, each EmbeddedFile carrying the decoded name, its FileSpec and the stream's /Subtype (Table 45) and /Params /Size, /CreationDate, /ModDate and /CheckSum (Table 46); embedded_file_data_with decodes the stream through the source's filter chain, MissingKey(EF) when the specification embeds nothing; Document::embedded_files and embedded_file_data, AsyncDocument twins (tests embedded_file.rs embedded_files_come_from_the_name_tree, documents_without_embedded_files_have_none; crates/pdfboss-aio/tests/parity.rs names fixture). Related files arrays (7.11.4.2) and the Mac subdictionary (Table 47) are not read; file attachment annotations belong to 12.5.6.15. The writer emits /Type /EmbeddedFile streams with /Subtype and /Params /Size and /ModDate but no related-files array (crates/pdfboss-write/src/pdf.rs embedded_files_dict; tests crates/pdfboss-write/tests/roundtrip.rs attachment_params_size_and_conditional_moddate and tests/test_write.py test_multiple_attachments_and_page_labels_compose). The CLI and Python bindings do not list attachments yet.
7.11.5URL Specificationsimplemented21FileSpec::url returns the /F of a specification whose /FS is URL, read as the 7-bit ASCII the clause requires (crates/pdfboss-core/src/embedded_file.rs; test url_specifications_name_a_url), so a URL file attachment or action target reaches Python as FileSpec.url; the writer does not emit URL specifications (crates/pdfboss-write/src/pdf.rs writes a /URI action, which belongs to chapter 12).
7.11.6Collection Itemsnot implemented00No /CI collection item dictionaries are read or written.
7.11.7Maintenance of File Specificationsout of scope00This clause describes how applications that move or rename files keep specifications valid, a file-management duty pdfboss has no role in.
7.12.2Developer Extensions Dictionaryimplemented12crates/pdfboss-core/src/extension.rs extensions_with reads the catalog's /Extensions dictionary into one DeveloperExtension per developer prefix (Table 50: the prefix is the key, /BaseVersion a name, /ExtensionLevel an integer; the dictionary's own /Type entry and an entry missing either value are skipped, indirect objects followed although the clause asks for direct ones, the result sorted by prefix since a dictionary keeps no order); Document::extensions and AsyncDocument::extensions expose it and the CLI's info command prints each extension after the version (tests extension.rs the_catalog_extensions_are_read_sorted_by_prefix with the clause's EXAMPLE 3, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_lists_developer_extensions). Not exposed to Python; pdfboss-write emits no extensions dictionary.
7.12.3BaseVersionimplemented22DeveloperExtension::base_version keeps the /BaseVersion name as written and DeveloperExtension::base_version_numbers reads it as the two integers around a period the clause prescribes, not as a real number, None for anything else (test extension.rs base_versions_are_two_integers). The clause's requirement that the base version not exceed the header or catalog version is not checked.
7.12.4ExtensionLevelimplemented11DeveloperExtension::extension_level carries the /ExtensionLevel integer of each developer extensions dictionary, read with 7.12.2 (test extension.rs the_catalog_extensions_are_read_sorted_by_prefix: the levels 3 and 1002 of the clause's EXAMPLE 3). Ordering the levels over time is the developer's business and nothing pdfboss checks.

8 Graphics

ClauseFeatureStatusCode citationsTest citationsNote
8.2Graphics Objectsimplemented35Path, text, XObject, inline image and shading objects all execute, BX/EX parse and unknown operators are skipped; the object nesting rules bind writers and are not validated on read.
8.3.2.2Device Spaceimplemented23The crop box, /Rotate and the scale factor build the base matrix with the y flip that puts the first device row at the top.
8.3.2.3User Spaceimplemented22The page's default user space is the base matrix, cm concatenates onto the CTM and q/Q restores it.
8.3.2.4Other Coordinate Spacesimplemented24Form, image (unit square), pattern, text and Type3 glyph spaces each map through their own matrix onto the CTM.
8.3.2.5Relationships among Coordinate Spacesimplemented22Transformations compose by matrix concatenation from the innermost space out to device space.
8.3.3Common Transformationsimplemented21Translation, scaling and rotation constructors follow the clause's matrix forms; skew is expressed through the general six-element matrix.
8.3.4Transformation Matricesimplemented24The six-element matrix, its concatenation order and inversion follow the clause; a non-finite cm operand is skipped.
8.4.1Generalincomplete22Table 52's CTM, clip, colour spaces and colours, line width/cap/join/miter/dash, alpha, blend mode, soft mask and text state are tracked with the clause's initial values; rendering intent, stroke adjustment and alpha source are not, and none of Table 53 (overprint, black generation, undercolor removal, transfer, halftone, flatness, smoothness) is tracked.
8.4.2Graphics State Stackimplemented25q pushes and Q pops the whole state including clip and soft mask, a form's unbalanced Q cannot pop its caller's state (case form_with_unbalanced_restore), and nesting is capped at 64 levels.
8.4.3.2Line Widthimplemented33The pen is a user-space circle carried through the CTM (anisotropic scaling widens it correctly) and a zero or sub-pixel width paints the thinnest visible hairline; the writer emits w through canvas.rs set_line_width.
8.4.3.3Line Cap Styleimplemented75J and /LC set stroke.rs LineCap, which stroke_path reads at every run end: butt caps stop at the endpoint, round caps add the pen disc, projecting square caps carry the band on by half the line width in user space (stroke.rs butt_caps_stop_at_the_endpoints, square_caps_extend_the_band_by_half_the_width, executor.rs line_caps_follow_j_and_lc); zero-length dashes take the cap style along the path's direction; a J operand outside 0 to 2 sets butt; the writer emits J through canvas.rs set_line_cap.
8.4.3.4Line Join Styleimplemented77j and /LJ set stroke.rs LineJoin, which stroke_path reads at every interior vertex and at the start of a closed subpath: a miter carries the two bands' outer edges to their meeting point, a bevel cuts across their ends, a round join adds the pen disc; collinear segments get no join and a segment turning straight back gets only the round disc (stroke.rs miter_joins_fill_the_outer_corner, bevel_joins_cut_the_corner, round_joins_keep_the_pen_disc, joins_follow_a_flipped_matrix, executor.rs line_joins_follow_j_lj_m_and_ml); a j operand outside 0 to 2 sets miter; the writer emits j through canvas.rs set_line_join.
8.4.3.5Miter Limitimplemented63M and /ML set miter_limit (values under 1 are ignored, default 10); stroke.rs miter_fits measures the two segment directions in user space and cuts a miter to a bevel when 1/sin(angle/2) is over the limit (stroke.rs the_miter_limit_cuts_sharp_joins_to_bevels, executor.rs line_joins_follow_j_lj_m_and_ml); the writer emits M through canvas.rs set_miter_limit.
8.4.3.6Line Dash Patternimplemented36The dash array and phase are measured in user space through the inverse CTM, an empty array restores solid strokes and /D in an ExtGState is honored; the writer emits d through canvas.rs set_dash.
8.4.4Graphics State Operatorsimplemented23All eleven operators parse and set their state, and ri and i are accepted with no effect as the clause allows.
8.4.5Graphics State Parameter Dictionariesincomplete47/LW /LC /LJ /ML /D /CA /ca /BM /SMask are applied from the named resource; /RI /Font /OP /op /OPM /TR /TR2 /HT /FL /SM /SA /BG /BG2 /UCR /UCR2 /TK /AIS are ignored; the writer emits /ca /CA /BM only.
8.5.2.1Generalimplemented34All seven construction operators build subpaths with the current point semantics of Table 59, re starts a closed rectangle subpath, and a segment before any m starts at the origin leniently instead of erroring.
8.5.2.2Cubic Bezier Curvesimplemented24c, v and y build the four control points per Table 59 and the curve is flattened adaptively within tolerance; the writer emits c through canvas.rs curve_to.
8.5.3.1Generalimplemented23Every painting operator ends the path, the closing variants close the subpath first, F is accepted as f, and n discards the path while still applying a pending clip.
8.5.3.2Strokingimplemented49Strokes are offset quads under the full CTM with dashes, stroke patterns and the cap style applied; path.rs keeps a degenerate subpath (a closed single point or coincident points), which stroke_path paints as a filled disc under round caps and not at all under butt or square caps, while a single-point open subpath paints nothing and zero-length dashes keep their caps (stroke.rs degenerate_subpaths_paint_a_dot_only_under_round_caps, zero_length_dashes_take_the_cap_style, executor.rs tests of the same names, path.rs degenerate_subpaths_are_kept_for_the_stroker); joins follow 8.4.3.4 and 8.4.3.5.
8.5.3.3.1Generalimplemented22Open subpaths are implicitly closed before filling and coverage is anti-aliased per pixel.
8.5.3.3.2Nonzero Winding Number Ruleimplemented32f, B and b accumulate signed winding per scanline span and paint where it is nonzero.
8.5.3.3.3Even-Odd Ruleimplemented23f*, B* and b* count crossings and paint where the count is odd; the writer emits f* through canvas.rs fill_even_odd.
8.5.4Clipping Path Operatorsimplemented34W and W* mark a pending clip that intersects the current clip after the following painting operator (n included), nested clips and a clip straddling a form (cases nested_clips, clip_straddling_a_form) intersect correctly, and the writer emits W n and W* n through canvas.rs clip.
8.6.2Colour Valuesimplemented23Each family declares its component count and decode range, operands are clamped to the space's range, and every colour ends as RGB for compositing.
8.6.3Colour Space Familiesimplemented22The device, CIE-based (CalGray, CalRGB, Lab, ICCBased) and special (Indexed, Separation, DeviceN, Pattern) families all parse from name or array form, with unknown names falling back to DeviceGray.
8.6.4.2DeviceGray Colour Spaceimplemented12g/G and DeviceGray image samples convert gray to equal RGB channels (case device_colorspaces).
8.6.4.3DeviceRGB Colour Spaceimplemented12rg/RG and DeviceRGB image samples pass through unchanged.
8.6.4.4DeviceCMYK Colour Spaceincomplete13k/K and CMYK images convert, but by a deliberate multiplicative formula (1-c)(1-k) rather than the clause's 1 - min(1, c + k), chosen so deep colours keep their hue.
8.6.5.2CalGray Colour Spacesimplemented22/Gamma and /WhitePoint are read and gray decodes through XYZ to sRGB; /BlackPoint is ignored and a bare /CalGray name reads as DeviceGray.
8.6.5.3CalRGB Colour Spacesimplemented12Per-channel /Gamma and the column-major /Matrix map to XYZ with identity defaults; /BlackPoint is ignored and a bare /CalRGB name or a dictionary without /WhitePoint falls back to DeviceRGB.
8.6.5.4Lab Colour Spacesimplemented34/Range defaults to plus or minus 100, the default image decode is the Lab range, and the inverse transfer follows the clause's two-branch form (the source comment at math.rs cites the clause).
8.6.5.5ICCBased Colour Spacesimplemented16Matrix/TRC, lut8/lut16 and lutAtoB profiles transform through the PCS, sRGB-equivalent profiles take the device path, /N mismatches and unparseable profiles fall back to /N or /Alternate, and /Range and /Metadata are ignored.
8.6.5.6Default Colour Spacesimplemented32executor.rs device_or_default replaces a device space selected by g/G, rg/RG, k/K or a cs/CS device name (a pattern space's base included) with the /DefaultGray, /DefaultRGB or /DefaultCMYK entry of the /ColorSpace resources when that entry has the same component count and is neither Indexed nor Lab, passing the component values through; image_colorspace does the same for image XObjects and inline images (executor.rs default_colour_spaces_remap_device_colours, an_unsuitable_default_colour_space_is_ignored); the defaults are resolved once per content stream and inherited through the resource chain; not remapped: the initial colour of a page or form, shading dictionaries, the base of an Indexed space and the alternate of a Separation or DeviceN.
8.6.5.7Implicit Conversion of CIE-Based Colour Spacesimplemented23Every CIE-based colour converts to the sRGB output space through XYZ with Bradford adaptation of the declared white point; the output space is fixed to sRGB.
8.6.5.8Rendering Intentsincomplete21ri parses and is accepted, but neither it nor /RI changes anything; the ICC pipeline uses one fixed transform regardless of the requested intent.
8.6.6.2Pattern Colour Spacesincomplete25/Pattern and [/Pattern base] spaces select tiling and shading patterns for path fills and strokes, but text glyphs (executor.rs) and image stencils (executor.rs) painted with a pattern colour get a mid-gray stand-in and a report entry.
8.6.6.3Indexed Colour Spacesimplemented35String and stream lookup tables over any base (Lab and nested Indexed included) resolve, out-of-range indices clamp, and the image decode default is [0 2^bpc-1] (case indexed_image_and_stencil).
8.6.6.4Separation Colour Spacesimplemented1010The tint transform (function types 0, 2, 3, 4) runs into the alternate space with initial tint 1.0 (crates/pdfboss-render/src/color.rs parse_with and to_rgb, executor.rs initial_components). The special colorant All ignores the alternate space and the transform and puts the tint on every colorant, gray 1 - tint on the composite output (ColorSpace::SeparationAll). None never marks the page: fills, strokes and text carry a zero-alpha colour (executor.rs fill_rgba8, stroke_rgba8), a stencil mask returns before painting (blit_image), an image in the space returns from image.rs draw, and a shading from shading.rs paint and paint_background. Not caught: a Type3 glyph that sets its own colour inside a None fill, and an Indexed space over a None base; a DeviceN whose colorants are all None belongs to 8.6.6.5. Tests: executor.rs separation_none_never_marks_the_page and separation_all_paints_the_tint_on_every_colorant, color.rs separation_all_and_none_ignore_the_alternate_and_transform.
8.6.6.5DeviceN Colour Spacesincomplete14n-colourant tint transforms evaluate into the alternate space, but spaces with more than 8 colourants (the clause allows 32) fall back to an ink approximation and the attributes dictionary is ignored.
8.6.6.6Multitone Examplesimplemented11Informative examples of 8.6.6.5; a two-colourant DeviceN with a sampled tint transform (the duotone shape) evaluates through the general DeviceN path.
8.6.7Overprint Controlnot implemented00Nothing reads /OP, /op or /OPM; every paint composites over the backdrop in RGB with no overprint simulation.
8.6.8Colour Operatorsimplemented35All twelve operators set the stroking or nonstroking space and colour, black DeviceGray is the initial colour and cs/CS applies the space's initial colour; the writer emits rg/RG/g/G/k/K through canvas.rs set_fill and 219 set_stroke.
8.7.2General Properties of Patternsimplemented23The pattern matrix maps pattern space to the default space of the page or of the form whose resource it is, not to the CTM at painting time, and loaded patterns are cached per document.
8.7.3.1Generalimplemented26Table 75's /BBox, /XStep, /YStep, /Resources and /Matrix are read (a zero or missing step falls back to the bbox size), /TilingType is ignored as a spacing hint, and the tile count is capped at 65536 with a report.
8.7.3.2Coloured Tiling Patternsimplemented23/PaintType 1 cells run with their own colour operators and inherit the paint site's blend mode and soft mask.
8.7.3.3Uncoloured Tiling Patternsimplemented22/PaintType 2 cells paint in the scn colour given in the [/Pattern base] underlying space with the cell's own colour operators locked out.
8.7.4.1Generalincomplete13/Shading and /Matrix of a Table 76 pattern dictionary are read and the fill paints through the path with /Background, but the pattern's own /ExtGState entry is not applied.
8.7.4.2Shading Operatorimplemented24sh paints the named shading over the current clip intersected with its /BBox, ignores /Background as the clause requires, and reports a singular CTM instead of painting; the writer serializes sh at content.rs.
8.7.4.3Shading Dictionariesimplemented14/ShadingType, /ColorSpace, /Background and /BBox are honored (/Background only behind pattern fills, per Table 78); /AntiAlias is ignored as a hint.
8.7.4.4Colour Space: Special Considerationsincomplete22A /Function turns colour data into one parametric value and mesh vertex colours decode through the shading's space, but an /Indexed space combined with /Function and a /Pattern shading space are not rejected as the clause requires.
8.7.4.5.2Type 1 (Function-Based) Shadingsimplemented12The 2-in function evaluates per pixel over /Domain under /Matrix and points outside the domain stay unpainted.
8.7.4.5.3Type 2 (Axial) Shadingsimplemented23/Coords, /Domain, /Extend and a single or array-valued /Function are honored.
8.7.4.5.4Type 3 (Radial) Shadingsimplemented11The two circles, /Domain and /Extend are honored and a negative radius is rejected; the test covers concentric circles without extension.
8.7.4.5.5Type 4 Shadings (Free-Form Gouraud-Shaded Triangle Meshes)implemented24Flags 0, 1 and 2 with /BitsPerCoordinate, /BitsPerComponent, /BitsPerFlag and /Decode are read from the stream and Gouraud interpolation paints each triangle.
8.7.4.5.6Type 5 Shadings (Lattice-Form Gouraud-Shaded Triangle Meshes)implemented21/VerticesPerRow rows are split into triangle pairs and painted like type 4.
8.7.4.5.7Type 6 Shadings (Coons Patch Meshes)implemented23The 12 control points per patch in Table 85 order, edge sharing for flags 1 to 3, and interior points derived by the Coons formula are all handled.
8.7.4.5.8Type 7 Shadings (Tensor-Product Patch Meshes)implemented21The four extra interior points are read in Table 86 order and the patch is evaluated as a bicubic tensor patch.
8.8.1Generalimplemented23Do dispatches on /Subtype (indirect values resolved) to image or form drawing and reports a missing or untyped XObject; the writer emits Do for images and form groups (canvas.rs draw_image, 387 draw_group).
8.8.2PostScript XObjectsout of scope00pdfboss does not interpret PostScript, so a /PS XObject is skipped with an Unsupported report entry, which is what the clause tells a non-PostScript reader to do.
8.9.2Image Parametersimplemented24/Width, /Height, /BitsPerComponent and /ColorSpace drive decoding; bad dimensions are rejected and short sample data reports as truncated.
8.9.3Sample Representationimplemented231, 2, 4, 8 and 16 bit samples are read big-endian with each row padded to a whole byte, 16-bit samples scaling by 65535.
8.9.4Image Coordinate Systemimplemented34The image fills the unit square under the CTM with row 0 at the top edge (v = 1) by inverse mapping of each device pixel.
8.9.5.1Generalincomplete34Of Table 89 the entries Width, Height, BitsPerComponent, ColorSpace, Decode, ImageMask, Mask, SMask, SMaskInData and OC are honored, while Intent, Interpolate, Alternates, Name, StructParent, ID, OPI and Metadata are ignored.
8.9.5.2Decode Arraysimplemented67/Decode maps native samples, stencils and the Lab and Indexed defaults per Table 90 (crates/pdfboss-render/src/image.rs decode_samples, decode_stencil), maps DCTDecode samples per component through apply_decode (so a Photoshop-style CMYK JPEG with /Decode [1 0 1 0 1 0 1 0] comes out right), and is ignored for JPXDecode as 7.4.9 asks. Tests: image.rs decode_array_inverts_gray, dct_decode_array_applies_to_jpeg_samples and dct_decode_array_inverts_photoshop_cmyk.
8.9.5.3Image Interpolationimplemented32image.rs ImageMeta reads /Interpolate (the inline /I is expanded at content.rs) and draw_rgba samples an interpolating image bilinearly (Rgba::bilinear) wherever it is drawn larger than itself, alpha included so stencil edges soften too; without the flag magnification stays nearest-neighbour and minification keeps its box average either way (image.rs interpolated_magnification_blends_between_samples, executor.rs interpolate_blends_a_magnified_image); the clause leaves the algorithm to the reader.
8.9.5.4Alternate Imagesnot implemented10No code reads /Alternates; the base image is always drawn, which the clause permits a reader to do.
8.9.6.1Generalimplemented13Stencil, explicit and colour key masking are dispatched from one place, with /SMask taking precedence over /Mask when both are present.
8.9.6.2Stencil Maskingincomplete25/ImageMask true paints the nonstroking colour where the decoded sample is 0 (with /Decode [1 0] inverting and JPX stencils repacked to 1 bit), but a pattern nonstroking colour paints a mid-gray stand-in with a report entry.
8.9.6.3Explicit Maskingimplemented21A stream-valued /Mask must be an /ImageMask stencil, its 1 samples hide the base image and a mask of different dimensions is resampled onto the base's unit square; a non-stencil /Mask stream is reported; the test comment cites §8.9.6.4, which is not the -1 number for explicit masking.
8.9.6.4Colour Key Maskingimplemented21An array /Mask of 2n integer ranges hides samples whose raw values all fall inside, on native sample data only (DCT and JPX images decline with a report, since the clause warns the result is unpredictable there).
8.9.7Inline Imagesimplemented58BI/ID/EI with the Table 93 key abbreviations, the /G /RGB /CMYK /I colour space abbreviations, the Table 94 filter abbreviations and /L are honored and a named colour space resolves through the /ColorSpace resource; the writer serializes inline images with /L.
8.10.2Form Dictionariesimplemented26/Matrix, /BBox (as a clip) and /Resources (prepended to the lookup chain with page fallback; output lib.rs cites the clause) are honored to depth 16 (cases nested_forms, form_resource_shadowing), /Group /Ref /Metadata /PieceInfo /StructParent are ignored, and the writer emits form XObjects with /BBox and /Resources at pdf.rs.
8.10.3Group XObjectsimplemented34/Group is read on form XObjects and annotation appearance streams: /S /Transparency makes the form a transparency group (11.6.6) and any other subtype, or no /Group, paints as a plain form (executor.rs transparency_group; tests group_with_an_unknown_subtype_is_a_plain_form and non_isolated_group_blends_with_the_page_and_isolated_does_not in executor.rs); the soft-mask /G form also runs as a group (11.6.5.2).
8.10.4.1Generalincomplete10/Ref is never read, so a reference XObject paints its own proxy content as an ordinary form, which the clause permits when the referenced file is not consulted.
8.10.4.2Printing Reference XObjectsout of scope00Printer-side substitution of the referenced page has no meaning for a rasterizer that never opens the referenced file.
8.10.4.3Special Considerationsout of scope00The guidance on /Page and /ID matching against the referenced file does not apply while /Ref is not consulted.
8.11.2.1Generalimplemented75A group is identified by its indirect reference and is off when the configuration's state says so; /Name, /Intent and /Usage are read as data by optional_content_groups_with (crates/pdfboss-core/src/oc.rs, test oc.rs groups_read_as_data), with Python and async twins.
8.11.2.2Optional Content Membership Dictionariesimplemented26/OCGs (single or array, null entries ignored) under /P AnyOn, AllOn, AnyOff, AllOff and /VE And/Or/Not expressions with nesting all evaluate, /VE taking precedence.
8.11.2.3Intentimplemented25A group's /Intent (a name or an array, default View) must share a name with the configuration's /Intent (default View, All matching every group) for its state to affect visibility, so an off Design group under a View configuration still paints, and an empty configuration array hides nothing (oc.rs retain_matching_intents; tests oc.rs a_group_of_another_intent_never_hides_content, the_configuration_intent_selects_the_groups_it_controls and executor.rs usage_application_and_intent_decide_what_paints).
8.11.3.2Optional Content in Content Streamsimplemented28BDC /OC with a named /Properties resource or an inline dictionary opens a span whose painting is suppressed while graphics state, text position and clipping still take effect, spans nest and EMC balances; text extraction applies the same gating.
8.11.3.3Optional Content in XObjects and Annotationsimplemented23An /OC entry on an image or form XObject or on an annotation dictionary hides it (counted, not reported as a drop); the code cites this as §8.11.3.5, which is not the -1 number.
8.11.4.2Optional Content Properties Dictionaryimplemented43/OCGs and the default /D configuration are read from the catalog; /Configs alternates are ignored because only /D applies when a document opens.
8.11.4.3Optional Content Configuration Dictionariesimplemented34/BaseState, /ON and /OFF apply in specification order, /Intent selects the groups the configuration controls and /AS applies the usage application dictionaries of the requested event (oc.rs configured_off, apply_usage); /Name, /Creator, /Order, /ListMode, /RBGroups and /Locked are viewer UI entries with no rendering effect, and /Configs alternates are not read because only /D applies when a document opens (8.11.4.2).
8.11.4.4Usage and Usage Application Dictionariesimplemented67The configuration's /AS dictionaries whose /Event is the requested event set each group in their /OCGs from the View, Print or Export entry of its /Usage that the /Category array names, ON turning the group on and OFF off, in array order with later dictionaries winning (oc.rs apply_usage; tests oc.rs view_usage_application_hides_a_view_state_off_group, each_event_applies_its_own_dictionaries, usage_without_application_or_category_changes_nothing). The Zoom, Language and User categories depend on a viewer's magnification, locale and user, which pdfboss has none of, and leave the state unchanged. Every Table 102 entry is read as data (oc.rs read_usage, test oc.rs groups_read_as_data).
8.11.4.5Determining the State of Optional Content Groupsimplemented410The state comes from /BaseState then /ON then /OFF of the default configuration, then the /AS dictionaries of one event, then the intent rule; a document without /OCProperties shows everything. Document::oc_state, which rendering and text extraction use, is the View event's state a viewer shows on screen (pdfium and pdf.js render the same), while oc_state_for(None) is the default configuration alone that the clause prescribes for printing and aggregating applications, and oc_state_for(Some(Print)) the printed state (tests executor.rs usage_application_and_intent_decide_what_paints, extract.rs usage_application_decides_which_layers_are_extracted, parity.rs optional_content_states_and_groups_agree).

9 Text

ClauseFeatureStatusCode citationsTest citationsNote
9.2.2Basics of Showing Textimplemented24BT, Tf, Td, Tj and ET are parsed, executed for extraction and painted by the renderer; the writer emits the same five-operator sequence from Canvas::text (crates/pdfboss-write/src/canvas.rs).
9.2.3Achieving Special Graphical Effectsimplemented13The renderer paints every Tr mode of Table 106 for outline fonts: fills in the fill colour, strokes in the stroke colour through stroke_path with the graphics state's line parameters, and the clipping modes intersect the clip with the glyph outlines at ET so later content shows through the text (executor.rs clip_render_modes_intersect_the_clip_when_the_text_object_ends); SkippedKind::TextClip is no longer produced.
9.2.4Glyph Positioning and Metricsimplemented33Widths are read in 1/1000 text-space units (Type3 through /FontMatrix), the horizontal displacement w0 advances the text matrix and vertical fonts use w1 with the position vector; the writer's Standard14::text_width sums AFM widths the same way (crates/pdfboss-write/src/font.rs).
9.3.1Generalimplemented25The Table 104 text state parameters live in the graphics state on both the extraction and the render side, so q/Q restore them and BT/ET leave them alone; Tf takes the font resource name and size.
9.3.2Character Spacingimplemented23Tc is added to every glyph displacement, scaled by Th horizontally and unscaled in vertical writing; only the render tests measure the Tc advance, extraction has no dedicated one.
9.3.3Word Spacingimplemented34Tw applies to the single-byte code 32 only, including composite fonts whose CMap has a one-byte codespace covering it; the writer's justified paragraphs set Tw per line and reset it (crates/pdfboss-write/src/element.rs).
9.3.4Horizontal Scalingimplemented22Tz/100 scales horizontal advances, TJ offsets and the glyph rendering matrix and is not applied to vertical displacements; only the extraction side has an advance test.
9.3.5Leadingimplemented12TL sets the leading, TD sets it to -ty, and T*, ' and " move the line matrix down by it.
9.3.6Text Rendering Modeimplemented89executor.rs show_text and paint_glyph follow Table 106: modes 0, 2, 4, 6 fill, modes 1, 2, 5, 6 stroke each glyph after its fill in the stroke colour with the user-space line width, caps, joins and dash, modes 4 to 7 accumulate the outlines from BT (or from the Tr inside the text object) and ET intersects the clip with their nonzero union, no outlines meaning no clip, and Q restores it; mode 3 paints nothing; an operand outside the table is read by its bits (fill and stroke in the low two, clip in the third) as pdf.js reads it; a font that paints at no tier contributes no outlines, so a clipping mode over its text leaves later content unclipped; a Type3 font is affected by mode 3 only, so its glyphs paint in every other mode and never clip (executor.rs stroke_render_modes_paint_the_outline_in_the_stroke_colour, clip_render_modes_intersect_the_clip_when_the_text_object_ends, type3_text_ignores_every_mode_but_invisible, text_render_mode_is_restored_by_q); extraction marks modes 3 and 7 invisible.
9.3.7Text Riseimplemented22Ts shifts the glyph origin in unscaled text space and is recorded on each TextSpan; the renderer applies it in the text rendering matrix without a dedicated test.
9.3.8Text Knockoutnot implemented00The ExtGState /TK flag is never read; glyphs are composited one at a time as they paint, which matches only the TK false behavior.
9.4.1Generalimplemented32BT resets Tm and Tlm to identity and ET ends the object while the text state parameters survive across text objects; nested BT is not diagnosed.
9.4.2Text-Positioning Operatorsimplemented24Td, TD, Tm and T* update Tlm and Tm per Table 108 in both consumers; the writer positions text with Td (crates/pdfboss-write/src/canvas.rs).
9.4.3Text-Showing Operatorsimplemented24Tj, TJ (number offsets as -n/1000 times Tfs and Th, or ty in vertical mode), ' and " are all executed; TJ offsets and the two quote operators have parser tests only, no extraction or render test; the writer emits Tj.
9.4.4Text Space Detailsimplemented64The rendering matrix [Tfs*Th 0 0 Tfs 0 Ts] x Tm x CTM and the tx/ty displacement formulas are applied per glyph, with the vertical position vector v shifting the glyph origin.
9.5Introduction to Font Data Structuresimplemented24Font dictionaries dispatch on /Subtype to the Type0, Type1/MMType1, TrueType and Type3 loaders on both consuming sides; the writer emits /Type /Font dictionaries for the standard 14 (crates/pdfboss-write/src/font.rs).
9.6.2Type 1 Fontsincomplete1017Parent row: see 9.6.2.1 (implemented), 9.6.2.2 (incomplete) and 9.6.2.3 (incomplete).
9.6.2.1Generalimplemented25Table 111 entries BaseFont, FirstChar, Widths, FontDescriptor, Encoding and ToUnicode are read (LastChar is implied by the Widths length); the writer emits Type, Subtype, BaseFont and Encoding for the standard 14 (crates/pdfboss-write/src/font.rs).
9.6.2.2Standard Type 1 Fonts (Standard 14 Fonts)incomplete612The twelve text faces (plus Arial, TimesNewRoman and CourierNew aliases) get AFM widths without /Widths (as rasterized advances and as extracted span widths) and Croscore substitute faces at GlyphPainting::Full (bundled only with the substitute-fonts feature, on by default in the CLI), and the writer emits them WinAnsi-encoded; Symbol and ZapfDingbats have their built-in encodings (Annex D.5 and D.6, crates/pdfboss-encoding/src/symbol.rs) but no metrics and no substitute face, so they stay unpainted and the writer encodes text for them without widths (crates/pdfboss-write/src/font.rs symbol_faces_encode_through_their_built_in_encodings).
9.6.2.3Multiple Master Fontsincomplete20/MMType1 is dispatched like /Type1 by the renderer and falls into the simple-font loader in extraction, but no test names an /MMType1 font.
9.6.3TrueType Fontsimplemented24Simple /TrueType fonts with a FontFile2 program are parsed (head, maxp, loca, glyf, cmap, hmtx, post) and painted at every tier with /Widths taking precedence over hmtx; extraction reads the cmap platform ids to decide the WinAnsi gap fill.
9.6.4Font Subsetsimplemented52The six-uppercase-plus tag is stripped before standard-14 metric lookup, substitution and picture-font detection while extracted spans keep /BaseFont as written; the writer embeds no programs, so it never subsets.
9.6.5Type 3 Fontsimplemented1112FontMatrix, CharProcs, Encoding, Widths and the font's own Resources are read, CharProcs run as nested frames, d1 glyphs ignore their own color operators and the advance comes from /Widths through FontMatrix; FontBBox is not used, and several comments cite "9.6.5.2", a subclause number the 32000-1 outline does not have.
9.6.6Character Encodingincomplete2130Parent row: see 9.6.6.1 (incomplete), 9.6.6.2 (incomplete), 9.6.6.3 (implemented) and 9.6.6.4 (incomplete).
9.6.6.1Generalincomplete46Named encodings and encoding dictionaries with /BaseEncoding and /Differences are applied for WinAnsiEncoding, MacRomanEncoding and StandardEncoding; MacExpertEncoding is not implemented and silently falls back to StandardEncoding.
9.6.6.2Encodings for Type 1 Fontsincomplete610An embedded program's built-in encoding (a Type 1 program's /Encoding dup/put entries or StandardEncoding token; a CFF program's Encoding table, standard or custom format 0/1 with supplements, expert not bundled) is the base when the PDF names none, /Differences apply over it, a named encoding outranks it, and a nonsymbolic or standard-14 font without /Encoding defaults to StandardEncoding; the built-in encodings of the non-embedded Symbol and ZapfDingbats faces are not available.
9.6.6.3Encodings for Type 3 Fontsimplemented23The /Encoding dictionary's /Differences names select CharProcs (StandardEncoding names fill unmapped codes as a leniency); extraction decodes Type3 codes through the same simple-font encoding path.
9.6.6.4Encodings for TrueType Fontsimplemented711crates/pdfboss-render/src/truetype.rs Cmap keeps the best Unicode subtable ((3,10), (3,1) or platform 0), the (3,0) symbol table and the (1,0) Macintosh table, each reachable on its own (gid_for_unicode, gid_for_symbol, gid_for_mac); glyph.rs load_simple builds the code table as the clause says: a glyph name from /Differences, else from the base encoding when the font names MacRoman or WinAnsi, is flagged Nonsymbolic, or carries any /Encoding without the Symbolic flag, StandardEncoding filling what the base leaves undefined; the name goes through the glyph list into the Unicode subtable, or without one back to a Mac OS Roman code (pdfboss-encoding mac_os_roman_code, Table 115 included) into the (1,0) subtable, and failing both through post; a code still unmapped takes the symbolic route, the (3,0) subtable at the byte and in the 0xF000, 0xF100 and 0xF200 ranges, else the (1,0) subtable at the byte, then this reader's own mapping, a Unicode subtable at the byte or 0xF000 plus the byte, and in a program without a cmap the byte as the glyph index (tests glyph.rs differences_name_paints_via_post, base_encoding_letter_still_paints, symbolic_font_codes_go_through_the_3_0_subtable_ranges, mac_roman_subtable_takes_the_name_mapped_to_a_mac_os_code, a_font_without_a_cmap_uses_the_code_as_the_glyph_index; pdfboss-encoding lib.rs mac_os_roman_codes_follow_table_115). A symbolic font with a (3,0) table is addressed by code first, whatever its /Encoding says, and its /Differences names reach glyphs only through post or by their glyph-list value in the (3,0) table, never through Mac OS Roman, since its (1,0) table is keyed by the font's own codes (pdf.js canvas.pdf; test a_symbolic_fonts_differences_names_do_not_go_through_mac_os_roman), the leniency the clause's NOTE 1 allows.
9.7.2CID-Keyed Fonts Overviewimplemented23The code to CID (CMap) to glyph (CIDToGIDMap or CFF charset) pipeline is in place for CIDFontType0 and CIDFontType2 descendants.
9.7.3CIDSystemInfo Dictionariesincomplete31Only /Ordering is read, to pick the Japan1, GB1, CNS1, Korea1 or KR CID-to-Unicode table; /Registry and /Supplement are ignored and the CMap-versus-CIDFont collection compatibility rule is not checked.
9.7.4CIDFontsimplemented1618Parent row: see 9.7.4.1, 9.7.4.2 and 9.7.4.3, all implemented (crates/pdfboss-render/src/glyph.rs load_type0 and load_cff_cid; tests there cover each child).
9.7.4.1Generalimplemented23Table 117 entries Subtype, CIDSystemInfo, FontDescriptor, DW, W, DW2, W2 and CIDToGIDMap are read from the first descendant; BaseFont is used only as style evidence.
9.7.4.2Glyph Selection in CIDFontsimplemented69CIDFontType2 honours /CIDToGIDMap as /Identity or a stream (crates/pdfboss-render/src/glyph.rs). CIDFontType0 maps CIDs through the CFF charset when the Top DICT carries CIDFont operators and uses the CID directly as the glyph index when it does not (glyph.rs load_cff_cid over cff.rs cid_to_gid, None for the second case). A FontFile3 of /Subtype /OpenType is read from the container's CFF table (cff.rs parse, cff_table), and so is a CIDFontType2 FontFile2 holding a CFF-flavoured OpenType program (OTTO), as macOS Quartz writes it (glyph.rs load_type0_truetype into cff_cid, at the CFF tier; the descendant's /CIDToGIDMap applies when the CFF has no CIDFont operators). Tests: glyph.rs cff_cid_font_paints_at_all_embedded_not_embedded_truetype_only, cff_cid_font_over_a_non_cid_program_uses_cids_as_gids, cff_cid_font_in_an_opentype_wrapper_paints, cff_opentype_in_a_cid_fontfile2_paints_at_the_cff_tier; cff.rs an_opentype_wrapped_cff_parses_from_its_cff_table.
9.7.4.3Glyph Metrics in CIDFontsimplemented66Both forms of /W and /W2 are parsed, /DW defaults to 1000 also when neither /W nor /DW is present, never falling back to the program's hmtx (glyph.rs cid_widths; test a_cid_font_without_w_or_dw_advances_by_the_default_width), /DW2 to [880 -1000] and the default position vector is (w0/2, vy); ranges are capped at 65536 entries as a hostile-input guard.
9.7.5CMapsimplemented1411Parent row: see 9.7.5.1 to 9.7.5.4, all implemented.
9.7.5.1Generalimplemented23CMaps split one to four byte codes by codespace range, map them to CIDs and carry the writing mode; the CID value domain is kept apart from the ToUnicode parser.
9.7.5.2Predefined CMapsimplemented71All Table 118 names (20 Japan1, 14 GB1, 14 CNS1, 11 Korea1, Identity-H/V) plus UniAKR-UTF16-H load from packed Adobe data behind the predefined-cmaps feature, which is off in pdfboss-core by default but on in the CLI defaults and in pdfboss-py; without it every name but Identity falls back to the Identity assumption.
9.7.5.3Embedded CMap Filesimplemented23A CMap stream as /Encoding is parsed with its /WMode and /UseCMap chain (names or streams, depth bounded at 4) and the in-content usecmap operator; the stream dictionary's CMapName and CIDSystemInfo are not read.
9.7.5.4CMap Example and Operator Summaryimplemented14begincodespacerange, begincidchar, begincidrange, beginnotdefchar, beginnotdefrange and usecmap are executed and the remaining Table 119 operators (begincmap, endcmap, CMapName, def) are skipped as inert; the bf* operators belong to ToUnicode (9.10.3).
9.7.6Type 0 Font Dictionariesincomplete79Parent row: see 9.7.6.1 (implemented), 9.7.6.2 (implemented) and 9.7.6.3 (incomplete).
9.7.6.1Generalimplemented23Table 121 entries Subtype, Encoding, DescendantFonts (first element) and ToUnicode are read; BaseFont feeds style detection only.
9.7.6.2CMap Mappingimplemented33Codes are matched byte-wise against the codespace ranges (shortest match wins) and looked up by (length, value), so equal values of different lengths stay distinct; with no codespaces at all two-byte codes are assumed.
9.7.6.3Handling Undefined Charactersincomplete23Notdef ranges are consulted after every real mapping, an invalid code consumes the shortest codespace whose first byte fits and maps to CID 0, and CID 0 still advances by /W or /DW; the renderer never draws the notdef glyph (gid 0 is skipped and reported as NoGlyph) and with no partial match one byte is consumed instead of the shortest codespace length.
9.8.1Generalimplemented1417Table 122 entries FontName, Flags, ItalicAngle, Ascent, Descent, CapHeight, StemV, FontWeight, MissingWidth and the three FontFile keys are consumed for metrics, style and substitution, and FontBBox stands in for an absent or zero Ascent or Descent in extracted span boxes (not for Type 3 fonts, whose descriptor is in FontMatrix space); XHeight, StemH, Leading, AvgWidth, MaxWidth and CharSet are not read.
9.8.2Font Descriptor Flagsimplemented128FixedPitch, Serif, Nonsymbolic, Italic and ForceBold (Table 123 bits 1, 2, 6, 7 and 19) drive span style, substitute face choice and the StandardEncoding default; Symbolic, Script, AllCap and SmallCap are not consulted.
9.8.3Font Descriptors for CIDFontsnot implemented22Parent row: see 9.8.3.1 (incomplete), 9.8.3.2 (notImplemented) and 9.8.3.3 (notImplemented).
9.8.3.1Generalincomplete22The descendant's descriptor is read through the same Table 122 path as simple fonts (Flags, metrics, FontFile2 and FontFile3), but none of the Table 124 CIDFont-only entries (Style, Lang, FD, CIDSet) is consulted.
9.8.3.2Stylenot implemented00The /Style dictionary and its /Panose string are never read.
9.8.3.3FDnot implemented00The /FD dictionary of per-class descriptors is never read; only the top-level descriptor metrics apply.
9.9Embedded Font Programsincomplete46FontFile (Type 1 as PFA or PFB, eexec decrypted), FontFile2 (TrueType) and FontFile3 as bare CFF (Type1C and CIDFontType0C) are parsed and painted; a CFF-flavoured OpenType program (OTTO) is read from its CFF table, in FontFile3 and in FontFile2 alike (cff.rs cff_table; glyph.rs load_simple and load_type0_truetype); the FontFile3 /Subtype is never read, Length1, Length2 and Length3 are not used, and the writer embeds no font programs.
9.10.2Mapping Character Codes to Unicode Valuesincomplete34ToUnicode is consulted first, simple fonts then map through the encoding's glyph names (Adobe Glyph List, uniXXXX and uXXXX[XX] forms, underscore ligatures, period variants) and composite fonts through the collection's Uni*-UTF16-H inverse keyed by /Ordering, and a marked-content sequence's /ActualText replaces the mapped text of everything shown inside it (14.9.4, 14.8.2.4.2); gXX or cidXX style glyph names are not resolved.
9.10.3ToUnicode CMapsimplemented26begincodespacerange, beginbfchar and both beginbfrange forms are parsed with UTF-16BE destinations (multi-character and surrogate pairs) for simple and Type0 fonts alike; a lone U+FFFD destination is treated as no mapping and usecmap inside a ToUnicode CMap is not followed.

10 Rendering

ClauseFeatureStatusCode citationsTest citationsNote
10.2CIE-Based Colour to Device Colourimplemented14CalRGB, CalGray, Lab and ICCBased colours convert through XYZ with Bradford adaptation into sRGB, the only output device; /BlackPoint is never read and the ri operator and /RI are parsed then discarded.
10.3.2Conversion between DeviceGray and DeviceRGBimplemented32Gray to RGB replicates the value; RGB to gray uses the clause's 0.3/0.59/0.11 weights inside blend luminosity and Rec. 601 integer weights (77/150/29 over 256) for luminosity soft masks, and no gray output device exists.
10.3.3Conversion between DeviceGray and DeviceCMYKout of scope00The rasterizer targets only an RGB device, so gray never converts to or from CMYK.
10.3.4Conversion from DeviceRGB to DeviceCMYKout of scope00There is no CMYK output device, so RGB is never converted to CMYK.
10.3.5Conversion from DeviceCMYK to DeviceRGBimplemented13Every CMYK fill, stroke and image sample converts to RGB, but on purpose with the multiplicative (1-c)(1-k) form rather than the clause's 1-min(1, c+k) so deep colours keep their hue.
10.4Transfer Functionsout of scope00ExtGState /TR and /TR2 are output-device calibration hints a screen rasterizer ignores and they are never read; the soft-mask dictionary /TR is applied and assessed under 11.6.5.2.
10.5.2Halftone Screensout of scope00Halftoning is a printer feature; /HT is never read and the rasterizer paints continuous-tone anti-aliased RGB.
10.5.3Spot Functionsout of scope00No halftone screens exist, so spot functions have nothing to drive.
10.5.4Threshold Arraysout of scope00No halftone screens exist, so threshold arrays are never read.
10.5.5Halftone Dictionariesout of scope00Halftone dictionaries of every type (1, 5, 6, 10, 16) are never parsed; the code does not distinguish 10.5.5.2 to 10.5.5.6.
10.6.2Flatness Toleranceincomplete22Curves flatten to a fixed 0.1 device-pixel error bound, which satisfies any permitted flatness, but the i operator is parsed then discarded and ExtGState /FL is never read.
10.6.3Smoothness Toleranceincomplete20Shadings are evaluated per device pixel, so any smoothness tolerance is met, but ExtGState /SM is never read.
10.6.4Scan Conversion Rulesimplemented110Anti-aliased analytic coverage (vertical subsamples, exact horizontal spans) under the nonzero and even-odd rules, clips applied as coverage masks, and open subpaths closed for filling.
10.6.5Automatic Stroke Adjustmentnot implemented00ExtGState /SA is never read; the 0.75 device-pixel minimum pen width at stroke.rs is a hairline floor, not stroke adjustment.

11 Transparency

ClauseFeatureStatusCode citationsTest citationsNote
11.2Overview of Transparencyincomplete22Constant alpha, blend modes, soft masks and transparency groups (isolated or not, composited once with the alpha, blend mode and soft mask in force at their Do) are painted, but knockout groups, group colour spaces and separate shape and opacity values are absent.
11.3.2Basic Notation for Compositing Computationsincomplete10Definitions only; the formula they name is assessed under 11.3.3.
11.3.3Basic Compositing Formulaimplemented23Union alpha, alpha-weighted colour and the backdrop-alpha weighting of the blend result ((1-ab)Cs + abB(Cb,Cs), raster.rs blended) all follow the formula; a non-Normal mode over a transparent backdrop passes the source colour through (raster.rs blend_over_a_transparent_backdrop_passes_the_source_through).
11.3.4Blending Colour Spaceincomplete10Blending always happens in 8-bit device sRGB after colour conversion; a group /CS is never read, so CMYK or Lab content blends in RGB rather than its own space.
11.3.5Blend Modeimplemented913All sixteen Table 136 and 137 modes are parsed (name or array form, unknown names read as Normal) and painted on fills, strokes, glyphs, images, shadings and pattern tiles; the twelve separable formulas (32000-2 §11.3.5.2) are coded with Multiply and Screen checked against their formula, and the four non-separable modes (32000-2 §11.3.5.3) are checked against hand-computed vectors.
11.3.6Interpretation of Alphaincomplete21One alpha per paint (coverage times constant alpha times soft mask times image sample alpha) stands in for shape and opacity, which are never carried as separate values; the difference only shows in knockout groups (11.4.6).
11.3.7Shape and Opacity Computationsincomplete55Source shape and opacity combine as the clause asks (11.3.7.2); result shape and opacity are not tracked (11.3.7.3).
11.3.7.2Source Shape and Opacityimplemented24Source alpha is the product of anti-aliased object coverage, the graphics-state soft mask intersected with the clip, the constant alpha and any image sample alpha, matching fjfmqk with the constant shape fixed at 1.
11.3.7.3Result Shape and Opacityincomplete10composite_over unions alpha (as + ab*(1-as)) on the page and offscreen mask buffers, but result shape and opacity are never kept apart, so group results (11.4) cannot be formed.
11.3.8Summary of Basic Compositing Computationsincomplete21Summary clause; the formulas it collects are assessed under 11.3.2 to 11.3.7, and raster.rs blended cites it for the backdrop-alpha weighting.
11.4.2Notation for Group Compositing Computationsincomplete10Definitions only; the group compositing formulas they serve are assessed under 11.4.4 (raster.rs mix_group cites the clause).
11.4.3Group Structure and Nomenclatureincomplete31Nested groups form a tree of frames, each painting into its own offscreen buffer whose initial backdrop is a copy of the group backdrop or, for an isolated group, transparent (executor.rs run, raster.rs copy_region), and the finished group composites onto the group backdrop once; the knockout alteration of the group backdrop (11.4.6) is absent.
11.4.4Group Compositing Computationsincomplete32The non-isolated, non-knockout case runs as the clause's formulas reduce for one alpha per element: the group paints over a copy of its backdrop and raster.rs mix_group removes that backdrop's contribution so it counts once (Note 3), while an isolated group composites through raster.rs composite_group with the alpha, soft mask and blend mode at its Do; a non-isolated group invoked under a non-Normal blend mode is rendered isolated instead, shape and opacity are one value (/AIS is never read) and knockout is absent (11.4.6).
11.4.5Isolated Groupsimplemented32/Group /I true paints the group onto a transparent initial backdrop, so blend modes inside it see only the group's own objects (executor.rs group_kind and run; test non_isolated_group_blends_with_the_page_and_isolated_does_not in executor.rs); /CS is never read, so the flag alone decides and a group naming a blending colour space without /I true stays non-isolated.
11.4.6Knockout Groupsnot implemented11/Group /K is read and a true value is reported as an unsupported Knockout skip (executor.rs group_kind), but nothing knocks out: ExtGState /TK is never read and elements always composite over each other, glyphs one at a time through fill_spans.
11.4.7Page Groupincomplete42The page renders onto an opaque white pixmap, matching a page group composited against white, and nested groups composite by the group formula (raster.rs mix_group, composite_group), but the page dictionary's /Group (/CS, /I, /K) is never read.
11.4.8Summary of Group Compositing Computationsincomplete10Summary clause; the general formula is assessed under 11.4.4, the isolated initialization under 11.4.5 and the knockout backdrop choice under 11.4.6 (raster.rs composite_group cites it).
11.5.2Deriving a Soft Mask from Group Alphaimplemented21The /G form renders on a transparent offscreen page as an isolated group and its alpha channel becomes the mask.
11.5.3Deriving a Soft Mask from Group Luminosityimplemented22The group renders over an opaque black or /BC backdrop and Rec. 601 luma times alpha becomes the mask with /TR applied; /BC is read as one gray component whatever the group colour space and has no test, and luminosity is taken in device RGB rather than the group /CS.
11.6.2Specifying Source and Backdrop Coloursimplemented12The source colour is the current fill or stroke colour, image sample or shading value converted to RGB, and the backdrop is the pixel already on the page or offscreen mask buffer; no group backdrop exists (11.4).
11.6.3Specifying Blending Colour Space and Blend Modeincomplete22/BM as a name or array is honoured, but the blending colour space is never read from a group /CS and is always device RGB.
11.6.4Specifying Shape and Opacityimplemented1016Object coverage, the graphics-state soft mask and the constant alpha all feed one source alpha; see 11.6.4.2 to 11.6.4.4.
11.6.4.2Object Shape and Opacityimplemented23Fills, strokes, glyph outlines and stencil masks contribute anti-aliased coverage as shape, and image sample alpha (/SMask, /Mask, JPX alpha) contributes opacity.
11.6.4.3Mask Shape and Opacityimplemented24The ExtGState soft mask multiplies into the alpha of every paint (fills, strokes, glyphs, images, shadings, tiles) and follows q/Q; it is folded into one alpha rather than kept as mask opacity distinct from shape.
11.6.4.4Constant Shape and Opacityimplemented47/ca and /CA are clamped to 0..=1 and applied to fills, glyphs, images, shadings and tiles (ca) and to strokes (CA); stroke alpha /CA has no test anywhere in the tree.
11.6.5Specifying Soft Masksincomplete916Soft-mask dictionaries are complete (11.6.5.2); soft-mask images lack /Matte handling (11.6.5.3).
11.6.5.2Soft-Mask Dictionariesimplemented28/S (Alpha or Luminosity), /G rendered at the CTM in force at gs from a fresh graphics state, /BC, /TR (function, /Identity, /Default) and /SMask /None are handled with failures reported; /BC is approximated as gray and untested.
11.6.5.3Soft-Mask Imagesimplemented56An image /SMask (with /Decode, any dimensions) and JPX /SMaskInData 1 and 2 give per-sample alpha; a /Matte is converted through the base image's colour space (image.rs matte_rgb) and image.rs Rgba::unmatted gives each sample its original colour back before compositing, exact for device gray and RGB and approximate through a non-linear conversion, while a mask whose dimensions differ from the base's is used as stored (image.rs matte_is_unblended_before_compositing, executor.rs matte_samples_are_unblended_before_painting).
11.6.6Transparency Group XObjectsincomplete66A /Group /S /Transparency form (page content or annotation appearance) runs from alphas 1, blend Normal and no soft mask, renders offscreen and composites once with the alpha, blend mode and soft mask in force at its Do (executor.rs FrameKind::TransparencyGroup, tests transparency_group_composites_with_the_outer_alpha, nested_transparency_groups_multiply_their_alphas, non_isolated_group_blends_with_the_page_and_isolated_does_not). An isolated group renders onto a transparent backdrop, a non-isolated one onto a copy of its backdrop and mixes back by the constant alpha; a non-isolated group under a non-Normal blend mode at its Do renders isolated instead. /K true is reported as SkippedKind::Knockout and not honoured, and /CS is never read (blending stays device RGB).
11.6.7Patterns and Transparencyincomplete26Shading patterns and tiling cells take the invoking state's constant alpha, blend mode and soft mask, but a tiling cell is not composited as one transparency group, so translucent or overlapping tiles composite tile by tile.
11.7.2Colour Spaces for Transparency Groupsnot implemented00Group /CS is never read and no group colour space exists; everything blends in device RGB.
11.7.3Spot Colours and Transparencyout of scope00Separation and DeviceN colours pass through their tint transforms into RGB before compositing, so this screen device has no spot colourants to composite separately.
11.7.4Overprinting and Transparencyout of scope00/OP, /op and /OPM are never read; overprint simulation is a print-preview feature the screen rasterizer does not attempt, which covers 11.7.4.2 to 11.7.4.5 alike.
11.7.5Rendering Parameters and Transparencynot implemented00Neither halftone and transfer parameters (11.7.5.2) nor rendering intents (11.7.5.3) take part in compositing.
11.7.5.2Halftone and Transfer Functionout of scope00No halftones or graphics-state transfer functions exist (10.4, 10.5), so there is nothing to apply after group compositing.
11.7.5.3Rendering Intent and Colour Conversionsnot implemented00ri and /RI are parsed then discarded (executor.rs), the ICC crate uses only a profile's default transform (icc lib.rs), and all colour converts to sRGB at paint time rather than at group compositing.

12 Interactive Features

ClauseFeatureStatusCode citationsTest citationsNote
12.2Viewer Preferencesimplemented72crates/pdfboss-core/src/viewer_preferences.rs viewer_preferences_with reads the catalog's /ViewerPreferences into ViewerPreferences with every entry of Table 150: the six flags (false by default), NonFullScreenPageMode, Direction, the four page-boundary names (ViewArea, ViewClip, PrintArea, PrintClip, CropBox by default), PrintScaling (AppDefault by default and for a name the standard does not list, as the table says), Duplex, PickTrayByPDFSize and NumCopies (None where the table leaves the default to the reader) and PrintPageRange as (first, last) pairs of positive integers, indirect values followed and a name the standard does not list left at the default; Document::viewer_preferences and AsyncDocument::viewer_preferences expose it, None for a catalog without the dictionary (tests viewer_preferences.rs every_viewer_preference_is_read, missing_and_unknown_viewer_preferences_take_the_defaults; crates/pdfboss-aio/tests/parity.rs). Nothing acts on the preferences yet: Direction does not turn the reading order and DisplayDocTitle does not change what info prints; the writer's Viewer struct at crates/pdfboss-write/src/pdf.rs emits the catalog's /PageLayout, /PageMode and /OpenAction, which belong to 7.7.2, and no /ViewerPreferences.
12.3.2Destinationsincomplete1616Explicit /XYZ destinations are written for links, outline items and /OpenAction; crates/pdfboss-core/src/destination.rs parses explicit destination arrays (12.3.2.2) and looks named destinations up in the /Names /Dests tree and the catalog /Dests dictionary (12.3.2.3), and Document::destination follows a /Dest or /D value of either kind; no writer API accepts a destination by name, no destination is followed to a page index, and the importer does not rewrite destinations.
12.3.2.2Explicit Destinationsimplemented710crates/pdfboss-core/src/destination.rs Destination::parse reads every Table 151 form: the page as a reference (DestinationPage::Object) or, for a remote go-to action, a 0-based number (DestinationPage::Number); Fit::Xyz, FitH, FitV, FitBH and FitBV keep a null or left-out coordinate as None (the viewer's current value) and read a zoom of 0 as null, FitR needs all four numbers, an unknown fit name or a page that is neither rejects the array; destination_with resolves an indirect array and indirect numbers while leaving the page reference a reference (tests destination.rs every_table_151_form_parses, null_zero_zoom_and_missing_parameters_keep_the_current_value, remote_destinations_carry_a_page_number, malformed_destinations_are_rejected, indirect_arrays_and_numbers_resolve). The writer emits [page /XYZ null null null] only, which the table allows; the importer copies a source /GoTo /D unchanged instead of rewriting it onto the imported page, a limitation of page assembly documented in docs/src/guide/assembling.md and pinned by crates/pdfboss-write/tests/importer.rs page_import_of_a_linked_source_carries_the_linked_page_along.
12.3.2.3Named Destinationsimplemented53crates/pdfboss-core/src/destination.rs named_destination_with looks a name up in the catalog's /Names /Dests tree (PDF 1.2, string keys, through tree.rs lookup) and then in the catalog's /Dests dictionary (PDF 1.1, name keys), taking a destination array or a dictionary whose /D holds one; named_destinations_with lists both sources sorted by name with the tree winning a duplicate; destination_value_with follows a /Dest or /D value that is an array, a name, a string or a dictionary with /D. Document::named_destination, named_destinations and destination on the sync side, AsyncDocument twins on the async side (tests destination.rs named_destinations_come_from_the_name_tree_and_the_catalog_dictionary, destination_values_follow_arrays_names_strings_and_d_dictionaries; crates/pdfboss-aio/tests/parity.rs names fixture). The writer still has no API that accepts a destination by name.
12.3.3Document Outlineimplemented77crates/pdfboss-core/src/outline.rs outline_with walks the catalog /Outlines through /First and /Next in display order and returns OutlineItem trees: /Title decoded as a text string, the destination from /Dest or from a /GoTo action's /D through destination.rs, open from a positive /Count (Table 153's recursive count is the writer's concern), /C as the title colour, the italic and bold bits of /F (Table 154) and /SE as a reference; a node seen twice or one that is not a dictionary ends its chain, 65536 items and 64 levels bound the walk. Document::outline and AsyncDocument::outline (tests outline.rs outline_items_nest_by_first_and_next, cycles_and_missing_outlines_end_quietly; crates/pdfboss-aio/tests/parity.rs outline fixture). The writer emits a complete outline tree (/Outlines with /First, /Last and /Count; items with /Title, /Parent, /Prev, /Next, /First, /Last, /Count and /Dest) with every item open and never writes /A, /SE, /C or /F, all optional (crates/pdfboss-write/src/pdf.rs Outline and Bookmark).
12.3.4Thumbnail Imagesimplemented96crates/pdfboss-core/src/thumbnail.rs thumbnail_with reads a page's /Thumb image XObject into a Thumbnail with the five significant entries (Width, Height, ColorSpace as written, BitsPerComponent, Decode) and the stream itself (Document::thumbnail, AsyncDocument::thumbnail); crates/pdfboss-render/src/extract.rs page_thumbnail decodes it through the image decoder at its own size; pdfboss info counts the pages that carry one and pdfboss images --thumbnails writes them as page-N-thumb.png (tests thumbnail.rs reads_the_pages_thumbnail_and_its_significant_entries, extract.rs page_thumbnail_decodes_the_thumb_image_at_its_own_size, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs cmd_images_writes_thumbnails_with_the_flag). The writer emits no thumbnails, which the clause leaves optional.
12.3.5Collectionsnot implemented00The catalog /Collection dictionary and collection schemas are never read or written (the only Collection identifiers in the code are CJK CMap character collections).
12.4.2Page Labelsimplemented99crates/pdfboss-core/src/page_label.rs holds LabelStyle (the five /S styles of Table 159, with numeral giving decimal, subtractive Roman and repeated-letter forms) and PageLabel (first page, style, /P prefix, /St start); page_labels_with reads the catalog /PageLabels number tree through tree.rs entries, skipping non-dictionary values, reading an unknown /S as no style and a /St below 1 as 1; page_label picks the last range at or before a page and falls back to the 1-based page number for a page before the first range, which the clause forbids but files do. Document::page_labels and page_label, AsyncDocument twins (tests page_label.rs styles_number_pages_as_table_159_says, the_clauses_example_labels_its_pages, ranges_without_a_style_or_a_page_zero_still_label; crates/pdfboss-aio/tests/parity.rs outline fixture). The writer re-exports the same types and emits the /PageLabels tree with /S, /P and /St per range, validating the page-0 range, duplicate first pages and start_at (crates/pdfboss-write/src/pdf.rs page_labels_dict). The CLI, TUI and Python bindings do not show labels yet.
12.4.3Articlesimplemented124crates/pdfboss-core/src/article.rs articles_with reads the catalog's /Threads array into ArticleThread records (Table 160: the /I information dictionary through the shared metadata reader, the beads from /F along /N in chain order until the chain returns to a seen bead or breaks, each Bead with its Table 161 /P page and normalized /R rectangle), and page_beads_with reads a page's /B array in drawing order; Document::articles and page_beads and their AsyncDocument twins expose them, and pdfboss info counts threads and beads (tests article.rs walks_each_threads_beads_in_order_and_reads_its_information and a_broken_chain_stops_and_a_document_without_threads_has_none, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_counts_article_threads). The writer emits no threads, which the clause leaves optional.
12.4.4Presentationsimplemented84crates/pdfboss-core/src/presentation.rs presentation_with reads a page's /Dur display duration and its /Trans transition dictionary into a Presentation whose Transition carries every Table 162 entry with the table's defaults filled in (TransitionStyle from /S with R as the default, /D, Dimension from /Dm, Motion from /M, TransitionDirection from /Di as an angle or the name None, /SS, /B); Document::presentation and AsyncDocument::presentation expose it and pdfboss info counts the pages that are slides (tests presentation.rs reads_the_duration_and_the_transition_with_the_tables_defaults and pages_without_the_entries_have_no_presentation_and_odd_values_fall_back, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_counts_slide_pages). Playing a presentation is viewer behaviour, /PresSteps belongs to 12.4.4.2, and the writer emits no /Dur or /Trans, which the clause leaves optional.
12.4.4.2Sub-page Navigationout of scope00Navigation nodes (/PresSteps) drive keyboard-stepped presentation playback, which needs a viewer; pdfboss neither reads nor writes them.
12.5.2Annotation Dictionariesimplemented159crates/pdfboss-core/src/annotation.rs annotations_with reads every Table 164 entry of a page's /Annots in order (Subtype, Rect normalized, Contents, P, NM, M as written and parsed, F, the presence of /AP, AS, Border with its dash array, C, StructParent, OC) into Annotation, exposed as Document::annotations, AsyncDocument::annotations and Page.annotations in Python, and pdfboss info counts annotations by subtype; the renderer reads /Subtype, /Rect, /F, /AP, /AS, /OC and /C for painting, the element listings expose only /Subtype, and the writer emits /Type, /Subtype, /Rect, /Border and /A for links. Tests: annotation.rs reads_the_entries_common_to_every_annotation and direct_malformed_and_missing_annotations.
12.5.3Annotation Flagsimplemented106Hidden (bit 2) and NoView (bit 6) suppress painting, Invisible (bit 1) suppresses it for annotations outside Table 169's standard types, NoRotate (bit 5) turns the appearance back by the page /Rotate about the upper-left corner of /Rect so it stays upright where the rotation put that corner (paint_annotations in crates/pdfboss-render/src/executor.rs), and /OC visibility is honoured. Print, NoZoom and the interactive flags (ReadOnly, Locked, ToggleNoView, LockedContents) have no effect on a raster of the on-screen view at a chosen resolution. Tests: invisible_flag_hides_only_unknown_annotation_types and no_rotate_annotations_stay_upright_on_a_rotated_page in crates/pdfboss-render/src/executor.rs.
12.5.4Border Stylesimplemented32Border::read in crates/pdfboss-render/src/annot.rs takes /BS (/W, /S, and /D under /S /D) or else /Border ([h v w [dash]]): Line, Square, Circle, Polygon, PolyLine and Ink appearances use the width and dash pattern, and a Link without /AP gets its border drawn inside /Rect in /C by Paint::link_border with the /Border corner radii, the U style as a line along the bottom edge, and a width past half the rectangle falling back to 1 as pdf.js does. The B and I styles are drawn solid, and a Link declaring neither /Border nor /BS draws no border, as Acrobat and pdf.js do rather than the clause's 1-point default. The writer puts /Border [0 0 0] on every link so no border shows. Test: link_borders_without_appearance_follow_the_border_style in crates/pdfboss-render/src/executor.rs.
12.5.5Appearance Streamsimplemented36The /AP /N normal appearance is selected (through /AS for a state dictionary, a single-state dictionary needing no /AS), its /BBox transformed by /Matrix is fitted onto the normalized /Rect per the clause's algorithm and run as a form XObject, and a declared appearance that cannot be read or placed is reported; /R and /D are pointer states a rasterizer does not show.
12.5.6Annotation Typesincomplete5141Assessed as a group: every type paints through its /AP /N appearance, /Popup annotations are skipped, /AS state selection serves Widget check boxes and radio buttons, and pdfboss-write emits /Link annotations; without /AP only Line, Square, Circle, Polygon, PolyLine, Ink and the four text markup types get an appearance synthesized from their own entries and a Link gets the border its /Border or /BS declares (crates/pdfboss-render/src/annot.rs), every other type paints nothing; text extraction reads the /QuadPoints of the four text markup subtypes (12.5.6.10) into span flags.
12.5.6.2Markup Annotationsimplemented42Every Table 170 entry (/T, /Popup, /CA with its default 1, /RC as a string or a text stream, /CreationDate, /IRT, /Subj, /RT with its default R, /IT) is read into Markup for the seventeen markup subtypes of Table 169, and a Link, Popup or Widget carries none (crates/pdfboss-core/src/annotation.rs read_markup; test reads_the_markup_entries); the renderer folds /CA into the appearances it builds for annotations without /AP and leaves an existing appearance stream as written (Paint::read in crates/pdfboss-render/src/annot.rs; test constant_opacity_applies_to_synthesized_appearances_only in executor.rs). /ExData, which only 3D markup uses, is not read.
12.5.6.3Annotation Statesimplemented11A text annotation's /StateModel and /State are read into AnnotationState, the state defaulting to Unmarked under Marked and None under Review when only the model is given, with the /IRT reference to the annotation whose state changes carried in its Markup (crates/pdfboss-core/src/annotation.rs read_state; test reads_the_state_a_reply_sets); no per-user state is aggregated across a reply thread, which is a viewer's comments pane.
12.5.6.4Text Annotationsincomplete21/Open, /Name (the icon), /State and /StateModel are read into Annotation (crates/pdfboss-core/src/annotation.rs read_annotation; test reads_the_state_a_reply_sets); painting goes only through the group /AP path (tested with /Stamp and Widget fixtures, not /Text), no icon is synthesized without /AP, and NoRotate keeps the icon upright on a rotated page (12.5.3) but the fixed-size NoZoom behaviour the clause expects is not applied.
12.5.6.5Link Annotationsimplemented611Link annotations with /Rect, /Border [0 0 0] and a /URI or /GoTo action are written from Rust, Python, Markdown and the TOML manifest; on read the annotation reader resolves /Dest, explicit or named, into a Destination and /A into an Action chain (crates/pdfboss-core/src/annotation.rs read_annotation; test reads_link_destinations_and_popups), and a Link without /AP paints only the border its /Border or /BS entry declares (12.5.4); /H, /PA and /QuadPoints are not read, and text extraction does not report link targets.
12.5.6.6Free Text Annotationsincomplete10Painted only through the group /AP path; /DA, /Q, /RC, /DS, /CL, /IT, /BE and /RD are ignored and no appearance is generated from them.
12.5.6.7Line Annotationsimplemented41A Line without /AP is painted from its own entries by Paint::line in crates/pdfboss-render/src/annot.rs: the /L segment stroked in /C with the /BS or /Border width and dash pattern, moved sideways by /LL with the leader lines, their /LLE extensions and /LLO gaps, and the /LE endings of Table 176 drawn at the line width with the closed ones filled in /IC; the appearance is placed in default user space, so an empty or wrong /Rect neither moves nor clips it. /Cap captions with /CO and /CP, /IT and /Measure are not drawn. Test: line_annotations_without_appearance_are_drawn in crates/pdfboss-render/src/executor.rs.
12.5.6.8Square and Circle Annotationsimplemented31A Square or Circle without /AP is painted from its own entries by synthesized in crates/pdfboss-render/src/annot.rs: the rectangle or the inscribed ellipse inside /Rect shrunk by /RD, stroked in /C with the /BS or /Border width and dash pattern and filled with /IC, at the /CA opacity; an /AP still takes precedence and a /BE cloudy border is drawn plain. Test: square_and_circle_annotations_without_appearance_are_drawn in crates/pdfboss-render/src/executor.rs.
12.5.6.9Polygon and Polyline Annotationsimplemented31A Polygon or PolyLine without /AP is painted from its own entries by Paint::polyline in crates/pdfboss-render/src/annot.rs: the /Vertices joined by straight lines in /C at the /BS or /Border width and dash pattern, a polygon closed back to its first vertex and filled with /IC, a polyline left open with the /LE endings of Table 176 at its first and last vertex; the appearance is placed in default user space by its own box. /BE cloudy borders are drawn plain and /IT and /Measure are not read. Test: polygon_and_polyline_annotations_without_appearance_are_drawn in crates/pdfboss-render/src/executor.rs.
12.5.6.10Text Markup Annotationsimplemented510A Highlight, Underline, StrikeOut or Squiggly without /AP is painted from its own entries by Paint::markup in crates/pdfboss-render/src/annot.rs: each /QuadPoints quadrilateral is taken by its extent (writers disagree on the corner order), a Highlight filling it in /C with the Multiply blend mode so the text under it stays legible, an Underline stroking a 1-unit line along its bottom edge, a StrikeOut one through its middle and a Squiggly a zigzag a quarter of its height along the bottom, all at the /CA opacity and placed in default user space by their own box. Text extraction reads the same /QuadPoints (each quadrilateral by its extent, /Rect without them) into the span flags underline, strikethrough and highlight, with the /C color on highlights and Hidden, NoView and /OC-hidden annotations skipped (pdfboss-text decorations.rs markup_annotations, tested by the annotation tests in pdfboss-text lib.rs). Test: text_markup_annotations_without_appearance_are_drawn in crates/pdfboss-render/src/executor.rs.
12.5.6.11Caret Annotationsincomplete10Painted only through the group /AP path; /RD and /Sy are ignored.
12.5.6.12/Stamp annotationsimplemented25/Stamp fixtures are the group's main test subject: the /AP appearance is fitted onto /Rect, hidden and OC-gated ones stay silent, and unreadable ones are reported; the /Name icon is not synthesized when /AP is absent.
12.5.6.13Ink Annotationsimplemented21An Ink without /AP is painted from its own entries by Paint::ink in crates/pdfboss-render/src/annot.rs: every /InkList path stroked through its points with straight segments, round caps and joins in /C at the /BS or /Border width and dash pattern, a single point becoming a dot of the line width, at the /CA opacity; the appearance is placed in default user space by its own box, so an unnormalized /Rect neither moves nor clips it. Curve smoothing between the points (permitted by ISO 32000-2) is not done. Test: ink_annotations_without_appearance_are_drawn in crates/pdfboss-render/src/executor.rs.
12.5.6.14Pop-up Annotationsimplemented22Pop-up annotations are skipped by the renderer even when they carry an /AP, which matches a static page render where the pop-up window is viewer UI; the annotation reader exposes /Parent and /Open (crates/pdfboss-core/src/annotation.rs read_annotation; test reads_link_destinations_and_popups).
12.5.6.15File Attachment Annotationsincomplete21The annotation reader carries /FS as a FileSpec and /Name as the icon name, and Document::file_spec_data decodes the embedded stream (crates/pdfboss-core/src/annotation.rs read_annotation; test reads_a_file_attachment_and_its_data); painting goes only through the group /AP path and no icon is synthesized without /AP.
12.5.6.16Sound Annotationsincomplete10Painted only through the group /AP path; /Sound and /Name are ignored and playback is viewer behavior.
12.5.6.17Movie Annotationsincomplete10Painted only through the group /AP path; /Movie, /A and /T are ignored and playback is viewer behavior.
12.5.6.18Screen Annotationsincomplete10Painted only through the group /AP path; /MK, /A and /AA are ignored and media playback is viewer behavior.
12.5.6.19Widget Annotationsimplemented41A widget's appearance comes from /AP /N with /AS selecting among the state dictionary's streams, and an /AS naming no state is reported as a dropped annotation; /H, /BS and the /MK colours and rotation are not read, so no appearance is generated for a widget lacking /AP. The /MK captions and icons reach the form fields (12.7.4.2.2).
12.5.6.20Printer’s Mark Annotationsincomplete10Painted through the group /AP path like any other annotation; /MN is ignored and there is no print-versus-display distinction, so marks always show.
12.5.6.21Trap Network Annotationsincomplete10Painted through the group /AP path; /LastModified, /Version, /AnnotStates and /FontFauxing are ignored and the page /LastModified consistency rule is not checked.
12.5.6.22Watermark Annotationsincomplete10Painted through the group /AP path with the page; the /FixedPrint dictionary (/Matrix, /H, /V) is ignored, and the overlay command in crates/pdfboss-write/src/update.rs adds page content rather than a /Watermark annotation.
12.5.6.23Redaction Annotationsincomplete10Painted through the group /AP path; /QuadPoints, /IC, /RO, /OverlayText, /Repeat, /DA and /Q are ignored, and applying a redaction (removing the content beneath) is not offered.
12.6.2Action Dictionariesimplemented75crates/pdfboss-core/src/annotation.rs action_with reads an action dictionary's /S and its /Next entry, a single dictionary or an array, into an Action tree in order, a visited set over references stopping a chain that points back at an earlier action and a budget of 256 dictionaries stopping a runaway tree (tests follows_next_chains_once and a_runaway_next_chain_stops_at_the_budget); the writer emits /URI and /GoTo dictionaries without /Next, and no action is ever executed.
12.6.3Trigger Eventsimplemented105The additional-actions dictionaries of annotations (Table 194, with the field triggers of Table 196 a merged widget and field dictionary carries), pages (Table 195) and the catalog (Table 197) are read into TriggeredAction lists in table order (crates/pdfboss-core/src/annotation.rs triggered_actions, page_additional_actions_with and document_additional_actions_with; test reads_trigger_events_on_annotations_pages_and_the_catalog); the writer emits the catalog /OpenAction as an explicit destination to the open_to page, and firing a trigger is viewer behaviour.
12.6.4Action Typesimplemented3118Every Table 198 type is read as data and none is executed: GoTo, GoToR, GoToE, Launch, URI, Named and JavaScript into their own ActionKind entries, the other ten kinds with their dictionary kept as written (crates/pdfboss-core/src/annotation.rs read_action_kind; test reads_every_action_type); the writer emits only /URI and /GoTo.
12.6.4.2Go-To Actionsimplemented43/GoTo actions with /S /GoTo and an explicit /D destination are written for page-target links with the target page range-checked; on read the destination, explicit or named, resolves through the document's destinations into ActionKind::GoTo, the name kept beside it (crates/pdfboss-core/src/annotation.rs destination_entry; test reads_link_destinations_and_popups), a /GoTo is never followed (viewer behavior) and the importer copies it unchanged.
12.6.4.3Remote Go-To Actionsimplemented22/GoToR actions read their /F file specification, an explicit /D whose first element is a 0-based page number of the other document or a named /D kept as the name, and /NewWindow (crates/pdfboss-core/src/annotation.rs read_action_kind and destination_entry; test reads_remote_go_to_actions); nothing writes or follows one.
12.6.4.4Embedded Go-To Actionsimplemented81/GoToE actions read /F, /D, /NewWindow and the /T target chain, each Table 202 step with /R, /N, /P (a page number or a named destination) and /A (an annotation index or name), nested through /T to a depth of 32 (crates/pdfboss-core/src/annotation.rs read_target; test reads_embedded_go_to_targets); nothing writes or follows one.
12.6.4.5Launch Actionsimplemented61/Launch actions read /F, /NewWindow and the Windows parameters of /Win (Table 204: /F, /D, /O with its default open, /P) as data (crates/pdfboss-core/src/annotation.rs read_windows_launch; test reads_launch_actions); /Mac and /Unix are undefined by the standard, nothing writes a launch action, and launching an application is viewer behaviour.
12.6.4.6Thread Actionsout of scope00Thread actions step through article beads inside a viewer; pdfboss executes no actions and does not model article threads.
12.6.4.7URI Actionsimplemented46/URI actions with /S /URI and the URI as a text string are written for external links from every writer front end; on read the annotation reader carries /URI and /IsMap in ActionKind::Uri (crates/pdfboss-core/src/annotation.rs read_action_kind; test reads_every_action_type); the catalog-level /URI base dictionary is not handled, and text extraction does not report URIs.
12.6.4.8Sound Actionsout of scope00Sound playback needs a viewer with audio output; pdfboss executes no actions.
12.6.4.9Movie Actionsout of scope00Movie playback needs a viewer with media output; pdfboss executes no actions.
12.6.4.10Hide Actionsout of scope00Hiding or showing annotations at run time is viewer state; the renderer honors only the static Hidden flag.
12.6.4.11Named Actionsout of scope11NextPage, PrevPage, FirstPage and LastPage are viewer navigation; pdfboss executes no actions.
12.6.4.12Set-OCG-State Actionsout of scope00Toggling optional content groups at run time is viewer state; the renderer applies only the document's default /OCProperties /D configuration.
12.6.4.13Rendition Actionsout of scope00Multimedia rendition playback needs a viewer; pdfboss executes no actions.
12.6.4.14Transition Actionsout of scope00Page transitions are presentation playback inside a viewer; pdfboss executes no actions.
12.6.4.15Go-To-3D-View Actionsout of scope003D artwork views need a 3D-capable viewer; pdfboss neither renders 3D streams nor executes actions.
12.6.4.16JavaScript Actionsout of scope11pdfboss has no JavaScript engine and never executes document scripts.
12.7.2Interactive Form Dictionaryimplemented82Document::interactive_form reads the catalog /AcroForm dictionary: /Fields and /CO as reference lists, /NeedAppearances, the /SigFlags bits SignaturesExist and AppendOnly, /DR, /DA as written, /Q as a quadding and whether /XFA is present; absent or ill-typed entries take the Table 218 defaults. Nothing writes the dictionary.
12.7.3Field Dictionariesimplemented1511Document::form_fields (crates/pdfboss-core/src/form.rs form_fields_with) walks the /Fields tree depth first into one FormField per field dictionary: /FT, /Ff, /V and /DV are taken from the nearest ancestor when the field lacks them, /T, /TU and /TM are decoded as text strings, /AA is kept as written, a /Kids entry that is a /Subtype /Widget dictionary without a /T is recorded as one of the field's Widget records (reference, /AS, on state) and a merged field is its own widget; a reference seen before, a non-dictionary kid and nesting past 64 levels are skipped. Only the render path uses fields so far, and nothing writes them.
12.7.3.2Field Namesimplemented12FormField::name is the fully qualified name (crates/pdfboss-core/src/form.rs qualified_name): the partial /T names from the root field down joined by a PERIOD, a field without /T sharing its parent's name, an unnamed root the empty name. /T itself is kept as partial_name. Nothing checks that a partial name is free of periods.
12.7.3.3Variable Textincomplete34FormField::default_appearance and quadding (crates/pdfboss-core/src/form.rs) carry a field's /DA and /Q from its own dictionary, its nearest ancestor or the interactive form's document-wide defaults, left-justified when none sets /Q; DefaultAppearance::parse reads the Tf font resource name and size and the g, rg or k fill colour out of the string; default_style and rich_text hold /DS and /RV as text, a /RV text stream read into a string. No appearance stream is generated for text-bearing fields, so /NeedAppearances documents still show only pre-existing /AP streams.
12.7.3.4Rich Text Stringsnot implemented00/RV reaches FormField::rich_text as text and /DS FormField::default_style (12.7.3.3); the rich text markup and the style string are not parsed.
12.7.4Field Typesimplemented1821FieldType (crates/pdfboss-core/src/form.rs) names the four field types of Table 220 and each has its row: buttons by kind and state (12.7.4.2), text with its text and MaxLen (12.7.4.3), choice with options and selection (12.7.4.4), signature as a signature dictionary (12.7.4.5); the widget-level /AS state selection that check boxes and radio buttons rely on is handled by the render path. No appearance stream is generated for any type (12.7.3.3).
12.7.4.2Button Fieldsimplemented1012FormField::button_kind (crates/pdfboss-core/src/form.rs) tells pushbuttons, check boxes and radio button fields apart by the Table 226 Pushbutton and Radio bits, FieldFlags exposes no_toggle_to_off, radio, pushbutton and radios_in_unison, and FormField::state returns the /V appearance-state name of a check box or radio button field (none for a pushbutton, which keeps no value); the render path selects each widget's appearance through /AS. Matching /V to the individual widgets' states is the radio button row's.
12.7.4.2.2Pushbuttonsimplemented12Each Widget carries its /MK appearance characteristics (crates/pdfboss-core/src/form.rs AppearanceCharacteristics, Table 189): the normal, rollover and alternate captions decoded as text strings, the normal, rollover and alternate icon references and the /TP caption position; FormField::button_kind names a pushbutton by its flag and FormField::state gives it no value. No appearance is generated from these entries: a pushbutton still paints only through a pre-existing /AP.
12.7.4.2.3Check Boxesimplemented43FormField::checked (crates/pdfboss-core/src/form.rs) reports a check box as checked when its /V state is a name other than Off, an absent value being the off state; each Widget carries its /AS and the on state read as the one non-Off key of its /AP /N dictionary, FormField::on_widgets lists the widgets whose on state is the field's state, and the Table 227 /Opt export values are the field's options, indexed like the widgets. The render path selects the on or off appearance by /AS.
12.7.4.2.4Radio Buttonsimplemented24FormField::on_widgets (crates/pdfboss-core/src/form.rs) picks the kid widgets whose on state, the non-Off key of their /AP /N, is the parent field's /V, several at once for a RadiosInUnison group; FormField::state is Off for a radio button field without /V, the clause's default; the Table 227 /Opt export values are the field's options, indexed like the widgets; NoToggleToOff and RadiosInUnison are FieldFlags bits. Toggling is viewer interaction, which pdfboss has none of; the render path selects each widget's appearance by /AS.
12.7.4.3Text Fieldsimplemented13FormField::text (crates/pdfboss-core/src/form.rs) decodes a text field's /V as a text string, a PDF 1.5 stream value being read into a string during the walk; FormField::max_len reads the inheritable /MaxLen of Table 229; FieldFlags exposes the Table 228 bits multiline, password, file_select, do_not_spell_check, do_not_scroll, comb and rich_text. No appearance stream is generated (12.7.3.3) and /RV is not parsed (12.7.3.4), so a text field still shows only a pre-existing /AP.
12.7.4.4Choice Fieldsimplemented43FormField::options (crates/pdfboss-core/src/form.rs) reads /Opt into ChoiceOption export_value/name pairs, a lone text string naming both; top_index reads /TI, selected_indices reads /I as written, and FormField::selected returns the /V name or names of a choice field; FieldFlags exposes the Table 230 bits combo, edit, sort, multi_select and commit_on_sel_change (do_not_spell_check is shared with text fields). /I is not reconciled with /V and no appearance stream is generated.
12.7.4.5Signature Fieldsimplemented12FormField::signature (crates/pdfboss-core/src/form.rs) reads a signature field's /V as a Signature record with the Table 252 entries Filter, SubFilter, ByteRange pairs, Contents bytes, Name, M as written, Location, Reason and ContactInfo, and FormField::lock and seed_value hold the Table 232 /Lock and /SV references. The lock and seed value dictionaries' entries are not read and nothing is signed or verified (12.8).
12.7.5Form Actionsout of scope00Submit, reset and import-data actions need a viewer, a form-filling state and a network; pdfboss executes no actions.
12.7.5.2Submit-Form Actionout of scope00Submitting field data over HTTP is viewer behavior; pdfboss executes no actions.
12.7.5.3Reset-Form Actionout of scope00Resetting fields to their defaults needs a form-filling state; pdfboss executes no actions.
12.7.5.4Import-Data Actionout of scope00Importing FDF data into a form needs a form-filling state; pdfboss executes no actions.
12.7.6Named Pagesnot implemented00The /Names /Pages and /Names /Templates name trees are never read or written.
12.7.7Forms Data Formatnot implemented00No FDF file is parsed or written anywhere in the code.
12.7.7.2FDF File Structurenot implemented00The document loader recognizes only %PDF headers and never reads an FDF header, body or trailer.
12.7.7.2.2FDF Headernot implemented00The %FDF header is not recognized.
12.7.7.2.3FDF Bodynot implemented00FDF body objects are never parsed.
12.7.7.2.4FDF Trailernot implemented00An FDF trailer (/Root only, no xref) is never parsed.
12.7.7.3FDF Catalognot implemented00The FDF catalog and its /FDF dictionary are never read or written.
12.7.7.3.2FDF Fieldsnot implemented00FDF field dictionaries are never read or written.
12.7.7.3.3FDF Pagesnot implemented00FDF page and template dictionaries are never read or written.
12.7.7.3.4FDF Annotation Dictionariesnot implemented00FDF annotation dictionaries with /Page are never read or written.
12.7.8XFA Formsnot implemented00The /AcroForm /XFA entry and XFA packets are never read, so an XFA document renders only its PDF page content.
12.8.1Generalincomplete50The Signature record (crates/pdfboss-core/src/form.rs) reads Table 252's Filter, SubFilter, ByteRange, Contents, Name, M, Location, Reason and ContactInfo as data through FormField::signature; Cert, Reference, Changes and the Prop_ entries are not read, nothing is written, and there is no signing or verification.
12.8.2Transform Methodsnot implemented00Signature reference dictionaries and their /TransformMethod are never read.
12.8.2.2DocMDPnot implemented00DocMDP transform parameters (/P, /V) are never read.
12.8.2.2.2Validating Signatures That Use the DocMDP Transform Methodnot implemented00No DocMDP validation exists, and the incremental-update writer in crates/pdfboss-write/src/update.rs appends changes without checking for a certifying signature.
12.8.2.3URnot implemented00Usage-rights transform parameters are never read.
12.8.2.4FieldMDPnot implemented00FieldMDP transform parameters (/Action, /Fields) are never read.
12.8.3Signature Interoperabilitynot implemented00No /SubFilter value is recognized and no signature bytes are decoded.
12.8.3.2PKCS#1 Signaturesnot implemented00adbe.x509.rsa_sha1 signatures and /Cert chains are never decoded or verified.
12.8.3.3PKCS#7 Signatures as used in ISO 32000not implemented00PKCS#7 signature containers are never decoded or verified.
12.8.3.3.2Revocation Informationnot implemented00Embedded CRL and OCSP data are never read.
12.8.4Permissionsincomplete24crates/pdfboss-core/src/permission.rs permission_handlers_with reads the catalog's /Perms dictionary (Table 258) into PermissionHandlers: the /DocMDP and /UR3 signature dictionaries as Signature records through the shared Signature::from_dict (Table 252 as data); Document::permission_handlers and AsyncDocument::permission_handlers expose them and pdfboss info names the handlers present (tests permission.rs reads_the_doc_mdp_and_usage_rights_handlers and missing_or_malformed_permissions_read_as_absent, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_lists_permission_handlers). The signatures are not verified and the permissions their DocMDP and UR transform parameters carry (12.8.2.2, 12.8.2.3) are neither read nor enforced; the /Perms handling in crates/pdfboss-core/src/crypt.rs is the AES-256 encryption dictionary entry of §7.6.3.3.
12.8.5Legal Content Attestationsimplemented64crates/pdfboss-core/src/legal.rs legal_attestation_with reads the catalog's /Legal dictionary (Table 259) into LegalAttestation: every count entry as an integer, 0 when absent or not a non-negative integer, and the /Attestation text; Document::legal_attestation and AsyncDocument::legal_attestation expose it and pdfboss info prints the non-zero counts by key and whether an attestation is present (tests legal.rs reads_the_legal_attestation_counts and missing_or_malformed_legal_reads_as_absent, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_lists_the_legal_attestation). The counts are taken as written and not recomputed from the document, and pdfboss-write emits no /Legal dictionary (Legal elsewhere in the code is a page size).
12.9Measurement Propertiesimplemented125crates/pdfboss-core/src/measure.rs viewports_with reads a page's /VP array (Table 260) into Viewport records with the normalized /BBox, the /Name and the /Measure dictionary, which measure_from reads (Tables 261 and 262) as a Measure with the /Subtype (RL default, others kept by name), the /R scale ratio, the /X, /Y, /D, /A, /T and /S number-format chains (Table 263, every entry with the table's default), the /O origin and /CYX; a viewport without a four-number /BBox and a number format without /U or /C are skipped, and array order is kept so the last viewport containing a point wins as the clause says. Document::viewports and AsyncDocument::viewports expose them and pdfboss info counts the viewports and the pages holding them (tests measure.rs reads_viewports_with_their_measures_and_the_tables_defaults, reads_the_optional_measure_entries and missing_or_malformed_viewports_read_as_none, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_counts_viewports). The /Measure entry a line annotation may carry (Table 175) is not read there, and pdfboss-write emits no viewport.
12.10.1Generalimplemented24crates/pdfboss-core/src/requirement.rs requirements_with reads the catalog's /Requirements array (Table 266) into Requirement records carrying the /S type and the /RH handlers (12.10.2), in array order, skipping an entry that is no dictionary or has no /S name; Document::requirements and AsyncDocument::requirements expose them and pdfboss info prints the types (tests requirement.rs reads_the_requirements_in_order and missing_or_malformed_requirements_read_as_none, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_lists_requirements). Whether the reader meets a requirement is the consumer's call; pdfboss-write emits no /Requirements, which the clause does not ask of a writer.
12.10.2Requirement Handlersimplemented23crates/pdfboss-core/src/requirement.rs handlers_with reads a requirement's /RH entry, one handler dictionary or an array of them (Table 267), into RequirementHandler records with the /S kind (JS or NoOp, other names kept as written) and the /Script name of the document-level JavaScript, skipping a handler that is no dictionary or has no /S (tests requirement.rs reads_the_requirement_handlers and missing_or_malformed_handlers_read_as_none, crates/pdfboss-aio/tests/parity.rs). The handler is data: pdfboss has no JavaScript engine, so running the named script is viewer behavior.

13 Multimedia Features

ClauseFeatureStatusCode citationsTest citationsNote
13.1Generalout of scope00pdfboss parses, extracts, renders and creates PDFs and is not a viewer, so multimedia playback has no place in it.
13.2Multimediaout of scope10No crate reads a rendition, media clip or media player dictionary; a grep for those key names finds nothing in the Rust sources.
13.2.1Generalout of scope00A Screen annotation's /AP /N appearance still paints generically through crates/pdfboss-render/src/executor.rs, but its /A rendition action and /MK are never read because playback is a viewer concern.
13.2.2Viabilityout of scope00Viability (the /MH must-honor and /BE best-effort dictionaries) only matters when choosing a player to run, which pdfboss never does.
13.2.3Renditionsout of scope10Rendition dictionaries are never read because pdfboss does not play media.
13.2.3.1Generalout of scope10Same as 13.2.3: no rendition dictionary is read.
13.2.3.2Media Renditionsout of scope00Media rendition dictionaries (/S /MR) are never read because pdfboss does not play media.
13.2.3.3Selector Renditionsout of scope00Selector renditions (/S /SR) are never read because pdfboss does not play media.
13.2.4Media Clip Objectsout of scope00Media clip dictionaries are never read because pdfboss does not play media.
13.2.4.1Generalout of scope00Same as 13.2.4: no media clip dictionary is read.
13.2.4.2Media Clip Dataout of scope00Media clip data (/S /MCD) and its file specification are never read; pdfboss neither extracts nor plays embedded media.
13.2.4.3Media Clip Sectionout of scope00Media clip sections (/S /MCS) are never read because pdfboss does not play media.
13.2.5Media Play Parametersout of scope00Play parameters (volume, repeat, duration) only apply during playback, which pdfboss never does.
13.2.6Media Screen Parametersout of scope00Screen parameters (window, floating window, background) describe a viewer's playback window and are never read.
13.2.6.1Generalout of scope00Same as 13.2.6: no screen parameter dictionary is read.
13.2.6.2Media Offset Dictionaryout of scope00Media offsets (time, frame, marker) are only meaningful during playback and are never read.
13.2.6.3Timespan Dictionaryout of scope00Timespan dictionaries are only meaningful during playback and are never read.
13.2.7Other Multimedia Objectsout of scope00Media players, player info, software identifiers and monitor specifiers describe a viewer's environment and are never read.
13.2.7.1Generalout of scope00Same as 13.2.7: none of these dictionaries is read.
13.2.7.2Media Players Dictionaryout of scope00Player selection lists (/MU, /A, /NU) are never read because pdfboss does not select or run players.
13.2.7.3Media Player Info Dictionaryout of scope00Player info dictionaries are never read because pdfboss does not select or run players.
13.2.7.4Software Identifier Dictionaryout of scope00Software identifiers are never read because pdfboss does not select or run players.
13.2.7.4.1Generalout of scope00Same as 13.2.7.4: no software identifier is read.
13.2.7.4.2Software URIsout of scope00Software URIs are never read because pdfboss does not select or run players.
13.2.7.4.3Version arraysout of scope00Player version arrays are never read because pdfboss does not select or run players.
13.2.7.5Monitor Specifierout of scope00Monitor specifiers describe the viewer's display and are never read.
13.3Soundsout of scope00Sound objects and a Sound annotation's /Sound stream are never read; the annotation's /AP /N appearance paints generically through crates/pdfboss-render/src/executor.rs and crates/pdfboss-core/src/elements.rs lists it by subtype like any other annotation.
13.4Moviesout of scope00Movie dictionaries and movie activation dictionaries are never read; a Movie annotation's /AP /N appearance paints generically through crates/pdfboss-render/src/executor.rs.
13.5Alternate Presentationsout of scope00The /AlternatePresentations name tree and SlideShow dictionaries are never read because pdfboss does not present documents.
13.6.1Generalout of scope003D artwork (3D annotations, 3D streams, U3D data) is never read because pdfboss neither renders nor extracts 3D content.
13.6.3.1Generalout of scope003D stream dictionaries (/Type /3D) are never read; pdfboss does no 3D work.
13.6.4.1Generalout of scope003D view dictionaries are never read; pdfboss does no 3D work.
13.6.4.2Projection Dictionariesout of scope003D projection dictionaries are never read; pdfboss does no 3D work.
13.6.5Coordinate Systems for 3Dout of scope003D coordinate systems are never used; pdfboss does no 3D work.

14 Document Interchange

ClauseFeatureStatusCode citationsTest citationsNote
14.2Procedure Setsnot implemented00Nothing reads or emits /ProcSet: the reader ignores the resource (which the clause allows) and pdfboss-write never adds a /ProcSet array to the resource dictionaries it writes.
14.3.2Metadata Streamsincomplete24The writer emits an unfiltered /Type /Metadata /Subtype /XML packet on the catalog and rewrites it on update, and the reader honors EncryptMetadata false for that stream, but no reader path parses XMP (Document::metadata reads only /Info) and metadata streams on pages, XObjects or other components are neither read nor written.
14.3.3Document Information Dictionaryincomplete45Title, Author, Subject, Keywords, Creator, Producer, CreationDate and ModDate are read as decoded text strings and written back (with the XMP packet kept in step on update), but the /Trapped entry of Table 317 is neither read nor written.
14.4File Identifiersimplemented24The writer emits a two-element /ID of 16-byte SHA-256-derived strings, an incremental update keeps the first half and replaces the second, the strings stay unencrypted, and the reader consumes the trailer /ID for the encryption key.
14.5Page-Piece Dictionariesimplemented104crates/pdfboss-core/src/piece_info.rs piece_info_with reads a dictionary's /PieceInfo (Table 318) into one PagePiece per product with the Table 319 entries, /LastModified as written and parsed on request through PagePiece::last_modified_parsed and /Private kept as the object written; it serves the catalog (document_piece_info_with, Document::piece_info, AsyncDocument::piece_info), a page (Document::page_piece_info, AsyncDocument::page_piece_info) and, applied to a form dictionary, a form XObject. pdfboss info lists the product names (tests piece_info.rs reads_each_products_data_dictionary_of_a_page_piece_dictionary and a_pages_piece_info_reads_the_same_way_and_a_missing_entry_is_empty, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_lists_page_piece_products). The writer emits no /PieceInfo, the clause making it optional.
14.6.1Generalimplemented32MP, DP, BMC, BDC and EMC (Table 320) parse into distinct operators, and the text and render executors keep a nesting stack per content stream so EMC pops the matching open.
14.6.2Property Listsimplemented23BDC and DP operands are accepted as an inline dictionary or as a name looked up in the resource chain's /Properties subdictionary, and the named form keeps the indirect-reference identity the optional-content code keys on.
14.6.3Marked Content and Clippingincomplete20Marked-content nesting is tracked on its own stack and EMC never touches the graphics state, so a clip set inside a sequence persists as the clause requires, but no test exercises a clipping path or q/Q pair that straddles a marked-content sequence.
14.7.2Structure Hierarchyincomplete24The catalog's /StructTreeRoot is loaded and elements are walked through /K (single or array) and /P up to the root, but /IDTree, /ParentTreeNextKey and the element entries /T, /ID and /ActualText are never read (/S and /RoleMap are, under 14.7.3, /A, /C, /R and /ClassMap under 14.7.5, /Alt under 14.9.3, /Lang under 14.9.2 and /E under 14.9.5), and pdfboss-write neither emits a structure tree nor carries a base document's tree through assembly.
14.7.3Structure Typesimplemented63crates/pdfboss-core/src/structure.rs reads each element's /S while placing a page's marked-content sequences (Walk::key_of) and the root's /RoleMap once per document (role_map_of, an indirect map resolved); StructureTree::mapped_type follows the map from a type name until a name it has no entry for, a chain in case a mapped name is itself mapped, a cycle leaving the name as written; StructureTree::place_with returns both names per sequence as Placement { rank, structure_type, mapped_type }, and ranks_with stays the rank-only view (tests structure.rs structure_types_are_read_and_mapped_through_the_role_map, an_element_without_a_type_places_with_none). Nothing yet classifies the mapped name as one of the standard types of 14.8.4.
14.7.4Structure Contentimplemented2525Both kinds of content item are located: marked-content sequences through the parent tree's arrays (14.7.4.2, 14.7.4.4) and whole objects through the parent tree's element under their /StructParent and the object reference among the element's kids (14.7.4.3). crates/pdfboss-core/src/structure.rs ContentItem names the two kinds, StructureTree::content_items_with enumerates a page's sequences and annotations in one shared rank space (test the_page_enumeration_skips_null_entries_and_needs_a_parent_tree), place_items_with places any set of items, and Document::content_items, the async twin and Python Page.content_items expose them; the pdfboss info tagged line counts what the parent tree files for each page (content_item_counts_with) without reading the elements. Element dictionaries, paths and ancestries are cached on the tree across pages, and the parent tree's entries are read once at load; the depth-4 rows give the detail.
14.7.4.2Marked-Content Sequences as Content Itemsimplemented34Bare integer MCIDs (matched against the element's /Pg) and marked-content reference dictionaries are matched, and MCIDs are collected from BDC in page and form content. A reference's own /Pg overrides the element's, as Table 324 says, and its /Stm is read: the named stream's /StructParents must be the key the sequence was filed under, so a form's sequence numbered like the page's no longer stands in for it (crates/pdfboss-core/src/structure.rs Walk::is_reference_for, test a_reference_naming_a_stream_matches_only_that_streams_sequence). /StmOwn only says which object owns the stream and is not needed to match.
14.7.4.3PDF Objects as Content Itemsimplemented58An object reference dictionary (Table 325) is matched by its /Obj against the object's reference, direct kids first and then indirect ones, with its own /Pg overriding the element's and the element's used when it has none (crates/pdfboss-core/src/structure.rs Walk::object_key_of and Walk::object_index; tests an_annotation_takes_its_place_between_the_sequences, an_indirect_object_reference_is_read_after_the_direct_kids, the_object_reference_page_overrides_the_elements, a_link_element_orders_its_sequence_and_annotation_by_kid_index). The element itself comes from the parent tree under the object's /StructParent (14.7.4.4). StructureTree::content_items_with enumerates a page's annotations from /Annots; an XObject referenced this way is placed when handed to place_items_with (test a_form_xobject_is_placed_when_handed_in), since nothing enumerates the XObjects a page draws without running its content. A direct annotation dictionary in /Annots has no reference an OBJR could name and is skipped.
14.7.4.4Finding Structure Elements from Content Itemsimplemented910The /ParentTree number tree (/Nums, /Kids, /Limits) is searched under the /StructParents key of the page or of a form XObject that declares its own, its array indexed by MCID (crates/pdfboss-core/src/structure.rs Walk::parent_array, test a_form_key_ranks_alongside_the_page), and under the singular /StructParent key of a whole object, whose entry is a reference to the element itself (Walk::parent_element; test an_annotation_the_tree_does_not_hold_is_no_content_item covers a key the tree lacks, an object without the key and an element without a reference to the object). /ParentTreeNextKey is a writer's concern and is not read.
14.7.5Structure Attributesimplemented144crates/pdfboss-core/src/structure.rs Walk::attribute_objects reads every element's attribute objects while placing a page's marked-content sequences: the objects its /C classes name first, then its direct /A objects (Walk::direct_attributes: one dictionary or an array of dictionaries, indirect ones resolved), each as AttributeObject { element, owner (/O), revision, entries }; Placement::attributes lists them for the element and its ancestors, the root's child first, and Placement::attribute(element, owner, key) answers with the last object that has the key, so a direct object overrides a class's (test structure.rs placements_carry_the_elements_attribute_objects). What the standard owners' entries mean is the 14.8.5 rows' business.
14.7.5.2Attribute Classesimplemented41StructureTree loads the root's /ClassMap once (class_map_of) and Walk::class_attributes looks up each name an element's /C gives, one name or an array, taking the class's attribute objects as if they were the element's own and putting them before the direct /A objects, so those override; a name the map lacks brings nothing (test placements_carry_the_elements_attribute_objects: /C [/Wide 4] with /A overriding the class's ColSpan).
14.7.5.3Attribute Revision Numbersimplemented21An element's /R lands on StructureElement::revision (0 when absent); in an /A array an integer after an attribute object is that object's revision, in a /C array an integer after a class name is the revision of every object the class brought, both on AttributeObject::revision (Walk::direct_attributes, Walk::class_attributes; test placements_carry_the_elements_attribute_objects checks /R 1, a revision 2 in /A and a revision 4 in /C). No consumer compares revisions yet.
14.7.5.4User Propertiesincomplete10A /O /UserProperties attribute object is read like any other (structure.rs attribute_object) and reaches Placement::attributes with its owner, but its /P array of property dictionaries (/N, /V, /F, /H) is not interpreted.
14.7.6Example of Logical Structureout of scope00A worked example of a structure tree with no requirement of its own; the structure it illustrates is assessed under 14.7.2 to 14.7.5.
14.8.2Tagged PDF and Page Contentincomplete3030Structure-tree reading order is offered on every extraction entry point and ranks the page's marked-content sequences by the tree (14.8.2.3); artifacts are marked on their spans (14.8.2.2), reversed show strings put back in logical order (14.8.2.3.3) and ActualText substituted for the shown glyphs (14.9.4), and annotations take their place among the sequences through Page::content_items (14.8.2.3.2). The font characteristics come from the descriptor: style from its flags, weight, stem and the name, and the family, stretch and weight as written (14.8.2.4.3); the depth-3 and depth-4 rows give the detail.
14.8.2.2Real Content and Artifactsimplemented62crates/pdfboss-text/src/extract.rs marks every span shown inside an /Artifact marked-content sequence: a bare BMC gives ArtifactKind::Unspecified, a BDC's property list (inline or named through /Properties) gives the /Type of Table 330 (Pagination, Layout, Page, Background) and the /Subtype (Header, Footer, Watermark), on TextSpan::artifact (Mark::artifact, marked_artifact; test lib.rs artifact_marks_reach_the_spans); the Python Span exposes artifact and artifact_subtype. crates/pdfboss-output/src/structure.rs lays pagination artifacts out apart from the real content and gives them the PageHeader or PageFooter role by their position against the page's text midline, so Markdown drops a producer-marked running head or folio on a single page while text keeps it (attach_pagination_artifacts; test lib.rs tagged_pagination_artifacts_are_page_headers_and_footers_on_one_page); the repetition heuristic still covers untagged files. /BBox and /Attached are not read, and layout, page and background artifacts are marked but not treated differently from real content.
14.8.2.3Page Content Orderimplemented910Under ReadingOrder::StructureTree each marked-content sequence is ranked by its depth-first position in the tree and untagged content keeps its place (crates/pdfboss-text/src/extract.rs structure_order), a /ReversedChars sequence's show strings are put back in logical order under every order (14.8.2.3.3), and the annotations the tree holds are ranked in the same order as the sequences by StructureTree::content_items_with (14.8.2.3.2), which is where an annotation's slot among the text is read from, the span list carrying no annotation text.
14.8.2.3.2Sequencing of Annotationsimplemented12An annotation's place in the page content order is read off the structure tree: crates/pdfboss-core/src/structure.rs StructureTree::content_items_with ranks the page's marked-content sequences and the annotations held through object references in one depth-first order, so an annotation whose element sits between two sequences' elements comes out between them and a Link, Form or Annot element's own sequence and annotation come out in kid order (tests an_annotation_takes_its_place_between_the_sequences, a_link_element_orders_its_sequence_and_annotation_by_kid_index; Python tests/test_content_items.py). Text extraction itself returns no annotation text, so the slot is read from the items rather than from the span list; docs/src/guide/text.md gives the recipe.
14.8.2.3.3Reverse-Order Show Stringsimplemented43crates/pdfboss-text/src/extract.rs marks a /ReversedChars sequence (BMC or BDC, Mark::reversed) and puts every show string inside it back in logical order as it is emitted (reversed_chars): the characters reversed, a space at the beginning or end of the string kept in place, and TextSpan::glyph_x reversed with the characters; a sequence's /ActualText still wins as replacement text (test crates/pdfboss-text/src/lib.rs reversed_chars_sequences_put_their_strings_back_in_order). The span's geometry stays the visual one, and the span is marked TextSpan::logical so crates/pdfboss-output/src/bidi.rs, which puts right-to-left lines in reading order, does not reverse it a second time (test crates/pdfboss-output/src/lib.rs reversed_chars_and_actual_text_are_not_reversed_twice).
14.8.2.4Extraction of Character Propertiesimplemented812Unicode values come from ToUnicode, the encoding's glyph names, the CID collection's CMap and a marked-content sequence's ActualText (14.8.2.4.2), font style from the FontDescriptor and the base font name, and the family, stretch and weight from the descriptor as written (14.8.2.4.3); the depth-4 rows give the detail.
14.8.2.4.2Unicode Mapping in Tagged PDFimplemented34The clause's methods in its order: crates/pdfboss-text/src/font.rs decode_into maps a code through the font's /ToUnicode first (load_to_unicode, an empty CMap treated as absent), a simple font then through its encoding's glyph names (the Adobe Glyph List with uniXXXX and uXXXX forms, underscore ligatures and period variants; crates/pdfboss-encoding), a composite font through its /CIDSystemInfo ordering's Uni*-UTF16-H CMap inverted (cid_to_unicode), the same relation as the Registry-Ordering-UCS2 CMaps the clause names; and crates/pdfboss-text/src/extract.rs emit puts a marked-content sequence's /ActualText in place of everything shown inside it (14.9.4), so the property overrides the mapping as the clause requires (tests font.rs tounicode_beats_encoding, differences_agl_ligatures_and_variants_decode, type0_predefined_rksj_maps_codes_to_cids, crates/pdfboss-text/src/lib.rs actual_text_replaces_the_shown_glyphs). A code none of the methods maps comes out as U+FFFD, the reader's choice the clause leaves open; gXX and cidXX style glyph names are not resolved (9.10.2).
14.8.2.4.3Font Characteristicsimplemented36crates/pdfboss-text/src/font.rs Font::style reads the descriptor's /FontFamily (a byte string, decoded as a text string), /FontStretch (the nine names of Table 122, FontStretch::from_name) and /FontWeight as written onto TextSpan::font_family, font_stretch and font_weight, next to the bold, italic, serif and fixed-pitch readings of /Flags, /FontWeight, /StemV, /ItalicAngle and the /BaseFont name (tests crates/pdfboss-text/src/lib.rs descriptor_family_stretch_and_weight_reach_the_span, descriptor_without_font_characteristics_gives_none, font_stretch_names_round_trip_in_table_order; Python tests/test_spans.py test_style_attributes). A name outside the table gives no stretch.
14.8.2.5Identifying Word Breaksimplemented34Words are found the way the clause has it, in the Unicode character stream: a space glyph decodes into the text wherever it is shown, the split into show strings carrying no meaning (crates/pdfboss-text/src/extract.rs show; test lib.rs a_space_glyph_before_a_word_gap_is_one_space), and a sequence's /ActualText replaces the shown glyphs with its spaces included (emit, 14.9.4; test lib.rs actual_text_spaces_are_word_breaks). For files whose producer left the spaces out, which the clause forbids in Tagged PDF but untagged files do freely, a horizontal gap between spans becomes a space (crates/pdfboss-output/src/structure.rs push_span; test structure.rs shrunk_justified_word_gaps_still_become_spaces), the positional guess NOTE 2 says a reader need not make. Splitting the text into words is left to the consumer, as NOTE 4 allows; no UAX #29 boundary analysis is done.
14.8.3Basic Layout Modelincomplete21Under ReadingOrder::StructureTree the page is laid out on the clause's model: crates/pdfboss-core/src/structure.rs StandardKind sorts the standard types into grouping, block-level, inline-level and illustration elements (14.8.4), crates/pdfboss-output/src/structure.rs run_of gives every span the outermost block-level element above it, and push_tagged_blocks stacks those elements as blocks in the tree's order, each block's text laid out in lines by position with its inline-level elements flowing inside (Span, Code as inline code under 14.8.4.4); a list's items and a table's rows and cells nest inside their block (tests crates/pdfboss-output/src/lib.rs tagged_block_elements_decide_the_markdown_under_structure_tree_order). The Placement and WritingMode attributes that re-class an element or turn the progression directions (14.8.5.4) are not read, so the classification comes from the standard type alone and every page is laid out left to right, top to bottom; a block-level element inside a table cell or a list body, a nested list apart, is laid out within that cell's line or item's lines rather than as a block of its own; content the tree does not reach goes through the layout heuristics.
14.8.4Standard Structure Typesimplemented1914crates/pdfboss-core/src/structure.rs StandardType names the 49 standard types with the standard's own spelling, StandardType::from_name recognizes a role-mapped name (case-sensitive, a document's own types staying None), StandardType::kind sorts each into the clause that defines it (StandardKind::Grouping, BlockLevel, InlineLevel, Illustration), and Placement::standard_type and Placement::path carry the result, with the standard-typed ancestry, for every placed marked-content sequence (tests structure.rs standard_types_are_recognized_by_name_and_grouped_by_clause, placements_carry_the_standard_typed_ancestry). What each set means for extraction is the depth-4 rows' business: the block-level elements are the blocks of structure-tree order (14.8.4.3), the grouping elements quote, caption and nest them (14.8.4.2), Code is the inline-level element with an effect of its own (14.8.4.4) and the illustration elements are laid out apart from the text around them (14.8.4.5).
14.8.4.2Grouping Elementsimplemented43Under ReadingOrder::StructureTree the paragraphs inside a BlockQuote take Role::Quote (crates/pdfboss-output/src/structure.rs in_block_quote) and the Markdown adapter renders them as a block quote, every line opened with > (markdown.rs quote); a Caption or TOCI holding text with no block-level element inside is a block of its own (block_element), so a table of contents comes out one entry per block and a figure's caption never merges with its neighbour; Sect, Part and Art nesting sets the level of an H heading (14.8.4.3); Document, Div, TOC, Index, NonStruct and Private hold blocks rather than being one and leave their content as shown, Private included since its text is on the page (tests crates/pdfboss-output/src/lib.rs block_quote_paragraphs_render_as_markdown_quotes, toc_entries_are_their_own_blocks). Headings and lists inside a BlockQuote are not quoted.
14.8.4.3Block-Level Structure Elementsimplemented47Under ReadingOrder::StructureTree every span the tree reaches carries TextSpan::structure (crates/pdfboss-text/src/lib.rs Structure: the holding element's standard type and the standard-typed ancestry from Placement::path, read by crates/pdfboss-core/src/structure.rs Walk::ancestry) and crates/pdfboss-output/src/structure.rs push_tagged_blocks builds the page's blocks from it: consecutive spans of one outermost block-level element are one block, H1 to H6 a heading at that level, H a heading as deep as its Part, Art and Sect ancestors nest, L a list with an item per LI and the Lbl text as the marker (digits closed by . or ) are the number, anything else a bullet; the label stays in the item's first line and the Markdown adapter strips it by length; an item's label is its own Lbl, not a nested list's; an item without a Lbl reads its marker off its first line as an untagged page does, and a first line that is nothing but the marker stays in the layout for plain text while the Markdown adapter opens the item on the line after it), Table a table with a row per TR (THead, TBody, TFoot transparent) and a cell per TH or TD (a cell's lines joined with a space), every other block-level element a paragraph, so two P elements a line apart stay two paragraphs; spans with no block-level element go through the layout heuristics, and ruled grids are not consulted on a tagged segment (tests crates/pdfboss-output/src/lib.rs tagged_block_elements_decide_the_markdown_under_structure_tree_order, a_tagged_list_without_labels_reads_its_markers_off_the_lines, a_nested_list_keeps_its_labels_out_of_the_outer_marker, the_same_page_in_content_order_reads_by_the_layout_heuristics; crates/pdfboss-text/src/lib.rs structure_tree_order_attaches_each_span_s_standard_typed_ancestry). ListNumbering is read under 14.8.5.5 and ColSpan and RowSpan under 14.8.5.7; a nested list's content stays inside the item holding it; a label that does not open its item's first line stays in the text as a bullet item; content order, the default, still infers headings, lists and tables from layout.
14.8.4.4Inline-Level Structure Elementsincomplete42Under ReadingOrder::StructureTree text shown inside a Code element becomes its own inline run (crates/pdfboss-output/src/structure.rs in_code, Inline::code) and the Markdown adapter sets it as a CommonMark code span, backticks one longer than any run inside and no emphasis (markdown.rs push_code; test crates/pdfboss-output/src/lib.rs code_elements_render_as_inline_code); Span, Quote, Reference, BibEntry and Annot flow into their block as shown, which is all text and Markdown have for them. Not acted on: Link keeps its text without a target (object references are unread, 14.7.4.3), Note stays in place with no footnote syntax, and Ruby and Warichu keep every part (RB, RT, RP, WT, WP) in reading order.
14.8.4.5Illustration Elementsincomplete21Under ReadingOrder::StructureTree the text inside a Figure, Formula or Form with no block-level element inside forms an illustration run (crates/pdfboss-output/src/structure.rs Run::Illustration, run_of, push_tagged_blocks): the layout heuristics lay it out on its own, adjacent illustrations together so a table a producer drew as a row of figures is still read as a table, and none of it joins the untagged text or the tagged blocks around it; a Figure's /Alt reaches its spans (14.9.3; test crates/pdfboss-output/src/lib.rs illustration_elements_are_their_own_blocks). A figure that shows no text has no span and so no block, and no adapter renders a description in its place; image extraction works from XObjects, not from the tree.
14.8.5Standard Structure Attributesincomplete106Attribute objects reach Placement::attributes and TextSpan::structure with their owner and entries (14.7.5) and the standard owners are told apart (14.8.5.2); of the standard attributes themselves the Table owner's ColSpan and RowSpan (14.8.5.7) and the List owner's ListNumbering (14.8.5.5, inherited down the tree as 14.8.5.3 says) are interpreted, the layout and PrintField attributes are not (14.8.5.4, 14.8.5.6).
14.8.5.2Standard Attribute Ownersimplemented31crates/pdfboss-core/src/structure.rs StandardOwner names the eleven owners of Table 331 (Layout, List, PrintField, Table and the XML-1.00, HTML-3.2, HTML-4.01, OEB-1.0, RTF-1.05, CSS-1.00 and CSS-2.00 formats) with the standard's spelling; StandardOwner::from_name recognizes an /O name case-sensitively, a producer's own owner and UserProperties (14.7.5.4) staying None, and AttributeObject::standard_owner applies it, which is how the tagged table code asks for the Table owner's spans (test structure.rs standard_attribute_owners_are_recognized_by_name).
14.8.5.3Attribute Values and Inheritanceincomplete21The clause's priority list is followed for the attributes that are read: crates/pdfboss-core/src/structure.rs Placement::attribute resolves a key for one element with a direct /A object beating the classes its /C entry names and a later class an earlier one (steps b and c; 14.7.5.2), and crates/pdfboss-output/src/structure.rs list_numbering resolves ListNumbering, the one inheritable attribute read (Table 347), as step d says: the L element's own value, else the nearest ancestor's, an explicit None stopping the inheritance (test crates/pdfboss-output/src/lib.rs list_numbering_is_inherited_from_an_ancestor_unless_the_list_says_none); ColSpan and RowSpan are not inheritable and take the default 1 of step e (14.8.5.7). The export-format owners of step a are told apart (14.8.5.2) but never consulted, text and Markdown being none of their formats, and an ancestor without a standard type is skipped on the way up.
14.8.5.4Layout Attributesnot implemented00Placement, WritingMode, BBox, spacing, color and column attributes are never read.
14.8.5.5List Attributeimplemented33crates/pdfboss-output/src/structure.rs list_numbering reads a tagged list's ListNumbering from the L element's attribute objects of owner List (the last object with the key winning). When it names a numbering system (Decimal, UpperRoman, LowerRoman, UpperAlpha, LowerAlpha) push_tagged_list numbers the items: a label is read in that system (numbered_label: digits; a letter of the system's case, repeated past Z; Roman numerals of the system's case; closed by . or ) or by nothing), an unlabelled item takes the number after the previous item's, and a label outside the system (a lettered sub-item inside a Decimal list, as the 049 corpus tags them) keeps the marker its text gives (numbered_marker; tests crates/pdfboss-output/src/lib.rs list_numbering_numbers_the_items_of_a_numbered_list, structure.rs labels_are_read_in_the_list_s_numbering_system). None, Disc, Circle, Square and a missing attribute leave the markers to the labels and lines, which already read as bullets; Markdown writes every ordered item with a decimal number, and the Lang-dependent alphabet of the alphabetic systems is not consulted.
14.8.5.6PrintField Attributesnot implemented00Role, checked and Desc attributes of form-field elements are never read.
14.8.5.7Table Attributesincomplete11crates/pdfboss-output/src/structure.rs table_span reads a tagged cell's ColSpan and RowSpan from the TH or TD element's attribute objects of owner Table (TextSpan::structure carries them, the last object with the key winning, values clamped to 1..255) onto Cell::colspan and Cell::rowspan, and the Markdown adapter renders a table with either span as HTML with colspan and rowspan attributes (test crates/pdfboss-output/src/lib.rs tagged_cells_carry_their_column_and_row_spans). Headers, Scope and Summary are not read, and an untagged table's structure is still inferred from column gaps and rulings.
14.9.2Natural Language Specificationimplemented126/Lang is read at all three levels: crates/pdfboss-core/src/language.rs language_with reads the catalog's as the document language (Document::language, AsyncDocument::language, Python Document.language and AsyncDocument.language), crates/pdfboss-core/src/structure.rs Walk::ancestry reads each structure element's and Placement::lang carries the nearest, and crates/pdfboss-text/src/extract.rs reads a BDC's property-list /Lang (inline or named) onto every span shown inside as TextSpan::lang, a span with none taking the nearest element's under ReadingOrder::StructureTree (Python Span.lang); every value is a text string decoded, the tag itself kept as written (tests language.rs the_catalog_lang_is_the_document_language, structure.rs placements_carry_the_nearest_language, crates/pdfboss-text/src/lib.rs natural_languages_reach_the_spans, crates/pdfboss-aio/tests/parity.rs). No output adapter uses the language yet.
14.9.2.2Language Identifiersimplemented31crates/pdfboss-core/src/language.rs LanguageTag::parse reads a /Lang value in the RFC 3066 form the clause prescribes: a primary subtag (an ISO 639 code, or i for a registered and x for a private-use tag) and hyphen-separated subtags of one to eight ASCII letters (digits allowed after the first), the second subtag a country code when it has two letters; case carries no meaning, so the language comes out lower-case and the country upper-case, the remaining subtags as written, and an empty or overlong subtag, a character outside ASCII or a separator other than the hyphen makes the tag malformed (None). Document::language_tag applies it to the catalog's /Lang (test language.rs language_tags_split_into_language_and_country). The codes themselves are not checked against the ISO 639 and ISO 3166 lists.
14.9.2.3Language Specification Hierarchyincomplete12The two inner levels chain per span: a marked-content sequence's /Lang wins over the enclosing structure elements', the nearest of which wins over its ancestors' (TextSpan::lang, Placement::lang, test natural_languages_reach_the_spans); the outermost level, the catalog's /Lang, is Document::language and is not folded into a span whose sequence and elements declare none, so a caller takes it as the default itself.
14.9.2.4Multi-language Text Arraysnot implemented00Multi-language text arrays are not parsed.
14.9.3Alternate Descriptionsimplemented43crates/pdfboss-text/src/extract.rs reads /Alt from a BDC's property list (inline or named through /Properties, decoded as a text string like /ActualText, Executor::marked_text_string) onto every span shown inside the sequence as TextSpan::alt, the shown text untouched, and under ReadingOrder::StructureTree a span with no description of its own takes the nearest structure element's /Alt, which crates/pdfboss-core/src/structure.rs Walk::ancestry reads on the way to the root and Placement::alt carries; the Python Span exposes alt (tests structure.rs placements_carry_the_nearest_alternate_description, crates/pdfboss-text/src/lib.rs alternate_descriptions_from_property_lists_reach_the_spans, alternate_descriptions_from_structure_elements_reach_the_spans). No output adapter renders a description yet, and a Figure that shows no text has no span to carry its /Alt.
14.9.4Replacement Textimplemented55crates/pdfboss-text/src/extract.rs reads /ActualText from a BDC's property list (inline or named through /Properties, any tag, decoded as a text string) and treats it as a character substitution for the sequence: the first string shown inside becomes one span carrying the replacement, every later string only widens that span's extent, and an empty replacement leaves the sequence without text (Mark::actual, ActualText, emit, marked_actual_text; tests lib.rs actual_text_replaces_the_shown_glyphs with the clause's Drucker example, actual_text_covers_the_whole_sequence_and_may_be_empty). /ActualText on a structure element dictionary is not read; the language escape sequences of 7.9.2.2 are not interpreted.
14.9.5Expansion of Abbreviations and Acronymsimplemented13crates/pdfboss-text/src/extract.rs reads /E from a BDC's property list (inline or named through /Properties, decoded as a text string like /Alt, Executor::marked_text_string) onto every span shown inside the sequence as TextSpan::expansion, the shown abbreviation untouched, and under ReadingOrder::StructureTree a span with no expansion of its own takes the nearest structure element's /E, which crates/pdfboss-core/src/structure.rs Walk::ancestry reads on the way to the root and Placement::expansion carries; the Python Span exposes expansion (tests structure.rs placements_carry_the_nearest_expansion, crates/pdfboss-text/src/lib.rs expansions_from_property_lists_reach_the_spans, expansions_from_structure_elements_reach_the_spans). No output adapter renders an expansion.
14.10.2Web Capture Information Dictionaryout of scope00pdfboss reads, renders and writes PDFs and is not a web-capture tool, so /SpiderInfo is ignored.
14.10.3Content Databaseout of scope00Web capture is outside pdfboss's scope; the /IDS and /URLS name trees are ignored.
14.10.4Content Setsout of scope00Web capture is outside pdfboss's scope; /SPS and /SIS content sets are ignored.
14.10.5Source Informationout of scope00Web capture is outside pdfboss's scope; source information dictionaries are ignored.
14.10.6Object Attributes Related to Web Captureout of scope00Web capture is outside pdfboss's scope; the /ID entry on page and XObject dictionaries is ignored (the trailer /ID is a different key and is handled under 14.4).
14.11.2Page boxesimplemented65MediaBox, CropBox, BleedBox, TrimBox and ArtBox are read with the clause's defaults (crop falls back to media, the other three to crop), clipped to the media box and treated as non-inheritable, in the sync, async and Python APIs.
14.11.2.2Display of page boxesincomplete10Rendering maps the CropBox (turned by /Rotate) onto the output image as the clause's default visible area, but /BoxColorInfo is never read, no option renders the media, bleed, trim or art box instead, and no test renders a page whose CropBox is narrower than its MediaBox.
14.11.3Printer’s Marksnot implemented00Nothing recognizes the PrinterMark annotation subtype or its /MN entry; such an annotation paints only incidentally through the generic appearance-stream path at crates/pdfboss-render/src/executor.rs, which skips only Popup, Hidden and NoView annotations.
14.11.4Separation Dictionariesimplemented74crates/pdfboss-core/src/separation_info.rs separation_info_with reads a page's /SeparationInfo dictionary (Table 364) into SeparationInfo: the /Pages references of the separation set, the /DeviceColorant as a name or text string and the /ColorSpace array as written; a dictionary naming no colorant reads as absent. Document::separation_info and AsyncDocument::separation_info expose it and pdfboss info counts the pre-separated pages with their colorants (tests separation_info.rs reads_the_separation_dictionary and missing_or_malformed_separation_info_reads_as_absent, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_counts_separations). Nothing recombines separations into a composite page, and pdfboss-write emits no separation dictionary.
14.11.5Output Intentsincomplete84crates/pdfboss-core/src/output_intent.rs output_intents_with reads the catalog's /OutputIntents array into OutputIntent records with the Table 365 entries (S, OutputCondition, OutputConditionIdentifier, RegistryName, Info and the DestOutputProfile reference), skipping entries without the required /S; Document::output_intents and AsyncDocument::output_intents expose them and pdfboss info prints one line per intent (tests output_intent.rs reads_every_entry_of_the_output_intent_dictionaries, crates/pdfboss-aio/tests/parity.rs, crates/pdfboss-cli/src/main.rs info_text_lists_output_intents). Rendering never applies an intent's destination profile even though pdfboss-icc could; colours are converted from their source spaces only.
14.11.6Trapping Supportout of scope00Trapping is a prepress step pdfboss does not perform; /TrapNet annotations get no special reading or painting (an appearance stream would paint like any other annotation).
14.11.7Open Prepress Interface (OPI)out of scope00OPI proxy substitution is a prepress workflow pdfboss does not take part in; /OPI on image and form XObjects is ignored and the XObject itself is used.

Annexes

ClauseFeatureStatusCode citationsTest citationsNote
A.2PDF Content Stream Operatorsimplemented219Every operator in Table A.1 parses into an Op (BI, ID and EI become one InlineImage op; f and F share Op::Fill) and none is unparsed; the rasterizer treats ri and i as an explicit no-op (executor.rs) and lets MP, DP, BX, EX, ET and d0 fall to the empty arm at executor.rs, while d1 is only checked for presence at executor.rs to choose the uncolored Type3 glyph path.
B.2Arithmetic Operatorsimplemented24All 21 arithmetic operators (abs add atan ceiling cos cvi cvr div exp floor idiv ln log mod mul neg round sin sqrt sub truncate) are recognized at shading.rs and evaluated by run_calculator; the tests feed add, mul, exp, log, sqrt, div, ln, atan, idiv, mod, round, truncate, floor, ceiling and cvi, leaving abs, neg, sub, sin, cos and cvr without a direct test.
B.3Relational, Boolean and Bitwise Operatorsimplemented23and bitshift eq false ge gt le lt ne not or true xor are all recognized; and, or, xor and not split on boolean versus integer operands, bitshift shifts in both directions, and eq/ne handle mixed-type operands.
B.4Conditional Operatorsimplemented11if and ifelse compile procedure blocks into forward jumps, nested conditionals branch correctly, and a block not followed by if or ifelse is a load error (shading.rs).
B.5Stack Operatorsimplemented22copy dup exch index pop roll are recognized, roll rotates in both directions, and the operand stack is capped at 100 entries (CALC_STACK, shading.rs) as §7.10.5.1 requires, with a 10,000-step runaway limit.
C.2Architectural Limitsimplemented28The parser meets or exceeds every Table C.1 minimum (integers are i64 and reals f64 with integer overflow degrading to a real, no cap on string, name, array or dictionary size, CID range 0 to 0xFFFF) and sets its own caps above the spec's minima: object nesting 128 (spec 28), q/Q depth 64 (spec 28), indirect-reference chain 32 (MAX_RESOLVE_DEPTH), page-tree depth 256, form nesting 16; it never rejects a file for exceeding Table C.1.
C.3Memory Limitsimplemented22Memory is bounded per stream rather than per document: each decoded stream is capped at 256 MiB (MAX_DECODED_LEN) so a decompression bomb is an error, CCITT checks its packed size against that cap and JBIG2 budgets its regions up front, and the aio tail scan stops at 64 KiB; there is no document-wide object-count or total-memory cap.
D.2Latin Character Set and Encodingsincomplete911The StandardEncoding, MacRomanEncoding and WinAnsiEncoding columns exist as code-to-Unicode tables (glyph-name tables only for Standard and WinAnsi), the PDFDocEncoding column decodes text strings (crates/pdfboss-core/src/object.rs pdf_doc_char) and pdfboss-write encodes text as WinAnsi; missing are a MacRoman glyph-name table, the WinAnsi footnote mapping unused codes above 0x27 to bullet (0x81, 0x8D, 0x8F, 0x90 and 0x9D return None), and /StandardEncoding named explicitly is only honored because it falls through the default arm at font.rs.
D.3PDFDocEncoding Character Setimplemented25The full PDFDocEncoding table decodes text strings without a BOM (crates/pdfboss-core/src/object.rs pdf_doc_char: accents at 0x18-0x1F, punctuation and ligatures at 0x80-0x9E, Euro at 0xA0, undefined codes to U+FFFD); the writer emits text strings as ASCII or UTF-16BE, which the clause allows, so no encoder is needed.
D.4Expert Set and MacExpertEncodingnot implemented00No MacExpertEncoding table exists anywhere; a font naming /MacExpertEncoding falls through the default arm at crates/pdfboss-text/src/font.rs and is decoded as StandardEncoding.
D.5Symbol Set and Encodingimplemented72crates/pdfboss-encoding/src/symbol.rs SYMBOL holds the 189 code-to-glyph-name pairs of the annex (no apple at 0o360), written by a script from the standard's text and cross-checked against unicode.org's Adobe Symbol mapping; lib.rs symbol_glyph_name and symbol expose them, the character coming through the Adobe Glyph List, so Omega, Delta and mu read as the ohm, increment and micro signs as pdf.js reads them (test lib.rs symbol_encoding_follows_annex_d5). Text extraction takes the table as the base encoding of a font whose /BaseFont family is Symbol when the file names no encoding and embeds no Type 1 program, /Differences on top (crates/pdfboss-text/src/font.rs standard_symbol_encoding, test symbol_and_zapf_dingbats_decode_through_their_built_in_encodings); the writer encodes Symbol text through the same table (crates/pdfboss-write/src/font.rs Standard14::decoder, test symbol_faces_encode_through_their_built_in_encodings). Rendering still leaves non-embedded Symbol unpainted, there being no license-clean substitute face (crates/pdfboss-render/src/substitute.rs), and no metrics are bundled for it (9.6.2.2).
D.6ZapfDingbats Set and Encodingimplemented62crates/pdfboss-encoding/src/symbol.rs ZAPF_DINGBATS holds the 188 codes of the annex (space and 187 dingbats; the annex leaves 0o200 to 0o215 out) with their glyph names and, since the Adobe Glyph List does not carry the a1 to a191 names, the Unicode characters of unicode.org's Adobe ZapfDingbats mapping; lib.rs zapf_dingbats_glyph_name and zapf_dingbats expose them (test lib.rs zapf_dingbats_encoding_follows_annex_d6). Text extraction and the writer use the table the same way as Symbol's (crates/pdfboss-text/src/font.rs standard_symbol_encoding, crates/pdfboss-write/src/font.rs Standard14::decoder). Rendering still leaves non-embedded ZapfDingbats unpainted and no metrics are bundled for it (9.6.2.2).
E.2Name Registryout of scope00pdfboss neither registers names nor checks second-class or third-class prefixes; unknown dictionary keys pass through as plain names, and registration is a publishing process rather than a parser feature.
F.2Background and Assumptionsout of scope00Design background for linearization with no requirement of its own; the file structure it motivates is assessed under F.3 and F.4.
F.3Linearized PDF Document Structureincomplete106crates/pdfboss-core/src/linearization.rs linearization_dictionary reads the linearization parameter dictionary of Table F.1 from the first object within the first 1024 bytes (Linearized, L, the H offset and length pairs, O, E, N, T, and P with its default of 0); Document::linearization and AsyncDocument::linearization return it as written, the async document from the header window it fetches at open, and is_linearized applies the /L rule of Table F.1: a length that no longer matches the file, as after an appended update, makes the file ordinary PDF (tests linearization.rs a_document_is_linearized_only_while_the_declared_length_matches, crates/pdfboss-aio/tests/parity.rs linearization_dictionaries_agree). The CLI's info prints the result (crates/pdfboss-cli/src/main.rs info_text). Nothing acts on the dictionary: pdfboss-aio still locates objects by scanning the tail for startxref and following the xref chain (crates/pdfboss-aio/src/document.rs find_tail) instead of reading the first-page cross-reference table and first-page section it points to, and pdfboss-write emits no /Linearized dictionary or first-page section (crates/pdfboss-write/src/writer.rs writes a plain header).
F.4Hint Tablesnot implemented00No hint stream is parsed or produced; the /H offsets and the page offset and shared object hint tables are never read.
IPDF Versions and Compatibilityincomplete25The %PDF-M.m header is parsed within the first 1 KiB with one to three digit components, a missing or malformed header silently defaults to 1.4, any parsed version including future ones is accepted without a warning, unknown operators and keys are skipped as Annex I asks, and pdfboss-write emits a configurable header; missing is the catalog /Version override, which no crate reads.