diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ad277b..3d46e71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- The `html` output method now names the document type declaration `HTML` rather than the document element name (XSLT 1.0 16.2: "The name following the `` regardless of the result's root element; the `xml` method still uses the document element name (16.1). Closes Apache Xalan conformance cases `output40`, `output48`, and `output60`. + - The `html` output method no longer escapes a `<` character in an attribute value (XSLT 1.0 16.2). The HTML output method escapes `&` and `"` in attribute values but leaves `<` and `>` literal, unlike the `xml` method; the serializer was escaping `<` to `<` there. The `xml` output method is unchanged (it still escapes both). Closes Apache Xalan conformance cases `output49` and `output74`. - A string-form `document()` reference now resolves against the base URI of the stylesheet element holding it (XSLT 1.0 12.1), which for a declaration in an `xsl:include`d or `xsl:import`ed file is that file's URI rather than the top stylesheet's. The parser already tracked each top-level declaration's source-file base; that base is now carried onto the compiled templates and global variables and threaded into expression evaluation, so a relative `document('data.xml')` (or `document('')`, the self-reference) in an included file resolves against the included file, not the importing one. Closes Apache Xalan conformance cases `mdocs12` and `mdocs13`. diff --git a/Sources/XSLT/XSLTTransform.swift b/Sources/XSLT/XSLTTransform.swift index 4add3ca..45f443d 100644 --- a/Sources/XSLT/XSLTTransform.swift +++ b/Sources/XSLT/XSLTTransform.swift @@ -186,7 +186,12 @@ public extension PureXML.XSLT { /// The `` prologue for the result's root element when the output /// declares `doctype-system` (optionally with `doctype-public`), else empty. private static func doctype(for result: PureXML.Model.Node, _ output: Output, method: String) -> String { - guard let name = rootName(of: result) else { return "" } + // A doctype precedes the first element, so emit none without one. The + // html output method then names it HTML (16.2: "The name following + // \n" diff --git a/Tests/Fixtures/xalan-baseline.txt b/Tests/Fixtures/xalan-baseline.txt index f76b0a3..478ce94 100644 --- a/Tests/Fixtures/xalan-baseline.txt +++ b/Tests/Fixtures/xalan-baseline.txt @@ -29,9 +29,6 @@ numbering/numbering63 output/output22 output/output26 output/output32 -output/output40 -output/output48 -output/output60 output/output63 output/output70 output/output72 diff --git a/Tests/XSLTDoctypeOutputTests.swift b/Tests/XSLTDoctypeOutputTests.swift index 6a5fd49..10b7099 100644 --- a/Tests/XSLTDoctypeOutputTests.swift +++ b/Tests/XSLTDoctypeOutputTests.swift @@ -20,17 +20,44 @@ struct XSLTDoctypeOutputTests { #expect(try transform(style, "") == "\n") } - @Test("doctype-public and doctype-system emit a PUBLIC doctype") + @Test("the html method names the doctype HTML regardless of the document element") func test_publicAndSystem() throws { + // XSLT 1.0 16.2: the html output method's doctype name is HTML, not the + // document element name (here `root`), unlike the xml method (Apache + // Xalan output40, output48, output60). let style = """ - + """ let out = try transform(style, "") - #expect(out.hasPrefix("\n")) - #expect(out.contains("")) + #expect(out.hasPrefix("\n")) + #expect(out.contains("")) + } + + @Test("the xml method still uses the document element name for the doctype") + func test_xmlMethodUsesElementName() throws { + let style = """ + + + + + """ + #expect(try transform(style, "") == "\n") + } + + @Test("the html method emits no doctype when the result has no element") + func test_htmlNoElementNoDoctype() throws { + // A doctype precedes the first element (16.2); a text-only result has + // none, so even the html method with doctype-public emits nothing. + let style = """ + + + just text + + """ + #expect(try transform(style, "") == "just text") } @Test("Without doctype-system no doctype is emitted")