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.
1,178 lines (1,150 loc) • 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 val