diff --git a/apps/web/src/components/files/FilePreviewPanel.test.ts b/apps/web/src/components/files/FilePreviewPanel.test.ts index ddc77e5748f97..25816f1fe917e 100644 --- a/apps/web/src/components/files/FilePreviewPanel.test.ts +++ b/apps/web/src/components/files/FilePreviewPanel.test.ts @@ -189,6 +189,24 @@ describe("document preview contents", () => { expect(html).not.toContain("https:"); }); + it("treats a backslash before a line break as a paragraph break, as TextEdit writes them", () => { + const html = (rtf: string) => { + const result = openRtf(bytes(rtf)); + if (result.type !== "document") throw new Error("Expected document"); + return result.pages.map((page) => page.html); + }; + const textEdit = String.raw`{\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;} +\pard\f0\fs24 \cf0 First\ +Second\ +\ +Third}`; + const expected = html( + String.raw`{\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\pard\f0\fs24 \cf0 First\par Second\par \par Third}`, + ); + expect(html(textEdit)).toEqual(expected); + expect(html(textEdit.replaceAll("\n", "\r\n"))).toEqual(expected); + }); + it("preserves rich text page breaks and formatting without splitting hidden destinations", () => { const result = openRtf( bytes(String.raw`{\rtf1\b First\page Second\b0{\*\unknown \page hidden}\page Third\page}`), diff --git a/apps/web/src/components/files/extendedDocumentPreview.ts b/apps/web/src/components/files/extendedDocumentPreview.ts index 46cf7dbb67a83..abd07afb23c68 100644 --- a/apps/web/src/components/files/extendedDocumentPreview.ts +++ b/apps/web/src/components/files/extendedDocumentPreview.ts @@ -565,6 +565,8 @@ export function openRtf(bytes: ArrayBuffer): OfficeWorkerResult { const match = /^([a-z]+)(-?\d+)? ?/i.exec(source.slice(i)); if (!match) { if (next === "~") emit("\u00a0"); + // A backslash before a line break is \par; TextEdit ends paragraphs this way. + else if ((next === "\n" || next === "\r") && !state.skip) html += "
"; i++; continue; }