From 2c946e50e0954741cefbe4393b78e91ae5303312 Mon Sep 17 00:00:00 2001 From: as-op Date: Wed, 10 Jul 2024 15:27:58 +0200 Subject: [PATCH 1/3] add page break plugin and html filter exception --- package-lock.json | 19 +++++++++++++++++++ package.json | 1 + src/commonmark/commonmarkdataprocessor.js | 14 ++++++++++++++ src/op-ckeditor.js | 2 ++ src/op-plugins.js | 2 ++ 5 files changed, 38 insertions(+) diff --git a/package-lock.json b/package-lock.json index 368b627..731525a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "@ckeditor/ckeditor5-list": "41.1.0", "@ckeditor/ckeditor5-media-embed": "41.1.0", "@ckeditor/ckeditor5-mention": "41.1.0", + "@ckeditor/ckeditor5-page-break": "41.1.0", "@ckeditor/ckeditor5-paragraph": "41.1.0", "@ckeditor/ckeditor5-paste-from-office": "41.1.0", "@ckeditor/ckeditor5-remove-format": "41.1.0", @@ -2408,6 +2409,15 @@ "lodash-es": "4.17.21" } }, + "node_modules/@ckeditor/ckeditor5-page-break": { + "version": "41.1.0", + "resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-page-break/-/ckeditor5-page-break-41.1.0.tgz", + "integrity": "sha512-Z6lurmisqQg6RnJ/y85gvDYamNbG5wZuHhkawxk1hujhDMECjS2qaQL1eYYc4w8evwteTZAIkGNaimZ6/QuuEA==", + "dev": true, + "dependencies": { + "ckeditor5": "41.1.0" + } + }, "node_modules/@ckeditor/ckeditor5-paragraph": { "version": "41.1.0", "resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-paragraph/-/ckeditor5-paragraph-41.1.0.tgz", @@ -11689,6 +11699,15 @@ "lodash-es": "4.17.21" } }, + "@ckeditor/ckeditor5-page-break": { + "version": "41.1.0", + "resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-page-break/-/ckeditor5-page-break-41.1.0.tgz", + "integrity": "sha512-Z6lurmisqQg6RnJ/y85gvDYamNbG5wZuHhkawxk1hujhDMECjS2qaQL1eYYc4w8evwteTZAIkGNaimZ6/QuuEA==", + "dev": true, + "requires": { + "ckeditor5": "41.1.0" + } + }, "@ckeditor/ckeditor5-paragraph": { "version": "41.1.0", "resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-paragraph/-/ckeditor5-paragraph-41.1.0.tgz", diff --git a/package.json b/package.json index 72f33f4..5273fa8 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@ckeditor/ckeditor5-media-embed": "41.1.0", "@ckeditor/ckeditor5-mention": "41.1.0", "@ckeditor/ckeditor5-paragraph": "41.1.0", + "@ckeditor/ckeditor5-page-break": "41.1.0", "@ckeditor/ckeditor5-paste-from-office": "41.1.0", "@ckeditor/ckeditor5-remove-format": "41.1.0", "@ckeditor/ckeditor5-table": "41.1.0", diff --git a/src/commonmark/commonmarkdataprocessor.js b/src/commonmark/commonmarkdataprocessor.js index f3c9a21..0919bd7 100644 --- a/src/commonmark/commonmarkdataprocessor.js +++ b/src/commonmark/commonmarkdataprocessor.js @@ -179,6 +179,20 @@ export default class CommonMarkDataProcessor { } }); + + turndownService.addRule( 'openProjectPageBreak', { + filter: (node) => { + return ( + node.nodeName === 'DIV' && + node.classList.contains('page-break') + ) + }, + replacement: ( _content, node ) => { + // return the page break in a format CKEditor detects as page break if rereading it + return '
'; + } + }); + turndownService.addRule( 'mentions', { filter: (node) => { return ( diff --git a/src/op-ckeditor.js b/src/op-ckeditor.js index 5f7c132..8a0fe6e 100644 --- a/src/op-ckeditor.js +++ b/src/op-ckeditor.js @@ -40,6 +40,8 @@ FullEditor.defaultConfig.toolbar = { 'redo', 'openProjectShowFormattingHelp', '|', + 'pageBreak', + '|', 'preview', 'opShowSource' ] diff --git a/src/op-plugins.js b/src/op-plugins.js index 6e467d8..e0b19f9 100644 --- a/src/op-plugins.js +++ b/src/op-plugins.js @@ -40,6 +40,7 @@ import { ImageResize } from '@ckeditor/ckeditor5-image'; import OpCustomCssClassesPlugin from "./plugins/op-custom-css-classes-plugin"; import { ImageBlock } from '@ckeditor/ckeditor5-image'; import { ImageInline } from '@ckeditor/ckeditor5-image'; +import { PageBreak } from '@ckeditor/ckeditor5-page-break'; // We divide our plugins into separate concerns here // in order to enable / disable each group by configuration @@ -75,6 +76,7 @@ export const builtinPlugins = [ Link, List, TodoList, + PageBreak, Paragraph, Typing, From 63d71e640b2a93e33c15f65d1cc8df55437ebedc Mon Sep 17 00:00:00 2001 From: as-op Date: Mon, 26 Aug 2024 16:32:02 +0200 Subject: [PATCH 2/3] re-add deps --- package-lock.json | 1 + package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/package-lock.json b/package-lock.json index c396bd5..f7c0f11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "@ckeditor/ckeditor5-list": "43.0.0", "@ckeditor/ckeditor5-media-embed": "43.0.0", "@ckeditor/ckeditor5-mention": "43.0.0", + "@ckeditor/ckeditor5-page-break": "43.0.0", "@ckeditor/ckeditor5-paragraph": "43.0.0", "@ckeditor/ckeditor5-paste-from-office": "43.0.0", "@ckeditor/ckeditor5-remove-format": "43.0.0", diff --git a/package.json b/package.json index 9f212c9..9649d32 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@ckeditor/ckeditor5-list": "43.0.0", "@ckeditor/ckeditor5-media-embed": "43.0.0", "@ckeditor/ckeditor5-mention": "43.0.0", + "@ckeditor/ckeditor5-page-break": "43.0.0", "@ckeditor/ckeditor5-paragraph": "43.0.0", "@ckeditor/ckeditor5-paste-from-office": "43.0.0", "@ckeditor/ckeditor5-remove-format": "43.0.0", From afb3ad51cab4758cd781ac292c9338c1cce2d105 Mon Sep 17 00:00:00 2001 From: as-op Date: Tue, 24 Sep 2024 12:10:44 +0200 Subject: [PATCH 3/3] merge release branch & add test --- src/commonmark/commonmarkdataprocessor.js | 9 ++++++--- src/commonmark/utils/fix-breaks.js | 12 ++++++++++-- src/commonmark/utils/page-breaks.js | 6 ++++++ tests/commonmark/page-brakes.test.js | 15 +++++++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 src/commonmark/utils/page-breaks.js create mode 100644 tests/commonmark/page-brakes.test.js diff --git a/src/commonmark/commonmarkdataprocessor.js b/src/commonmark/commonmarkdataprocessor.js index b0b1a4e..14a4146 100644 --- a/src/commonmark/commonmarkdataprocessor.js +++ b/src/commonmark/commonmarkdataprocessor.js @@ -17,6 +17,7 @@ import {fixTasklistWhitespaces} from './utils/fix-tasklist-whitespaces'; import {fixBreaksInTables, fixBreaksInLists, fixBreaksOnRootLevel} from "./utils/fix-breaks"; import markdownIt from 'markdown-it'; import markdownItTaskLists from 'markdown-it-task-lists'; +import {isPageBreakNode, PAGE_BREAK_MARKDOWN} from "./utils/page-breaks"; export const originalSrcAttribute = 'data-original-src'; @@ -240,22 +241,24 @@ export default class CommonMarkDataProcessor { replacement: (_content, node) => { if (!node.parentElement && !node.nextSibling && !node.previousSibling) { //document with only one empty paragraph return ''; + } else if (node.childNodes.length === 1 && isPageBreakNode(node.childNodes[0])) { + return PAGE_BREAK_MARKDOWN + '\n\n' } else { return '
\n\n' } }, }); - turndownService.addRule( 'openProjectPageBreak', { + turndownService.addRule('openProjectPageBreak', { filter: (node) => { return ( node.nodeName === 'DIV' && node.classList.contains('page-break') ) }, - replacement: ( _content, node ) => { + replacement: (_content, _node) => { // return the page break in a format CKEditor detects as page break if rereading it - return '
'; + return PAGE_BREAK_MARKDOWN; } }); diff --git a/src/commonmark/utils/fix-breaks.js b/src/commonmark/utils/fix-breaks.js index 4205b93..dc710a7 100644 --- a/src/commonmark/utils/fix-breaks.js +++ b/src/commonmark/utils/fix-breaks.js @@ -1,3 +1,5 @@ +import {isPageBreakNode} from "./page-breaks"; + /** * Remove breaks in empty table paragraphs * @@ -35,6 +37,7 @@ export function fixBreaksInTables(root) { * e.g. `

Demo



End

` converted to `

Demo




End

` * to avoid these, we exchange all root level breaks with paragraphs * e.g. `

Demo



End

` will be converted to `

Demo

End

` + * (except for page breaks, which are kept but are wrapped in a paragraph) */ export function fixBreaksOnRootLevel(root) { let walker = document.createNodeIterator( @@ -55,8 +58,13 @@ export function fixBreaksOnRootLevel(root) { list.push(node); } for (const node of list) { - root.insertBefore(document.createElement('p'), node); - node.remove(); + const p = document.createElement('p'); + root.insertBefore(p, node); + if (isPageBreakNode(node)) { + p.appendChild(node); + } else { + node.remove(); + } } } diff --git a/src/commonmark/utils/page-breaks.js b/src/commonmark/utils/page-breaks.js new file mode 100644 index 0000000..08307ac --- /dev/null +++ b/src/commonmark/utils/page-breaks.js @@ -0,0 +1,6 @@ + +export const PAGE_BREAK_MARKDOWN = '
'; +export function isPageBreakNode(node) { + const style = node.getAttribute('style') || ''; + return style.includes('page-break-'); +} diff --git a/tests/commonmark/page-brakes.test.js b/tests/commonmark/page-brakes.test.js new file mode 100644 index 0000000..c42d523 --- /dev/null +++ b/tests/commonmark/page-brakes.test.js @@ -0,0 +1,15 @@ +import {testDataProcessor} from './_utils/utils.js'; + +describe('CommonMarkProcessor', () => { + describe('page breaks', () => { + it('should process page break', () => { + testDataProcessor( + 'First page\n\n' + + '
\n\n' + + 'Second page', + + '

First page



Second page

' + ); + }); + }); +});