survey-pdf
Version:
survey.pdf.js is a SurveyJS PDF Library. It is a easy way to export SurveyJS surveys to PDF. It uses JSON for survey metadata.
9,270 lines • 383 kB
JavaScript
/*!
* surveyjs - SurveyJS PDF library v2.0.3
* Copyright (c) 2015-2025 Devsoft Baltic OÜ - http://surveyjs.io/
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
*/
import * as SurveyCore from 'survey-core';
import { PanelModel, Serializer, settings, LocalizableString, EventBase, SurveyModel, ItemValue, checkLibraryVersion } from 'survey-core';
import { jsPDF } from 'jspdf';
var SurveyPDFModule = /*#__PURE__*/Object.freeze({
__proto__: null,
get BooleanItemBrick () { return BooleanItemBrick; },
get CheckItemBrick () { return CheckItemBrick; },
get CheckboxItemBrick () { return CheckboxItemBrick; },
get CommentBrick () { return CommentBrick; },
get CompositeBrick () { return CompositeBrick; },
get CustomBrick () { return CustomBrick; },
get DocController () { return DocController; },
get DocOptions () { return DocOptions; },
get DrawCanvas () { return DrawCanvas; },
get DropdownBrick () { return DropdownBrick; },
get EmptyBrick () { return EmptyBrick; },
get EventHandler () { return EventHandler; },
get FlatBoolean () { return FlatBooleanCheckbox; },
get FlatCheckbox () { return FlatCheckbox; },
get FlatComment () { return FlatComment; },
get FlatCustomModel () { return FlatCustomModel; },
get FlatDropdown () { return FlatDropdown; },
get FlatExpression () { return FlatExpression; },
get FlatFile () { return FlatFile; },
get FlatHTML () { return FlatHTML; },
get FlatImage () { return FlatImage; },
get FlatImagePicker () { return FlatImagePicker; },
get FlatMatrix () { return FlatMatrix; },
get FlatMatrixDynamic () { return FlatMatrixDynamic; },
get FlatMatrixMultiple () { return FlatMatrixMultiple; },
get FlatMultipleText () { return FlatMultipleText; },
get FlatPanelDynamic () { return FlatPanelDynamic; },
get FlatQuestion () { return FlatQuestion; },
get FlatQuestionDefault () { return FlatQuestionDefault; },
get FlatRadiogroup () { return FlatRadiogroup; },
get FlatRanking () { return FlatRanking; },
get FlatRating () { return FlatRating; },
get FlatRepository () { return FlatRepository; },
get FlatSelectBase () { return FlatSelectBase; },
get FlatSignaturePad () { return FlatSignaturePad; },
get FlatSurvey () { return FlatSurvey; },
get FlatTextbox () { return FlatTextbox; },
get HTMLBrick () { return HTMLBrick; },
get HorizontalAlign () { return HorizontalAlign; },
get ImageBrick () { return ImageBrick; },
get LinkBrick () { return LinkBrick; },
get PagePacker () { return PagePacker; },
get PdfBrick () { return PdfBrick; },
get RadioItemBrick () { return RadioItemBrick; },
get RankingItemBrick () { return RankingItemBrick; },
get RowlineBrick () { return RowlineBrick; },
get SurveyHelper () { return SurveyHelper; },
get SurveyPDF () { return SurveyPDF; },
get TextBoldBrick () { return TextBoldBrick; },
get TextBoxBrick () { return TextBoxBrick; },
get TextBrick () { return TextBrick; },
get TextFieldBrick () { return TextFieldBrick; },
get TitlePanelBrick () { return TitlePanelBrick; },
get VerticalAlign () { return VerticalAlign; }
});
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
class CompositeBrick {
constructor(...bricks) {
this.bricks = [];
this.isPageBreak = false;
this._xLeft = 0.0;
this._xRight = 0.0;
this._yTop = 0.0;
this._yBot = 0.0;
this.addBrick(...bricks);
}
get xLeft() { return this._xLeft; }
set xLeft(xLeft) {
this.shift(xLeft - this.xLeft, 0.0, 0.0, 0.0);
this._xLeft = xLeft;
}
get xRight() { return this._xRight; }
set xRight(xRight) {
this.shift(0.0, xRight - this.xRight, 0.0, 0.0);
this._xRight = xRight;
}
get yTop() { return this._yTop; }
set yTop(yTop) {
this.shift(0.0, 0.0, yTop - this.yTop, 0.0);
this._yTop = yTop;
}
get yBot() { return this._yBot; }
set yBot(yBot) {
this.shift(0.0, 0.0, 0.0, yBot - this.yBot);
this._yBot = yBot;
}
shift(leftShift, rightShift, topShift, botShift) {
this.bricks.forEach((brick) => {
brick.xLeft += leftShift;
brick.xRight += rightShift;
brick.yTop += topShift;
brick.yBot += botShift;
});
}
get width() {
return this.xRight - this.xLeft;
}
get height() {
return this.yBot - this.yTop;
}
render() {
return __awaiter(this, void 0, void 0, function* () {
for (let i = 0; i < this.bricks.length; i++) {
yield this.bricks[i].render();
}
});
}
get isEmpty() {
return this.bricks.length === 0;
}
addBrick(...bricks) {
if (bricks.length != 0) {
this.bricks.push(...bricks);
let mergeRect = SurveyHelper.mergeRects(...this.bricks);
this._xLeft = mergeRect.xLeft;
this._xRight = mergeRect.xRight;
this._yTop = mergeRect.yTop;
this._yBot = mergeRect.yBot;
}
}
unfold() {
const unfoldBricks = [];
this.bricks.forEach((brick) => {
unfoldBricks.push(...brick.unfold());
});
return unfoldBricks;
}
translateX(func) {
this.bricks.forEach(brick => brick.translateX(func));
const res = func(this.xLeft, this.xRight);
this._xLeft = res.xLeft;
this._xRight = res.xRight;
}
}
class RowlineBrick {
constructor(controller, rect, color) {
this.controller = controller;
this.color = color;
this.isPageBreak = false;
this.xLeft = rect.xLeft;
this.xRight = rect.xRight;
this.yTop = rect.yTop;
this.yBot = rect.yBot;
}
get width() {
return this.xRight - this.xLeft;
}
get height() {
return this.yBot - this.yTop;
}
render() {
return __awaiter(this, void 0, void 0, function* () {
if (this.color !== null) {
let oldDrawColor = this.controller.doc.getDrawColor();
this.controller.doc.setDrawColor(this.color);
this.controller.doc.line(this.xLeft, this.yTop, this.xRight, this.yTop);
this.controller.doc.setDrawColor(oldDrawColor);
}
});
}
unfold() {
return [this];
}
translateX(_) { }
}
class AdornersBaseOptions {
constructor(point, bricks, controller, repository, module) {
this.point = point;
this.bricks = bricks;
this.controller = controller;
this.repository = repository;
this.module = module;
}
}
class AdornersOptions extends AdornersBaseOptions {
constructor(point, bricks, question, controller, repository, module) {
super(point, bricks, controller, repository, module);
this.question = question;
}
}
class AdornersPanelOptions extends AdornersBaseOptions {
constructor(point, bricks, panel, controller, repository, module) {
super(point, bricks, controller, repository, module);
this.panel = panel;
}
}
class AdornersPageOptions extends AdornersBaseOptions {
constructor(point, bricks, page, controller, repository, module) {
super(point, bricks, controller, repository, module);
this.page = page;
}
}
class FlatSurvey {
static generateFlatsPanel(survey, controller, panel, point) {
return __awaiter(this, void 0, void 0, function* () {
const panelFlats = [];
const panelContentPoint = SurveyHelper.clone(point);
controller.pushMargins();
controller.margins.left += controller.measureText(panel.innerIndent).width;
panelContentPoint.xLeft += controller.measureText(panel.innerIndent).width;
panelFlats.push(...yield this.generateFlatsPagePanel(survey, controller, panel, panelContentPoint));
controller.popMargins();
const adornersOptions = new AdornersPanelOptions(point, panelFlats, panel, controller, FlatRepository.getInstance(), SurveyPDFModule);
yield survey.onRenderPanel.fire(survey, adornersOptions);
return [...adornersOptions.bricks];
});
}
static generateFlatsPagePanel(survey, controller, pagePanel, point) {
return __awaiter(this, void 0, void 0, function* () {
if (!pagePanel.isVisible)
return;
pagePanel.onFirstRendering();
const pagePanelFlats = [];
let currPoint = SurveyHelper.clone(point);
if (pagePanel.getType() !== 'page' || survey.showPageTitles) {
const compositeFlat = new CompositeBrick();
if (pagePanel.title) {
if (pagePanel instanceof PanelModel && pagePanel.no) {
const noFlat = yield SurveyHelper.createTitlePanelFlat(currPoint, controller, pagePanel.no, pagePanel.getType() === 'page');
compositeFlat.addBrick(noFlat);
currPoint.xLeft = noFlat.xRight + controller.measureText(' ').width;
}
const pagelPanelTitleFlat = yield SurveyHelper.createTitlePanelFlat(currPoint, controller, pagePanel.locTitle, pagePanel.getType() === 'page');
compositeFlat.addBrick(pagelPanelTitleFlat);
currPoint = SurveyHelper.createPoint(pagelPanelTitleFlat);
}
if (pagePanel.description) {
if (pagePanel.title) {
currPoint.yTop += controller.unitWidth * FlatSurvey.PANEL_DESC_GAP_SCALE;
}
const pagePanelDescFlat = yield SurveyHelper.createDescFlat(currPoint, null, controller, pagePanel.locDescription);
compositeFlat.addBrick(pagePanelDescFlat);
currPoint = SurveyHelper.createPoint(pagePanelDescFlat);
}
if (!compositeFlat.isEmpty) {
const rowLinePoint = SurveyHelper.createPoint(compositeFlat);
compositeFlat.addBrick(SurveyHelper.createRowlineFlat(rowLinePoint, controller));
pagePanelFlats.push(compositeFlat);
currPoint.yTop += controller.unitHeight * FlatSurvey.PANEL_CONT_GAP_SCALE + SurveyHelper.EPSILON;
}
}
for (const row of pagePanel.rows) {
if (!row.visible)
continue;
controller.pushMargins();
const width = SurveyHelper.getPageAvailableWidth(controller);
let nextMarginLeft = controller.margins.left;
const rowFlats = [];
const visibleElements = row.elements.filter(el => el.isVisible);
for (let i = 0; i < visibleElements.length; i++) {
let element = visibleElements[i];
if (!element.isVisible)
continue;
const persWidth = SurveyHelper.parseWidth(element.renderWidth, width - (visibleElements.length - 1) * controller.unitWidth, visibleElements.length);
controller.margins.left = nextMarginLeft + ((i !== 0) ? controller.unitWidth : 0);
controller.margins.right = controller.paperWidth - controller.margins.left - persWidth;
currPoint.xLeft = controller.margins.left;
nextMarginLeft = controller.margins.left + persWidth;
if (element instanceof PanelModel) {
rowFlats.push(...yield this.generateFlatsPanel(survey, controller, element, currPoint));
}
else {
rowFlats.push(...yield SurveyHelper.generateQuestionFlats(survey, controller, element, currPoint));
}
}
controller.popMargins();
currPoint.xLeft = controller.margins.left;
if (rowFlats.length !== 0) {
currPoint.yTop = SurveyHelper.mergeRects(...rowFlats).yBot;
currPoint.xLeft = point.xLeft;
currPoint.yTop += controller.unitHeight * FlatSurvey.QUES_GAP_VERT_SCALE;
pagePanelFlats.push(...rowFlats);
pagePanelFlats.push(SurveyHelper.createRowlineFlat(currPoint, controller));
currPoint.yTop += SurveyHelper.EPSILON;
}
}
return pagePanelFlats;
});
}
static popRowlines(flats) {
while (flats.length > 0 && flats[flats.length - 1] instanceof RowlineBrick) {
flats.pop();
}
}
static generateFlatTitle(survey, controller, point) {
return __awaiter(this, void 0, void 0, function* () {
const compositeFlat = new CompositeBrick();
if (survey.showTitle) {
if (survey.title) {
const surveyTitleFlat = yield SurveyHelper.createTitleSurveyFlat(point, controller, survey.locTitle);
compositeFlat.addBrick(surveyTitleFlat);
point = SurveyHelper.createPoint(surveyTitleFlat);
}
if (survey.description) {
if (survey.title) {
point.yTop += controller.unitWidth * FlatSurvey.PANEL_DESC_GAP_SCALE;
}
compositeFlat.addBrick(yield SurveyHelper.createDescFlat(point, null, controller, survey.locDescription));
}
}
return compositeFlat;
});
}
static generateFlatLogoImage(survey, controller, point) {
return __awaiter(this, void 0, void 0, function* () {
const logoUrl = SurveyHelper.getLocString(survey.locLogo);
const logoSize = yield SurveyHelper.getCorrectedImageSize(controller, { imageLink: logoUrl, imageHeight: survey.logoHeight, imageWidth: survey.logoWidth, defaultImageWidth: '300px', defaultImageHeight: '200px' });
const logoFlat = yield SurveyHelper.createImageFlat(point, null, controller, { link: logoUrl,
width: logoSize.width, height: logoSize.height });
let shift = 0;
if (survey.logoPosition === 'right') {
shift = SurveyHelper.getPageAvailableWidth(controller) - logoFlat.width;
}
else if (survey.logoPosition !== 'left') {
shift = SurveyHelper.getPageAvailableWidth(controller) / 2.0 - logoFlat.width / 2.0;
}
logoFlat.xLeft += shift;
logoFlat.xRight += shift;
return logoFlat;
});
}
static generateFlats(survey, controller) {
return __awaiter(this, void 0, void 0, function* () {
const flats = [];
if (!survey.hasLogo) {
const titleFlat = yield this.generateFlatTitle(survey, controller, controller.leftTopPoint);
if (!titleFlat.isEmpty)
flats.push([titleFlat]);
}
else if (survey.isLogoBefore) {
const logoFlat = yield this.generateFlatLogoImage(survey, controller, controller.leftTopPoint);
flats.push([logoFlat]);
const titlePoint = SurveyHelper.createPoint(logoFlat, survey.logoPosition === 'top', survey.logoPosition !== 'top');
if (survey.logoPosition !== 'top') {
controller.pushMargins();
titlePoint.xLeft += controller.unitWidth;
controller.margins.left += logoFlat.width + controller.unitWidth;
}
else {
titlePoint.xLeft = controller.leftTopPoint.xLeft;
titlePoint.yTop += controller.unitHeight / 2.0;
}
const titleFlat = yield this.generateFlatTitle(survey, controller, titlePoint);
if (survey.logoPosition !== 'top')
controller.popMargins();
if (!titleFlat.isEmpty)
flats[0].push(titleFlat);
}
else {
if (survey.logoPosition === 'right') {
const logoFlat = yield this.generateFlatLogoImage(survey, controller, controller.leftTopPoint);
flats.push([logoFlat]);
controller.pushMargins();
controller.margins.right += logoFlat.width + controller.unitWidth;
const titleFlat = yield this.generateFlatTitle(survey, controller, controller.leftTopPoint);
if (!titleFlat.isEmpty)
flats[0].unshift(titleFlat);
controller.popMargins();
}
else {
const titleFlat = yield this.generateFlatTitle(survey, controller, controller.leftTopPoint);
let logoPoint = controller.leftTopPoint;
if (!titleFlat.isEmpty) {
flats.push([titleFlat]);
logoPoint = SurveyHelper.createPoint(titleFlat);
logoPoint.yTop += controller.unitHeight / 2.0;
}
const logoFlat = yield this.generateFlatLogoImage(survey, controller, logoPoint);
if (flats.length !== 0)
flats[0].push(logoFlat);
else
flats.push([logoFlat]);
}
}
let point = controller.leftTopPoint;
if (flats.length !== 0) {
point.yTop = SurveyHelper.createPoint(SurveyHelper.mergeRects(...flats[0])).yTop;
flats[0].push(SurveyHelper.createRowlineFlat(point, controller));
point.yTop += controller.unitHeight * FlatSurvey.PANEL_CONT_GAP_SCALE + SurveyHelper.EPSILON;
}
for (let i = 0; i < survey.visiblePages.length; i++) {
survey.currentPage = survey.visiblePages[i];
let pageFlats = [];
pageFlats.push(...yield this.generateFlatsPagePanel(survey, controller, survey.visiblePages[i], point));
const adornersOptions = new AdornersPageOptions(point, pageFlats, survey.visiblePages[i], controller, FlatRepository.getInstance(), SurveyPDFModule);
yield survey.onRenderPage.fire(survey, adornersOptions);
pageFlats = [...adornersOptions.bricks];
if (i === 0 && flats.length !== 0) {
flats[0].push(...pageFlats);
}
else
flats.push(pageFlats);
this.popRowlines(flats[flats.length - 1]);
point.yTop = controller.leftTopPoint.yTop;
}
return flats;
});
}
}
FlatSurvey.QUES_GAP_VERT_SCALE = 1.5;
FlatSurvey.PANEL_CONT_GAP_SCALE = 1.0;
FlatSurvey.PANEL_DESC_GAP_SCALE = 0.25;
/**
* An object that describes a PDF brick—a simple element with specified content, size, and location. Bricks are fundamental elements used to construct a PDF document.
*
* You can access `PdfBrick` objects within functions that handle `SurveyPDF`'s [`onRenderQuestion`](https://surveyjs.io/pdf-generator/documentation/api-reference/surveypdf#onRenderQuestion), [`onRenderPanel`](https://surveyjs.io/pdf-generator/documentation/api-reference/surveypdf#onRenderPanel), and [`onRenderPage`](https://surveyjs.io/pdf-generator/documentation/api-reference/surveypdf#onRenderPage) events.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/add-markup-to-customize-pdf-forms/ (linkStyle))
*/
class PdfBrick {
/**
* An X-coordinate for the left brick edge.
*/
get xLeft() {
return this._xLeft;
}
set xLeft(val) {
this.setXLeft(val);
}
/**
* An X-coordinate for the right brick edge.
*/
get xRight() {
return this._xRight;
}
set xRight(val) {
this.setXRight(val);
}
/**
* A Y-coordinate for the top brick edge.
*/
get yTop() {
return this._yTop;
}
set yTop(val) {
this.setYTop(val);
}
/**
* A Y-coordinate for the bottom brick edge.
*/
get yBot() {
return this._yBot;
}
set yBot(val) {
this.setYBottom(val);
}
constructor(question, controller, rect) {
this.question = question;
this.controller = controller;
/**
* The color of text within the brick.
*
* Default value: `"#404040"`
*/
this.textColor = SurveyHelper.TEXT_COLOR;
this.formBorderColor = SurveyHelper.FORM_BORDER_COLOR;
this.isPageBreak = false;
this.xLeft = rect.xLeft;
this.xRight = rect.xRight;
this.yTop = rect.yTop;
this.yBot = rect.yBot;
this.fontSize = !!controller ?
controller.fontSize : DocController.FONT_SIZE;
}
translateX(func) {
const res = func(this.xLeft, this.xRight);
this.xLeft = res.xLeft;
this.xRight = res.xRight;
}
/**
* The brick's width in pixels.
*/
get width() {
return this.xRight - this.xLeft;
}
/**
* The brick's height in pixels.
*/
get height() {
return this.yBot - this.yTop;
}
getShouldRenderReadOnly() {
return SurveyHelper.shouldRenderReadOnly(this.question, this.controller);
}
render() {
return __awaiter(this, void 0, void 0, function* () {
if (this.getShouldRenderReadOnly()) {
yield this.renderReadOnly();
}
else
yield this.renderInteractive();
this.afterRenderCallback && this.afterRenderCallback();
});
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () { });
}
renderReadOnly() {
return __awaiter(this, void 0, void 0, function* () {
yield this.renderInteractive();
});
}
/**
* Allows you to get a flat array of nested PDF bricks.
* @returns A flat array of nested PDF bricks.
*/
unfold() {
return [this];
}
getCorrectedText(val) {
return this.controller.isRTL ? (val || '').split('').reverse().join('') : val;
}
setXLeft(val) {
this._xLeft = val;
}
setXRight(val) {
this._xRight = val;
}
setYTop(val) {
this._yTop = val;
}
setYBottom(val) {
this._yBot = val;
}
}
class TextBrick extends PdfBrick {
constructor(question, controller, rect, text) {
super(question, controller, rect);
this.text = text;
this.align = {
isInputRtl: false,
isOutputRtl: controller.isRTL,
align: controller.isRTL ? 'right' : 'left',
baseline: 'middle'
};
}
escapeText() {
while (this.text.indexOf('\t') > -1) {
this.text = this.text.replace('\t', Array(5).join(String.fromCharCode(160)));
}
return this.text;
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
let alignPoint = this.alignPoint(this);
let oldFontSize = this.controller.fontSize;
this.controller.fontSize = this.fontSize;
let oldTextColor = this.controller.doc.getTextColor();
this.controller.doc.setTextColor(this.textColor);
this.controller.doc.text(this.escapeText(), alignPoint.xLeft, alignPoint.yTop, this.align);
this.controller.doc.setTextColor(oldTextColor);
this.controller.fontSize = oldFontSize;
});
}
alignPoint(rect) {
return {
xLeft: this.controller.isRTL ? rect.xRight : rect.xLeft,
yTop: rect.yTop + (rect.yBot - rect.yTop) / 2.0
};
}
}
class FlatQuestion {
constructor(survey, question, controller) {
this.survey = survey;
this.controller = controller;
this.question = question;
}
generateFlatTitle(point) {
return __awaiter(this, void 0, void 0, function* () {
return yield SurveyHelper.createTitleFlat(point, this.question, this.controller);
});
}
generateFlatDescription(point) {
return __awaiter(this, void 0, void 0, function* () {
return yield SurveyHelper.createDescFlat(point, this.question, this.controller, this.question.locDescription);
});
}
generateFlatHeader(point) {
return __awaiter(this, void 0, void 0, function* () {
const titleFlat = yield this.generateFlatTitle(point);
const compositeFlat = new CompositeBrick(titleFlat);
if (this.question.hasDescriptionUnderTitle) {
const descPoint = SurveyHelper.createPoint(titleFlat, true, false);
descPoint.yTop += FlatQuestion.DESC_GAP_SCALE * this.controller.unitHeight;
descPoint.xLeft += this.controller.unitWidth * FlatQuestion.CONTENT_INDENT_SCALE;
compositeFlat.addBrick(yield this.generateFlatDescription(descPoint));
}
return compositeFlat;
});
}
generateFlatsComment(point) {
return __awaiter(this, void 0, void 0, function* () {
const text = this.question.locCommentText;
const otherTextFlat = yield SurveyHelper.createTextFlat(point, this.question, this.controller, text, TextBrick);
const otherPoint = SurveyHelper.createPoint(otherTextFlat);
otherPoint.yTop += this.controller.unitHeight * SurveyHelper.GAP_BETWEEN_ROWS;
return new CompositeBrick(otherTextFlat, yield SurveyHelper.createCommentFlat(otherPoint, this.question, this.controller, false, { rows: SurveyHelper.OTHER_ROWS_COUNT }));
});
}
generateFlatsComposite(point) {
return __awaiter(this, void 0, void 0, function* () {
const contentPanel = this.question.contentPanel;
if (!!contentPanel) {
return yield FlatSurvey.generateFlatsPanel(this.survey, this.controller, contentPanel, point);
}
this.question = SurveyHelper.getContentQuestion(this.question);
return yield this.generateFlatsContent(point);
});
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
return null;
});
}
generateFlatsContentWithOptionalElements(point) {
return __awaiter(this, void 0, void 0, function* () {
const flats = [];
const contentFlats = yield this.generateFlatsComposite(point);
flats.push(...contentFlats);
const getLatestPoint = () => {
const res = SurveyHelper.clone(point);
if (contentFlats !== null && contentFlats.length !== 0) {
res.yTop = SurveyHelper.mergeRects(...flats).yBot + this.controller.unitHeight * SurveyHelper.GAP_BETWEEN_ROWS;
}
return res;
};
if (this.question.hasComment) {
flats.push(yield this.generateFlatsComment(getLatestPoint()));
}
if (this.question.hasDescriptionUnderInput) {
flats.push(yield this.generateFlatDescription(getLatestPoint()));
}
return flats;
});
}
generateFlats(point) {
return __awaiter(this, void 0, void 0, function* () {
this.controller.pushMargins();
this.controller.margins.left += this.controller.measureText(this.question.indent).width;
const indentPoint = {
xLeft: point.xLeft + this.controller.measureText(this.question.indent).width,
yTop: point.yTop
};
const flats = [];
let titleLocation = this.question.getTitleLocation();
titleLocation = this.question.hasTitle ? titleLocation : 'hidden';
switch (titleLocation) {
case 'top':
case 'default': {
const headerFlat = yield this.generateFlatHeader(indentPoint);
let contentPoint = SurveyHelper.createPoint(headerFlat);
contentPoint.xLeft += this.controller.unitWidth * FlatQuestion.CONTENT_INDENT_SCALE;
headerFlat.addBrick(SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(headerFlat), this.controller));
contentPoint.yTop += this.controller.unitHeight *
FlatQuestion.CONTENT_GAP_VERT_SCALE + SurveyHelper.EPSILON;
this.controller.pushMargins();
this.controller.margins.left += this.controller.unitWidth * FlatQuestion.CONTENT_INDENT_SCALE;
const contentFlats = yield this.generateFlatsContentWithOptionalElements(contentPoint);
this.controller.popMargins();
if (contentFlats !== null && contentFlats.length !== 0) {
headerFlat.addBrick(contentFlats.shift());
}
flats.push(headerFlat);
flats.push(...contentFlats);
break;
}
case 'bottom': {
const contentPoint = SurveyHelper.clone(indentPoint);
this.controller.pushMargins();
contentPoint.xLeft += this.controller.unitWidth * FlatQuestion.CONTENT_INDENT_SCALE;
this.controller.margins.left += this.controller.unitWidth * FlatQuestion.CONTENT_INDENT_SCALE;
const contentFlats = yield this.generateFlatsContentWithOptionalElements(contentPoint);
this.controller.popMargins();
flats.push(...contentFlats);
const titlePoint = indentPoint;
if (flats.length !== 0) {
titlePoint.yTop = flats[flats.length - 1].yBot;
}
titlePoint.yTop += this.controller.unitHeight * FlatQuestion.CONTENT_GAP_VERT_SCALE;
flats.push(yield this.generateFlatHeader(titlePoint));
break;
}
case 'left': {
this.controller.pushMargins(this.controller.margins.left, this.controller.paperWidth - this.controller.margins.left -
SurveyHelper.getPageAvailableWidth(this.controller) *
SurveyHelper.MULTIPLETEXT_TEXT_PERS);
const headerFlat = yield this.generateFlatHeader(indentPoint);
const contentPoint = SurveyHelper.createPoint(headerFlat, false, true);
this.controller.popMargins();
contentPoint.xLeft += this.controller.unitWidth * FlatQuestion.CONTENT_GAP_HOR_SCALE;
this.controller.margins.left = contentPoint.xLeft;
const contentFlats = yield this.generateFlatsContentWithOptionalElements(contentPoint);
if (contentFlats !== null && contentFlats.length !== 0) {
headerFlat.addBrick(contentFlats.shift());
}
flats.push(headerFlat);
flats.push(...contentFlats);
break;
}
case 'hidden':
case SurveyHelper.TITLE_LOCATION_MATRIX:
default: {
const contentPoint = SurveyHelper.clone(indentPoint);
this.controller.pushMargins();
if (titleLocation !== SurveyHelper.TITLE_LOCATION_MATRIX) {
contentPoint.xLeft += this.controller.unitWidth * FlatQuestion.CONTENT_INDENT_SCALE;
this.controller.margins.left += this.controller.unitWidth * FlatQuestion.CONTENT_INDENT_SCALE;
}
flats.push(...yield this.generateFlatsContentWithOptionalElements(contentPoint));
this.controller.popMargins();
break;
}
}
this.controller.popMargins();
return flats;
});
}
get shouldRenderAsComment() {
return SurveyHelper.shouldRenderReadOnly(this.question, this.controller);
}
}
FlatQuestion.CONTENT_GAP_VERT_SCALE = 0.5;
FlatQuestion.CONTENT_GAP_HOR_SCALE = 1.0;
FlatQuestion.CONTENT_INDENT_SCALE = 1.0;
FlatQuestion.DESC_GAP_SCALE = 0.0625;
Serializer.addProperty('question', {
name: 'readonlyRenderAs',
default: 'auto',
choices: ['auto', 'text', 'acroform'],
visible: false
});
class FlatQuestionDefault extends FlatQuestion {
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
const valueBrick = yield SurveyHelper.createTextFlat(point, this.question, this.controller, `${this.question.displayValue}`, TextBrick);
return [valueBrick];
});
}
}
class FlatRepository {
constructor() {
this.questions = {};
}
static getInstance() {
return FlatRepository.instance;
}
register(modelType, rendererConstructor) {
this.questions[modelType] = rendererConstructor;
}
isTypeRegistered(type) {
return !!this.questions[type];
}
getRenderer(type) {
return this.questions[type];
}
create(survey, question, docController, type) {
var _a;
const questionType = typeof type === 'undefined' ? question.getType() : type;
let rendererConstructor = this.getRenderer(questionType);
if (!rendererConstructor) {
if (!!((_a = question.customWidget) === null || _a === void 0 ? void 0 : _a.pdfRender)) {
rendererConstructor = FlatQuestion;
}
else {
rendererConstructor = FlatQuestionDefault;
}
}
return new rendererConstructor(survey, question, docController);
}
static register(type, rendererConstructor) {
this.getInstance().register(type, rendererConstructor);
}
static getRenderer(type) {
return this.getInstance().getRenderer(type);
}
}
FlatRepository.instance = new FlatRepository();
class TextBoldBrick extends TextBrick {
constructor(question, controller, rect, text) {
super(question, controller, rect, text);
}
renderInteractive() {
const _super = Object.create(null, {
renderInteractive: { get: () => super.renderInteractive }
});
return __awaiter(this, void 0, void 0, function* () {
this.controller.fontStyle = 'bold';
yield _super.renderInteractive.call(this);
this.controller.fontStyle = 'normal';
});
}
}
class TitlePanelBrick extends TextBoldBrick {
constructor(question, controller, rect, text) {
super(question, controller, rect, text);
}
renderInteractive() {
const _super = Object.create(null, {
renderInteractive: { get: () => super.renderInteractive }
});
return __awaiter(this, void 0, void 0, function* () {
let oldFontSize = this.controller.fontSize;
this.controller.fontSize = oldFontSize * SurveyHelper.TITLE_PANEL_FONT_SIZE_SCALE;
yield _super.renderInteractive.call(this);
this.controller.fontSize = oldFontSize;
});
}
}
class DescriptionBrick extends TextBrick {
constructor(question, controller, rect, text) {
super(question, controller, rect, text);
}
}
class TextFieldBrick extends PdfBrick {
constructor(question, controller, rect, isQuestion, fieldName, value, placeholder, isReadOnly, isMultiline, inputType) {
super(question, controller, rect);
this.isQuestion = isQuestion;
this.fieldName = fieldName;
this.value = value;
this.placeholder = placeholder;
this.isReadOnly = isReadOnly;
this.isMultiline = isMultiline;
this.inputType = inputType;
this.question = question;
}
renderColorQuestion() {
let oldFillColor = this.controller.doc.getFillColor();
this.controller.doc.setFillColor(this.question.value || 'black');
this.controller.doc.rect(this.xLeft, this.yTop, this.width, this.height, 'F');
this.controller.doc.setFillColor(oldFillColor);
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
if (this.inputType === 'color') {
this.renderColorQuestion();
return;
}
const inputField = this.inputType === 'password' ?
new this.controller.doc.AcroFormPasswordField() :
new this.controller.doc.AcroFormTextField();
inputField.fieldName = this.fieldName;
inputField.fontName = this.controller.fontName;
inputField.fontSize = this.fontSize;
inputField.isUnicode = SurveyHelper.isCustomFont(this.controller, inputField.fontName);
if (this.inputType !== 'password') {
inputField.V = ' ' + this.getCorrectedText(this.value);
inputField.DV = ' ' + this.getCorrectedText(this.placeholder);
}
else
inputField.value = '';
inputField.multiline = this.isMultiline;
inputField.readOnly = this.isReadOnly;
inputField.color = this.textColor;
let formScale = SurveyHelper.formScale(this.controller, this);
inputField.maxFontSize = this.controller.fontSize * formScale;
inputField.Rect = SurveyHelper.createAcroformRect(SurveyHelper.scaleRect(this, formScale));
this.controller.doc.addField(inputField);
SurveyHelper.renderFlatBorders(this.controller, this);
});
}
shouldRenderFlatBorders() {
return settings.readOnlyTextRenderMode === 'input';
}
getShouldRenderReadOnly() {
return SurveyHelper.shouldRenderReadOnly(this.question, this.controller, this.isReadOnly);
}
get textBrick() {
return this._textBrick;
}
set textBrick(val) {
this._textBrick = val;
const unFoldedBricks = val.unfold();
const bricksCount = unFoldedBricks.length;
let renderedBricksCount = 0;
const bricksByPage = {};
const afterRenderTextBrickCallback = (brick) => {
if (this.shouldRenderFlatBorders()) {
renderedBricksCount++;
const currentPageNumber = this.controller.getCurrentPageIndex();
if (!bricksByPage[currentPageNumber]) {
bricksByPage[currentPageNumber] = [];
}
bricksByPage[currentPageNumber].push(brick);
if (renderedBricksCount >= bricksCount) {
const keys = Object.keys(bricksByPage);
const renderedOnOnePage = keys.length == 1;
keys.forEach((key) => {
const compositeBrick = new CompositeBrick();
bricksByPage[key].forEach((brick) => {
compositeBrick.addBrick(brick);
});
const padding = this.controller.unitHeight * SurveyHelper.VALUE_READONLY_PADDING_SCALE;
const borderRect = {
xLeft: this.xLeft,
xRight: this.xRight,
width: this.width,
yTop: renderedOnOnePage ? this.yTop : compositeBrick.yTop - padding,
yBot: renderedOnOnePage ? this.yBot : compositeBrick.yBot + padding,
height: renderedOnOnePage ? this.height : compositeBrick.height + 2 * padding,
formBorderColor: this.formBorderColor,
};
this.controller.setPage(Number(key));
SurveyHelper.renderFlatBorders(this.controller, borderRect);
this.controller.setPage(currentPageNumber);
});
}
}
};
unFoldedBricks.forEach((brick) => {
brick.afterRenderCallback = afterRenderTextBrickCallback.bind(this, brick);
});
}
renderReadOnly() {
return __awaiter(this, void 0, void 0, function* () {
this.controller.pushMargins(this.xLeft, this.controller.paperWidth - this.xRight);
if (this.inputType === 'color') {
this.renderColorQuestion();
}
else {
yield this.textBrick.render();
}
this.controller.popMargins();
});
}
unfold() {
if (this.getShouldRenderReadOnly() && this.inputType !== 'color') {
return this.textBrick.unfold();
}
else {
return super.unfold();
}
}
translateX(func) {
const res = func(this.xLeft, this.xRight);
this._xLeft = res.xLeft;
this._xRight = res.xRight;
if (this.textBrick) {
this.textBrick.translateX(func);
}
}
setXLeft(val) {
const delta = val - this._xLeft;
super.setXLeft(val);
if (this.textBrick) {
this.textBrick.xLeft = this.textBrick.xLeft + delta;
}
}
setXRight(val) {
const delta = val - this._xRight;
super.setXRight(val);
if (this.textBrick) {
this.textBrick.xRight = this.textBrick.xRight + delta;
}
}
setYTop(val) {
const delta = val - this._yTop;
super.setYTop(val);
if (this.textBrick) {
this.textBrick.yTop = this.textBrick.yTop + delta;
}
}
setYBottom(val) {
const delta = val - this._yBot;
super.setYBottom(val);
if (this.textBrick) {
this.textBrick.yBot = this.textBrick.yBot + delta;
}
}
}
class TextBoxBrick extends TextFieldBrick {
constructor(question, controller, rect, isQuestion = true, isMultiline = false, index = 0) {
super(question, controller, rect, isQuestion, question.id + (isQuestion ? '' : '_comment' + index), SurveyHelper.getQuestionOrCommentValue(question, isQuestion), isQuestion && question.locPlaceHolder ? SurveyHelper.getLocString(question.locPlaceHolder) : '', question.isReadOnly, isMultiline, question.inputType);
this.isQuestion = isQuestion;
this.isMultiline = isMultiline;
}
}
class CommentBrick extends TextBoxBrick {
constructor(question, controller, rect, isQuestion, index = 0) {
super(question, controller, rect, isQuestion, true, index);
this.controller = controller;
}
shouldRenderFlatBorders() {
if (this.isQuestion && this.question.getType() !== 'comment')
return super.shouldRenderFlatBorders();
return settings.readOnlyCommentRenderMode === 'textarea';
}
}
class LinkBrick extends TextBrick {
constructor(textFlat, link) {
super(textFlat.question, textFlat.controller, textFlat, textFlat.text);
this.link = link;
this.textColor = LinkBrick.COLOR;
}
renderInteractive() {
const _super = Object.create(null, {
renderInteractive: { get: () => super.renderInteractive }
});
return __awaiter(this, void 0, void 0, function* () {
let oldTextColor = this.controller.doc.getTextColor();
this.controller.doc.setTextColor(SurveyHelper.BACKGROUND_COLOR);
let descent = this.controller.unitHeight *
(this.controller.doc.getLineHeightFactor() -
LinkBrick.SCALE_FACTOR_MAGIC);
let yTopLink = this.yTop +
(this.yBot - this.yTop) - descent;
this.controller.doc.textWithLink(this.text, this.xLeft, yTopLink, { url: this.link });
yield _super.renderInteractive.call(this);
this.controller.doc.setTextColor(oldTextColor);
});
}
renderReadOnly() {
const _super = Object.create(null, {
renderInteractive: { get: () => super.renderInteractive }
});
return __awaiter(this, void 0, void 0, function* () {
if (SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'text') {
return this.renderInteractive();
}
yield _super.renderInteractive.call(this);
});
}
}
LinkBrick.SCALE_FACTOR_MAGIC = 0.955;
LinkBrick.COLOR = '#0000EE';
class HTMLBrick extends PdfBrick {
constructor(question, controller, rect, html, isImage = false) {
super(question, controller, rect);
this.html = html;
if (isImage) {
this.margins = {
top: 0.0,
bottom: 0.0
};
}
else {
this.margins = {
top: controller.margins.top,
bottom: controller.margins.bot
};
}
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
let oldFontSize = this.controller.fontSize;
this.controller.fontSize = this.fontSize;
yield new Promise((resolve) => {
this.controller.doc.fromHTML(this.html, this.xLeft, this.yTop, {
width: this.width, pagesplit: true,
}, function () {
[].slice.call(document.querySelectorAll('.sjs-pdf-hidden-html-div')).forEach(function (el) {
el.parentNode.removeChild(el);
});
resolve();
}, this.margins);
});
this.controller.fontSize = oldFontSize;
});
}
}
class ImageBrick extends PdfBrick {
constructor(question, controller, image, point, originalWidth, originalHeight) {
super(question, controller, {
xLeft: point.xLeft,
xRight: point.xLeft + (originalWidth || 0),
yTop: point.yTop,
yBot: point.yTop + (originalHeight || 0)
});
this.image = image;
this.originalWidth = originalWidth;
this.originalHeight = originalHeight;
this.isPageBreak = this.originalHeight === undefined;
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
yield new Promise((resolve) => {
try {
this.controller.doc.addImage(this.image, 'PNG', this.xLeft, this.yTop, this.originalWidth, this.originalHeight);
}
finally {
resolve();
}
});
});
}
}
class EmptyBrick extends PdfBrick {
constructor(rect, controller = null, isBorderVisible = false) {
super(null, controller, rect);
this.controller = controller;
this.isBorderVisible = false;
this.isBorderVisible = isBorderVisible;
}
resizeBorder(isIncrease) {
const coef = isIncrease ? 1 : -1;
const borderPadding = this.controller.doc.getFontSize() * SurveyHelper.VALUE_READONLY_PADDING_SCALE;
this.xLeft -= coef * borderPadding;
this.xRight += coef * borderPadding;
this.yBot += coef * borderPadding;
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
if (this.isBorderVisible) {
this.resizeBorder(true);
SurveyHelper.renderFlatBorders(this.controller, this);
this.resizeBorder(false);
}
});
}
}
class SurveyHelper {
static parseWidth(width, maxWidth, columnsCount = 1, defaultUnit) {
if (width.indexOf('calc') === 0) {
return maxWidth / columnsCount;
}
const value = parseFloat(width);
const unit = width.replace(/[^A-Za-z%]/g, '') || defaultUnit;
let k;
switch (unit) {
case 'pt':
k = 1.0;
break;
case 'mm':
k = 72.0 / 25.4;
break;
case 'cm':
k = 72.0 / 2.54;
break;
case 'in':
k = 72.0;
break;
case 'px':
k = 72.0 / 96.0;
break;
case 'pc':
k = 12.0;
break;
case 'em':
k = 12.0;
break;
case 'ex':
k = 6.0;
break;
default:
case '%':
k = maxWidth / 100.0;
break;
}
return Math.min(value * k, maxWidth);
}
static pxToPt(value) {
if (typeof value === 'string') {
if (!isNaN(Number(value))) {
value += 'px';
}
return SurveyHelper.parseWidth(value, Number.MAX_VALUE);
}
return value * 72.0 / 96.0;
}
static mergeRects(...rects) {
const resultRect = {
xLeft: rects[0].xLeft,
xRight: rects[0].xRight,
yTop: rects[0].yTop,
yBot: rects[0].yBot
};
rects.forEach((rect) => {
resultRect.xLeft = Math.min(resultRect.xLeft, rect.xLeft),
resultRect.xRight = Math.max(resultRect.xRight, rect.xRight),
resultRect.yTop = Math.min(resultRect.yTop, rect.yTop),
resultRect.yBot = Math.max(resultRect.yBot, rect.yBot);
});
return resultRect;
}
static createPoint(rect, isLeft = true, isTop = false) {
return {
xLeft: isLeft ? rect.xLeft : rect.xRight,
yTop: isTop ? rect.yTop : rect.yBot
};
}
static createRect(point, width, height) {
return {
xLeft: point.xLeft,
xRight: point.xLeft + width,
yTop: point.yTop,
yBot: point.yTop + height
};
}
static createHeaderRect(controller) {
return {
xLeft: 0.0,
xRight: controller.paperWidth,
yTop: 0.0,
yBot: controller.margins.top
};
}
static createFooterRect(controller) {
return {
xLeft: 0.0,
xRight: controller.paperWidth,
yTop: controller.paperHeight - controller.margins.bot,
yBot: controller.paperHeight
};
}
static chooseHtmlFont(controller) {
return controller.useCustomFontInHtml ? controller.fontName : this.STANDARD_FONT;
}
static generateCssTextRule(fontSize, fontStyle, fontName) {
return `"font-size: ${fontSize}pt; font-weight: ${fontStyle}; font-family: ${fontName}; color: ${this.TEXT_COLOR};"`;
}
static createHtmlContainerBlock(html, controller, renderAs) {
const font = this.chooseHtmlFont(controller);
return `<div class="__surveypdf_html" style=${this.generateCssTextRule(controller.fontSize, controller.fontStyle, font)}>` +
`<style>.__surveypdf_html p { margin: 0; line-height: ${controller.fontSize}pt } body { margin: 0; }</style>${html}</div>`;
}
static splitHtmlRect(controller, htmlBrick) {
const bricks = [];
const htmlHeight = htmlBrick.height;
const minHeight = controller.doc.getFontSize();
htmlBrick.yBot = htmlBrick.yTop + minHeight;
const emptyBrickCount = Math.floor(htmlHeight / minHeight) - 1;
bricks.push(htmlBrick);
const currPoint = this.createPoint(htmlBrick);
for (let i = 0; i < emptyBrickCount; i++) {
bricks.push(new EmptyBrick(this.createRect(currPoint, htmlBrick.width, minHeight)));
currPoint.yTop += minHeight;
}
const remainingHeight = htmlHeight - (emptyBrickCount + 1) * minHeight;
if (remainingHeight > 0) {
bricks.push(new EmptyBrick(this.createRect(currPoint, htmlBrick.width, remainingHeight)));
}
return new CompositeBrick(...bricks);
}
static createPlainTextFlat(point, question, controller, text, fabric) {
const lines = controller.doc.splitTextToSize(text, controller.paperWidth - controller.margins.right - point.xLeft);
const currPoint = this.clone(point);
const composite = new CompositeBrick();
lines.forEach((line) => {
const size = controller.measureText(line);
composite.addBrick(new fabric(question, controller, this.createRect(currPoint, size.width, size.height), line));
currPoint.yTop += size.height;
});
return composite;
}
static createTextFlat(point, question, controller, text, fabric) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof text === 'string' || !this.hasHtml(text)) {
return this.createPlainTextFlat(point, question, controller, typeof text === 'string' ?
text : this.getLocString(text), fabric);
}
else {
return this.splitHtmlRect(controller, yield this.createHTMLFlat(point, question, controller, this.createHtmlContainerBlock(this.getLocString(text), controller, 'standard')));
}
});
}
static hasHtml(text) {
const pattern = /<\/?[a-z][\s\S]*>/i;
return text.hasHtml && (pattern.test(text.renderedText) || pattern.test(text.renderedHtml));
}
static getHtmlMargins(controller, point) {
const width = controller.paperWidth - point.xLeft - controller.margins.right;
return {
top: controller.margins.top,
bottom: controller.margins.bot,
width: width > controller.unitWidth ? width : controller.unitWidth
};
}
static createHTMLRect(point, controller, margins, resultY) {
const availablePageHeight = controller.paperHeight - controller.margins.bot - controller.margins.top;
const height = (controller.helperDoc.getNumberOfPages() - 1) *
(controller.fontSize * Math.floor(availablePageHeight / controller.fontSize))
+ resultY - margins.top + SurveyHelper.HTML_TAIL_TEXT_SCALE * controller.fontSize;
const numberOfPages = controller.helperDoc.getNumberOfPages();
controller.helperDoc.addPage();
for (let i = 0; i < numberOfPages; i++) {
controller.helperDoc.deletePage(1);
}
return SurveyHelper.createRect(point, margins.width, height);
}
static createHTMLFlat(point, question, controller, html) {
return __awaiter(this, void 0, void 0, function* () {
const margins = this.getHtmlMargins(controller, point);
return yield new Promise((resolve) => {
controller.helperDoc.fromHTML(html, point.xLeft, margins.top, {
pagesplit: true, width: margins.width
}, function (result) {
const rect = SurveyHelper.createHTMLRect(point, controller, margins, result.y);
resolve(new HTMLBrick(question, controller, rect, html));
}, margins);
});
});
}
static generateFontFace(fontName, fontBase64, fontWeight) {
return `@font-face { font-family: ${fontName}; ` +
`src: url(data:application/font-woff;charset=utf-8;base64,${fontBase64}) format('woff'); ` +
`font-weight: ${fontWeight}; }`;
}
static generateFontFaceWithItalicStyle(fontName, fontBase64, fontWeight) {
return `@font-face { font-family: ${fontName}; ` +
`src: url(data:application/font-woff;charset=utf-8;base64,${fontBase64}) format('woff'); ` +
`font-weight: ${fontWeight}; font-style: italic}`;
}
static htmlToXml(html) {
const htmlDoc = document.implementation.createHTMLDocument('');
htmlDoc.write(html.replace(/\#/g, '%23'));
htmlDoc.documentElement.setAttribute('xmlns', htmlDoc.documentElement.namespaceURI);
htmlDoc.body.style.margin = 'unset';
return (new XMLSerializer()).serializeToString(htmlDoc.body).replace(/%23/g, '#');
}
static createSvgContent(html, width, controller) {
const style = document.createElement('style');
style.innerHTML = '.__surveypdf_html p { margin: unset; line-height: 22px; } body { margin: unset; }';
document.body.appendChild(style);
const div = document.createElement('div');
div.className = '__surveypdf_html';
div.style.display = 'block';
div.style.position = 'fixed';
div.style.top = '-10000px';
div.style.left = '-10000px';
div.style.width = (width / 72.0 * 96.0) + 'px';
div.style.boxSizing = 'initial';
div.style.color = 'initial';
div.style.fontFamily = 'initial';
div.style.font = 'initial';
div.style.lineHeight = 'initial';
div.insertAdjacentHTML('beforeend', html);
document.body.appendChild(div);
const divWidth = div.offsetWidth;
const divHeight = div.offsetHeight;
div.remove();
style.remove();
let defs = '';
if (controller.useCustomFontInHtml) {
defs = `<defs><style>${this.generateFontFace(controller.fontName, controller.base64Normal, 'normal')}` +
` ${this.generateFontFace(controller.fontName, controller.base64Bold, 'bold')}</style></defs>`;
}
else {
Object.keys(DocController.customFonts).forEach(fontName => {
const font = DocController.customFonts[fontName];
Object.keys(font).forEach((fontStyle) => {
if (fontStyle === 'normal' || fontStyle === 'bold') {
defs += `${this.generateFontFace(fontName, font[fontStyle], fontStyle)}`;
}
else {
defs += `${this.generateFontFaceWithItalicStyle(fontName, font[fontStyle], fontStyle === 'italic' ? 'normal' : 'bold')}`;
}
});
defs = '<defs><style>' + defs + '</style></defs>';
});
}
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${divWidth}px" height="${divHeight}px">` + defs +
'<style>.__surveypdf_html p { margin: unset; line-height: 22px; }</style>' +
`<foreignObject width="${divWidth}px" height="${divHeight}px">` +
this.htmlToXml(html) + '</foreignObject></svg>';
return { svg, divWidth, divHeight };
}
static setCanvas(canvas, divWidth, divHeight, img) {
canvas.width = divWidth * SurveyHelper.HTML_TO_IMAGE_QUALITY;
canvas.height = divHeight * SurveyHelper.HTML_TO_IMAGE_QUALITY;
const context = canvas.getContext('2d');
context.scale(SurveyHelper.HTML_TO_IMAGE_QUALITY, SurveyHelper.HTML_TO_IMAGE_QUALITY);
context.fillStyle = SurveyHelper.BACKGROUND_COLOR;
context.fillRect(0, 0, divWidth, divHeight);
context.drawImage(img, 0, 0);
}
static htmlToImage(html, width, controller) {
return __awaiter(this, void 0, void 0, function* () {
const { svg, divWidth, divHeight } = SurveyHelper.createSvgContent(html, width, controller);
const data = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg)));
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = data;
return new Promise((resolve) => {
img.onload = function () {
const canvas = document.createElement('canvas');
SurveyHelper.setCanvas(canvas, divWidth, divHeight, img);
const url = canvas.toDataURL('image/jpeg', SurveyHelper.HTML_TO_IMAGE_QUALITY);
canvas.remove();
resolve({ url: url, aspect: divWidth / divHeight });
};
img.onerror = function () {
resolve({ url: 'data:,', aspect: width / this.EPSILON });
};
});
});
}
static createBoldTextFlat(point, question, controller, text) {
return __awaiter(this, void 0, void 0, function* () {
controller.fontStyle = 'bold';
const composite = yield this.createTextFlat(point, question, controller, text, TextBoldBrick);
controller.fontStyle = 'normal';
return composite;
});
}
static createTitleFlat(point, question, controller) {
return __awaiter(this, void 0, void 0, function* () {
const composite = new CompositeBrick();
let currPoint = this.clone(point);
const oldFontSize = controller.fontSize;
controller.fontSize *= this.TITLE_FONT_SCALE;
if (question.no) {
const noText = question.no + ' ';
let noFlat;
if (this.hasHtml(question.locTitle)) {
controller.fontStyle = 'bold';
controller.pushMargins();
controller.margins.right = controller.paperWidth -
controller.margins.left - controller.measureText(noText, 'bold').width;
noFlat = yield this.createHTMLFlat(currPoint, question, controller, this.createHtmlContainerBlock(noText, controller, 'standard'));
controller.popMargins();
controller.fontStyle = 'normal';
}
else {
noFlat = yield this.createBoldTextFlat(currPoint, question, controller, noText);
}
composite.addBrick(noFlat);
currPoint.xLeft = noFlat.xRight;
}
controller.pushMargins();
controller.margins.left = currPoint.xLeft;
const textFlat = yield this.createBoldTextFlat(currPoint, question, controller, question.locTitle);
composite.addBrick(textFlat);
controller.popMargins();
if (question.isRequired) {
const requiredText = question.requiredText;
if (this.hasHtml(question.locTitle)) {
currPoint = this.createPoint(textFlat.unfold()[0], false, false);
controller.fontStyle = 'bold';
controller.pushMargins();
controller.margins.right = controller.paperWidth -
controller.margins.left - controller.measureText(requiredText, 'bold').width;
composite.addBrick(yield this.createHTMLFlat(currPoint, question, controller, this.createHtmlContainerBlock(requiredText, controller, 'standard')));
controller.popMargins();
controller.fontStyle = 'normal';
}
else {
currPoint = this.createPoint(textFlat.unfold().pop(), false, true);
composite.addBrick(yield this.createBoldTextFlat(currPoint, question, controller, requiredText));
}
}
controller.fontSize = oldFontSize;
return composite;
});
}
static createTitleSurveyPanelFlat(point, controller, text, fontSizeScale) {
return __awaiter(this, void 0, void 0, function* () {
const oldFontSize = controller.fontSize;
controller.fontSize = oldFontSize * fontSizeScale;
controller.fontStyle = 'bold';
const titleFlat = yield this.createTextFlat(point, null, controller, text, TitlePanelBrick);
controller.fontStyle = 'normal';
controller.fontSize = oldFontSize;
return titleFlat;
});
}
static createTitleSurveyFlat(point, controller, text) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.createTitleSurveyPanelFlat(point, controller, text, this.TITLE_SURVEY_FONT_SIZE_SCALE);
});
}
static createTitlePanelFlat(point_1, controller_1, text_1) {
return __awaiter(this, arguments, void 0, function* (point, controller, text, isPage = false) {
return yield this.createTitleSurveyPanelFlat(point, controller, text, isPage ? this.TITLE_PAGE_FONT_SIZE_SCALE : this.TITLE_PANEL_FONT_SIZE_SCALE);
});
}
static createDescFlat(point, question, controller, text) {
return __awaiter(this, void 0, void 0, function* () {
const oldFontSize = controller.fontSize;
controller.fontSize = oldFontSize * this.DESCRIPTION_FONT_SIZE_SCALE;
const composite = yield this.createTextFlat(point, question, controller, text, DescriptionBrick);
controller.fontSize = oldFontSize;
return composite;
});
}
static getReadonlyRenderAs(question, controller) {
return question.readonlyRenderAs === 'auto' ? controller.readonlyRenderAs : question.readonlyRenderAs;
}
static createCommentFlat(point_1, question_1, controller_1, isQuestion_1) {
return __awaiter(this, arguments, void 0, function* (point, question, controller, isQuestion, options = {}) {
var _a, _b, _c;
const rect = this.createTextFieldRect(point, controller, (_a = options.rows) !== null && _a !== void 0 ? _a : 1);
let textFlat;
if ((_b = options.readOnly) !== null && _b !== void 0 ? _b : SurveyHelper.shouldRenderReadOnly(question, controller)) {
const renderedValue = (_c = options.value) !== null && _c !== void 0 ? _c : this.getQuestionOrCommentValue(question, isQuestion);
textFlat = yield this.createReadOnlyTextFieldTextFlat(point, controller, question, renderedValue);
const padding = controller.unitHeight * this.VALUE_READONLY_PADDING_SCALE;
if (textFlat.yBot + padding > rect.yBot)
rect.yBot = textFlat.yBot + padding;
}
const comment = new CommentBrick(question, controller, rect, isQuestion, options.index);
if (options.readOnly !== null && options.readOnly !== undefined) {
comment.isReadOnly = options.readOnly;
}
if (textFlat) {
comment.textBrick = textFlat;
}
return comment;
});
}
static getQuestionOrCommentValue(question, isQuestion = true) {
return isQuestion ? (question.value !== undefined && question.value !== null ? question.value : '') :
(question.comment !== undefined && question.comment !== null ? question.comment : '');
}
static getQuestionOrCommentDisplayValue(question, isQuestion = true) {
return isQuestion ? question.displayValue : SurveyHelper.getQuestionOrCommentValue(question, isQuestion);
}
static get hasDocument() {
return typeof document !== 'undefined';
}
static getImageBase64(imageLink) {
return __awaiter(this, void 0, void 0, function* () {
const image = new Image();
image.crossOrigin = 'anonymous';
return new Promise((resolve) => {
image.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.height = image.naturalHeight;
canvas.width = image.naturalWidth;
ctx.drawImage(image, 0, 0);
const dataUrl = canvas.toDataURL();
resolve(dataUrl);
};
image.onerror = () => {
resolve('');
};
image.src = imageLink;
});
});
}
static getImageLink(controller, imageOptions, applyImageFit) {
return __awaiter(this, void 0, void 0, function* () {
const ptToPx = 96.0 / 72.0;
let url = this.shouldConvertImageToPng ? yield SurveyHelper.getImageBase64(imageOptions.link) : imageOptions.link;
if (typeof XMLSerializer === 'function' && applyImageFit) {
const canvasHtml = `<div style='overflow: hidden; width: ${imageOptions.width * ptToPx}px; height: ${imageOptions.height * ptToPx}px;'>
<img src='${url}' style='object-fit: ${imageOptions.objectFit}; width: 100%; height: 100%;'/>
</div>`;
url = (yield SurveyHelper.htmlToImage(canvasHtml, imageOptions.width, controller)).url;
}
return url;
});
}
static createImageFlat(point, question, controller, imageOptions, applyImageFit) {
return __awaiter(this, void 0, void 0, function* () {
if (SurveyHelper.inBrowser) {
imageOptions.objectFit = !!question && !!question.imageFit ? question.imageFit : (imageOptions.objectFit || 'contain');
if (applyImageFit !== null && applyImageFit !== void 0 ? applyImageFit : controller.applyImageFit) {
if (imageOptions.width > controller.paperWidth - controller.margins.left - controller.margins.right) {
const newWidth = controller.paperWidth - controller.margins.left - controller.margins.right;
imageOptions.height *= newWidth / imageOptions.width;
imageOptions.width = newWidth;
}
}
const html = `<img src='${yield SurveyHelper.getImageLink(controller, imageOptions, applyImageFit !== null && applyImageFit !== void 0 ? applyImageFit : controller.applyImageFit)}' width='${imageOptions.width}' height='${imageOptions.height}'/>`;
return new HTMLBrick(question, controller, this.createRect(point, imageOptions.width, imageOptions.height), html, true);
}
return new ImageBrick(question, controller, imageOptions.link, point, imageOptions.width, imageOptions.height);
});
}
static canPreviewImage(question, item, url) {
return question.canPreviewImage(item);
// && await this.getImageSize(url) !== null;
}
static getImageSize(url) {
return __awaiter(this, void 0, void 0, function* () {
if (!SurveyHelper.inBrowser) {
return yield new Promise((resolve) => {
return resolve({ width: undefined, height: undefined });
});
}
return yield new Promise((resolve) => {
const image = new Image();
image.src = url;
image.onload = function () {
return resolve({ width: image.width, height: image.height });
};
image.onerror = function () { return resolve(null); };
});
});
}
static createRowlineFlat(point, controller, width, color) {
let xRight = typeof width === 'undefined' ?
controller.paperWidth - controller.margins.right :
point.xLeft + width;
xRight = xRight > point.xLeft ? xRight : point.xLeft + this.EPSILON;
return new RowlineBrick(controller, {
xLeft: point.xLeft,
xRight: xRight,
yTop: point.yTop + this.EPSILON,
yBot: point.yTop + this.EPSILON
}, typeof color === 'undefined' ? null : color);
}
static createLinkFlat(point, question, controller, text, link) {
return __awaiter(this, void 0, void 0, function* () {
const compositeText = yield this.
createTextFlat(point, question, controller, text, TextBrick);
const compositeLink = new CompositeBrick();
compositeText.unfold().forEach((text) => {
compositeLink.addBrick(new LinkBrick(text, link));
const linePoint = this.createPoint(compositeLink);
compositeLink.addBrick(this.createRowlineFlat(linePoint, controller, compositeLink.width, LinkBrick.COLOR));
});
return compositeLink;
});
}
static createAcroformRect(rect) {
return [
rect.xLeft,
rect.yTop,
rect.xRight - rect.xLeft,
rect.yBot - rect.yTop
];
}
static createTextFieldRect(point, controller, lines = 1) {
let width = controller.paperWidth - point.xLeft - controller.margins.right;
width = Math.max(width, controller.unitWidth);
const height = controller.unitHeight * lines;
return this.createRect(point, width, height);
}
static createReadOnlyTextFieldTextFlat(point, controller, question, value) {
return __awaiter(this, void 0, void 0, function* () {
const padding = controller.unitWidth * this.VALUE_READONLY_PADDING_SCALE;
point.yTop += padding;
point.xLeft += padding;
controller.pushMargins(point.xLeft, controller.margins.right + padding);
const textFlat = yield this.createTextFlat(point, question, controller, value.toString(), TextBrick);
controller.popMargins();
return textFlat;
});
}
static renderFlatBorders(controller, borderOptions) {
var _a, _b, _c, _d;
if (!this.FORM_BORDER_VISIBLE)
return;
borderOptions.rounded = (_a = borderOptions.rounded) !== null && _a !== void 0 ? _a : true;
borderOptions.outside = (_b = borderOptions.outside) !== null && _b !== void 0 ? _b : false;
const minSide = Math.min(borderOptions.width, borderOptions.height);
const borderWidth = this.getBorderWidth(controller);
const visibleWidth = controller.unitHeight * this.VISIBLE_BORDER_SCALE * this.BORDER_SCALE;
const visibleScale = borderOptions.outside ? (minSide + borderWidth) / minSide - visibleWidth / minSide : (minSide - borderWidth) / minSide + visibleWidth / minSide;
const oldDrawColor = controller.doc.getDrawColor();
controller.doc.setDrawColor(borderOptions.formBorderColor);
controller.doc.setLineWidth(visibleWidth);
const scaledRect = this.scaleRect(borderOptions, visibleScale);
if (borderOptions.dashStyle) {
const dashStyle = borderOptions.dashStyle;
const borderLength = (Math.abs(scaledRect.yTop - scaledRect.yBot) + Math.abs(scaledRect.xLeft - scaledRect.xRight)) * 2;
const dashWithSpaceSize = dashStyle.dashArray[0] + ((_c = dashStyle.dashArray[1]) !== null && _c !== void 0 ? _c : dashStyle.dashArray[0]);
const dashSize = dashStyle.dashArray[0] + (borderLength % dashWithSpaceSize) / Math.floor(borderLength / dashWithSpaceSize);
controller.doc.setLineDashPattern([dashSize, (_d = dashStyle.dashArray[1]) !== null && _d !== void 0 ? _d : dashStyle.dashArray[0]], dashStyle.dashPhase);
}
controller.doc.rect(...this.createAcroformRect(scaledRect));
if (borderOptions.rounded) {
const unvisibleWidth = controller.unitHeight * this.UNVISIBLE_BORDER_SCALE * this.BORDER_SCALE;
const unvisibleScale = 1.0 - unvisibleWidth / minSide;
const unvisibleRadius = this.RADIUS_SCALE * unvisibleWidth;
controller.doc.setDrawColor(this.BACKGROUND_COLOR);
controller.doc.setLineWidth(unvisibleWidth);
controller.doc.roundedRect(...this.createAcroformRect(this.scaleRect(borderOptions, unvisibleScale)), unvisibleRadius, unvisibleRadius);
}
if (borderOptions.dashStyle) {
controller.doc.setLineDashPattern([]);
}
controller.doc.setDrawColor(oldDrawColor);
}
static getLocString(text) {
if (this.hasHtml(text))
return text.renderedHtml;
return text.renderedText || text.renderedHtml;
}
static getDropdownQuestionValue(question) {
const qDropDown = question;
if (qDropDown.isOtherSelected) {
return qDropDown.otherText;
}
else if (!!question.displayValue) {
return question.displayValue;
}
else if (qDropDown.showOptionsCaption) {
return qDropDown.optionsCaption;
}
return '';
}
static getContentQuestion(question) {
return !!question.contentQuestion ? question.contentQuestion : question;
}
static getContentQuestionTypeRenderAs(question, survey) {
let renderAs = question.renderAs;
if (question.getType() === 'boolean' && survey.options.useLegacyBooleanRendering) {
renderAs = 'checkbox';
}
if (renderAs !== 'default') {
const type = `${question.getType()}-${renderAs}`;
if (FlatRepository.getInstance().isTypeRegistered(type))
return type;
}
return question.getType();
}
static getContentQuestionType(question, survey) {
if (!!question.customWidget)
return question.customWidget.pdfQuestionType;
return !!question.contentQuestion ? 'custom_model' : this.getContentQuestionTypeRenderAs(question, survey);
}
static getRatingMinWidth(controller) {
return controller.measureText(this.RATING_MIN_WIDTH).width;
}
static getRatingItemText(question, index, locText) {
const ratingItemLocText = new LocalizableString(locText.owner, locText.useMarkdown);
ratingItemLocText.text = this.getLocString(locText);
if (index === 0 && question.minRateDescription) {
ratingItemLocText.text = question.locMinRateDescription.text + ' ' + this.getLocString(locText);
}
else if (index === question.visibleRateValues.length - 1 && question.maxRateDescription) {
ratingItemLocText.text = this.getLocString(locText) + ' ' + question.locMaxRateDescription.text;
}
return ratingItemLocText;
}
static getPageAvailableWidth(controller) {
return controller.paperWidth - controller.margins.left - controller.margins.right;
}
static getImagePickerAvailableWidth(controller) {
const width = (this.getPageAvailableWidth(controller) -
(this.IMAGEPICKER_COUNT - 1) * controller.unitHeight);
return width > 0 ? width : controller.unitHeight;
}
static getColumnWidth(controller, colCount) {
return (this.getPageAvailableWidth(controller) - (colCount - 1) *
controller.unitWidth * this.GAP_BETWEEN_COLUMNS) / colCount;
}
static setColumnMargins(controller, colCount, column) {
const cellWidth = this.getColumnWidth(controller, colCount);
controller.margins.left = controller.margins.left + column *
(cellWidth + controller.unitWidth * this.GAP_BETWEEN_COLUMNS);
controller.margins.right = controller.margins.right + (colCount - column - 1) *
(cellWidth + controller.unitWidth * this.GAP_BETWEEN_COLUMNS);
}
static moveRect(rect, left = rect.xLeft, top = rect.yTop) {
return {
xLeft: left,
yTop: top,
xRight: left + rect.xRight - rect.xLeft,
yBot: top + rect.yBot - rect.yTop
};
}
static scaleRect(rect, scale) {
const scaleWidth = Math.min(rect.xRight - rect.xLeft, rect.yBot - rect.yTop) * (1.0 - scale) / 2.0;
return {
xLeft: rect.xLeft + scaleWidth,
yTop: rect.yTop + scaleWidth,
xRight: rect.xRight - scaleWidth,
yBot: rect.yBot - scaleWidth
};
}
static getBorderWidth(controller) {
return 2.0 * controller.unitWidth * this.BORDER_SCALE;
}
static formScale(controller, flat) {
const minSide = Math.min(flat.width, flat.height);
return (minSide - this.getBorderWidth(controller)) / minSide;
}
static generateQuestionFlats(survey, controller, question, point) {
return __awaiter(this, void 0, void 0, function* () {
const questionType = this.getContentQuestionType(question, survey);
const flatQuestion = FlatRepository.getInstance().
create(survey, question, controller, questionType);
const questionFlats = yield flatQuestion.generateFlats(point);
const adornersOptions = new AdornersOptions(point, questionFlats, question, controller, FlatRepository.getInstance(), SurveyPDFModule);
if (question.customWidget && question.customWidget.isFit(question) &&
question.customWidget.pdfRender) {
survey.onRenderQuestion.unshift(question.customWidget.pdfRender);
}
yield survey.onRenderQuestion.fire(survey, adornersOptions);
return [...adornersOptions.bricks];
});
}
static isFontExist(controller, fontName) {
return controller.doc.internal.getFont(fontName).fontName === fontName;
}
static isCustomFont(controller, fontName) {
return controller.doc.internal.getFont(fontName).encoding === this.CUSTOM_FONT_ENCODING;
}
static fixFont(controller) {
if (this.isCustomFont(controller, controller.fontName)) {
controller.doc.text('load font', 0, 0);
controller.doc.deletePage(1);
controller.addPage();
}
}
static clone(src) {
const target = {};
for (const prop in src) {
if (src.hasOwnProperty(prop)) {
target[prop] = src[prop];
}
}
return target;
}
static shouldRenderReadOnly(question, controller, readOnly) {
return ((!!question && question.isReadOnly || readOnly) && SurveyHelper.getReadonlyRenderAs(question, controller) !== 'acroform') || (controller === null || controller === void 0 ? void 0 : controller.compress);
}
static isSizeEmpty(val) {
return !val || val === 'auto';
}
static isHeightEmpty(val) {
return this.isSizeEmpty(val) || val == '100%';
}
static getCorrectedImageSize(controller, imageOptions) {
return __awaiter(this, void 0, void 0, function* () {
let { imageWidth, imageLink, imageHeight, defaultImageWidth, defaultImageHeight } = imageOptions;
imageWidth = typeof imageWidth === 'number' ? imageWidth.toString() : imageWidth;
imageHeight = typeof imageHeight === 'number' ? imageHeight.toString() : imageHeight;
let widthPt = imageWidth && SurveyHelper.parseWidth(imageWidth, SurveyHelper.getPageAvailableWidth(controller), 1, 'px');
let heightPt = imageHeight && SurveyHelper.parseWidth(imageHeight, SurveyHelper.getPageAvailableWidth(controller), 1, 'px');
defaultImageWidth = typeof defaultImageWidth === 'number' ? defaultImageWidth.toString() : defaultImageWidth;
defaultImageHeight = typeof defaultImageHeight === 'number' ? defaultImageHeight.toString() : defaultImageHeight;
let defaultWidthPt = defaultImageWidth && SurveyHelper.parseWidth(defaultImageWidth, SurveyHelper.getPageAvailableWidth(controller), 1, 'px');
let defaultHeightPt = defaultImageHeight && SurveyHelper.parseWidth(defaultImageHeight, SurveyHelper.getPageAvailableWidth(controller), 1, 'px');
if (SurveyHelper.isSizeEmpty(imageWidth) || SurveyHelper.isHeightEmpty(imageHeight)) {
const imageSize = yield SurveyHelper.getImageSize(imageLink);
if (!SurveyHelper.isSizeEmpty(imageWidth)) {
if (imageSize && imageSize.width) {
heightPt = imageSize.height * widthPt / imageSize.width;
}
}
else if (!SurveyHelper.isHeightEmpty(imageHeight)) {
if (imageSize && imageSize.height) {
widthPt = imageSize.width * heightPt / imageSize.height;
}
}
else if (imageSize && imageSize.height && imageSize.width) {
heightPt = SurveyHelper.parseWidth(imageSize.height.toString(), SurveyHelper.getPageAvailableWidth(controller), 1, 'px');
widthPt = SurveyHelper.parseWidth(imageSize.width.toString(), SurveyHelper.getPageAvailableWidth(controller), 1, 'px');
}
}
return { width: widthPt || defaultWidthPt || 0, height: heightPt || defaultHeightPt || 0 };
});
}
}
SurveyHelper.EPSILON = 2.2204460492503130808472633361816e-15;
SurveyHelper.TITLE_SURVEY_FONT_SIZE_SCALE = 1.7;
SurveyHelper.TITLE_PAGE_FONT_SIZE_SCALE = 1.3;
SurveyHelper.TITLE_PANEL_FONT_SIZE_SCALE = 1.3;
SurveyHelper.DESCRIPTION_FONT_SIZE_SCALE = 2.0 / 3.0;
SurveyHelper.OTHER_ROWS_COUNT = 2;
SurveyHelper.RATING_MIN_WIDTH = 3;
SurveyHelper.RATING_MIN_HEIGHT = 2;
SurveyHelper.RATING_COLUMN_WIDTH = 5;
SurveyHelper.MATRIX_COLUMN_WIDTH = 5;
SurveyHelper.IMAGEPICKER_COUNT = 4;
SurveyHelper.IMAGEPICKER_RATIO = 4.0 / 3.0;
SurveyHelper.MULTIPLETEXT_TEXT_PERS = Math.E / 10.0;
SurveyHelper.HTML_TAIL_TEXT_SCALE = 0.24;
SurveyHelper.SELECT_ITEM_FLAT_SCALE = 0.95;
SurveyHelper.GAP_BETWEEN_ROWS = 0.25;
SurveyHelper.GAP_BETWEEN_COLUMNS = 1.5;
SurveyHelper.GAP_BETWEEN_ITEM_TEXT = 0.25;
SurveyHelper.FORM_BORDER_VISIBLE = true;
SurveyHelper.BORDER_SCALE = 0.1;
SurveyHelper.VISIBLE_BORDER_SCALE = 0.8;
SurveyHelper.UNVISIBLE_BORDER_SCALE = 0.2;
SurveyHelper.RADIUS_SCALE = 3.0;
SurveyHelper.TITLE_FONT_SCALE = 1.1;
SurveyHelper.VALUE_READONLY_PADDING_SCALE = 0.3;
SurveyHelper.HTML_TO_IMAGE_QUALITY = 1.0;
SurveyHelper.FORM_BORDER_COLOR = '#9f9f9f';
SurveyHelper.TEXT_COLOR = '#404040';
SurveyHelper.BACKGROUND_COLOR = '#FFFFFF';
SurveyHelper.TITLE_LOCATION_MATRIX = 'matrix';
SurveyHelper.STANDARD_FONT = 'helvetica';
SurveyHelper.CUSTOM_FONT_ENCODING = 'Identity-H';
SurveyHelper.inBrowser = typeof Image === 'function';
SurveyHelper.shouldConvertImageToPng = true;
function setRadioAppearance(doc) {
const oldAppearanceFuncition = doc.AcroFormAppearance.RadioButton.Circle.YesNormal;
doc.AcroFormAppearance.RadioButton.Circle.YesNormal = function (formObject) {
const xobj = oldAppearanceFuncition(formObject);
const stream = xobj.stream.split('\n');
const encodeColor = doc.__private__.encodeColorString(formObject.color);
stream[0] = stream[0] + '\n' + encodeColor + '\n' + encodeColor.toUpperCase();
xobj.stream = stream.join('\n');
return xobj;
};
}
/* global jsPDF */
/**
* @license
* Copyright (c) 2016 Alexander Weidt,
* https://github.com/BiggA94
*
* Licensed under the MIT License. http://opensource.org/licenses/mit-license
*/
(function (jsPDF, globalObj) {
var jsPDFAPI = jsPDF.API;
var scope;
var scaleFactor = 1;
function toUnicode(str) {
var unicodeString = '';
for (var i = 0; i < str.length; i++) {
var theUnicode = str.charCodeAt(i).toString(16).toUpperCase();
while (theUnicode.length < 4) {
theUnicode = '0' + theUnicode;
} unicodeString += theUnicode;
}
return '<FEFF' + unicodeString + '>';
}
var pdfEscape = function (value) { return value.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)') };
var pdfUnescape = function (value) { return value.replace(/\\\\/g, '\\').replace(/\\\(/g, '(').replace(/\\\)/g, ')'); };
function arrayToPdfUnicodeArray(value) {
var result = '[ ';
for (var i = 0; i < value.length; i++) {
result += toUnicode(value[i]);
}
result += ' ]';
return result;
}
var f2 = function (number) {
return number.toFixed(2); // Ie, %.2f
};
var f5 = function (number) {
return number.toFixed(5); // Ie, %.2f
};
jsPDFAPI.__acroform__ = {};
var inherit = function (child, parent) {
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
};
var scale = function (x) {
return (x * scaleFactor);
};
var antiScale = function (x) {
return (x / scaleFactor);
};
var createFormXObject = function (formObject) {
var xobj = new AcroFormXObject();
var height = AcroFormAppearance.internal.getHeight(formObject) || 0;
var width = AcroFormAppearance.internal.getWidth(formObject) || 0;
xobj.BBox = [0, 0, Number(f2(width)), Number(f2(height))];
return xobj;
};
/**
* Bit-Operations
*/
var setBit = jsPDFAPI.__acroform__.setBit = function (number, bitPosition) {
number = number || 0;
bitPosition = bitPosition || 0;
if (isNaN(number) || isNaN(bitPosition)) {
throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.setBit');
}
var bitMask = 1 << bitPosition;
number |= bitMask;
return number;
};
var clearBit = jsPDFAPI.__acroform__.clearBit = function (number, bitPosition) {
number = number || 0;
bitPosition = bitPosition || 0;
if (isNaN(number) || isNaN(bitPosition)) {
throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.clearBit');
}
var bitMask = 1 << bitPosition;
number &= ~bitMask;
return number;
};
var getBit = jsPDFAPI.__acroform__.getBit = function (number, bitPosition) {
if (isNaN(number) || isNaN(bitPosition)) {
throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.getBit');
}
return (number & (1 << bitPosition)) === 0 ? 0 : 1;
};
/*
* Ff starts counting the bit position at 1 and not like javascript at 0
*/
var getBitForPdf = jsPDFAPI.__acroform__.getBitForPdf = function (number, bitPosition) {
if (isNaN(number) || isNaN(bitPosition)) {
throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.getBitForPdf');
}
return getBit(number, bitPosition - 1);
};
var setBitForPdf = jsPDFAPI.__acroform__.setBitForPdf = function (number, bitPosition) {
if (isNaN(number) || isNaN(bitPosition)) {
throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.setBitForPdf');
}
return setBit(number, bitPosition - 1);
};
var clearBitForPdf = jsPDFAPI.__acroform__.clearBitForPdf = function (number, bitPosition) {
if (isNaN(number) || isNaN(bitPosition)) {
throw new Error('Invalid arguments passed to jsPDF.API.__acroform__.clearBitForPdf');
}
return clearBit(number, bitPosition - 1);
};
var calculateCoordinates = jsPDFAPI.__acroform__.calculateCoordinates = function (args) {
var getHorizontalCoordinate = this.internal.getHorizontalCoordinate;
var getVerticalCoordinate = this.internal.getVerticalCoordinate;
var x = args[0];
var y = args[1];
var w = args[2];
var h = args[3];
var coordinates = {};
coordinates.lowerLeft_X = getHorizontalCoordinate(x) || 0;
coordinates.lowerLeft_Y = getVerticalCoordinate(y + h) || 0;
coordinates.upperRight_X = getHorizontalCoordinate(x + w) || 0;
coordinates.upperRight_Y = getVerticalCoordinate(y) || 0;
return [Number(f2(coordinates.lowerLeft_X)), Number(f2(coordinates.lowerLeft_Y)), Number(f2(coordinates.upperRight_X)), Number(f2(coordinates.upperRight_Y))];
};
var calculateAppearanceStream = function (formObject) {
if (formObject.appearanceStreamContent) {
return formObject.appearanceStreamContent;
}
if (!formObject.V && !formObject.DV) {
return;
}
// else calculate it
var stream = [];
var text = formObject.V || formObject.DV;
var calcRes = calculateX(formObject, text);
var fontKey = scope.internal.getFont(formObject.fontName, formObject.fontStyle).id;
//PDF 32000-1:2008, page 444
stream.push('/Tx BMC');
stream.push('q');
stream.push('BT'); // Begin Text
stream.push(scope.__private__.encodeColorString(formObject.color));
stream.push('/' + fontKey + ' ' + f2(calcRes.fontSize) + ' Tf');
stream.push('1 0 0 1 0 0 Tm');// Transformation Matrix
stream.push(calcRes.text);
stream.push('ET'); // End Text
stream.push('Q');
stream.push('EMC');
var appearanceStreamContent = new createFormXObject(formObject);
appearanceStreamContent.stream = stream.join("\n");
return appearanceStreamContent;
};
var calculateX = function (formObject, text) {
if (formObject.isUnicode) text = formObject.trueValue;
var maxFontSize =
formObject.fontSize === 0 ? formObject.maxFontSize : formObject.fontSize;
var returnValue = {
text: "",
fontSize: ""
};
// Remove Brackets
text = text.substr(0, 1) == "(" ? text.substr(1) : text;
text =
text.substr(text.length - 1) == ")"
? text.substr(0, text.length - 1)
: text;
// split into array of words
var textSplit = text.split(" ");
textSplit = textSplit.map(function (word) {
return word.split("\n");
});
if (!formObject.multiline) {
textSplit = textSplit.map(function (arr) {
return [arr.join(" ")]
});
}
var fontSize = maxFontSize; // The Starting fontSize (The Maximum)
var lineSpacing = 2;
var borderPadding = 2;
var height = AcroFormAppearance.internal.getHeight(formObject) || 0;
height = height < 0 ? -height : height;
var width = AcroFormAppearance.internal.getWidth(formObject) || 0;
width = width < 0 ? -width : width;
var isSmallerThanWidth = function(i, lastLine, fontSize) {
if (i + 1 < textSplit.length) {
var tmp = lastLine + " " + textSplit[i + 1][0];
var TextWidth = calculateFontSpace(tmp, formObject, fontSize).width;
var FieldWidth = width - 2 * borderPadding;
return TextWidth <= FieldWidth;
} else {
return false;
}
};
fontSize++;
FontSize: while (fontSize > 0) {
text = "";
fontSize--;
var textHeight = calculateFontSpace("3", formObject, fontSize).height;
var startY = formObject.multiline
? height - fontSize
: (height - textHeight) / 2;
startY += lineSpacing;
var startX;
var lastY = startY;
var firstWordInLine = 0,
lastWordInLine = 0;
var lastLength;
var currWord = 0;
if (fontSize <= 0) {
// In case, the Text doesn't fit at all
fontSize = 12;
text = "(...) Tj\n";
text +=
"% Width of Text: " +
calculateFontSpace(text, formObject, fontSize).width +
", FieldWidth:" +
width +
"\n";
break;
}
var lastLine = "";
var lineCount = 0;
Line: for (var i = 0; i < textSplit.length; i++) {
if (textSplit.hasOwnProperty(i)) {
var isWithNewLine = false;
if (textSplit[i].length !== 1 && currWord !== textSplit[i].length - 1) {
if (
(textHeight + lineSpacing) * (lineCount + 2) + lineSpacing >
height
) {
continue FontSize;
}
lastLine += textSplit[i][currWord];
isWithNewLine = true;
lastWordInLine = i;
i--;
} else {
lastLine += textSplit[i][currWord] + " ";
lastLine =
lastLine.substr(lastLine.length - 1) == " "
? lastLine.substr(0, lastLine.length - 1)
: lastLine;
var key = parseInt(i);
var nextLineIsSmaller = isSmallerThanWidth(key, lastLine, fontSize);
var isLastWord = i >= textSplit.length - 1;
if (nextLineIsSmaller && !isLastWord) {
lastLine += " ";
currWord = 0;
continue; // Line
} else if (!nextLineIsSmaller && !isLastWord) {
if (!formObject.multiline) {
continue FontSize;
} else {
if (
(textHeight + lineSpacing) * (lineCount + 2) + lineSpacing >
height
) {
// If the Text is higher than the
// FieldObject
continue FontSize;
}
lastWordInLine = key;
// go on
}
} else if (isLastWord) {
lastWordInLine = key;
} else {
if (
formObject.multiline &&
(textHeight + lineSpacing) * (lineCount + 2) + lineSpacing >
height
) {
// If the Text is higher than the FieldObject
continue FontSize;
}
}
}
// Remove last blank
var line = "";
for (var x = firstWordInLine; x <= lastWordInLine; x++) {
var currLine = textSplit[x];
if (formObject.multiline) {
if (x === lastWordInLine) {
line += currLine[currWord] + " ";
currWord = (currWord + 1) % currLine.length;
continue;
}
if (x === firstWordInLine) {
line += currLine[currLine.length - 1] + " ";
continue;
}
}
line += currLine[0] + " ";
}
// Remove last blank
line =
line.substr(line.length - 1) == " "
? line.substr(0, line.length - 1)
: line;
// lastLength -= blankSpace.width;
lastLength = calculateFontSpace(line, formObject, fontSize).width;
// Calculate startX
switch (formObject.textAlign) {
case "right":
startX = width - lastLength - borderPadding;
break;
case "center":
startX = (width - lastLength) / 2;
break;
case "left":
default:
startX = borderPadding;
break;
}
text += f2(startX) + " " + f2(lastY) + " Td\n";
if (formObject.isUnicode) {
var fontList = {};
fontList[scope.internal.getFont().id] = scope.internal.getFont();
var payload = {
text: line,
x: null,
y: null,
options: {
lang: null
},
mutex: {
pdfEscape: pdfEscape,
activeFontKey: scope.internal.getFont().id,
fonts: fontList,
activeFontSize: formObject.fontSize
}
};
scope.internal.events.publish('postProcessText', payload);
text += '<' + payload.text + '> Tj\n';
}
else {
text += '(' + pdfEscape(line) + ') Tj\n';
}
// reset X in PDF
text += -f2(startX) + " 0 Td\n";
// After a Line, adjust y position
lastY = -(fontSize + lineSpacing);
// Reset for next iteration step
lastLength = 0;
firstWordInLine = isWithNewLine ? lastWordInLine : lastWordInLine + 1;
lineCount++;
lastLine = "";
continue Line;
}
}
break;
}
returnValue.text = text;
returnValue.fontSize = fontSize;
return returnValue;
};
/**
* Small workaround for calculating the TextMetric approximately.
*
* @param text
* @param fontsize
* @returns {TextMetrics} (Has Height and Width)
*/
var calculateFontSpace = function (text, formObject, fontSize) {
var font = scope.internal.getFont(formObject.fontName, formObject.fontStyle);
var width = scope.getStringUnitWidth(text, { font: font, fontSize: parseFloat(fontSize), charSpace: 0 }) * parseFloat(fontSize);
var height = scope.getStringUnitWidth("3", { font: font, fontSize: parseFloat(fontSize), charSpace: 0 }) * parseFloat(fontSize) * 1.5;
return { height: height, width: width };
};
var acroformPluginTemplate = {
fields: [],
xForms: [],
/**
* acroFormDictionaryRoot contains information about the AcroForm
* Dictionary 0: The Event-Token, the AcroFormDictionaryCallback has
* 1: The Object ID of the Root
*/
acroFormDictionaryRoot: null,
/**
* After the PDF gets evaluated, the reference to the root has to be
* reset, this indicates, whether the root has already been printed
* out
*/
printedOut: false,
internal: null,
isInitialized: false
};
var annotReferenceCallback = function () {
//set objId to undefined and force it to get a new objId on buildDocument
scope.internal.acroformPlugin.acroFormDictionaryRoot.objId = undefined;
var fields = scope.internal.acroformPlugin.acroFormDictionaryRoot.Fields;
for (var i in fields) {
if (fields.hasOwnProperty(i)) {
var formObject = fields[i];
//set objId to undefined and force it to get a new objId on buildDocument
formObject.objId = undefined;
// add Annot Reference!
if (formObject.hasAnnotation) {
// If theres an Annotation Widget in the Form Object, put the
// Reference in the /Annot array
createAnnotationReference.call(scope, formObject);
}
}
}
};
var putForm = function (formObject) {
if (scope.internal.acroformPlugin.printedOut) {
scope.internal.acroformPlugin.printedOut = false;
scope.internal.acroformPlugin.acroFormDictionaryRoot = null;
}
if (!scope.internal.acroformPlugin.acroFormDictionaryRoot) {
initializeAcroForm.call(scope);
}
scope.internal.acroformPlugin.acroFormDictionaryRoot.Fields.push(formObject);
};
/**
* Create the Reference to the widgetAnnotation, so that it gets referenced
* in the Annot[] int the+ (Requires the Annotation Plugin)
*/
var createAnnotationReference = function (object) {
var options = {
type: 'reference',
object: object
};
var findEntry = function (entry) { return (entry.type === options.type && entry.object === options.object); };
if (scope.internal.getPageInfo(object.page).pageContext.annotations.find(findEntry) === undefined) {
scope.internal.getPageInfo(object.page).pageContext.annotations.push(options);
}
};
// Callbacks
var putCatalogCallback = function () {
// Put reference to AcroForm to DocumentCatalog
if (typeof scope.internal.acroformPlugin.acroFormDictionaryRoot != 'undefined') {
// for safety, shouldn't normally be the case
scope.internal.write('/AcroForm ' + scope.internal.acroformPlugin.acroFormDictionaryRoot.objId + ' ' + 0 + ' R');
} else {
throw new Error('putCatalogCallback: Root missing.');
}
};
/**
* Adds /Acroform X 0 R to Document Catalog, and creates the AcroForm
* Dictionary
*/
var AcroFormDictionaryCallback = function () {
// Remove event
scope.internal.events.unsubscribe(scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID);
delete scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID;
scope.internal.acroformPlugin.printedOut = true;
};
/**
* Creates the single Fields and writes them into the Document
*
* If fieldArray is set, use the fields that are inside it instead of the
* fields from the AcroRoot (for the FormXObjects...)
*/
var createFieldCallback = function (fieldArray) {
var standardFields = (!fieldArray);
if (!fieldArray) {
// in case there is no fieldArray specified, we want to print out
// the Fields of the AcroForm
// Print out Root
scope.internal.newObjectDeferredBegin(scope.internal.acroformPlugin.acroFormDictionaryRoot.objId, true);
scope.internal.acroformPlugin.acroFormDictionaryRoot.putStream();
}
fieldArray = fieldArray || scope.internal.acroformPlugin.acroFormDictionaryRoot.Kids;
for (var i in fieldArray) {
if (fieldArray.hasOwnProperty(i)) {
var fieldObject = fieldArray[i];
var keyValueList = [];
var oldRect = fieldObject.Rect;
if (fieldObject.Rect) {
fieldObject.Rect = calculateCoordinates.call(this, fieldObject.Rect);
}
// Start Writing the Object
scope.internal.newObjectDeferredBegin(fieldObject.objId, true);
fieldObject.DA = AcroFormAppearance.createDefaultAppearanceStream(fieldObject);
if (typeof fieldObject === "object" && typeof fieldObject.getKeyValueListForStream === "function") {
keyValueList = fieldObject.getKeyValueListForStream();
}
fieldObject.Rect = oldRect;
if (fieldObject.hasAppearanceStream && !fieldObject.appearanceStreamContent) {
// Calculate Appearance
var appearance = calculateAppearanceStream.call(this, fieldObject);
keyValueList.push({ key: 'AP', value: "<</N " + appearance + ">>" });
scope.internal.acroformPlugin.xForms.push(appearance);
}
// Assume AppearanceStreamContent is a Array with N,R,D (at least
// one of them!)
if (fieldObject.appearanceStreamContent) {
var appearanceStreamString = "";
// Iterate over N,R and D
for (var k in fieldObject.appearanceStreamContent) {
if (fieldObject.appearanceStreamContent.hasOwnProperty(k)) {
var value = fieldObject.appearanceStreamContent[k];
appearanceStreamString += ("/" + k + " ");
appearanceStreamString += "<<";
if (Object.keys(value).length >= 1 || Array.isArray(value)) {
// appearanceStream is an Array or Object!
for (var i in value) {
if (value.hasOwnProperty(i)) {
var obj = value[i];
if (typeof obj === 'function') {
// if Function is referenced, call it in order
// to get the FormXObject
obj = obj.call(this, fieldObject);
}
appearanceStreamString += ("/" + i + " " + obj + " ");
// In case the XForm is already used, e.g. OffState
// of CheckBoxes, don't add it
if (!(scope.internal.acroformPlugin.xForms.indexOf(obj) >= 0))
scope.internal.acroformPlugin.xForms.push(obj);
}
}
} else {
obj = value;
if (typeof obj === 'function') {
// if Function is referenced, call it in order to
// get the FormXObject
obj = obj.call(this, fieldObject);
}
appearanceStreamString += ("/" + i + " " + obj);
if (!(scope.internal.acroformPlugin.xForms.indexOf(obj) >= 0))
scope.internal.acroformPlugin.xForms.push(obj);
}
appearanceStreamString += ">>";
}
}
// appearance stream is a normal Object..
keyValueList.push({ key: 'AP', value: "<<\n" + appearanceStreamString + ">>" });
}
scope.internal.putStream({ additionalKeyValues: keyValueList });
scope.internal.out("endobj");
}
}
if (standardFields) {
createXFormObjectCallback.call(this, scope.internal.acroformPlugin.xForms);
}
};
var createXFormObjectCallback = function (fieldArray) {
for (var i in fieldArray) {
if (fieldArray.hasOwnProperty(i)) {
var key = i;
var fieldObject = fieldArray[i];
// Start Writing the Object
scope.internal.newObjectDeferredBegin(fieldObject && fieldObject.objId, true);
if (typeof fieldObject === "object" && typeof fieldObject.putStream === "function") {
fieldObject.putStream();
}
delete fieldArray[key];
}
}
};
var initializeAcroForm = function () {
if (this.internal !== undefined && (this.internal.acroformPlugin === undefined || this.internal.acroformPlugin.isInitialized === false)) {
scope = this;
AcroFormField.FieldNum = 0;
this.internal.acroformPlugin = JSON.parse(JSON.stringify(acroformPluginTemplate));
if (this.internal.acroformPlugin.acroFormDictionaryRoot) {
throw new Error("Exception while creating AcroformDictionary");
}
scaleFactor = scope.internal.scaleFactor;
// The Object Number of the AcroForm Dictionary
scope.internal.acroformPlugin.acroFormDictionaryRoot = new AcroFormDictionary();
// add Callback for creating the AcroForm Dictionary
scope.internal.acroformPlugin.acroFormDictionaryRoot._eventID = scope.internal.events.subscribe('postPutResources', AcroFormDictionaryCallback);
scope.internal.events.subscribe('buildDocument', annotReferenceCallback); // buildDocument
// Register event, that is triggered when the DocumentCatalog is
// written, in order to add /AcroForm
scope.internal.events.subscribe('putCatalog', putCatalogCallback);
// Register event, that creates all Fields
scope.internal.events.subscribe('postPutPages', createFieldCallback);
scope.internal.acroformPlugin.isInitialized = true;
}
};
//PDF 32000-1:2008, page 26, 7.3.6
var arrayToPdfArray = jsPDFAPI.__acroform__.arrayToPdfArray = function (array) {
if (Array.isArray(array)) {
var content = '[';
for (var i = 0; i < array.length; i++) {
if (i !== 0) {
content += ' ';
}
switch (typeof array[i]) {
case 'boolean':
case 'number':
case 'object':
content += array[i].toString();
break;
case 'string':
if (array[i].substr(0, 1) !== '/') {
content += '(' + pdfEscape(array[i].toString()) + ')';
} else {
content += array[i].toString();
}
break;
}
}
content += ']';
return content;
}
throw new Error('Invalid argument passed to jsPDF.__acroform__.arrayToPdfArray');
};
function getMatches(string, regex, index) {
index || (index = 1); // default to the first capturing group
var matches = [];
var match;
while (match = regex.exec(string)) {
matches.push(match[index]);
}
return matches;
}
var pdfArrayToStringArray = function (array) {
var result = [];
if (typeof array === "string") {
result = getMatches(array, /\((.*?)\)/g);
}
return result;
};
var toPdfString = function (string) {
string = string || "";
string.toString();
string = '(' + pdfEscape(string) + ')';
return string;
};
// ##########################
// Classes
// ##########################
/**
* @class AcroFormPDFObject
* @classdesc A AcroFormPDFObject
*/
var AcroFormPDFObject = function () {
var _objId;
/**
* @name AcroFormPDFObject#objId
* @type {any}
*/
Object.defineProperty(this, 'objId', {
configurable: true,
get: function () {
if (!_objId) {
_objId = scope.internal.newObjectDeferred();
}
return _objId
},
set: function (value) {
_objId = value;
}
});
};
/**
* @function AcroFormPDFObject.toString
*/
AcroFormPDFObject.prototype.toString = function () {
return this.objId + " 0 R";
};
AcroFormPDFObject.prototype.putStream = function () {
var keyValueList = this.getKeyValueListForStream();
scope.internal.putStream({ data: this.stream, additionalKeyValues: keyValueList });
scope.internal.out("endobj");
};
/**
* Returns an key-value-List of all non-configurable Variables from the Object
*
* @name getKeyValueListForStream
* @returns {string}
*/
AcroFormPDFObject.prototype.getKeyValueListForStream = function () {
var createKeyValueListFromFieldObject = function (fieldObject) {
var keyValueList = [];
var keys = Object.getOwnPropertyNames(fieldObject).filter(function (key) {
return (key != 'content' && key != 'appearanceStreamContent' && key.substring(0, 1) != "_");
});
for (var i in keys) {
var propertyDescriptor = Object.getOwnPropertyDescriptor(fieldObject, keys[i]);
if (propertyDescriptor && propertyDescriptor.configurable === false) {
var key = keys[i];
var value = fieldObject[key];
if (value) {
if (Array.isArray(value)) {
keyValueList.push({ key: key, value: arrayToPdfArray(value) });
} else if (value instanceof AcroFormPDFObject) {
// In case it is a reference to another PDFObject,
// take the reference number
keyValueList.push({ key: key, value: value.objId + " 0 R" });
} else if (typeof value !== "function") {
keyValueList.push({ key: key, value: value });
}
}
}
}
return keyValueList;
};
return createKeyValueListFromFieldObject(this);
};
var AcroFormXObject = function () {
AcroFormPDFObject.call(this);
Object.defineProperty(this, 'Type', {
value: "/XObject",
configurable: false,
writeable: true
});
Object.defineProperty(this, 'Subtype', {
value: "/Form",
configurable: false,
writeable: true
});
Object.defineProperty(this, 'FormType', {
value: 1,
configurable: false,
writeable: true
});
var _BBox = [];
Object.defineProperty(this, 'BBox', {
configurable: false,
writeable: true,
get: function () {
return _BBox;
},
set: function (value) {
_BBox = value;
}
});
Object.defineProperty(this, 'Resources', {
value: "2 0 R",
configurable: false,
writeable: true
});
var _stream;
Object.defineProperty(this, 'stream', {
enumerable: false,
configurable: true,
set: function (value) {
_stream = value.trim();
},
get: function () {
if (_stream) {
return _stream;
} else {
return null;
}
}
});
};
inherit(AcroFormXObject, AcroFormPDFObject);
var AcroFormDictionary = function () {
AcroFormPDFObject.call(this);
var _Kids = [];
Object.defineProperty(this, 'Kids', {
enumerable: false,
configurable: true,
get: function () {
if (_Kids.length > 0) {
return _Kids;
} else {
return undefined;
}
}
});
Object.defineProperty(this, 'Fields', {
enumerable: false,
configurable: false,
get: function () {
return _Kids;
}
});
// Default Appearance
var _DA;
Object.defineProperty(this, 'DA', {
enumerable: false,
configurable: false,
get: function () {
if (!_DA) {
return undefined;
}
return '(' + _DA + ')'
},
set: function (value) {
_DA = value;
}
});
};
inherit(AcroFormDictionary, AcroFormPDFObject);
/**
* The Field Object contains the Variables, that every Field needs
*
* @class AcroFormField
* @classdesc An AcroForm FieldObject
*/
var AcroFormField = function () {
AcroFormPDFObject.call(this);
//Annotation-Flag See Table 165
var _F = 4;
this.isUnicode = false;
this.trueValue = '';
Object.defineProperty(this, 'F', {
enumerable: false,
configurable: false,
get: function () {
return _F;
},
set: function (value) {
if (!isNaN(value)) {
_F = value;
} else {
throw new Error('Invalid value "' + value + '" for attribute F supplied.');
}
}
});
/**
* (PDF 1.2) If set, print the annotation when the page is printed. If clear, never print the annotation, regardless of wether is is displayed on the screen.
* NOTE 2 This can be useful for annotations representing interactive pushbuttons, which would serve no meaningful purpose on the printed page.
*
* @name AcroFormField#showWhenPrinted
* @default true
* @type {boolean}
*/
Object.defineProperty(this, 'showWhenPrinted', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(_F, 3));
},
set: function (value) {
if (Boolean(value) === true) {
this.F = setBitForPdf(_F, 3);
} else {
this.F = clearBitForPdf(_F, 3);
}
}
});
var _Ff = 0;
Object.defineProperty(this, 'Ff', {
enumerable: false,
configurable: false,
get: function () {
return _Ff;
},
set: function (value) {
if (!isNaN(value)) {
_Ff = value;
} else {
throw new Error('Invalid value "' + value + '" for attribute Ff supplied.');
}
}
});
var _Rect = [];
Object.defineProperty(this, 'Rect', {
enumerable: false,
configurable: false,
get: function () {
if (_Rect.length === 0) {
return undefined;
}
return _Rect;
},
set: function (value) {
if (typeof value !== "undefined") {
_Rect = value;
} else {
_Rect = [];
}
}
});
/**
* The x-position of the field.
*
* @name AcroFormField#x
* @default null
* @type {number}
*/
Object.defineProperty(this, 'x', {
enumerable: true,
configurable: true,
get: function () {
if (!_Rect || isNaN(_Rect[0])) {
return 0;
}
return antiScale(_Rect[0]);
},
set: function (value) {
_Rect[0] = scale(value);
}
});
/**
* The y-position of the field.
*
* @name AcroFormField#y
* @default null
* @type {number}
*/
Object.defineProperty(this, 'y', {
enumerable: true,
configurable: true,
get: function () {
if (!_Rect || isNaN(_Rect[1])) {
return 0;
}
return antiScale(_Rect[1]);
},
set: function (value) {
_Rect[1] = scale(value);
}
});
/**
* The width of the field.
*
* @name AcroFormField#width
* @default null
* @type {number}
*/
Object.defineProperty(this, 'width', {
enumerable: true,
configurable: true,
get: function () {
if (!_Rect || isNaN(_Rect[2])) {
return 0;
}
return antiScale(_Rect[2]);
},
set: function (value) {
_Rect[2] = scale(value);
}
});
/**
* The height of the field.
*
* @name AcroFormField#height
* @default null
* @type {number}
*/
Object.defineProperty(this, 'height', {
enumerable: true,
configurable: true,
get: function () {
if (!_Rect || isNaN(_Rect[3])) {
return 0;
}
return antiScale(_Rect[3]);
},
set: function (value) {
_Rect[3] = scale(value);
}
});
var _FT = "";
Object.defineProperty(this, 'FT', {
enumerable: true,
configurable: false,
get: function () {
return _FT
},
set: function (value) {
switch (value) {
case '/Btn':
case '/Tx':
case '/Ch':
case '/Sig':
_FT = value;
break;
default:
throw new Error('Invalid value "' + value + '" for attribute FT supplied.');
}
}
});
var _T = null;
Object.defineProperty(this, 'T', {
enumerable: true,
configurable: false,
get: function () {
if (!_T || _T.length < 1) {
// In case of a Child from a Radio´Group, you don't need a FieldName
if (this instanceof AcroFormChildClass) {
return undefined;
}
_T = "FieldObject" + (AcroFormField.FieldNum++);
}
return '(' + pdfEscape(_T) + ')';
},
set: function (value) {
_T = value.toString();
}
});
/**
* (Optional) The partial field name (see 12.7.3.2, “Field Names”).
*
* @name AcroFormField#fieldName
* @default null
* @type {string}
*/
Object.defineProperty(this, 'fieldName', {
configurable: true,
enumerable: true,
get: function () {
return _T;
},
set: function (value) {
_T = value;
}
});
var _fontName = 'helvetica';
/**
* The fontName of the font to be used.
*
* @name AcroFormField#fontName
* @default 'helvetica'
* @type {string}
*/
Object.defineProperty(this, 'fontName', {
enumerable: true,
configurable: true,
get: function () {
return _fontName;
},
set: function (value) {
_fontName = value;
}
});
var _fontStyle = 'normal';
/**
* The fontStyle of the font to be used.
*
* @name AcroFormField#fontStyle
* @default 'normal'
* @type {string}
*/
Object.defineProperty(this, 'fontStyle', {
enumerable: true,
configurable: true,
get: function () {
return _fontStyle;
},
set: function (value) {
_fontStyle = value;
}
});
var _fontSize = 0;
/**
* The fontSize of the font to be used.
*
* @name AcroFormField#fontSize
* @default 0 (for auto)
* @type {number}
*/
Object.defineProperty(this, 'fontSize', {
enumerable: true,
configurable: true,
get: function () {
return antiScale(_fontSize);
},
set: function (value) {
_fontSize = scale(value);
}
});
var _maxFontSize = 50;
/**
* The maximum fontSize of the font to be used.
*
* @name AcroFormField#maxFontSize
* @default 0 (for auto)
* @type {number}
*/
Object.defineProperty(this, 'maxFontSize', {
enumerable: true,
configurable: true,
get: function () {
return antiScale(_maxFontSize);
},
set: function (value) {
_maxFontSize = scale(value);
}
});
var _color = 'black';
/**
* The color of the text
*
* @name AcroFormField#color
* @default 'black'
* @type {string|rgba}
*/
Object.defineProperty(this, 'color', {
enumerable: true,
configurable: true,
get: function () {
return _color;
},
set: function (value) {
_color = value;
}
});
var _DA = '/F1 0 Tf 0 g';
// Defines the default appearance (Needed for variable Text)
Object.defineProperty(this, 'DA', {
enumerable: true,
configurable: false,
get: function () {
if (!_DA
|| this instanceof AcroFormChildClass
|| this instanceof AcroFormTextField) {
return undefined;
}
return toPdfString(_DA);
},
set: function (value) {
value = value.toString();
_DA = value;
}
});
var _DV = null;
Object.defineProperty(this, 'DV', {
enumerable: false,
configurable: false,
get: function () {
if (!_DV) {
return undefined;
}
if ((this instanceof AcroFormButton === false)) {
return toPdfString(_DV);
}
return _DV;
},
set: function (value) {
value = value.toString();
if ((this instanceof AcroFormButton === false)) {
if (value.substr(0, 1) === '(') {
_DV = pdfUnescape(value.substr(1, value.length - 2));
} else {
_DV = pdfUnescape(value);
}
} else {
_DV = value;
}
}
});
/**
* (Optional; inheritable) The default value to which the field reverts when a reset-form action is executed (see 12.7.5.3, “Reset-Form Action”). The format of this value is the same as that of value.
*
* @name AcroFormField#defaultValue
* @default null
* @type {any}
*/
Object.defineProperty(this, 'defaultValue', {
enumerable: true,
configurable: true,
get: function () {
if ((this instanceof AcroFormButton === true)) {
return pdfUnescape(_DV.substr(1, _DV.length - 1));
} else {
return _DV;
}
},
set: function (value) {
value = value.toString();
if ((this instanceof AcroFormButton === true)) {
_DV = '/' + value;
} else {
_DV = value;
}
}
});
var _V = null;
Object.defineProperty(this, 'V', {
enumerable: false,
configurable: false,
get: function () {
if (this.isUnicode) {
return _V;
}
if (!_V) {
return undefined;
}
if ((this instanceof AcroFormButton === false)) {
return toPdfString(_V);
}
return _V;
},
set: function (value) {
value = value.toString();
if (this.isUnicode) {
_V = toUnicode(value);
this.trueValue = value;
}
else {
if ((this instanceof AcroFormButton === false)) {
if (value.substr(0, 1) === '(') {
_V = pdfUnescape(value.substr(1, value.length - 2));
} else {
_V = pdfUnescape(value);
}
} else {
_V = value;
}
}
}
});
/**
* (Optional; inheritable) The field’s value, whose format varies depending on the field type. See the descriptions of individual field types for further information.
*
* @name AcroFormField#value
* @default null
* @type {any}
*/
Object.defineProperty(this, 'value', {
enumerable: true,
configurable: true,
get: function () {
if ((this instanceof AcroFormButton === true)) {
return pdfUnescape(_V.substr(1, _V.length - 1));
} else {
return _V;
}
},
set: function (value) {
value = value.toString();
if ((this instanceof AcroFormButton === true)) {
_V = '/' + value;
} else {
_V = value;
}
}
});
/**
* Check if field has annotations
*
* @name AcroFormField#hasAnnotation
* @readonly
* @type {boolean}
*/
Object.defineProperty(this, 'hasAnnotation', {
enumerable: true,
configurable: true,
get: function () {
return (this.Rect);
}
});
Object.defineProperty(this, 'Type', {
enumerable: true,
configurable: false,
get: function () {
return (this.hasAnnotation) ? "/Annot" : null;
}
});
Object.defineProperty(this, 'Subtype', {
enumerable: true,
configurable: false,
get: function () {
return (this.hasAnnotation) ? "/Widget" : null;
}
});
var _hasAppearanceStream = false;
/**
* true if field has an appearanceStream
*
* @name AcroFormField#hasAppearanceStream
* @readonly
* @type {boolean}
*/
Object.defineProperty(this, 'hasAppearanceStream', {
enumerable: true,
configurable: true,
writeable: true,
get: function () {
return _hasAppearanceStream;
},
set: function (value) {
value = Boolean(value);
_hasAppearanceStream = value;
}
});
/**
* The page on which the AcroFormField is placed
*
* @name AcroFormField#page
* @type {number}
*/
var _page;
Object.defineProperty(this, 'page', {
enumerable: true,
configurable: true,
writeable: true,
get: function () {
if (!_page) {
return undefined;
}
return _page
},
set: function (value) {
_page = value;
}
});
/**
* If set, the user may not change the value of the field. Any associated widget annotations will not interact with the user; that is, they will not respond to mouse clicks or change their appearance in response to mouse motions. This flag is useful for fields whose values are computed or imported from a database.
*
* @name AcroFormField#readOnly
* @default false
* @type {boolean}
*/
Object.defineProperty(this, 'readOnly', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 1));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 1);
} else {
this.Ff = clearBitForPdf(this.Ff, 1);
}
}
});
/**
* If set, the field shall have a value at the time it is exported by a submitform action (see 12.7.5.2, “Submit-Form Action”).
*
* @name AcroFormField#required
* @default false
* @type {boolean}
*/
Object.defineProperty(this, 'required', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 2));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 2);
} else {
this.Ff = clearBitForPdf(this.Ff, 2);
}
}
});
/**
* If set, the field shall not be exported by a submit-form action (see 12.7.5.2, “Submit-Form Action”)
*
* @name AcroFormField#noExport
* @default false
* @type {boolean}
*/
Object.defineProperty(this, 'noExport', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 3));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 3);
} else {
this.Ff = clearBitForPdf(this.Ff, 3);
}
}
});
var _Q = null;
Object.defineProperty(this, 'Q', {
enumerable: true,
configurable: false,
get: function () {
if (_Q === null) {
return undefined;
}
return _Q;
},
set: function (value) {
if ([0, 1, 2].indexOf(value) !== -1) {
_Q = value;
} else {
throw new Error('Invalid value "' + value + '" for attribute Q supplied.');
}
}
});
/**
* (Optional; inheritable) A code specifying the form of quadding (justification) that shall be used in displaying the text:
* 'left', 'center', 'right'
*
* @name AcroFormField#textAlign
* @default 'left'
* @type {string}
*/
Object.defineProperty(this, 'textAlign', {
get: function () {
var result;
switch (_Q) {
case 0:
default:
result = 'left';
break;
case 1:
result = 'center';
break;
case 2:
result = 'right';
break;
}
return result;
},
configurable: true,
enumerable: true,
set: function (value) {
switch (value) {
case 'right':
case 2:
_Q = 2;
break;
case 'center':
case 1:
_Q = 1;
break;
case 'left':
case 0:
default:
_Q = 0;
}
}
});
};
inherit(AcroFormField, AcroFormPDFObject);
/**
* @class AcroFormChoiceField
* @extends AcroFormField
*/
var AcroFormChoiceField = function () {
AcroFormField.call(this);
// Field Type = Choice Field
this.FT = "/Ch";
// options
this.V = '()';
this.fontName = 'zapfdingbats';
// Top Index
var _TI = 0;
Object.defineProperty(this, 'TI', {
enumerable: true,
configurable: false,
get: function () {
return _TI;
},
set: function (value) {
_TI = value;
}
});
// MK fix for Acrobat
var _MK = '<< /BG [ 0.975 0.975 0.975 ] >>';
Object.defineProperty(this, 'MK', {
enumerable: true,
configurable: false,
get: function () {
return _MK;
},
set: function (value) {
_MK = value;
}
});
/**
* (Optional) For scrollable list boxes, the top index (the index in the Opt array of the first option visible in the list). Default value: 0.
*
* @name AcroFormChoiceField#topIndex
* @default 0
* @type {number}
*/
Object.defineProperty(this, 'topIndex', {
enumerable: true,
configurable: true,
get: function () {
return _TI;
},
set: function (value) {
_TI = value;
}
});
var _Opt = [];
Object.defineProperty(this, 'Opt', {
enumerable: true,
configurable: false,
get: function () {
if (this.isUnicode) {
return arrayToPdfUnicodeArray(_Opt);
}
return arrayToPdfArray(_Opt);
},
set: function (value) {
_Opt = pdfArrayToStringArray(value);
}
});
/**
* @memberof AcroFormChoiceField
* @name getOptions
* @function
* @instance
* @returns {array} array of Options
*/
this.getOptions = function () {
return _Opt;
};
/**
* @memberof AcroFormChoiceField
* @name setOptions
* @function
* @instance
* @param {array} value
*/
this.setOptions = function (value) {
_Opt = value;
if (this.sort) {
_Opt.sort();
}
};
/**
* @memberof AcroFormChoiceField
* @name addOption
* @function
* @instance
* @param {string} value
*/
this.addOption = function (value) {
value = value || "";
value = value.toString();
_Opt.push(value);
if (this.sort) {
_Opt.sort();
}
};
/**
* @memberof AcroFormChoiceField
* @name removeOption
* @function
* @instance
* @param {string} value
* @param {boolean} allEntries (default: false)
*/
this.removeOption = function (value, allEntries) {
allEntries = allEntries || false;
value = value || "";
value = value.toString();
while (_Opt.indexOf(value) !== -1) {
_Opt.splice(_Opt.indexOf(value), 1);
if (allEntries === false) {
break;
}
}
};
/**
* If set, the field is a combo box; if clear, the field is a list box.
*
* @name AcroFormChoiceField#combo
* @default false
* @type {boolean}
*/
Object.defineProperty(this, 'combo', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 18));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 18);
} else {
this.Ff = clearBitForPdf(this.Ff, 18);
}
}
});
/**
* If set, the combo box shall include an editable text box as well as a drop-down list; if clear, it shall include only a drop-down list. This flag shall be used only if the Combo flag is set.
*
* @name AcroFormChoiceField#edit
* @default false
* @type {boolean}
*/
Object.defineProperty(this, 'edit', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 19));
},
set: function (value) {
//PDF 32000-1:2008, page 444
if (this.combo === true) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 19);
} else {
this.Ff = clearBitForPdf(this.Ff, 19);
}
}
}
});
/**
* If set, the field’s option items shall be sorted alphabetically. This flag is intended for use by writers, not by readers. Conforming readers shall display the options in the order in which they occur in the Opt array (see Table 231).
*
* @name AcroFormChoiceField#sort
* @default false
* @type {boolean}
*/
Object.defineProperty(this, 'sort', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 20));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 20);
_Opt.sort();
} else {
this.Ff = clearBitForPdf(this.Ff, 20);
}
}
});
/**
* (PDF 1.4) If set, more than one of the field’s option items may be selected simultaneously; if clear, at most one item shall be selected
*
* @name AcroFormChoiceField#multiSelect
* @default false
* @type {boolean}
*/
Object.defineProperty(this, 'multiSelect', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 22));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 22);
} else {
this.Ff = clearBitForPdf(this.Ff, 22);
}
}
});
/**
* (PDF 1.4) If set, text entered in the field shall not be spellchecked. This flag shall not be used unless the Combo and Edit flags are both set.
*
* @name AcroFormChoiceField#doNotSpellCheck
* @default false
* @type {boolean}
*/
Object.defineProperty(this, 'doNotSpellCheck', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 23));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 23);
} else {
this.Ff = clearBitForPdf(this.Ff, 23);
}
}
});
/**
* (PDF 1.5) If set, the new value shall be committed as soon as a selection is made (commonly with the pointing device). In this case, supplying a value for a field involves three actions: selecting the field for fill-in, selecting a choice for the fill-in value, and leaving that field, which finalizes or “commits” the data choice and triggers any actions associated with the entry or changing of this data. If this flag is on, then processing does not wait for leaving the field action to occur, but immediately proceeds to the third step.
* This option enables applications to perform an action once a selection is made, without requiring the user to exit the field. If clear, the new value is not committed until the user exits the field.
*
* @name AcroFormChoiceField#commitOnSelChange
* @default false
* @type {boolean}
*/
Object.defineProperty(this, 'commitOnSelChange', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 27));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 27);
} else {
this.Ff = clearBitForPdf(this.Ff, 27);
}
}
});
this.hasAppearanceStream = false;
};
inherit(AcroFormChoiceField, AcroFormField);
/**
* @class AcroFormListBox
* @extends AcroFormChoiceField
* @extends AcroFormField
*/
var AcroFormListBox = function () {
AcroFormChoiceField.call(this);
this.fontName = 'helvetica';
//PDF 32000-1:2008, page 444
this.combo = false;
};
inherit(AcroFormListBox, AcroFormChoiceField);
/**
* @class AcroFormComboBox
* @extends AcroFormListBox
* @extends AcroFormChoiceField
* @extends AcroFormField
*/
var AcroFormComboBox = function () {
AcroFormListBox.call(this);
this.combo = true;
};
inherit(AcroFormComboBox, AcroFormListBox);
/**
* @class AcroFormEditBox
* @extends AcroFormComboBox
* @extends AcroFormListBox
* @extends AcroFormChoiceField
* @extends AcroFormField
*/
var AcroFormEditBox = function () {
AcroFormComboBox.call(this);
this.edit = true;
};
inherit(AcroFormEditBox, AcroFormComboBox);
/**
* @class AcroFormButton
* @extends AcroFormField
*/
var AcroFormButton = function () {
AcroFormField.call(this);
this.FT = "/Btn";
/**
* (Radio buttons only) If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect. If clear, clicking the selected button deselects it, leaving no button selected.
*
* @name AcroFormButton#noToggleToOff
* @type {boolean}
*/
Object.defineProperty(this, 'noToggleToOff', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 15));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 15);
} else {
this.Ff = clearBitForPdf(this.Ff, 15);
}
}
});
/**
* If set, the field is a set of radio buttons; if clear, the field is a checkbox. This flag may be set only if the Pushbutton flag is clear.
*
* @name AcroFormButton#radio
* @type {boolean}
*/
Object.defineProperty(this, 'radio', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 16));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 16);
} else {
this.Ff = clearBitForPdf(this.Ff, 16);
}
}
});
/**
* If set, the field is a pushbutton that does not retain a permanent value.
*
* @name AcroFormButton#pushButton
* @type {boolean}
*/
Object.defineProperty(this, 'pushButton', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 17));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 17);
} else {
this.Ff = clearBitForPdf(this.Ff, 17);
}
}
});
/**
* (PDF 1.5) If set, a group of radio buttons within a radio button field that use the same value for the on state will turn on and off in unison; that is if one is checked, they are all checked. If clear, the buttons are mutually exclusive (the same behavior as HTML radio buttons).
*
* @name AcroFormButton#radioIsUnison
* @type {boolean}
*/
Object.defineProperty(this, 'radioIsUnison', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 26));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 26);
} else {
this.Ff = clearBitForPdf(this.Ff, 26);
}
}
});
var _MK = {};
Object.defineProperty(this, 'MK', {
enumerable: false,
configurable: false,
get: function () {
if (Object.keys(_MK).length !== 0) {
var result = [];
result.push('<<');
var key;
for (key in _MK) {
result.push('/' + key + ' (' + _MK[key] + ')');
}
result.push('>>');
return result.join('\n');
}
return undefined;
},
set: function (value) {
if (typeof value === "object") {
_MK = value;
}
}
});
/**
* From the PDF reference:
* (Optional, button fields only) The widget annotation's normal caption which shall be displayed when it is not interacting with the user.
* Unlike the remaining entries listed in this Table which apply only to widget annotations associated with pushbutton fields (see Pushbuttons in 12.7.4.2, "Button Fields"), the CA entry may be used with any type of button field, including check boxes (see Check Boxes in 12.7.4.2, "Button Fields") and radio buttons (Radio Buttons in 12.7.4.2, "Button Fields").
*
* - '8' = Cross,
* - 'l' = Circle,
* - '' = nothing
* @name AcroFormButton#caption
* @type {string}
*/
Object.defineProperty(this, 'caption', {
enumerable: true,
configurable: true,
get: function () {
return _MK.CA || '';
},
set: function (value) {
if (typeof value === "string") {
_MK.CA = value;
}
}
});
var _AS;
Object.defineProperty(this, 'AS', {
enumerable: false,
configurable: false,
get: function () {
return _AS;
},
set: function (value) {
_AS = value;
}
});
/**
* (Required if the appearance dictionary AP contains one or more subdictionaries; PDF 1.2) The annotation's appearance state, which selects the applicable appearance stream from an appearance subdictionary (see Section 12.5.5, "Appearance Streams")
*
* @name AcroFormButton#appearanceState
* @type {any}
*/
Object.defineProperty(this, 'appearanceState', {
enumerable: true,
configurable: true,
get: function () {
return _AS.substr(1, _AS.length - 1);
},
set: function (value) {
_AS = '/' + value;
}
});
};
inherit(AcroFormButton, AcroFormField);
/**
* @class AcroFormPushButton
* @extends AcroFormButton
* @extends AcroFormField
*/
var AcroFormPushButton = function () {
AcroFormButton.call(this);
this.pushButton = true;
};
inherit(AcroFormPushButton, AcroFormButton);
/**
* @class AcroFormRadioButton
* @extends AcroFormButton
* @extends AcroFormField
*/
var AcroFormRadioButton = function () {
AcroFormButton.call(this);
this.radio = true;
this.pushButton = false;
var _Kids = [];
Object.defineProperty(this, 'Kids', {
enumerable: true,
configurable: false,
get: function () {
return _Kids;
},
set: function (value) {
if (typeof value !== "undefined") {
_Kids = value;
} else {
_Kids = [];
}
}
});
};
inherit(AcroFormRadioButton, AcroFormButton);
/**
* The Child class of a RadioButton (the radioGroup) -> The single Buttons
*
* @class AcroFormChildClass
* @extends AcroFormField
* @ignore
*/
var AcroFormChildClass = function () {
AcroFormField.call(this);
var _parent;
Object.defineProperty(this, 'Parent', {
enumerable: false,
configurable: false,
get: function () {
return _parent;
},
set: function (value) {
_parent = value;
}
});
var _optionName;
Object.defineProperty(this, 'optionName', {
enumerable: false,
configurable: true,
get: function () {
return _optionName;
},
set: function (value) {
_optionName = value;
}
});
var _MK = {};
Object.defineProperty(this, 'MK', {
enumerable: false,
configurable: false,
get: function () {
var result = [];
result.push('<<');
var key;
for (key in _MK) {
result.push('/' + key + ' (' + _MK[key] + ')');
}
result.push('>>');
return result.join('\n');
},
set: function (value) {
if (typeof value === "object") {
_MK = value;
}
}
});
/**
* From the PDF reference:
* (Optional, button fields only) The widget annotation's normal caption which shall be displayed when it is not interacting with the user.
* Unlike the remaining entries listed in this Table which apply only to widget annotations associated with pushbutton fields (see Pushbuttons in 12.7.4.2, "Button Fields"), the CA entry may be used with any type of button field, including check boxes (see Check Boxes in 12.7.4.2, "Button Fields") and radio buttons (Radio Buttons in 12.7.4.2, "Button Fields").
*
* - '8' = Cross,
* - 'l' = Circle,
* - '' = nothing
* @name AcroFormButton#caption
* @type {string}
*/
Object.defineProperty(this, 'caption', {
enumerable: true,
configurable: true,
get: function () {
return _MK.CA || '';
},
set: function (value) {
if (typeof value === "string") {
_MK.CA = value;
}
}
});
var _AS;
Object.defineProperty(this, 'AS', {
enumerable: false,
configurable: false,
get: function () {
return _AS;
},
set: function (value) {
_AS = value;
}
});
/**
* (Required if the appearance dictionary AP contains one or more subdictionaries; PDF 1.2) The annotation's appearance state, which selects the applicable appearance stream from an appearance subdictionary (see Section 12.5.5, "Appearance Streams")
*
* @name AcroFormButton#appearanceState
* @type {any}
*/
Object.defineProperty(this, 'appearanceState', {
enumerable: true,
configurable: true,
get: function () {
return _AS.substr(1, _AS.length - 1);
},
set: function (value) {
_AS = '/' + value;
}
});
this.caption = 'l';
this.appearanceState = 'Off';
// todo: set AppearanceType as variable that can be set from the
// outside...
this._AppearanceType = AcroFormAppearance.RadioButton.Circle;
// The Default appearanceType is the Circle
this.appearanceStreamContent = this._AppearanceType.createAppearanceStream(this.optionName);
};
inherit(AcroFormChildClass, AcroFormField);
AcroFormRadioButton.prototype.setAppearance = function (appearance) {
if (!('createAppearanceStream' in appearance && 'getCA' in appearance)) {
throw new Error("Couldn't assign Appearance to RadioButton. Appearance was Invalid!");
}
for (var objId in this.Kids) {
if (this.Kids.hasOwnProperty(objId)) {
var child = this.Kids[objId];
child.appearanceStreamContent = appearance.createAppearanceStream(child.optionName);
child.caption = appearance.getCA();
}
}
};
AcroFormRadioButton.prototype.createOption = function (name) {
// Create new Child for RadioGroup
var child = new AcroFormChildClass();
child.Parent = this;
child.optionName = name;
// Add to Parent
this.Kids.push(child);
addField.call(this, child);
return child;
};
/**
* @class AcroFormCheckBox
* @extends AcroFormButton
* @extends AcroFormField
*/
var AcroFormCheckBox = function () {
AcroFormButton.call(this);
this.fontName = 'zapfdingbats';
this.caption = '3';
this.appearanceState = 'On';
this.value = "On";
this.textAlign = 'center';
this.appearanceStreamContent = AcroFormAppearance.CheckBox.createAppearanceStream();
};
inherit(AcroFormCheckBox, AcroFormButton);
/**
* @class AcroFormTextField
* @extends AcroFormField
*/
var AcroFormTextField = function () {
AcroFormField.call(this);
this.FT = '/Tx';
/**
* If set, the field may contain multiple lines of text; if clear, the field’s text shall be restricted to a single line.
*
* @name AcroFormTextField#multiline
* @type {boolean}
*/
Object.defineProperty(this, 'multiline', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 13));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 13);
} else {
this.Ff = clearBitForPdf(this.Ff, 13);
}
}
});
/**
* (PDF 1.4) If set, the text entered in the field represents the pathname of a file whose contents shall be submitted as the value of the field.
*
* @name AcroFormTextField#fileSelect
* @type {boolean}
*/
Object.defineProperty(this, 'fileSelect', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 21));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 21);
} else {
this.Ff = clearBitForPdf(this.Ff, 21);
}
}
});
/**
* (PDF 1.4) If set, text entered in the field shall not be spell-checked.
*
* @name AcroFormTextField#doNotSpellCheck
* @type {boolean}
*/
Object.defineProperty(this, 'doNotSpellCheck', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 23));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 23);
} else {
this.Ff = clearBitForPdf(this.Ff, 23);
}
}
});
/**
* (PDF 1.4) If set, the field shall not scroll (horizontally for single-line fields, vertically for multiple-line fields) to accommodate more text than fits within its annotation rectangle. Once the field is full, no further text shall be accepted for interactive form filling; for noninteractive form filling, the filler should take care not to add more character than will visibly fit in the defined area.
*
* @name AcroFormTextField#doNotScroll
* @type {boolean}
*/
Object.defineProperty(this, 'doNotScroll', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 24));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 24);
} else {
this.Ff = clearBitForPdf(this.Ff, 24);
}
}
});
/**
* (PDF 1.5) May be set only if the MaxLen entry is present in the text field dictionary (see Table 229) and if the Multiline, Password, and FileSelect flags are clear. If set, the field shall be automatically divided into as many equally spaced positions, or combs, as the value of MaxLen, and the text is laid out into those combs.
*
* @name AcroFormTextField#comb
* @type {boolean}
*/
Object.defineProperty(this, 'comb', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 25));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 25);
} else {
this.Ff = clearBitForPdf(this.Ff, 25);
}
}
});
/**
* (PDF 1.5) If set, the value of this field shall be a rich text string (see 12.7.3.4, “Rich Text Strings”). If the field has a value, the RV entry of the field dictionary (Table 222) shall specify the rich text string.
*
* @name AcroFormTextField#richText
* @type {boolean}
*/
Object.defineProperty(this, 'richText', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 26));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 26);
} else {
this.Ff = clearBitForPdf(this.Ff, 26);
}
}
});
var _MaxLen = null;
Object.defineProperty(this, 'MaxLen', {
enumerable: true,
configurable: false,
get: function () {
return _MaxLen;
},
set: function (value) {
_MaxLen = value;
}
});
/**
* (Optional; inheritable) The maximum length of the field’s text, in characters.
*
* @name AcroFormTextField#maxLength
* @type {number}
*/
Object.defineProperty(this, 'maxLength', {
enumerable: true,
configurable: true,
get: function () {
return _MaxLen;
},
set: function (value) {
if (Number.isInteger(value)) {
_MaxLen = value;
}
}
});
Object.defineProperty(this, 'hasAppearanceStream', {
enumerable: true,
configurable: true,
get: function () {
return (this.V || this.DV);
}
});
};
inherit(AcroFormTextField, AcroFormField);
/**
* @class AcroFormPasswordField
* @extends AcroFormTextField
* @extends AcroFormField
*/
var AcroFormPasswordField = function () {
AcroFormTextField.call(this);
/**
* If set, the field is intended for entering a secure password that should not be echoed visibly to the screen. Characters typed from the keyboard shall instead be echoed in some unreadable form, such as asterisks or bullet characters.
* NOTE To protect password confidentiality, readers should never store the value of the text field in the PDF file if this flag is set.
*
* @name AcroFormTextField#password
* @type {boolean}
*/
Object.defineProperty(this, 'password', {
enumerable: true,
configurable: true,
get: function () {
return Boolean(getBitForPdf(this.Ff, 14));
},
set: function (value) {
if (Boolean(value) === true) {
this.Ff = setBitForPdf(this.Ff, 14);
} else {
this.Ff = clearBitForPdf(this.Ff, 14);
}
}
});
this.password = true;
};
inherit(AcroFormPasswordField, AcroFormTextField);
// Contains Methods for creating standard appearances
var AcroFormAppearance = {
CheckBox: {
createAppearanceStream: function () {
var appearance = {
N: {
On: AcroFormAppearance.CheckBox.YesNormal
},
D: {
On: AcroFormAppearance.CheckBox.YesPushDown,
Off: AcroFormAppearance.CheckBox.OffPushDown
}
};
return appearance;
},
/**
* Returns the standard On Appearance for a CheckBox
*
* @returns {AcroFormXObject}
*/
YesPushDown: function (formObject) {
var xobj = new createFormXObject(formObject);
var stream = [];
var fontKey = scope.internal.getFont(formObject.fontName, formObject.fontStyle).id;
var encodedColor = scope.__private__.encodeColorString(formObject.color);
var calcRes = calculateX(formObject, formObject.caption);
stream.push("0.749023 g");
stream.push("0 0 " + f2(AcroFormAppearance.internal.getWidth(formObject)) + " " + f2(AcroFormAppearance.internal.getHeight(formObject)) + " re");
stream.push("f");
stream.push("BMC");
stream.push("q");
stream.push("0 0 1 rg");
stream.push("/" + fontKey + " " + f2(calcRes.fontSize) + " Tf " + encodedColor);
stream.push("BT");
stream.push(calcRes.text);
stream.push("ET");
stream.push("Q");
stream.push("EMC");
xobj.stream = stream.join("\n");
return xobj;
},
YesNormal: function (formObject) {
var xobj = new createFormXObject(formObject);
var fontKey = scope.internal.getFont(formObject.fontName, formObject.fontStyle).id;
var encodedColor = scope.__private__.encodeColorString(formObject.color);
var stream = [];
var height = AcroFormAppearance.internal.getHeight(formObject);
var width = AcroFormAppearance.internal.getWidth(formObject);
var calcRes = calculateX(formObject, formObject.caption);
stream.push("1 g");
stream.push("0 0 " + f2(width) + " " + f2(height) + " re");
stream.push("f");
stream.push("q");
stream.push("0 0 1 rg");
stream.push("0 0 " + f2(width - 1) + " " + f2(height - 1) + " re");
stream.push("W");
stream.push("n");
stream.push("0 g");
stream.push("BT");
stream.push("/" + fontKey + " " + f2(calcRes.fontSize) + " Tf " + encodedColor);
stream.push(calcRes.text);
stream.push("ET");
stream.push("Q");
xobj.stream = stream.join("\n");
return xobj;
},
/**
* Returns the standard Off Appearance for a CheckBox
*
* @returns {AcroFormXObject}
*/
OffPushDown: function (formObject) {
var xobj = new createFormXObject(formObject);
var stream = [];
stream.push("0.749023 g");
stream.push("0 0 " + f2(AcroFormAppearance.internal.getWidth(formObject)) + " " + f2(AcroFormAppearance.internal.getHeight(formObject)) + " re");
stream.push("f");
xobj.stream = stream.join("\n");
return xobj;
}
},
RadioButton: {
Circle: {
createAppearanceStream: function (name) {
var appearanceStreamContent = {
D: {
'Off': AcroFormAppearance.RadioButton.Circle.OffPushDown
},
N: {}
};
appearanceStreamContent.N[name] = AcroFormAppearance.RadioButton.Circle.YesNormal;
appearanceStreamContent.D[name] = AcroFormAppearance.RadioButton.Circle.YesPushDown;
return appearanceStreamContent;
},
getCA: function () {
return 'l';
},
YesNormal: function (formObject) {
var xobj = new createFormXObject(formObject);
var stream = [];
// Make the Radius of the Circle relative to min(height, width) of formObject
var DotRadius = (AcroFormAppearance.internal.getWidth(formObject) <= AcroFormAppearance.internal.getHeight(formObject)) ? AcroFormAppearance.internal.getWidth(formObject) / 4 : AcroFormAppearance.internal.getHeight(formObject) / 4;
// The Borderpadding...
DotRadius = Number((DotRadius * 0.9).toFixed(5));
var c = AcroFormAppearance.internal.Bezier_C;
var DotRadiusBezier = Number((DotRadius * c).toFixed(5));
/*
* The Following is a Circle created with Bezier-Curves.
*/
stream.push("q");
stream.push("1 0 0 1 " + f5(AcroFormAppearance.internal.getWidth(formObject) / 2) + " " + f5(AcroFormAppearance.internal.getHeight(formObject) / 2) + " cm");
stream.push(DotRadius + " 0 m");
stream.push(DotRadius + " " + DotRadiusBezier + " " + DotRadiusBezier + " " + DotRadius + " 0 " + DotRadius + " c");
stream.push("-" + DotRadiusBezier + " " + DotRadius + " -" + DotRadius + " " + DotRadiusBezier + " -" + DotRadius + " 0 c");
stream.push("-" + DotRadius + " -" + DotRadiusBezier + " -" + DotRadiusBezier + " -" + DotRadius + " 0 -" + DotRadius + " c");
stream.push(DotRadiusBezier + " -" + DotRadius + " " + DotRadius + " -" + DotRadiusBezier + " " + DotRadius + " 0 c");
stream.push("f");
stream.push("Q");
xobj.stream = stream.join("\n");
return xobj;
},
YesPushDown: function (formObject) {
var xobj = new createFormXObject(formObject);
var stream = [];
var DotRadius = (AcroFormAppearance.internal.getWidth(formObject) <= AcroFormAppearance.internal.getHeight(formObject)) ?
AcroFormAppearance.internal.getWidth(formObject) / 4 : AcroFormAppearance.internal.getHeight(formObject) / 4;
// The Borderpadding...
var DotRadius = Number((DotRadius * 0.9).toFixed(5));
// Save results for later use; no need to waste
// processor ticks on doing math
var k = Number((DotRadius * 2).toFixed(5));
var kc = Number((k * AcroFormAppearance.internal.Bezier_C).toFixed(5));
var dc = Number((DotRadius * AcroFormAppearance.internal.Bezier_C).toFixed(5));
stream.push("0.749023 g");
stream.push("q");
stream.push("1 0 0 1 " + f5(AcroFormAppearance.internal.getWidth(formObject) / 2) + " " + f5(AcroFormAppearance.internal.getHeight(formObject) / 2) + " cm");
stream.push(k + " 0 m");
stream.push(k + " " + kc + " " + kc + " " + k + " 0 " + k + " c");
stream.push("-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c");
stream.push("-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c");
stream.push(kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c");
stream.push("f");
stream.push("Q");
stream.push("0 g");
stream.push("q");
stream.push("1 0 0 1 " + f5(AcroFormAppearance.internal.getWidth(formObject) / 2) + " " + f5(AcroFormAppearance.internal.getHeight(formObject) / 2) + " cm");
stream.push(DotRadius + " 0 m");
stream.push("" + DotRadius + " " + dc + " " + dc + " " + DotRadius + " 0 " + DotRadius + " c");
stream.push("-" + dc + " " + DotRadius + " -" + DotRadius + " " + dc + " -" + DotRadius + " 0 c");
stream.push("-" + DotRadius + " -" + dc + " -" + dc + " -" + DotRadius + " 0 -" + DotRadius + " c");
stream.push(dc + " -" + DotRadius + " " + DotRadius + " -" + dc + " " + DotRadius + " 0 c");
stream.push("f");
stream.push("Q");
xobj.stream = stream.join("\n");
return xobj;
},
OffPushDown: function (formObject) {
var xobj = new createFormXObject(formObject);
var stream = [];
var DotRadius = (AcroFormAppearance.internal.getWidth(formObject) <= AcroFormAppearance.internal.getHeight(formObject)) ?
AcroFormAppearance.internal.getWidth(formObject) / 4 : AcroFormAppearance.internal.getHeight(formObject) / 4;
// The Borderpadding...
DotRadius = Number((DotRadius * 0.9).toFixed(5));
// Save results for later use; no need to waste
// processor ticks on doing math
var k = Number((DotRadius * 2).toFixed(5));
var kc = Number((k * AcroFormAppearance.internal.Bezier_C).toFixed(5));
stream.push("0.749023 g");
stream.push("q");
stream.push("1 0 0 1 " + f5(AcroFormAppearance.internal.getWidth(formObject) / 2) + " " + f5(AcroFormAppearance.internal.getHeight(formObject) / 2) + " cm");
stream.push(k + " 0 m");
stream.push(k + " " + kc + " " + kc + " " + k + " 0 " + k + " c");
stream.push("-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c");
stream.push("-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c");
stream.push(kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c");
stream.push("f");
stream.push("Q");
xobj.stream = stream.join("\n");
return xobj;
},
},
Cross: {
/**
* Creates the Actual AppearanceDictionary-References
*
* @param {string} name
* @returns {Object}
* @ignore
*/
createAppearanceStream: function (name) {
var appearanceStreamContent = {
D: {
'Off': AcroFormAppearance.RadioButton.Cross.OffPushDown
},
N: {}
};
appearanceStreamContent.N[name] = AcroFormAppearance.RadioButton.Cross.YesNormal;
appearanceStreamContent.D[name] = AcroFormAppearance.RadioButton.Cross.YesPushDown;
return appearanceStreamContent;
},
getCA: function () {
return '8'
},
YesNormal: function (formObject) {
var xobj = new createFormXObject(formObject);
var stream = [];
var cross = AcroFormAppearance.internal.calculateCross(formObject);
stream.push("q");
stream.push("1 1 " + f2(AcroFormAppearance.internal.getWidth(formObject) - 2) + " " + f2(AcroFormAppearance.internal.getHeight(formObject) - 2) + " re");
stream.push("W");
stream.push("n");
stream.push(f2(cross.x1.x) + " " + f2(cross.x1.y) + " m");
stream.push(f2(cross.x2.x) + " " + f2(cross.x2.y) + " l");
stream.push(f2(cross.x4.x) + " " + f2(cross.x4.y) + " m");
stream.push(f2(cross.x3.x) + " " + f2(cross.x3.y) + " l");
stream.push("s");
stream.push("Q");
xobj.stream = stream.join("\n");
return xobj;
},
YesPushDown: function (formObject) {
var xobj = new createFormXObject(formObject);
var cross = AcroFormAppearance.internal.calculateCross(formObject);
var stream = [];
stream.push("0.749023 g");
stream.push("0 0 " + f2(AcroFormAppearance.internal.getWidth(formObject)) + " " + f2(AcroFormAppearance.internal.getHeight(formObject)) + " re");
stream.push("f");
stream.push("q");
stream.push("1 1 " + f2(AcroFormAppearance.internal.getWidth(formObject) - 2) + " " + f2(AcroFormAppearance.internal.getHeight(formObject) - 2) + " re");
stream.push("W");
stream.push("n");
stream.push(f2(cross.x1.x) + " " + f2(cross.x1.y) + " m");
stream.push(f2(cross.x2.x) + " " + f2(cross.x2.y) + " l");
stream.push(f2(cross.x4.x) + " " + f2(cross.x4.y) + " m");
stream.push(f2(cross.x3.x) + " " + f2(cross.x3.y) + " l");
stream.push("s");
stream.push("Q");
xobj.stream = stream.join("\n");
return xobj;
},
OffPushDown: function (formObject) {
var xobj = new createFormXObject(formObject);
var stream = [];
stream.push("0.749023 g");
stream.push("0 0 " + f2(AcroFormAppearance.internal.getWidth(formObject)) + " " + f2(AcroFormAppearance.internal.getHeight(formObject)) + " re");
stream.push("f");
xobj.stream = stream.join("\n");
return xobj;
}
},
},
/**
* Returns the standard Appearance
*
* @returns {AcroFormXObject}
*/
createDefaultAppearanceStream: function (formObject) {
// Set Helvetica to Standard Font (size: auto)
// Color: Black
var fontKey = scope.internal.getFont(formObject.fontName, formObject.fontStyle).id;
var encodedColor = scope.__private__.encodeColorString(formObject.color);
var fontSize = formObject.fontSize;
var result = '/' + fontKey + ' ' + fontSize + ' Tf ' + encodedColor;
return result;
}
};
AcroFormAppearance.internal = {
Bezier_C: 0.551915024494,
calculateCross: function (formObject) {
var width = AcroFormAppearance.internal.getWidth(formObject);
var height = AcroFormAppearance.internal.getHeight(formObject);
var a = Math.min(width, height);
var cross = {
x1: { // upperLeft
x: (width - a) / 2,
y: ((height - a) / 2) + a,// height - borderPadding
},
x2: { // lowerRight
x: ((width - a) / 2) + a,
y: ((height - a) / 2)// borderPadding
},
x3: { // lowerLeft
x: (width - a) / 2,
y: ((height - a) / 2)// borderPadding
},
x4: { // upperRight
x: ((width - a) / 2) + a,
y: ((height - a) / 2) + a,// height - borderPadding
}
};
return cross;
},
};
AcroFormAppearance.internal.getWidth = function (formObject) {
var result = 0;
if (typeof formObject === "object") {
result = scale(formObject.Rect[2]);
}
return result;
};
AcroFormAppearance.internal.getHeight = function (formObject) {
var result = 0;
if (typeof formObject === "object") {
result = scale(formObject.Rect[3]);
}
return result;
};
// Public:
/**
* Add an AcroForm-Field to the jsPDF-instance
*
* @name addField
* @function
* @instance
* @param {Object} fieldObject
* @returns {jsPDF}
*/
var addField = jsPDFAPI.addField = function (fieldObject) {
initializeAcroForm.call(this);
if (fieldObject instanceof AcroFormField) {
putForm.call(this, fieldObject);
} else {
throw new Error('Invalid argument passed to jsPDF.addField.');
}
fieldObject.page = scope.internal.getCurrentPageInfo().pageNumber;
return this;
};
/**
* @name addButton
* @function
* @instance
* @param {AcroFormButton} options
* @returns {jsPDF}
* @deprecated
*/
jsPDFAPI.addButton = function (button) {
if (button instanceof AcroFormButton === false) {
throw new Error('Invalid argument passed to jsPDF.addButton.');
}
return addField.call(this, button);
};
/**
* @name addTextField
* @function
* @instance
* @param {AcroFormTextField} textField
* @returns {jsPDF}
* @deprecated
*/
jsPDFAPI.addTextField = function (textField) {
if (textField instanceof AcroFormTextField === false) {
throw new Error('Invalid argument passed to jsPDF.addTextField.');
}
return addField.call(this, textField);
};
/**
* @name addChoiceField
* @function
* @instance
* @param {AcroFormChoiceField}
* @returns {jsPDF}
* @deprecated
*/
jsPDFAPI.addChoiceField = function (choiceField) {
if (choiceField instanceof AcroFormChoiceField === false) {
throw new Error('Invalid argument passed to jsPDF.addChoiceField.');
}
return addField.call(this, choiceField);
};
if (typeof globalObj == "object" &&
typeof (globalObj["ChoiceField"]) === "undefined" &&
typeof (globalObj["ListBox"]) === "undefined" &&
typeof (globalObj["ComboBox"]) === "undefined" &&
typeof (globalObj["EditBox"]) === "undefined" &&
typeof (globalObj["Button"]) === "undefined" &&
typeof (globalObj["PushButton"]) === "undefined" &&
typeof (globalObj["RadioButton"]) === "undefined" &&
typeof (globalObj["CheckBox"]) === "undefined" &&
typeof (globalObj["TextField"]) === "undefined" &&
typeof (globalObj["PasswordField"]) === "undefined"
) {
globalObj["ChoiceField"] = AcroFormChoiceField;
globalObj["ListBox"] = AcroFormListBox;
globalObj["ComboBox"] = AcroFormComboBox;
globalObj["EditBox"] = AcroFormEditBox;
globalObj["Button"] = AcroFormButton;
globalObj["PushButton"] = AcroFormPushButton;
globalObj["RadioButton"] = AcroFormRadioButton;
globalObj["CheckBox"] = AcroFormCheckBox;
globalObj["TextField"] = AcroFormTextField;
globalObj["PasswordField"] = AcroFormPasswordField;
// backwardsCompatibility
globalObj["AcroForm"] = { Appearance: AcroFormAppearance };
}
jsPDFAPI.AcroFormChoiceField = AcroFormChoiceField;
jsPDFAPI.AcroFormListBox = AcroFormListBox;
jsPDFAPI.AcroFormComboBox = AcroFormComboBox;
jsPDFAPI.AcroFormEditBox = AcroFormEditBox;
jsPDFAPI.AcroFormButton = AcroFormButton;
jsPDFAPI.AcroFormPushButton = AcroFormPushButton;
jsPDFAPI.AcroFormRadioButton = AcroFormRadioButton;
jsPDFAPI.AcroFormCheckBox = AcroFormCheckBox;
jsPDFAPI.AcroFormTextField = AcroFormTextField;
jsPDFAPI.AcroFormPasswordField = AcroFormPasswordField;
jsPDFAPI.AcroFormAppearance = AcroFormAppearance;
jsPDFAPI.AcroForm = {
ChoiceField: AcroFormChoiceField,
ListBox: AcroFormListBox,
ComboBox: AcroFormComboBox,
EditBox: AcroFormEditBox,
Button: AcroFormButton,
PushButton: AcroFormPushButton,
RadioButton: AcroFormRadioButton,
CheckBox: AcroFormCheckBox,
TextField: AcroFormTextField,
PasswordField: AcroFormPasswordField,
Appearance: AcroFormAppearance
};
jsPDF.AcroForm = {
ChoiceField: AcroFormChoiceField,
ListBox: AcroFormListBox,
ComboBox: AcroFormComboBox,
EditBox: AcroFormEditBox,
Button: AcroFormButton,
PushButton: AcroFormPushButton,
RadioButton: AcroFormRadioButton,
CheckBox: AcroFormCheckBox,
TextField: AcroFormTextField,
PasswordField: AcroFormPasswordField,
Appearance: AcroFormAppearance
};
})(jsPDF, (typeof window !== 'undefined' && window || typeof global !== 'undefined' && global));
/**
* jsPDF fromHTML plugin. BETA stage. API subject to change. Needs browser
* Copyright (c) 2012 Willow Systems Corporation, willow-systems.com
* 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
* 2014 Diego Casorran, https://github.com/diegocr
* 2014 Daniel Husar, https://github.com/danielhusar
* 2014 Wolfgang Gassler, https://github.com/woolfg
* 2014 Steven Spungin, https://github.com/flamenco
*
* @license
*
* ====================================================================
*/
(function (jsPDFAPI) {
var clone, _DrillForContent, FontNameDB, FontStyleMap, TextAlignMap, FontWeightMap, FloatMap, ClearMap, GetCSS, PurgeWhiteSpace, Renderer, ResolveFont, ResolveUnitedNumber, UnitedNumberMap, elementHandledElsewhere, images, loadImgs, checkForFooter, process, tableToJson;
clone = function () {
return function (obj) {
Clone.prototype = obj;
return new Clone();
};
function Clone() {}
}();
PurgeWhiteSpace = function PurgeWhiteSpace(array) {
var fragment, i, l, lTrimmed, r, rTrimmed, trailingSpace;
i = 0;
l = array.length;
fragment = void 0;
lTrimmed = false;
rTrimmed = false;
while (!lTrimmed && i !== l) {
fragment = array[i] = array[i].trimLeft();
if (fragment) {
lTrimmed = true;
}
i++;
}
i = l - 1;
while (l && !rTrimmed && i !== -1) {
fragment = array[i] = array[i].trimRight();
if (fragment) {
rTrimmed = true;
}
i--;
}
r = /\s+$/g;
trailingSpace = true;
i = 0;
while (i !== l) {
// Leave the line breaks intact
if (array[i] != "\u2028") {
fragment = array[i].replace(/\s+/g, " ");
if (trailingSpace) {
fragment = fragment.trimLeft();
}
if (fragment) {
trailingSpace = r.test(fragment);
}
array[i] = fragment;
}
i++;
}
return array;
};
Renderer = function Renderer(pdf, x, y, settings) {
this.pdf = pdf;
this.x = x;
this.y = y;
this.settings = settings; //list of functions which are called after each element-rendering process
this.watchFunctions = [];
this.init();
return this;
};
ResolveFont = function ResolveFont(css_font_family_string) {
var name, part, parts;
name = void 0;
parts = css_font_family_string.split(",");
part = parts.shift();
while (!name && part) {
name = FontNameDB[part.trim().toLowerCase()];
part = parts.shift();
}
return name;
};
ResolveUnitedNumber = function ResolveUnitedNumber(css_line_height_string) {
//IE8 issues
css_line_height_string = css_line_height_string === "auto" ? "0px" : css_line_height_string;
if (css_line_height_string.indexOf("em") > -1 && !isNaN(Number(css_line_height_string.replace("em", "")))) {
css_line_height_string = Number(css_line_height_string.replace("em", "")) * 18.719 + "px";
}
if (css_line_height_string.indexOf("pt") > -1 && !isNaN(Number(css_line_height_string.replace("pt", "")))) {
css_line_height_string = Number(css_line_height_string.replace("pt", "")) * 1.333 + "px";
}
var normal, undef, value;
undef = void 0;
normal = 16.00;
value = UnitedNumberMap[css_line_height_string];
if (value) {
return value;
}
value = {
"xx-small": 9,
"x-small": 11,
small: 13,
medium: 16,
large: 19,
"x-large": 23,
"xx-large": 28,
auto: 0
}[css_line_height_string];
if (value !== undef) {
return UnitedNumberMap[css_line_height_string] = value / normal;
}
if (value = parseFloat(css_line_height_string)) {
return UnitedNumberMap[css_line_height_string] = value / normal;
}
value = css_line_height_string.match(/([\d\.]+)(px)/);
if (Array.isArray(value) && value.length === 3) {
return UnitedNumberMap[css_line_height_string] = parseFloat(value[1]) / normal;
}
return UnitedNumberMap[css_line_height_string] = 1;
};
GetCSS = function GetCSS(element) {
var css, tmp, computedCSSElement;
computedCSSElement = function (el) {
var compCSS;
compCSS = function (el) {
if (document.defaultView && document.defaultView.getComputedStyle) {
return document.defaultView.getComputedStyle(el, null);
} else if (el.currentStyle) {
return el.currentStyle;
} else {
return el.style;
}
}(el);
return function (prop) {
prop = prop.replace(/-\D/g, function (match) {
return match.charAt(1).toUpperCase();
});
return compCSS[prop];
};
}(element);
css = {};
tmp = void 0;
css["font-family"] = ResolveFont(computedCSSElement("font-family")) || "times";
css["font-style"] = FontStyleMap[computedCSSElement("font-style")] || "normal";
css["text-align"] = TextAlignMap[computedCSSElement("text-align")] || "left";
tmp = FontWeightMap[computedCSSElement("font-weight")] || "normal";
if (tmp === "bold") {
if (css["font-style"] === "normal") {
css["font-style"] = tmp;
} else {
css["font-style"] = tmp + css["font-style"];
}
}
css["font-size"] = ResolveUnitedNumber(computedCSSElement("font-size")) || 1;
css["line-height"] = ResolveUnitedNumber(computedCSSElement("line-height")) || 1;
css["display"] = computedCSSElement("display") === "inline" ? "inline" : "block";
tmp = css["display"] === "block";
css["margin-top"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-top")) || 0;
css["margin-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-bottom")) || 0;
css["padding-top"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-top")) || 0;
css["padding-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-bottom")) || 0;
css["margin-left"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-left")) || 0;
css["margin-right"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-right")) || 0;
css["padding-left"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-left")) || 0;
css["padding-right"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-right")) || 0;
css["page-break-before"] = computedCSSElement("page-break-before") || "auto"; //float and clearing of floats
css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none";
css["clear"] = ClearMap[computedCSSElement("clear")] || "none";
css["color"] = computedCSSElement("color");
return css;
};
elementHandledElsewhere = function elementHandledElsewhere(element, renderer, elementHandlers) {
var handlers, i, isHandledElsewhere, l, classNames;
isHandledElsewhere = false;
i = void 0;
l = void 0;
handlers = elementHandlers["#" + element.id];
if (handlers) {
if (typeof handlers === "function") {
isHandledElsewhere = handlers(element, renderer);
} else {
i = 0;
l = handlers.length;
while (!isHandledElsewhere && i !== l) {
isHandledElsewhere = handlers[i](element, renderer);
i++;
}
}
}
handlers = elementHandlers[element.nodeName];
if (!isHandledElsewhere && handlers) {
if (typeof handlers === "function") {
isHandledElsewhere = handlers(element, renderer);
} else {
i = 0;
l = handlers.length;
while (!isHandledElsewhere && i !== l) {
isHandledElsewhere = handlers[i](element, renderer);
i++;
}
}
} // Try class names
classNames = typeof element.className === 'string' ? element.className.split(' ') : [];
for (i = 0; i < classNames.length; i++) {
handlers = elementHandlers['.' + classNames[i]];
if (!isHandledElsewhere && handlers) {
if (typeof handlers === "function") {
isHandledElsewhere = handlers(element, renderer);
} else {
i = 0;
l = handlers.length;
while (!isHandledElsewhere && i !== l) {
isHandledElsewhere = handlers[i](element, renderer);
i++;
}
}
}
}
return isHandledElsewhere;
};
tableToJson = function tableToJson(table, renderer) {
var data, headers, i, j, rowData, tableRow, table_with, cell, l;
data = [];
headers = [];
i = 0;
l = 0;
for (var j = 0; j < table.rows[0].cells.length; j++) {
l += table.rows[0].cells[j].colSpan;
}
table_with = table.clientWidth;
while (i < l) {
cell = table.rows[0].cells[i];
for (var j = 0; j < cell.colSpan; j++) {
headers[i + j] = {
name: cell.textContent.toLowerCase().replace(/\s+/g, '') + '_' + j,
prompt: cell.textContent.replace(/\r?\n/g, ''),
width: cell.clientWidth / table_with * renderer.settings.width / cell.colSpan
};
}
i += j;
}
i = 1;
while (i < table.rows.length) {
tableRow = table.rows[i];
rowData = {};
j = 0;
while (j < tableRow.cells.length) {
rowData[headers[j].name] = tableRow.cells[j].textContent.replace(/\r?\n/g, '');
j++;
}
data.push(rowData);
i++;
}
return {
rows: data,
headers: headers
};
};
var SkipNode = {
SCRIPT: 1,
STYLE: 1,
NOSCRIPT: 1,
OBJECT: 1,
EMBED: 1,
SELECT: 1
};
var listCount = 1;
_DrillForContent = function DrillForContent(element, renderer, elementHandlers) {
var cn, cns, fragmentCSS, i, isBlock, l, table2json, cb;
cns = element.childNodes;
cn = void 0;
fragmentCSS = GetCSS(element);
isBlock = fragmentCSS.display === "block";
if (isBlock) {
renderer.setBlockBoundary();
renderer.setBlockStyle(fragmentCSS);
}
i = 0;
l = cns.length;
while (i < l) {
cn = cns[i];
if (typeof(cn) === "object") {
//execute all watcher functions to e.g. reset floating
renderer.executeWatchFunctions(cn);
/*** HEADER rendering **/
if (cn.nodeType === 1 && cn.nodeName === 'HEADER') {
var header = cn; //store old top margin
var oldMarginTop = renderer.pdf.margins_doc.top; //subscribe for new page event and render header first on every page
renderer.pdf.internal.events.subscribe('addPage', function (pageInfo) {
//set current y position to old margin
renderer.y = oldMarginTop; //render all child nodes of the header element
_DrillForContent(header, renderer, elementHandlers); //set margin to old margin + rendered header + 10 space to prevent overlapping
//important for other plugins (e.g. table) to start rendering at correct position after header
renderer.pdf.margins_doc.top = renderer.y + 10;
renderer.y += 10;
}, false);
}
if (cn.nodeType === 8 && cn.nodeName === "#comment") {
if (~cn.textContent.indexOf("ADD_PAGE")) {
renderer.pdf.addPage();
renderer.y = renderer.pdf.margins_doc.top;
}
} else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) {
/*** IMAGE RENDERING ***/
var cached_image;
if (cn.nodeName === "IMG") {
var url = cn.getAttribute("src");
cached_image = images[(renderer.pdf.sHashCode && renderer.pdf.sHashCode(url)) || url];
}
if (cached_image) {
if (renderer.pdf.internal.pageSize.getHeight() - renderer.pdf.margins_doc.bottom < renderer.y + cn.height && renderer.y > renderer.pdf.margins_doc.top) {
renderer.pdf.addPage();
renderer.y = renderer.pdf.margins_doc.top; //check if we have to set back some values due to e.g. header rendering for new page
renderer.executeWatchFunctions(cn);
}
var imagesCSS = GetCSS(cn);
var imageX = renderer.x;
var fontToUnitRatio = 12 / renderer.pdf.internal.scaleFactor; //define additional paddings, margins which have to be taken into account for margin calculations
var additionalSpaceLeft = (imagesCSS["margin-left"] + imagesCSS["padding-left"]) * fontToUnitRatio;
var additionalSpaceRight = (imagesCSS["margin-right"] + imagesCSS["padding-right"]) * fontToUnitRatio;
var additionalSpaceTop = (imagesCSS["margin-top"] + imagesCSS["padding-top"]) * fontToUnitRatio;
var additionalSpaceBottom = (imagesCSS["margin-bottom"] + imagesCSS["padding-bottom"]) * fontToUnitRatio; //if float is set to right, move the image to the right border
//add space if margin is set
if (imagesCSS['float'] !== undefined && imagesCSS['float'] === 'right') {
imageX += renderer.settings.width - cn.width - additionalSpaceRight;
} else {
imageX += additionalSpaceLeft;
}
renderer.pdf.addImage(cached_image, imageX, renderer.y + additionalSpaceTop, cn.width, cn.height);
cached_image = undefined; //if the float prop is specified we have to float the text around the image
if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') {
//add functiont to set back coordinates after image rendering
renderer.watchFunctions.push(function (diffX, thresholdY, diffWidth, el) {
//undo drawing box adaptions which were set by floating
if (renderer.y >= thresholdY) {
renderer.x += diffX;
renderer.settings.width += diffWidth;
return true;
} else if (el && el.nodeType === 1 && !SkipNode[el.nodeName] && renderer.x + el.width > renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width) {
renderer.x += diffX;
renderer.y = thresholdY;
renderer.settings.width += diffWidth;
return true;
} else {
return false;
}
}.bind(this, imagesCSS['float'] === 'left' ? -cn.width - additionalSpaceLeft - additionalSpaceRight : 0, renderer.y + cn.height + additionalSpaceTop + additionalSpaceBottom, cn.width)); //reset floating by clear:both divs
//just set cursorY after the floating element
renderer.watchFunctions.push(function (yPositionAfterFloating, pages, el) {
if (renderer.y < yPositionAfterFloating && pages === renderer.pdf.internal.getNumberOfPages()) {
if (el.nodeType === 1 && GetCSS(el).clear === 'both') {
renderer.y = yPositionAfterFloating;
return true;
} else {
return false;
}
} else {
return true;
}
}.bind(this, renderer.y + cn.height, renderer.pdf.internal.getNumberOfPages())); //if floating is set we decrease the available width by the image width
renderer.settings.width -= cn.width + additionalSpaceLeft + additionalSpaceRight; //if left just add the image width to the X coordinate
if (imagesCSS['float'] === 'left') {
renderer.x += cn.width + additionalSpaceLeft + additionalSpaceRight;
}
} else {
//if no floating is set, move the rendering cursor after the image height
renderer.y += cn.height + additionalSpaceTop + additionalSpaceBottom;
}
/*** TABLE RENDERING ***/
} else if (cn.nodeName === "TABLE") {
if(!renderer.pdf.autoTable) {
table2json = tableToJson(cn, renderer);
renderer.y += 10;
renderer.pdf.table(renderer.x, renderer.y, table2json.rows, table2json.headers, {
autoSize: false,
printHeaders: elementHandlers.printHeaders,
margins: renderer.pdf.margins_doc,
css: GetCSS(cn)
});
renderer.y = renderer.pdf.internal.__cell__.lastCell.y +
renderer.pdf.internal.__cell__.lastCell.height;
} else {
renderer.y += 10;
renderer.pdf.autoTable({ theme: "grid", html: cn, startY: renderer.y,
styles: {
font: renderer.pdf.getFont().fontName,
fontSize: renderer.pdf.getFontSize(),
textColor: renderer.pdf.getTextColor()
},
margin: {
top: renderer.pdf.margins_doc.top,
left: renderer.x,
right: renderer.pdf.internal.pageSize.getWidth() - (renderer.x + renderer.settings.width),
bottom: renderer.pdf.margins_doc.bottom },
});
renderer.y = renderer.pdf.lastAutoTable.finalY;
}
} else if (cn.nodeName === "OL" || cn.nodeName === "UL") {
listCount = 1;
if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
_DrillForContent(cn, renderer, elementHandlers);
}
renderer.y += 10;
} else if (cn.nodeName === "LI") {
var temp = renderer.x;
renderer.x += 20 / renderer.pdf.internal.scaleFactor;
renderer.y += 3;
if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
_DrillForContent(cn, renderer, elementHandlers);
}
renderer.x = temp;
} else if (cn.nodeName === "BR") {
renderer.y += fragmentCSS["font-size"] * renderer.pdf.internal.scaleFactor;
renderer.addText("\u2028", clone(fragmentCSS));
} else {
if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
_DrillForContent(cn, renderer, elementHandlers);
}
}
} else if (cn.nodeType === 3) {
var value = cn.nodeValue;
if (cn.nodeValue && cn.parentNode.nodeName === "LI") {
if (cn.parentNode.parentNode.nodeName === "OL") {
value = listCount++ + '. ' + value;
} else {
var fontSize = fragmentCSS["font-size"];
var offsetX = (3 - fontSize * 0.75) * renderer.pdf.internal.scaleFactor;
var offsetY = fontSize * 0.75 * renderer.pdf.internal.scaleFactor;
var radius = fontSize * 1.74 / renderer.pdf.internal.scaleFactor;
cb = function cb(x, y) {
this.pdf.circle(x + offsetX, y + offsetY, radius, 'FD');
};
}
} // Only add the text if the text node is in the body element
// Add compatibility with IE11
if (!!(cn.ownerDocument.body.compareDocumentPosition(cn) & 16)) {
renderer.addText(value, fragmentCSS);
}
} else if (typeof cn === "string") {
renderer.addText(cn, fragmentCSS);
}
}
i++;
}
elementHandlers.outY = renderer.y;
if (isBlock) {
return renderer.setBlockBoundary(cb);
}
};
images = {};
loadImgs = function loadImgs(element, renderer, elementHandlers, cb) {
var imgs = element.getElementsByTagName('img'),
l = imgs.length,
found_images,
x = 0;
function done() {
renderer.pdf.internal.events.publish('imagesLoaded');
cb(found_images);
}
function loadImage(url, width, height) {
if (!url) return;
var img = new Image();
found_images = ++x;
img.crossOrigin = '';
img.onerror = img.onload = function () {
if (img.complete) {
//to support data urls in images, set width and height
//as those values are not recognized automatically
if (img.src.indexOf('data:image/') === 0) {
img.width = width || img.width || 0;
img.height = height || img.height || 0;
} //if valid image add to known images array
if (img.width + img.height) {
var hash = (renderer.pdf.sHashCode && renderer.pdf.sHashCode(url)) || url;
images[hash] = images[hash] || img;
}
}
if (! --x) {
done();
}
};
img.src = url;
}
while (l--) {
loadImage(imgs[l].getAttribute("src"), imgs[l].width, imgs[l].height);
}
return x || done();
};
checkForFooter = function checkForFooter(elem, renderer, elementHandlers) {
//check if we can found a <footer> element
var footer = elem.getElementsByTagName("footer");
if (footer.length > 0) {
footer = footer[0]; //bad hack to get height of footer
//creat dummy out and check new y after fake rendering
var oldOut = renderer.pdf.internal.write;
var oldY = renderer.y;
renderer.pdf.internal.write = function () {};
_DrillForContent(footer, renderer, elementHandlers);
var footerHeight = Math.ceil(renderer.y - oldY) + 5;
renderer.y = oldY;
renderer.pdf.internal.write = oldOut; //add 20% to prevent overlapping
renderer.pdf.margins_doc.bottom += footerHeight; //Create function render header on every page
var renderFooter = function renderFooter(pageInfo) {
var pageNumber = pageInfo !== undefined ? pageInfo.pageNumber : 1; //set current y position to old margin
var oldPosition = renderer.y; //render all child nodes of the header element
renderer.y = renderer.pdf.internal.pageSize.getHeight() - renderer.pdf.margins_doc.bottom;
renderer.pdf.margins_doc.bottom -= footerHeight; //check if we have to add page numbers
var spans = footer.getElementsByTagName('span');
for (var i = 0; i < spans.length; ++i) {
//if we find some span element with class pageCounter, set the page
if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" pageCounter ") > -1) {
spans[i].innerHTML = pageNumber;
} //if we find some span element with class totalPages, set a variable which is replaced after rendering of all pages
if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
spans[i].innerHTML = '###jsPDFVarTotalPages###';
}
} //render footer content
_DrillForContent(footer, renderer, elementHandlers); //set bottom margin to previous height including the footer height
renderer.pdf.margins_doc.bottom += footerHeight; //important for other plugins (e.g. table) to start rendering at correct position after header
renderer.y = oldPosition;
}; //check if footer contains totalPages which should be replace at the disoposal of the document
var spans = footer.getElementsByTagName('span');
for (var i = 0; i < spans.length; ++i) {
if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
renderer.pdf.internal.events.subscribe('htmlRenderingFinished', renderer.pdf.putTotalPages.bind(renderer.pdf, '###jsPDFVarTotalPages###'), true);
}
} //register event to render footer on every new page
renderer.pdf.internal.events.subscribe('addPage', renderFooter, false); //render footer on first page
renderFooter(); //prevent footer rendering
SkipNode['FOOTER'] = 1;
}
};
process = function process(pdf, element, x, y, settings, callback) {
if (!element) return false;
if (typeof element !== "string" && !element.parentNode) element = '' + element.innerHTML;
if (typeof element === "string") {
element = function (element) {
var $frame, $hiddendiv, framename, visuallyhidden;
framename = "jsPDFhtmlText" + Date.now().toString() + (Math.random() * 1000).toFixed(0);
visuallyhidden = "position: absolute !important;" + "clip: rect(1px 1px 1px 1px); /* IE6, IE7 */" + "clip: rect(1px, 1px, 1px, 1px);" + "padding:0 !important;" + "border:0 !important;" + "height: 1px !important;" + "width: 1px !important; " + "top:auto;" + "left:-100px;" + "overflow: hidden;";
$hiddendiv = document.createElement('div');
$hiddendiv.className = "sjs-pdf-hidden-html-div";
$hiddendiv.style.cssText = visuallyhidden;
$hiddendiv.innerHTML = "<iframe style=\"height:1px;width:1px\" name=\"" + framename + "\" />";
document.body.appendChild($hiddendiv);
$frame = window.frames[framename];
$frame.document.open();
$frame.document.writeln(element);
$frame.document.close();
return $frame.document.body;
}(element.replace(/<\/?script[^>]*?>/gi, ''));
}
var availableFonts = Object.keys(pdf.getFontList());
for(var i = 0; i < availableFonts.length; ++i) {
var fontName = availableFonts[i];
var fontFamily = fontName.toLowerCase();
if(!FontNameDB[fontFamily]) {
FontNameDB[fontFamily] = fontName;
}
}
var r = new Renderer(pdf, x, y, settings),
out; // 1. load images
// 2. prepare optional footer elements
// 3. render content
loadImgs.call(this, element, r, settings.elementHandlers, function (found_images) {
checkForFooter(element, r, settings.elementHandlers);
_DrillForContent(element, r, settings.elementHandlers); //send event dispose for final taks (e.g. footer totalpage replacement)
r.pdf.internal.events.publish('htmlRenderingFinished');
out = r.dispose();
if (typeof callback === 'function') callback(out);else if (found_images) console.error('jsPDF Warning: rendering issues? provide a callback to fromHTML!');
});
return out || {
x: r.x,
y: r.y
};
};
Renderer.prototype.init = function () {
this.paragraph = {
text: [],
style: []
};
return this.pdf.internal.write("q");
};
Renderer.prototype.dispose = function () {
this.pdf.internal.write("Q");
return {
x: this.x,
y: this.y,
ready: true
};
}; //Checks if we have to execute some watcher functions
//e.g. to end text floating around an image
Renderer.prototype.executeWatchFunctions = function (el) {
var ret = false;
var narray = [];
if (this.watchFunctions.length > 0) {
for (var i = 0; i < this.watchFunctions.length; ++i) {
if (this.watchFunctions[i](el) === true) {
ret = true;
} else {
narray.push(this.watchFunctions[i]);
}
}
this.watchFunctions = narray;
}
return ret;
};
Renderer.prototype.splitFragmentsIntoLines = function (fragments, styles) {
var currentLineLength, defaultFontSize, ff, fontMetrics, fontMetricsCache, fragment, fragmentChopped, fragmentLength, fragmentSpecificMetrics, fs, k, line, lines, maxLineLength, style;
defaultFontSize = 12;
k = this.pdf.internal.scaleFactor;
fontMetricsCache = {};
ff = void 0;
fs = void 0;
fontMetrics = void 0;
fragment = void 0;
style = void 0;
fragmentSpecificMetrics = void 0;
fragmentLength = void 0;
fragmentChopped = void 0;
line = [];
lines = [line];
currentLineLength = 0;
maxLineLength = this.settings.width;
while (fragments.length) {
fragment = fragments.shift();
style = styles.shift();
if (fragment) {
ff = style["font-family"];
fs = style["font-style"];
fontMetrics = fontMetricsCache[ff + fs];
if (!fontMetrics) {
fontMetrics = this.pdf.internal.getFont(ff, fs).metadata.Unicode;
fontMetricsCache[ff + fs] = fontMetrics;
}
fragmentSpecificMetrics = {
widths: fontMetrics.widths,
kerning: fontMetrics.kerning,
fontSize: style["font-size"] * defaultFontSize,
textIndent: currentLineLength
};
fragmentLength = this.pdf.getStringUnitWidth(fragment, fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
if (fragment == "\u2028") {
line = [];
lines.push(line);
} else if (currentLineLength + fragmentLength > maxLineLength) {
fragmentChopped = this.pdf.splitTextToSize(fragment, maxLineLength, fragmentSpecificMetrics);
line.push([fragmentChopped.shift(), style]);
while (fragmentChopped.length) {
line = [[fragmentChopped.shift(), style]];
lines.push(line);
}
currentLineLength = this.pdf.getStringUnitWidth(line[0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
} else {
line.push([fragment, style]);
currentLineLength += fragmentLength;
}
}
} //if text alignment was set, set margin/indent of each line
if (style['text-align'] !== undefined && (style['text-align'] === 'center' || style['text-align'] === 'right' || style['text-align'] === 'justify')) {
for (var i = 0; i < lines.length; ++i) {
var length = this.pdf.getStringUnitWidth(lines[i][0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k; //if there is more than on line we have to clone the style object as all lines hold a reference on this object
if (i > 0) {
lines[i][0][1] = clone(lines[i][0][1]);
}
var space = maxLineLength - length;
if (style['text-align'] === 'right') {
lines[i][0][1]['margin-left'] = space; //if alignment is not right, it has to be center so split the space to the left and the right
} else if (style['text-align'] === 'center') {
lines[i][0][1]['margin-left'] = space / 2; //if justify was set, calculate the word spacing and define in by using the css property
} else if (style['text-align'] === 'justify') {
var countSpaces = lines[i][0][0].split(' ').length - 1;
lines[i][0][1]['word-spacing'] = space / countSpaces; //ignore the last line in justify mode
if (i === lines.length - 1) {
lines[i][0][1]['word-spacing'] = 0;
}
}
}
}
return lines;
};
Renderer.prototype.RenderTextFragment = function (text, style) {
var defaultFontSize, font, maxLineHeight;
maxLineHeight = 0;
defaultFontSize = 12;
if (this.pdf.internal.pageSize.getHeight() - this.pdf.margins_doc.bottom < this.y + this.pdf.internal.getFontSize()) {
this.pdf.internal.write("ET", "Q", "Q");
const currentPageNumber = this.pdf.getCurrentPageInfo().pageNumber;
if (this.pdf.getNumberOfPages() === currentPageNumber) this.pdf.addPage();
else this.pdf.setPage(currentPageNumber + 1);
this.y = this.pdf.margins_doc.top;
this.pdf.internal.write("q", "q", "BT", this.getPdfColor(style.color), this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td"); //move cursor by one line on new page
maxLineHeight = Math.max(maxLineHeight, style["line-height"], style["font-size"]);
this.pdf.internal.write(0, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td");
}
font = this.pdf.internal.getFont(style["font-family"], style["font-style"]); // text color
var pdfTextColor = this.getPdfColor(style["color"]);
if (pdfTextColor !== this.lastTextColor) {
this.pdf.internal.write(pdfTextColor);
this.lastTextColor = pdfTextColor;
} //set the word spacing for e.g. justify style
if (style['word-spacing'] !== undefined && style['word-spacing'] > 0) {
this.pdf.internal.write(style['word-spacing'].toFixed(2), "Tw");
}
var pdfEscape16 = function(text, font) {
var widths = font.metadata.Unicode.widths;
var padz = ["", "0", "00", "000", "0000"];
var ar = [""];
for (var i = 0, l = text.length, t; i < l; ++i) {
t = font.metadata.characterToGlyph(text.charCodeAt(i));
font.metadata.glyIdsUsed.push(t);
font.metadata.toUnicode[t] = text.charCodeAt(i);
if (widths.indexOf(t) == -1) {
widths.push(t);
widths.push([parseInt(font.metadata.widthOfGlyph(t), 10)]);
}
if (t == "0") {
//Spaces are not allowed in cmap.
return ar.join("");
} else {
t = t.toString(16);
ar.push(padz[4 - t.length], t);
}
}
return ar.join("");
};
var utf8TextFunction = function(text, font) {
var text = text || "";
var str = "",
s = 0,
cmapConfirm;
var strText = "";
var encoding = font.encoding;
if (font.encoding !== "Identity-H") {
return text;
}
strText = text;
for (s = 0; s < strText.length; s += 1) {
if (font.metadata.hasOwnProperty("cmap")) {
cmapConfirm = font.metadata.cmap.unicode.codeMap[strText[s].charCodeAt(0)];
}
if (!cmapConfirm) {
if (
strText[s].charCodeAt(0) < 256 &&
font.metadata.hasOwnProperty("Unicode")
) {
str += strText[s];
} else {
str += "";
}
} else {
str += strText[s];
}
}
var result = "";
if (parseInt(font.id.slice(1)) < 14 || encoding === "WinAnsiEncoding") {
result = this.pdf.internal.pdfEscape(str, key)
.split("")
.map(function(cv) {
return cv.charCodeAt(0).toString(16);
})
.join("");
} else if (encoding === "Identity-H") {
result = pdfEscape16(str, font);
}
return result;
};
// var escapedText = this.pdf.internal.pdfEscape(text);
// var escapedText = utf8TextFunction(text, font);
// if(escapedText != text) {
// escapedText = "<" + escapedText + ">";
// } else {
// escapedText = "(" + escapedText + ")";
// }
var escapedText = "";
if(font.encoding !== "Identity-H") {
escapedText = "(" + this.pdf.internal.pdfEscape(text) + ")";
} else {
escapedText = "<" + utf8TextFunction(text, font) + ">";
}
this.pdf.internal.write("/" + font.id, (defaultFontSize * style["font-size"]).toFixed(2), "Tf", escapedText + " Tj"); //set the word spacing back to neutral => 0
if (style['word-spacing'] !== undefined) {
this.pdf.internal.write(0, "Tw");
}
}; // Accepts #FFFFFF, rgb(int,int,int), or CSS Color Name
Renderer.prototype.getPdfColor = function (style) {
var textColor;
var r, g, b;
var rx = /rgb\s*\(\s*(\d+),\s*(\d+),\s*(\d+\s*)\)/;
var m = rx.exec(style);
if (m != null) {
r = parseInt(m[1]);
g = parseInt(m[2]);
b = parseInt(m[3]);
} else {
if (typeof style === "string" && style.charAt(0) != '#') {
var rgbColor = new RGBColor(style);
if (rgbColor.ok) {
style = rgbColor.toHex();
} else {
style = '#000000';
}
}
r = style.substring(1, 3);
r = parseInt(r, 16);
g = style.substring(3, 5);
g = parseInt(g, 16);
b = style.substring(5, 7);
b = parseInt(b, 16);
}
if (typeof r === 'string' && /^#[0-9A-Fa-f]{6}$/.test(r)) {
var hex = parseInt(r.substr(1), 16);
r = hex >> 16 & 255;
g = hex >> 8 & 255;
b = hex & 255;
}
var f3 = this.f3;
if (r === 0 && g === 0 && b === 0 || typeof g === 'undefined') {
textColor = f3(r / 255) + ' g';
} else {
textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' ');
}
return textColor;
};
Renderer.prototype.f3 = function (number) {
return number.toFixed(3); // Ie, %.3f
}, Renderer.prototype.renderParagraph = function (cb) {
var blockstyle, defaultFontSize, fontToUnitRatio, fragments, i, l, line, lines, maxLineHeight, out, paragraphspacing_after, paragraphspacing_before, styles, fontSize;
fragments = PurgeWhiteSpace(this.paragraph.text);
styles = this.paragraph.style;
blockstyle = this.paragraph.blockstyle;
this.paragraph.priorblockstyle || {};
this.paragraph = {
text: [],
style: [],
blockstyle: {},
priorblockstyle: blockstyle
};
if (!fragments.join("").trim()) {
return;
}
lines = this.splitFragmentsIntoLines(fragments, styles);
line = void 0;
maxLineHeight = void 0;
defaultFontSize = 12;
fontToUnitRatio = defaultFontSize / this.pdf.internal.scaleFactor;
this.priorMarginBottom = this.priorMarginBottom || 0;
paragraphspacing_before = (Math.max((blockstyle["margin-top"] || 0) - this.priorMarginBottom, 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio;
paragraphspacing_after = ((blockstyle["margin-bottom"] || 0) + (blockstyle["padding-bottom"] || 0)) * fontToUnitRatio;
this.priorMarginBottom = blockstyle["margin-bottom"] || 0;
if (blockstyle['page-break-before'] === 'always') {
this.pdf.addPage();
this.y = 0;
paragraphspacing_before = ((blockstyle["margin-top"] || 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio;
}
out = this.pdf.internal.write;
i = void 0;
l = void 0;
this.y += paragraphspacing_before;
out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td"); //stores the current indent of cursor position
var currentIndent = 0;
while (lines.length) {
line = lines.shift();
maxLineHeight = 0;
i = 0;
l = line.length;
while (i !== l) {
if (line[i][0].trim()) {
maxLineHeight = Math.max(maxLineHeight, line[i][1]["line-height"], line[i][1]["font-size"]);
fontSize = line[i][1]["font-size"] * 7;
}
i++;
} //if we have to move the cursor to adapt the indent
var indentMove = 0;
var wantedIndent = 0; //if a margin was added (by e.g. a text-alignment), move the cursor
if (line[0][1]["margin-left"] !== undefined && line[0][1]["margin-left"] > 0) {
wantedIndent = this.pdf.internal.getCoordinateString(line[0][1]["margin-left"]);
indentMove = wantedIndent - currentIndent;
currentIndent = wantedIndent;
}
var indentMore = Math.max(blockstyle["margin-left"] || 0, 0) * fontToUnitRatio; //move the cursor
out(indentMove + indentMore, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td");
i = 0;
l = line.length;
while (i !== l) {
if (line[i][0]) {
this.RenderTextFragment(line[i][0], line[i][1]);
}
i++;
}
this.y += maxLineHeight * fontToUnitRatio; //if some watcher function was executed successful, so e.g. margin and widths were changed,
//reset line drawing and calculate position and lines again
//e.g. to stop text floating around an image
if (this.executeWatchFunctions(line[0][1]) && lines.length > 0) {
var localFragments = [];
var localStyles = []; //create fragment array of
lines.forEach(function (localLine) {
var i = 0;
var l = localLine.length;
while (i !== l) {
if (localLine[i][0]) {
localFragments.push(localLine[i][0] + ' ');
localStyles.push(localLine[i][1]);
}
++i;
}
}); //split lines again due to possible coordinate changes
lines = this.splitFragmentsIntoLines(PurgeWhiteSpace(localFragments), localStyles); //reposition the current cursor
out("ET", "Q");
out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");
}
}
if (cb && typeof cb === "function") {
cb.call(this, this.x - 9, this.y - fontSize / 2);
}
out("ET", "Q");
return this.y += paragraphspacing_after;
};
Renderer.prototype.setBlockBoundary = function (cb) {
return this.renderParagraph(cb);
};
Renderer.prototype.setBlockStyle = function (css) {
return this.paragraph.blockstyle = css;
};
Renderer.prototype.addText = function (text, css) {
this.paragraph.text.push(text);
return this.paragraph.style.push(css);
};
FontNameDB = {
helvetica: "helvetica",
"sans-serif": "helvetica",
"times new roman": "times",
serif: "times",
times: "times",
monospace: "courier",
courier: "courier"
};
FontWeightMap = {
100: "normal",
200: "normal",
300: "normal",
400: "normal",
500: "bold",
600: "bold",
700: "bold",
800: "bold",
900: "bold",
normal: "normal",
bold: "bold",
bolder: "bold",
lighter: "normal"
};
FontStyleMap = {
normal: "normal",
italic: "italic",
oblique: "italic"
};
TextAlignMap = {
left: "left",
right: "right",
center: "center",
justify: "justify"
};
FloatMap = {
none: 'none',
right: 'right',
left: 'left'
};
ClearMap = {
none: 'none',
both: 'both'
};
UnitedNumberMap = {
normal: 1
};
/**
* Converts HTML-formatted text into formatted PDF text.
*
* Notes:
* 2012-07-18
* Plugin relies on having browser, DOM around. The HTML is pushed into dom and traversed.
* Plugin relies on jQuery for CSS extraction.
* Targeting HTML output from Markdown templating, which is a very simple
* markup - div, span, em, strong, p. No br-based paragraph separation supported explicitly (but still may work.)
* Images, tables are NOT supported.
*
* @public
* @function
* @param HTML {String|Object} HTML-formatted text, or pointer to DOM element that is to be rendered into PDF.
* @param x {Number} starting X coordinate in jsPDF instance's declared units.
* @param y {Number} starting Y coordinate in jsPDF instance's declared units.
* @param settings {Object} Additional / optional variables controlling parsing, rendering.
* @returns {Object} jsPDF instance
*/
jsPDFAPI.fromHTML = function (HTML, x, y, settings, callback, margins) {
this.margins_doc = margins || {
top: 0,
bottom: 0
};
if (!settings) settings = {};
if (!settings.elementHandlers) settings.elementHandlers = {};
return process(this, HTML, isNaN(x) ? 4 : x, isNaN(y) ? 4 : y, settings, callback);
};
})(jsPDF.API);
class DocOptions {
constructor(options) {
this._base64Normal = undefined;
this._base64Bold = undefined;
if (typeof options.orientation === 'undefined') {
if (typeof options.format === 'undefined' ||
options.format[0] < options.format[1]) {
this._orientation = 'p';
}
else
this._orientation = 'l';
}
else
this._orientation = options.orientation;
this._format = options.format || 'a4';
if (Array.isArray(this._format)) {
this._format = this._format.map(f => f * DocOptions.MM_TO_PT);
}
this._fontSize = options.fontSize || DocOptions.FONT_SIZE;
if (!options.fontName) {
if (!DocOptions.SEGOE_BOLD && !DocOptions.SEGOE_NORMAL) {
this._fontName = SurveyHelper.STANDARD_FONT;
}
else {
this._fontName = 'segoe';
}
}
else {
this._fontName = options.fontName;
}
if ((typeof options.fontName !== 'undefined' &&
(typeof options.base64Normal !== 'undefined' ||
typeof options.base64Bold !== 'undefined'))) {
this._base64Normal = options.base64Normal || options.base64Bold;
this._base64Bold = options.base64Bold || options.base64Normal;
}
else if (this.fontName === 'segoe') {
// this._base64Normal = Fonts.SEGOE_NORMAL;
// this._base64Bold = Fonts.SEGOE_BOLD;
this._base64Normal = DocOptions.SEGOE_NORMAL;
this._base64Bold = DocOptions.SEGOE_BOLD;
}
this._margins = SurveyHelper.clone(options.margins);
if (typeof this._margins === 'undefined') {
this._margins = {};
}
if (typeof this._margins.top === 'undefined') {
this._margins.top = 10.0;
}
if (typeof this._margins.bot === 'undefined') {
this._margins.bot = 10.0;
}
if (typeof this._margins.left === 'undefined') {
this._margins.left = 10.0;
}
if (typeof this._margins.right === 'undefined') {
this._margins.right = 10.0;
}
Object.keys(this._margins).forEach((name) => {
this._margins[name] = this._margins[name] * DocOptions.MM_TO_PT;
});
this._htmlRenderAs = options.htmlRenderAs || 'auto';
this._matrixRenderAs = options.matrixRenderAs || 'auto';
this._readonlyRenderAs = options.readonlyRenderAs || 'auto';
this._compress = options.compress || false;
this._applyImageFit = options.applyImageFit || false;
this._useLegacyBooleanRendering = options.useLegacyBooleanRendering || false;
this._isRTL = options.isRTL || false;
this._tagboxSelectedChoicesOnly = options.tagboxSelectedChoicesOnly || false;
}
get leftTopPoint() {
return {
xLeft: this.margins.left,
yTop: this.margins.top
};
}
get fontSize() {
return this._fontSize;
}
get fontName() {
return this._fontName;
}
get base64Normal() {
return this._base64Normal;
}
get base64Bold() {
return this._base64Bold;
}
get useCustomFontInHtml() {
return this._useCustomFontInHtml;
}
get margins() {
return this._margins;
}
get format() {
return this._format;
}
get orientation() {
return this._orientation;
}
get htmlRenderAs() {
return this._htmlRenderAs;
}
get matrixRenderAs() {
return this._matrixRenderAs;
}
get readonlyRenderAs() {
return this._readonlyRenderAs;
}
get compress() {
return this._compress;
}
get applyImageFit() {
return this._applyImageFit;
}
get useLegacyBooleanRendering() {
return this._useLegacyBooleanRendering;
}
get isRTL() {
return this._isRTL;
}
get tagboxSelectedChoicesOnly() {
return this._tagboxSelectedChoicesOnly;
}
}
DocOptions.MM_TO_PT = 72 / 25.4;
DocOptions.FONT_SIZE = 14;
/**
* The `DocController` object includes an API that allows you to configure the resulting PDF document. You can access this object within functions that handle the `SurveyPDF`'s [`onRender...`](https://surveyjs.io/pdf-generator/documentation/api-reference/surveypdf#onRenderFooter) events.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/how-to-use-adorners-in-pdf-forms/ (linkStyle))
*/
class DocController extends DocOptions {
constructor(options = {}) {
super(options);
const jspdfOptions = {
orientation: this.orientation,
unit: 'pt',
format: this.format,
compress: this.compress
};
this._doc = new jsPDF(jspdfOptions);
if (typeof this.base64Normal !== 'undefined' && !SurveyHelper.isFontExist(this, this.fontName)) {
DocController.addFont(this.fontName, this.base64Normal, 'normal');
DocController.addFont(this.fontName, this.base64Bold, 'bold');
this._doc = new jsPDF(jspdfOptions);
}
setRadioAppearance(this._doc);
this._useCustomFontInHtml = options.useCustomFontInHtml && SurveyHelper.isFontExist(this, this.fontName);
this._helperDoc = new jsPDF(jspdfOptions);
this._doc.setFont(this.fontName);
this._helperDoc.setFont(this.fontName);
this._doc.setFontSize(this.fontSize);
this._helperDoc.setFontSize(this.fontSize);
this._fontStyle = 'normal';
this.marginsStack = [];
}
static addFont(fontName, base64, fontStyle) {
let font = DocController.customFonts[fontName];
if (!font) {
font = {};
DocController.customFonts[fontName] = font;
}
font[fontStyle] = base64;
const addFontCallback = function () {
const customFont = DocController.customFonts[fontName];
if (!!customFont && !!customFont[fontStyle]) {
const fontFile = `${fontName}-${fontStyle}.ttf`;
this.addFileToVFS(fontFile, customFont[fontStyle]);
this.addFont(fontFile, fontName, fontStyle);
}
};
jsPDF.API.events.push(['addFonts', addFontCallback]);
}
get doc() {
return this._doc;
}
get helperDoc() {
return this._helperDoc;
}
get fontName() {
return this._fontName;
}
set fontName(fontName) {
this._fontName = fontName;
this._doc.setFont(fontName);
this._helperDoc.setFont(fontName);
}
get fontSize() {
return this._fontSize;
}
set fontSize(fontSize) {
this._fontSize = fontSize;
this._doc.setFontSize(fontSize);
this._helperDoc.setFontSize(fontSize);
}
get fontStyle() {
return this._fontStyle;
}
set fontStyle(fontStyle) {
this._fontStyle = fontStyle;
this._doc.setFont(this._fontName, fontStyle);
this._helperDoc.setFont(this._fontName, fontStyle);
}
measureText(text = 1, fontStyle = this._fontStyle, fontSize = this._fontSize) {
const oldFontSize = this._helperDoc.getFontSize();
this._helperDoc.setFontSize(fontSize);
this._helperDoc.setFont(this._fontName, fontStyle);
const height = this._helperDoc.getLineHeight() / this._helperDoc.internal.scaleFactor;
let width = 0.0;
if (typeof text === 'number') {
width = height * text;
}
else {
text = typeof text === 'string' ? text : SurveyHelper.getLocString(text);
width = text.split('').reduce((sm, cr) => sm + this._helperDoc.getTextWidth(cr), 0.0);
}
this._helperDoc.setFontSize(oldFontSize);
this._helperDoc.setFont(this._fontName, 'normal');
return {
width: width,
height: height
};
}
/**
* The width of one character in pixels.
*/
get unitWidth() {
return this.measureText().width;
}
/**
* The heigth of one character in pixels.
*/
get unitHeight() {
return this.measureText().height;
}
pushMargins(left, right) {
this.marginsStack.push({ left: this.margins.left, right: this.margins.right });
if (typeof left !== 'undefined')
this.margins.left = left;
if (typeof right !== 'undefined')
this.margins.right = right;
}
popMargins() {
const margins = this.marginsStack.pop();
this.margins.left = margins.left;
this.margins.right = margins.right;
}
/**
* The width of a PDF page in pixels.
*/
get paperWidth() {
return this.doc.internal.pageSize.width;
}
/**
* The height of a PDF page in pixels.
*/
get paperHeight() {
return this.doc.internal.pageSize.height;
}
getNumberOfPages() {
return this.doc.getNumberOfPages();
}
addPage() {
this.doc.addPage();
}
getCurrentPageIndex() {
return this.doc.getCurrentPageInfo().pageNumber - 1;
}
setPage(index) {
this.doc.setPage(index + 1);
}
}
DocController.customFonts = {};
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var lib = {};
var shallowequal;
var hasRequiredShallowequal;
function requireShallowequal () {
if (hasRequiredShallowequal) return shallowequal;
hasRequiredShallowequal = 1;
//
shallowequal = function shallowEqual(objA, objB, compare, compareContext) {
var ret = compare ? compare.call(compareContext, objA, objB) : void 0;
if (ret !== void 0) {
return !!ret;
}
if (objA === objB) {
return true;
}
if (typeof objA !== "object" || !objA || typeof objB !== "object" || !objB) {
return false;
}
var keysA = Object.keys(objA);
var keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);
// Test for A's keys different from B.
for (var idx = 0; idx < keysA.length; idx++) {
var key = keysA[idx];
if (!bHasOwnProperty(key)) {
return false;
}
var valueA = objA[key];
var valueB = objB[key];
ret = compare ? compare.call(compareContext, valueA, valueB, key) : void 0;
if (ret === false || (ret === void 0 && valueA !== valueB)) {
return false;
}
}
return true;
};
return shallowequal;
}
var hasRequiredLib;
function requireLib () {
if (hasRequiredLib) return lib;
hasRequiredLib = 1;
// An augmented AVL Tree where each node maintains a list of records and their search intervals.
// Record is composed of an interval and its underlying data, sent by a client. This allows the
// interval tree to have the same interval inserted multiple times, as long its data is different.
// Both insertion and deletion require O(log n) time. Searching requires O(k*logn) time, where `k`
// is the number of intervals in the output list.
Object.defineProperty(lib, "__esModule", { value: true });
var isSame = requireShallowequal();
function height(node) {
if (node === undefined) {
return -1;
}
else {
return node.height;
}
}
var Node = /** @class */ (function () {
function Node(intervalTree, record) {
this.intervalTree = intervalTree;
this.records = [];
this.height = 0;
this.key = record.low;
this.max = record.high;
// Save the array of all records with the same key for this node
this.records.push(record);
}
// Gets the highest record.high value for this node
Node.prototype.getNodeHigh = function () {
var high = this.records[0].high;
for (var i = 1; i < this.records.length; i++) {
if (this.records[i].high > high) {
high = this.records[i].high;
}
}
return high;
};
// Updates height value of the node. Called during insertion, rebalance, removal
Node.prototype.updateHeight = function () {
this.height = Math.max(height(this.left), height(this.right)) + 1;
};
// Updates the max value of all the parents after inserting into already existing node, as well as
// removing the node completely or removing the record of an already existing node. Starts with
// the parent of an affected node and bubbles up to root
Node.prototype.updateMaxOfParents = function () {
if (this === undefined) {
return;
}
var thisHigh = this.getNodeHigh();
if (this.left !== undefined && this.right !== undefined) {
this.max = Math.max(Math.max(this.left.max, this.right.max), thisHigh);
}
else if (this.left !== undefined && this.right === undefined) {
this.max = Math.max(this.left.max, thisHigh);
}
else if (this.left === undefined && this.right !== undefined) {
this.max = Math.max(this.right.max, thisHigh);
}
else {
this.max = thisHigh;
}
if (this.parent) {
this.parent.updateMaxOfParents();
}
};
/*
Left-Left case:
z y
/ \ / \
y T4 Right Rotate (z) x z
/ \ - - - - - - - - -> / \ / \
x T3 T1 T2 T3 T4
/ \
T1 T2
Left-Right case:
z z x
/ \ / \ / \
y T4 Left Rotate (y) x T4 Right Rotate(z) y z
/ \ - - - - - - - - -> / \ - - - - - - - -> / \ / \
T1 x y T3 T1 T2 T3 T4
/ \ / \
T2 T3 T1 T2
*/
// Handles Left-Left case and Left-Right case after rebalancing AVL tree
Node.prototype._updateMaxAfterRightRotate = function () {
var parent = this.parent;
var left = parent.left;
// Update max of left sibling (x in first case, y in second)
var thisParentLeftHigh = left.getNodeHigh();
if (left.left === undefined && left.right !== undefined) {
left.max = Math.max(thisParentLeftHigh, left.right.max);
}
else if (left.left !== undefined && left.right === undefined) {
left.max = Math.max(thisParentLeftHigh, left.left.max);
}
else if (left.left === undefined && left.right === undefined) {
left.max = thisParentLeftHigh;
}
else {
left.max = Math.max(Math.max(left.left.max, left.right.max), thisParentLeftHigh);
}
// Update max of itself (z)
var thisHigh = this.getNodeHigh();
if (this.left === undefined && this.right !== undefined) {
this.max = Math.max(thisHigh, this.right.max);
}
else if (this.left !== undefined && this.right === undefined) {
this.max = Math.max(thisHigh, this.left.max);
}
else if (this.left === undefined && this.right === undefined) {
this.max = thisHigh;
}
else {
this.max = Math.max(Math.max(this.left.max, this.right.max), thisHigh);
}
// Update max of parent (y in first case, x in second)
parent.max = Math.max(Math.max(parent.left.max, parent.right.max), parent.getNodeHigh());
};
/*
Right-Right case:
z y
/ \ / \
T1 y Left Rotate(z) z x
/ \ - - - - - - - -> / \ / \
T2 x T1 T2 T3 T4
/ \
T3 T4
Right-Left case:
z z x
/ \ / \ / \
T1 y Right Rotate (y) T1 x Left Rotate(z) z y
/ \ - - - - - - - - -> / \ - - - - - - - -> / \ / \
x T4 T2 y T1 T2 T3 T4
/ \ / \
T2 T3 T3 T4
*/
// Handles Right-Right case and Right-Left case in rebalancing AVL tree
Node.prototype._updateMaxAfterLeftRotate = function () {
var parent = this.parent;
var right = parent.right;
// Update max of right sibling (x in first case, y in second)
var thisParentRightHigh = right.getNodeHigh();
if (right.left === undefined && right.right !== undefined) {
right.max = Math.max(thisParentRightHigh, right.right.max);
}
else if (right.left !== undefined && right.right === undefined) {
right.max = Math.max(thisParentRightHigh, right.left.max);
}
else if (right.left === undefined && right.right === undefined) {
right.max = thisParentRightHigh;
}
else {
right.max = Math.max(Math.max(right.left.max, right.right.max), thisParentRightHigh);
}
// Update max of itself (z)
var thisHigh = this.getNodeHigh();
if (this.left === undefined && this.right !== undefined) {
this.max = Math.max(thisHigh, this.right.max);
}
else if (this.left !== undefined && this.right === undefined) {
this.max = Math.max(thisHigh, this.left.max);
}
else if (this.left === undefined && this.right === undefined) {
this.max = thisHigh;
}
else {
this.max = Math.max(Math.max(this.left.max, this.right.max), thisHigh);
}
// Update max of parent (y in first case, x in second)
parent.max = Math.max(Math.max(parent.left.max, right.max), parent.getNodeHigh());
};
Node.prototype._leftRotate = function () {
var rightChild = this.right;
rightChild.parent = this.parent;
if (rightChild.parent === undefined) {
this.intervalTree.root = rightChild;
}
else {
if (rightChild.parent.left === this) {
rightChild.parent.left = rightChild;
}
else if (rightChild.parent.right === this) {
rightChild.parent.right = rightChild;
}
}
this.right = rightChild.left;
if (this.right !== undefined) {
this.right.parent = this;
}
rightChild.left = this;
this.parent = rightChild;
this.updateHeight();
rightChild.updateHeight();
};
Node.prototype._rightRotate = function () {
var leftChild = this.left;
leftChild.parent = this.parent;
if (leftChild.parent === undefined) {
this.intervalTree.root = leftChild;
}
else {
if (leftChild.parent.left === this) {
leftChild.parent.left = leftChild;
}
else if (leftChild.parent.right === this) {
leftChild.parent.right = leftChild;
}
}
this.left = leftChild.right;
if (this.left !== undefined) {
this.left.parent = this;
}
leftChild.right = this;
this.parent = leftChild;
this.updateHeight();
leftChild.updateHeight();
};
// Rebalances the tree if the height value between two nodes of the same parent is greater than
// two. There are 4 cases that can happen which are outlined in the graphics above
Node.prototype._rebalance = function () {
if (height(this.left) >= 2 + height(this.right)) {
var left = this.left;
if (height(left.left) >= height(left.right)) {
// Left-Left case
this._rightRotate();
this._updateMaxAfterRightRotate();
}
else {
// Left-Right case
left._leftRotate();
this._rightRotate();
this._updateMaxAfterRightRotate();
}
}
else if (height(this.right) >= 2 + height(this.left)) {
var right = this.right;
if (height(right.right) >= height(right.left)) {
// Right-Right case
this._leftRotate();
this._updateMaxAfterLeftRotate();
}
else {
// Right-Left case
right._rightRotate();
this._leftRotate();
this._updateMaxAfterLeftRotate();
}
}
};
Node.prototype.insert = function (record) {
if (record.low < this.key) {
// Insert into left subtree
if (this.left === undefined) {
this.left = new Node(this.intervalTree, record);
this.left.parent = this;
}
else {
this.left.insert(record);
}
}
else {
// Insert into right subtree
if (this.right === undefined) {
this.right = new Node(this.intervalTree, record);
this.right.parent = this;
}
else {
this.right.insert(record);
}
}
// Update the max value of this ancestor if needed
if (this.max < record.high) {
this.max = record.high;
}
// Update height of each node
this.updateHeight();
// Rebalance the tree to ensure all operations are executed in O(logn) time. This is especially
// important in searching, as the tree has a high chance of degenerating without the rebalancing
this._rebalance();
};
Node.prototype._getOverlappingRecords = function (currentNode, low, high) {
if (currentNode.key <= high && low <= currentNode.getNodeHigh()) {
// Nodes are overlapping, check if individual records in the node are overlapping
var tempResults = [];
for (var i = 0; i < currentNode.records.length; i++) {
if (currentNode.records[i].high >= low) {
tempResults.push(currentNode.records[i]);
}
}
return tempResults;
}
return [];
};
Node.prototype.search = function (low, high) {
// Don't search nodes that don't exist
if (this === undefined) {
return [];
}
var leftSearch = [];
var ownSearch = [];
var rightSearch = [];
// If interval is to the right of the rightmost point of any interval in this node and all its
// children, there won't be any matches
if (low > this.max) {
return [];
}
// Search left children
if (this.left !== undefined && this.left.max >= low) {
leftSearch = this.left.search(low, high);
}
// Check this node
ownSearch = this._getOverlappingRecords(this, low, high);
// If interval is to the left of the start of this interval, then it can't be in any child to
// the right
if (high < this.key) {
return leftSearch.concat(ownSearch);
}
// Otherwise, search right children
if (this.right !== undefined) {
rightSearch = this.right.search(low, high);
}
// Return accumulated results, if any
return leftSearch.concat(ownSearch, rightSearch);
};
// Searches for a node by a `key` value
Node.prototype.searchExisting = function (low) {
if (this === undefined) {
return undefined;
}
if (this.key === low) {
return this;
}
else if (low < this.key) {
if (this.left !== undefined) {
return this.left.searchExisting(low);
}
}
else {
if (this.right !== undefined) {
return this.right.searchExisting(low);
}
}
return undefined;
};
// Returns the smallest node of the subtree
Node.prototype._minValue = function () {
if (this.left === undefined) {
return this;
}
else {
return this.left._minValue();
}
};
Node.prototype.remove = function (node) {
var parent = this.parent;
if (node.key < this.key) {
// Node to be removed is on the left side
if (this.left !== undefined) {
return this.left.remove(node);
}
else {
return undefined;
}
}
else if (node.key > this.key) {
// Node to be removed is on the right side
if (this.right !== undefined) {
return this.right.remove(node);
}
else {
return undefined;
}
}
else {
if (this.left !== undefined && this.right !== undefined) {
// Node has two children
var minValue = this.right._minValue();
this.key = minValue.key;
this.records = minValue.records;
return this.right.remove(this);
}
else if (parent.left === this) {
// One child or no child case on left side
if (this.right !== undefined) {
parent.left = this.right;
this.right.parent = parent;
}
else {
parent.left = this.left;
if (this.left !== undefined) {
this.left.parent = parent;
}
}
parent.updateMaxOfParents();
parent.updateHeight();
parent._rebalance();
return this;
}
else if (parent.right === this) {
// One child or no child case on right side
if (this.right !== undefined) {
parent.right = this.right;
this.right.parent = parent;
}
else {
parent.right = this.left;
if (this.left !== undefined) {
this.left.parent = parent;
}
}
parent.updateMaxOfParents();
parent.updateHeight();
parent._rebalance();
return this;
}
}
};
return Node;
}());
lib.Node = Node;
var IntervalTree = /** @class */ (function () {
function IntervalTree() {
this.count = 0;
}
IntervalTree.prototype.insert = function (record) {
if (record.low > record.high) {
throw new Error('`low` value must be lower or equal to `high` value');
}
if (this.root === undefined) {
// Base case: Tree is empty, new node becomes root
this.root = new Node(this, record);
this.count++;
return true;
}
else {
// Otherwise, check if node already exists with the same key
var node = this.root.searchExisting(record.low);
if (node !== undefined) {
// Check the records in this node if there already is the one with same low, high, data
for (var i = 0; i < node.records.length; i++) {
if (isSame(node.records[i], record)) {
// This record is same as the one we're trying to insert; return false to indicate
// nothing has been inserted
return false;
}
}
// Add the record to the node
node.records.push(record);
// Update max of the node and its parents if necessary
if (record.high > node.max) {
node.max = record.high;
if (node.parent) {
node.parent.updateMaxOfParents();
}
}
this.count++;
return true;
}
else {
// Node with this key doesn't already exist. Call insert function on root's node
this.root.insert(record);
this.count++;
return true;
}
}
};
IntervalTree.prototype.search = function (low, high) {
if (this.root === undefined) {
// Tree is empty; return empty array
return [];
}
else {
return this.root.search(low, high);
}
};
IntervalTree.prototype.remove = function (record) {
if (this.root === undefined) {
// Tree is empty; nothing to remove
return false;
}
else {
var node = this.root.searchExisting(record.low);
if (node === undefined) {
return false;
}
else if (node.records.length > 1) {
var removedRecord = void 0;
// Node with this key has 2 or more records. Find the one we need and remove it
for (var i = 0; i < node.records.length; i++) {
if (isSame(node.records[i], record)) {
removedRecord = node.records[i];
node.records.splice(i, 1);
break;
}
}
if (removedRecord) {
removedRecord = undefined;
// Update max of that node and its parents if necessary
if (record.high === node.max) {
var nodeHigh = node.getNodeHigh();
if (node.left !== undefined && node.right !== undefined) {
node.max = Math.max(Math.max(node.left.max, node.right.max), nodeHigh);
}
else if (node.left !== undefined && node.right === undefined) {
node.max = Math.max(node.left.max, nodeHigh);
}
else if (node.left === undefined && node.right !== undefined) {
node.max = Math.max(node.right.max, nodeHigh);
}
else {
node.max = nodeHigh;
}
if (node.parent) {
node.parent.updateMaxOfParents();
}
}
this.count--;
return true;
}
else {
return false;
}
}
else if (node.records.length === 1) {
// Node with this key has only 1 record. Check if the remaining record in this node is
// actually the one we want to remove
if (isSame(node.records[0], record)) {
// The remaining record is the one we want to remove. Remove the whole node from the tree
if (this.root.key === node.key) {
// We're removing the root element. Create a dummy node that will temporarily take
// root's parent role
var rootParent = new Node(this, { low: record.low, high: record.low });
rootParent.left = this.root;
this.root.parent = rootParent;
var removedNode = this.root.remove(node);
this.root = rootParent.left;
if (this.root !== undefined) {
this.root.parent = undefined;
}
if (removedNode) {
removedNode = undefined;
this.count--;
return true;
}
else {
return false;
}
}
else {
var removedNode = this.root.remove(node);
if (removedNode) {
removedNode = undefined;
this.count--;
return true;
}
else {
return false;
}
}
}
else {
// The remaining record is not the one we want to remove
return false;
}
}
else {
// No records at all in this node?! Shouldn't happen
return false;
}
}
};
IntervalTree.prototype.inOrder = function () {
return new InOrder(this.root);
};
IntervalTree.prototype.preOrder = function () {
return new PreOrder(this.root);
};
return IntervalTree;
}());
lib.IntervalTree = IntervalTree;
var DataIntervalTree = /** @class */ (function () {
function DataIntervalTree() {
this.tree = new IntervalTree();
}
DataIntervalTree.prototype.insert = function (low, high, data) {
return this.tree.insert({ low: low, high: high, data: data });
};
DataIntervalTree.prototype.remove = function (low, high, data) {
return this.tree.remove({ low: low, high: high, data: data });
};
DataIntervalTree.prototype.search = function (low, high) {
return this.tree.search(low, high).map(function (v) { return v.data; });
};
DataIntervalTree.prototype.inOrder = function () {
return this.tree.inOrder();
};
DataIntervalTree.prototype.preOrder = function () {
return this.tree.preOrder();
};
Object.defineProperty(DataIntervalTree.prototype, "count", {
get: function () {
return this.tree.count;
},
enumerable: true,
configurable: true
});
return DataIntervalTree;
}());
lib.default = DataIntervalTree;
var InOrder = /** @class */ (function () {
function InOrder(startNode) {
this.stack = [];
if (startNode !== undefined) {
this.push(startNode);
}
}
InOrder.prototype.next = function () {
// Will only happen if stack is empty and pop is called
if (this.currentNode === undefined) {
return {
done: true,
value: undefined,
};
}
// Process this node
if (this.i < this.currentNode.records.length) {
return {
done: false,
value: this.currentNode.records[this.i++],
};
}
if (this.currentNode.right !== undefined) {
this.push(this.currentNode.right);
}
else {
// Might pop the last and set this.currentNode = undefined
this.pop();
}
return this.next();
};
InOrder.prototype.push = function (node) {
this.currentNode = node;
this.i = 0;
while (this.currentNode.left !== undefined) {
this.stack.push(this.currentNode);
this.currentNode = this.currentNode.left;
}
};
InOrder.prototype.pop = function () {
this.currentNode = this.stack.pop();
this.i = 0;
};
return InOrder;
}());
lib.InOrder = InOrder;
if (typeof Symbol === 'function') {
InOrder.prototype[Symbol.iterator] = function () { return this; };
}
var PreOrder = /** @class */ (function () {
function PreOrder(startNode) {
this.stack = [];
this.i = 0;
this.currentNode = startNode;
}
PreOrder.prototype.next = function () {
// Will only happen if stack is empty and pop is called,
// which only happens if there is no right node (i.e we are done)
if (this.currentNode === undefined) {
return {
done: true,
value: undefined,
};
}
// Process this node
if (this.i < this.currentNode.records.length) {
return {
done: false,
value: this.currentNode.records[this.i++],
};
}
if (this.currentNode.right !== undefined) {
this.push(this.currentNode.right);
}
if (this.currentNode.left !== undefined) {
this.push(this.currentNode.left);
}
this.pop();
return this.next();
};
PreOrder.prototype.push = function (node) {
this.stack.push(node);
};
PreOrder.prototype.pop = function () {
this.currentNode = this.stack.pop();
this.i = 0;
};
return PreOrder;
}());
lib.PreOrder = PreOrder;
if (typeof Symbol === 'function') {
PreOrder.prototype[Symbol.iterator] = function () { return this; };
}
return lib;
}
var libExports = requireLib();
var IntervalTree = /*@__PURE__*/getDefaultExportFromCjs(libExports);
class PagePacker {
static findBotInterval(tree, xLeft, xRight, options) {
const intervals = tree.search(xLeft, xRight);
intervals.push({
pageIndex: 0, xLeft: options.margins.left, xRight: options.margins.left,
yBot: options.margins.top, absBot: options.margins.top
});
return intervals.reduce((mx, cr) => {
if (Math.abs(cr.xRight - xLeft) < SurveyHelper.EPSILON ||
Math.abs(cr.xLeft - xRight) < SurveyHelper.EPSILON)
return mx;
if (cr.pageIndex < mx.pageIndex)
return mx;
if (cr.pageIndex > mx.pageIndex)
return cr;
return cr.yBot > mx.yBot ? cr : mx;
}, intervals[intervals.length - 1]);
}
static addPack(packs, index, brick) {
for (let i = packs.length; i <= index; i++) {
packs.push([]);
}
packs[index].push(brick);
}
static pack(flats, controller) {
const pageHeight = controller.paperHeight -
controller.margins.top - controller.margins.bot;
const unfoldFlats = [];
flats.forEach((flatsPage) => {
unfoldFlats.push([]);
flatsPage.forEach((flat) => {
if (flat.height > pageHeight + SurveyHelper.EPSILON) {
unfoldFlats[unfoldFlats.length - 1].push(...flat.unfold());
}
else
unfoldFlats[unfoldFlats.length - 1].push(flat);
});
});
unfoldFlats.forEach((unfoldFlatsPage) => {
unfoldFlatsPage.sort((a, b) => {
if (a.yTop < b.yTop)
return -1;
if (a.yTop > b.yTop)
return 1;
if (a.xLeft > b.xLeft)
return 1;
if (a.xLeft < b.xLeft)
return -1;
return 0;
});
});
let pageIndexModel = 0;
const packs = [];
const pageBot = controller.paperHeight - controller.margins.bot;
unfoldFlats.forEach((unfoldFlatsPage) => {
const tree = new IntervalTree();
let pageIndexShift = 0;
unfoldFlatsPage.forEach((flat) => {
let { pageIndex, yBot, absBot } = PagePacker.findBotInterval(tree, flat.xLeft, flat.xRight, controller);
const height = flat.height;
flat.yTop = yBot + flat.yTop - absBot;
if (Math.abs(flat.yTop - controller.margins.top) > SurveyHelper.EPSILON &&
flat.yTop + height > pageBot + SurveyHelper.EPSILON || flat.isPageBreak) {
flat.yTop = controller.margins.top;
pageIndex++;
pageIndexShift = Math.max(pageIndexShift, pageIndex);
}
tree.insert(flat.xLeft, flat.xRight, {
pageIndex: pageIndex,
xLeft: flat.xLeft, xRight: flat.xRight,
yBot: flat.yTop + height, absBot: flat.yBot
});
flat.yBot = flat.yTop + height;
PagePacker.addPack(packs, pageIndexModel + pageIndex, flat);
});
pageIndexModel += pageIndexShift + 1;
});
return packs;
}
}
/**
* Horizontal alignment types in onRenderHeader and onRenderFooter events
*/
var HorizontalAlign;
(function (HorizontalAlign) {
HorizontalAlign["NotSet"] = "notset";
HorizontalAlign["Left"] = "left";
HorizontalAlign["Center"] = "center";
HorizontalAlign["Right"] = "right";
})(HorizontalAlign || (HorizontalAlign = {}));
/**
* Vertical alignment types in onRenderHeader and onRenderFooter events
*/
var VerticalAlign;
(function (VerticalAlign) {
VerticalAlign["NotSet"] = "notset";
VerticalAlign["Top"] = "top";
VerticalAlign["Middle"] = "middle";
VerticalAlign["Bottom"] = "bottom";
})(VerticalAlign || (VerticalAlign = {}));
/**
* An object that describes a drawing area and enables you to draw an image or a piece of text within the area. You can access this object within functions that handle `SurveyPDF`'s [`onRenderHeader`](https://surveyjs.io/pdf-generator/documentation/api-reference/surveypdf#onRenderHeader) and [`onRenderFooter`](https://surveyjs.io/pdf-generator/documentation/api-reference/surveypdf#onRenderFooter) events.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/ (linkStyle))
*/
class DrawCanvas {
constructor(packs, controller, _rect, _countPages, _pageNumber) {
this.packs = packs;
this.controller = controller;
this._rect = _rect;
this._countPages = _countPages;
this._pageNumber = _pageNumber;
}
/**
* A total number of pages in the document.
*/
get pageCount() {
return this._countPages;
}
get countPages() {
return this._countPages;
}
/**
* The number of the page that contains the drawing area. Enumeration starts with 1.
*/
get pageNumber() {
return this._pageNumber;
}
/**
* An object with coordinates of a rectangle that limits the drawing area. This object contain the following fields: `xLeft`, `xRight`, `yTop`, `yBot`.
*/
get rect() {
return this._rect;
}
alignRect(rectOptions, itemSize) {
if (typeof rectOptions.margins === 'undefined') {
rectOptions.margins = { left: 0.0, right: 0.0, top: 0.0, bot: 0.0 };
}
else {
if (typeof rectOptions.margins.left === 'undefined') {
rectOptions.margins.left = 0.0;
}
if (typeof rectOptions.margins.right === 'undefined') {
rectOptions.margins.right = 0.0;
}
if (typeof rectOptions.margins.top === 'undefined') {
rectOptions.margins.top = 0.0;
}
if (typeof rectOptions.margins.bot === 'undefined') {
rectOptions.margins.bot = 0.0;
}
}
if (typeof rectOptions.rect === 'undefined') {
if (typeof rectOptions.horizontalAlign === 'undefined' ||
rectOptions.horizontalAlign === HorizontalAlign.NotSet) {
rectOptions.horizontalAlign = HorizontalAlign.Center;
}
if (typeof rectOptions.verticalAlign === 'undefined' ||
rectOptions.verticalAlign === VerticalAlign.NotSet) {
rectOptions.verticalAlign = VerticalAlign.Middle;
}
}
const rect = SurveyHelper.clone(this.rect);
if (typeof rectOptions.horizontalAlign !== 'undefined') {
switch (rectOptions.horizontalAlign) {
case HorizontalAlign.Left:
rect.xLeft = this.rect.xLeft + rectOptions.margins.left;
rect.xRight = Math.min(this.rect.xRight - rectOptions.margins.right, this.rect.xLeft + rectOptions.margins.left + itemSize.width);
break;
case HorizontalAlign.Center:
rect.xLeft = Math.max(this.rect.xLeft + rectOptions.margins.left, (this.rect.xLeft + this.rect.xRight - itemSize.width) / 2.0);
rect.xRight = Math.min(this.rect.xRight - rectOptions.margins.right, (this.rect.xLeft + this.rect.xRight + itemSize.width) / 2.0);
break;
case HorizontalAlign.Right:
rect.xLeft = Math.max(this.rect.xLeft + rectOptions.margins.left, this.rect.xRight - rectOptions.margins.right - itemSize.width);
rect.xRight = this.rect.xRight - rectOptions.margins.right;
break;
}
}
else {
rect.xLeft = rectOptions.rect.xLeft || this.rect.xLeft;
rect.xRight = rectOptions.rect.xRight || this.rect.xRight;
}
if (typeof rectOptions.verticalAlign !== 'undefined') {
switch (rectOptions.verticalAlign) {
case VerticalAlign.Top:
rect.yTop = this.rect.yTop + rectOptions.margins.top;
rect.yBot = Math.min(this.rect.yBot - rectOptions.margins.bot, this.rect.yTop + rectOptions.margins.top + itemSize.height);
break;
case VerticalAlign.Middle:
rect.yTop = Math.max(this.rect.yTop + rectOptions.margins.top, (this.rect.yTop + this.rect.yBot - itemSize.height) / 2.0);
rect.yBot = Math.min(this.rect.yBot - rectOptions.margins.bot, (this.rect.yTop + this.rect.yBot + itemSize.height) / 2.0);
break;
case VerticalAlign.Bottom:
rect.yTop = Math.max(this.rect.yTop + rectOptions.margins.top, this.rect.yBot - rectOptions.margins.bot - itemSize.height);
rect.yBot = this.rect.yBot - rectOptions.margins.bot;
break;
}
}
else {
rect.yTop = rectOptions.rect.yTop || this.rect.yTop;
rect.yBot = rectOptions.rect.yBot || this.rect.yBot;
}
return rect;
}
/**
* Draws a piece of text within the drawing area.
* @param textOptions An [`IDrawTextOptions`](https://surveyjs.io/pdf-generator/documentation/api-reference/idrawtextoptions) object that configures the drawing.
*/
drawText(textOptions) {
textOptions = SurveyHelper.clone(textOptions);
if (typeof textOptions.fontSize === 'undefined') {
textOptions.fontSize = DocOptions.FONT_SIZE;
}
if (typeof textOptions.isBold === 'undefined') {
textOptions.isBold = false;
}
const textSize = this.controller.measureText(textOptions.text, textOptions.isBold ? 'bold' : 'normal', textOptions.fontSize);
const textRect = this.alignRect(textOptions, textSize);
if (!textOptions.isBold) {
this.packs.push(new TextBrick(null, this.controller, textRect, textOptions.text));
}
else {
this.packs.push(new TextBoldBrick(null, this.controller, textRect, textOptions.text));
}
this.packs[this.packs.length - 1].fontSize = textOptions.fontSize;
}
/**
* Draws an image within the drawing area.
* @param imageOptions An [`IDrawImageOptions`](https://surveyjs.io/pdf-generator/documentation/api-reference/idrawimageoptions) object that configures drawing.
*/
drawImage(imageOptions) {
return __awaiter(this, void 0, void 0, function* () {
imageOptions = SurveyHelper.clone(imageOptions);
if (typeof imageOptions.width === 'undefined') {
imageOptions.width = this.rect.xRight - this.rect.xLeft;
}
if (typeof imageOptions.height === 'undefined') {
imageOptions.height = this.rect.yBot - this.rect.yTop;
}
const imageSize = {
width: imageOptions.width,
height: imageOptions.height
};
const imageRect = this.alignRect(imageOptions, imageSize);
this.controller.pushMargins(0, 0);
this.packs.push(yield SurveyHelper.createImageFlat(SurveyHelper.createPoint(imageRect, true, true), null, this.controller, { link: imageOptions.base64,
width: imageRect.xRight - imageRect.xLeft,
height: imageRect.yBot - imageRect.yTop, objectFit: imageOptions.imageFit }, !!imageOptions.imageFit || this.controller.applyImageFit));
this.controller.popMargins();
});
}
}
class EventAsync extends EventBase {
unshift(func) {
if (this.hasFunc(func))
return;
if (this.callbacks == null) {
this.callbacks = new Array();
}
this.callbacks.unshift(func);
}
fire(sender, options) {
return __awaiter(this, void 0, void 0, function* () {
if (this.callbacks == null)
return;
for (var i = 0; i < this.callbacks.length; i++) {
yield this.callbacks[i](sender, options);
}
});
}
}
class EventHandler {
static process_header_events(survey, controller, packs) {
return __awaiter(this, void 0, void 0, function* () {
if (!survey.haveCommercialLicense) {
survey.onRenderHeader.add((_, canvas) => {
canvas.drawText({
text: 'SurveyJS PDF | Please purchase a SurveyJS PDF developer license to use it in your app | https://surveyjs.io/Buy',
fontSize: 10
});
});
}
for (let i = 0; i < packs.length; i++) {
yield survey.onRenderHeader.fire(survey, new DrawCanvas(packs[i], controller, SurveyHelper.createHeaderRect(controller), packs.length, i + 1));
yield survey.onRenderFooter.fire(survey, new DrawCanvas(packs[i], controller, SurveyHelper.createFooterRect(controller), packs.length, i + 1));
}
});
}
}
/**
* The `SurveyPDF` object enables you to export your surveys and forms to PDF documents.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/ (linkStyle))
*/
class SurveyPDF extends SurveyModel {
constructor(jsonObject, options) {
super(jsonObject);
/**
* An event that is raised when SurveyJS PDF Generator renders a page header. Handle this event to customize the header.
*
* Parameters:
*
* - `sender`: `SurveyPDF`\
* A SurveyPDF instance that raised the event.
*
* - `canvas`: [`DrawCanvas`](https://surveyjs.io/pdf-generator/documentation/api-reference/drawcanvas)\
* An object that you can use to draw text and images in the page header.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/ (linkStyle))
*/
this.onRenderHeader = new EventAsync();
/**
* An event that is raised when SurveyJS PDF Generator renders a page footer. Handle this event to customize the footer.
*
* Parameters:
*
* - `sender`: `SurveyPDF`\
* A SurveyPDF instance that raised the event.
*
* - `canvas`: [`DrawCanvas`](https://surveyjs.io/pdf-generator/documentation/api-reference/drawcanvas)\
* An object that you can use to draw text and images in the page footer.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/ (linkStyle))
*/
this.onRenderFooter = new EventAsync();
/**
* An event that is raised when SurveyJS PDF Generator renders a survey question. Handle this event to customize question rendering.
*
* Parameters:
*
* - `sender`: `SurveyPDF`\
* A SurveyPDF instance that raised the event.
*
* - `options.question`: [`Question`](https://surveyjs.io/form-library/documentation/api-reference/question)\
* A survey question that is being rendered.
*
* - `options.point`: `IPoint`\
* An object with coordinates of the top-left corner of the element being rendered. This object contains the following properties: `{ xLeft: number, yTop: number }`.
*
* - `options.bricks`: [`PdfBrick[]`](https://surveyjs.io/pdf-generator/documentation/api-reference/pdfbrick)\
* An array of [bricks](https://surveyjs.io/pdf-generator/documentation/customize-survey-question-rendering-in-pdf-form#custom-rendering) used to render the element.
*
* - `options.controller`: [`DocController`](https://surveyjs.io/pdf-generator/documentation/api-reference/doccontroller)\
* An object that provides access to main PDF document properties (font, margins, page width and height) and allows you to modify them.
*
* - `options.repository`: `FlatRepository`\
* A repository with classes that render elements to PDF. Use its `create` method if you need to create a new instance of a rendering class.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/how-to-use-adorners-in-pdf-forms/ (linkStyle))
*/
this.onRenderQuestion = new EventAsync();
/**
* An event that is raised when SurveyJS PDF Generator renders a panel. Handle this event to customize panel rendering.
*
* Parameters:
*
* - `sender`: `SurveyPDF`\
* A SurveyPDF instance that raised the event.
*
* - `options.panel`: [`PanelModel`](https://surveyjs.io/form-library/documentation/api-reference/panel-model)\
* A panel that is being rendered.
*
* - `options.point`: `IPoint`\
* An object with coordinates of the top-left corner of the element being rendered. This object contains the following properties: `{ xLeft: number, yTop: number }`.
*
* - `options.bricks`: [`PdfBrick[]`](https://surveyjs.io/pdf-generator/documentation/api-reference/pdfbrick)\
* An array of [bricks](https://surveyjs.io/pdf-generator/documentation/customize-survey-question-rendering-in-pdf-form#custom-rendering) used to render the element.
*
* - `options.controller`: [`DocController`](https://surveyjs.io/pdf-generator/documentation/api-reference/doccontroller)\
* An object that provides access to main PDF document properties (font, margins, page width and height) and allows you to modify them.
*
* - `options.repository`: `FlatRepository`\
* A repository with classes that render elements to PDF. Use its `create` method if you need to create a new instance of a rendering class.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/how-to-use-adorners-in-pdf-forms/ (linkStyle))
*/
this.onRenderPanel = new EventAsync();
/**
* An event that is raised when SurveyJS PDF Generator renders a page. Handle this event to customize page rendering.
*
* Parameters:
*
* - `sender`: `SurveyPDF`\
* A SurveyPDF instance that raised the event.
*
* - `options.page`: [`PageModel`](https://surveyjs.io/form-library/documentation/api-reference/page-model)\
* A page that is being rendered.
*
* - `options.point`: `IPoint`\
* An object with coordinates of the top-left corner of the element being rendered. This object contains the following properties: `{ xLeft: number, yTop: number }`.
*
* - `options.bricks`: [`PdfBrick[]`](https://surveyjs.io/pdf-generator/documentation/api-reference/pdfbrick)\
* An array of [bricks](https://surveyjs.io/pdf-generator/documentation/customize-survey-question-rendering-in-pdf-form#custom-rendering) used to render the element.
*
* - `options.controller`: [`DocController`](https://surveyjs.io/pdf-generator/documentation/api-reference/doccontroller)\
* An object that provides access to main PDF document properties (font, margins, page width and height) and allows you to modify them.
*
* - `options.repository`: `FlatRepository`\
* A repository with classes that render elements to PDF. Use its `create` method if you need to create a new instance of a rendering class.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/how-to-use-adorners-in-pdf-forms/ (linkStyle))
*/
this.onRenderPage = new EventAsync();
this.onDocControllerCreated = new EventBase();
this.onRenderCheckItemAcroform = new EventAsync();
this.onRenderRadioGroupWrapAcroform = new EventAsync();
this.onRenderRadioItemAcroform = new EventAsync();
if (typeof options === 'undefined') {
options = {};
}
this.options = SurveyHelper.clone(options);
}
get haveCommercialLicense() {
const f = SurveyCore.hasLicense;
return !!f && f(2);
}
set haveCommercialLicense(val) {
// eslint-disable-next-line no-console
console.error('As of v1.9.101, the haveCommercialLicense property is not supported. To activate your license, use the setLicenseKey(key) method as shown on the following page: https://surveyjs.io/remove-alert-banner');
}
ensureQuestionDisplayValue(question) {
question.displayValue;
}
waitForQuestionIsReady(question) {
return new Promise((resolve) => {
this.ensureQuestionDisplayValue(question);
if (question.isReady) {
resolve();
}
else {
const readyCallback = (_, options) => {
if (options.isReady) {
question.onReadyChanged.remove(readyCallback);
resolve();
}
};
question.onReadyChanged.add(readyCallback);
}
});
}
waitForCoreIsReady() {
return __awaiter(this, void 0, void 0, function* () {
for (const question of this.getAllQuestions()) {
if (!!question.contentPanel) {
const list = [];
question.contentPanel.addQuestionsToList(list);
for (const innerQuestion of list) {
yield this.waitForQuestionIsReady(innerQuestion);
}
continue;
}
else
yield this.waitForQuestionIsReady(SurveyHelper.getContentQuestion(question));
}
});
}
getUpdatedCheckItemAcroformOptions(options) {
this.onRenderCheckItemAcroform.fire(this, options);
}
getUpdatedRadioGroupWrapOptions(options) {
this.onRenderRadioGroupWrapAcroform.fire(this, options);
}
getUpdatedRadioItemAcroformOptions(options) {
this.onRenderRadioItemAcroform.fire(this, options);
}
correctBricksPosition(controller, flats) {
if (controller.isRTL) {
flats.forEach(flatsArr => {
flatsArr.forEach(flat => {
flat.translateX((xLeft, xRight) => {
const shiftWidth = controller.paperWidth - xLeft - xRight;
return { xLeft: xLeft + shiftWidth, xRight: xRight + shiftWidth };
});
});
});
}
}
renderSurvey(controller) {
return __awaiter(this, void 0, void 0, function* () {
this.visiblePages.forEach(page => page.onFirstRendering());
yield this.waitForCoreIsReady();
const flats = yield FlatSurvey.generateFlats(this, controller);
this.correctBricksPosition(controller, flats);
const packs = PagePacker.pack(flats, controller);
yield EventHandler.process_header_events(this, controller, packs);
for (let i = 0; i < packs.length; i++) {
for (let j = 0; j < packs[i].length; j++) {
if (controller.getNumberOfPages() === i) {
controller.addPage();
}
controller.setPage(i);
//gizmos bricks borders for debug
// packs[i][j].unfold().forEach((rect: IPdfBrick) => {
// controller.doc.setDrawColor('green');
// controller.doc.rect(...SurveyHelper.createAcroformRect(rect));
// controller.doc.setDrawColor('black');
// }
// );
yield packs[i][j].render();
}
}
});
}
/**
* An asynchronous method that starts download of the generated PDF file in the web browser.
*
* @param fileName *(Optional)* A file name with the ".pdf" extension. Default value: `"survey_result.pdf"`.
*/
save() {
return __awaiter(this, arguments, void 0, function* (fileName = 'survey_result.pdf') {
if (!SurveyPDF.currentlySaving) {
const controller = new DocController(this.options);
this.onDocControllerCreated.fire(this, { controller: controller });
SurveyPDF.currentlySaving = true;
SurveyHelper.fixFont(controller);
yield this.renderSurvey(controller);
const promise = controller.doc.save(fileName, { returnPromise: true });
promise.then(() => {
SurveyPDF.currentlySaving = false;
const saveFunc = SurveyPDF.saveQueue.shift();
if (!!saveFunc) {
saveFunc();
}
});
return promise;
}
else {
SurveyPDF.saveQueue.push(() => {
this.save(fileName);
});
}
});
}
/**
* An asynchronous method that allows you to get PDF content in different formats.
*
* [View Demo](https://surveyjs.io/pdf-generator/examples/convert-pdf-form-blob-base64-raw-pdf-javascript/ (linkStyle))
*
* @param type *(Optional)* One of `"blob"`, `"bloburl"`, `"dataurlstring"`. Do not specify this parameter if you want to get raw PDF content as a string value.
*
*/
raw(type) {
return __awaiter(this, void 0, void 0, function* () {
const controller = new DocController(this.options);
this.onDocControllerCreated.fire(this, { controller: controller });
SurveyHelper.fixFont(controller);
yield this.renderSurvey(controller);
return controller.doc.output(type);
});
}
}
SurveyPDF.currentlySaving = false;
SurveyPDF.saveQueue = [];
class CheckItemBrick extends PdfBrick {
constructor(controller, rect, fieldName, context) {
super(context.question, controller, rect);
this.fieldName = fieldName;
this.context = context;
this.question = this.context.question;
this.textColor = this.formBorderColor;
}
getShouldRenderReadOnly() {
return this.context.readOnly && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress;
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const checkBox = new this.controller.doc.AcroFormCheckBox();
const formScale = SurveyHelper.formScale(this.controller, this);
const options = {};
options.maxFontSize = this.height * formScale * CheckItemBrick.FONT_SIZE_SCALE;
options.caption = CheckItemBrick.CHECKMARK_READONLY_SYMBOL;
options.textAlign = 'center';
options.fieldName = this.fieldName;
options.readOnly = this.context.readOnly;
options.color = this.formBorderColor;
options.value = this.context.checked ? 'On' : false;
options.AS = this.context.checked ? '/On' : '/Off';
options.context = this.context;
options.Rect = SurveyHelper.createAcroformRect(SurveyHelper.scaleRect(this, formScale));
this.controller.doc.addField(checkBox);
(_a = this.question.survey) === null || _a === void 0 ? void 0 : _a.getUpdatedCheckItemAcroformOptions(options);
checkBox.maxFontSize = options.maxFontSize;
checkBox.caption = options.caption;
checkBox.textAlign = options.textAlign;
checkBox.fieldName = options.fieldName;
checkBox.readOnly = options.readOnly;
checkBox.color = options.color;
checkBox.value = options.value;
checkBox.AS = options.AS;
checkBox.Rect = options.Rect;
SurveyHelper.renderFlatBorders(this.controller, this);
});
}
renderReadOnly() {
return __awaiter(this, void 0, void 0, function* () {
SurveyHelper.renderFlatBorders(this.controller, this);
if (this.context.checked) {
const checkmarkPoint = SurveyHelper.createPoint(this, true, true);
const oldFontName = this.controller.fontName;
this.controller.fontName = CheckItemBrick.CHECKMARK_READONLY_FONT;
const oldFontSize = this.controller.fontSize;
this.controller.fontSize = oldFontSize *
CheckItemBrick.CHECKMARK_READONLY_FONT_SIZE_SCALE;
const checkmarkSize = this.controller.measureText(CheckItemBrick.CHECKMARK_READONLY_SYMBOL);
checkmarkPoint.xLeft += this.width / 2.0 - checkmarkSize.width / 2.0;
checkmarkPoint.yTop += this.height / 2.0 - checkmarkSize.height / 2.0;
const checkmarkFlat = yield SurveyHelper.createTextFlat(checkmarkPoint, this.question, this.controller, CheckItemBrick.CHECKMARK_READONLY_SYMBOL, TextBrick);
checkmarkFlat.unfold()[0].textColor = this.textColor;
this.controller.fontSize = oldFontSize;
yield checkmarkFlat.render();
this.controller.fontName = oldFontName;
}
});
}
}
CheckItemBrick.FONT_SIZE_SCALE = 0.7;
CheckItemBrick.CHECKMARK_READONLY_SYMBOL = '3';
CheckItemBrick.CHECKMARK_READONLY_FONT = 'zapfdingbats';
CheckItemBrick.CHECKMARK_READONLY_FONT_SIZE_SCALE = 1.0 - Math.E / 10.0;
class BooleanItemBrick extends CheckItemBrick {
constructor(question, controller, rect) {
super(controller, rect, question.id, { question: question, readOnly: question.isReadOnly, checked: question.checkedValue });
}
}
class RadioGroupWrap {
constructor(name, controller, context) {
this.name = name;
this.controller = controller;
this.context = context;
}
addToPdf(color) {
var _a;
this._radioGroup = new this.controller.doc.AcroFormRadioButton();
const options = {};
options.fieldName = this.name;
options.readOnly = this.readOnly;
options.color = color;
options.context = this.context;
(_a = this.context.question.survey) === null || _a === void 0 ? void 0 : _a.getUpdatedRadioGroupWrapOptions(options);
this._radioGroup.fieldName = options.fieldName;
this._radioGroup.readOnly = options.readOnly;
this._radioGroup.color = options.color;
this._radioGroup.value = '';
this.controller.doc.addField(this._radioGroup);
}
get radioGroup() {
return this._radioGroup;
}
get readOnly() {
return this.context.readOnly;
}
}
class RadioItemBrick extends PdfBrick {
constructor(controller, rect, context, radioGroupWrap) {
super(context.question, controller, rect);
this.context = context;
this.radioGroupWrap = radioGroupWrap;
this.textColor = this.formBorderColor;
}
getShouldRenderReadOnly() {
return this.radioGroupWrap.readOnly && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress;
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (this.context.index == 0) {
this.radioGroupWrap.addToPdf(this.formBorderColor);
}
const options = {};
options.fieldName = this.radioGroupWrap.name + 'index' + this.context.index;
let formScale = SurveyHelper.formScale(this.controller, this);
options.Rect = SurveyHelper.createAcroformRect(SurveyHelper.scaleRect(this, formScale));
options.color = this.formBorderColor;
options.appearance = this.controller.doc.AcroForm.Appearance.RadioButton.Circle;
options.radioGroup = this.radioGroupWrap.radioGroup;
options.context = this.context;
(_a = this.context.question.survey) === null || _a === void 0 ? void 0 : _a.getUpdatedRadioItemAcroformOptions(options);
let radioButton = this.radioGroupWrap.radioGroup.createOption(options.fieldName);
if (this.context.checked) {
if (!options.AS) {
radioButton.AS = '/' + options.fieldName;
}
if (!this.radioGroupWrap.radioGroup.value) {
this.radioGroupWrap.radioGroup.value = options.fieldName;
}
}
else {
if (!options.AS) {
options.AS = '/Off';
}
}
radioButton.Rect = options.Rect;
radioButton.color = options.color;
SurveyHelper.renderFlatBorders(this.controller, this);
this.radioGroupWrap.radioGroup.setAppearance(options.appearance);
});
}
renderReadOnly() {
return __awaiter(this, void 0, void 0, function* () {
SurveyHelper.renderFlatBorders(this.controller, this);
if (this.context.checked) {
const radiomarkerPoint = SurveyHelper.createPoint(this, true, true);
const oldFontSize = this.controller.fontSize;
const oldFontName = this.controller.fontName;
this.controller.fontName = RadioItemBrick.RADIOMARKER_READONLY_FONT;
this.controller.fontSize = oldFontSize *
RadioItemBrick.RADIOMARKER_READONLY_FONT_SIZE_SCALE;
let radiomarkerSize = this.controller.measureText(RadioItemBrick.RADIOMARKER_READONLY_SYMBOL);
radiomarkerPoint.xLeft += this.width / 2.0 - radiomarkerSize.width / 2.0;
radiomarkerPoint.yTop += this.height / 2.0 - radiomarkerSize.height / 2.0;
let radiomarkerFlat = yield SurveyHelper.createTextFlat(radiomarkerPoint, this.question, this.controller, RadioItemBrick.RADIOMARKER_READONLY_SYMBOL, TextBrick);
radiomarkerFlat.unfold()[0].textColor = this.textColor;
yield radiomarkerFlat.render();
this.controller.fontSize = oldFontSize;
this.controller.fontName = oldFontName;
}
});
}
}
RadioItemBrick.RADIOMARKER_READONLY_SYMBOL = 'l';
RadioItemBrick.RADIOMARKER_READONLY_FONT = 'zapfdingbats';
RadioItemBrick.RADIOMARKER_READONLY_FONT_SIZE_SCALE = 1.0 - ((2.0 + Math.E) / 10.0);
class FlatSelectBase extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
generateFlatComposite(point, item, index) {
return __awaiter(this, void 0, void 0, function* () {
const compositeFlat = new CompositeBrick();
const itemRect = SurveyHelper.createRect(point, this.controller.unitWidth, this.controller.unitHeight);
const itemFlat = this.generateFlatItem(SurveyHelper.moveRect(SurveyHelper.scaleRect(itemRect, SurveyHelper.SELECT_ITEM_FLAT_SCALE), point.xLeft), item, index);
compositeFlat.addBrick(itemFlat);
const textPoint = SurveyHelper.clone(point);
textPoint.xLeft = itemFlat.xRight + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_ITEM_TEXT;
if (item.locText.renderedHtml !== null) {
compositeFlat.addBrick(yield SurveyHelper.createTextFlat(textPoint, this.question, this.controller, item.locText, TextBrick));
}
if (item === this.question.otherItem && (item.value === this.question.value ||
(typeof this.question.isOtherSelected !== 'undefined' && this.question.isOtherSelected))) {
const otherPoint = SurveyHelper.createPoint(compositeFlat);
otherPoint.yTop += this.controller.unitHeight * SurveyHelper.GAP_BETWEEN_ROWS;
compositeFlat.addBrick(yield SurveyHelper.createCommentFlat(otherPoint, this.question, this.controller, false, { index, rows: SurveyHelper.OTHER_ROWS_COUNT }));
}
return compositeFlat;
});
}
getVisibleChoices() {
return this.question.visibleChoices;
}
getColCount() {
return this.question.colCount;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
const colCount = this.getColCount();
const visibleChoices = this.getVisibleChoices();
let currentColCount = colCount;
if (colCount == 0) {
currentColCount = Math.floor(SurveyHelper.getPageAvailableWidth(this.controller)
/ this.controller.measureText(SurveyHelper.MATRIX_COLUMN_WIDTH).width) || 1;
if (visibleChoices.length < currentColCount) {
currentColCount = visibleChoices.length;
}
}
else if (colCount > 1) {
currentColCount = (SurveyHelper.getColumnWidth(this.controller, colCount) <
this.controller.measureText(SurveyHelper.MATRIX_COLUMN_WIDTH).width) ? 1 : colCount;
}
return (currentColCount == 1) ? yield this.generateVerticallyItems(point, visibleChoices) :
yield this.generateHorisontallyItems(point, currentColCount);
});
}
generateVerticallyItems(point, itemValues) {
return __awaiter(this, void 0, void 0, function* () {
const currPoint = SurveyHelper.clone(point);
const flats = [];
for (let i = 0; i < itemValues.length; i++) {
const itemFlat = yield this.generateFlatComposite(currPoint, itemValues[i], i);
currPoint.yTop = itemFlat.yBot + SurveyHelper.GAP_BETWEEN_ROWS * this.controller.unitHeight;
flats.push(itemFlat);
}
return flats;
});
}
generateHorisontallyItems(point, colCount) {
return __awaiter(this, void 0, void 0, function* () {
const visibleChoices = this.getVisibleChoices();
const currPoint = SurveyHelper.clone(point);
const flats = [];
let row = new CompositeBrick();
for (let i = 0; i < visibleChoices.length; i++) {
this.controller.pushMargins(this.controller.margins.left, this.controller.margins.right);
SurveyHelper.setColumnMargins(this.controller, colCount, i % colCount);
currPoint.xLeft = this.controller.margins.left;
const itemFlat = yield this.generateFlatComposite(currPoint, visibleChoices[i], i);
row.addBrick(itemFlat);
this.controller.popMargins();
if (i % colCount === colCount - 1 || i === visibleChoices.length - 1) {
const rowLineFlat = SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(row), this.controller);
currPoint.yTop = rowLineFlat.yBot +
SurveyHelper.GAP_BETWEEN_ROWS * this.controller.unitHeight;
flats.push(row, rowLineFlat);
row = new CompositeBrick();
}
}
return flats;
});
}
}
class FlatRadiogroup extends FlatSelectBase {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
isItemSelected(item, checked) {
return (typeof checked === 'undefined') ?
(item === this.question.otherItem ? this.question.isOtherSelected :
(item.value === this.question.value ||
(typeof this.question.isItemSelected !== 'undefined' &&
this.question.isItemSelected(item)))) : checked;
}
generateFlatItem(rect, item, index, key, checked, context = {}) {
if (index === 0) {
this.radioGroupWrap = new RadioGroupWrap(this.question.id + ((typeof key === 'undefined') ? '' : key), this.controller, Object.assign({ readOnly: this.question.isReadOnly, question: this.question }, context));
this.question.pdfRadioGroupWrap = this.radioGroupWrap;
}
else if (typeof this.radioGroupWrap === 'undefined') {
this.radioGroupWrap = this.question.pdfRadioGroupWrap;
}
const isChecked = this.isItemSelected(item, checked);
return new RadioItemBrick(this.controller, rect, { question: this.question, index: index, checked: isChecked, item: item }, this.radioGroupWrap);
}
}
FlatRepository.getInstance().register('radiogroup', FlatRadiogroup);
FlatRepository.getInstance().register('buttongroup', FlatRadiogroup);
class FlatBooleanCheckbox extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
const compositeFlat = new CompositeBrick();
const height = this.controller.unitHeight;
const itemFlat = new BooleanItemBrick(this.question, this.controller, SurveyHelper.moveRect(SurveyHelper.scaleRect(SurveyHelper.createRect(point, height, height), SurveyHelper.SELECT_ITEM_FLAT_SCALE), point.xLeft));
compositeFlat.addBrick(itemFlat);
const textPoint = SurveyHelper.clone(point);
textPoint.xLeft = itemFlat.xRight + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_ITEM_TEXT;
const locLabelText = this.question.isIndeterminate ? null :
this.question.checkedValue ? this.question.locLabelTrue : this.question.locLabelFalse;
if (locLabelText !== null && locLabelText.renderedHtml !== null) {
compositeFlat.addBrick(yield SurveyHelper.createTextFlat(textPoint, this.question, this.controller, locLabelText, TextBrick));
}
return [compositeFlat];
});
}
}
class FlatBoolean extends FlatRadiogroup {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.buildItems();
}
buildItems() {
const question = this.question;
const falseChoice = new ItemValue(question.valueFalse !== undefined ? question.valueFalse : false);
const trueChoice = new ItemValue(question.valueTrue !== undefined ? question.valueTrue : true);
falseChoice.locOwner = question;
falseChoice.setLocText(question.locLabelFalse);
trueChoice.locOwner = question;
trueChoice.setLocText(question.locLabelTrue);
this.items = [falseChoice, trueChoice];
}
getVisibleChoices() {
return this.items;
}
getColCount() {
return 0;
}
}
FlatRepository.getInstance().register('boolean', FlatBoolean);
FlatRepository.getInstance().register('boolean-checkbox', FlatBooleanCheckbox);
class CheckboxItemBrick extends CheckItemBrick {
constructor(question, controller, rect, item, index) {
super(controller, rect, question.id + 'index' + index, { question: question, readOnly: question.isReadOnly || !item.isEnabled, item: item, checked: question.isItemSelected(item), index: index });
}
}
class FlatCheckbox extends FlatSelectBase {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
generateFlatItem(rect, item, index) {
return new CheckboxItemBrick(this.question, this.controller, rect, item, index);
}
}
class FlatTagbox extends FlatCheckbox {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
getVisibleChoices() {
if (this.controller.tagboxSelectedChoicesOnly) {
return this.question.selectedChoices;
}
else {
return super.getVisibleChoices();
}
}
}
FlatRepository.getInstance().register('tagbox', FlatTagbox);
FlatRepository.getInstance().register('checkbox', FlatCheckbox);
class FlatCustomModel extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
const flat = FlatRepository.getInstance().create(this.survey, this.question, this.controller, this.question.getType());
return flat.generateFlatsContent(point);
});
}
}
FlatRepository.getInstance().register('custom_model', FlatCustomModel);
class FlatComment extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
return [yield SurveyHelper.createCommentFlat(point, this.question, this.controller, true, { rows: this.question.rows })];
});
}
}
FlatRepository.getInstance().register('comment', FlatComment);
class DropdownBrick extends PdfBrick {
constructor(question, controller, rect) {
super(question, controller, rect);
this.controller = controller;
this.question = question;
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
const comboBox = new this.controller.doc.AcroFormComboBox();
comboBox.fieldName = this.question.id;
comboBox.Rect = SurveyHelper.createAcroformRect(SurveyHelper.scaleRect(this, SurveyHelper.formScale(this.controller, this)));
comboBox.edit = false;
comboBox.color = this.textColor;
const options = [];
if (this.question.showOptionsCaption) {
options.push(this.getCorrectedText(this.question.optionsCaption));
}
this.question.visibleChoices.forEach((item) => {
options.push(this.getCorrectedText(SurveyHelper.getLocString(item.locText)));
});
comboBox.setOptions(options);
comboBox.fontName = this.controller.fontName;
comboBox.fontSize = this.fontSize;
comboBox.readOnly = this.question.isReadOnly;
comboBox.isUnicode = SurveyHelper.isCustomFont(this.controller, comboBox.fontName);
comboBox.V = this.getCorrectedText(SurveyHelper.getDropdownQuestionValue(this.question));
this.controller.doc.addField(comboBox);
SurveyHelper.renderFlatBorders(this.controller, this);
});
}
}
class FlatDropdown extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
const valueBrick = !this.shouldRenderAsComment ? new DropdownBrick(this.question, this.controller, SurveyHelper.createTextFieldRect(point, this.controller)) : yield SurveyHelper.createCommentFlat(point, this.question, this.controller, true, { value: SurveyHelper.getDropdownQuestionValue(this.question) });
const compositeFlat = new CompositeBrick(valueBrick);
if (this.question.isOtherSelected) {
const otherPoint = SurveyHelper.createPoint(compositeFlat);
otherPoint.yTop += this.controller.unitHeight * SurveyHelper.GAP_BETWEEN_ROWS;
compositeFlat.addBrick(yield SurveyHelper.createCommentFlat(otherPoint, this.question, this.controller, false, { rows: SurveyHelper.OTHER_ROWS_COUNT }));
}
return [compositeFlat];
});
}
}
FlatRepository.getInstance().register('dropdown', FlatDropdown);
class FlatExpression extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.question = question;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
return [yield SurveyHelper.createCommentFlat(point, this.question, this.controller, true, { value: this.question.displayValue, readOnly: true })];
});
}
}
FlatRepository.getInstance().register('expression', FlatExpression);
class FlatFile extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.question = question;
}
generateFlatItem(point, item) {
return __awaiter(this, void 0, void 0, function* () {
const compositeFlat = new CompositeBrick(yield SurveyHelper.createLinkFlat(point, this.question, this.controller, item.name === undefined ? 'image' : item.name, item.content));
if (SurveyHelper.canPreviewImage(this.question, item, item.content)) {
const imagePoint = SurveyHelper.createPoint(compositeFlat);
imagePoint.yTop += this.controller.unitHeight * FlatFile.IMAGE_GAP_SCALE;
compositeFlat.addBrick(yield SurveyHelper.createImageFlat(imagePoint, this.question, this.controller, { link: item.content, width: item.imageSize.width, height: item.imageSize.height, objectFit: FlatFile.DEFAULT_IMAGE_FIT }));
}
return compositeFlat;
});
}
addLine(rowsFlats, currPoint, index) {
if (index !== this.question.previewValue.length - 1) {
rowsFlats[rowsFlats.length - 1].addBrick(SurveyHelper.createRowlineFlat(currPoint, this.controller));
currPoint.yTop += SurveyHelper.EPSILON;
rowsFlats.push(new CompositeBrick());
}
}
getImagePreviewContentWidth(item) {
return __awaiter(this, void 0, void 0, function* () {
return Math.max(item.imageSize.width, FlatFile.TEXT_MIN_SCALE * this.controller.unitWidth);
});
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
if (this.question.previewValue.length === 0) {
return [yield SurveyHelper.createTextFlat(point, this.question, this.controller, this.question.noFileChosenCaption, TextBrick)];
}
const rowsFlats = [new CompositeBrick()];
const currPoint = SurveyHelper.clone(point);
let yBot = currPoint.yTop;
for (let i = 0; i < this.question.previewValue.length; i++) {
let item = Object.assign({}, this.question.previewValue[i]);
const canPreviewImage = SurveyHelper.canPreviewImage(this.question, item, item.content);
if (canPreviewImage) {
item.imageSize = yield SurveyHelper.getCorrectedImageSize(this.controller, { imageWidth: this.question.imageWidth, imageHeight: this.question.imageHeight, imageLink: this.question.previewValue[i].content, defaultImageWidth: 200, defaultImageHeight: 150 });
}
const availableWidth = this.controller.paperWidth -
this.controller.margins.right - currPoint.xLeft;
if (canPreviewImage) {
const compositeWidth = yield this.getImagePreviewContentWidth(item);
if (availableWidth < compositeWidth) {
currPoint.xLeft = point.xLeft;
currPoint.yTop = yBot;
this.addLine(rowsFlats, currPoint, i);
}
this.controller.pushMargins(currPoint.xLeft, this.controller.paperWidth - currPoint.xLeft - compositeWidth);
const itemFlat = yield this.generateFlatItem(currPoint, item);
rowsFlats[rowsFlats.length - 1].addBrick(itemFlat);
currPoint.xLeft += itemFlat.width;
yBot = Math.max(yBot, itemFlat.yBot);
this.controller.popMargins();
}
else {
if (availableWidth < this.controller.unitWidth) {
currPoint.xLeft = point.xLeft;
currPoint.yTop = yBot;
this.addLine(rowsFlats, currPoint, i);
}
const itemFlat = yield this.generateFlatItem(currPoint, item);
rowsFlats[rowsFlats.length - 1].addBrick(itemFlat);
currPoint.xLeft += itemFlat.xRight - itemFlat.xLeft;
yBot = Math.max(yBot, itemFlat.yBot);
}
currPoint.xLeft += this.controller.unitWidth;
}
return rowsFlats;
});
}
}
FlatFile.IMAGE_GAP_SCALE = 0.195;
FlatFile.TEXT_MIN_SCALE = 5.0;
FlatFile.DEFAULT_IMAGE_FIT = 'contain';
FlatRepository.getInstance().register('file', FlatFile);
class FlatHTML extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
}
chooseRender(html) {
if (/<[^>]*style[^<]*>/.test(html) ||
/<[^>]*table[^<]*>/.test(html) ||
/&\w+;/.test(html)) {
return 'image';
}
return 'standard';
}
correctHtml(html) {
FlatHTML.correctHtmlRules.forEach((rule) => {
html = html.replace(rule.searchRegExp, rule.replaceString);
});
return html;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
let renderAs = this.question.renderAs;
if (!SurveyHelper.hasDocument) {
return [new EmptyBrick(SurveyHelper.createRect(point, 0, 0))];
}
if (renderAs === 'auto')
renderAs = this.controller.htmlRenderAs;
if (renderAs === 'auto')
renderAs = this.chooseRender(SurveyHelper.getLocString(this.question.locHtml));
const html = SurveyHelper.createHtmlContainerBlock(SurveyHelper.getLocString(this.question.locHtml), this.controller, renderAs);
if (renderAs === 'image') {
const width = SurveyHelper.getPageAvailableWidth(this.controller);
const { url, aspect } = yield SurveyHelper.htmlToImage(html, width, this.controller);
const height = width / aspect;
return [yield SurveyHelper.createImageFlat(point, this.question, this.controller, { link: url, width, height })];
}
return [SurveyHelper.splitHtmlRect(this.controller, yield SurveyHelper.createHTMLFlat(point, this.question, this.controller, this.correctHtml(html)))];
});
}
}
FlatHTML.correctHtmlRules = [
{ searchRegExp: /(<\/?br\s*?\/?\s*?>\s*){2,}/g, replaceString: '<br>' }
];
Serializer.removeProperty('html', 'renderAs');
Serializer.addProperty('html', {
name: 'renderAs',
default: 'auto',
visible: false,
choices: ['auto', 'standard', 'image']
});
FlatRepository.getInstance().register('html', FlatHTML);
class FlatImage extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.question = question;
}
getCorrectImageSize() {
return __awaiter(this, void 0, void 0, function* () {
return yield SurveyHelper.getCorrectedImageSize(this.controller, { imageWidth: this.question.imageWidth, imageHeight: this.question.imageHeight, imageLink: this.question.imageLink });
});
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
const imageSize = yield this.getCorrectImageSize();
return [yield SurveyHelper.createImageFlat(point, this.question, this.controller, { link: this.question.imageLink, width: imageSize.width, height: imageSize.height })];
});
}
}
FlatRepository.getInstance().register('image', FlatImage);
class FlatImagePicker extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.question = question;
}
generateFlatItem(point, item, index) {
return __awaiter(this, void 0, void 0, function* () {
const pageAvailableWidth = SurveyHelper.getPageAvailableWidth(this.controller);
const imageFlat = yield SurveyHelper.createImageFlat(point, this.question, this.controller, { link: item.imageLink, width: pageAvailableWidth, height: pageAvailableWidth / SurveyHelper.IMAGEPICKER_RATIO });
const compositeFlat = new CompositeBrick(imageFlat);
let buttonPoint = SurveyHelper.createPoint(compositeFlat);
if (this.question.showLabel) {
let labelFlat = yield SurveyHelper.createTextFlat(buttonPoint, this.question, this.controller, item.text || item.value, TextBrick);
compositeFlat.addBrick(labelFlat);
buttonPoint = SurveyHelper.createPoint(labelFlat);
}
const height = this.controller.unitHeight;
const buttonRect = SurveyHelper.createRect(buttonPoint, pageAvailableWidth, height);
if (this.question.multiSelect) {
compositeFlat.addBrick(new CheckItemBrick(this.controller, buttonRect, this.question.id + 'index' + index, { readOnly: this.question.isReadOnly || !item.isEnabled, question: this.question, item: item, checked: this.question.value.indexOf(item.value) !== -1, index: index }));
}
else {
compositeFlat.addBrick(this.radio.generateFlatItem(buttonRect, item, index));
}
return compositeFlat;
});
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
this.radio = this.question.multiSelect ? null :
new FlatRadiogroup(this.survey, this.question, this.controller);
const rowsFlats = [new CompositeBrick()];
const colWidth = SurveyHelper.getImagePickerAvailableWidth(this.controller) / SurveyHelper.IMAGEPICKER_COUNT;
let cols = ~~(SurveyHelper.
getPageAvailableWidth(this.controller) / colWidth) || 1;
const count = this.question.visibleChoices.length;
cols = cols <= count ? cols : count;
const rows = ~~(Math.ceil(count / cols));
const currPoint = SurveyHelper.clone(point);
for (let i = 0; i < rows; i++) {
let yBot = currPoint.yTop;
this.controller.pushMargins();
let currMarginLeft = this.controller.margins.left;
for (let j = 0; j < cols; j++) {
const index = i * cols + j;
if (index == count)
break;
this.controller.margins.left = currMarginLeft;
this.controller.margins.right = this.controller.paperWidth -
currMarginLeft - colWidth;
currMarginLeft = this.controller.paperWidth -
this.controller.margins.right + this.controller.unitWidth;
currPoint.xLeft = this.controller.margins.left;
const itemFlat = yield this.generateFlatItem(currPoint, this.question.visibleChoices[index], index);
rowsFlats[rowsFlats.length - 1].addBrick(itemFlat);
yBot = Math.max(yBot, itemFlat.yBot);
}
this.controller.popMargins();
currPoint.xLeft = point.xLeft;
currPoint.yTop = yBot;
if (i !== rows - 1) {
rowsFlats[rowsFlats.length - 1].addBrick(SurveyHelper.createRowlineFlat(currPoint, this.controller));
currPoint.yTop += SurveyHelper.EPSILON;
rowsFlats.push(new CompositeBrick());
}
}
return rowsFlats;
});
}
}
FlatRepository.getInstance().register('imagepicker', FlatImagePicker);
class FlatPanelDynamic extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
const flats = [];
const currPoint = SurveyHelper.clone(point);
for (const panel of this.question.panels) {
const panelFlats = yield FlatSurvey.generateFlatsPanel(this.survey, this.controller, panel, currPoint);
if (panelFlats.length !== 0) {
currPoint.yTop = SurveyHelper.mergeRects(...panelFlats).yBot;
currPoint.yTop += this.controller.unitHeight * FlatPanelDynamic.GAP_BETWEEN_PANELS;
flats.push(...panelFlats);
}
}
return flats;
});
}
}
FlatPanelDynamic.GAP_BETWEEN_PANELS = 0.75;
FlatRepository.getInstance().register('paneldynamic', FlatPanelDynamic);
class RankingItemBrick extends PdfBrick {
constructor(question, controller, rect, mark) {
super(question, controller, rect);
this.mark = mark;
this.question = question;
this.textColor = this.formBorderColor;
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
SurveyHelper.renderFlatBorders(this.controller, this);
const markPoint = SurveyHelper.createPoint(this, true, true);
const oldFontSize = this.controller.fontSize;
this.controller.fontSize = oldFontSize *
CheckItemBrick.CHECKMARK_READONLY_FONT_SIZE_SCALE;
const markSize = this.controller.measureText(this.mark);
markPoint.xLeft += this.width / 2.0 - markSize.width / 2.0;
markPoint.yTop += this.height / 2.0 - markSize.height / 2.0;
const markFlat = yield SurveyHelper.createTextFlat(markPoint, this.question, this.controller, this.mark, TextBrick);
markFlat.unfold()[0].textColor = this.textColor;
this.controller.fontSize = oldFontSize;
yield markFlat.render();
});
}
}
class ColoredBrick extends PdfBrick {
constructor(controller, rect, color, renderWidth, renderHeight) {
super(undefined, controller, rect);
this.color = color;
this.renderWidth = renderWidth;
this.renderHeight = renderHeight;
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
let oldFillColor = this.controller.doc.getFillColor();
this.controller.doc.setFillColor(this.color || 'black');
this.controller.doc.rect(this.xLeft, this.yTop, (_a = this.renderWidth) !== null && _a !== void 0 ? _a : this.width, (_b = this.renderHeight) !== null && _b !== void 0 ? _b : this.height, 'F');
this.controller.doc.setFillColor(oldFillColor);
});
}
}
class FlatRanking extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
generateFlatComposite(point_1, item_1, index_1) {
return __awaiter(this, arguments, void 0, function* (point, item, index, unrankedItem = false) {
const itemRect = SurveyHelper.createRect(point, this.controller.unitWidth, this.controller.unitHeight);
const itemScaledRect = SurveyHelper.moveRect(SurveyHelper.scaleRect(itemRect, SurveyHelper.SELECT_ITEM_FLAT_SCALE), point.xLeft);
const itemFlat = new RankingItemBrick(this.question, this.controller, itemScaledRect, unrankedItem ? '-' : this.question.getNumberByIndex(index));
const textPoint = SurveyHelper.clone(point);
textPoint.xLeft = itemFlat.xRight + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_ITEM_TEXT;
const textFlat = yield SurveyHelper.createTextFlat(textPoint, this.question, this.controller, item.locText, TextBrick);
return new CompositeBrick(itemFlat, textFlat);
});
}
generateChoicesColumn(point_1, choices_1) {
return __awaiter(this, arguments, void 0, function* (point, choices, unrankedChoices = false) {
const currPoint = SurveyHelper.clone(point);
const flats = [];
for (let i = 0; i < choices.length; i++) {
const itemFlat = yield this.generateFlatComposite(currPoint, choices[i], i, unrankedChoices);
currPoint.yTop = itemFlat.yBot + SurveyHelper.GAP_BETWEEN_ROWS * this.controller.unitHeight;
flats.push(itemFlat);
}
return flats;
});
}
generateSelectToRankItemsVertically(point) {
return __awaiter(this, void 0, void 0, function* () {
const currPoint = SurveyHelper.clone(point);
const flats = [];
if (this.question.rankingChoices.length !== 0) {
flats.push(...yield this.generateChoicesColumn(currPoint, this.question.rankingChoices));
currPoint.yTop = flats[flats.length - 1].yBot + 2 * (SurveyHelper.GAP_BETWEEN_ROWS * this.controller.unitHeight);
}
const separatorRect = SurveyHelper.createRect({
xLeft: currPoint.xLeft,
yTop: currPoint.yTop - (SurveyHelper.GAP_BETWEEN_ROWS * this.controller.unitHeight) - 0.5,
}, this.controller.paperWidth - this.controller.margins.right - currPoint.xLeft, 1);
flats.push(new ColoredBrick(this.controller, separatorRect, SurveyHelper.FORM_BORDER_COLOR));
flats.push(...yield this.generateChoicesColumn(currPoint, this.question.unRankingChoices, true));
return flats;
});
}
generateSelectToRankItemsHorizontally(point) {
return __awaiter(this, void 0, void 0, function* () {
const colCount = 2;
const currPoint = SurveyHelper.clone(point);
const flats = [];
const rowsCount = Math.max(this.question.unRankingChoices.length, this.question.rankingChoices.length);
let row = new CompositeBrick();
for (let i = 0; i < rowsCount; i++) {
let colIndex = 0;
for (let item of [this.question.unRankingChoices[i], this.question.rankingChoices[i]]) {
if (!!item) {
this.controller.pushMargins(this.controller.margins.left, this.controller.margins.right);
SurveyHelper.setColumnMargins(this.controller, colCount, colIndex);
currPoint.xLeft = this.controller.margins.left;
const itemFlat = yield this.generateFlatComposite(currPoint, item, i, colIndex == 0);
row.addBrick(itemFlat);
this.controller.popMargins();
}
colIndex++;
}
const rowLineFlat = SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(row), this.controller);
flats.push(row, rowLineFlat);
const separatorRect = SurveyHelper.createRect({
xLeft: this.controller.margins.left + SurveyHelper.getPageAvailableWidth(this.controller) / 2 - 0.5,
yTop: currPoint.yTop,
}, 0, 0);
row.addBrick(new ColoredBrick(this.controller, separatorRect, SurveyHelper.FORM_BORDER_COLOR, 1, rowLineFlat.yBot - currPoint.yTop + (i !== rowsCount - 1 ?
SurveyHelper.GAP_BETWEEN_ROWS * this.controller.unitHeight : 0)));
currPoint.yTop = rowLineFlat.yBot +
SurveyHelper.GAP_BETWEEN_ROWS * this.controller.unitHeight;
row = new CompositeBrick();
}
return flats;
});
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.question.selectToRankEnabled) {
return this.generateChoicesColumn(point, this.question.rankingChoices);
}
else if (this.question.selectToRankAreasLayout == 'vertical') {
return this.generateSelectToRankItemsVertically(point);
}
else {
return this.generateSelectToRankItemsHorizontally(point);
}
});
}
}
FlatRepository.getInstance().register('ranking', FlatRanking);
class FlatRating extends FlatRadiogroup {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.questionRating = question;
}
generateFlatHorisontalComposite(point, item, index) {
return __awaiter(this, void 0, void 0, function* () {
const itemText = SurveyHelper.getRatingItemText(this.questionRating, index, item.locText);
this.controller.pushMargins();
const halfWidth = this.controller.unitWidth / 2.0;
this.controller.margins.left += halfWidth;
this.controller.margins.right += halfWidth;
const textPoint = SurveyHelper.clone(point);
textPoint.xLeft += halfWidth;
const compositeFlat = new CompositeBrick(yield SurveyHelper.
createBoldTextFlat(textPoint, this.questionRating, this.controller, itemText));
this.controller.popMargins();
let textWidth = compositeFlat.width;
if (textWidth < SurveyHelper.getRatingMinWidth(this.controller)) {
compositeFlat.xLeft += (SurveyHelper.getRatingMinWidth(this.controller) - textWidth) / 2.0 - halfWidth;
textWidth = SurveyHelper.getRatingMinWidth(this.controller);
}
else {
textWidth += this.controller.unitWidth;
}
const radioPoint = SurveyHelper.createPoint(compositeFlat);
radioPoint.xLeft = point.xLeft;
compositeFlat.addBrick(this.generateFlatItem(SurveyHelper.createRect(radioPoint, textWidth, this.controller.unitHeight), item, index, undefined, this.question.value == item.value));
return compositeFlat;
});
}
generateFlatComposite(point, item, index) {
return __awaiter(this, void 0, void 0, function* () {
const compositeFlat = new CompositeBrick();
const itemRect = SurveyHelper.createRect(point, this.controller.unitHeight, this.controller.unitHeight);
const itemFlat = this.generateFlatItem(SurveyHelper.moveRect(SurveyHelper.scaleRect(itemRect, SurveyHelper.SELECT_ITEM_FLAT_SCALE), point.xLeft), item, index, undefined, this.question.value == item.value);
compositeFlat.addBrick(itemFlat);
const textPoint = SurveyHelper.clone(point);
textPoint.xLeft = itemFlat.xRight + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_ITEM_TEXT;
const itemText = SurveyHelper.getRatingItemText(this.questionRating, index, item.locText);
itemText == null || compositeFlat.addBrick(yield SurveyHelper.createTextFlat(textPoint, this.question, this.controller, itemText, TextBrick));
return compositeFlat;
});
}
generateHorisontallyItems(point) {
return __awaiter(this, void 0, void 0, function* () {
const rowsFlats = [new CompositeBrick()];
const currPoint = SurveyHelper.clone(point);
for (let i = 0; i < this.questionRating.visibleRateValues.length; i++) {
const itemFlat = yield this.generateFlatHorisontalComposite(currPoint, this.questionRating.visibleRateValues[i], i);
rowsFlats[rowsFlats.length - 1].addBrick(itemFlat);
const leftWidth = this.controller.paperWidth -
this.controller.margins.right - itemFlat.xRight;
if (SurveyHelper.getRatingMinWidth(this.controller) <= leftWidth + SurveyHelper.EPSILON) {
currPoint.xLeft = itemFlat.xRight;
}
else {
currPoint.xLeft = point.xLeft;
currPoint.yTop = itemFlat.yBot;
if (i !== this.questionRating.visibleRateValues.length - 1) {
rowsFlats[rowsFlats.length - 1].addBrick(SurveyHelper.createRowlineFlat(currPoint, this.controller));
currPoint.yTop += SurveyHelper.EPSILON;
rowsFlats.push(new CompositeBrick());
}
}
}
return rowsFlats;
});
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
let isVertical = false;
for (let i = 0; i < this.questionRating.visibleRateValues.length; i++) {
const itemText = SurveyHelper.getRatingItemText(this.questionRating, i, this.questionRating.visibleRateValues[i].locText);
if (this.controller.measureText(itemText).width > this.controller.measureText(SurveyHelper.RATING_COLUMN_WIDTH).width) {
isVertical = true;
}
}
return isVertical ? this.generateVerticallyItems(point, this.questionRating.visibleRateValues) : this.generateHorisontallyItems(point);
});
}
}
FlatRepository.getInstance().register('rating', FlatRating);
class FlatSignaturePad extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.question = question;
}
generateBackgroundImage(point) {
return __awaiter(this, void 0, void 0, function* () {
return yield SurveyHelper.createImageFlat(point, this.question, this.controller, { link: this.question.backgroundImage, width: SurveyHelper.pxToPt(this.question.signatureWidth), height: SurveyHelper.pxToPt(this.question.signatureHeight), objectFit: 'cover' }, true);
});
}
getSignImageUrl() {
return this.question.storeDataAsText || !this.question.loadedData ? this.question.value : this.question.loadedData;
}
generateSign(point) {
return __awaiter(this, void 0, void 0, function* () {
const width = SurveyHelper.pxToPt(this.question.signatureWidth);
const height = SurveyHelper.pxToPt(this.question.signatureHeight);
let brick;
if (this.question.value) {
brick = (yield SurveyHelper.createImageFlat(point, this.question, this.controller, { link: this.getSignImageUrl(),
width: width,
height: height }, false));
}
else {
brick = new EmptyBrick(SurveyHelper.createRect(point, width, height));
}
if (FlatSignaturePad.BORDER_STYLE !== 'none') {
brick.afterRenderCallback = () => {
const borderOptions = {
height: brick.width,
width: brick.width,
yTop: brick.yTop,
yBot: brick.yBot,
xLeft: brick.xLeft,
xRight: brick.xRight,
formBorderColor: brick.formBorderColor,
rounded: false,
outside: true,
dashStyle: FlatSignaturePad.BORDER_STYLE == 'dashed' ? {
dashArray: [5],
dashPhase: 0
} : undefined
};
SurveyHelper.renderFlatBorders(this.controller, borderOptions);
};
}
return brick;
});
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
const compositeBrick = new CompositeBrick();
if (this.question.backgroundImage) {
compositeBrick.addBrick(yield this.generateBackgroundImage(point));
}
compositeBrick.addBrick(yield this.generateSign(point));
return [compositeBrick];
});
}
}
FlatSignaturePad.BORDER_STYLE = 'dashed';
FlatRepository.getInstance().register('signaturepad', FlatSignaturePad);
class FlatTextbox extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.question = question;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.shouldRenderAsComment) {
const rect = SurveyHelper.createTextFieldRect(point, this.controller);
return [new TextBoxBrick(this.question, this.controller, rect)];
}
return [yield SurveyHelper.createCommentFlat(point, this.question, this.controller, true, { rows: FlatTextbox.MULTILINE_TEXT_ROWS_COUNT })];
});
}
}
FlatTextbox.MULTILINE_TEXT_ROWS_COUNT = 1;
FlatRepository.getInstance().register('text', FlatTextbox);
class FlatMatrix extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
generateFlatsHeader(point) {
return __awaiter(this, void 0, void 0, function* () {
const headers = [];
const currPoint = SurveyHelper.clone(point);
if (this.question.hasRows) {
currPoint.xLeft += this.rowTitleWidth + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_COLUMNS;
}
for (let i = 0; i < this.question.visibleColumns.length; i++) {
this.controller.pushMargins();
this.controller.margins.left = currPoint.xLeft;
this.controller.margins.right += (SurveyHelper.getPageAvailableWidth(this.controller) - this.columnWidth);
headers.push(yield SurveyHelper.createBoldTextFlat(currPoint, this.question, this.controller, this.question.visibleColumns[i].locText));
currPoint.xLeft += this.columnWidth + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_COLUMNS;
this.controller.popMargins();
}
const compositeBrick = new CompositeBrick(...headers);
return [compositeBrick, SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(compositeBrick), this.controller)];
});
}
generateFlatsRows(point, isVertical) {
return __awaiter(this, void 0, void 0, function* () {
const cells = [];
let currPoint = SurveyHelper.clone(point);
for (let i = 0; i < this.question.visibleRows.length; i++) {
const key = 'row' + i;
const flatsRow = yield new FlatMatrixRow(this.survey, this.question, this.controller, this.question.visibleRows[i], i, key, i == 0, isVertical, this.rowTitleWidth, this.columnWidth).generateFlatsContent(currPoint);
currPoint = SurveyHelper.createPoint(SurveyHelper.mergeRects(...flatsRow));
currPoint.yTop += this.controller.unitHeight * FlatMatrix.GAP_BETWEEN_ROWS;
cells.push(...flatsRow);
}
return cells;
});
}
calculateColumnsWidthes() {
const availableWidth = SurveyHelper.getPageAvailableWidth(this.controller);
if (this.question.hasRows && this.question.rowTitleWidth) {
this.controller.pushMargins();
this.rowTitleWidth = SurveyHelper.parseWidth(this.question.rowTitleWidth, availableWidth);
this.controller.margins.left += (this.rowTitleWidth + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_COLUMNS);
this.columnWidth = SurveyHelper.getColumnWidth(this.controller, this.question.visibleColumns.length);
this.controller.popMargins();
}
else {
this.columnWidth = this.rowTitleWidth = SurveyHelper.getColumnWidth(this.controller, this.question.visibleColumns.length + (this.question.hasRows ? 1 : 0));
}
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
this.calculateColumnsWidthes();
const isVertical = this.question.renderAs === 'list' || this.controller.matrixRenderAs === 'list' ||
this.columnWidth < this.controller.measureText(SurveyHelper.MATRIX_COLUMN_WIDTH).width;
let currPoint = SurveyHelper.clone(point);
const cells = [];
if (!isVertical && this.question.showHeader && this.question.visibleColumns.length != 0) {
let headers = yield this.generateFlatsHeader(currPoint);
currPoint = SurveyHelper.createPoint(SurveyHelper.mergeRects(...headers));
currPoint.yTop += FlatMatrix.GAP_BETWEEN_ROWS * this.controller.unitHeight;
cells.push(...headers);
}
cells.push(...yield this.generateFlatsRows(currPoint, isVertical));
return cells;
});
}
}
FlatMatrix.GAP_BETWEEN_ROWS = 0.5;
class FlatMatrixRow extends FlatRadiogroup {
constructor(survey, question, controller, row, rowIndex, key, isFirst = false, isVertical = false, rowTitleWidth, columnWidth) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.row = row;
this.rowIndex = rowIndex;
this.key = key;
this.isFirst = isFirst;
this.isVertical = isVertical;
this.rowTitleWidth = rowTitleWidth;
this.columnWidth = columnWidth;
this.questionMatrix = question;
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
return this.isVertical ?
yield this.generateFlatsVerticallyCells(point) :
yield this.generateFlatsHorizontallyCells(point);
});
}
generateTextComposite(point, column, index) {
return __awaiter(this, void 0, void 0, function* () {
const currPoint = SurveyHelper.clone(point);
const checked = this.row.value == column.value;
const itemRect = SurveyHelper.createRect(currPoint, SurveyHelper.getPageAvailableWidth(this.controller), this.controller.unitHeight);
const radioFlat = this.generateFlatItem(itemRect, column, index, this.key, checked, { row: this.row, rowIndex: this.rowIndex });
currPoint.yTop = radioFlat.yBot + this.controller.unitHeight * SurveyHelper.GAP_BETWEEN_ITEM_TEXT;
const cellTextFlat = yield SurveyHelper.createTextFlat(currPoint, this.questionMatrix, this.controller, this.questionMatrix.getCellDisplayLocText(this.row.name, column), TextBrick);
return new CompositeBrick(radioFlat, cellTextFlat);
});
}
generateItemCompoiste(point, column, index) {
return __awaiter(this, void 0, void 0, function* () {
const currPoint = SurveyHelper.clone(point);
const checked = this.row.value == column.value;
const itemRect = SurveyHelper.createRect(currPoint, this.controller.unitHeight, this.controller.unitHeight);
const radioFlat = this.generateFlatItem(SurveyHelper.moveRect(SurveyHelper.scaleRect(itemRect, SurveyHelper.SELECT_ITEM_FLAT_SCALE), itemRect.xLeft), column, index, this.key, checked, { row: this.row, rowIndex: this.rowIndex });
currPoint.xLeft = radioFlat.xRight + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_ITEM_TEXT;
const radioText = yield SurveyHelper.createTextFlat(currPoint, this.questionMatrix, this.controller, column.locText, TextBrick);
return new CompositeBrick(radioFlat, radioText);
});
}
generateFlatsHorizontallyCells(point) {
return __awaiter(this, void 0, void 0, function* () {
const cells = [];
const currPoint = SurveyHelper.clone(point);
if (this.questionMatrix.hasRows) {
this.controller.pushMargins();
currPoint.xLeft = this.controller.margins.left;
this.controller.margins.right += (SurveyHelper.getPageAvailableWidth(this.controller) - this.rowTitleWidth);
cells.push(yield SurveyHelper.createTextFlat(currPoint, this.questionMatrix, this.controller, this.row.locText, TextBrick));
currPoint.xLeft += this.rowTitleWidth + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_COLUMNS;
this.controller.popMargins();
}
for (let i = 0; i < this.questionMatrix.visibleColumns.length; i++) {
const column = this.questionMatrix.visibleColumns[i];
const checked = this.row.value == column.value;
this.controller.pushMargins();
this.controller.margins.left = currPoint.xLeft;
this.controller.margins.right += (SurveyHelper.getPageAvailableWidth(this.controller) - this.columnWidth);
if (this.questionMatrix.hasCellText) {
cells.push(yield this.generateTextComposite(currPoint, column, i));
}
else {
const itemRect = SurveyHelper.createRect(currPoint, this.controller.unitHeight, this.controller.unitHeight);
cells.push(this.generateFlatItem(SurveyHelper.moveRect(SurveyHelper.scaleRect(itemRect, SurveyHelper.SELECT_ITEM_FLAT_SCALE), currPoint.xLeft), column, i, this.key, checked, { row: this.row, rowIndex: this.rowIndex }));
}
currPoint.xLeft += this.columnWidth + this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_COLUMNS;
this.controller.popMargins();
}
const compositeBrick = new CompositeBrick(...cells);
return [compositeBrick, SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(compositeBrick), this.controller)];
});
}
generateFlatsVerticallyCells(point) {
return __awaiter(this, void 0, void 0, function* () {
const cells = [];
const currPoint = SurveyHelper.clone(point);
if (this.questionMatrix.hasRows) {
const rowTextFlat = yield SurveyHelper.createTextFlat(currPoint, this.questionMatrix, this.controller, this.row.locText, TextBrick);
currPoint.yTop = rowTextFlat.yBot + FlatQuestion.CONTENT_GAP_VERT_SCALE * this.controller.unitHeight;
cells.push(rowTextFlat);
}
this.generateFlatComposite = (this.questionMatrix.hasCellText) ? this.generateTextComposite : this.generateItemCompoiste;
cells.push(...yield this.generateVerticallyItems(currPoint, this.questionMatrix.visibleColumns));
const compositeBrick = new CompositeBrick(...cells);
return [compositeBrick, SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(compositeBrick), this.controller)];
});
}
}
Serializer.removeProperty('matrix', 'renderAs');
Serializer.addProperty('matrix', {
name: 'renderAs',
default: 'auto',
visible: false,
choices: ['auto', 'list']
});
FlatRepository.getInstance().register('matrix', FlatMatrix);
class FlatMatrixMultiple extends FlatQuestion {
constructor(survey, question, controller, isMultiple = true) {
super(survey, question, controller);
this.survey = survey;
this.isMultiple = isMultiple;
this.question = question;
}
get visibleRows() {
if (!this.visibleRowsValue) {
this.visibleRowsValue = this.question.renderedTable.rows.filter(row => row.visible);
}
return this.visibleRowsValue;
}
generateFlatsCellTitle(point, locTitle) {
return __awaiter(this, void 0, void 0, function* () {
const composite = new CompositeBrick();
composite.addBrick(yield SurveyHelper.createTextFlat(point, this.question, this.controller, locTitle, TextBrick));
return composite;
});
}
generateFlatsCell(point_1, cell_1, location_1) {
return __awaiter(this, arguments, void 0, function* (point, cell, location, isWide = true) {
const composite = new CompositeBrick();
if (cell.hasQuestion) {
if (location == 'footer' && !cell.question.isAnswered) ;
else if (isWide && cell.isChoice) {
const flatMultipleColumnsQuestion = FlatRepository.getInstance().create(this.survey, cell.question, this.controller, cell.question.getType());
const itemRect = SurveyHelper.moveRect(SurveyHelper.scaleRect(SurveyHelper.createRect(point, this.controller.unitHeight, this.controller.unitHeight), SurveyHelper.SELECT_ITEM_FLAT_SCALE), point.xLeft);
composite.addBrick(flatMultipleColumnsQuestion
.generateFlatItem(itemRect, cell.item, cell.choiceIndex));
}
else {
cell.question.titleLocation = SurveyHelper.TITLE_LOCATION_MATRIX;
composite.addBrick(...yield SurveyHelper.generateQuestionFlats(this.survey, this.controller, cell.question, point));
}
}
else if (cell.hasTitle) {
if (location == 'header') {
composite.addBrick(yield SurveyHelper.createBoldTextFlat(point, this.question, this.controller, cell.locTitle));
}
else {
composite.addBrick(yield SurveyHelper.createTextFlat(point, this.question, this.controller, cell.locTitle, TextBrick));
}
}
return composite;
});
}
get hasDetailPanel() {
return this.visibleRows.some((renderedRow) => renderedRow.row && this.question.hasDetailPanel(renderedRow.row));
}
ignoreCell(cell, index, location, isWide = true) {
if (!isWide && location == 'footer' && cell.hasQuestion && !cell.question.isAnswered)
return true;
return !(cell.hasQuestion || cell.hasTitle || (this.isMultiple && (this.hasDetailPanel ? index == 1 : index == 0)));
}
getRowLocation(row) {
return row === this.question.renderedTable.headerRow ? 'header' : (this.question.renderedTable.footerRow === row ? 'footer' : undefined);
}
generateFlatsRowHorisontal(point, row, columnWidth) {
return __awaiter(this, void 0, void 0, function* () {
const composite = new CompositeBrick();
const currPoint = SurveyHelper.clone(point);
let lastRightMargin = this.controller.paperWidth - this.controller.margins.left +
this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_COLUMNS;
this.controller.pushMargins();
let cnt = 0;
const rowLocation = this.getRowLocation(row);
for (let i = 0; i < row.cells.length; i++) {
if (this.ignoreCell(row.cells[i], i, rowLocation))
continue;
this.controller.margins.left = this.controller.paperWidth - lastRightMargin +
this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_COLUMNS;
this.controller.margins.right = this.controller.paperWidth -
this.controller.margins.left - columnWidth[cnt];
lastRightMargin = this.controller.margins.right;
currPoint.xLeft = this.controller.margins.left;
const cellContent = yield this.generateFlatsCell(currPoint, row.cells[i], rowLocation);
if (!cellContent.isEmpty) {
composite.addBrick(cellContent);
}
cnt++;
}
this.controller.popMargins();
return composite;
});
}
generateFlatsRowVertical(point, row) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const composite = new CompositeBrick();
const currPoint = SurveyHelper.clone(point);
const rowLocation = this.getRowLocation(row);
for (let i = 0; i < row.cells.length; i++) {
if (this.ignoreCell(row.cells[i], i, rowLocation, false))
continue;
if (this.question.renderedTable.showHeader && (!this.isMultiple || i > 0) && ((_b = (_a = row.cells[i].cell) === null || _a === void 0 ? void 0 : _a.column) === null || _b === void 0 ? void 0 : _b.locTitle)) {
composite.addBrick(yield this.generateFlatsCellTitle(currPoint, row.cells[i].cell.column.locTitle));
currPoint.yTop = composite.yBot + FlatMatrixMultiple.GAP_BETWEEN_ROWS * this.controller.unitHeight;
}
composite.addBrick(yield this.generateFlatsCell(currPoint, row.cells[i], rowLocation, false));
currPoint.yTop = composite.yBot + FlatMatrixMultiple.GAP_BETWEEN_ROWS * this.controller.unitHeight;
}
return composite;
});
}
getAvalableWidth(colCount) {
return SurveyHelper.getPageAvailableWidth(this.controller) -
(colCount - 1) * this.controller.unitWidth * SurveyHelper.GAP_BETWEEN_COLUMNS;
}
calculateColumnWidth(rows, colCount) {
const availableWidth = this.getAvalableWidth(colCount);
let remainWidth = availableWidth;
let remainColCount = colCount;
const columnWidth = [];
const unsetCells = [];
let cells = rows[0].cells.filter((cell, index) => !this.ignoreCell(cell, index));
for (let i = 0; i < colCount; i++) {
const width = SurveyHelper.parseWidth(cells[i].width, availableWidth, colCount) || 0.0;
remainWidth -= width;
if (width !== 0.0) {
remainColCount--;
}
else {
unsetCells.push(cells[i]);
}
columnWidth.push(width);
}
if (remainColCount === 0)
return columnWidth;
const heuristicWidth = this.controller.measureText(SurveyHelper.MATRIX_COLUMN_WIDTH).width;
unsetCells.sort((cell1, cell2) => {
let minWidth1 = SurveyHelper.parseWidth(cell1.minWidth, availableWidth, colCount) || 0.0;
let minWidth2 = SurveyHelper.parseWidth(cell2.minWidth, availableWidth, colCount) || 0.0;
return minWidth2 > minWidth1 ? 1 : -1;
}).forEach((cell) => {
const equalWidth = remainWidth / remainColCount;
const columnMinWidth = SurveyHelper.parseWidth(cell.minWidth, availableWidth, colCount) || 0.0;
if (columnMinWidth > equalWidth && columnMinWidth > heuristicWidth) {
remainWidth -= columnMinWidth;
remainColCount--;
}
columnWidth[cells.indexOf(cell)] = Math.max(heuristicWidth, columnMinWidth, equalWidth);
});
return columnWidth;
}
generateOneRow(point, row, isWide, columnWidth) {
return __awaiter(this, void 0, void 0, function* () {
if (isWide) {
return yield this.generateFlatsRowHorisontal(point, row, columnWidth);
}
return yield this.generateFlatsRowVertical(point, row);
});
}
generateFlatsRows(point, rows, colCount, isWide) {
return __awaiter(this, void 0, void 0, function* () {
const currPoint = SurveyHelper.clone(point);
const rowsFlats = [];
for (let i = 0; i < rows.length; i++) {
let rowFlat = yield this.generateOneRow(currPoint, rows[i], isWide, this.calculateColumnWidth(rows, colCount));
if (rowFlat.isEmpty && !(rows[i].row && rows[i].row.hasPanel))
continue;
if (!rowFlat.isEmpty) {
if (i !== rows.length - 1) {
currPoint.yTop = rowFlat.yBot;
rowFlat.addBrick(SurveyHelper.createRowlineFlat(currPoint, this.controller));
}
rowsFlats.push(rowFlat);
currPoint.yTop = rowFlat.yBot + FlatMatrixMultiple.GAP_BETWEEN_ROWS * this.controller.unitHeight;
}
if (!!rows[i].row && rows[i].row.hasPanel) {
rows[i].row.showDetailPanel();
const currentDetailPanel = rows[i].row.detailPanel;
for (let j = 0; j < currentDetailPanel.questions.length; j++) {
currentDetailPanel.questions[j].id += '_' + i;
}
const panelBricks = yield FlatSurvey.generateFlatsPanel(this.survey, this.controller, currentDetailPanel, currPoint);
const currComposite = new CompositeBrick();
currComposite.addBrick(...panelBricks);
currPoint.yTop = currComposite.yBot + FlatMatrixMultiple.GAP_BETWEEN_ROWS * this.controller.unitHeight;
rowsFlats.push(currComposite);
if (i !== rows.length - 1 && this.question.renderedTable.showHeader && isWide) {
const header = yield this.generateOneRow(currPoint, rows[0], isWide, this.calculateColumnWidth(rows, colCount));
let currYTop = currComposite.yBot;
if (!header.isEmpty) {
currYTop = header.yBot;
rowsFlats.push(header);
}
currPoint.yTop = currYTop + FlatMatrixMultiple.GAP_BETWEEN_ROWS * this.controller.unitHeight;
}
}
}
return rowsFlats;
});
}
calculateIsWide(table, colCount) {
const rows = [];
if (table.showHeader) {
rows.push(table.headerRow);
}
rows.push(...this.visibleRows);
if (rows.length === 0)
return true;
const columnWidthSum = this.calculateColumnWidth(rows, colCount).reduce((widthSum, width) => widthSum += width, 0);
return this.question.renderAs !== 'list' && this.controller.matrixRenderAs !== 'list' && Math.floor(columnWidthSum) <= Math.floor(this.getAvalableWidth(colCount));
}
getRowsToRender(table, isVertical, isWide) {
const rows = [];
const renderedRows = this.visibleRows;
if (table.showHeader && isWide)
rows.push(table.headerRow);
rows.push(...renderedRows);
if (table.hasRemoveRows && isVertical)
rows.pop();
if (table.showFooter)
rows.push(table.footerRow);
return rows;
}
getColCount(table, renderedRows) {
if (!!renderedRows[0]) {
return renderedRows[0].cells.filter((cell, index) => !this.ignoreCell(cell, index)).length;
}
else {
return table.showHeader && table.headerRow ? table.headerRow.cells.length :
table.showFooter && table.footerRow ? table.footerRow.cells.length : 0;
}
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
let table = this.question.renderedTable;
let isVertical = this.question.columnLayout === 'vertical';
let colCount = this.getColCount(table, this.visibleRows);
if (colCount === 0 && !this.hasDetailPanel) {
return [new CompositeBrick(SurveyHelper.createRowlineFlat(point, this.controller))];
}
const isWide = this.calculateIsWide(table, colCount);
if (!isWide) {
this.question.isMobile = true;
isVertical = false;
table = this.question.renderedTable;
this.visibleRowsValue = undefined;
colCount = this.getColCount(table, this.visibleRows);
}
const rows = this.getRowsToRender(table, isVertical, isWide);
return yield this.generateFlatsRows(point, rows, colCount, isWide);
});
}
}
FlatMatrixMultiple.GAP_BETWEEN_ROWS = 0.5;
Serializer.removeProperty('matrixdropdown', 'renderAs');
Serializer.addProperty('matrixdropdown', {
name: 'renderAs',
default: 'auto',
visible: false,
choices: ['auto', 'list']
});
FlatRepository.getInstance().register('matrixdropdown', FlatMatrixMultiple);
class FlatMatrixDynamic extends FlatMatrixMultiple {
constructor(survey, question, controller) {
super(survey, question, controller, false);
this.survey = survey;
}
}
Serializer.removeProperty('matrixdynamic', 'renderAs');
Serializer.addProperty('matrixdynamic', {
name: 'renderAs',
default: 'auto',
visible: false,
choices: ['auto', 'list']
});
FlatRepository.getInstance().register('matrixdynamic', FlatMatrixDynamic);
class FlatMultipleText extends FlatQuestion {
constructor(survey, question, controller) {
super(survey, question, controller);
this.survey = survey;
this.controller = controller;
this.question = question;
}
getVisibleRows() {
return this.question.getRows().filter(row => row.isVisible);
}
generateFlatItem(point, row_index, col_index, item) {
return __awaiter(this, void 0, void 0, function* () {
const colWidth = SurveyHelper.getPageAvailableWidth(this.controller);
this.controller.pushMargins();
this.controller.margins.right = this.controller.paperWidth -
this.controller.margins.left - colWidth * SurveyHelper.MULTIPLETEXT_TEXT_PERS;
const compositeFlat = new CompositeBrick(yield SurveyHelper.
createBoldTextFlat(point, this.question, this.controller, item.locTitle));
this.controller.popMargins();
const flatMultipleTextItemQuestion = FlatRepository.getInstance().create(this.survey, item.editor, this.controller, 'text');
const itemPoint = SurveyHelper.createTextFieldRect({
xLeft: point.xLeft + colWidth * SurveyHelper.MULTIPLETEXT_TEXT_PERS, yTop: point.yTop
}, this.controller);
compositeFlat.addBrick(...yield flatMultipleTextItemQuestion.generateFlatsContent(itemPoint));
return compositeFlat;
});
}
generateFlatsContent(point) {
return __awaiter(this, void 0, void 0, function* () {
const rowsFlats = [];
const currPoint = SurveyHelper.clone(point);
const rows = this.getVisibleRows();
for (let i = 0; i < rows.length; i++) {
rowsFlats.push(new CompositeBrick());
let yBot = currPoint.yTop;
this.controller.pushMargins();
for (let j = 0; j < rows[i].cells.length; j++) {
this.controller.pushMargins();
SurveyHelper.setColumnMargins(this.controller, this.question.colCount, j);
currPoint.xLeft = this.controller.margins.left;
const itemFlat = yield this.generateFlatItem(currPoint, i, j, rows[i].cells[j].item);
rowsFlats[rowsFlats.length - 1].addBrick(itemFlat);
yBot = Math.max(yBot, itemFlat.yBot);
this.controller.popMargins();
}
this.controller.popMargins();
currPoint.xLeft = point.xLeft;
currPoint.yTop = yBot;
rowsFlats[rowsFlats.length - 1].addBrick(SurveyHelper.createRowlineFlat(currPoint, this.controller));
currPoint.yTop += SurveyHelper.EPSILON;
currPoint.yTop += this.controller.unitHeight * FlatMultipleText.ROWS_GAP_SCALE;
}
return rowsFlats;
});
}
}
FlatMultipleText.ROWS_GAP_SCALE = 0.195;
FlatRepository.getInstance().register('multipletext', FlatMultipleText);
class CustomBrick extends PdfBrick {
constructor(question, controller, renderFunc) {
super(question, controller, renderFunc(controller.helperDoc, question, 0, 0));
this.renderFunc = renderFunc;
}
renderInteractive() {
return __awaiter(this, void 0, void 0, function* () {
yield new Promise((resolve) => {
this.renderFunc(this.controller.doc, this.question, this.xLeft, this.yTop);
resolve();
});
});
}
}
checkLibraryVersion(`${"2.0.3"}`, 'survey-pdf');
export { BooleanItemBrick, CheckItemBrick, CheckboxItemBrick, CommentBrick, CompositeBrick, CustomBrick, DocController, DocOptions, DrawCanvas, DropdownBrick, EmptyBrick, EventHandler, FlatBooleanCheckbox as FlatBoolean, FlatCheckbox, FlatComment, FlatCustomModel, FlatDropdown, FlatExpression, FlatFile, FlatHTML, FlatImage, FlatImagePicker, FlatMatrix, FlatMatrixDynamic, FlatMatrixMultiple, FlatMultipleText, FlatPanelDynamic, FlatQuestion, FlatQuestionDefault, FlatRadiogroup, FlatRanking, FlatRating, FlatRepository, FlatSelectBase, FlatSignaturePad, FlatSurvey, FlatTextbox, HTMLBrick, HorizontalAlign, ImageBrick, LinkBrick, PagePacker, PdfBrick, RadioItemBrick, RankingItemBrick, RowlineBrick, SurveyHelper, SurveyPDF, TextBoldBrick, TextBoxBrick, TextBrick, TextFieldBrick, TitlePanelBrick, VerticalAlign };
//# sourceMappingURL=survey.pdf.mjs.map