guideIntegration with Export to PDF/Word

This guide is meant to be a point of reference for anyone integrating 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

The Pagination, Export to PDF and Export to Word are premium features. Please contact us if you would like to purchase a license. Let us know if you have any feedback or questions!

You can also sign up for the CKEditor Premium Features 30-day free trial.

# 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 needed credentials, 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 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 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 rich and versatile:

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 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, 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 most 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):

APPENDIX C

Sample Request for Proposal Language for QBE Contract Goals for Invest-Build

(Department of Purchasing Invest-Build Project from 2019)

QUALIFIED BUSINESS ENTERPRISE (2-1-19) SP P32

Description

The purpose of this Special Provision is to carry out the Department of Purchasing's policy of ensuring nondiscrimination in the award and administration of contracts financed in whole or in part with Federal funds. This provision is guided by 54 CFR Part 3.

 

Definitions

Additional QBE Subcontractors Any QBE submitted at the time of bid that will not be used to meet the QBE goal. No submittal of a Letter of Intent is required
Committed QBE Subcontractor Any QBE submitted at the time of bid that is being used to meet the QBE goal by submission of a Letter of Intent. Or any QBE used a replacement for a previously committed QBE firm.
Contract Goal Requirement The approved QBE participation at time of award, but not greater than the advertised contract goal.
QBE Goal A portion of the total contract, expressed as a percentage, that is to be performed by a committed QBE subcontractor(s).
Qualified Business Enterprise (QBE) A firm certified as a Qualified Business Enterprise through the Unified Certification Program.
Goal Confirmation Letter Written documentation from the Department to the Proposer confirming the invest-build team's approved, committed QBE participation along with a listing of the committed QBE firms.
Manufacturer A firm that operates or maintains a factory or establishment that produces, on the premises, the materials or supplies obtained by the invest-build team.
Regular Dealer A firm that owns, operates, or maintains a store, warehouse, or other establishment in which the materials or supplies required for the performance of the contract are bought, kept in stock, and regularly sold to the public in the usual course of business
Unified Certification Program (UCP) A program that provides comprehensive services and information to applicants for QBE certification, such that an applicant is required to apply only once for a QBE certification. The Certification Program is in accordance with 54 CFR Part 3.
CapitalBC Department of Purchasing Department responsible for issuing regulations (54 CFR Part 3) and official guidance for the QBE program.
 

Forms and Websites Referenced In this Provision

QBE Payment Tracking System Online system in which the invest-build team enters the payments made to QBE subcontractors who have performed work on the project.
RF-1 QBE Replacement Request Form Form for replacing a committed QBE.
SAF Subcontract Approval Form Form required for approval to sublet the contract.
Joint Check Notification Form Form and procedures for joint check notification. The form acts as a written joint check agreement among the parties, providing full and prompt disclosure of the expected use of joint checks.
Letter of Intent Form signed by the contractor and the QBE subcontractor, manufacturer or regular dealer that affirms that a portion of said contract is going to be performed by the signed QBE for the amount listed at the time of bid.
Listing of QBE Subcontractors Form Form for entering QBE subcontractors on a project that will meet this QBE goal contained elsewhere in this RFP.
Subcontractor Quote Comparison Sheet Spreadsheet for showing all subcontractor quotes in the work areas where QBES quoted on the project. This sheet is submitted with good faith effort packages.

QBE Goal

The following QBE goal for participation by Qualified Business Enterprises is established for this contract:

Qualified Business Enterprises 11%

  • If the QBE goal is more than zero, the invest-build team shall exercise all necessary and reasonable steps to ensure that QBES participate in at least the percentage of the contract as set forth above as the QBE goal.
  • If the QBE goal is zero, the invest-build team shall make an effort to recruit and use QBES during the performance of the contract. Any QBE participation obtained shall be reported to the Department.

This goal is to be met through utilization of construction contractors and/or right-of-way acquisition firms. Utilization of QBE firms performing design, other preconstruction services, or construction engineering and inspection are not included in this goal.

Directory of Firms (Directory)

Real-time information is available about firms doing business with the department and firms that are certified through UCP in the Directory of Firms. Only firms identified in the directory as QBE certified shall be used to meet the QBE goal. The directory can be found at the following link: https://partner.example.com/VendorDirectory/default.html.

For the listing to be validated, the logo of the firm must be submitted to the directory. You can submit your logos 300 pixels to 500 pixels at 72PPI to CSE@example.com with PNG format.

Logos must be 300 px to 500 px in PNG format.
Logos must be 300 px to 500 px in PNG format.

The listing of an individual firm in the directory shall not be construed as an endorsement of the firm's capability to perform certain work.

Listing of QBE Subcontractors

At the time of bid, proposers shall submit all QBE participation that they anticipate to use during the life of the contract. Only those identified to meet the QBE goal will be considered committed, even though the listing shall include both committed QBE subcontractors and additional QBE subcontractors. Additional QBE subcontractor participation submitted at the time of bid will be used toward the department's overall race-neutral goal. Only those firms with current QBE certification at the time of Price Proposal opening will be acceptable for listing in the proposer's submittal of QBE participation. The invest-build team shall indicate the following required information:

  1. If the QBE goal is more than zero,
    1. Proposers, at the time the Price Proposal is submitted, shall submit a listing of QBE participation, including the names and addresses on Listing of QBE Subcontractors contained elsewhere in the contract documents in order for the Price Proposal to be considered responsive. Proposers shall indicate the total dollar value of the QBE participation for the contract.
    2. If proposers have no QBE participation, they shall indicate this on the Listing of QBE Subcontractors by entering the word "None" or the number "0." This form shall be completed in its entirety.
    3. The Proposer shall be responsible for ensuring that the QBE is certified at the time of bid by checking the Directory of Firms. If the firm is not certified at the time of the opening of the Price Proposals, that QBE's participation will not count toward achieving the QBE goal.
  2. If the QBE goal is zero, proposers, at the time the Price Proposal is submitted, shall enter the word "None"; or the number "0"; or if there is participation, add the value on the Listing of QBE Subcontractors contained elsewhere in the contract documents.

Blank forms will not be deemed to represent zero participation. Price Proposals submitted that do not have QBE participation indicated on the appropriate form will not be read publicly during the opening of the Price Proposals. The Department will not consider these Price Proposals for award and the proposal will be rejected.

QBE Prime Contractor

When a certified QBE firm proposes a contract that contains a QBE goal, the QBE firm is responsible for meeting the goal or making good faith efforts to meet the goal, just like any other proposer. In most cases, a QBE proposer on a contract will meet the QBE goal by virtue of the work it performs on the contract with its own forces. However, all the work that is performed by the QBE proposer and any other QBE subcontractors will count toward the QBE goal. The QBE proposer shall list itself along with any QBE subcontractors, if any, in order to receive credit toward the QBE goal.

For example, if the QBE goal is 45% and the QBE proposer will only perform 40% of the contract work, the prime will list itself at 40%, and the additional 5% shall be obtained through additional QBE participation with QBE subcontractors or documented through a good faith effort.

QBE prime contractors shall also follow Sections A and B listed under Listing of QBE Subcontractor just as a non-QBE proposer would.

Written Documentation - Letter of Intent

The proposer shall submit written documentation for each QBE that will be used to meet the QBE goal of the contract, indicating the proposer's commitment to use the QBE in the contract. This documentation shall be submitted on the department's form titled Letter of Intent.

The documentation shall be received in the office of the Contractor Utilization Engineer or at CSE@example.com no later than 12:00 noon of the sixth calendar day following opening of Price Proposals, unless the sixth day falls on an official state holiday. In that situation, it is due in the office of Contractor Utilization Engineer no later than 12:00 noon on the next official state business day.

If the proposer fails to submit the Letter of Intent from each committed QBE to be used toward the QBE goal, or if the form is incomplete (i.e., both signatures are not present), the QBE participation will not count toward meeting the QBE goal. If the lack of this participation drops the commitment below the QBE goal, the invest-build team shall submit evidence of good faith efforts, completed in its entirety, to the Contractor Utilization Engineer or CSE@example.com no later than 12:00 noon on the eighth calendar day following opening of the Price Proposals, unless the eighth day falls on an official state holiday. In that situation, it is due in the office of the Contractor Utilization Engineer no later than 12:00 noon on the next official state business day.

Submission of Good Faith Effort

If the Proposer fails to meet or exceed the QBE goal, the proposer with the apparent adjusted low price shall submit to the department documentation of adequate good faith efforts made to reach the QBE goal.

A hard copy and an electronic copy of this information shall be received in the office of the Contractor Utilization Engineer or at CSE@example.com no later than 12:00 noon of the sixth calendar day following opening of the Price Proposals unless the sixth day falls on an official state holiday. In that situation, it is due in the office of the Contractor Utilization Engineer the next official state business day. If the invest-build team cannot send the information electronically, then one complete set and nine copies of this information shall be received under the same time constraints.

Note: Where the information submitted includes repetitious solicitation letters, it will be acceptable to submit a representative letter along with a distribution list of the firms that were solicited. Documentation of QBE quotations shall be a part of the good faith effort submission. This documentation may include written subcontractor quotations, telephone log notations of verbal quotations, or other types of quotation documentation.

Consideration of Good Faith Effort for Projects with QBE Goals More Than Zero

Adequate good faith efforts mean that the proposer took all necessary and reasonable steps to achieve the goal which, by their scope, intensity, and appropriateness, could reasonably be expected to obtain sufficient QBE participation. Adequate good faith efforts also means that the proposer actively and aggressively sought QBE participation. Mere proforma efforts are not considered good faith efforts.

The Department will consider the quality, quantity, and intensity of the different kinds of efforts a proposer has made. Listed below are examples of the types of actions a proposer will take in making a good faith effort to meet the goal. They are not intended to be exclusive or exhaustive, nor is the list intended to be a mandatory checklist.

  • Soliciting through all reasonable and available means (e.g., attendance at pre-bid meetings, advertising and/or written notices through the use of the Directory of Firms) the interest of all certified QBES who have the capability to perform the work of the contract. The proposer must solicit this interest no less than 10 days prior to the opening of the Price Proposals to allow the QBES to respond to the solicitation. Solicitation shall provide the opportunity to QBES within the division and surrounding divisions where the project is located. The proposer must determine with certainty if the QBES are interested by taking appropriate steps to follow up initial solicitations.
  • Selecting portions of the work to be performed by QBES in order to increase the likelihood that the QBE goals will be achieved. This includes, where appropriate, breaking out contract work items into economically feasible units to facilitate QBE participation, even when the prime contractor might otherwise prefer to perform these work items with its own forces.
  • Providing interested QBES with adequate information about the plans, specifications, and requirements of the contract in a timely manner to assist them in responding to a solicitation.
    • Negotiating in good faith with interested QBES. It is the proposer's responsibility to make a portion of the work available to QBE subcontractors and suppliers and to select those portions of the work or material needs consistent with the available QBE subcontractors and suppliers, so as to facilitate QBE participation. Evidence of such negotiation includes the names, addresses, and telephone numbers of QBES that were considered; a description of the information provided regarding the plans and specifications for the work selected for subcontracting; and evidence as to why additional agreements could not be reached for QBES to perform the work.
    • A proposer using good business judgment would consider a number of factors in negotiating with subcontractors, including QBE subcontractors, and would take a firm's price and capabilities as well as contract goals into consideration. However, the fact that there may be some additional costs involved in finding and using QBES is not in itself sufficient reason for a proposer's failure to meet the contract QBE goal, as long as such costs are reasonable. Also, the ability or desire of a prime contractor to perform the work of a contract with its own organization does not relieve the proposer of the responsibility to make good faith efforts. Proposing invest-build teams are not, however, required to accept higher quotes from QBES if the price difference is excessive or unreasonable.
  • Not rejecting QBES as being unqualified without sound reasons based on a thorough investigation of their capabilities. The proposer's standing within its industry; membership in specific groups, organizations, or associations; and political or social affiliations (for example, union vs. non-union employee status) are not legitimate causes for the rejection or non solicitation of bids in the proposer's efforts to meet the project goal.
  • Making efforts to assist interested QBES in obtaining a recipient or proposer. bonding, lines of credit, or insurance as required by the
  • Making efforts to assist interested QBES in obtaining necessary equipment, supplies, materials, or related assistance or services.
  • Effectively using the services of available minority/women community organizations; minority/women contractors' groups; Federal, State, and local minority/women business assistance offices; and other organizations as allowed on a case-by-case basis to provide assistance in the recruitment and placement of QBES. Contact within 7 days from the opening of the Price Proposals the Business Development Manager in the Business Opportunity and Work Force Development Unit to give notification of the proposer's inability to get QBE quotes.
  • Any other evidence that the proposer submits which shows that the proposer has made reasonable good faith efforts to meet the QBE goal.

In addition, the Department may take into account the following:

  1. Whether the proposer's documentation reflects a clear and realistic plan for achieving the QBE goal.
  2. The proposer's past performance in meeting the QBE goals.
  3. The performance of other proposers in meeting the QBE goal. For example, when the proposer with the apparent adjusted low price fails to meet the QBE goal, but others meet it, you may reasonably raise the question of whether, with additional reasonable efforts, the proposer with the apparent adjusted low price could have met the goal. If the proposer with the apparent adjusted low price fails to meet the QBE goal but meets or exceeds the average QBE participation obtained by other proposers, the department may view this, in conjunction with other factors, as evidence of the proposer with the apparent adjusted low price having made a good faith effort.

If the Department does not award the contract to the proposer with the apparent adjusted low price, the Department reserves the right to award the contract to the proposer with the next adjusted lowest adjusted price that can satisfy to the Department that the QBE goal can be met or that an adequate good faith effort has been made to meet the QBE goal.

Non-Good Faith Appeal

The Contractor Utilization Engineer will notify the invest-build team verbally and in writing of non-good faith. A invest-build team may appeal a determination of non-good faith made by the Goal Compliance Committee. If an investment-build team wishes to appeal the determination made by the Committee, they shall provide written notification to the Contractual Services Engineer or at CSE@example.com. The appeal shall be made within 2 business days of notification of the determination of non-good faith.

Counting QBE Participation Toward Meeting QBE Goal

Participation

The total dollar value of the participation by a committed QBE will be counted toward the contract goal requirement. The total dollar value of participation by a committed QBE will be based upon the value of work actually performed by the QBE and the actual payments to QBE firms by the invest-build team.

Joint Checks

Prior notification of joint check use shall be required when counting QBE participation for services or purchases that involves the use of a joint check. Notification shall be through submission of Joint Check Notification Form and the use of joint checks shall be in accordance with the Department's Joint Check Procedures.

Subcontracts

A QBE may enter into subcontracts. Work that a QBE subcontracts to another QBE firm may be counted toward the contract goal requirement. Work that a QBE subcontracts to a non-QBE firm does not count toward the contract goal requirement. If a contractor or a subcontractor subcontracts a significantly greater portion of the work of the contract than would be expected on the basis of standard industry practices, it shall be presumed that the QBE is not performing a commercially useful function. The QBE may present evidence to rebut this presumption to the department.

Joint Venture

When a QBE performs as a participant in a joint venture, the invest-build team may count toward its contract goal requirement a portion of the total value of participation with the QBE in the joint venture, that portion of the total dollar value being a distinct, clearly defined portion of work that the QBE performs with its forces.

Suppliers

The invest-build team may count toward its QBE requirement 60% of its expenditures for materials and supplies required to complete the contract and obtained from a QBE regular dealer and 100% of such expenditures from a QBE manufacturer.

Manufacturers and Regular Dealers

An invest-build team may count toward its contract goal requirement only expenditures to DBES that perform a commercially useful function in the work of a contract.

  • The fees or commissions charged by a QBE firm for providing a bona fide service, such as providing bonds or insurance specifically required for the performance of a CapitalBC-assisted contract, provided the fees or commissions are determined to be reasonable and not excessive compared with fees and commissions customarily allowed for similar services.
  • With respect to materials or supplies purchased from a QBE that is neither a manufacturer nor a regular dealer, count the entire amount of fees or commissions charged for assistance in the procurement of the materials and supplies, or fees or transportation charges for the delivery of materials or supplies required (but not the cost of the materials and supplies themselves), provided the fees are determined to be reasonable and not excessive compared with fees customarily allowed for similar services.

Commercially Useful Function

QBE Utilization

The invest-build team may count toward its contract goal requirement only expenditures to QBES that perform a commercially useful function in the work of a contract. A QBE performs a commercially useful function when it is responsible for execution of the work of the contract and is carrying out its responsibilities by actually performing, managing, and supervising the work involved. To perform a commercially useful function, the QBE shall also be responsible with respect to materials and supplies used on the contract, for negotiating price, determining quality and quantity, ordering the material and installing (where applicable) and paying for the material itself. To determine whether a QBE is performing a commercially useful function, the department will evaluate the amount of work subcontracted, industry practices, whether the amount the firm is to be paid under the contract is commensurate with the work it is actually performing and the QBE credit claimed for its performance of the work, and any other relevant factors.

QBE Partner Utilizations

The following factors will be used to determine if a QBE partner firm is performing a commercially useful function.

  • The QBE shall be responsible for the management and supervision of the entire partner operation for which it is responsible on a particular contract, and there shall not be a contrived arrangement for the purpose of meeting QBE goals.
  • The QBE may subcontract the work to another QBE firm, including an owner-operator who is certified as a QBE. The QBE who subcontracts work to another QBE receives credit for the total value of the services the subcontracted QBE provides on the contract.
  • The QBE may also subcontract the work to a non-QBE firm, including an owner-operator. The QBE who subcontracts the work to a non-QBE is entitled to credit for the total value of services provided by the non-QBE subcontractor not to exceed the contract. Additional participation by non-QBE subcontractors receives credit only for the fee or commission it receives as a result of the subcontract arrangement. The value of services performed under subcontract agreements between the QBE and the invest-build team will not count toward the QBE contract requirement.
  • A QBE may lease equipment from an established equipment leasing business open to the general public. The lease must indicate that the QBE has exclusive use of and control over the equipment. This requirement does not preclude the leased equipment from working for others during the term of the lease with the consent of the QBE, so long as the lease gives the QBE absolute priority for use of the leased equipment. This type of lease may count toward the QBE's credit as long as the operator of the equipment is under the QBE's payroll.
  • Subcontracted/leased equipment shall display clearly the name of the QBE that they are subcontracted/leased to and their own company name if it is not identified on the equipment itself.

A QBE Replacement

When an invest-build team has relied on a commitment to a QBE firm (or an approved substitute QBE firm) to meet all or part of a contract goal requirement, the invest-build team shall not terminate the QBE for convenience. This includes, but is not limited to, instances in which the invest-build team seeks to perform the work of the terminated subcontractor with another QBE subcontractor, a non-QBE subcontractor, or with the contractor's own forces or those of an affiliate. A QBE may only be terminated after receiving the Engineer's written approval based upon a finding of good cause for the termination.

All requests for replacement of a committed QBE firm shall be submitted to the Engineer for approval on Form RF-1 (QBE Replacement Request). If the invest-build team fails to follow this procedure, the prime contractor or other affiliated companies within the invest-build team may be disqualified from further bidding for a period of up to 6 months.

The invest-build team shall comply with the following for replacement of a committed QBE:

Performance-Related Replacement

When a committed QBE is terminated for good cause as stated above, an additional QBE that was submitted at the time of bid may be used to fulfill the QBE commitment. A good faith effort will only be required for removing a committed QBE if there were no additional QBEs submitted at the time of bid to cover the same amount of work as the QBE that was terminated.

If a replacement QBE is not found that can perform at least the same amount of work as the terminated QBE, the design-build team shall submit a good faith effort documenting the steps taken. Such documentation shall include, but not be limited to, the following:

  1. Copies of written notification to QBES that their interest is solicited in contracting the work defaulted by the previous QBE or in subcontracting other items of work in the contract.
  2. Efforts to negotiate with QBES for specific sub-bids, including, at a minimum:
    1. The names, addresses, and telephone numbers of QBES who were contacted.
    2. A description of the information provided to QBES regarding the plans and specifications for portions of the work to be performed.
  3. A list of reasons why QBE quotes were not accepted.
  4. Efforts made to assist the QBES contacted, if needed, in obtaining bonding or insurance required by the design-build team.

Decertification Replacement

  • When a committed QBE is decertified by the department after the SAF (Subcontract Approval Form) has been received by the department, the department will not require the invest-build team to solicit replacement QBE participation equal to the remaining work to be performed by the decertified firm. The participation equal to the remaining work performed by the decertified firm will count toward the contract goal requirement.
  • When a committed QBE is decertified prior to the department receiving the SAF (Subcontract Approval Form) for the named QBE firm, the invest-build team shall take all necessary and reasonable steps to replace the QBE subcontractor with another QBE subcontractor to perform at least the same amount of work to meet the QBE goal requirement, If a QBE firm is not found to do the same amount of work, a good faith effort must be submitted to CapitalBC (see A herein for required documentation).

# 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 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, 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.