Integration with Export to PDF/Word
This guide is meant as 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 to 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, contact us.
If you already have a valid license, 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 a 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.
# Activating the feature
To use this premium feature, you need to activate it with proper credentials. Refer to the License key and activation guide for details.
For the export plugins, you will need a special token endpoint. 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 obtaining all the credentials needed, 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 that 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 install all packages needed to 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 configuration taken care of, you can now proceed with creating a basic editor.
Create two files, first app.js
file:
// app.js
import { DecoupledEditor } from '@ckeditor/ckeditor5-editor-decoupled';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
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, for example, http-server).
Now the editor is up and running. However, it is quite basic. In the next section, you will add more plugins and see how to configure them.
# 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, you will also add the @ckeditor/ckeditor5-page-break
feature.
Apart from the plugins listed above, add a few more and make your 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.
Update app.js
, which should now look like this:
// app.js
import { DecoupledEditor } from '@ckeditor/ckeditor5-editor-decoupled';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { CloudServices } from '@ckeditor/ckeditor5-cloud-services';
import { ExportPdf } from '@ckeditor/ckeditor5-export-pdf';
import { ExportWord } from '@ckeditor/ckeditor5-export-word';
import { Font } from '@ckeditor/ckeditor5-font';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Indent, IndentBlock } from '@ckeditor/ckeditor5-indent';
import { Link } from '@ckeditor/ckeditor5-link';
import { List } from '@ckeditor/ckeditor5-list';
import { MediaEmbed } from '@ckeditor/ckeditor5-media-embed';
import { PageBreak } from '@ckeditor/ckeditor5-page-break';
import { Pagination } from '@ckeditor/ckeditor5-pagination';
import { Table, TableCellProperties, TableProperties, TableToolbar } from '@ckeditor/ckeditor5-table';
DecoupledEditor
.create( document.querySelector( '#editor' ), {
plugins: [
Essentials, Paragraph, Bold, Heading, Italic, Font, Indent, IndentBlock,
BlockQuote, List, Link, MediaEmbed, Table, TableProperties, TableCellProperties, TableToolbar, PageBreak,
Pagination, ExportPdf, ExportWord, CloudServices
],
toolbar: [
'undo', 'redo',
'|', 'previousPage', 'nextPage', 'pageNavigation',
'|', 'exportWord', 'exportPdf',
'|', 'heading',
'|', 'fontfamily', 'fontsize', 'fontColor', 'fontBackgroundColor',
'|', 'bold', 'italic',
'|', 'link', 'uploadImage', 'insertTable', 'blockQuote', 'mediaEmbed',
'|', 'bulletedList', 'numberedList', 'outdent', 'indent'
],
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
The pagination configuration has to be in sync with the configuration of the export plugins. All three configurations need to have the same margin values and page format set. This is due to the handling of default editor styles. In this guide, you will use the A4 format but you can use other paper sizes as well.
You will also need to provide a license key for the pagination feature and the Cloud Services configuration for the export plugins (which you obtained in step one).
This guide assumes that you do not use any other plugin that needs a cloudServices
configuration. If you used, for example, real-time collaboration or the Easy Image plugin, you would have to provide all three values, which are: tokenUrl
, webSocketUrl
, and uploadUrl
.
After the table
configuration add:
// More editor's configuration.
// ...
cloudServices: {
// Provide a 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 reflect the 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',
// More editor's configuration.
// ...
# 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 the export to PDF or export to Word features. Additionally, you just want the editor to look good.
To address this issue, create a new styles.css
file with 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. This means they will be actually applied to the content when generating the document. They should also 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 the case of PDF or Word export configurations are defined as:
// More editor's configuration.
// ...
exportPdf: {
stylesheets: [
'EDITOR_STYLES'
],
// More configuration of the Export to PDF.
// ...
},
exportWord: {
stylesheets: [
'EDITOR_STYLES'
],
// More configuration of the export to Word.
// ...
}
// More editor's configuration.
// ...
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:
// More editor's configuration.
// ...
exportPdf: {
stylesheets: [
'EDITOR_STYLES',
'./styles.css'
],
// More configuration of the Export to PDF.
// ...
},
exportWord: {
stylesheets: [
'EDITOR_STYLES',
'./styles.css'
],
// More configuration of the export to Word.
// ...
}
// More editor's configuration.
// ...
And also do not forget to import the CSS file inside app.js
:
// More imports.
// ...
import './styles.css';
DecoupledEditor
.create( document.querySelector( '#editor' ), {
// More of editor's configuartion.
// ...
}
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
The editor below has the Pagination
, Export to PDF
and Export to Word
features enabled. Keep in mind that this demo may slightly differ from the editor you have just built. This is because of fonts and styles specific to this 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';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { CloudServices } from '@ckeditor/ckeditor5-cloud-services';
import { ExportPdf } from '@ckeditor/ckeditor5-export-pdf';
import { ExportWord } from '@ckeditor/ckeditor5-export-word';
import { Font } from '@ckeditor/ckeditor5-font';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Indent, IndentBlock } from '@ckeditor/ckeditor5-indent';
import { Link } from '@ckeditor/ckeditor5-link';
import { List } from '@ckeditor/ckeditor5-list';
import { MediaEmbed } from '@ckeditor/ckeditor5-media-embed';
import { PageBreak } from '@ckeditor/ckeditor5-page-break';
import { Pagination } from '@ckeditor/ckeditor5-pagination';
import { Table, TableCellProperties, TableProperties, TableToolbar } from '@ckeditor/ckeditor5-table';
import './styles.css';
DecoupledEditor
.create( document.querySelector( '#editor' ), {
plugins: [
Essentials, Paragraph, Bold, Heading, Italic, Font, Indent, IndentBlock,
BlockQuote, List, Link, MediaEmbed, Table, TableProperties, TableCellProperties, TableToolbar, PageBreak,
Pagination, ExportPdf, ExportWord, CloudServices
],
toolbar: [
'undo', 'redo',
'|', 'previousPage', 'nextPage', 'pageNavigation',
'|', 'exportWord', 'exportPdf',
'|', 'heading',
'|', 'fontfamily', 'fontsize', 'fontColor', 'fontBackgroundColor',
'|', 'bold', 'italic',
'|', 'link', 'uploadImage', 'insertTable', 'blockQuote', 'mediaEmbed',
'|', 'bulletedList', 'numberedList', 'outdent', 'indent'
],
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 you 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.