diff --git a/src/lib/Parser.ts b/src/lib/Parser.ts
index 8081cb8..8f4c429 100644
--- a/src/lib/Parser.ts
+++ b/src/lib/Parser.ts
@@ -65,12 +65,19 @@ export class Parser {
/**
* Adds the given _text_ to the document, either by appending it to a
* preceding `XmlText` node (if possible) or by creating a new `XmlText` node.
+ *
+ * When _normalize_ is `true` (the default), line breaks in _text_ are
+ * normalized per section 2.11 of the XML spec. This must be `false` for text
+ * that comes from a character or entity reference, since references aren't
+ * subject to line break normalization.
*/
- addText(text: string, charIndex: number) {
+ addText(text: string, charIndex: number, normalize = true) {
let { children } = this.currentNode;
let { length } = children;
- text = normalizeLineBreaks(text);
+ if (normalize) {
+ text = normalizeLineBreaks(text);
+ }
if (length > 0) {
let prevNode = children[length - 1];
@@ -297,7 +304,7 @@ export class Parser {
let ref = this.consumeReference();
return ref
- ? this.addText(ref, startIndex)
+ ? this.addText(ref, startIndex, false)
: false;
}
diff --git a/tests/lib/Parser.test.js b/tests/lib/Parser.test.js
index 1eddd91..d7f5f6e 100644
--- a/tests/lib/Parser.test.js
+++ b/tests/lib/Parser.test.js
@@ -390,6 +390,24 @@ describe('Parser', () => {
assert.strictEqual(root.attributes.d, " a z ");
});
+ // A character reference for a carriage return (`
` / `
`) in content
+ // must be preserved as a literal `\r`. Line break normalization only applies
+ // to literal line breaks in the input, not to characters that come from a
+ // character reference.
+ // https://www.w3.org/TR/2008/REC-xml-20081126/#sec-line-ends
+ // https://www.w3.org/TR/2008/REC-xml-20081126/#entproc
+ it("doesn't normalize a character reference for a carriage return in content", () => {
+ assert.strictEqual(parseXml('
').root.children[0].text, '\r');
+ assert.strictEqual(parseXml('
').root.children[0].text, '\r');
+
+ // A literal `\r` is normalized to `\n`, but a `
` reference following
+ // it must remain a `\r`.
+ assert.strictEqual(parseXml('x\r
y').root.children[0].text, 'x\n\ry');
+
+ // `
` must remain `\r\n` rather than collapsing to `\n` or `\n\n`.
+ assert.strictEqual(parseXml('
').root.children[0].text, '\r\n');
+ });
+
it('handles many character references in a single attribute', () => {
let { root } = parseXml('');
assert.strictEqual(root.attributes.b, "<".repeat(35));