Commit Graph

159 Commits

Author SHA1 Message Date
e62995a7a8 feat(emitting): output encoding for UTF and single-byte families
Add PureXML.Parsing.ByteEncoder, the inverse of ByteDecoder, and a
Serializer.serialize(_:encoding:) / PureXML.serialize(_:encoding:) that
emits bytes in a chosen encoding (the libxml2 save-with-encoding model).
Covers the Unicode transformation formats and the single-byte legacy
families, built by inverting the existing vendored to-Unicode tables, so
no new data is introduced. The declaration carries the canonical encoding
name, a UTF-16/32 stream is preceded by a byte-order mark, and any
unrepresentable scalar becomes a decimal numeric character reference, so
output round-trips. CJK output encodings remain (tracked on #106).

Part of #106, #105
2026-06-09 09:32:39 +02:00
36a40eec08 fix(xsd): locate identity-constraint errors at the offending node
Refine the identity-constraint path: rather than locating at the element
that declares the key, walk from the constraint-bearing element down to the
selected target and report the actual offender, so a duplicate key renders
"at path: list/item[2]" and a dangling keyref "at path: orders/line".
Also locate the "no element declaration for the root" XSD error at the root
element, matching the namespace check beside it that already did so. Lock
both with coding-path assertions in the identity tests.
2026-06-09 08:57:33 +02:00
9730903803 fix(xsd): locate identity-constraint errors at their element
The IdentityValidator emitted every key/keyref/unique error with an empty
coding path (at: []), so each rendered "at root of document" regardless of
which element violated the constraint. Thread a [PathKey] through the walk,
the way the content-model validator already does, so the failures carry the
constraining element's path. This restores the validation framework's
path-carrying contract for the one rule that was bypassing it.
2026-06-09 08:50:39 +02:00
74acad4628 feat(parsing): Big5 (Traditional Chinese / HKSCS)
Add the WHATWG big5 decoder over the vendored index-big5 (18,590 entries),
for both the whole-buffer and streaming decoders. A lead in 0x81-0xFE plus
a trail in 0x40-0x7E or 0xA1-0xFE resolves through the index, covering
Traditional Chinese and the HKSCS extensions including the astral CJK
ideographs. Four pointers decode to a base letter plus a combining mark.
Selected by the big5 / big5-hkscs declaration names.

Closes #103
2026-06-09 08:30:57 +02:00
2b2c1fb692 feat(parsing): full GB18030 four-byte sequences
Extend the GBK two-byte subset to full GB18030: a lead byte followed by
a digit opens a four-byte form whose pointer is mapped through the WHATWG
index-gb18030-ranges linear ranges, reaching the supplementary planes.
The 207 ranges are vendored verbatim. Wired into the whole-buffer and
streaming decoders and the encoding-name table (gb18030/gb-18030).

Closes #102
2026-06-09 08:24:00 +02:00
b688952cef feat(parsing): GBK/GB2312 decoding completes the CJK encodings (#99) 2026-06-09 08:08:52 +02:00
7f2991a90b Merge #99 (stage 3): EUC-KR 2026-06-09 08:03:02 +02:00
d8b2db4943 feat(parsing): EUC-KR (CP949) decoding with vendored index (#99) 2026-06-09 08:02:14 +02:00
5b5ee7b3fe feat(parsing): EUC-JP decoding with vendored JIS X 0212 index (#99) 2026-06-09 07:57:52 +02:00
4ff9306a0e feat(parsing): Shift-JIS decoding with vendored JIS X 0208 index (#99) 2026-06-09 07:54:05 +02:00
a07537007e feat(html): vendor the complete WHATWG named entity set (#84) 2026-06-09 07:45:55 +02:00
7eb33c0d08 feat(html): full HTML 4.01 named entity set (#84) 2026-06-09 07:39:32 +02:00
3f45dacf62 feat(parsing): Windows-1255/1256/1258, KOI8-U complete the single-byte set (#97) 2026-06-09 07:29:25 +02:00
5646e81b94 feat(parsing): Windows-1250/1251/1253/1257, KOI8-R, ISO-8859-11 (#97) 2026-06-09 07:25:18 +02:00
3f293ec1e7 feat(parsing): ISO-8859-6/8/10/14/16 vendored from Unicode mappings (#97) 2026-06-09 07:19:50 +02:00
43dc7b00c4 feat(parsing): ISO-8859-3/4/7/13 vendored from Unicode mappings (#97) 2026-06-09 07:15:35 +02:00
5f35621ae6 feat(parsing): ISO-8859-2 (Latin-2) vendored from Unicode mapping (#97) 2026-06-09 07:08:19 +02:00
a005def333 feat(parsing): Windows-1254 (Turkish) encoding (#97) 2026-06-09 07:02:13 +02:00
94890172f2 feat(parsing): single-byte encoding framework + ISO-8859-5/9/15 (#97) 2026-06-09 06:57:21 +02:00
a5b032e9dc feat(html): in-select-in-table closes select on table tags (#83) 2026-06-09 06:37:35 +02:00
3ebbaff07c feat(html): select content model (optgroup/nested-select/input close) (#83) 2026-06-09 06:32:24 +02:00
7add3b3fa5 refactor(html): build head on the live TreeNode model, remove DocFrame 2026-06-09 06:29:00 +02:00
b245640ee2 fix(html): keep template content nested instead of splitting to body (#83) 2026-06-09 06:21:10 +02:00
f637b8e154 feat(html): frameset documents and void frame element (#83) 2026-06-09 06:17:55 +02:00
2945de0855 feat(html): SVG HTML integration points switch back to HTML namespace (#83) 2026-06-09 06:06:54 +02:00
47e0e3e644 feat(html): table foster-parenting for stray content (#83) 2026-06-09 06:04:01 +02:00
038774cfc2 feat(html): SVG attribute-name case adjustment in foreign content (#83) 2026-06-09 05:52:26 +02:00
9092ab3dbd feat(html): adoption agency algorithm and active formatting elements (#83) 2026-06-09 05:46:12 +02:00
fccb407797 refactor(html): build body as a live mutable TreeNode open-elements stack (#83) 2026-06-09 05:35:26 +02:00
40c5973d5a feat(html): SVG element-name case adjustment in foreign content (#83) 2026-06-09 05:29:14 +02:00
d64bcaf2fa feat(html): SVG/MathML foreign-content namespace switching (#83) 2026-06-09 05:22:37 +02:00
b7f7e15fbc feat(html): table tree construction with implied tbody/tr (#83) 2026-06-09 05:18:13 +02:00
e86c87af3c feat(html): numeric-ref validation, NUL, RCDATA decoding, semicolon-less refs (#84) 2026-06-09 05:11:18 +02:00
3a55eeeaa6 feat(html): conformance validator as composable Validation rules 2026-06-09 04:39:03 +02:00
506c502602 refactor(validation): decompose DTD attribute checks into per-constraint rules (#101) 2026-06-09 04:28:03 +02:00
ecb9b8c881 feat(xinclude): range inclusion via XPointer range model (#87) 2026-06-09 04:19:38 +02:00
8871fee977 feat(xpointer): range(), range-to(), and string-range() schemes (#88) 2026-06-09 04:16:23 +02:00
b3bbacb4e8 feat(canonical): C14N 2.0 sequential prefix rewrite, QName-aware values, node-set selection (#85) 2026-06-09 04:06:02 +02:00
e77b39a449 feat(canonical): node-subset C14N with ancestor namespace and xml:* inheritance (#85) 2026-06-09 03:55:54 +02:00
87fd7ba889 feat(emitting): textEscaping round-trip option to preserve carriage returns (#91) 2026-06-09 03:48:20 +02:00
46131bff1b feat(model): first-class doctype/entity-reference/namespace nodes, ownerDocument, adoption (#90) 2026-06-09 03:43:24 +02:00
2db9915fb2 feat(schematron): support key(), document(), and current() in tests
Schematron tests, lets, and message expressions can now use key() backed
by xsl:key declarations parsed from the schema, document() loading an
external file through an injected documentLoader, and current() resolving
to the rule context node. A node-keyed function-table factory threads
through the evaluation. Completes the Schematron audit.
2026-06-09 03:30:43 +02:00
dbdf98e231 feat(relaxng): skip RNC >> follow-annotations, completing #79
A >> follow-annotation after a pattern (a foreign element name with
optional [ ... ] content) is skipped, chaining and combining with leading
annotations; neither carries schema semantics. Namespace declarations
inside annotations were already dropped by the annotation skipper, so the
RNC compact syntax is now fully covered.
2026-06-09 03:20:47 +02:00
7c24ada92b feat(xslt): document() fragments and base-URI resolution, completing #82
A document() reference with a #fragment selects a subset of the loaded
document via XPointer; a node-set first argument unions the documents
named by each node's string value; and a relative URI is resolved against
a caller-supplied baseURI (the loader is wrapped so xsl:include/xsl:import
resolve the same way). Closes the XSLT function/output audit.
2026-06-09 03:12:49 +02:00
4ea84b3d58 feat(xslt): make result-tree fragments usable as node-sets
An xsl:variable with a body now binds a queryable document fragment (its
instantiated result converted to a TreeNode document) instead of a plain
string, so count($rtf/*), $rtf/child, and for-each over $rtf/... work.
It still has its concatenated text as a string value, so existing string
uses are unchanged. Part of #82.
2026-06-09 03:04:56 +02:00
3fec78da51 feat(xslt): implement xsl:output cdata-section-elements
The text content of the named result elements is emitted in CDATA
sections rather than escaped: the result tree's text children of those
elements are converted to CDATA nodes before serialization, leaving
nested and other elements unaffected. Part of #82.
2026-06-09 02:58:34 +02:00
2a40300479 feat(xslt): emit xsl:output doctype-public/doctype-system
When doctype-system (optionally with doctype-public) is set on xsl:output,
a <!DOCTYPE> for the result's root element is emitted before the
serialized output: PUBLIC when both identifiers are given, SYSTEM
otherwise. Works for the XML and HTML output methods. Part of #82.
2026-06-09 02:53:50 +02:00
47b88f19b2 feat(xslt): key() over a node-set second argument
key(name, node-set), including key('k', .), now unions the indexed nodes
for every value node's string value and de-duplicates, instead of using
only the first node's string value. A string value still works directly.
Part of #82.
2026-06-09 02:50:24 +02:00
41a48d3130 feat(xslt): serialize method=html output by the HTML rules
An xsl:output method=html result is serialized through PureXML.HTML
(void elements emitted without a self-closing slash, raw-text elements
like script/style left unescaped) instead of as XML. Part of #82.
2026-06-09 02:48:00 +02:00
e30672d72b feat(xslt): implement xsl:sort case-order
case-order=upper-first/lower-first makes the string comparison
case-insensitive, with case used only to break ties among otherwise-equal
strings; without it the default codepoint order is unchanged. Part of #82.
2026-06-09 02:37:43 +02:00