Integration with Export to PDF/Word
This guide is meant to be a point of reference for anyone integrating the Pagination
, Export to PDF
and Export to Word
features together. It describes the whole process from creating a custom editor and configuring it, up to the point of having a working solution.
We strongly advise you to get familiar with all three mentioned plugins’ user guides before reading this guide.
# Before you start
This is a premium feature and a license key is needed to authenticate. If you do not have one yet, please contact us.
If you already have a valid license, please log into your user dashboard to access the feature settings.
You can also sign up for the CKEditor Premium Features 30-day free trial to test the feature.
If you have more than one license for CKEditor premium features (Comments, Track changes, Revision history, or Pagination), you may use any key of those in your CKEditor configuration to activate all of the features.
Since the introduction of single key for all premium features (Comments, Track changes, Revision history, or Pagination), the one key can be used to activate any and all of the features.
# Accessing authorization data
Assuming you have signed up for the CKEditor Premium Features 30-day free trial or you already are a licensed user, you now need to get the authorization data from the CKEditor Ecosystem Dashboard.
To use the pagination feature you will need a license key to enable it in the editor. If this feature is used without it, the editor will initialize in a read-only mode, which prevents user from editing any content. Log in to your CKEditor Ecosystem Dashboard account and follow the guide on getting the license key.
For the export plugins you will need is a special token endpoint. In order to get it, log into your CKEditor Ecosystem Dashboard account and follow the guide on creating token URL. When export features are used without this token, all generated documents will contain a watermark at the bottom of every page.
After you have successfully obtained all the credentials needed, let’s create a custom editor and configure it.
# Creating a custom editor
For the purpose of this guide, we will create a simple editor from scratch. We will base the whole process on the quick start guide, but we will use a decoupled editor instead of classic. This guide assumes that you are familiar with npm and your project uses npm already.
First, install the packages needed to build CKEditor 5:
npm install --save \
postcss-loader@3 \
raw-loader@3 \
style-loader@1 \
webpack@4 \
webpack-cli@3
Next, create webpack.config.js
file with the following content:
// webpack.config.js
'use strict';
const path = require( 'path' );
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );
module.exports = {
// https://webpack.js.org/configuration/entry-context/
entry: './app.js',
// https://webpack.js.org/configuration/output/
output: {
path: path.resolve( __dirname, 'dist' ),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
use: [ 'raw-loader' ]
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
options: {
injectType: 'singletonStyleTag',
attributes: {
'data-cke': true
}
}
},
{
loader: 'postcss-loader',
options: styles.getPostCssConfig( {
themeImporter: {
themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
},
minify: true
} )
}
]
}
]
},
// Useful for debugging.
devtool: 'source-map',
// By default webpack logs warnings if the bundle is bigger than 200kb.
performance: { hints: false }
};
Now let’s install all packages needed to actually create an editor:
npm install --save \
@ckeditor/ckeditor5-dev-utils \
@ckeditor/ckeditor5-editor-decoupled \
@ckeditor/ckeditor5-essentials \
@ckeditor/ckeditor5-paragraph \
@ckeditor/ckeditor5-basic-styles \
@ckeditor/ckeditor5-theme-lark
With all the packages installed and the Webpack config taken care of, we can now proceed with creating our basic editor.
Create two files, first app.js
file:
// app.js
import DecoupledEditor from '@ckeditor/ckeditor5-editor-decoupled/src/decouplededitor';
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
DecoupledEditor
.create( document.querySelector( '#editor' ), {
plugins: [ Essentials, Paragraph, Bold, Italic ],
toolbar: [ 'bold', 'italic' ]
} )
.then( editor => {
console.log( 'Editor was initialized', editor );
const toolbarContainer = document.querySelector( '#toolbar-container' );
toolbarContainer.appendChild( editor.ui.view.toolbar.element );
} )
.catch( error => {
console.error( error.stack );
} );
And index.html
file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pagination - integration with Export to PDF/Word</title>
</head>
<body>
<div class="document-container">
<div id="toolbar-container"></div>
<div class="editor-container">
<div id="editor">
<p>This is the initial editor content.</p>
</div>
</div>
</div>
<script src="dist/bundle.js"></script>
</body>
</html>
The final step for this part of the guide is building the editor with the following command:
./node_modules/.bin/webpack --mode development
However, it will be more handy to add an npm script inside the package.json
file:
"scripts": {
"build": "./node_modules/.bin/webpack --mode development"
}
This way every time you will need to rebuild the editor, you can just run:
npm run build
After the build process is complete, you can open the index.html
file (preferably using some simple server, like, for example http-server).
Now the editor is up and running, however it is very basic. In the next section we will add more plugins and see how to configure them properly.
# Configuration
# Installing additional plugins
Now it is time to install the @ckeditor/ckeditor5-pagination
, @ckeditor/ckeditor5-export-pdf
and @ckeditor/ckeditor5-export-word
packages using npm
. To use the full potential of the pagination plugin, we will also add the @ckeditor/ckeditor5-page-break
feature.
Apart from the plugins listed above, let’s add a few more and make our custom editor more versatile and rich:
npm install --save \
@ckeditor/ckeditor5-pagination \
@ckeditor/ckeditor5-page-break \
@ckeditor/ckeditor5-export-pdf \
@ckeditor/ckeditor5-export-word \
@ckeditor/ckeditor5-cloud-services \
@ckeditor/ckeditor5-list \
@ckeditor/ckeditor5-heading \
@ckeditor/ckeditor5-table \
@ckeditor/ckeditor5-font \
@ckeditor/ckeditor5-block-quote
Read more about this in the installing plugins guide.
Let’s update app.js
, which should now look like this:
// app.js
import DecoupledEditor from '@ckeditor/ckeditor5-editor-decoupled/src/decouplededitor';
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import Heading from '@ckeditor/ckeditor5-heading/src/heading';
import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote';
import Font from '@ckeditor/ckeditor5-font/src/font';
import List from '@ckeditor/ckeditor5-list/src/list';
import Table from '@ckeditor/ckeditor5-table/src/table';
import TableProperties from '@ckeditor/ckeditor5-table/src/tableproperties';
import TableCellProperties from '@ckeditor/ckeditor5-table/src/tablecellproperties';
import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar';
import Pagination from '@ckeditor/ckeditor5-pagination/src/pagination';
import PageBreak from '@ckeditor/ckeditor5-page-break/src/pagebreak';
import ExportPdf from '@ckeditor/ckeditor5-export-pdf/src/exportpdf';
import ExportWord from '@ckeditor/ckeditor5-export-word/src/exportword';
import CloudServices from '@ckeditor/ckeditor5-cloud-services/src/cloudservices';
DecoupledEditor
.create( document.querySelector( '#editor' ), {
plugins: [
Essentials, Paragraph, Bold, Heading, Italic, Font,
BlockQuote, List, Table, TableProperties, TableCellProperties, TableToolbar, PageBreak,
Pagination, ExportPdf, ExportWord, CloudServices
],
toolbar: [
'exportPdf',
'exportWord',
'|',
'previousPage',
'nextPage',
'pageNavigation',
'pageBreak',
'|',
'heading',
'|',
'bold',
'italic',
'|',
'fontSize',
'fontFamily',
'fontColor',
'fontBackgroundColor',
'|',
'blockQuote',
'bulletedList',
'numberedList',
'insertTable'
],
table: {
contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
}
} )
.then( editor => {
console.log( 'Editor was initialized', editor );
const toolbarContainer = document.querySelector( '#toolbar-container' );
toolbarContainer.appendChild( editor.ui.view.toolbar.element );
} )
.catch( error => {
console.error( error.stack );
} );
# Adding proper configuration
Pagination configuration has to be in sync with the configuration of the exports plugins. All three configs need to have the same margin values and page format set. This is due to the handling of default editor styles which we will mention later. For the purpose of this guide, we will use A4 format but of course you can use other paper sizes as well.
You will also need to provide a license key for the pagination feature and also the Cloud Services configuration for the exports plugins (which you obtained in step one).
This guide assumes that we do not use any other plugin that needs a cloudServices
configuration. If we used, for example, real-time collaboration or the EasyImage plugin, we would have to provide all three values, which are: tokenUrl
, webSocketUrl
and uploadUrl
.
After the table
configuration add:
// ...
cloudServices: {
// Provide correct value here. You can find it in the CKEditor Dashboard:
// https://dashboard.ckeditor.com/login
tokenUrl: 'https://example.com/cs-token-endpoint'
},
pagination: {
// Page width and height correspond to A4 format
pageWidth: '21cm',
pageHeight: '29.7cm',
pageMargins: {
top: '20mm',
bottom: '20mm',
right: '12mm',
left: '12mm'
}
},
exportPdf: {
stylesheets: [
'EDITOR_STYLES'
],
fileName: 'my-sample-file.pdf',
converterOptions: {
format: 'A4',
margin_top: '20mm',
margin_bottom: '20mm',
margin_right: '12mm',
margin_left: '12mm',
page_orientation: 'portrait'
}
},
exportWord: {
stylesheets: [
'EDITOR_STYLES'
],
fileName: 'my-sample-file.docx',
converterOptions: {
format: 'A4',
margin_top: '20mm',
margin_bottom: '20mm',
margin_right: '12mm',
margin_left: '12mm'
}
},
licenseKey: 'your-license-key',
// ...
# Styling the editor
When using the decoupled editor (document editor), you need to make sure that the editor styles match precisely the configuration options that you provided to the feature and to the export to PDF or export to Word features. Additionally, we just want the editor to look good.
To address this issue, let’s create a new styles.css
file the following declarations:
/* Editor styles */
.document-container {
width: 1000px;
margin: auto;
background-color: rgb(238, 238, 238);
}
.editor-container {
border: 1px solid hsl( 0, 0%, 80% );
max-height: calc( 100vh - 100px );
overflow: auto;
}
.ck.ck-editor__editable_inline {
/*
A4 size.
Expand the width by 2px because of the border and "box-sizing: border-box".
*/
width: calc( 210mm + 2px );
height: auto;
padding: 20mm 12mm;
box-sizing: border-box;
border: 1px solid hsl( 0, 0%, 88% );
background: hsl( 0, 0%, 100% );
box-shadow: 0 2px 8px hsla( 0, 0%, 0%, .08 );
margin: 40px auto;
}
# Styling the content
Apart from styling the editor’s editable element, there are also styles that control how the content inside the editor looks. When using the pagination feature together with export to PDF and export to Word, you need to make sure that these styles are sent to the converters (which means they will be actually applied to the content when generating the document) and that they do not create a discrepancy between the pagination lines in the editor and page breaks in the generated document.
Currently, our exports and pagination features use only default editor styles, which in case of PDF/Word export configs are defined as:
// ...
exportPdf: {
stylesheets: [
'EDITOR_STYLES'
],
...
...
exportWord: {
stylesheets: [
'EDITOR_STYLES'
],
...
// ...
This means that only the default CKEditor styles are sent to the converter when generating the PDF/Word document. This default configuration might be enough in some simple scenarios, but more often than not it will need some adjustments (depending on the content that you create and the environment in which you use the editor).
We will now add a few declarations, that are specific to the pagination plugin and also adjust the line height:
/* Content styles */
@media print {
body {
margin: 0 !important;
}
}
.ck-content * {
line-height: 1.6;
}
.ck-content .table thead {
display: table-row-group;
}
.ck-content .table tr {
break-inside: avoid;
break-after: auto;
}
To send the file with custom styles to the converter, update the PDF/Word export configurations like this:
// ...
exportPdf: {
stylesheets: [
'EDITOR_STYLES',
'./styles.css'
],
...
...
exportWord: {
stylesheets: [
'EDITOR_STYLES',
'./styles.css'
],
...
// ...
And also do not forget to import the CSS file inside app.js
:
// ...
import './styles.css';
DecoupledEditor
.create( document.querySelector( '#editor' ), {
// ...
After updating the configuration and adding styles, rebuild the editor again:
npm run build
You can read more about sending styles to the server in the troubleshooting section of Pagination overview.
# Demo
Presented below is a sample editor with the Pagination
, Export to PDF
and Export to Word
features enabled (keep in mind, however, that this demo might be slightly different than the editor you just built because of the fonts and other styling, that is specific to the whole documentation page):
Please note, that, as of now, the Pagination feature demo does not work properly in the Firefox and Safari browsers. Refer to the browser compatibility section for further details.
# Full implementation
After following this guide and building the editor, your application should now have the following structure:
- dist/
- bundle.js
- bundle.js.map
- node_modules/
- app.js
- index.html
- package-lock.json
- package.json
- styles.css
- webpack.config.js
# app.js
:
// app.js
import DecoupledEditor from '@ckeditor/ckeditor5-editor-decoupled/src/decouplededitor';
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import Heading from '@ckeditor/ckeditor5-heading/src/heading';
import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote';
import Font from '@ckeditor/ckeditor5-font/src/font';
import List from '@ckeditor/ckeditor5-list/src/list';
import Table from '@ckeditor/ckeditor5-table/src/table';
import TableProperties from '@ckeditor/ckeditor5-table/src/tableproperties';
import TableCellProperties from '@ckeditor/ckeditor5-table/src/tablecellproperties';
import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar';
import Pagination from '@ckeditor/ckeditor5-pagination/src/pagination';
import PageBreak from '@ckeditor/ckeditor5-page-break/src/pagebreak';
import ExportPdf from '@ckeditor/ckeditor5-export-pdf/src/exportpdf';
import ExportWord from '@ckeditor/ckeditor5-export-word/src/exportword';
import CloudServices from '@ckeditor/ckeditor5-cloud-services/src/cloudservices';
import './styles.css';
DecoupledEditor
.create( document.querySelector( '#editor' ), {
plugins: [
Essentials, Paragraph, Bold, Heading, Italic, Font,
BlockQuote, List, Table, TableProperties, TableCellProperties, TableToolbar, PageBreak,
Pagination, ExportPdf, ExportWord, CloudServices
],
toolbar: [
'exportPdf',
'exportWord',
'|',
'previousPage',
'nextPage',
'pageNavigation',
'pageBreak',
'|',
'heading',
'|',
'bold',
'italic',
'|',
'fontSize',
'fontFamily',
'fontColor',
'fontBackgroundColor',
'|',
'blockQuote',
'bulletedList',
'numberedList',
'insertTable'
],
table: {
contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
},
cloudServices: {
// Provide correct value here. You can find it in the CKEditor Dashboard:
// https://dashboard.ckeditor.com/login
tokenUrl: 'https://example.com/cs-token-endpoint'
},
pagination: {
// Page width and height correspond to A4 format
pageWidth: '21cm',
pageHeight: '29.7cm',
pageMargins: {
top: '20mm',
bottom: '20mm',
right: '12mm',
left: '12mm'
}
},
exportPdf: {
stylesheets: [
'EDITOR_STYLES',
'./styles.css'
],
fileName: 'my-sample-file.pdf',
converterOptions: {
format: 'A4',
margin_top: '20mm',
margin_bottom: '20mm',
margin_right: '12mm',
margin_left: '12mm',
page_orientation: 'portrait'
}
},
exportWord: {
stylesheets: [
'EDITOR_STYLES',
'./styles.css'
],
fileName: 'my-sample-file.docx',
converterOptions: {
format: 'A4',
margin_top: '20mm',
margin_bottom: '20mm',
margin_right: '12mm',
margin_left: '12mm'
}
},
licenseKey: 'your-license-key'
} )
.then( editor => {
console.log( 'Editor was initialized', editor );
const toolbarContainer = document.querySelector( '#toolbar-container' );
toolbarContainer.appendChild( editor.ui.view.toolbar.element );
} )
.catch( error => {
console.error( error.stack );
} );
# index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pagination - integration with Export to PDF/Word</title>
</head>
<body>
<div class="document-container">
<div id="toolbar-container"></div>
<div class="editor-container">
<div id="editor">
<p>This is the initial editor content.</p>
</div>
</div>
</div>
<script src="dist/bundle.js"></script>
</body>
</html>
# styles.css
:
/* Editor styles */
.document-container {
width: 1000px;
margin: auto;
background-color: rgb(238, 238, 238);
}
.editor-container {
border: 1px solid hsl( 0, 0%, 80% );
max-height: calc( 100vh - 100px );
overflow: auto;
}
.ck.ck-editor__editable_inline {
/*
A4 size.
Expand the width by 2px because of the border and "box-sizing: border-box".
*/
width: calc( 210mm + 2px );
height: auto;
padding: 20mm 12mm;
box-sizing: border-box;
border: 1px solid hsl( 0, 0%, 88% );
background: hsl( 0, 0%, 100% );
box-shadow: 0 2px 8px hsla( 0, 0%, 0%, .08 );
margin: 40px auto;
}
/* Content styles */
@media print {
body {
margin: 0 !important;
}
}
.ck-content * {
line-height: 1.6;
}
.ck-content .table thead {
display: table-row-group;
}
.ck-content .table tr {
break-inside: avoid;
break-after: auto;
}
# Troubleshooting
Please keep in mind that each of the plugins used in this guide has some known issues - this could be partial or missing support for other CKEditor 5 plugins, partial browser support, some known bugs, etc. When combined, these sometimes might cause problems for certain more complicated use cases. We strongly advise to go through all Known issues sections in the features’ respective documentation pages and make sure that you understand all their limitations.
If you would like to report a bug or propose enhancements for these plugins, we encourage you to use the CKEditor 5 issue tracker. If you would like to use CKEditor 5 as a part of a commercial solution or have a request for custom development, feel free to contact us through our support channel - our team will make sure to answer all your questions.
Every day, we work hard to keep our documentation complete. Have you spotted outdated information? Is something missing? Please report it via our issue tracker.