UNPKG

survey-pdf

Version:

Renders JSON-driven SurveyJS forms and their responses as PDF documents in the browser or Node.js: fillable interactive PDF forms (AcroForm) or static printouts.

11,503 lines 497 kB
/*!
 * surveyjs - SurveyJS PDF library v3.0.2
 * Copyright (c) 2015-2026 Devsoft Baltic OÜ  - http://surveyjs.io/
 * License: MIT (http://www.opensource.org/licenses/mit-license.php)
 */

import { Serializer, settings, EventBase, SurveyModel, BaseTheme, glc, hasLicense, PanelModel, LocalizableString, checkLibraryVersion } from 'survey-core';
import { jsPDF } from 'jspdf';

function mergeRects(...rects) {
    if (rects.length == 0)
        return { xLeft: 0, xRight: 0, yTop: 0, yBot: 0 };
    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;
}
function parseSideValues(padding) {
    if (Array.isArray(padding) && padding.length > 1) {
        if (padding.length == 2) {
            return {
                top: padding[0],
                bot: padding[0],
                left: padding[1],
                right: padding[1],
            };
        }
        if (padding.length == 3) {
            return {
                top: padding[0],
                left: padding[1],
                right: padding[1],
                bot: padding[2],
            };
        }
        if (padding.length == 4) {
            return {
                top: padding[0],
                right: padding[1],
                bot: padding[2],
                left: padding[3],
            };
        }
    }
    else {
        const value = Array.isArray(padding) ? padding[0] : padding;
        return {
            top: value,
            bot: value,
            right: value,
            left: value,
        };
    }
}

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 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);
    }
    addBeforeRenderCallback(func) {
        this.bricks.forEach(brick => brick.addBeforeRenderCallback(func));
    }
    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;
    }
    async render() {
        for (let i = 0; i < this.bricks.length; i++) {
            await this.bricks[i].render();
        }
    }
    get isEmpty() {
        return this.bricks.length === 0;
    }
    _updateRect() {
        if (this.bricks.length > 0) {
            let mergeRect = mergeRects(...this.bricks);
            this._xLeft = mergeRect.xLeft;
            this._xRight = mergeRect.xRight;
            this._yTop = mergeRect.yTop;
            this._yBot = mergeRect.yBot;
        }
    }
    addBrick(...bricks) {
        if (bricks.length != 0) {
            this.bricks.push(...bricks);
            this._updateRect();
        }
    }
    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;
    }
    translateY(func) {
        this.bricks.forEach(brick => brick.translateY(func));
        this._updateRect();
    }
    setPageNumber(number) {
        this.bricks.forEach(brick => brick.setPageNumber(number));
    }
    getPageNumber() {
        return this.bricks[0].getPageNumber();
    }
    updateRect() {
        this.bricks.forEach(brick => {
            brick.updateRect();
        });
        this._updateRect();
    }
    increasePadding(val) {
        if (val.top == 0 && val.bottom == 0)
            return;
        const tree = new libExports.IntervalTree();
        const notEmptyBricks = this.bricks.filter(brick => !brick.isEmpty);
        notEmptyBricks.forEach(brick => {
            tree.insert({ low: brick.xLeft, high: brick.xRight, yTop: brick.yTop, yBot: brick.yBot });
        });
        this.bricks.forEach(brick => {
            const padding = {
                top: 0,
                bottom: 0
            };
            const res = tree.search(brick.xLeft, brick.xRight).reduce((acc, val) => {
                acc.yTop = Math.min(acc.yTop, val.yTop);
                acc.yBot = Math.max(acc.yBot, val.yBot);
                return acc;
            }, { yTop: Number.MAX_VALUE, yBot: Number.MIN_VALUE });
            if (brick.yTop <= res.yTop) {
                padding.top = val.top;
            }
            if (brick.yBot >= res.yBot) {
                padding.bottom = val.bottom;
            }
            if (padding.top !== 0 || padding.bottom !== 0) {
                brick.increasePadding(padding);
            }
        });
        this.updateRect();
    }
    get contentRect() {
        const rect = mergeRects(...this.bricks.map(brick => brick.contentRect));
        return { ...rect, width: rect.xRight - rect.xLeft, height: rect.yBot - rect.yTop };
    }
}

class AdornersBaseOptions {
    constructor(point, bricks, controller, repository) {
        this.point = point;
        this.bricks = bricks;
        this.controller = controller;
        this.repository = repository;
    }
}
class AdornersOptions extends AdornersBaseOptions {
    constructor(point, bricks, question, controller, repository) {
        super(point, bricks, controller, repository);
        this.question = question;
    }
}
class AdornersPanelOptions extends AdornersBaseOptions {
    constructor(point, bricks, panel, controller, repository) {
        super(point, bricks, controller, repository);
        this.panel = panel;
    }
}
class AdornersPageOptions extends AdornersBaseOptions {
    constructor(point, bricks, page, controller, repository) {
        super(point, bricks, controller, repository);
        this.page = page;
    }
}

const defaultContainerOptions = {
    padding: 0,
    borderWidth: 0,
    backgroundColor: null,
    borderColor: null,
    borderMode: 1,
    borderRadius: 0
};
class ContainerBrick extends CompositeBrick {
    get includedBorderWidth() {
        if (!this.includedBorderWidthValue) {
            const value = parseSideValues(this.style.borderWidth);
            Object.keys(value).forEach((key) => {
                switch (this.style.borderMode) {
                    case BorderMode.Inside:
                        break;
                    case BorderMode.Middle:
                        value[key] = value[key] / 2;
                        break;
                    default:
                        return 0;
                }
            });
            this.includedBorderWidthValue = value;
        }
        return this.includedBorderWidthValue;
    }
    get padding() {
        if (!this._padding) {
            this._padding = parseSideValues(this.style.padding);
        }
        return this._padding;
    }
    constructor(controller, layout, style) {
        super();
        this.controller = controller;
        this.layout = layout;
        this.style = SurveyHelper.mergeObjects({}, defaultContainerOptions, style);
    }
    getStartPoint() {
        return { yTop: this.layout.yTop + this.padding.top + this.includedBorderWidth.top, xLeft: this.layout.xLeft + this.padding.left + this.includedBorderWidth.left };
    }
    startSetup() {
        this.controller.pushMargins();
        this.controller.margins.left = this.layout.xLeft + this.padding.left + this.includedBorderWidth.left;
        this.controller.margins.right = this.controller.paperWidth - (this.layout.xLeft + this.layout.width) + this.padding.right + this.includedBorderWidth.right;
    }
    finishSetup() {
        this.controller.popMargins();
        this.increasePadding({ top: this.padding.top + this.includedBorderWidth.top, bottom: this.padding.bot + this.includedBorderWidth.bot });
        let renderedPageIndex = -1;
        const callback = () => {
            const currentPageIndex = this.controller.getCurrentPageIndex();
            if (currentPageIndex == renderedPageIndex) {
                return;
            }
            else {
                renderedPageIndex = currentPageIndex;
                const unfoldedBricks = this.unfold().filter(brick => !brick.isEmpty);
                const unfoldedBricksOnPage = unfoldedBricks.filter(brick => brick.getPageNumber() == currentPageIndex);
                const mergedRect = SurveyHelper.mergeRects(...unfoldedBricksOnPage);
                const keys = ['top', 'right', 'bot', 'left'];
                const borderWidth = new Array(4).fill(0, 0, 4).map((_, i) => { var _a; return (_a = (Array.isArray(this.style.borderWidth) ? this.style.borderWidth[i] : this.style.borderWidth)) !== null && _a !== void 0 ? _a : 0; });
                const borderRadius = new Array(4).fill(0, 0, 4).map((_, i) => {
                    var _a;
                    const borderRadius = (_a = (Array.isArray(this.style.borderRadius) ? this.style.borderRadius[i] : this.style.borderRadius)) !== null && _a !== void 0 ? _a : 0;
                    return Math.min(Math.max(borderRadius - borderWidth[i] - this.includedBorderWidth[keys[i]]), 0),
                        Math.max(borderRadius - (borderWidth[i - 1 < 0 ? borderWidth.length - 1 : i - 1] - this.includedBorderWidth[keys[i - 1 < 0 ? keys.length - 1 : i - 1]]), 0);
                });
                if (unfoldedBricks[0] != unfoldedBricksOnPage[0]) {
                    borderRadius[0] = 0;
                    borderRadius[1] = 0;
                    borderWidth[0] = 0;
                }
                if (unfoldedBricks[unfoldedBricks.length - 1] !== unfoldedBricksOnPage[unfoldedBricksOnPage.length - 1]) {
                    borderRadius[2] = 0;
                    borderRadius[3] = 0;
                    borderWidth[2] = 0;
                }
                if (this.style.backgroundColor !== null) {
                    this.controller.setFillColor(this.style.backgroundColor);
                    const rect = SurveyHelper.createRect({ xLeft: this.layout.xLeft + this.includedBorderWidth.left, yTop: mergedRect.yTop + this.includedBorderWidth.top }, this.layout.width - this.includedBorderWidth.left - this.includedBorderWidth.right, (mergedRect.yBot - mergedRect.yTop) - this.includedBorderWidth.top - this.includedBorderWidth.bot);
                    const { lines, point } = SurveyHelper.getDocLinesFromShape(SurveyHelper.createRoundedShape(rect, { ...this.style, borderRadius }));
                    this.controller.doc.lines(lines, ...point, [1, 1], 'F', true);
                    this.controller.restoreFillColor();
                }
                if (this.style.borderColor !== null) {
                    SurveyHelper.renderFlatBorders(this.controller, {
                        xLeft: this.layout.xLeft,
                        xRight: this.layout.xLeft + this.layout.width,
                        yTop: mergedRect.yTop,
                        yBot: mergedRect.yBot,
                        width: this.layout.width,
                        height: mergedRect.yBot - mergedRect.yTop
                    }, { ...this.style, borderRadius, borderWidth });
                }
            }
        };
        this.bricks.forEach(brick => {
            brick.addBeforeRenderCallback(callback);
        });
    }
    getBricks() {
        return this.bricks.slice();
    }
    async setup(callback) {
        this.startSetup();
        const bricks = [];
        await callback(this.getStartPoint(), bricks);
        this.addBrick(...bricks);
        this.finishSetup();
    }
    set xLeft(val) { }
    get xLeft() {
        return this.layout.xLeft;
    }
    set xRight(val) { }
    get xRight() {
        return this.layout.xLeft + this.layout.width;
    }
    get width() {
        return this.layout.width;
    }
    fitToHeight(height, alignCenter = false) {
        if (alignCenter) {
            const shift = (height - this.height) / 2;
            this.translateY((yTop, yBot) => {
                return {
                    yTop: yTop + shift,
                    yBot: yBot + shift
                };
            });
            this.increasePadding({ top: shift, bottom: shift });
        }
        else {
            this.increasePadding({ top: 0, bottom: height - this.height });
        }
    }
}

class FlatQuestion {
    constructor(survey, question, controller, style) {
        this.survey = survey;
        this.question = question;
        this.controller = controller;
        this.style = style;
    }
    async generateFlatTitle(point) {
        const composite = new CompositeBrick();
        let currPoint = SurveyHelper.clone(point);
        const textStyle = { ...this.style.title };
        if (this.question.no) {
            const numberStyle = SurveyHelper.mergeObjects({}, textStyle, this.style.number);
            const noText = this.question.no;
            let noFlat;
            if (SurveyHelper.hasHtml(this.question.locTitle)) {
                this.controller.pushMargins();
                this.controller.margins.right = this.controller.paperWidth -
                    this.controller.margins.left - this.controller.measureText(noText, numberStyle).width;
                noFlat = await SurveyHelper.createHTMLFlat(currPoint, this.controller, SurveyHelper.createHtmlContainerBlock(noText, this.controller, numberStyle), numberStyle);
                this.controller.popMargins();
            }
            else {
                noFlat = await SurveyHelper.createTextFlat(currPoint, this.controller, noText, numberStyle);
            }
            composite.addBrick(noFlat);
            currPoint.xLeft = noFlat.xRight + this.style.spacing.titleNumberGap;
        }
        this.controller.pushMargins();
        this.controller.margins.left = currPoint.xLeft;
        const textFlat = await SurveyHelper.createTextFlat(currPoint, this.controller, this.question.locTitle, textStyle);
        composite.addBrick(textFlat);
        this.controller.popMargins();
        if (this.question.isRequired) {
            const requiredStyle = SurveyHelper.mergeObjects({}, textStyle, this.style.requiredMark);
            const requiredText = this.question.requiredMark;
            if (SurveyHelper.hasHtml(this.question.locTitle)) {
                currPoint = SurveyHelper.createPoint(textFlat.unfold()[0], false, false);
                currPoint.xLeft += this.style.spacing.titleRequiredMarkGap;
                this.controller.pushMargins();
                this.controller.margins.right = this.controller.paperWidth -
                    this.controller.margins.left - this.controller.measureText(requiredText, requiredStyle).width;
                composite.addBrick(await SurveyHelper.createHTMLFlat(currPoint, this.controller, SurveyHelper.createHtmlContainerBlock(requiredText, this.controller, requiredStyle), requiredStyle));
                this.controller.popMargins();
            }
            else {
                const lastTitleBrick = textFlat.unfold().pop();
                if (this.style.spacing.titleRequiredMarkGap + this.controller.measureText(requiredText, requiredStyle).width < SurveyHelper.getPageAvailableWidth(this.controller) + this.controller.leftTopPoint.xLeft - lastTitleBrick.xRight) {
                    currPoint = SurveyHelper.createPoint(lastTitleBrick, false, true);
                    currPoint.xLeft += this.style.spacing.titleRequiredMarkGap;
                }
                else {
                    const titleXLeft = currPoint.xLeft;
                    currPoint = SurveyHelper.createPoint(lastTitleBrick, false, false);
                    currPoint.xLeft = titleXLeft;
                }
                composite.addBrick(await SurveyHelper.createTextFlat(currPoint, this.controller, requiredText, requiredStyle));
            }
        }
        return composite;
    }
    async generateFlatDescription(point) {
        return await SurveyHelper.createTextFlat(point, this.controller, this.question.locDescription, { ...this.style.description });
    }
    async generateFlatHeader(point) {
        const containerBrick = new ContainerBrick(this.controller, {
            ...point,
            width: SurveyHelper.getPageAvailableWidth(this.controller)
        }, this.style.header);
        await containerBrick.setup(async (point, bricks) => {
            const titleFlat = await this.generateFlatTitle(point);
            bricks.push(titleFlat);
            if (this.question.hasDescriptionUnderTitle) {
                const descPoint = SurveyHelper.createPoint(titleFlat, true, false);
                descPoint.yTop += this.style.spacing.titleDescriptionGap;
                descPoint.xLeft += this.style.spacing.contentIndentStart;
                bricks.push(await this.generateFlatDescription(descPoint));
            }
        });
        return containerBrick;
    }
    async generateFlatsComment(point) {
        const text = this.question.locCommentText;
        const otherTextFlat = await SurveyHelper.createTextFlat(point, this.controller, text, this.style.commentLabel);
        const otherPoint = SurveyHelper.createPoint(otherTextFlat);
        otherPoint.yTop += this.style.spacing.commentLabelGap;
        const shouldRenderReadOnly = SurveyHelper.shouldRenderReadOnly(this.question, this.controller, this.question.isReadOnly);
        const style = SurveyHelper.getPatchedTextStyle(this.controller, SurveyHelper.mergeObjects({}, this.style.comment, shouldRenderReadOnly ? this.style.commentReadOnly : undefined));
        return new CompositeBrick(otherTextFlat, await SurveyHelper.createCommentFlat(otherPoint, this.controller, {
            fieldName: this.question.id + '_comment',
            rows: this.controller.otherRowsCount,
            value: this.question.comment !== undefined && this.question.comment !== null ? this.question.comment : '',
            shouldRenderBorders: settings.readOnlyCommentRenderMode === 'textarea',
            shouldRenderReadOnly,
            isReadOnly: this.question.isReadOnly,
            isMultiline: true,
            placeholder: ''
        }, style));
    }
    async generateFlatsComposite(point) {
        const contentPanel = this.question.contentPanel;
        if (!!contentPanel) {
            return await SurveyHelper.generatePanelFlats(this.survey, this.controller, contentPanel, point);
        }
        this.question = SurveyHelper.getContentQuestion(this.question);
        return await this.generateFlatsContent(point);
    }
    async generateFlatsContent(point) {
        return null;
    }
    async generateFlatsContentWithOptionalElements(point) {
        const flats = [];
        const contentFlats = await this.generateFlatsComposite(point);
        if (Array.isArray(contentFlats)) {
            flats.push(...contentFlats);
        }
        const currPoint = SurveyHelper.clone(point);
        if (contentFlats && contentFlats.length > 0) {
            currPoint.yTop = SurveyHelper.mergeRects(...contentFlats).yBot;
        }
        if (this.question.hasComment) {
            currPoint.yTop += this.style.spacing.contentCommentGap;
            flats.push(await this.generateFlatsComment(currPoint));
        }
        if (this.question.hasDescriptionUnderInput) {
            const descriptionContentBrick = new CompositeBrick();
            if (flats !== null && flats.length !== 0) {
                descriptionContentBrick.addBrick(flats.pop());
            }
            currPoint.yTop += this.style.spacing.contentDescriptionGap;
            descriptionContentBrick.addBrick(await this.generateFlatDescription(currPoint));
            flats.push(descriptionContentBrick);
        }
        return flats;
    }
    async generateFlats(point) {
        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';
        const titleLocationMatrix = 'matrix';
        switch (titleLocation) {
            case 'top':
            case 'default': {
                const compositeBrick = new CompositeBrick();
                const headerFlat = await this.generateFlatHeader(indentPoint);
                compositeBrick.addBrick(headerFlat);
                let contentPoint = SurveyHelper.createPoint(headerFlat);
                const indent = this.style.spacing.contentIndentStart;
                contentPoint.xLeft += indent;
                compositeBrick.addBrick(SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(headerFlat), this.controller));
                contentPoint.yTop += this.style.spacing.headerContentGap + SurveyHelper.EPSILON;
                this.controller.pushMargins();
                this.controller.margins.left += indent;
                const contentFlats = await this.generateFlatsContentWithOptionalElements(contentPoint);
                this.controller.popMargins();
                if (contentFlats !== null && contentFlats.length !== 0) {
                    compositeBrick.addBrick(contentFlats.shift());
                }
                flats.push(compositeBrick);
                flats.push(...contentFlats);
                break;
            }
            case 'bottom': {
                const contentPoint = SurveyHelper.clone(indentPoint);
                const contentHeaderBrick = new CompositeBrick();
                this.controller.pushMargins();
                const indent = this.style.spacing.contentIndentStart;
                contentPoint.xLeft += indent;
                this.controller.margins.left += indent;
                const contentFlats = await this.generateFlatsContentWithOptionalElements(contentPoint);
                this.controller.popMargins();
                if (contentFlats !== null && contentFlats.length !== 0) {
                    contentHeaderBrick.addBrick(contentFlats.pop());
                }
                const titlePoint = indentPoint;
                if (flats.length !== 0) {
                    titlePoint.yTop = flats[flats.length - 1].yBot;
                }
                titlePoint.yTop += this.style.spacing.headerContentGap;
                contentHeaderBrick.addBrick(await this.generateFlatHeader(titlePoint));
                flats.push(...contentFlats);
                flats.push(contentHeaderBrick);
                break;
            }
            case 'left': {
                this.controller.pushMargins(this.controller.margins.left, this.controller.paperWidth - this.controller.margins.left -
                    SurveyHelper.getPageAvailableWidth(this.controller) * this.style.inlineHeaderWidthPercentage);
                const headerFlat = await this.generateFlatHeader(indentPoint);
                const contentPoint = SurveyHelper.createPoint(headerFlat, false, true);
                this.controller.popMargins();
                contentPoint.xLeft += this.style.spacing.inlineHeaderContentGap;
                this.controller.margins.left = contentPoint.xLeft;
                const contentFlats = await this.generateFlatsContentWithOptionalElements(contentPoint);
                if (contentFlats !== null && contentFlats.length !== 0) {
                    headerFlat.addBrick(contentFlats.shift());
                }
                flats.push(headerFlat);
                flats.push(...contentFlats);
                break;
            }
            case 'hidden':
            case titleLocationMatrix:
            default: {
                const contentPoint = SurveyHelper.clone(indentPoint);
                this.controller.pushMargins();
                if (titleLocation !== titleLocationMatrix) {
                    const indent = this.style.spacing.contentIndentStart;
                    contentPoint.xLeft += indent;
                    this.controller.margins.left += indent;
                }
                flats.push(...await this.generateFlatsContentWithOptionalElements(contentPoint));
                this.controller.popMargins();
                break;
            }
        }
        this.controller.popMargins();
        const adornersOptions = new AdornersOptions(point, flats, this.question, this.controller, FlatRepository.getInstance());
        if (this.question.customWidget && this.question.customWidget.isFit(this.question) &&
            this.question.customWidget.pdfRender) {
            this.survey.onRenderQuestion.unshift(this.question.customWidget.pdfRender);
        }
        await this.survey.onRenderQuestion.fire(this.survey, adornersOptions);
        const bricks = [...adornersOptions.bricks];
        this.survey.afterRenderSurveyElement(this.question, bricks);
        return bricks;
    }
}
Serializer.addProperty('question', {
    name: 'readonlyRenderAs',
    default: 'auto',
    choices: ['auto', 'text', 'acroform'],
    visible: false
});

class FlatQuestionDefault extends FlatQuestion {
    async generateFlatsContent(point) {
        const valueBrick = await SurveyHelper.createTextFlat(point, this.controller, `${this.question.displayValue}`);
        return [valueBrick];
    }
}

class FlatRepository {
    constructor() {
        this.questions = {};
    }
    static getInstance() {
        return FlatRepository.instance;
    }
    register(modelType, rendererConstructor) {
        this.questions[modelType] = rendererConstructor;
    }
    registerPanel(rendererConstructor) {
        this.panel = rendererConstructor;
    }
    registerPage(rendererConstructor) {
        this.page = rendererConstructor;
    }
    registerSurvey(rendererConstructor) {
        this.survey = rendererConstructor;
    }
    isTypeRegistered(type) {
        return !!this.questions[type];
    }
    getRenderer(type) {
        return this.questions[type];
    }
    create(survey, question, docController, style, 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, style);
    }
    createPanel(survey, panel, docController, style) {
        return new this.panel(survey, panel, docController, style);
    }
    createPage(survey, page, docController, style) {
        return new this.page(survey, page, docController, style);
    }
    createSurvey(survey, docController, style) {
        return new this.survey(survey, docController, style);
    }
    static registerPanel(rendererConstructor) {
        this.getInstance().registerPanel(rendererConstructor);
    }
    static registerPage(rendererConstructor) {
        this.getInstance().registerPage(rendererConstructor);
    }
    static registerSurvey(rendererConstructor) {
        this.getInstance().registerSurvey(rendererConstructor);
    }
    static register(type, rendererConstructor) {
        this.getInstance().register(type, rendererConstructor);
    }
    static getRenderer(type) {
        return this.getInstance().getRenderer(type);
    }
}
FlatRepository.instance = new FlatRepository();

class EventAsync extends EventBase {
    constructor() {
        super(...arguments);
        this.isProcessing = false;
    }
    unshift(func) {
        if (this.hasFunc(func))
            return;
        if (this.callbacks == null) {
            this.callbacks = new Array();
        }
        this.callbacks.unshift(func);
    }
    async fire(sender, options) {
        if (this.callbacks == null || this.isProcessing)
            return;
        this.isProcessing = true;
        for (var i = 0; i < this.callbacks.length; i++) {
            await this.callbacks[i](sender, options);
            this.isProcessing = false;
        }
    }
}

/**
 * An object that describes a PDF brick&mdash;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(controller, rect, options = {}) {
        this.controller = controller;
        this.options = options;
        this.isPageBreak = false;
        this.beforeRenderEvent = new EventAsync();
        this.padding = { top: 0, bottom: 0 };
        this.xLeft = rect.xLeft;
        this.xRight = rect.xRight;
        this.yTop = rect.yTop;
        this.yBot = rect.yBot;
    }
    translateY(func) {
        const res = func(this.yTop, this.yBot);
        this.yTop = res.yTop;
        this.yBot = res.yBot;
    }
    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 this.options.shouldRenderReadOnly;
    }
    async render() {
        await this.beforeRenderEvent.fire(this, {});
        if (this.getShouldRenderReadOnly()) {
            await this.renderReadOnly();
        }
        else
            await this.renderInteractive();
        this.afterRenderCallback && this.afterRenderCallback();
    }
    async renderInteractive() { }
    async renderReadOnly() {
        await 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;
        this.resetContentRect();
    }
    setXRight(val) {
        this._xRight = val;
        this.resetContentRect();
    }
    setYTop(val) {
        this._yTop = val;
        this.resetContentRect();
    }
    setYBottom(val) {
        this._yBot = val;
        this.resetContentRect();
    }
    getPageNumber() {
        return this._pageNumber;
    }
    setPageNumber(val) {
        this._pageNumber = val;
    }
    addBeforeRenderCallback(func) {
        this.beforeRenderEvent.unshift(func);
    }
    increasePadding(padding) {
        if (padding.top == 0 && padding.bottom == 0)
            return;
        Object.keys(this.padding).forEach((key) => {
            this.padding[key] += padding[key];
        });
        this._yTop -= padding.top;
        this._yBot += padding.bottom;
        this.resetContentRect();
    }
    get contentRect() {
        if (!this._contentRect) {
            this._contentRect = this.getContentRect();
        }
        return this._contentRect;
    }
    resetContentRect() {
        this._contentRect = undefined;
    }
    getContentRect() {
        const yBot = this.yBot - this.padding.bottom;
        const yTop = this.yTop + this.padding.top;
        return {
            yBot, yTop,
            xLeft: this.xLeft,
            xRight: this.xRight,
            width: this.width,
            height: Math.max(yBot - yTop, 0)
        };
    }
    updateRect() { }
    get isEmpty() {
        return false;
    }
}

class TextBrick extends PdfBrick {
    constructor(controller, rect, options, style) {
        super(controller, rect, options);
        this.options = options;
        this.style = style;
        this.align = {
            isInputRtl: false,
            isOutputRtl: controller.isRTL,
            align: controller.isRTL ? 'right' : 'left',
            baseline: 'middle',
            lineHeightFactor: 1.15
        };
    }
    escapeText() {
        return this.options.text.replace(/\t/g, Array(5).join(String.fromCharCode(160)));
    }
    async renderInteractive() {
        const alignPoint = this.alignPoint(this.contentRect);
        this.controller.setTextStyle(this.style);
        this.controller.doc.text(this.escapeText(), alignPoint.xLeft, alignPoint.yTop, this.align);
        this.controller.restoreTextStyle();
    }
    alignPoint(rect) {
        return {
            xLeft: this.controller.isRTL ? rect.xRight : rect.xLeft,
            yTop: rect.yTop + (rect.yBot - rect.yTop) / 2.0
        };
    }
}

class LinkBrick extends TextBrick {
    constructor(controller, rect, options, style) {
        super(controller, rect, options, style);
        this.options = options;
        this.style = style;
    }
    async renderInteractive() {
        this.controller.setTextStyle(SurveyHelper.mergeObjects({}, this.style, { fontColor: '#FFFFFF00' }));
        let descent = this.controller.unitHeight *
            (this.controller.doc.getLineHeightFactor() -
                LinkBrick.SCALE_FACTOR_MAGIC);
        let yTopLink = this.contentRect.yTop +
            (this.contentRect.yBot - this.contentRect.yTop) - descent;
        this.controller.doc.textWithLink(this.options.text, this.contentRect.xLeft, yTopLink, { url: this.options.link });
        this.controller.restoreTextStyle();
        await super.renderInteractive();
    }
    async renderReadOnly() {
        if (this.options.readOnlyShowLink) {
            super.renderInteractive();
        }
        else {
            this.renderInteractive();
        }
    }
}
LinkBrick.SCALE_FACTOR_MAGIC = 0.955;

class HTMLBrick extends PdfBrick {
    constructor(controller, rect, options, style) {
        super(controller, rect);
        this.options = options;
        this.style = style;
    }
    async renderInteractive() {
        this.controller.setTextStyle(this.style);
        await new Promise((resolve) => {
            this.controller.doc.fromHTML(this.options.html, this.contentRect.xLeft, this.contentRect.yTop, {
                width: this.contentRect.width, pagesplit: true,
            }, function () {
                [].slice.call(document.querySelectorAll('.sjs-pdf-hidden-html-div')).forEach(function (el) {
                    el.parentNode.removeChild(el);
                });
                resolve();
            }, {
                top: this.controller.margins.top,
                bottom: this.controller.margins.bot
            });
        });
        this.controller.restoreTextStyle();
    }
}

class ImageBrick extends PdfBrick {
    constructor(controller, rect, options) {
        super(controller, rect);
        this.options = options;
        this.isPageBreak = this.options.height === undefined;
    }
    async renderInteractive() {
        const imageWidth = this.options.width || this.contentRect.width;
        const imageHeight = this.options.height || this.contentRect.height;
        const targetWidth = this.contentRect.width;
        const targetHeight = this.contentRect.height;
        const xLeft = targetWidth > imageWidth ? this.contentRect.xLeft + (targetWidth - imageWidth) / 2 : this.contentRect.xLeft;
        const yTop = targetHeight > imageHeight ? this.contentRect.yTop + (targetHeight - imageHeight) / 2 : this.contentRect.yTop;
        await new Promise((resolve) => {
            try {
                this.controller.doc.addImage(this.options.link, 'PNG', xLeft, yTop, Math.min(imageWidth, targetWidth), Math.min(imageHeight, targetHeight), this.options.imageId, 'MEDIUM');
            }
            finally {
                resolve();
            }
        });
    }
}

class EmptyBrick extends PdfBrick {
    constructor(controller, rect, style = {}) {
        super(controller, rect);
        this.style = style;
    }
    async renderInteractive() {
        if (this.style.color) {
            this.controller.setFillColor(this.style.color);
            const { lines: docLines, point } = SurveyHelper.getDocLinesFromShape(SurveyHelper.createRoundedShape(this.contentRect, this.style));
            this.controller.doc.lines(docLines, ...point, [1, 1], 'F', true);
            this.controller.restoreFillColor();
        }
        SurveyHelper.renderFlatBorders(this.controller, this.contentRect, this.style);
    }
}

class RowlineBrick {
    constructor(controller, rect, color) {
        this.controller = controller;
        this.color = color;
        this.isPageBreak = false;
        this.beforeRenderEvent = new EventAsync();
        this.padding = { top: 0, bottom: 0 };
        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;
    }
    async render() {
        await this.beforeRenderEvent.fire(this, {});
        if (this.color !== null) {
            this.controller.setDrawColor(this.color);
            this.controller.doc.line(this.contentRect.xLeft, this.yTop, this.contentRect.xRight, this.contentRect.yTop);
            this.controller.restoreDrawColor();
        }
    }
    getPageNumber() {
        return this._pageNumber;
    }
    setPageNumber(val) {
        this._pageNumber = val;
    }
    addBeforeRenderCallback(func) {
        this.beforeRenderEvent.add(func);
    }
    unfold() {
        return [this];
    }
    translateX(_) { }
    translateY(func) {
        const res = func(this.yTop, this.yBot);
        this.yTop = res.yTop;
        this.yBot = res.yBot;
    }
    increasePadding(padding) {
        if (padding.top == 0 && padding.bottom == 0)
            return;
        Object.keys(this.padding).forEach((key) => {
            this.padding[key] += padding[key];
        });
        this.yTop -= padding.top;
        this.yBot += padding.bottom;
        this._contentRect = undefined;
    }
    get contentRect() {
        if (!this._contentRect) {
            const yTop = this.yTop += this.padding.top;
            const yBot = this.yBot -= this.padding.bottom;
            this._contentRect = {
                yBot, yTop,
                xLeft: this.xLeft,
                xRight: this.xRight,
                width: this.width,
                height: yBot - yTop
            };
        }
        return this._contentRect;
    }
    updateRect() { }
    get isEmpty() {
        return this.color === null;
    }
}

class TextFieldBrick extends PdfBrick {
    constructor(controller, rect, options, style) {
        var _a, _b, _c, _d;
        super(controller, rect);
        this.options = options;
        this.style = style;
        options.isMultiline = (_a = options.isMultiline) !== null && _a !== void 0 ? _a : false;
        options.placeholder = (_b = options.placeholder) !== null && _b !== void 0 ? _b : '';
        options.inputType = (_c = options.inputType) !== null && _c !== void 0 ? _c : '';
        options.value = (_d = options.value) !== null && _d !== void 0 ? _d : '';
    }
    renderColorQuestion() {
        this.controller.setFillColor(this.options.value || 'black');
        this.controller.doc.rect(this.contentRect.xLeft, this.contentRect.yTop, this.contentRect.width, this.contentRect.height, 'F');
        this.controller.restoreFillColor();
    }
    async renderInteractive() {
        var _a;
        if (this.options.inputType === 'color') {
            this.renderColorQuestion();
            return;
        }
        const scaledAcroformRect = SurveyHelper.createAcroformRect(SurveyHelper.createRectInsideBorders(this.contentRect, (_a = this.style.borderWidth) !== null && _a !== void 0 ? _a : 0));
        if (this.style.backgroundColor) {
            this.controller.setFillColor(this.style.backgroundColor);
            this.controller.doc.rect(...scaledAcroformRect, 'F');
            this.controller.restoreFillColor();
        }
        const { color: fontColor } = SurveyHelper.parseColor(this.style.fontColor);
        const inputField = this.options.inputType === 'password' ?
            new this.controller.doc.AcroFormPasswordField() :
            new (this.controller.AcroFormTextField)();
        inputField.fieldName = this.options.fieldName;
        inputField.fontName = this.style.fontName;
        inputField.fontSize = this.style.fontSize;
        inputField.maxFontSize = this.style.fontSize;
        inputField.isUnicode = SurveyHelper.isCustomFont(this.controller, inputField.fontName);
        if (this.options.inputType !== 'password') {
            inputField.value = this.getCorrectedText(this.options.value);
            inputField.defaultValue = this.getCorrectedText(this.options.placeholder);
        }
        else
            inputField.value = '';
        inputField.multiline = this.options.isMultiline;
        inputField.readOnly = this.options.isReadOnly;
        inputField.color = fontColor;
        inputField.Rect = scaledAcroformRect;
        this.controller.doc.addField(inputField);
        SurveyHelper.renderFlatBorders(this.controller, this.contentRect, this.style);
    }
    shouldRenderFlatBorders() {
        return this.options.shouldRenderBorders;
    }
    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.contentRect);
                if (renderedBricksCount >= bricksCount) {
                    const keys = Object.keys(bricksByPage);
                    const renderedOnOnePage = keys.length == 1;
                    keys.forEach((key) => {
                        const mergedRect = mergeRects(...bricksByPage[key]);
                        const borderRect = {
                            xLeft: this.contentRect.xLeft,
                            xRight: this.contentRect.xRight,
                            width: this.contentRect.width,
                            yTop: renderedOnOnePage ? this.contentRect.yTop : mergedRect.yTop,
                            yBot: renderedOnOnePage ? this.contentRect.yBot : mergedRect.yBot,
                            height: renderedOnOnePage ? this.contentRect.height : mergedRect.yBot - mergedRect.yTop,
                        };
                        this.controller.setPage(Number(key));
                        SurveyHelper.renderFlatBorders(this.controller, borderRect, this.style);
                        this.controller.setPage(currentPageNumber);
                    });
                }
            }
        };
        unFoldedBricks.forEach((brick) => {
            brick.afterRenderCallback = afterRenderTextBrickCallback.bind(this.contentRect, brick);
        });
        this.updateRect();
    }
    updateRect() {
        if (this.textBrick) {
            const width = this.width;
            this.textBrick.updateRect();
            this._xLeft = this.textBrick.xLeft;
            this._xRight = this.textBrick.xLeft + width;
            this._yTop = this.textBrick.yTop;
            this._yBot = this.textBrick.yBot;
        }
    }
    async renderReadOnly() {
        this.controller.pushMargins(this.contentRect.xLeft, this.controller.paperWidth - this.contentRect.xRight);
        if (this.options.inputType === 'color') {
            this.renderColorQuestion();
        }
        else {
            await this.textBrick.render();
        }
        this.controller.popMargins();
    }
    unfold() {
        if (this.getShouldRenderReadOnly() && this.options.inputType !== 'color') {
            return this.textBrick.unfold();
        }
        else {
            return super.unfold();
        }
    }
    translateX(func) {
        const res = func(this.contentRect.xLeft, this.contentRect.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;
        }
    }
    setPageNumber(val) {
        if (this.getShouldRenderReadOnly() && this.options.inputType !== 'color') {
            this.textBrick.setPageNumber(val);
        }
        else {
            super.setPageNumber(val);
        }
    }
    getPageNumber() {
        if (this.getShouldRenderReadOnly() && this.options.inputType !== 'color') {
            return this.textBrick.getPageNumber();
        }
        else {
            return super.getPageNumber();
        }
    }
    increasePadding(val) {
        if (val.top == 0 && val.bottom == 0)
            return;
        if (this.getShouldRenderReadOnly() && this.options.inputType !== 'color') {
            this.textBrick.increasePadding(val);
            this.updateRect();
        }
        else {
            super.increasePadding(val);
        }
    }
    addBeforeRenderCallback(func) {
        if (this.getShouldRenderReadOnly() && this.options.inputType !== 'color') {
            this.textBrick.addBeforeRenderCallback(func);
        }
        else {
            super.addBeforeRenderCallback(func);
        }
    }
    getContentRect() {
        if (this.textBrick)
            return {
                ...this.textBrick.contentRect,
                xLeft: this.xLeft,
                xRight: this.xRight,
                width: this.xRight - this.xLeft
            };
        else
            return super.getContentRect();
    }
}

class BaseImageUtils {
    constructor() {
        this.hash = {};
        this.imageId = 1;
    }
    getImageId() {
        return `image_${this.imageId++}`;
    }
    async _getImageInfo(url) {
        return { data: url, width: 0, height: 0, id: this.getImageId() };
    }
    async getImageInfo(url) {
        if (!this.hash[url]) {
            try {
                this.hash[url] = await this._getImageInfo(url);
            }
            catch {
                this.hash[url] = this.emptyImage;
            }
        }
        return this.hash[url];
    }
    async applyImageFit(imageInfo, imageFit, targetWidth, targetHeight) {
        if (imageFit == 'fill') {
            return { data: imageInfo.data, id: imageInfo.id, width: targetWidth, height: targetHeight };
        }
        if ((imageFit == 'contain' || imageFit == 'cover') && !!imageInfo.width && !!imageInfo.height && !!targetWidth && !!targetWidth) {
            const scale = Math.min(targetWidth / imageInfo.width, targetHeight / imageInfo.height);
            return { data: imageInfo.data, id: imageInfo.id, width: imageInfo.width * scale, height: imageInfo.height * scale };
        }
        else {
            return imageInfo;
        }
    }
    get emptyImage() {
        return { data: '', width: 0, height: 0, id: 'image_0' };
    }
    clear() {
        this.hash = {};
        this.imageId = 1;
    }
}
let imageUtils = new BaseImageUtils();
function getImageUtils() {
    return imageUtils;
}
function registerImageUtils(val) {
    imageUtils = val;
}

var BorderMode;
(function (BorderMode) {
    BorderMode[BorderMode["Inside"] = 0] = "Inside";
    BorderMode[BorderMode["Middle"] = 1] = "Middle";
    BorderMode[BorderMode["Outside"] = 2] = "Outside";
})(BorderMode || (BorderMode = {}));
var BorderRect;
(function (BorderRect) {
    BorderRect[BorderRect["None"] = 0] = "None";
    BorderRect[BorderRect["Top"] = 1] = "Top";
    BorderRect[BorderRect["Bottom"] = 2] = "Bottom";
    BorderRect[BorderRect["Right"] = 4] = "Right";
    BorderRect[BorderRect["Left"] = 8] = "Left";
    BorderRect[BorderRect["All"] = 15] = "All";
})(BorderRect || (BorderRect = {}));
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 parseColor(color) {
        let opacity;
        let match;
        if ((color !== null && color !== void 0 ? color : '').match(/^rgba/)) {
            const matches = color.match(/[\d.]+/g);
            if (matches.length == 4) {
                color = `rgb(${matches[0]}, ${matches[1]}, ${matches[2]})`;
                opacity = parseFloat(matches[3]);
            }
        }
        else if ((match = (color !== null && color !== void 0 ? color : '').match(/(#[A-Fa-f0-9]{6})([A-Fa-f0-9]{2})/))) {
            color = match[1];
            opacity = (Math.round(parseInt(match[2], 16) / 255 * 100)) / 100;
        }
        return { color, opacity };
    }
    static mergeRects(...rects) {
        return mergeRects(...rects);
    }
    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, fontName) {
        return controller.useCustomFontInHtml ? fontName !== null && fontName !== void 0 ? fontName : controller.fontName : 'helvetica';
    }
    static generateCssTextRule(style) {
        return `"font-size: ${style.fontSize}pt; font-weight: ${style.fontStyle}; font-family: ${style.fontName}; color: ${SurveyHelper.parseColor(style.fontColor).color}; lineHeight: ${style.lineHeight}; margin: 0"`;
    }
    static createHtmlContainerBlock(html, controller, style) {
        const newStyle = SurveyHelper.getPatchedTextStyle(controller, style);
        this.chooseHtmlFont(controller, newStyle.fontName);
        return `<div class="__surveypdf_html" style=${this.generateCssTextRule(newStyle)}>` +
            `<style>.__surveypdf_html p { margin: 0; line-height: ${newStyle.lineHeight}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(controller, this.createRect(currPoint, htmlBrick.width, minHeight)));
            currPoint.yTop += minHeight;
        }
        const remainingHeight = htmlHeight - (emptyBrickCount + 1) * minHeight;
        if (remainingHeight > 0) {
            bricks.push(new EmptyBrick(controller, this.createRect(currPoint, htmlBrick.width, remainingHeight)));
        }
        return new CompositeBrick(...bricks);
    }
    static createPlainTextFlat(point, controller, text, style) {
        controller.setTextStyle(style, true);
        const lines = controller.helperDoc.splitTextToSize(text, controller.paperWidth - controller.margins.right - point.xLeft);
        controller.restoreTextStyle(true);
        const currPoint = this.clone(point);
        const composite = new CompositeBrick();
        lines.forEach((text) => {
            const size = controller.measureText(text, style);
            composite.addBrick(new TextBrick(controller, this.createRect(currPoint, size.width, size.height), { text }, style));
            currPoint.yTop += size.height;
        });
        if (style.textAlign == 'right') {
            const spaceXRight = point.xLeft + SurveyHelper.getPageAvailableWidth(controller);
            composite.translateX((xLeft, xRight) => {
                return { xLeft: xLeft + (spaceXRight - xRight), xRight: spaceXRight };
            });
        }
        if (style.textAlign == 'center') {
            const spaceXCenter = point.xLeft + SurveyHelper.getPageAvailableWidth(controller) / 2;
            composite.translateX((xLeft, xRight) => {
                const shift = spaceXCenter - (xLeft + xRight) / 2;
                return { xLeft: xLeft + shift, xRight: xRight + shift };
            });
        }
        return composite;
    }
    static async createTextFlat(point, controller, text, style) {
        var _a;
        const newStyle = { ...SurveyHelper.getPatchedTextStyle(controller, style), textAlign: (_a = style === null || style === void 0 ? void 0 : style.textAlign) !== null && _a !== void 0 ? _a : 'left' };
        let result;
        if (typeof text === 'string' || !this.hasHtml(text)) {
            result = this.createPlainTextFlat(point, controller, typeof text === 'string' ?
                text : this.getLocString(text), newStyle);
        }
        else {
            result = this.splitHtmlRect(controller, await this.createHTMLFlat(point, controller, this.createHtmlContainerBlock(this.getLocString(text), controller, newStyle), newStyle));
        }
        return result;
    }
    static mergeObjects(dest, ...sources) {
        sources.forEach(source => {
            if (!source)
                return;
            Object.keys(source).forEach(key => {
                var _a;
                if (typeof source[key] == 'object' && source[key] !== null && !Array.isArray(source[key])) {
                    dest[key] = SurveyHelper.mergeObjects((_a = dest[key]) !== null && _a !== void 0 ? _a : {}, source[key]);
                }
                else {
                    dest[key] = source[key];
                }
            });
        });
        return dest;
    }
    static getPatchedTextStyle(controller, style) {
        const newStyle = SurveyHelper.mergeObjects(SurveyHelper.getDefaultTextStyle(controller), style !== null && style !== void 0 ? style : {});
        if (style && style.lineHeight == undefined) {
            newStyle.lineHeight = newStyle.fontSize;
        }
        return newStyle;
    }
    static getDefaultTextStyle(controller) {
        return {
            fontSize: controller.fontSize,
            fontName: controller.fontName,
            fontStyle: controller.fontStyle,
            lineHeight: controller.fontSize,
            fontColor: '#404040'
        };
    }
    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, style) {
        const newStyle = this.getPatchedTextStyle(controller, style);
        const availablePageHeight = controller.paperHeight - controller.margins.bot - controller.margins.top;
        const height = (controller.helperDoc.getNumberOfPages() - 1) *
            (newStyle.fontSize * Math.floor(availablePageHeight / newStyle.fontSize))
            + resultY - margins.top + SurveyHelper.HTML_TAIL_TEXT_SCALE * newStyle.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 async createHTMLFlat(point, controller, html, style) {
        const margins = this.getHtmlMargins(controller, point);
        const newStyle = this.getPatchedTextStyle(controller, style);
        return await 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, newStyle);
                resolve(new HTMLBrick(controller, rect, { html }, newStyle));
            }, 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(controller, canvas, divWidth, divHeight, img) {
        canvas.width = divWidth * controller.htmlToImageQuality;
        canvas.height = divHeight * controller.htmlToImageQuality;
        const context = canvas.getContext('2d');
        context.scale(controller.htmlToImageQuality, controller.htmlToImageQuality);
        context.fillStyle = '#FFFFFF';
        context.fillRect(0, 0, divWidth, divHeight);
        context.drawImage(img, 0, 0);
    }
    static async htmlToImage(html, width, controller) {
        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(controller, canvas, divWidth, divHeight, img);
                const url = canvas.toDataURL('image/jpeg', controller.htmlToImageQuality);
                canvas.remove();
                resolve({ url: url, aspect: divWidth / divHeight });
            };
            img.onerror = function () {
                resolve({ url: 'data:,', aspect: width / this.EPSILON });
            };
        });
    }
    static getReadonlyRenderAs(question, controller) {
        return question.readonlyRenderAs === 'auto' ? controller.readonlyRenderAs : question.readonlyRenderAs;
    }
    static async createCommentFlat(point, controller, options, style) {
        var _a, _b;
        const newStyle = SurveyHelper.getPatchedTextStyle(controller, style);
        options.rows = (_a = options.rows) !== null && _a !== void 0 ? _a : 1;
        options.value = (_b = options.value) !== null && _b !== void 0 ? _b : '';
        const rect = this.createTextFieldRect(point, controller, options.rows, newStyle.lineHeight);
        let textFlat;
        if (options.shouldRenderReadOnly) {
            textFlat = await this.createReadOnlyTextFieldTextFlat(point, controller, options.value, newStyle);
        }
        const comment = new TextFieldBrick(controller, rect, options, newStyle);
        if (textFlat) {
            comment.textBrick = textFlat;
        }
        return comment;
    }
    static get hasDocument() {
        return typeof document !== 'undefined';
    }
    static async createImageFlat(point, question, controller, options, applyImageFit) {
        const imageUtils = getImageUtils();
        let imageInfo = await imageUtils.getImageInfo(options.link);
        if (applyImageFit !== null && applyImageFit !== void 0 ? applyImageFit : controller.applyImageFit) {
            imageInfo = await imageUtils.applyImageFit(imageInfo, options.objectFit || 'fill', options.width, options.height);
        }
        const rect = SurveyHelper.createRect(point, options.width, options.height);
        return new ImageBrick(controller, rect, { width: imageInfo.width, height: imageInfo.height, imageId: imageInfo.id, link: imageInfo.data });
    }
    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 async createLinkFlat(point, controller, options, style) {
        const newStyle = SurveyHelper.getPatchedTextStyle(controller, style);
        const compositeText = await this.
            createTextFlat(point, controller, options.text, newStyle);
        const compositeLink = new CompositeBrick();
        compositeText.unfold().forEach((text) => {
            compositeLink.addBrick(new LinkBrick(controller, text, {
                link: options.link,
                text: text.options.text,
                readOnlyShowLink: options.readOnlyShowLink,
                shouldRenderReadOnly: options.shouldRenderReadOnly
            }, newStyle));
            const linePoint = this.createPoint(compositeLink);
            compositeLink.addBrick(this.createRowlineFlat(linePoint, controller, compositeLink.width, newStyle.fontColor));
        });
        return compositeLink;
    }
    static createAcroformRect(rect) {
        return [
            rect.xLeft,
            rect.yTop,
            rect.xRight - rect.xLeft,
            rect.yBot - rect.yTop
        ];
    }
    static createTextFieldRect(point, controller, lines = 1, lineHeight = controller.unitHeight) {
        let width = controller.paperWidth - point.xLeft - controller.margins.right;
        width = Math.max(width, controller.unitWidth);
        const height = lineHeight * lines;
        return this.createRect(point, width, height);
    }
    static async createReadOnlyTextFieldTextFlat(point, controller, value, style) {
        controller.pushMargins(point.xLeft, controller.margins.right);
        const textFlat = await this.createTextFlat(point, controller, value.toString(), style);
        controller.popMargins();
        return textFlat;
    }
    static createRoundedShape(rect, style, mergeAngles = true) {
        var _a;
        const parsedMergeAngles = typeof mergeAngles == 'object' ? mergeAngles : { top: mergeAngles, bot: mergeAngles, left: mergeAngles, right: mergeAngles };
        const parsedRadius = parseSideValues((_a = style.borderRadius) !== null && _a !== void 0 ? _a : 0);
        Object.keys(parsedRadius).forEach((side) => {
            parsedRadius[side] = Math.max(0, Math.min(parsedRadius[side], (rect.xRight - rect.xLeft) / 2, (rect.yBot - rect.yTop) / 2));
        });
        function calcAngleLine(x, y, r, angle, rotAngle) {
            const l = 4 / 3 * Math.tan(angle / 4) * r;
            const base = [
                [r, 0],
                [r, l],
                [r * Math.cos(angle) + l * Math.sin(angle), r * Math.sin(angle) - l * Math.cos(angle)],
                [r * Math.cos(angle), r * Math.sin(angle)],
            ];
            return base.reduce((acc, point) => {
                acc.push(Math.cos(rotAngle) * point[0] - Math.sin(rotAngle) * point[1] + x);
                acc.push(Math.sin(rotAngle) * point[0] + Math.cos(rotAngle) * point[1] + y);
                return acc;
            }, []);
        }
        const lines = new Map();
        lines.set('top', [[rect.xLeft + parsedRadius.top, rect.yTop, rect.xRight - parsedRadius.right, rect.yTop]]);
        lines.set('right', [[rect.xRight, rect.yTop + parsedRadius.right, rect.xRight, rect.yBot - parsedRadius.bot]]);
        lines.set('bot', [[rect.xRight - parsedRadius.bot, rect.yBot, rect.xLeft + parsedRadius.left, rect.yBot]]);
        lines.set('left', [[rect.xLeft, rect.yBot - parsedRadius.left, rect.xLeft, rect.yTop + parsedRadius.top]]);
        if (parsedRadius.top) {
            const angle = parsedMergeAngles.top ? Math.PI / 2 : Math.PI / 4;
            const r = parsedRadius.top;
            lines.get('left').push(calcAngleLine(rect.xLeft + r, rect.yTop + r, r, angle, Math.PI));
            if (!parsedMergeAngles.top) {
                lines.get('top').unshift(calcAngleLine(rect.xLeft + r, rect.yTop + r, r, angle, Math.PI + angle));
            }
        }
        if (parsedRadius.right) {
            const angle = parsedMergeAngles.right ? Math.PI / 2 : Math.PI / 4;
            const r = parsedRadius.right;
            lines.get('top').push(calcAngleLine(rect.xRight - r, rect.yTop + r, r, angle, 3 / 2 * Math.PI));
            if (!parsedMergeAngles.right) {
                lines.get('right').unshift(calcAngleLine(rect.xRight - r, rect.yTop + r, r, angle, 3 / 2 * Math.PI + angle));
            }
        }
        if (parsedRadius.bot) {
            const angle = parsedMergeAngles.bot ? Math.PI / 2 : Math.PI / 4;
            const r = parsedRadius.bot;
            lines.get('right').push(calcAngleLine(rect.xRight - r, rect.yBot - r, r, angle, 0));
            if (!parsedMergeAngles.bot) {
                lines.get('bot').unshift(calcAngleLine(rect.xRight - r, rect.yBot - r, r, angle, angle));
            }
        }
        if (parsedRadius.left) {
            const angle = parsedMergeAngles.left ? Math.PI / 2 : Math.PI / 4;
            const r = parsedRadius.left;
            lines.get('bot').push(calcAngleLine(rect.xLeft + r, rect.yBot - r, r, angle, Math.PI / 2));
            if (!parsedMergeAngles.left) {
                lines.get('left').unshift(calcAngleLine(rect.xLeft + r, rect.yBot - r, r, angle, Math.PI / 2 + angle));
            }
        }
        return lines;
    }
    static getDocLinesFromShape(lines) {
        const startPoint = lines.get(lines.keys().next().value)[0].slice(0, 2);
        let currPoint = startPoint;
        const docLines = [];
        lines.forEach((value) => {
            const borderLines = value;
            return borderLines.forEach(line => {
                line = line.slice(2);
                docLines.push(line.map((v, i) => v - currPoint[i % 2]));
                currPoint = line.slice(line.length - 2);
            });
        });
        return { lines: docLines, point: startPoint };
    }
    static renderFlatBorders(controller, options, style) {
        var _a, _b, _c;
        const newStyle = SurveyHelper.mergeObjects({}, { borderMode: BorderMode.Inside }, style);
        const borderWidth = parseSideValues((_a = style.borderWidth) !== null && _a !== void 0 ? _a : 0);
        const borderColor = parseSideValues(style.borderColor);
        if ([borderColor, borderWidth].some(map => Object.keys(map).every((k) => !map[k])))
            return;
        const scaleFactor = newStyle.borderMode == BorderMode.Middle ? 0 : (newStyle.borderMode == BorderMode.Inside ? 0.5 : -0.5);
        const scaledRect = {
            xLeft: options.xLeft + scaleFactor * borderWidth.left,
            yTop: options.yTop + scaleFactor * borderWidth.top,
            xRight: options.xRight - scaleFactor * borderWidth.right,
            yBot: options.yBot - scaleFactor * borderWidth.bot,
        };
        const mergeAngles = {
            top: borderWidth.left == borderWidth.top && borderColor.left == borderColor.top,
            right: borderWidth.top == borderWidth.right && borderColor.top == borderColor.right,
            bot: borderWidth.right == borderWidth.bot && borderColor.right == borderColor.bot,
            left: borderWidth.bot == borderWidth.left && borderColor.bot == borderColor.left,
        };
        const lines = SurveyHelper.createRoundedShape(scaledRect, { ...newStyle }, mergeAngles);
        const continuousLines = [];
        let maxSequenceIndex = 0;
        let maxSequenceLength = 0;
        let currentSequenceIndex = 0;
        let currentSequenceLength = 0;
        let prevBorderWidth;
        let prevBorderColor;
        const keys = Array.from(lines.keys());
        keys.concat(keys).forEach((key, index) => {
            const currentBorderWidth = borderWidth[key];
            const currentBorderColor = borderColor[key];
            if (index !== 0 && currentBorderWidth == prevBorderWidth && currentBorderColor == prevBorderColor) {
                currentSequenceLength++;
            }
            else {
                prevBorderWidth = currentBorderWidth;
                prevBorderColor = currentBorderColor;
                if (currentSequenceLength > maxSequenceLength) {
                    maxSequenceIndex = currentSequenceIndex;
                    maxSequenceLength = currentSequenceLength;
                }
                currentSequenceIndex = index;
                currentSequenceLength = 1;
            }
        });
        if (maxSequenceLength == 0) {
            maxSequenceLength = currentSequenceLength;
            maxSequenceIndex = currentSequenceIndex;
        }
        if (maxSequenceLength >= 2) {
            const maxContinuousLine = new Map();
            for (let i = maxSequenceIndex; i < maxSequenceIndex + maxSequenceLength; i++) {
                const key = keys[i % keys.length];
                maxContinuousLine.set(key, lines.get(key));
            }
            continuousLines.push(maxContinuousLine);
            const leftKeys = keys.filter(key => !maxContinuousLine.has(key));
            if (leftKeys.length !== 0) {
                if (leftKeys.length == 2 && leftKeys[0] == 'top' && leftKeys[1] == 'left')
                    leftKeys.reverse();
                if (leftKeys.every(key => borderWidth[leftKeys[0]] == borderWidth[key] && borderColor[leftKeys[0]] == borderColor[key])) {
                    const continuousLine = new Map();
                    leftKeys.forEach(key => { continuousLine.set(key, lines.get(key)); });
                    continuousLines.push(continuousLine);
                }
                else {
                    leftKeys.forEach(key => {
                        const map = new Map();
                        map.set(key, lines.get(key)),
                            continuousLines.push(map);
                    });
                }
            }
        }
        else {
            keys.forEach(key => {
                const map = new Map();
                map.set(key, lines.get(key)),
                    continuousLines.push(map);
            });
        }
        if (newStyle.dashStyle) {
            const dashStyle = newStyle.dashStyle;
            const borderLength = (Math.abs(scaledRect.yTop - scaledRect.yBot) + Math.abs(scaledRect.xLeft - scaledRect.xRight)) * 2;
            const dashWithSpaceSize = dashStyle.dashArray[0] + ((_b = dashStyle.dashArray[1]) !== null && _b !== void 0 ? _b : dashStyle.dashArray[0]);
            const dashSize = dashStyle.dashArray[0] + (borderLength % dashWithSpaceSize) / Math.floor(borderLength / dashWithSpaceSize);
            controller.doc.setLineDashPattern([dashSize, (_c = dashStyle.dashArray[1]) !== null && _c !== void 0 ? _c : dashStyle.dashArray[0]], dashStyle.dashPhase);
        }
        continuousLines.forEach(line => {
            const key = line.keys().next().value;
            if (!borderColor[key] || !borderWidth[key])
                return;
            controller.setDrawColor(borderColor[key]);
            controller.doc.setLineWidth(borderWidth[key]);
            const { lines: docLines, point } = this.getDocLinesFromShape(line);
            controller.doc.lines(docLines, ...point, [1, 1], 'S', line.size == 4);
            controller.restoreDrawColor();
        });
        if (newStyle.dashStyle) {
            controller.doc.setLineDashPattern([]);
        }
    }
    static getLocString(text) {
        if (this.hasHtml(text))
            return text.renderedHtml;
        return text.renderedText || text.renderedHtml;
    }
    static getContentQuestion(question) {
        return !!question.contentQuestion ? question.contentQuestion : question;
    }
    static isBooleanDisplayMode(val) {
        return val === 'radio' || val === 'checkbox' || val === 'switch';
    }
    static isEmptyRenderAs(val) {
        return !val || val === 'default';
    }
    static getBooleanRenderAsValue(question) {
        if (!this.isEmptyRenderAs(question.renderAs))
            return question.renderAs;
        const booleanQuestion = question;
        const displayMode = booleanQuestion.displayMode;
        if (displayMode === 'custom' && booleanQuestion.customRenderAs)
            return booleanQuestion.customRenderAs;
        return this.isBooleanDisplayMode(displayMode) ? displayMode : 'default';
    }
    static getContentQuestionTypeRenderAs(question, survey) {
        let renderAs = question.renderAs;
        if (question.getType() === 'boolean') {
            if (survey.options.useLegacyBooleanRendering) {
                renderAs = 'checkbox';
            }
            else {
                renderAs = this.getBooleanRenderAsValue(question);
            }
        }
        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 getPageAvailableWidth(controller) {
        return controller.paperWidth - controller.margins.left - controller.margins.right;
    }
    static getColumnWidth(controller, colCount, gapBetweenColumns) {
        return (this.getPageAvailableWidth(controller) - (colCount - 1) *
            gapBetweenColumns) / colCount;
    }
    static setColumnMargins(controller, colCount, column, gapBetweenColumns) {
        const cellWidth = this.getColumnWidth(controller, colCount, gapBetweenColumns);
        controller.margins.left = controller.margins.left + column *
            (cellWidth + gapBetweenColumns);
        controller.margins.right = controller.margins.right + (colCount - column - 1) *
            (cellWidth + gapBetweenColumns);
    }
    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 createRectInsideBorders(rect, borderWidth) {
        const parsedBorderWidth = parseSideValues(borderWidth);
        return {
            xLeft: rect.xLeft + parsedBorderWidth.left,
            yTop: rect.yTop + parsedBorderWidth.top,
            xRight: rect.xRight - parsedBorderWidth.right,
            yBot: rect.yBot - parsedBorderWidth.bot
        };
    }
    static getFlatQuestion(survey, controller, question) {
        const questionType = this.getContentQuestionType(question, survey);
        const style = survey.getElementStyle(question);
        const flatQuestion = FlatRepository.getInstance().
            create(survey, question, controller, style, questionType);
        return flatQuestion;
    }
    static async generateQuestionFlats(survey, controller, question, point) {
        const flatQuestion = SurveyHelper.getFlatQuestion(survey, controller, question);
        const questionFlats = await flatQuestion.generateFlats(point);
        return [...questionFlats];
    }
    static async generatePanelFlats(survey, controller, panel, point) {
        return [...await FlatRepository.getInstance().createPanel(survey, panel, controller, survey.getElementStyle(panel)).generateFlats(point)];
    }
    static async generatePageFlats(survey, controller, page, point) {
        return [...await FlatRepository.getInstance().createPage(survey, page, controller, survey.getElementStyle(page)).generateFlats(point)];
    }
    static isFontExist(controller, fontName) {
        return controller.doc.internal.getFont(fontName).fontName === fontName;
    }
    static isCustomFont(controller, fontName) {
        return controller.doc.internal.getFont(fontName).encoding === 'Identity-H';
    }
    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 async getCorrectedImageSize(controller, imageOptions) {
        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 = await getImageUtils().getImageInfo(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 = Math.min(imageSize.height, SurveyHelper.getPageAvailableWidth(controller));
                widthPt = Math.min(imageSize.width, SurveyHelper.getPageAvailableWidth(controller));
            }
        }
        return { width: widthPt || defaultWidthPt || 0, height: heightPt || defaultHeightPt || 0 };
    }
    static alignVerticallyBricks(align, ...bricks) {
        const mergedRect = SurveyHelper.mergeRects(...bricks);
        bricks.forEach((brick) => {
            switch (align) {
                case 'center': {
                    brick.translateY((yTop, yBot) => {
                        const shift = ((mergedRect.yTop + mergedRect.yBot) - (yTop + yBot)) / 2;
                        return { yTop: yTop + shift, yBot: yBot + shift };
                    });
                    break;
                }
                case 'bottom': {
                    brick.translateY((yTop, yBot) => {
                        return { yTop: yTop + mergedRect.yBot - yBot, yBot: mergedRect.yBot };
                    });
                    break;
                }
                default: {
                    brick.translateY((yTop, yBot) => {
                        return { yTop: mergedRect.yTop, yBot: yBot - yTop + mergedRect.yTop };
                    });
                }
            }
        });
    }
    static clear() {
        getImageUtils().clear();
    }
}
SurveyHelper.EPSILON = 2.2204460492503130808472633361816e-15;
SurveyHelper.HTML_TAIL_TEXT_SCALE = 0.24;

function pdfEscape(value) { return value.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); }
function toPDFString(string) {
    string = string || '';
    string.toString();
    string = '(' + pdfEscape(string) + ')';
    return string;
}
function getPatchedAcroFormTextField(doc) {
    class PatchedAcroformTextField extends doc.AcroFormTextField {
        getKeyValueListForStream() {
            const res = super.getKeyValueListForStream();
            res.push({ key: 'DA', value: toPDFString(doc.AcroFormAppearance.createDefaultAppearanceStream(this)) });
            return res;
        }
    }
    return PatchedAcroformTextField;
}
function getPatchedAcroFormRadioButton(doc) {
    class PatchedAcroFormCheckBox extends doc.AcroFormRadioButton {
        constructor() {
            super();
        }
        setAppearance(appearance) {
            super.setAppearance(appearance);
            const oldAppearanceFuncition = appearance.YesNormal;
            for (const objId in this.Kids) {
                if (this.Kids.hasOwnProperty(objId)) {
                    const child = this.Kids[objId];
                    child.appearanceStreamContent.N[child.optionName] = 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;
                    };
                }
            }
        }
    }
    return PatchedAcroFormCheckBox;
}
function getPatchedAcroFormCheckBox(doc) {
    class PatchedAcroFormCheckBox extends doc.AcroFormCheckBox {
        constructor() {
            super();
            const oldYerNormalAppearanceStream = this.appearanceStreamContent.N.On;
            this.appearanceStreamContent.N.On = function (formObject) {
                const xobj = oldYerNormalAppearanceStream(formObject);
                let stream = xobj.stream.split('\n');
                stream = stream.slice(3);
                xobj.stream = stream.join('\n');
                return xobj;
            };
        }
    }
    return PatchedAcroFormCheckBox;
}
function getPatchedAcroFormComboBox(doc) {
    class PatchedAcroformTextField extends doc.AcroFormComboBox {
        constructor() {
            super();
        }
        set backgroundColor(val) {
            if (val) {
                let color = doc.__private__.encodeColorString(val).replace(/\s+RG/i, '');
                if (color.includes('g')) {
                    color = color.replace('g', '').repeat(3);
                }
                this.MK = `<< /BG [ ${color} ]  >>`;
            }
        }
    }
    return PatchedAcroformTextField;
}

/* 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 = {};
                    const font = scope.internal.getFont(formObject.fontName, formObject.fontStyle);
                    fontList[font.id] = font;
                    var payload = {
                        text: line,
                        x: null,
                        y: null,
                        options: {
                            lang: null
                        },
                        mutex: {
                            pdfEscape: pdfEscape,
                            activeFontKey: font.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.isUnicode) {
                    return _V;
                }
                if ((this instanceof AcroFormButton === true)) {
                    return pdfUnescape(_V.substr(1, _V.length - 1));
                } else {
                    return _V;
                }
            },
            set: function (value) {
                value = value.toString();
                if (this.isUnicode) {
                    _V = toUnicode(value);
                    this.trueValue = value;
                } else {
                    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, fragment, fragmentChopped, fragmentLength, fragmentSpecificMetrics, fs, k, line, lines, maxLineLength, style;
      defaultFontSize = 12;
      k = this.pdf.internal.scaleFactor;
      ff = void 0;
      fs = 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;
      const oldFontName = this.pdf.getFont().fontName;
      const oldFontStyle = this.pdf.getFont().fontStyle;
      while (fragments.length) {
        fragment = fragments.shift();
        style = styles.shift();

        if (fragment) {
          ff = style["font-family"];
          fs = style["font-style"];
          this.pdf.setFont(ff, fs);
          fragmentSpecificMetrics = {
            textIndent: currentLineLength,
            fontSize: style["font-size"] * defaultFontSize
          };
          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;
            }
          }
        }
      }
      this.pdf.setFont(oldFontName, oldFontStyle);
      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) {
        var _a, _b;
        this._fontSize = 6;
        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);
        }
        if (!options.fontName) {
            if (!DocOptions.SEGOE_BOLD && !DocOptions.SEGOE_NORMAL) {
                this._fontName = 'helvetica';
            }
            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);
        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;
        this._htmlToImageQuality = (_a = options.htmlToImageQuality) !== null && _a !== void 0 ? _a : 1;
        this._otherRowsCount = (_b = options.otherRowsCount) !== null && _b !== void 0 ? _b : 2;
    }
    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;
    }
    get htmlToImageQuality() {
        return this._htmlToImageQuality;
    }
    get otherRowsCount() {
        return this._otherRowsCount;
    }
}
DocOptions.MM_TO_PT = 72 / 25.4;
/**
 * The `DocController` object includes an API that allows you to configure main PDF document properties (font, margins, page width and height).
 *
 * [View Demo](https://surveyjs.io/pdf-generator/examples/change-font-in-pdf-form/ (linkStyle))
 */
class DocController extends DocOptions {
    constructor(options = {}) {
        super(options);
        this.drawColorRestoreCallbacks = [];
        this.fillColorRestoreCallbacks = [];
        this.textColorRestoreCallbacks = [];
        this.textStyleRestoreCallbacks = [];
        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);
        }
        this._useCustomFontInHtml = options.useCustomFontInHtml && SurveyHelper.isFontExist(this, this.fontName);
        this._helperDoc = new jsPDF(jspdfOptions);
        this._doc.setFont('helvetica');
        this._helperDoc.setFont('helvetica');
        this._doc.setFontSize(6);
        this._helperDoc.setFontSize(6);
        this._fontStyle = 'normal';
        this.marginsStack = [];
    }
    /**
     * Adds a custom font to the PDF Generator.
     *
     * [View Demo](https://surveyjs.io/pdf-generator/examples/change-font-in-pdf-form/ (linkStyle))
     * @param fontName A custom name that you will use to apply the custom font.
     * @param base64 The custom font as a Base64-encoded string. To encode your font to Base64, obtain it as a TTF file and use any TTF-to-Base64 online converter.
     * @param fontStyle The style of the custom font: `"normal"`, `"bold"`, `"italic"`, or `"bolditalic"`.
     */
    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 lineHeightFactor() {
        return this._lineHeightFactor;
    }
    set lineHeightFactor(lineHeightFactor) {
        this._lineHeightFactor = lineHeightFactor;
        this._doc.setLineHeightFactor(lineHeightFactor);
        this._helperDoc.setLineHeightFactor(lineHeightFactor);
    }
    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, style) {
        this.setTextStyle(style, true);
        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.restoreTextStyle(true);
        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);
    }
    setColor(value, getOldColor, setColorFunc, gOpacityOption = 'opacity') {
        const { doc } = this;
        const oldColor = getOldColor();
        const { color, opacity } = SurveyHelper.parseColor(value);
        setColorFunc(color);
        let needRestoreGraphicsState = false;
        if (opacity !== undefined) {
            doc.saveGraphicsState();
            doc.setGState(new doc.GState({ [gOpacityOption]: opacity }));
            needRestoreGraphicsState = true;
        }
        return () => {
            setColorFunc(oldColor);
            if (needRestoreGraphicsState) {
                this.doc.restoreGraphicsState();
            }
        };
    }
    setDrawColor(color) {
        this.drawColorRestoreCallbacks.push(this.setColor(color, () => this.doc.getDrawColor(), (val) => this.doc.setDrawColor(val), 'stroke-opacity'));
    }
    restoreDrawColor() {
        if (this.drawColorRestoreCallbacks.length > 0) {
            this.drawColorRestoreCallbacks.pop()();
        }
    }
    setFillColor(color) {
        this.fillColorRestoreCallbacks.push(this.setColor(color, () => this.doc.getFillColor(), (val) => this.doc.setFillColor(val)));
    }
    restoreFillColor() {
        if (this.fillColorRestoreCallbacks.length > 0) {
            this.fillColorRestoreCallbacks.pop()();
        }
    }
    setTextColor(color) {
        this.textColorRestoreCallbacks.push(this.setColor(color, () => this.doc.getTextColor(), (val) => this.doc.setTextColor(val)));
    }
    restoreTextColor() {
        if (this.textColorRestoreCallbacks.length > 0) {
            this.textColorRestoreCallbacks.pop()();
        }
    }
    setTextStyle(style, isHelper = false) {
        var _a, _b, _c, _d;
        const doc = isHelper ? this.helperDoc : this.doc;
        const oldFontSize = doc.getFontSize();
        const oldFont = doc.getFont();
        const oldLineHeightFactor = doc.getLineHeightFactor();
        const fontSize = (_a = style === null || style === void 0 ? void 0 : style.fontSize) !== null && _a !== void 0 ? _a : oldFontSize;
        const needApplyColor = (style === null || style === void 0 ? void 0 : style.fontColor) && !isHelper;
        doc.setFont((_b = style === null || style === void 0 ? void 0 : style.fontName) !== null && _b !== void 0 ? _b : oldFont.fontName, (_c = style === null || style === void 0 ? void 0 : style.fontStyle) !== null && _c !== void 0 ? _c : oldFont.fontStyle);
        doc.setFontSize(fontSize);
        doc.setLineHeightFactor(((_d = style === null || style === void 0 ? void 0 : style.lineHeight) !== null && _d !== void 0 ? _d : fontSize) / fontSize);
        if (needApplyColor) {
            this.setTextColor(style.fontColor);
        }
        this.textStyleRestoreCallbacks.push({ isHelper: isHelper, callback: () => {
                doc.setFont(oldFont.fontName, oldFont.fontStyle);
                doc.setFontSize(oldFontSize);
                doc.setLineHeightFactor(oldLineHeightFactor);
                if (needApplyColor) {
                    this.restoreTextColor();
                }
            } });
    }
    restoreTextStyle(isHelper = false) {
        const index = this.textStyleRestoreCallbacks.length - 1 - this.textStyleRestoreCallbacks.slice().reverse().findIndex((value) => {
            return value.isHelper === isHelper;
        });
        if (index < this.textStyleRestoreCallbacks.length) {
            this.textStyleRestoreCallbacks.splice(index, 1)[0].callback();
        }
    }
    get AcroFormCheckBox() {
        if (!this._AcroFormCheckBox) {
            this._AcroFormCheckBox = getPatchedAcroFormCheckBox(this.doc);
        }
        return this._AcroFormCheckBox;
    }
    get AcroFormComboBox() {
        if (!this._AcroFormComboBox) {
            this._AcroFormComboBox = getPatchedAcroFormComboBox(this.doc);
        }
        return this._AcroFormComboBox;
    }
    get AcroFormTextField() {
        if (!this._AcroFormTextField) {
            this._AcroFormTextField = getPatchedAcroFormTextField(this.doc);
        }
        return this._AcroFormTextField;
    }
    get AcroFormRadioButton() {
        if (!this._AcroFormRadioButton) {
            this._AcroFormRadioButton = getPatchedAcroFormRadioButton(this.doc);
        }
        return this._AcroFormRadioButton;
    }
}
DocController.customFonts = {};

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 text fragment 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 text fragment within the drawing area.
     *
     * [View Demo](https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/ (linkStyle))
     * @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.isBold === 'undefined') {
            textOptions.isBold = false;
        }
        const style = SurveyHelper.getPatchedTextStyle(this.controller, {
            fontStyle: textOptions.isBold ? 'bold' : 'normal',
            fontSize: textOptions.fontSize,
            fontName: this.controller.fontName,
            fontColor: '#404040',
        });
        const textSize = this.controller.measureText(textOptions.text, style);
        const textRect = this.alignRect(textOptions, textSize);
        this.packs.push(new TextBrick(this.controller, textRect, textOptions, style));
    }
    /**
     * Draws an image within the drawing area.
     *
     * [View Demo](https://surveyjs.io/pdf-generator/examples/customize-header-and-footer-of-pdf-form/ (linkStyle))
     * @param imageOptions An [`IDrawImageOptions`](https://surveyjs.io/pdf-generator/documentation/api-reference/idrawimageoptions) object that configures drawing.
     */
    async drawImage(imageOptions) {
        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(await 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 FlatLicense {
    constructor(survey, controller, style) {
        this.survey = survey;
        this.controller = controller;
        this.style = style;
    }
    getTextStyle() {
        return this.style.text;
    }
    getLinkStyle() {
        var _a;
        return SurveyHelper.mergeObjects({}, this.getTextStyle(), (_a = this.style.link) !== null && _a !== void 0 ? _a : {});
    }
    getLicenseParts(license) {
        const regex = /\[([^\]]+)\]\(([^)]+)\)|([^\[]+)/g;
        const parts = [];
        let match;
        while ((match = regex.exec(license)) !== null) {
            if (match[1] && match[2]) {
                parts.push({
                    text: match[1],
                    isLink: true,
                    url: match[2]
                });
            }
            else if (match[3]) {
                parts.push({
                    text: match[3]
                });
            }
        }
        return parts;
    }
    splitPartsToFit(parts, width) {
        const lines = [];
        let currentLine = [];
        let currentLineWidth = 0;
        for (const part of parts) {
            let text = part.text;
            const words = text.split(/(\s+)/);
            for (const word of words) {
                if (word === '')
                    continue;
                const wordWidth = this.controller.measureText(word, this.getTextStyle()).width;
                if (currentLineWidth + wordWidth > width && currentLine.length > 0) {
                    lines.push(currentLine);
                    currentLine = [];
                    currentLineWidth = 0;
                }
                currentLine.push({ ...part, text: word });
                currentLineWidth += wordWidth;
            }
        }
        if (currentLine.length > 0) {
            lines.push(currentLine);
        }
        return lines.map((line) => {
            let currentPart;
            const mergedLine = [];
            for (const part of line) {
                if (!currentPart || !(currentPart.isLink === part.isLink && (currentPart.isLink ? part.isLink && currentPart.url === part.url : true))) {
                    currentPart = part;
                    mergedLine.push(part);
                }
                else {
                    currentPart.text += part.text;
                }
            }
            mergedLine[mergedLine.length - 1].text = mergedLine[mergedLine.length - 1].text.trimEnd();
            return mergedLine;
        });
    }
    generateLicenseFlats(point, lines, width) {
        const resultBrick = new CompositeBrick();
        const currPoint = SurveyHelper.clone(point);
        for (const line of lines) {
            const lineBrick = new CompositeBrick();
            for (const part of line) {
                const textSize = this.controller.measureText(part.text, this.getTextStyle());
                let brick;
                if (part.isLink) {
                    brick = new LinkBrick(this.controller, SurveyHelper.createRect(currPoint, textSize.width, textSize.height), { text: part.text, link: part.url, readOnlyShowLink: false }, this.getLinkStyle());
                }
                else {
                    brick = new TextBrick(this.controller, SurveyHelper.createRect(currPoint, textSize.width, textSize.height), { text: part.text }, this.getTextStyle());
                }
                lineBrick.addBrick(brick);
                currPoint.xLeft += textSize.width;
            }
            this.centerLineBrick(lineBrick, point.xLeft, width + point.xLeft);
            resultBrick.addBrick(lineBrick);
            currPoint.yTop += lineBrick.height;
        }
        return [resultBrick];
    }
    centerLineBrick(lineBrick, xLeft, xRight) {
        const shift = (xRight + xLeft - lineBrick.xRight - lineBrick.xLeft) / 2;
        lineBrick.translateX((xLeft, xRight) => {
            return { xLeft: xLeft + shift, xRight: xRight + shift };
        });
    }
    generateFlats(point) {
        if (this.survey.haveCommercialLicense) {
            return [];
        }
        const license = this.survey.licenseText;
        const availableWidth = SurveyHelper.getPageAvailableWidth(this.controller);
        const licenseParts = this.getLicenseParts(license);
        const licenseLines = this.splitPartsToFit(licenseParts, availableWidth);
        const res = this.generateLicenseFlats(point, licenseLines, availableWidth);
        return res;
    }
}

let variablesManagerCreator;
function registerVariablesManagerCreator(creator) {
    variablesManagerCreator = creator;
}
function createVariablesManager() {
    return variablesManagerCreator();
}

function createStyleFromTheme(theme, layout, callback) {
    const themeVariablesManager = createVariablesManager();
    const layoutVariablesManager = createVariablesManager();
    themeVariablesManager.setup(theme.cssVariables);
    layoutVariablesManager.setup(layout);
    themeVariablesManager.startCollectingVariables();
    layoutVariablesManager.startCollectingVariables();
    const res = callback({ getColorVariable: (name) => themeVariablesManager.getColorVariable(name), getSizeVariable: (name) => layoutVariablesManager.getSizeVariable(name) });
    themeVariablesManager.stopCollectingVariables();
    layoutVariablesManager.stopCollectingVariables();
    return res;
}
function getDefaultStyle(theme, layout) {
    return createStyleFromTheme(theme, layout, ({ getSizeVariable, getColorVariable }) => {
        const baseSize = getSizeVariable('--sjs2-base-unit-size');
        const baseFontSize = getSizeVariable('--sjs2-base-unit-font-size');
        const baseSpace = getSizeVariable('--sjs2-base-unit-spacing');
        return {
            survey: {
                title: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-large'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-large'),
                    fontStyle: 'bold',
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                },
                description: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                    fontStyle: 'normal',
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                },
                spacing: {
                    titleDescriptionGap: getSizeVariable('--sjs2-pdf-layout-title-large-gap'),
                    pageGap: baseSpace * 3, //todo: need variable
                    headerContentGap: getSizeVariable('--sjs2-pdf-layout-page-gap-vertical') + getSizeVariable('--sjs2-pdf-layout-title-large-padding-bottom'),
                },
                backgroundColor: getColorVariable('--sjs2-color-utility-body'),
                padding: [getSizeVariable('--sjs2-pdf-layout-page-padding-top'), getSizeVariable('--sjs2-pdf-layout-page-padding-right'), getSizeVariable('--sjs2-pdf-layout-page-padding-bottom'), getSizeVariable('--sjs2-pdf-layout-page-padding-left')],
            },
            question: {
                minWidth: baseSize * 25,
                inlineHeaderWidthPercentage: Math.E / 10.0,
                //TODO: need variable
                container: {
                    padding: [getSizeVariable('--sjs2-pdf-layout-question-padding-vertical'), getSizeVariable('--sjs2-pdf-layout-question-padding-horizontal')],
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-question'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-question'),
                    borderColor: getColorVariable('--sjs2-color-border-basic-secondary'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-primary')
                },
                title: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-small'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-small'),
                    fontStyle: 'normal',
                },
                description: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-small'),
                    fontStyle: 'normal',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-small')
                },
                comment: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                    fontColor: getColorVariable('--sjs2-color-component-input-default-value'),
                    backgroundColor: getColorVariable('--sjs2-color-component-formbox-default-bg'),
                },
                commentReadOnly: {
                    backgroundColor: null,
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                },
                input: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                },
                commentLabel: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontStyle: 'normal',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                },
                spacing: {
                    headerContentGap: getSizeVariable('--sjs2-pdf-layout-question-gap'),
                    inlineHeaderContentGap: baseSpace,
                    contentIndentStart: 0,
                    contentCommentGap: getSizeVariable('--sjs2-pdf-layout-question-gap'),
                    contentDescriptionGap: getSizeVariable('--sjs2-pdf-layout-question-gap'),
                    titleDescriptionGap: getSizeVariable('--sjs2-pdf-layout-question-labels-gap-vertical'),
                    titleRequiredMarkGap: baseFontSize / 2,
                    titleNumberGap: baseFontSize / 2,
                    commentLabelGap: getSizeVariable('--sjs2-spacing-x050')
                }
            },
            panel: {
                minWidth: baseSize * 75,
                header: {
                    padding: [getSizeVariable('--sjs2-pdf-layout-section-padding-vertical'), getSizeVariable('--sjs2-pdf-layout-section-padding-horizontal')],
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-section'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-secondary'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-section'),
                    borderColor: getColorVariable('--sjs2-color-border-basic-secondary'),
                },
                title: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontStyle: 'bold',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                },
                description: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-small'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-small'),
                },
                spacing: {
                    elementGap: getSizeVariable('--sjs2-pdf-layout-page-questions-gap-vertical'),
                    inlineElementGap: getSizeVariable('--sjs2-pdf-layout-page-questions-gap-horizontal'),
                    headerContentGap: getSizeVariable('--sjs2-pdf-layout-page-questions-gap-vertical'),
                    titleDescriptionGap: getSizeVariable('--sjs2-pdf-layout-title-default-gap'),
                }
            },
            page: {
                header: {
                    borderWidth: 0,
                    padding: 0,
                    backgroundColor: null
                },
                title: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-medium'),
                    fontStyle: 'bold',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-medium')
                },
                description: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                },
                spacing: {
                    headerContentGap: getSizeVariable('--sjs2-pdf-layout-page-gap-vertical') + getSizeVariable('--sjs2-pdf-layout-title-medium-padding-bottom'),
                    elementGap: getSizeVariable('--sjs2-pdf-layout-page-gap-vertical'),
                }
            },
            selectbase: {
                columnMinWidth: baseSize * 20,
                choiceText: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontStyle: 'normal',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                },
                input: {
                    width: getSizeVariable('--sjs2-size-x200'),
                    height: getSizeVariable('--sjs2-size-x200'),
                    fontSize: getSizeVariable('--sjs2-size-x200') * 0.625,
                    borderColor: getColorVariable('--sjs2-color-component-checkbox-false-default-border'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-check'),
                    fontName: 'zapfdingbats',
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-primary'),
                    fontColor: getColorVariable('--sjs2-color-fg-brand-primary'),
                },
                inputReadOnly: {
                    fontColor: getColorVariable('--sjs2-color-component-checkbox-true-default-icon'),
                    backgroundColor: getColorVariable('--sjs2-color-component-checkbox-false-default-bg'),
                },
                inputReadOnlyChecked: {
                    borderColor: getColorVariable('--sjs2-color-component-checkbox-true-default-border'),
                    backgroundColor: getColorVariable('--sjs2-color-component-checkbox-true-default-bg')
                },
                spacing: {
                    choiceColumnGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-horizontal'),
                    choiceGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-vertical'),
                    choiceTextGap: getSizeVariable('--sjs2-pdf-layout-check-gap'),
                }
            },
            checkbox: {
                input: {
                    checkMark: '3'
                },
                inputReadOnly: {
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-checkbox'),
                }
            },
            radiogroup: {
                input: {
                    checkMark: 'l',
                    borderColor: getColorVariable('--sjs2-color-component-radio-false-default-border'),
                },
                inputReadOnly: {
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-radio'),
                    fontColor: getColorVariable('--sjs2-color-component-radio-true-default-icon'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-false-default-bg'),
                },
                inputReadOnlyChecked: {
                    borderColor: getColorVariable('--sjs2-color-component-radio-true-default-border'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-true-default-bg')
                }
            },
            matrixbase: {
                minWidth: baseSize * 40,
                columnMinWidth: baseSize * 15,
                container: {
                    padding: 0,
                    borderWidth: 0,
                    backgroundColor: null
                },
                header: {
                    padding: [getSizeVariable('--sjs2-pdf-layout-section-padding-vertical'), getSizeVariable('--sjs2-pdf-layout-section-padding-horizontal')],
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-section'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-secondary'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-section'),
                    borderColor: getColorVariable('--sjs2-color-border-basic-secondary'),
                },
                title: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontStyle: 'bold',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                },
                description: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-small'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-small'),
                },
                cell: {
                    padding: [getSizeVariable('--sjs2-pdf-layout-question-padding-vertical'), getSizeVariable('--sjs2-pdf-layout-question-padding-horizontal')],
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-matrix'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-question'),
                    borderColor: getColorVariable('--sjs2-color-border-basic-secondary'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-primary'),
                },
                rowTitle: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontStyle: 'normal',
                    textAlign: 'right'
                },
                columnTitle: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontStyle: 'normal'
                },
                listSectionTitle: {
                    textAlign: 'left',
                },
                spacing: {
                    titleDescriptionGap: getSizeVariable('--sjs2-pdf-layout-title-default-gap'),
                    headerContentGap: getSizeVariable('--sjs2-pdf-layout-page-questions-gap-vertical'),
                    tableColumnGap: getSizeVariable('--sjs2-pdf-layout-page-matrix-gap-horizontal'),
                    tableRowGap: getSizeVariable('--sjs2-pdf-layout-page-matrix-gap-vertical'),
                    listSectionGap: getSizeVariable('--sjs2-pdf-layout-page-questions-gap-vertical'),
                    listItemTitleContentGap: getSizeVariable('--sjs2-pdf-layout-question-gap'),
                }
            },
            matrix: {
                columnMinWidth: baseSize * 12,
                listChoiceText: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontStyle: 'normal',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                },
                listSectionTitle: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-small'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-small'),
                    fontStyle: 'normal',
                },
                input: {
                    width: getSizeVariable('--sjs2-size-x200'),
                    height: getSizeVariable('--sjs2-size-x200'),
                    fontSize: getSizeVariable('--sjs2-size-x200') * 0.625,
                    borderColor: getColorVariable('--sjs2-color-component-checkbox-false-default-border'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-check'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-primary'),
                    fontColor: getColorVariable('--sjs2-color-fg-brand-primary'),
                    fontName: 'zapfdingbats',
                },
                inputReadOnly: {
                    fontColor: getColorVariable('--sjs2-color-component-checkbox-true-default-icon'),
                    backgroundColor: getColorVariable('--sjs2-color-component-checkbox-false-default-bg'),
                },
                inputReadOnlyChecked: {
                    borderColor: getColorVariable('--sjs2-color-component-checkbox-true-default-border'),
                    backgroundColor: getColorVariable('--sjs2-color-component-checkbox-true-default-bg')
                },
                radioInput: {
                    checkMark: 'l',
                    borderColor: getColorVariable('--sjs2-color-component-radio-false-default-border'),
                },
                radioInputReadOnly: {
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-radio'),
                    fontColor: getColorVariable('--sjs2-color-component-radio-true-default-icon'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-false-default-bg'),
                },
                radioInputReadOnlyChecked: {
                    borderColor: getColorVariable('--sjs2-color-component-radio-true-default-border'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-true-default-bg')
                },
                checkboxInput: {
                    checkMark: '3'
                },
                checkboxInputReadOnly: {
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-checkbox'),
                },
                spacing: {
                    gapBetweenItemText: getSizeVariable('--sjs2-pdf-layout-check-gap'),
                    listChoiceGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-vertical'),
                    listChoiceTextGap: getSizeVariable('--sjs2-pdf-layout-check-gap'),
                }
            },
            matrixdropdownbase: {
                listItemTitle: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-small'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-small'),
                    fontStyle: 'normal',
                },
            },
            matrixdropdown: {
                listSectionTitleContainer: {
                    padding: [getSizeVariable('--sjs2-pdf-layout-section-padding-vertical'), getSizeVariable('--sjs2-pdf-layout-section-padding-horizontal')],
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-section'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-secondary'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-section'),
                    borderColor: getColorVariable('--sjs2-color-border-basic-secondary'),
                },
                listSectionTitle: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontStyle: 'bold',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                },
            },
            multipletext: {
                itemTitleWidthPercentage: 0.4,
                container: {
                    padding: 0,
                    borderWidth: 0,
                    backgroundColor: null
                },
                header: {
                    padding: [getSizeVariable('--sjs2-pdf-layout-section-padding-vertical'), getSizeVariable('--sjs2-pdf-layout-section-padding-horizontal')],
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-section'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-secondary'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-section'),
                    borderColor: getColorVariable('--sjs2-color-border-basic-secondary'),
                },
                title: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontStyle: 'bold',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                },
                description: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-small'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-small'),
                },
                itemTitle: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontStyle: 'normal',
                },
                itemCell: {
                    padding: [getSizeVariable('--sjs2-pdf-layout-question-padding-vertical'), getSizeVariable('--sjs2-pdf-layout-question-padding-horizontal')],
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-matrix'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-question'),
                    borderColor: getColorVariable('--sjs2-color-border-basic-secondary'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-primary'),
                },
                spacing: {
                    titleDescriptionGap: getSizeVariable('--sjs2-pdf-layout-title-default-gap'),
                    headerContentGap: getSizeVariable('--sjs2-pdf-layout-page-questions-gap-vertical'),
                    itemColumnGap: getSizeVariable('--sjs2-pdf-layout-page-matrix-gap-horizontal'),
                    itemGap: getSizeVariable('--sjs2-pdf-layout-page-matrix-gap-vertical'),
                    itemTitleGap: getSizeVariable('--sjs2-pdf-layout-page-matrix-gap-horizontal'),
                }
            },
            rating: {
                choiceMinWidth: getSizeVariable('--sjs2-size-x300'),
                choiceText: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontStyle: 'normal',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                },
                input: {
                    width: getSizeVariable('--sjs2-size-x200'),
                    height: getSizeVariable('--sjs2-size-x200'),
                    fontSize: getSizeVariable('--sjs2-size-x200') * 0.625,
                    borderColor: getColorVariable('--sjs2-color-component-radio-false-default-border'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-check'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-primary'),
                    fontColor: getColorVariable('--sjs2-color-fg-brand-primary'),
                    fontName: 'zapfdingbats',
                    checkMark: 'l',
                },
                inputReadOnly: {
                    fontColor: getColorVariable('--sjs2-color-component-radio-true-default-icon'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-false-default-bg'),
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-checkbox'),
                },
                inputReadOnlyChecked: {
                    borderColor: getColorVariable('--sjs2-color-component-radio-true-default-border'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-true-default-bg')
                },
                spacing: {
                    choiceColumnGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-horizontal'),
                    choiceGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-vertical'),
                    choiceTextGap: getSizeVariable('--sjs2-pdf-layout-check-gap'),
                }
            },
            ranking: {
                input: {
                    width: getSizeVariable('--sjs2-size-x200'),
                    height: getSizeVariable('--sjs2-size-x200'),
                    fontSize: getSizeVariable('--sjs2-size-x200') * 0.625,
                    lineHeight: getSizeVariable('--sjs2-size-x200') * 0.625,
                    borderColor: getColorVariable('--sjs2-color-component-checkbox-false-default-border'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-check'),
                    fontName: 'helvetica',
                    fontStyle: 'normal',
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    backgroundColor: getColorVariable('--sjs2-color-component-checkbox-false-default-bg'),
                },
                selectToRankAreaSeparator: {
                    width: baseSize / 7,
                    color: getColorVariable('--sjs2-color-fg-basic-primary')
                },
                choiceText: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontStyle: 'normal',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                },
                spacing: {
                    choiceColumnGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-horizontal'),
                    choiceGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-vertical'),
                    choiceTextGap: getSizeVariable('--sjs2-pdf-layout-check-gap'),
                }
            },
            slider: {
                input: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                    backgroundColor: getColorVariable('--sjs2-color-component-formbox-default-bg'),
                    fontColor: getColorVariable('--sjs2-color-component-input-default-value'),
                },
                inputReadOnly: {
                    backgroundColor: null,
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                },
                rangeSeparator: {
                    width: baseSize * 3,
                    height: baseSize * 0.125,
                    color: getColorVariable('--sjs2-color-fg-basic-primary')
                },
                spacing: {
                    inputRangeGap: baseSpace * 8,
                }
            },
            dropdown: {
                input: {
                    borderWidth: 0,
                    fontName: undefined,
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                    fontColor: getColorVariable('--sjs2-color-component-input-default-value'),
                    backgroundColor: getColorVariable('--sjs2-color-component-formbox-default-bg'),
                },
                inputReadOnly: {
                    backgroundColor: null,
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                },
            },
            file: {
                fileItemMinWidth: getSizeVariable('--sjs2-size-x500'),
                defaultImageFit: 'contain',
                fileName: {
                    fontColor: getColorVariable('--sjs2-color-fg-note-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                },
                spacing: {
                    imageFileNameGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-vertical'),
                    fileItemColumnGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-horizontal'),
                    fileItemGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-vertical'),
                }
            },
            paneldynamic: {
                container: {
                    padding: 0,
                    borderWidth: 0,
                    backgroundColor: null
                },
                header: {
                    padding: [getSizeVariable('--sjs2-pdf-layout-section-padding-vertical'), getSizeVariable('--sjs2-pdf-layout-section-padding-horizontal')],
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-section'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-secondary'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-section'),
                    borderColor: getColorVariable('--sjs2-color-border-basic-secondary'),
                },
                title: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontStyle: 'bold',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                },
                description: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-small'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-small'),
                },
                spacing: {
                    headerContentGap: getSizeVariable('--sjs2-pdf-layout-page-questions-gap-vertical'),
                    titleDescriptionGap: getSizeVariable('--sjs2-pdf-layout-title-default-gap'),
                    panelGap: getSizeVariable('--sjs2-pdf-layout-page-questions-gap-vertical'),
                }
            },
            boolean: {
                choiceText: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontStyle: 'normal',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                },
                input: {
                    //todo may be we need variable
                    width: getSizeVariable('--sjs2-size-x200'),
                    height: getSizeVariable('--sjs2-size-x200'),
                    fontSize: getSizeVariable('--sjs2-size-x200') * 0.625,
                    borderColor: getColorVariable('--sjs2-color-component-checkbox-false-default-border'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-check'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-primary'),
                    fontColor: getColorVariable('--sjs2-color-fg-brand-primary'),
                    fontName: 'zapfdingbats',
                },
                inputReadOnly: {
                    fontColor: getColorVariable('--sjs2-color-component-checkbox-true-default-icon'),
                    backgroundColor: getColorVariable('--sjs2-color-component-checkbox-false-default-bg'),
                },
                inputReadOnlyChecked: {
                    borderColor: getColorVariable('--sjs2-color-component-checkbox-true-default-border'),
                    backgroundColor: getColorVariable('--sjs2-color-component-checkbox-true-default-bg')
                },
                radioInput: {
                    checkMark: 'l',
                    borderColor: getColorVariable('--sjs2-color-component-radio-false-default-border'),
                },
                radioInputReadOnly: {
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-radio'),
                    fontColor: getColorVariable('--sjs2-color-component-radio-true-default-icon'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-false-default-bg'),
                },
                radioInputReadOnlyChecked: {
                    borderColor: getColorVariable('--sjs2-color-component-radio-true-default-border'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-true-default-bg')
                },
                checkboxInput: {
                    checkMark: '3'
                },
                checkboxInputReadOnly: {
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-checkbox'),
                },
                spacing: {
                    choiceColumnGap: getSizeVariable('--sjs2-pdf-layout-question-items-gap-horizontal'),
                    choiceTextGap: getSizeVariable('--sjs2-pdf-layout-check-gap'),
                }
            },
            imagepicker: {
                imageRatio: 4 / 3,
                imageMinWidth: baseSize * 12.5,
                imageMaxWidth: baseSize * 37.5,
                inputReadOnly: {
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-checkbox'),
                },
                radioInput: {
                    checkMark: 'l',
                    borderColor: getColorVariable('--sjs2-color-component-radio-false-default-border'),
                },
                radioInputReadOnly: {
                    fontColor: getColorVariable('--sjs2-color-component-radio-true-default-icon'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-false-default-bg'),
                },
                radioInputReadOnlyChecked: {
                    borderColor: getColorVariable('--sjs2-color-component-radio-true-default-border'),
                    backgroundColor: getColorVariable('--sjs2-color-component-radio-true-default-bg')
                },
                checkboxInput: {
                    checkMark: '3'
                },
                spacing: {
                    imageInputGap: getSizeVariable('--sjs2-pdf-layout-image-picker-items-gap-image-check'),
                }
            },
            textbase: {
                input: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                    backgroundColor: getColorVariable('--sjs2-color-component-formbox-default-bg'),
                    fontColor: getColorVariable('--sjs2-color-component-input-default-value'),
                },
                inputReadOnly: {
                    backgroundColor: null,
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                }
            },
            expression: {
                input: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                }
            },
            html: {
                text: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default')
                }
            },
            composite: {
                container: {
                    padding: 0,
                    borderWidth: 0,
                    backgroundColor: null
                },
                header: {
                    padding: [getSizeVariable('--sjs2-pdf-layout-section-padding-vertical'), getSizeVariable('--sjs2-pdf-layout-section-padding-horizontal')],
                    borderRadius: getSizeVariable('--sjs2-pdf-radius-section'),
                    backgroundColor: getColorVariable('--sjs2-color-bg-basic-secondary'),
                    borderWidth: getSizeVariable('--sjs2-pdf-border-width-section'),
                    borderColor: getColorVariable('--sjs2-color-border-basic-secondary'),
                },
                title: {
                    fontSize: getSizeVariable('--sjs2-typography-font-size-default'),
                    fontColor: getColorVariable('--sjs2-color-fg-basic-primary'),
                    fontStyle: 'bold',
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-default'),
                },
                description: {
                    fontColor: getColorVariable('--sjs2-color-fg-basic-secondary'),
                    fontSize: getSizeVariable('--sjs2-typography-font-size-small'),
                    lineHeight: getSizeVariable('--sjs2-typography-line-height-small'),
                },
                spacing: {
                    headerContentGap: getSizeVariable('--sjs2-pdf-layout-page-questions-gap-vertical'),
                    titleDescriptionGap: getSizeVariable('--sjs2-pdf-layout-title-default-gap'),
                }
            }
        };
        // return res;
    });
}

class EventHandler {
    static async process_header_events(survey, controller, packs) {
        var _a;
        const style = createStyleFromTheme(survey.theme, survey.layout, (options) => {
            return {
                text: {
                    fontSize: 10,
                    lineHeight: 12,
                    fontStyle: 'normal',
                    fontName: 'helvetica',
                    fontColor: options.getColorVariable('--sjs2-color-fg-basic-primary'),
                },
                link: {
                    fontColor: options.getColorVariable('--sjs2-color-fg-note-primary'),
                }
            };
        });
        if (!survey.haveCommercialLicense) {
            const licenseFlats = new FlatLicense(survey, controller, style).generateFlats({ xLeft: (_a = controller.margins.left) !== null && _a !== void 0 ? _a : 0, yTop: 5 });
            survey.onRenderHeader.add((_, canvas) => {
                canvas.packs.push(...licenseFlats);
            });
        }
        for (let i = 0; i < packs.length; i++) {
            await survey.onRenderHeader.fire(survey, new DrawCanvas(packs[i], controller, SurveyHelper.createHeaderRect(controller), packs.length, i + 1));
            await survey.onRenderFooter.fire(survey, new DrawCanvas(packs[i], controller, SurveyHelper.createFooterRect(controller), packs.length, i + 1));
        }
    }
}

// Auto-generated theme: compact
var CompactLayout = {
    '--sjs2-base-unit-size': '8px',
    '--sjs2-base-unit-spacing': '8px',
    '--sjs2-base-unit-radius': '8px',
    '--sjs2-base-unit-border-width': '1px',
    '--sjs2-base-unit-font-size': '8px',
    '--sjs2-base-unit-line-height': '8px',
    '--sjs2-base-unit-opacity': '1%',
    '--sjs2-base-unit-scale': '1%',
    '--sjs2-scale-x000': 'calc(var(--sjs2-base-unit-scale) * 0)',
    '--sjs2-scale-x025': 'calc(var(--sjs2-base-unit-scale) * 25)',
    '--sjs2-scale-x050': 'calc(var(--sjs2-base-unit-scale) * 50)',
    '--sjs2-scale-x075': 'calc(var(--sjs2-base-unit-scale) * 75)',
    '--sjs2-scale-x095': 'calc(var(--sjs2-base-unit-scale) * 95)',
    '--sjs2-scale-x098': 'calc(var(--sjs2-base-unit-scale) * 98)',
    '--sjs2-scale-x100': 'calc(var(--sjs2-base-unit-scale) * 100)',
    '--sjs2-scale-x200': 'calc(var(--sjs2-base-unit-scale) * 200)',
    '--sjs2-size-x000': 'calc(var(--sjs2-base-unit-size) * 0)',
    '--sjs2-size-x025': 'calc(var(--sjs2-base-unit-size) * 0.25)',
    '--sjs2-size-x050': 'calc(var(--sjs2-base-unit-size) * 0.50)',
    '--sjs2-size-x075': 'calc(var(--sjs2-base-unit-size) * 0.75)',
    '--sjs2-size-x100': 'calc(var(--sjs2-base-unit-size) * 1)',
    '--sjs2-size-x125': 'calc(var(--sjs2-base-unit-size) * 1.25)',
    '--sjs2-size-x150': 'calc(var(--sjs2-base-unit-size) * 1.50)',
    '--sjs2-size-x200': 'calc(var(--sjs2-base-unit-size) * 2)',
    '--sjs2-size-x250': 'calc(var(--sjs2-base-unit-size) * 2.50)',
    '--sjs2-size-x300': 'calc(var(--sjs2-base-unit-size) * 3)',
    '--sjs2-size-x350': 'calc(var(--sjs2-base-unit-size) * 3.50)',
    '--sjs2-size-x400': 'calc(var(--sjs2-base-unit-size) * 4)',
    '--sjs2-size-x500': 'calc(var(--sjs2-base-unit-size) * 5)',
    '--sjs2-size-x600': 'calc(var(--sjs2-base-unit-size) * 6)',
    '--sjs2-size-x700': 'calc(var(--sjs2-base-unit-size) * 7)',
    '--sjs2-size-x800': 'calc(var(--sjs2-base-unit-size) * 8)',
    '--sjs2-size-x900': 'calc(var(--sjs2-base-unit-size) * 9)',
    '--sjs2-size-x1000': 'calc(var(--sjs2-base-unit-size) * 10)',
    '--sjs2-size-x1100': 'calc(var(--sjs2-base-unit-size) * 11)',
    '--sjs2-size-x1200': 'calc(var(--sjs2-base-unit-size) * 12)',
    '--sjs2-size-x1300': 'calc(var(--sjs2-base-unit-size) * 13)',
    '--sjs2-size-x1400': 'calc(var(--sjs2-base-unit-size) * 14)',
    '--sjs2-size-x1500': 'calc(var(--sjs2-base-unit-size) * 15)',
    '--sjs2-radius-x000': 'calc(var(--sjs2-base-unit-radius) * 0)',
    '--sjs2-radius-x025': 'calc(var(--sjs2-base-unit-radius) * 0.25)',
    '--sjs2-radius-x050': 'calc(var(--sjs2-base-unit-radius) * 0.50)',
    '--sjs2-radius-x075': 'calc(var(--sjs2-base-unit-radius) * 0.75)',
    '--sjs2-radius-x100': 'calc(var(--sjs2-base-unit-radius) * 1)',
    '--sjs2-radius-x125': 'calc(var(--sjs2-base-unit-radius) * 1.25)',
    '--sjs2-radius-x150': 'calc(var(--sjs2-base-unit-radius) * 1.50)',
    '--sjs2-radius-x200': 'calc(var(--sjs2-base-unit-radius) * 2)',
    '--sjs2-radius-x250': 'calc(var(--sjs2-base-unit-radius) * 2.50)',
    '--sjs2-radius-x300': 'calc(var(--sjs2-base-unit-radius) * 3)',
    '--sjs2-radius-x400': 'calc(var(--sjs2-base-unit-radius) * 4)',
    '--sjs2-radius-x500': 'calc(var(--sjs2-base-unit-radius) * 5)',
    '--sjs2-radius-x600': 'calc(var(--sjs2-base-unit-radius) * 6)',
    '--sjs2-radius-x700': 'calc(var(--sjs2-base-unit-radius) * 7)',
    '--sjs2-radius-x800': 'calc(var(--sjs2-base-unit-radius) * 8)',
    '--sjs2-radius-round': '9999px',
    '--sjs2-spacing-x000': 'calc(var(--sjs2-base-unit-spacing) * 0)',
    '--sjs2-spacing-x025': 'calc(var(--sjs2-base-unit-spacing) * 0.25)',
    '--sjs2-spacing-x050': 'calc(var(--sjs2-base-unit-spacing) * 0.50)',
    '--sjs2-spacing-x075': 'calc(var(--sjs2-base-unit-spacing) * 0.75)',
    '--sjs2-spacing-x100': 'calc(var(--sjs2-base-unit-spacing) * 1)',
    '--sjs2-spacing-x125': 'calc(var(--sjs2-base-unit-spacing) * 1.25)',
    '--sjs2-spacing-x150': 'calc(var(--sjs2-base-unit-spacing) * 1.5)',
    '--sjs2-spacing-x200': 'calc(var(--sjs2-base-unit-spacing) * 2)',
    '--sjs2-spacing-x250': 'calc(var(--sjs2-base-unit-spacing) * 2.50)',
    '--sjs2-spacing-x300': 'calc(var(--sjs2-base-unit-spacing) * 3)',
    '--sjs2-spacing-x400': 'calc(var(--sjs2-base-unit-spacing) * 4)',
    '--sjs2-spacing-x500': 'calc(var(--sjs2-base-unit-spacing) * 5)',
    '--sjs2-spacing-x550': 'calc(var(--sjs2-base-unit-spacing) * 5.5)',
    '--sjs2-spacing-x600': 'calc(var(--sjs2-base-unit-spacing) * 6)',
    '--sjs2-spacing-x700': 'calc(var(--sjs2-base-unit-spacing) * 7)',
    '--sjs2-spacing-x800': 'calc(var(--sjs2-base-unit-spacing) * 8)',
    '--sjs2-spacing-negative-x025': 'calc(var(--sjs2-base-unit-spacing) * -0.25)',
    '--sjs2-spacing-negative-x050': 'calc(var(--sjs2-base-unit-spacing) * -0.50)',
    '--sjs2-spacing-negative-x075': 'calc(var(--sjs2-base-unit-spacing) * -0.75)',
    '--sjs2-spacing-negative-x100': 'calc(var(--sjs2-base-unit-spacing) * -1)',
    '--sjs2-spacing-negative-x125': 'calc(var(--sjs2-base-unit-spacing) * -1.25)',
    '--sjs2-spacing-negative-x150': 'calc(var(--sjs2-base-unit-spacing) * -1.5)',
    '--sjs2-spacing-negative-x200': 'calc(var(--sjs2-base-unit-spacing) * -2)',
    '--sjs2-spacing-negative-x250': 'calc(var(--sjs2-base-unit-spacing) * -2.50)',
    '--sjs2-spacing-negative-x300': 'calc(var(--sjs2-base-unit-spacing) * -3)',
    '--sjs2-spacing-negative-x400': 'calc(var(--sjs2-base-unit-spacing) * -4)',
    '--sjs2-spacing-negative-x500': 'calc(var(--sjs2-base-unit-spacing) * -5)',
    '--sjs2-spacing-negative-x600': 'calc(var(--sjs2-base-unit-spacing) * -6)',
    '--sjs2-spacing-negative-x700': 'calc(var(--sjs2-base-unit-spacing) * -7)',
    '--sjs2-spacing-negative-x800': 'calc(var(--sjs2-base-unit-spacing) * -8)',
    '--sjs2-opacity-x000': 'calc(var(--sjs2-base-unit-opacity) * 0)',
    '--sjs2-opacity-x005': 'calc(var(--sjs2-base-unit-opacity) * 5)',
    '--sjs2-opacity-x010': 'calc(var(--sjs2-base-unit-opacity) * 10)',
    '--sjs2-opacity-x015': 'calc(var(--sjs2-base-unit-opacity) * 15)',
    '--sjs2-opacity-x020': 'calc(var(--sjs2-base-unit-opacity) * 20)',
    '--sjs2-opacity-x025': 'calc(var(--sjs2-base-unit-opacity) * 25)',
    '--sjs2-opacity-x030': 'calc(var(--sjs2-base-unit-opacity) * 30)',
    '--sjs2-opacity-x035': 'calc(var(--sjs2-base-unit-opacity) * 35)',
    '--sjs2-opacity-x040': 'calc(var(--sjs2-base-unit-opacity) * 40)',
    '--sjs2-opacity-x045': 'calc(var(--sjs2-base-unit-opacity) * 45)',
    '--sjs2-opacity-x050': 'calc(var(--sjs2-base-unit-opacity) * 50)',
    '--sjs2-opacity-x055': 'calc(var(--sjs2-base-unit-opacity) * 55)',
    '--sjs2-opacity-x060': 'calc(var(--sjs2-base-unit-opacity) * 60)',
    '--sjs2-opacity-x065': 'calc(var(--sjs2-base-unit-opacity) * 65)',
    '--sjs2-opacity-x070': 'calc(var(--sjs2-base-unit-opacity) * 70)',
    '--sjs2-opacity-x075': 'calc(var(--sjs2-base-unit-opacity) * 75)',
    '--sjs2-opacity-x080': 'calc(var(--sjs2-base-unit-opacity) * 80)',
    '--sjs2-opacity-x085': 'calc(var(--sjs2-base-unit-opacity) * 85)',
    '--sjs2-opacity-x090': 'calc(var(--sjs2-base-unit-opacity) * 90)',
    '--sjs2-opacity-x095': 'calc(var(--sjs2-base-unit-opacity) * 95)',
    '--sjs2-opacity-x100': 'calc(var(--sjs2-base-unit-opacity) * 100)',
    '--sjs2-border-width-x000': 'calc(var(--sjs2-base-unit-border-width) * 0)',
    '--sjs2-border-width-x100': 'calc(var(--sjs2-base-unit-border-width) * 1)',
    '--sjs2-border-width-x200': 'calc(var(--sjs2-base-unit-border-width) * 2)',
    '--sjs2-border-width-x400': 'calc(var(--sjs2-base-unit-border-width) * 4)',
    '--sjs2-font-weight-regular': '400',
    '--sjs2-font-weight-medium': '500',
    '--sjs2-font-weight-semibold': '600',
    '--sjs2-font-weight-bold': '700',
    '--sjs2-font-size-x000': 'calc(var(--sjs2-base-unit-font-size) * 0)',
    '--sjs2-font-size-x100': 'calc(var(--sjs2-base-unit-font-size) * 1)',
    '--sjs2-font-size-x150': 'calc(var(--sjs2-base-unit-font-size) * 1.5)',
    '--sjs2-font-size-x200': 'calc(var(--sjs2-base-unit-font-size) * 2)',
    '--sjs2-font-size-x250': 'calc(var(--sjs2-base-unit-font-size) * 2.5)',
    '--sjs2-font-size-x300': 'calc(var(--sjs2-base-unit-font-size) * 3)',
    '--sjs2-font-size-x350': 'calc(var(--sjs2-base-unit-font-size) * 3.5)',
    '--sjs2-font-size-x400': 'calc(var(--sjs2-base-unit-font-size) * 4)',
    '--sjs2-font-size-x500': 'calc(var(--sjs2-base-unit-font-size) * 5)',
    '--sjs2-font-size-x600': 'calc(var(--sjs2-base-unit-font-size) * 6)',
    '--sjs2-line-height-x000': 'calc(var(--sjs2-base-unit-line-height) * 0)',
    '--sjs2-line-height-x100': 'calc(var(--sjs2-base-unit-line-height) * 1)',
    '--sjs2-line-height-x200': 'calc(var(--sjs2-base-unit-line-height) * 2)',
    '--sjs2-line-height-x300': 'calc(var(--sjs2-base-unit-line-height) * 3)',
    '--sjs2-line-height-x400': 'calc(var(--sjs2-base-unit-line-height) * 4)',
    '--sjs2-line-height-x500': 'calc(var(--sjs2-base-unit-line-height) * 5)',
    '--sjs2-line-height-x600': 'calc(var(--sjs2-base-unit-line-height) * 6)',
    '--sjs2-text-case-default': 'none',
    '--sjs2-text-case-uppercase': 'uppercase',
    '--sjs2-typography-font-family-text': 'Open Sans',
    '--sjs2-typography-font-family-code': 'DM Mono',
    '--sjs2-typography-font-family-component-input-content': 'var(--sjs2-typography-font-family-text)',
    '--sjs2-typography-font-family-component-question-title': 'var(--sjs2-typography-font-family-text)',
    '--sjs2-typography-font-family-component-question-description': 'var(--sjs2-typography-font-family-text)',
    '--sjs2-typography-font-family-component-page-title': 'var(--sjs2-typography-font-family-text)',
    '--sjs2-typography-font-family-component-page-description': 'var(--sjs2-typography-font-family-text)',
    '--sjs2-typography-font-family-component-header-title': 'var(--sjs2-typography-font-family-text)',
    '--sjs2-typography-font-family-component-header-description': 'var(--sjs2-typography-font-family-text)',
    '--sjs2-typography-font-size-small': 'var(--sjs2-font-size-x150)',
    '--sjs2-typography-font-size-default': 'var(--sjs2-font-size-x200)',
    '--sjs2-typography-font-size-medium': 'var(--sjs2-font-size-x300)',
    '--sjs2-typography-font-size-large': 'var(--sjs2-font-size-x400)',
    '--sjs2-typography-font-size-component-input-content': 'var(--sjs2-typography-font-size-default)',
    '--sjs2-typography-font-size-component-question-title': 'var(--sjs2-typography-font-size-default)',
    '--sjs2-typography-font-size-component-question-description': 'var(--sjs2-typography-font-size-default)',
    '--sjs2-typography-font-size-component-page-title': 'var(--sjs2-typography-font-size-medium)',
    '--sjs2-typography-font-size-component-page-description': 'var(--sjs2-typography-font-size-default)',
    '--sjs2-typography-font-size-component-header-title': 'var(--sjs2-typography-font-size-large)',
    '--sjs2-typography-font-size-component-header-description': 'var(--sjs2-typography-font-size-medium)',
    '--sjs2-typography-line-height-small': 'var(--sjs2-line-height-x200)',
    '--sjs2-typography-line-height-default': 'var(--sjs2-line-height-x300)',
    '--sjs2-typography-line-height-medium': 'var(--sjs2-line-height-x400)',
    '--sjs2-typography-line-height-large': 'var(--sjs2-line-height-x500)',
    '--sjs2-typography-line-height-component-input-content': 'var(--sjs2-typography-line-height-default)',
    '--sjs2-typography-line-height-component-question-title': 'var(--sjs2-typography-line-height-default)',
    '--sjs2-typography-line-height-component-question-description': 'var(--sjs2-typography-line-height-default)',
    '--sjs2-typography-line-height-component-page-title': 'var(--sjs2-typography-line-height-medium)',
    '--sjs2-typography-line-height-component-page-description': 'var(--sjs2-typography-line-height-default)',
    '--sjs2-typography-line-height-component-header-title': 'var(--sjs2-typography-line-height-large)',
    '--sjs2-typography-line-height-component-header-description': 'var(--sjs2-typography-line-height-medium)',
    '--sjs2-typography-font-weight-basic': 'var(--sjs2-font-weight-regular)',
    '--sjs2-typography-font-weight-strong': 'var(--sjs2-font-weight-semibold)',
    '--sjs2-typography-font-weight-component-input-content': 'var(--sjs2-typography-font-weight-basic)',
    '--sjs2-typography-font-weight-component-question-title': 'var(--sjs2-typography-font-weight-strong)',
    '--sjs2-typography-font-weight-component-question-description': 'var(--sjs2-typography-font-weight-basic)',
    '--sjs2-typography-font-weight-component-page-title': 'var(--sjs2-typography-font-weight-strong)',
    '--sjs2-typography-font-weight-component-page-description': 'var(--sjs2-typography-font-weight-basic)',
    '--sjs2-typography-font-weight-component-header-title': 'var(--sjs2-typography-font-weight-strong)',
    '--sjs2-typography-font-weight-component-header-description': 'var(--sjs2-typography-font-weight-basic)',
    '--sjs2-is-panelless': 'false',
    '--sjs2-pdf-radius-question': 'var(--sjs2-radius-x000)',
    '--sjs2-pdf-radius-matrix': 'var(--sjs2-radius-x000)',
    '--sjs2-pdf-radius-checkbox': 'var(--sjs2-radius-x000)',
    '--sjs2-pdf-radius-radio': 'var(--sjs2-radius-round)',
    '--sjs2-pdf-radius-section': 'var(--sjs2-radius-x000)',
    '--sjs2-pdf-border-width-section': 'var(--sjs2-border-width-x100)',
    '--sjs2-pdf-border-width-question': 'var(--sjs2-border-width-x100)',
    '--sjs2-pdf-border-width-check': 'var(--sjs2-border-width-x100)',
    '--sjs2-pdf-layout-page-padding-top': 'var(--sjs2-spacing-x550)',
    '--sjs2-pdf-layout-page-padding-bottom': 'var(--sjs2-spacing-x550)',
    '--sjs2-pdf-layout-page-padding-left': 'var(--sjs2-spacing-x550)',
    '--sjs2-pdf-layout-page-padding-right': 'var(--sjs2-spacing-x550)',
    '--sjs2-pdf-layout-page-gap-vertical': 'var(--sjs2-spacing-x000)',
    '--sjs2-pdf-layout-page-gap-horizontal': 'var(--sjs2-spacing-x000)',
    '--sjs2-pdf-layout-page-questions-gap-horizontal': 'var(--sjs2-spacing-x000)',
    '--sjs2-pdf-layout-page-questions-gap-vertical': 'var(--sjs2-spacing-x000)',
    '--sjs2-pdf-layout-page-matrix-gap-horizontal': 'var(--sjs2-spacing-x000)',
    '--sjs2-pdf-layout-page-matrix-gap-vertical': 'var(--sjs2-spacing-x000)',
    '--sjs2-pdf-layout-title-large-gap': 'var(--sjs2-spacing-x150)',
    '--sjs2-pdf-layout-title-large-padding-bottom': 'var(--sjs2-spacing-x400)',
    '--sjs2-pdf-layout-title-medium-gap': 'var(--sjs2-spacing-x100)',
    '--sjs2-pdf-layout-title-medium-padding-bottom': 'var(--sjs2-spacing-x300)',
    '--sjs2-pdf-layout-title-default-gap': 'var(--sjs2-spacing-x050)',
    '--sjs2-pdf-layout-title-default-padding-bottom': 'var(--sjs2-spacing-x150)',
    '--sjs2-pdf-layout-section-padding-vertical': 'var(--sjs2-spacing-x100)',
    '--sjs2-pdf-layout-section-padding-horizontal': 'var(--sjs2-spacing-x100)',
    '--sjs2-pdf-layout-section-gap': 'var(--sjs2-spacing-x050)',
    '--sjs2-pdf-layout-check-padding-vertical': 'var(--sjs2-spacing-x000)',
    '--sjs2-pdf-layout-check-padding-horizontal': 'var(--sjs2-spacing-x050)',
    '--sjs2-pdf-layout-check-gap': 'var(--sjs2-spacing-x100)',
    '--sjs2-pdf-layout-matrix-padding-vertical': 'var(--sjs2-spacing-x000)',
    '--sjs2-pdf-layout-matrix-padding-horizontal': 'var(--sjs2-spacing-x050)',
    '--sjs2-pdf-layout-matrix-gap': 'var(--sjs2-spacing-x100)',
    '--sjs2-pdf-layout-question-padding-vertical': 'var(--sjs2-spacing-x100)',
    '--sjs2-pdf-layout-question-padding-horizontal': 'var(--sjs2-spacing-x100)',
    '--sjs2-pdf-layout-question-gap': 'var(--sjs2-spacing-x100)',
    '--sjs2-pdf-layout-question-labels-gap-vertical': 'var(--sjs2-spacing-x050)',
    '--sjs2-pdf-layout-question-items-gap-vertical': 'var(--sjs2-spacing-x050)',
    '--sjs2-pdf-layout-question-items-gap-horizontal': 'var(--sjs2-spacing-x150)',
    '--sjs2-pdf-layout-image-picker-items-gap-image-check': 'var(--sjs2-spacing-x000)'
};

/**
 * 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);
        this.legacyLayout = {};
        /**
         * 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.
         */
        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.
         */
        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.
         */
        this.onRenderPage = new EventAsync();
        this.onDocControllerCreated = new EventBase();
        this.onRenderCheckItemAcroform = new EventAsync();
        this.onRenderRadioGroupWrapAcroform = new EventAsync();
        this.onRenderRadioItemAcroform = new EventAsync();
        /**
         * An event that allows you to customize the visual style applied to a question in an exported PDF document.
         *
         * 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 whose style is being customized.
         * - `options.getColorVariable`: `(name: string) => string`\
         * A helper function that returns the value of a color variable by name.
         * - `options.getSizeVariable`: `(name: string) => number`\
         * A helper function that returns the value of a size variable by name.
         * - `options.style`: [`IQuestionStyle`](https://surveyjs.io/pdf-generator/documentation/api-reference/IQuestionStyle)\
         * An object that defines the question's visual style. Modify its properties to control how the question is rendered in the exported PDF document.
         *
         * [Customize Individual Element Styles in PDF](https://surveyjs.io/pdf-generator/documentation/customize-survey-question-rendering-in-pdf-form#customize-individual-element-styles (linkStyle))
         * @since 3.0.0
         */
        this.onGetQuestionStyle = new EventBase;
        /**
         * An event that allows you to customize the visual style applied to a panel in an exported PDF document.
         *
         * 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 whose style is being customized.
         * - `options.getColorVariable`: `(name: string) => string`\
         * A helper function that returns the value of a color variable by name.
         * - `options.getSizeVariable`: `(name: string) => number`\
         * A helper function that returns the value of a size variable by name.
         * - `options.style`: [`IPanelStyle`](https://surveyjs.io/pdf-generator/documentation/api-reference/IPanelStyle)\
         * An object that defines the panel's visual style. Modify its properties to control how the panel is rendered in the exported PDF document.
         *
         * [Customize Individual Element Styles in PDF](https://surveyjs.io/pdf-generator/documentation/customize-survey-question-rendering-in-pdf-form#customize-individual-element-styles (linkStyle))
         * @since 3.0.0
         */
        this.onGetPanelStyle = new EventBase;
        /**
         * An event that allows you to customize the visual style applied to a page in an exported PDF document.
         *
         * 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 whose style is being customized.
         * - `options.getColorVariable`: `(name: string) => string`\
         * A helper function that returns the value of a color variable by name.
         * - `options.getSizeVariable`: `(name: string) => number`\
         * A helper function that returns the value of a size variable by name.
         * - `options.style`: [`IPageStyle`](https://surveyjs.io/pdf-generator/documentation/api-reference/IPageStyle)\
         * An object that defines the page's visual style. Modify its properties to control how the page is rendered in the exported PDF document.
         *
         * [Customize Individual Element Styles in PDF](https://surveyjs.io/pdf-generator/documentation/customize-survey-question-rendering-in-pdf-form#customize-individual-element-styles (linkStyle))
         * @since 3.0.0
         */
        this.onGetPageStyle = new EventBase;
        /**
         * An event that allows you to customize the visual style applied to a choice item in an exported PDF document.
         *
         * Parameters:
         *
         * - `sender`: `SurveyPDF`\
         * A `SurveyPDF` instance that raised the event.
         * - `options.question`: [`Question`](https://surveyjs.io/form-library/documentation/api-reference/question)\
         * A question to which the item belongs.
         * - `options.item`: `ItemValue`\
         * A choice item whose style is being customized.
         * - `options.getColorVariable`: `(name: string) => string`\
         * A helper function that returns the value of a color variable by name.
         * - `options.getSizeVariable`: `(name: string) => number`\
         * A helper function that returns the value of a size variable by name.
         * - `options.style.choiceText`: [`ITextStyle`](https://surveyjs.io/pdf-generator/documentation/api-reference/ITextStyle)\
         * An object that defines the visual style applied to the item's text.
         * - `options.style.input`: [`ISelectionInputStyle`](https://surveyjs.io/pdf-generator/documentation/api-reference/ISelectionInputStyle)\
         * An object that defines the visual style applied to the item's input control.
         *
         * Modify the properties of `options.style.choiceText` and `options.style.input` to control how the item is rendered in the exported PDF document.
         *
         * [Customize Individual Element Styles in PDF](https://surveyjs.io/pdf-generator/documentation/customize-survey-question-rendering-in-pdf-form#customize-individual-element-styles (linkStyle))
         * @since 3.0.0
         */
        this.onGetItemStyle = new EventBase;
        this.stylesHash = {};
        this.navigationMap = {};
        if (typeof options === 'undefined') {
            options = {};
        }
        if (this.questionsOnPageMode == 'inputPerPage' || this.questionsOnPageMode == 'questionPerPage') {
            this.questionsOnPageMode = 'standard';
        }
        this.options = SurveyHelper.clone(options);
        if (this.options.fontSize !== undefined) {
            const base = `${this.options.fontSize / 14 * 8}px`;
            SurveyHelper.mergeObjects(this.legacyLayout, {
                '--sjs2-base-unit-size': base,
                '--sjs2-base-unit-spacing': base,
                '--sjs2-base-unit-radius': base,
                '--sjs2-base-unit-border-width': `${this.options.fontSize / 14}px`,
                '--sjs2-base-unit-font-size': base,
                '--sjs2-base-unit-line-height': base,
            });
        }
        if (this.options.margins) {
            for (const key in this.options.margins) {
                if (this.options.margins[key] !== undefined) {
                    this.legacyLayout[`--sjs2-pdf-layout-page-padding-${key == 'bot' ? 'bottom' : key}`] = `${96.0 / 25.4 * this.options.margins[key]}px`;
                }
            }
        }
        this.applyTheme(BaseTheme);
    }
    get haveCommercialLicense() {
        const f = 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');
    }
    get licenseText() {
        const d = !!glc ? glc(2) : false;
        if (!!d && d.toLocaleDateString) {
            return 'This banner appears because your maintenance subscription for the PDF Generator library expired on {date}. You may continue using [all versions released up to that date](https://surveyjs.io/stay-updated/release-notes). To remove this banner in the latest version, please [renew your subscription](https://surveyjs.io/manage#license-manager) and [set up a new license key](https://surveyjs.io/remove-alert-banner).'.replace('{date}', d.toLocaleDateString());
        }
        return "To use the PDF Generator library to create PDF forms such as this one, a [developer license](https://surveyjs.io/licensing) is required. If you have an active license, please [set up your license key](https://surveyjs.io/remove-alert-banner) and ensure you're using the [latest version](https://surveyjs.io/stay-updated/release-notes).";
    }
    updateCheckItemAcroformOptions(options, question, context) {
        this.onRenderCheckItemAcroform.fire(this, {
            options: options,
            question: question,
            ...(context !== null && context !== void 0 ? context : {})
        });
    }
    getUpdatedRadioGroupWrapOptions(options, question, context) {
        this.onRenderRadioGroupWrapAcroform.fire(this, {
            options: options,
            question: question,
            ...(context !== null && context !== void 0 ? context : {})
        });
    }
    updateRadioItemAcroformOptions(options, question, context) {
        this.onRenderRadioItemAcroform.fire(this, {
            options: options,
            question: question,
            ...(context !== null && context !== void 0 ? context : {})
        });
    }
    /**
     * An object that defines the visual style applied to UI elements in an exported PDF document.
     *
     * To apply a new visual style to the PDF document, call the [`applyStyle`](https://surveyjs.io/pdf-generator/documentation/api-reference/surveypdf#applyStyle) method.
     *
     * [PDF Appearance Customization - Styles Config](/pdf-generator/documentation/pdf-appearance-customization#styles-config (linkStyle))
     * @since 3.0.0
     */
    get style() {
        if (!this.styleValue) {
            this.styleValue = getDefaultStyle(this.theme, this.layout);
        }
        return this.styleValue;
    }
    clearStyles() {
        this.styleValue = undefined;
        this.stylesHash = {};
    }
    /**
     * Applies a visual style to UI elements in the exported PDF document.
     *
     * This method accepts either an [`IDocStyle`](https://surveyjs.io/pdf-generator/documentation/api-reference/IDocStyle) object that overrides properties in the default visual style, or a callback function that returns such an object. When a callback is used, it receives helper functions&mdash;`getSizeVariable(name)` and `getColorVariable(name)`&mdash;which allow you to derive dimensions and colors from the currently applied UI theme.
     *
     * [PDF Appearance Customization - Styles Config](/pdf-generator/documentation/pdf-appearance-customization#styles-config (linkStyle))
     * @param value An [`IDocStyle`](https://surveyjs.io/pdf-generator/documentation/api-reference/IDocStyle) object, or a callback function that returns an `IDocStyle` object.
     * @since 3.0.0
     */
    applyStyle(value) {
        if (typeof value == 'function') {
            this.styleValue = SurveyHelper.mergeObjects({}, this.style, createStyleFromTheme(this.theme, this.layout, value));
        }
        else {
            this.styleValue = SurveyHelper.mergeObjects({}, this.style, value);
        }
        this.stylesHash = {};
    }
    get theme() {
        return this._theme || BaseTheme;
    }
    /**
     * Applies a UI theme to the exported PDF document.
     *
     * A theme defines color- and shadow-related CSS variables. To configure spacing, sizing, typography, and other non-color variables, use the [`applyLayout`](#applyLayout) method.
     * @param theme An [`ITheme`](https://surveyjs.io/form-library/documentation/api-reference/itheme) object with theme settings.
     * @param baseTheme An optional `ITheme` object used as the base theme. When specified, it is deep-merged with `theme`, and the merged result is applied.
     * @since 3.0.0
     */
    applyTheme(theme, baseTheme) {
        this._theme = SurveyHelper.mergeObjects({}, BaseTheme, baseTheme !== null && baseTheme !== void 0 ? baseTheme : {}, theme);
        this.clearStyles();
    }
    get defaultLayout() {
        if (!this.defaultLayoutValue) {
            this.defaultLayoutValue = SurveyHelper.mergeObjects({}, CompactLayout, this.legacyLayout);
        }
        return this.defaultLayoutValue;
    }
    get layout() {
        return this._layout || this.defaultLayout;
    }
    /**
     * Applies a layout configuration to the exported PDF document.
     *
     * A layout defines non-color CSS variables, including spacing, sizing, typography, border radius, and other dimensional variables. To configure colors and shadows, use the [`applyTheme`](#applyTheme) method.
     * @param layout An `IDocLayout` object that specifies layout variables.
     * @param baseLayout An optional `IDocLayout` object used as the base layout. When specified, it is deep-merged with `layout`, and the merged result is applied.
     * @since 3.0.0
     */
    applyLayout(layout, baseLayout) {
        this._layout = SurveyHelper.mergeObjects({}, this.defaultLayout, baseLayout !== null && baseLayout !== void 0 ? baseLayout : {}, layout);
        this.clearStyles();
    }
    getItemStyle(question, item, style) {
        return createStyleFromTheme(this.theme, this.layout, (options) => {
            const eventOptions = {
                getColorVariable: options.getColorVariable,
                getSizeVariable: options.getSizeVariable,
                style: style
            };
            this.onGetItemStyle.fire(this, { question, item, ...eventOptions });
            return style;
        });
    }
    getElementStyle(element) {
        const uniqueId = element.uniqueId;
        if (!this.stylesHash[uniqueId]) {
            const style = this.style;
            const types = [element.getType()];
            let currentClass = Serializer.findClass(element.getType());
            while (!!currentClass.parentName) {
                types.unshift(currentClass.parentName);
                currentClass = Serializer.findClass(currentClass.parentName);
            }
            if (element.getTemplate() == 'composite') {
                types.push('composite');
            }
            const res = {};
            types.forEach(type => {
                var _a;
                SurveyHelper.mergeObjects(res, (_a = style[type]) !== null && _a !== void 0 ? _a : {});
            });
            this.stylesHash[uniqueId] = createStyleFromTheme(this.theme, this.layout, (options) => {
                const eventOptions = {
                    getColorVariable: options.getColorVariable,
                    getSizeVariable: options.getSizeVariable,
                    style: res
                };
                if (element.isPanel) {
                    this.onGetPanelStyle.fire(this, { panel: element, ...eventOptions });
                }
                if (element.isPage) {
                    this.onGetPageStyle.fire(this, { page: element, ...eventOptions });
                }
                if (element.isQuestion) {
                    this.onGetQuestionStyle.fire(this, { question: element, ...eventOptions });
                }
                return res;
            });
        }
        return this.stylesHash[uniqueId];
    }
    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 };
                    });
                });
            });
        }
    }
    afterRenderSurveyElement(element, bricks) {
        bricks.forEach(brick => brick.addBeforeRenderCallback(() => {
            var _a;
            if (brick.getPageNumber() !== undefined) {
                this.navigationMap[element.uniqueId] = Math.min((_a = this.navigationMap[element.uniqueId]) !== null && _a !== void 0 ? _a : Number.MAX_VALUE, brick.getPageNumber() + 1);
            }
        }));
    }
    renderPanelNavigation(controller, panel, rootChapter) {
        const { doc } = controller;
        if (!this.navigationMap[panel.uniqueId])
            return;
        const panelChapter = doc.outline.add(rootChapter, panel.title || panel.name, { pageNumber: this.navigationMap[panel.uniqueId] });
        panel.elements.forEach((el) => {
            if (el.isVisible && this.navigationMap[el.uniqueId]) {
                if (el.isPanel) {
                    this.renderPanelNavigation(controller, el, panelChapter);
                }
                else {
                    doc.outline.add(panelChapter, el.title || el.name, { pageNumber: this.navigationMap[el.uniqueId] });
                }
            }
        });
    }
    renderNavigation(controller) {
        var _a;
        if ((_a = this.options.showNavigation) !== null && _a !== void 0 ? _a : true) {
            this.visiblePages.forEach(page => {
                this.renderPanelNavigation(controller, page, null);
            });
            this.navigationMap = {};
        }
    }
    async renderSurvey(controller) {
        this.visiblePages.forEach(page => page.onFirstRendering());
        const flats = await FlatRepository.getInstance().createSurvey(this, controller, this.style.survey).generateFlats();
        this.correctBricksPosition(controller, flats);
        const packs = PagePacker.pack(flats, controller);
        packs.forEach((pagePack, i) => {
            pagePack.forEach(pack => {
                pack.setPageNumber(i);
            });
        });
        await EventHandler.process_header_events(this, controller, packs);
        for (let i = 0; i < packs.length; i++) {
            if (controller.getNumberOfPages() === i) {
                controller.addPage();
            }
            controller.setPage(i);
            controller.setFillColor(this.style.survey.backgroundColor);
            controller.doc.rect(0, 0, controller.doc.internal.pageSize.getWidth(), controller.doc.internal.pageSize.getHeight(), 'F');
            controller.restoreFillColor();
            for (let j = 0; j < packs[i].length; j++) {
                //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');
                //   }
                // );
                await packs[i][j].render();
            }
        }
        this.renderNavigation(controller);
        SurveyHelper.clear();
        this.stylesHash = {};
    }
    createDocController() {
        const marginsFromStyle = parseSideValues(this.style.survey.padding);
        const options = SurveyHelper.mergeObjects({}, this.options, {
            margins: marginsFromStyle
        });
        const controller = new DocController(options);
        this.onDocControllerCreated.fire(this, { controller: controller });
        return controller;
    }
    get docController() {
        if (!this.docControllerValue) {
            this.docControllerValue = this.createDocController();
        }
        return this.docControllerValue;
    }
    /**
     * An asynchronous method that starts to download the generated PDF file in the web browser.
     *
     * [View Demo](https://surveyjs.io/pdf-generator/examples/save-completed-forms-as-pdf-files/ (linkStyle))
     * @param fileName *(Optional)* A file name with the ".pdf" extension. Default value: `"survey_result.pdf"`.
     */
    async save(fileName = 'survey_result.pdf') {
        if (!SurveyPDF.currentlySaving) {
            const controller = this.docController;
            SurveyPDF.currentlySaving = true;
            SurveyHelper.fixFont(controller);
            await 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);
            });
        }
    }
    async raw(type) {
        const controller = this.createDocController();
        this.onDocControllerCreated.fire(this, { controller: controller });
        SurveyHelper.fixFont(controller);
        await this.renderSurvey(controller);
        return controller.doc.output(type);
    }
}
SurveyPDF.currentlySaving = false;
SurveyPDF.saveQueue = [];

class FlatPanel {
    constructor(survey, panel, controller, style) {
        this.survey = survey;
        this.panel = panel;
        this.controller = controller;
        this.style = style;
    }
    async generateFlats(point) {
        const panelFlats = [];
        const panelContentPoint = SurveyHelper.clone(point);
        this.controller.pushMargins();
        this.controller.margins.left += this.controller.measureText(this.panel.innerIndent).width;
        panelContentPoint.xLeft += this.controller.measureText(this.panel.innerIndent).width;
        panelFlats.push(...await this.generateContentFlats(panelContentPoint));
        this.controller.popMargins();
        const adornersOptions = new AdornersPanelOptions(point, panelFlats, this.panel, this.controller, FlatRepository.getInstance());
        await this.survey.onRenderPanel.fire(this.survey, adornersOptions);
        const bricks = [...adornersOptions.bricks];
        this.survey.afterRenderSurveyElement(this.panel, bricks);
        return bricks;
    }
    async generateContentFlats(point) {
        if (!this.panel.isVisible)
            return;
        this.panel.onFirstRendering();
        const panelFlats = [];
        let currPoint = SurveyHelper.clone(point);
        const headerContentBrick = new CompositeBrick();
        if (this.panel.hasDescriptionUnderTitle || this.panel.hasTitle) {
            const headerFlats = await this.createHeaderFlats(currPoint);
            headerContentBrick.addBrick(...headerFlats);
            currPoint.yTop = headerFlats[headerFlats.length - 1].yBot + this.style.spacing.headerContentGap + SurveyHelper.EPSILON;
        }
        const rowFlats = await this.generateRowsFlats(currPoint);
        if (!headerContentBrick.isEmpty && rowFlats.length > 0) {
            headerContentBrick.addBrick(rowFlats.shift());
        }
        if (!headerContentBrick.isEmpty) {
            panelFlats.push(headerContentBrick);
        }
        panelFlats.push(...rowFlats);
        return panelFlats;
    }
    async generateTitleFlat(point) {
        const composite = new CompositeBrick();
        const textOptions = { ...this.style.title };
        let currPoint = SurveyHelper.clone(point);
        if (this.panel.no) {
            const noFlat = await SurveyHelper.createTextFlat(currPoint, this.controller, this.panel.no, textOptions);
            composite.addBrick(noFlat);
            currPoint.xLeft = noFlat.xRight + this.controller.measureText(' ').width;
        }
        const panelTitleFlat = await SurveyHelper.createTextFlat(currPoint, this.controller, this.panel.locTitle, textOptions);
        composite.addBrick(panelTitleFlat);
        return composite;
    }
    async createHeaderFlats(point) {
        const containerBrick = new ContainerBrick(this.controller, {
            ...point,
            width: SurveyHelper.getPageAvailableWidth(this.controller)
        }, this.style.header);
        await containerBrick.setup(async (point, bricks) => {
            let currPoint = SurveyHelper.clone(point);
            if (this.panel.hasTitle) {
                const titleFlat = await this.generateTitleFlat(currPoint);
                bricks.push(titleFlat);
                currPoint = SurveyHelper.createPoint(titleFlat);
            }
            if (this.panel.description) {
                if (this.panel.title) {
                    currPoint.yTop += this.style.spacing.titleDescriptionGap;
                }
                const panelDescFlat = await SurveyHelper.createTextFlat(currPoint, this.controller, this.panel.locDescription, { ...this.style.description });
                bricks.push(panelDescFlat);
                currPoint = SurveyHelper.createPoint(panelDescFlat);
            }
            const rowLinePoint = SurveyHelper.createPoint(SurveyHelper.mergeRects(...bricks));
            bricks.push(SurveyHelper.createRowlineFlat(rowLinePoint, this.controller));
        });
        return [containerBrick];
    }
    getRows(controller) {
        const availableWidth = SurveyHelper.getPageAvailableWidth(controller);
        const rows = [];
        const gapBetweenElements = this.style.spacing.inlineElementGap;
        this.panel.rows.forEach(row => {
            let currentAvailableWidth = availableWidth + gapBetweenElements;
            let currentRow = [];
            if (!row.visible)
                return;
            const visibleElements = row.elements.filter(el => el.isVisible);
            visibleElements.forEach((el, i) => {
                const style = this.survey.getElementStyle(el);
                const minWidth = el.minWidth && el.minWidth !== 'auto' ? SurveyHelper.parseWidth(el.minWidth, availableWidth, undefined, 'px') : style.minWidth;
                const renderWidth = !!el.width ? SurveyHelper.parseWidth(el.width, availableWidth, undefined, 'px') : 0;
                const maxWidth = SurveyHelper.parseWidth(el.maxWidth ? el.maxWidth : '100%', availableWidth, undefined, 'px');
                const width = Math.min(Math.max(minWidth, renderWidth), maxWidth);
                if (currentAvailableWidth < width + gapBetweenElements) {
                    rows.push(currentRow);
                    currentAvailableWidth = availableWidth - gapBetweenElements;
                    currentRow = [];
                }
                currentAvailableWidth -= width + gapBetweenElements;
                currentRow.push({ element: el, width: width });
            });
            if (currentRow.length != 0) {
                rows.push(currentRow);
            }
        });
        rows.forEach((row) => {
            const widthSum = row.reduce((sum, rowEl) => sum + rowEl.width, 0);
            let alignValue = (availableWidth - widthSum - (row.length - 1) * gapBetweenElements) / row.length;
            let expandableElements = [].concat(row);
            let restWidth = alignValue * row.length;
            while (expandableElements.length > 0 && restWidth > 0) {
                expandableElements = expandableElements.filter(rowEl => {
                    const maxWidth = SurveyHelper.parseWidth(rowEl.element.maxWidth ? rowEl.element.maxWidth : '100%', availableWidth, undefined, 'px');
                    if (!!rowEl.element.width && row.length > 1) {
                        return false;
                    }
                    if (maxWidth > rowEl.width + alignValue) {
                        restWidth -= alignValue;
                        rowEl.width = rowEl.width + alignValue;
                        return true;
                    }
                    else {
                        restWidth -= maxWidth - rowEl.width;
                        rowEl.width = maxWidth;
                        return false;
                    }
                });
                alignValue = restWidth / expandableElements.length;
            }
            //expand elements with strict width if space is not fullfilled
            if (restWidth > 0) {
                const expandableElements = row.filter(rowEl => !rowEl.element.maxWidth);
                const alignValue = restWidth / expandableElements.length;
                expandableElements.forEach(rowEl => {
                    rowEl.width += alignValue;
                });
            }
        });
        return rows;
    }
    getGapBetweenRows() {
        return this.style.spacing.elementGap;
    }
    async generateRowsFlats(point) {
        const currPoint = SurveyHelper.clone(point);
        const rowsFlats = [];
        const rowsInfo = this.getRows(this.controller);
        for (const rowInfo of rowsInfo) {
            let nextMarginLeft = this.controller.margins.left;
            const rowContainers = [];
            for (let i = 0; i < rowInfo.length; i++) {
                const { element, width } = rowInfo[i];
                const gap = this.style.spacing.inlineElementGap;
                this.controller.pushMargins();
                this.controller.margins.left = nextMarginLeft;
                this.controller.margins.right = this.controller.paperWidth - this.controller.margins.left - width;
                currPoint.xLeft = this.controller.margins.left;
                const elementStyle = this.survey.getElementStyle(element);
                const containerBrick = new ContainerBrick(this.controller, { ...currPoint, width: SurveyHelper.getPageAvailableWidth(this.controller) }, elementStyle.container);
                await containerBrick.setup(async (point, bricks) => {
                    if (element instanceof PanelModel) {
                        bricks.push(...await SurveyHelper.generatePanelFlats(this.survey, this.controller, element, point));
                    }
                    else {
                        await element.waitForQuestionIsReady();
                        bricks.push(...await SurveyHelper.generateQuestionFlats(this.survey, this.controller, element, point));
                    }
                });
                rowContainers.push(containerBrick);
                this.controller.popMargins();
                nextMarginLeft += width + gap;
            }
            currPoint.xLeft = this.controller.margins.left;
            if (rowContainers.length !== 0) {
                const rowRect = SurveyHelper.mergeRects(...rowContainers);
                const yBot = rowRect.yBot;
                currPoint.yTop = yBot;
                rowContainers.forEach((brick) => {
                    brick.fitToHeight(rowRect.yBot - rowRect.yTop);
                });
                rowContainers.forEach((elementFlat) => rowsFlats.push(...elementFlat.getBricks()));
                rowsFlats.push(SurveyHelper.createRowlineFlat(currPoint, this.controller));
                currPoint.xLeft = point.xLeft;
                currPoint.yTop += this.getGapBetweenRows();
                nextMarginLeft = currPoint.xLeft;
            }
        }
        return rowsFlats;
    }
}
FlatRepository.registerPanel(FlatPanel);

class FlatPage extends FlatPanel {
    async generateTitleFlat(point) {
        return await SurveyHelper.createTextFlat(point, this.controller, this.panel.locTitle, { ...this.style.title });
    }
    async generateFlats(point) {
        const pageFlats = [];
        pageFlats.push(...await this.generateContentFlats(point));
        const adornersOptions = new AdornersPageOptions(point, pageFlats, this.panel, this.controller, FlatRepository.getInstance());
        await this.survey.onRenderPage.fire(this.survey, adornersOptions);
        const bricks = [...adornersOptions.bricks];
        this.survey.afterRenderSurveyElement(this.panel, bricks);
        return bricks;
    }
    getGapBetweenRows() {
        if (this.survey.isSinglePage && this.panel === this.survey.visiblePages[0]) {
            return this.survey.style.survey.spacing.pageGap;
        }
        return super.getGapBetweenRows();
    }
}
FlatRepository.registerPage(FlatPage);

class CheckItemBrick extends PdfBrick {
    constructor(controller, rect, options, style) {
        super(controller, rect);
        this.options = options;
        this.style = style;
    }
    async renderInteractive() {
        var _a;
        const checkBox = new this.controller.AcroFormCheckBox();
        const scaledAcroformRect = SurveyHelper.createAcroformRect(SurveyHelper.createRectInsideBorders(this.contentRect, (_a = this.style.borderWidth) !== null && _a !== void 0 ? _a : 0));
        const { color: fontColor } = SurveyHelper.parseColor(this.style.fontColor);
        if (this.style.backgroundColor) {
            this.controller.setFillColor(this.style.backgroundColor);
            this.controller.doc.rect(...scaledAcroformRect, 'F');
            this.controller.restoreFillColor();
        }
        const options = {};
        options.maxFontSize = this.style.fontSize;
        options.caption = this.style.checkMark;
        options.textAlign = 'center';
        options.fieldName = this.options.fieldName;
        options.readOnly = this.options.readOnly;
        options.color = fontColor;
        options.value = this.options.checked ? 'On' : false;
        options.AS = this.options.checked ? '/On' : '/Off';
        options.Rect = scaledAcroformRect;
        this.controller.doc.addField(checkBox);
        this.options.updateOptions(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.fillColor = [0, 0, 0];
        checkBox.value = options.value;
        checkBox.AS = options.AS;
        checkBox.Rect = options.Rect;
        SurveyHelper.renderFlatBorders(this.controller, this.contentRect, this.style);
    }
    async renderReadOnly() {
        if (!!this.style.backgroundColor) {
            this.controller.setFillColor(this.style.backgroundColor);
            const { lines: docLines, point } = SurveyHelper.getDocLinesFromShape(SurveyHelper.createRoundedShape(this.contentRect, this.style));
            this.controller.doc.lines(docLines, ...point, [1, 1], 'F', true);
            this.controller.restoreFillColor();
        }
        SurveyHelper.renderFlatBorders(this.controller, this.contentRect, this.style);
        if (this.options.checked) {
            const checkmarkPoint = SurveyHelper.createPoint(this.contentRect, true, true);
            const textOptions = {
                fontName: this.style.fontName,
                fontSize: this.style.fontSize,
                fontColor: this.style.fontColor
            };
            const checkmarkSize = this.controller.measureText(this.style.checkMark, textOptions);
            checkmarkPoint.xLeft += this.contentRect.width / 2.0 - checkmarkSize.width / 2.0;
            checkmarkPoint.yTop += this.contentRect.height / 2.0 - checkmarkSize.height / 2.0;
            const checkmarkFlat = await SurveyHelper.createTextFlat(checkmarkPoint, this.controller, this.style.checkMark, textOptions);
            await checkmarkFlat.render();
        }
    }
}

class RadioGroupWrap {
    constructor(controller, options) {
        this.controller = controller;
        this.options = options;
    }
    addToPdf(color) {
        this._radioGroup = new this.controller.AcroFormRadioButton();
        const options = {};
        options.fieldName = this.options.fieldName;
        options.readOnly = this.options.readOnly;
        options.color = color;
        this.options.updateOptions(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.options.readOnly;
    }
    get fieldName() {
        return this.options.fieldName;
    }
}
class RadioItemBrick extends PdfBrick {
    constructor(controller, rect, radioGroupWrap, options, style) {
        super(controller, rect);
        this.radioGroupWrap = radioGroupWrap;
        this.options = options;
        this.style = style;
    }
    async renderInteractive() {
        var _a;
        const scaledAcroformRect = SurveyHelper.createAcroformRect(SurveyHelper.createRectInsideBorders(this.contentRect, (_a = this.style.borderWidth) !== null && _a !== void 0 ? _a : 0));
        const { color: fontColor } = SurveyHelper.parseColor(this.style.fontColor);
        if (this.options.index == 0) {
            this.radioGroupWrap.addToPdf(fontColor);
        }
        if (this.style.backgroundColor) {
            this.controller.setFillColor(this.style.backgroundColor);
            this.controller.doc.rect(...scaledAcroformRect, 'F');
            this.controller.restoreFillColor();
        }
        const options = {};
        options.fieldName = this.radioGroupWrap.fieldName + 'index' + this.options.index;
        options.Rect = scaledAcroformRect;
        options.color = fontColor;
        options.multiSelect = true;
        options.style = this.controller.doc.AcroForm.Appearance.RadioButton.Circle;
        options.radioGroup = this.radioGroupWrap.radioGroup;
        this.options.updateOptions && this.options.updateOptions(options);
        let radioButton = this.radioGroupWrap.radioGroup.createOption(options.fieldName);
        if (this.options.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.contentRect, this.style);
        this.radioGroupWrap.radioGroup.setAppearance(options.style);
    }
    async renderReadOnly() {
        if (!!this.style.backgroundColor) {
            this.controller.setFillColor(this.style.backgroundColor);
            const { lines: docLines, point } = SurveyHelper.getDocLinesFromShape(SurveyHelper.createRoundedShape(this.contentRect, this.style));
            this.controller.doc.lines(docLines, ...point, [1, 1], 'F', true);
            this.controller.restoreFillColor();
        }
        SurveyHelper.renderFlatBorders(this.controller, this.contentRect, this.style);
        if (this.options.checked) {
            const textOptions = {
                fontName: this.style.fontName,
                fontSize: this.style.fontSize,
                fontColor: this.style.fontColor,
                lineHeight: this.style.lineHeight
            };
            const radiomarkerPoint = SurveyHelper.createPoint(this.contentRect, true, true);
            const radiomarkerSize = this.controller.measureText(this.style.checkMark, textOptions);
            radiomarkerPoint.xLeft += this.contentRect.width / 2.0 - radiomarkerSize.width / 2.0;
            radiomarkerPoint.yTop += this.contentRect.height / 2.0 - radiomarkerSize.height / 2.0;
            let radiomarkerFlat = await SurveyHelper.createTextFlat(radiomarkerPoint, this.controller, this.style.checkMark, textOptions);
            await radiomarkerFlat.render();
        }
    }
}

class FlatBooleanCheckbox extends FlatQuestion {
    getInputStyle(isReadOnly, isChecked) {
        const style = SurveyHelper.mergeObjects({}, this.style.input, this.style.checkboxInput);
        if (isReadOnly) {
            SurveyHelper.mergeObjects(style, this.style.inputReadOnly, this.style.checkboxInputReadOnly);
            if (isChecked) {
                SurveyHelper.mergeObjects(style, this.style.inputReadOnlyChecked, this.style.checkboxInputReadOnlyChecked);
            }
        }
        return style;
    }
    async generateFlatsContent(point) {
        const compositeFlat = new CompositeBrick();
        const isReadOnly = this.question.isReadOnly;
        const shouldRenderReadOnly = isReadOnly && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress;
        const style = SurveyHelper.getPatchedTextStyle(this.controller, this.getInputStyle(isReadOnly, this.question.booleanValue));
        const itemFlat = new CheckItemBrick(this.controller, SurveyHelper.createRect(point, style.width, style.height), {
            fieldName: this.question.id,
            readOnly: isReadOnly,
            updateOptions: (options) => this.survey.updateCheckItemAcroformOptions(options, this.question),
            shouldRenderReadOnly,
            checked: this.question.booleanValue
        }, style);
        compositeFlat.addBrick(itemFlat);
        if (this.question.isLabelRendered) {
            const textFlat = new CompositeBrick();
            const textPoint = SurveyHelper.clone(point);
            textPoint.xLeft = itemFlat.xRight + this.style.spacing.choiceTextGap;
            const locLabelText = this.question.locTitle;
            if (locLabelText !== null && locLabelText.renderedHtml !== null) {
                textFlat.addBrick(await SurveyHelper.createTextFlat(textPoint, this.controller, locLabelText, this.style.choiceText));
            }
            if (this.question.isRequired) {
                const requiredText = this.question.requiredText;
                const requiredStyle = SurveyHelper.mergeObjects({}, this.style.choiceText, this.style.requiredMark);
                if (SurveyHelper.hasHtml(this.question.locTitle)) {
                    const requiredPoint = SurveyHelper.createPoint(textFlat.unfold()[0], false, false);
                    requiredPoint.xLeft += this.style.spacing.titleRequiredMarkGap;
                    this.controller.pushMargins();
                    this.controller.margins.right = this.controller.paperWidth -
                        this.controller.margins.left - this.controller.measureText(requiredText, requiredStyle).width;
                    textFlat.addBrick(await SurveyHelper.createHTMLFlat(requiredPoint, this.controller, SurveyHelper.createHtmlContainerBlock(requiredText, this.controller, requiredStyle)));
                    this.controller.popMargins();
                }
                else {
                    const requiredPoint = SurveyHelper.createPoint(textFlat.unfold().pop(), false, true);
                    requiredPoint.xLeft += this.style.spacing.titleRequiredMarkGap;
                    textFlat.addBrick(await SurveyHelper.createTextFlat(requiredPoint, this.controller, requiredText, requiredStyle));
                }
            }
            SurveyHelper.alignVerticallyBricks('center', itemFlat, textFlat.unfold()[0]);
            textFlat.updateRect();
            compositeFlat.addBrick(textFlat);
        }
        return [compositeFlat];
    }
}
class FlatBoolean extends FlatQuestion {
    getInputStyle(isReadOnly, isChecked) {
        const style = SurveyHelper.mergeObjects({}, this.style.input, this.style.radioInput);
        if (isReadOnly) {
            SurveyHelper.mergeObjects(style, this.style.inputReadOnly, this.style.radioInputReadOnly);
            if (isChecked) {
                SurveyHelper.mergeObjects(style, this.style.inputReadOnlyChecked, this.style.radioInputReadOnlyChecked);
            }
        }
        return style;
    }
    get radioGroupWrap() {
        if (!this._radioGroupWrap) {
            this._radioGroupWrap = new RadioGroupWrap(this.controller, {
                readOnly: this.question.isReadOnly,
                fieldName: this.question.id,
                updateOptions: (options) => { this.survey.getUpdatedRadioGroupWrapOptions(options, this.question); }
            });
        }
        return this._radioGroupWrap;
    }
    generateFlatItem(point, item, index) {
        const isChecked = this.question.value == item.value;
        const shouldRenderReadOnly = this.radioGroupWrap.readOnly && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress;
        const style = SurveyHelper.getPatchedTextStyle(this.controller, this.getInputStyle(shouldRenderReadOnly, isChecked));
        const itemRect = SurveyHelper.createRect(point, style.width, style.height);
        return new RadioItemBrick(this.controller, itemRect, this.radioGroupWrap, {
            index,
            checked: isChecked,
            shouldRenderReadOnly
        }, style);
    }
    async generateFlatComposite(point, item, index) {
        const compositeFlat = new CompositeBrick();
        const textOptions = this.style.choiceText;
        const itemFlat = this.generateFlatItem(point, item, index);
        compositeFlat.addBrick(itemFlat);
        const textPoint = SurveyHelper.clone(point);
        textPoint.xLeft = itemFlat.xRight + this.style.spacing.choiceTextGap;
        if (item.locText.renderedHtml !== null) {
            const textFlat = await SurveyHelper.createTextFlat(textPoint, this.controller, item.locText, textOptions);
            SurveyHelper.alignVerticallyBricks('center', itemFlat, textFlat.unfold()[0]);
            textFlat.updateRect();
            compositeFlat.addBrick(textFlat);
        }
        return compositeFlat;
    }
    async generateFlatsContent(point) {
        const currPoint = SurveyHelper.clone(point);
        const rowFlat = new CompositeBrick();
        const items = [
            {
                locText: this.question.locLabelFalse,
                value: this.question.valueFalse !== undefined ? this.question.valueFalse : false
            },
            {
                locText: this.question.locLabelTrue,
                value: this.question.valueTrue !== undefined ? this.question.valueTrue : true
            }
        ];
        let index = 0;
        for (let item of items) {
            this.controller.pushMargins();
            SurveyHelper.setColumnMargins(this.controller, 2, index, this.style.spacing.choiceColumnGap);
            currPoint.xLeft = this.controller.margins.left;
            const itemFlat = await this.generateFlatComposite(currPoint, item, index);
            rowFlat.addBrick(itemFlat);
            this.controller.popMargins();
            index++;
        }
        const rowLineFlat = SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(rowFlat), this.controller);
        return [rowFlat, rowLineFlat];
    }
}
FlatRepository.getInstance().register('boolean', FlatBoolean);
FlatRepository.getInstance().register('boolean-checkbox', FlatBooleanCheckbox);

class FlatSelectBase extends FlatQuestion {
    constructor() {
        super(...arguments);
        this.generateVerticallyItems = async (point, itemValues, customGap) => {
            const currPoint = SurveyHelper.clone(point);
            const flats = [];
            for (let i = 0; i < itemValues.length; i++) {
                const itemFlat = await this.generateFlatComposite(currPoint, itemValues[i], i);
                currPoint.yTop = itemFlat.yBot + (customGap || this.style.spacing.choiceGap);
                flats.push(itemFlat);
            }
            return flats;
        };
    }
    async generateItemComment(point, item) {
        const shouldRenderReadOnly = SurveyHelper.shouldRenderReadOnly(this.question, this.controller, this.question.isReadOnly);
        const style = SurveyHelper.getPatchedTextStyle(this.controller, SurveyHelper.mergeObjects({}, this.style.comment, shouldRenderReadOnly ? this.style.commentReadOnly : undefined));
        const commentModel = this.question.getCommentTextAreaModel(item);
        return await SurveyHelper.createCommentFlat(point, this.controller, {
            shouldRenderReadOnly,
            fieldName: commentModel.id,
            rows: this.controller.otherRowsCount,
            value: commentModel.getTextValue(),
            shouldRenderBorders: settings.readOnlyCommentRenderMode === 'textarea',
            isReadOnly: this.question.isReadOnly,
            isMultiline: true,
        }, style);
    }
    getItemStyle(item) {
        const isChecked = this.question.isItemSelected(item);
        const shouldRenderReadOnly = this.question.isReadOnly || !item.isEnabled && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress;
        const style = { input: SurveyHelper.mergeObjects({}, this.style.input, shouldRenderReadOnly ? this.style.inputReadOnly : {}, shouldRenderReadOnly && isChecked ? this.style.inputReadOnlyChecked : {}), choiceText: { ...this.style.choiceText } };
        return this.survey.getItemStyle(this.question, item, style);
    }
    async generateFlatComposite(point, item, index) {
        const compositeFlat = new CompositeBrick();
        const style = this.getItemStyle(item);
        const itemFlat = this.generateFlatItem(point, item, index, style.input);
        compositeFlat.addBrick(itemFlat);
        const textPoint = SurveyHelper.clone(point);
        textPoint.xLeft = itemFlat.xRight + this.style.spacing.choiceTextGap;
        if (item.locText.renderedHtml !== null) {
            const textFlat = await SurveyHelper.createTextFlat(textPoint, this.controller, item.locText, { ...style.choiceText });
            SurveyHelper.alignVerticallyBricks('center', itemFlat, textFlat.unfold()[0]);
            textFlat.updateRect();
            compositeFlat.addBrick(textFlat);
        }
        if (item.isCommentShowing) {
            const otherPoint = SurveyHelper.createPoint(compositeFlat, true, false);
            otherPoint.yTop += this.style.spacing.choiceGap;
            compositeFlat.addBrick(await this.generateItemComment(otherPoint, item));
        }
        return compositeFlat;
    }
    getVisibleChoices() {
        return this.question.visibleChoices;
    }
    async generateFlatsContent(point) {
        const colCount = this.question.colCount;
        const visibleChoices = this.getVisibleChoices();
        let currentColCount = colCount;
        if (colCount == 0) {
            currentColCount = Math.floor(SurveyHelper.getPageAvailableWidth(this.controller)
                / this.style.columnMinWidth) || 1;
            if (visibleChoices.length < currentColCount) {
                currentColCount = visibleChoices.length;
            }
        }
        else if (colCount > 1) {
            currentColCount = (SurveyHelper.getColumnWidth(this.controller, colCount, this.style.spacing.choiceColumnGap) <
                this.style.columnMinWidth) ? 1 : colCount;
            if (currentColCount == colCount) {
                return await this.generateColumns(point);
            }
        }
        return (currentColCount == 1) ? await this.generateVerticallyItems(point, visibleChoices) :
            await this.generateHorisontallyItems(point, currentColCount);
    }
    async generateRows(point, rows) {
        var _a;
        const visibleChoices = this.getVisibleChoices();
        const currPoint = SurveyHelper.clone(point);
        const colCount = ((_a = rows[0]) !== null && _a !== void 0 ? _a : []).length;
        const flats = [];
        for (let row of rows) {
            const rowFlat = new CompositeBrick();
            for (let colIndex = 0; colIndex < row.length; colIndex++) {
                const item = row[colIndex];
                this.controller.pushMargins();
                SurveyHelper.setColumnMargins(this.controller, colCount, colIndex, this.style.spacing.choiceColumnGap);
                currPoint.xLeft = this.controller.margins.left;
                const itemFlat = await this.generateFlatComposite(currPoint, item, visibleChoices.indexOf(item));
                rowFlat.addBrick(itemFlat);
                this.controller.popMargins();
            }
            const rowLineFlat = SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(rowFlat), this.controller);
            currPoint.yTop = rowLineFlat.yBot + this.style.spacing.choiceGap;
            flats.push(rowFlat, rowLineFlat);
        }
        return flats;
    }
    async generateColumns(point) {
        const columns = this.question.columns;
        const rowsCount = columns.reduce((max, column) => Math.max(max, column.length), 0);
        const rows = [];
        for (let i = 0; i < rowsCount; i++) {
            const row = [];
            for (let column of columns) {
                if (column[i]) {
                    row.push(column[i]);
                }
            }
            rows.push(row);
        }
        return await this.generateRows(point, rows);
    }
    async generateHorisontallyItems(point, colCount) {
        const rows = [];
        const visibleChoices = this.getVisibleChoices();
        visibleChoices.forEach((item, index) => {
            const rowIndex = Math.floor(index / colCount);
            const colIndex = index % colCount;
            if (!rows[rowIndex])
                rows[rowIndex] = [];
            rows[rowIndex][colIndex] = item;
        });
        return await this.generateRows(point, rows);
    }
}

class FlatCheckbox extends FlatSelectBase {
    generateFlatItem(point, item, index, style) {
        const rect = SurveyHelper.createRect(point, style.width, style.height);
        const isReadOnly = this.question.isReadOnly || !item.isEnabled;
        const shouldRenderReadOnly = isReadOnly && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress;
        return new CheckItemBrick(this.controller, rect, {
            shouldRenderReadOnly,
            readOnly: isReadOnly,
            checked: this.question.isItemSelected(item),
            fieldName: this.question.id + 'index' + index,
            updateOptions: (options) => {
                this.survey.updateCheckItemAcroformOptions(options, this.question, { item });
            }
        }, SurveyHelper.getPatchedTextStyle(this.controller, { ...style }));
    }
    async generateFlats(point) {
        const oldMaxSelectedChoices = this.question.maxSelectedChoices;
        this.question.maxSelectedChoices = 0;
        const flats = await super.generateFlats(point);
        this.question.maxSelectedChoices = oldMaxSelectedChoices;
        return flats;
    }
}
class FlatTagbox extends FlatCheckbox {
    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 {
    async generateFlatsContent(point) {
        const flat = FlatRepository.getInstance().create(this.survey, this.question, this.controller, this.survey.getElementStyle(this.question), this.question.getType());
        return flat.generateFlatsContent(point);
    }
}
FlatRepository.getInstance().register('custom_model', FlatCustomModel);

class FlatComment extends FlatQuestion {
    async generateFlatsContent(point) {
        const shouldRenderReadOnly = SurveyHelper.shouldRenderReadOnly(this.question, this.controller, this.question.isReadOnly);
        const style = SurveyHelper.getPatchedTextStyle(this.controller, SurveyHelper.mergeObjects({}, this.style.input, shouldRenderReadOnly ? this.style.inputReadOnly : undefined));
        return [await SurveyHelper.createCommentFlat(point, this.controller, {
                shouldRenderReadOnly,
                rows: this.question.rows,
                isReadOnly: this.question.isReadOnly,
                isMultiline: true,
                fieldName: this.question.id,
                placeholder: SurveyHelper.getLocString(this.question.locPlaceHolder),
                shouldRenderBorders: settings.readOnlyCommentRenderMode === 'textarea',
                value: this.question.value
            }, SurveyHelper.getPatchedTextStyle(this.controller, style))];
    }
}
FlatRepository.getInstance().register('comment', FlatComment);

class DropdownBrick extends PdfBrick {
    constructor(controller, rect, options, style) {
        super(controller, rect);
        this.controller = controller;
        this.options = options;
        this.style = style;
    }
    async renderInteractive() {
        var _a;
        const { color: fontColor } = SurveyHelper.parseColor(this.style.fontColor);
        const { color: backgroundColor } = SurveyHelper.parseColor(this.style.backgroundColor);
        const comboBox = new this.controller.AcroFormComboBox();
        comboBox.backgroundColor = backgroundColor;
        comboBox.fieldName = this.options.fieldName;
        comboBox.Rect = SurveyHelper.createAcroformRect(SurveyHelper.createRectInsideBorders(this.contentRect, (_a = this.style.borderWidth) !== null && _a !== void 0 ? _a : 0));
        comboBox.edit = false;
        comboBox.color = fontColor;
        const options = [];
        if (this.options.showOptionsCaption) {
            options.push(this.getCorrectedText(this.options.optionsCaption));
        }
        this.options.items.forEach((item) => {
            options.push(this.getCorrectedText(item));
        });
        comboBox.setOptions(options);
        comboBox.fontName = this.style.fontName;
        comboBox.fontSize = this.style.fontSize;
        comboBox.readOnly = this.options.isReadOnly;
        comboBox.isUnicode = SurveyHelper.isCustomFont(this.controller, comboBox.fontName);
        comboBox.V = this.getCorrectedText(this.options.value);
        this.controller.doc.addField(comboBox);
        SurveyHelper.renderFlatBorders(this.controller, this.contentRect, this.style);
    }
}

class FlatDropdown extends FlatQuestion {
    async generateItemComment(point) {
        const commentModel = this.question.getCommentTextAreaModel(this.question.selectedItem);
        const shouldRenderReadOnly = SurveyHelper.shouldRenderReadOnly(this.question, this.controller, this.question.isReadOnly);
        const style = SurveyHelper.getPatchedTextStyle(this.controller, SurveyHelper.mergeObjects({}, this.style.comment, shouldRenderReadOnly ? this.style.commentReadOnly : undefined));
        return await SurveyHelper.createCommentFlat(point, this.controller, {
            shouldRenderReadOnly,
            fieldName: commentModel.id,
            rows: this.controller.otherRowsCount,
            value: commentModel.getTextValue(),
            shouldRenderBorders: settings.readOnlyCommentRenderMode === 'textarea',
            isReadOnly: this.question.isReadOnly,
            isMultiline: true,
        }, SurveyHelper.getPatchedTextStyle(this.controller, style));
    }
    async generateFlatsContent(point) {
        const shouldRenderReadOnly = SurveyHelper.shouldRenderReadOnly(this.question, this.controller, this.question.isReadOnly);
        const style = SurveyHelper.getPatchedTextStyle(this.controller, SurveyHelper.mergeObjects({}, this.style.input, shouldRenderReadOnly ? this.style.inputReadOnly : undefined));
        const valueBrick = !shouldRenderReadOnly ? new DropdownBrick(this.controller, SurveyHelper.createTextFieldRect(point, this.controller, 1, style.lineHeight), {
            fieldName: this.question.id,
            value: this.question.readOnlyText,
            isReadOnly: this.question.isReadOnly,
            optionsCaption: this.question.optionsCaption,
            showOptionsCaption: this.question.showOptionsCaption,
            items: this.question.visibleChoices.map(item => SurveyHelper.getLocString(item.locText))
        }, style) : await SurveyHelper.createCommentFlat(point, this.controller, {
            fieldName: this.question.id,
            shouldRenderReadOnly: shouldRenderReadOnly,
            shouldRenderBorders: settings.readOnlyTextRenderMode === 'input',
            value: this.question.readOnlyText || '',
            isReadOnly: this.question.isReadOnly,
            placeholder: SurveyHelper.getLocString(this.question.locPlaceholder)
        }, style);
        const compositeFlat = new CompositeBrick(valueBrick);
        if (this.question.isShowingChoiceComment) {
            const otherPoint = SurveyHelper.createPoint(compositeFlat);
            otherPoint.yTop += this.style.spacing.contentCommentGap;
            compositeFlat.addBrick(await this.generateItemComment(otherPoint));
        }
        return [compositeFlat];
    }
}
FlatRepository.getInstance().register('dropdown', FlatDropdown);

class FlatExpression extends FlatQuestion {
    async generateFlatsContent(point) {
        return [await SurveyHelper.createCommentFlat(point, this.controller, {
                value: this.question.displayValue,
                isReadOnly: true,
                shouldRenderReadOnly: SurveyHelper.shouldRenderReadOnly(this.question, this.controller, true),
                fieldName: this.question.id,
                shouldRenderBorders: settings.readOnlyTextRenderMode === 'input',
            }, SurveyHelper.getPatchedTextStyle(this.controller, this.style.input))];
    }
}
FlatRepository.getInstance().register('expression', FlatExpression);

class FlatFile extends FlatQuestion {
    async generateFlatItem(point, item) {
        const compositeFlat = new CompositeBrick(await SurveyHelper.createLinkFlat(point, this.controller, {
            text: item.name === undefined ? 'image' : item.name,
            link: typeof item.content == 'string' ? item.content : '',
            readOnlyShowLink: SurveyHelper.getReadonlyRenderAs(this.question, this.controller) === 'text',
            shouldRenderReadOnly: SurveyHelper.shouldRenderReadOnly(this.question, this.controller),
        }, { ...this.style.fileName }));
        if (this.question.canPreviewImage(item)) {
            const imagePoint = SurveyHelper.createPoint(compositeFlat);
            imagePoint.yTop += this.style.spacing.imageFileNameGap;
            compositeFlat.addBrick(await SurveyHelper.createImageFlat(imagePoint, this.question, this.controller, { link: item.content, width: item.imageSize.width, height: item.imageSize.height, objectFit: this.style.defaultImageFit }));
        }
        return compositeFlat;
    }
    addLine(rowsFlats, currPoint, index, previewValue) {
        if (index !== previewValue.length - 1) {
            rowsFlats[rowsFlats.length - 1].addBrick(SurveyHelper.createRowlineFlat(currPoint, this.controller));
            currPoint.yTop += SurveyHelper.EPSILON;
            rowsFlats.push(new CompositeBrick());
        }
    }
    async getImagePreviewContentWidth(item) {
        return Math.max(item.imageSize.width, this.style.fileItemMinWidth);
    }
    async generateFlatsContent(point) {
        const previewValue = this.question.showPreview ? this.question.previewValue : this.question.value;
        if (!previewValue || previewValue.length === 0) {
            return [await SurveyHelper.createTextFlat(point, this.controller, this.question.noFileChosenCaption)];
        }
        const rowsFlats = [new CompositeBrick()];
        const currPoint = SurveyHelper.clone(point);
        let yBot = currPoint.yTop;
        for (let i = 0; i < previewValue.length; i++) {
            let item = { ...previewValue[i] };
            const canPreviewImage = this.question.canPreviewImage(item);
            if (canPreviewImage) {
                item.imageSize = await SurveyHelper.getCorrectedImageSize(this.controller, { imageWidth: this.question.imageWidth, imageHeight: this.question.imageHeight, imageLink: previewValue[i].content, defaultImageWidth: 200, defaultImageHeight: 150 });
            }
            const availableWidth = this.controller.paperWidth -
                this.controller.margins.right - currPoint.xLeft;
            if (canPreviewImage) {
                const compositeWidth = await this.getImagePreviewContentWidth(item);
                if (availableWidth < compositeWidth) {
                    currPoint.xLeft = point.xLeft;
                    currPoint.yTop = yBot + this.style.spacing.fileItemGap;
                    this.addLine(rowsFlats, currPoint, i, previewValue);
                }
                this.controller.pushMargins(currPoint.xLeft, this.controller.paperWidth - currPoint.xLeft - compositeWidth);
                const itemFlat = await 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.style.spacing.fileItemGap;
                    this.addLine(rowsFlats, currPoint, i, previewValue);
                }
                const itemFlat = await 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.style.spacing.fileItemColumnGap;
        }
        return rowsFlats;
    }
}
FlatRepository.getInstance().register('file', FlatFile);

class FlatHTML extends FlatQuestion {
    chooseRender(html) {
        if (/<[^>]*style[^<]*>/.test(html) ||
            /<[^>]*table[^<]*>/.test(html) ||
            /&\w+;/.test(html)) {
            return 'image';
        }
        return 'standard';
    }
    get correctHtmlRules() {
        const result = [
            { searchRegExp: /(<\/?br\s*?\/?\s*?>\s*){2,}/g, replaceString: '<br>' }
        ];
        if (this.controller.fontName == 'helvetica') {
            result.push({ searchRegExp: /’/g, replaceString: '\'' });
            result.push({ searchRegExp: /—/g, replaceString: '-' });
        }
        return result;
    }
    correctHtml(html) {
        this.correctHtmlRules.forEach((rule) => {
            html = html.replace(rule.searchRegExp, rule.replaceString);
        });
        return html;
    }
    async generateFlatsContent(point) {
        let renderAs = this.question.renderAs;
        if (!SurveyHelper.hasDocument) {
            return [new EmptyBrick(this.controller, 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, this.style.text);
        if (renderAs === 'image') {
            const width = SurveyHelper.getPageAvailableWidth(this.controller);
            const { url, aspect } = await SurveyHelper.htmlToImage(html, width, this.controller);
            const height = width / aspect;
            return [await SurveyHelper.createImageFlat(point, this.question, this.controller, { link: url, width, height })];
        }
        return [SurveyHelper.splitHtmlRect(this.controller, await SurveyHelper.createHTMLFlat(point, this.controller, this.correctHtml(html), this.style.text))];
    }
}
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 {
    async getCorrectImageSize() {
        return await SurveyHelper.getCorrectedImageSize(this.controller, { imageWidth: this.question.imageWidth, imageHeight: this.question.imageHeight, imageLink: this.question.imageLink });
    }
    async generateFlatsContent(point) {
        const imageSize = await this.getCorrectImageSize();
        return [await 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 {
    get radioGroupWrap() {
        if (!this._radioGroupWrap) {
            this._radioGroupWrap = new RadioGroupWrap(this.controller, {
                readOnly: this.question.isReadOnly,
                fieldName: this.question.id,
                updateOptions: (options) => { this.survey.getUpdatedRadioGroupWrapOptions(options, this.question); }
            });
        }
        return this._radioGroupWrap;
    }
    getInputStyle(isReadOnly, isChecked, isMultiSelect) {
        const inputType = isMultiSelect ? 'checkbox' : 'radio';
        const style = SurveyHelper.mergeObjects({}, this.style.input, this.style[`${inputType}Input`]);
        if (isReadOnly) {
            SurveyHelper.mergeObjects(style, this.style.inputReadOnly, this.style[`${inputType}InputReadOnly`]);
            if (isChecked) {
                SurveyHelper.mergeObjects(style, this.style.inputReadOnlyChecked, this.style[`${inputType}InputReadOnlyChecked`]);
            }
        }
        return style;
    }
    async generateFlatItem(point, item, index) {
        const pageAvailableWidth = SurveyHelper.getPageAvailableWidth(this.controller);
        const isReadOnly = this.question.isReadOnly || !item.isEnabled;
        const isChecked = this.question.isItemSelected(item);
        const shouldRenderReadOnly = isReadOnly && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress;
        const itemStyle = SurveyHelper.mergeObjects({}, this.getInputStyle(isReadOnly, isChecked, this.question.multiSelect));
        const imageFlat = await SurveyHelper.createImageFlat(point, this.question, this.controller, { link: item.imageLink, width: pageAvailableWidth, height: pageAvailableWidth / this.style.imageRatio });
        const compositeFlat = new CompositeBrick(imageFlat);
        let buttonPoint = SurveyHelper.createPoint(compositeFlat);
        if (this.question.showLabel) {
            let labelFlat = await SurveyHelper.createTextFlat(buttonPoint, this.controller, item.text || item.value, {
                ...this.style.choiceText
            });
            compositeFlat.addBrick(labelFlat);
            buttonPoint = SurveyHelper.createPoint(labelFlat);
        }
        buttonPoint.yTop += this.style.spacing.imageInputGap;
        const height = itemStyle.height;
        const buttonRect = SurveyHelper.createRect(buttonPoint, pageAvailableWidth, height);
        if (this.question.multiSelect) {
            compositeFlat.addBrick(new CheckItemBrick(this.controller, buttonRect, {
                fieldName: this.question.id + 'index' + index,
                readOnly: isReadOnly,
                checked: isChecked,
                shouldRenderReadOnly: shouldRenderReadOnly,
                updateOptions: (options) => this.survey.updateCheckItemAcroformOptions(options, this.question, { item }),
            }, itemStyle));
        }
        else {
            compositeFlat.addBrick(new RadioItemBrick(this.controller, buttonRect, this.radioGroupWrap, {
                index,
                checked: isChecked,
                shouldRenderReadOnly: shouldRenderReadOnly,
                updateOptions: options => this.survey.updateRadioItemAcroformOptions(options, this.question, { item }),
            }, itemStyle));
        }
        return compositeFlat;
    }
    getColumnsInfo() {
        const { imageMinWidth, imageMaxWidth } = this.style;
        const { choiceColumnGap: gapBetweenColumns } = this.style.spacing;
        const availableWidth = SurveyHelper.getPageAvailableWidth(this.controller);
        let columnsCount = this.question.colCount == 0 ? this.question.visibleChoices.length : this.question.colCount;
        let columnWidth;
        const getColumnWidth = () => {
            return Math.max(Math.min((availableWidth - gapBetweenColumns * (columnsCount - 1)) / columnsCount, imageMaxWidth), 1);
        };
        if (imageMinWidth * columnsCount + gapBetweenColumns * (columnsCount - 1) < availableWidth) {
            columnWidth = getColumnWidth();
        }
        else {
            columnsCount = Math.max(Math.ceil((availableWidth + gapBetweenColumns) / (imageMinWidth + gapBetweenColumns)), 1);
            columnWidth = getColumnWidth();
        }
        return { columnsCount, columnWidth };
    }
    async generateFlatsContent(point) {
        const rowsFlats = [new CompositeBrick()];
        const { columnsCount: columnsCount, columnWidth: columnWidth } = this.getColumnsInfo();
        const rows = Math.ceil(this.question.visibleChoices.length / columnsCount);
        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 < columnsCount; j++) {
                const index = i * columnsCount + j;
                if (index == this.question.visibleChoices.length)
                    break;
                this.controller.margins.left = currMarginLeft;
                this.controller.margins.right = this.controller.paperWidth -
                    currMarginLeft - columnWidth;
                currPoint.xLeft = this.controller.margins.left;
                const itemFlat = await this.generateFlatItem(currPoint, this.question.visibleChoices[index], index);
                rowsFlats[rowsFlats.length - 1].addBrick(itemFlat);
                currMarginLeft = this.controller.paperWidth -
                    this.controller.margins.right + this.style.spacing.choiceColumnGap;
                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 += this.style.spacing.choiceGap;
                rowsFlats.push(new CompositeBrick());
            }
        }
        return rowsFlats;
    }
}
FlatRepository.getInstance().register('imagepicker', FlatImagePicker);

class FlatPanelDynamic extends FlatQuestion {
    async generateFlatsContent(point) {
        const flats = [];
        const currPoint = SurveyHelper.clone(point);
        for (const panel of this.question.panels) {
            const panelFlats = await SurveyHelper.generatePanelFlats(this.survey, this.controller, panel, currPoint);
            if (panelFlats.length !== 0) {
                currPoint.yTop = SurveyHelper.mergeRects(...panelFlats).yBot;
                currPoint.yTop += this.style.spacing.panelGap;
                flats.push(...panelFlats);
            }
        }
        return flats;
    }
}
FlatRepository.getInstance().register('paneldynamic', FlatPanelDynamic);

class FlatRadiogroup extends FlatSelectBase {
    get radioGroupWrap() {
        if (!this._radioGroupWrap) {
            this._radioGroupWrap = new RadioGroupWrap(this.controller, {
                readOnly: this.question.isReadOnly,
                fieldName: this.question.id,
                updateOptions: (options) => {
                    this.survey.getUpdatedRadioGroupWrapOptions(options, this.question);
                }
            });
        }
        return this._radioGroupWrap;
    }
    generateFlatItem(point, item, index, style) {
        const rect = SurveyHelper.createRect(point, style.width, style.height);
        return new RadioItemBrick(this.controller, rect, this.radioGroupWrap, {
            index,
            checked: this.question.isItemSelected(item),
            shouldRenderReadOnly: this.radioGroupWrap.readOnly && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress,
            updateOptions: options => this.survey.updateRadioItemAcroformOptions(options, this.question, { item }),
        }, SurveyHelper.getPatchedTextStyle(this.controller, { ...style }));
    }
}
FlatRepository.getInstance().register('radiogroup', FlatRadiogroup);
FlatRepository.getInstance().register('buttongroup', FlatRadiogroup);

class RankingItemBrick extends PdfBrick {
    constructor(controller, rect, options, style) {
        super(controller, rect);
        this.options = options;
        this.style = style;
    }
    async renderInteractive() {
        var _a;
        const scaledRect = SurveyHelper.createRectInsideBorders(this.contentRect, (_a = this.style.borderWidth) !== null && _a !== void 0 ? _a : 0);
        const scaledRectWidth = scaledRect.xRight - scaledRect.xLeft;
        const scaledRectHeight = scaledRect.yBot - scaledRect.yTop;
        const scaledAcroformRect = SurveyHelper.createAcroformRect(scaledRect);
        if (this.style.backgroundColor) {
            this.controller.setFillColor(this.style.backgroundColor);
            this.controller.doc.rect(...scaledAcroformRect, 'F');
            this.controller.restoreFillColor();
        }
        SurveyHelper.renderFlatBorders(this.controller, this.contentRect, this.style);
        if (this.options.mark) {
            const markPoint = SurveyHelper.createPoint(scaledRect, true, true);
            const textStyle = { ...this.style };
            const markSize = this.controller.measureText(this.options.mark, textStyle);
            markPoint.xLeft += scaledRectWidth / 2.0 - markSize.width / 2.0;
            markPoint.yTop += scaledRectHeight / 2.0 - markSize.height / 2.0;
            const markFlat = await SurveyHelper.createTextFlat(markPoint, this.controller, this.options.mark, textStyle);
            await markFlat.render();
        }
        else {
            this.controller.setFillColor(this.style.fontColor);
            let rect = SurveyHelper.createRect(scaledRect, this.style.fontSize, this.style.fontSize / 7);
            rect = SurveyHelper.moveRect(rect, rect.xLeft + scaledRectWidth / 2.0 - (rect.xRight - rect.xLeft) / 2.0, rect.yTop + scaledRectHeight / 2.0 - (rect.yBot - rect.yTop) / 2.0);
            this.controller.doc.rect(...SurveyHelper.createAcroformRect(rect), 'F');
            this.controller.restoreFillColor();
        }
    }
}

class ColoredBrick extends PdfBrick {
    constructor(controller, rect, options) {
        super(controller, rect);
        this.options = options;
    }
    async renderInteractive() {
        var _a, _b;
        this.controller.setFillColor(this.options.color || 'black');
        const { xLeft, yTop, width, height } = this.contentRect;
        this.controller.doc.rect(xLeft, yTop, (_a = this.options.renderWidth) !== null && _a !== void 0 ? _a : width, (_b = this.options.renderHeight) !== null && _b !== void 0 ? _b : height, 'F');
        this.controller.restoreFillColor();
    }
}

class FlatRanking extends FlatQuestion {
    async generateFlatComposite(point, item, index, unrankedItem = false) {
        const itemFlat = new RankingItemBrick(this.controller, SurveyHelper.createRect(point, this.style.input.width, this.style.input.height), {
            mark: unrankedItem ? '-' : this.question.getNumberByIndex(index) || ''
        }, SurveyHelper.getPatchedTextStyle(this.controller, this.style.input));
        const textPoint = SurveyHelper.clone(point);
        textPoint.xLeft = itemFlat.xRight + this.style.spacing.choiceTextGap;
        const textFlat = await SurveyHelper.createTextFlat(textPoint, this.controller, item.locText, this.style.choiceText);
        SurveyHelper.alignVerticallyBricks('center', itemFlat, textFlat.unfold()[0]);
        return new CompositeBrick(itemFlat, textFlat);
    }
    async generateChoicesColumn(point, choices, unrankedChoices = false) {
        const currPoint = SurveyHelper.clone(point);
        const flats = [];
        for (let i = 0; i < choices.length; i++) {
            const itemFlat = await this.generateFlatComposite(currPoint, choices[i], i, unrankedChoices);
            currPoint.yTop = itemFlat.yBot + this.style.spacing.choiceGap;
            flats.push(itemFlat);
        }
        return flats;
    }
    async generateSelectToRankItemsVertically(point) {
        const currPoint = SurveyHelper.clone(point);
        const flats = [];
        if (this.question.rankingChoices.length !== 0) {
            flats.push(...await this.generateChoicesColumn(currPoint, this.question.rankingChoices));
            currPoint.yTop = flats[flats.length - 1].yBot + 2 * this.style.spacing.choiceGap;
        }
        const separatorRect = SurveyHelper.createRect({
            xLeft: currPoint.xLeft,
            yTop: currPoint.yTop - this.style.spacing.choiceGap - 0.5,
        }, this.controller.paperWidth - this.controller.margins.right - currPoint.xLeft, this.style.selectToRankAreaSeparator.width);
        flats.push(new ColoredBrick(this.controller, separatorRect, { color: this.style.selectToRankAreaSeparator.color }));
        flats.push(...await this.generateChoicesColumn(currPoint, this.question.unRankingChoices, true));
        return flats;
    }
    async generateSelectToRankItemsHorizontally(point) {
        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, this.style.spacing.choiceColumnGap);
                    currPoint.xLeft = this.controller.margins.left;
                    const itemFlat = await 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);
            const gapBetweenRows = this.style.spacing.choiceGap;
            row.addBrick(new ColoredBrick(this.controller, separatorRect, {
                color: this.style.selectToRankAreaSeparator.color,
                renderWidth: this.style.selectToRankAreaSeparator.width,
                renderHeight: rowLineFlat.yBot - currPoint.yTop + (i !== rowsCount - 1 ? gapBetweenRows : 0)
            }));
            currPoint.yTop = rowLineFlat.yBot + gapBetweenRows;
            row = new CompositeBrick();
        }
        return flats;
    }
    async generateFlatsContent(point) {
        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 FlatQuestion {
    get radioGroupWrap() {
        if (!this._radioGroupWrap) {
            this._radioGroupWrap = new RadioGroupWrap(this.controller, {
                readOnly: this.question.isReadOnly,
                fieldName: this.question.id,
                updateOptions: (options) => { this.survey.getUpdatedRadioGroupWrapOptions(options, this.question); }
            });
        }
        return this._radioGroupWrap;
    }
    getItemWidth(title) {
        return Math.min(Math.max(this.controller.measureText(title, { ...this.style.choiceText }).width, this.style.choiceMinWidth), SurveyHelper.getPageAvailableWidth(this.controller));
    }
    getItemText(index, locText) {
        const ratingItemLocText = new LocalizableString(locText.owner, locText.useMarkdown);
        ratingItemLocText.text = SurveyHelper.getLocString(locText);
        if (index === 0 && this.question.minRateDescription) {
            ratingItemLocText.text = this.question.locMinRateDescription.text + ' ' + SurveyHelper.getLocString(locText);
        }
        else if (index === this.question.visibleRateValues.length - 1 && this.question.maxRateDescription) {
            ratingItemLocText.text = SurveyHelper.getLocString(locText) + ' ' + this.question.locMaxRateDescription.text;
        }
        return ratingItemLocText;
    }
    getInputStyle(isReadOnly, isChecked) {
        const style = { ...this.style.input };
        if (isReadOnly) {
            SurveyHelper.mergeObjects(style, this.style.inputReadOnly);
            if (isChecked) {
                SurveyHelper.mergeObjects(style, this.style.inputReadOnlyChecked);
            }
        }
        return style;
    }
    generateFlatItem(rect, item, index) {
        const isChecked = this.question.isItemSelected(item);
        const shouldRenderReadOnly = this.radioGroupWrap.readOnly && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress;
        return new RadioItemBrick(this.controller, rect, this.radioGroupWrap, {
            index,
            checked: isChecked,
            shouldRenderReadOnly: shouldRenderReadOnly,
            updateOptions: options => this.survey.updateRadioItemAcroformOptions(options, this.question, { item }),
        }, SurveyHelper.getPatchedTextStyle(this.controller, this.getInputStyle(shouldRenderReadOnly, isChecked)));
    }
    async generateItemComposite(point, itemInfo) {
        const currPoint = SurveyHelper.clone(point);
        const compositeFlat = new CompositeBrick();
        const textBrick = await SurveyHelper.
            createTextFlat(point, this.controller, itemInfo.locText, { ...this.style.choiceText });
        compositeFlat.addBrick(textBrick);
        currPoint.yTop = textBrick.yBot + this.style.spacing.choiceTextGap;
        compositeFlat.addBrick(this.generateFlatItem(SurveyHelper.createRect(currPoint, itemInfo.width, this.style.input.height), itemInfo.item, itemInfo.index));
        compositeFlat.translateX((xLeft, xRight) => {
            const shift = (compositeFlat.width - (xRight - xLeft)) / 2;
            return { xLeft: xLeft + shift, xRight: xRight + shift };
        });
        return compositeFlat;
    }
    getRows() {
        const res = [];
        let currentRowsIndex = 0;
        let currentColumnIndex = 0;
        const availableWidth = SurveyHelper.getPageAvailableWidth(this.controller);
        let leftWidth = availableWidth;
        this.question.visibleRateValues.forEach((item, index) => {
            const locText = this.getItemText(index, item.locText);
            const width = this.getItemWidth(locText);
            const itemInfo = { index, item, locText, width };
            const widthGap = width + (currentColumnIndex == 0 ? 0 : this.style.spacing.choiceColumnGap);
            if (currentColumnIndex !== 0 && widthGap > leftWidth) {
                currentRowsIndex++;
                leftWidth = availableWidth - width;
            }
            else {
                leftWidth -= widthGap;
            }
            currentColumnIndex++;
            if (res.length <= currentRowsIndex) {
                res.push([]);
            }
            res[currentRowsIndex].push(itemInfo);
        });
        return res;
    }
    async generateFlatsContent(point) {
        const currPoint = SurveyHelper.clone(point);
        const rowsFlats = [];
        const rows = this.getRows();
        for (const row of rows) {
            const rowFlat = new CompositeBrick();
            for (const itemInfo of row) {
                this.controller.pushMargins();
                this.controller.margins.left = currPoint.xLeft;
                this.controller.margins.right = SurveyHelper.getPageAvailableWidth(this.controller) - itemInfo.width - currPoint.xLeft;
                rowFlat.addBrick(await this.generateItemComposite(currPoint, itemInfo));
                this.controller.popMargins();
                currPoint.xLeft = rowFlat.xRight + this.style.spacing.choiceColumnGap;
            }
            rowFlat.addBrick(SurveyHelper.createRowlineFlat(currPoint, this.controller, rowFlat.width));
            if (row !== rows[rows.length - 1]) {
                currPoint.yTop = rowFlat.yBot + this.style.spacing.choiceGap;
                currPoint.xLeft = rowFlat.xLeft;
            }
            rowsFlats.push(rowFlat);
        }
        return rowsFlats;
    }
}
FlatRepository.getInstance().register('rating', FlatRating);

class FlatSlider extends FlatQuestion {
    async generateFlatsContent(point) {
        let currentPoint = SurveyHelper.clone(point);
        if (this.question.sliderType === 'single') {
            const options = this.getOptionsByValue(this.question.value);
            const inputBrick = await this.generateInputBrick(currentPoint, options);
            return [inputBrick];
        }
        if (this.question.sliderType === 'range') {
            const compositeBrick = new CompositeBrick();
            const bricks = [];
            for (let i = 0; i < this.question.renderedValue.length; i++) {
                const valueItem = this.question.renderedValue[i];
                const options = this.getOptionsByValue(valueItem.toString());
                const currentPoint = SurveyHelper.clone(point);
                this.controller.pushMargins();
                SurveyHelper.setColumnMargins(this.controller, 2, i, this.style.spacing.inputRangeGap);
                currentPoint.xLeft = this.controller.margins.left;
                if (i > 0) {
                    const separatorPoint = SurveyHelper.clone(currentPoint);
                    separatorPoint.xLeft -= this.style.spacing.inputRangeGap - (this.style.spacing.inputRangeGap - this.style.rangeSeparator.width) / 2;
                    bricks.push(new EmptyBrick(this.controller, { ...separatorPoint, xRight: separatorPoint.xLeft + this.style.rangeSeparator.width, yBot: separatorPoint.yTop + this.style.rangeSeparator.height }, this.style.rangeSeparator));
                }
                const inputBrick = await this.generateInputBrick(currentPoint, options);
                this.controller.popMargins();
                bricks.push(inputBrick);
            }
            const mergedRect = SurveyHelper.mergeRects(...bricks);
            bricks.forEach(brick => brick.translateY((yTop, yBot) => {
                const shift = (mergedRect.yBot - mergedRect.yTop - yBot + yTop) / 2;
                return {
                    yTop: yTop + shift,
                    yBot: yBot + shift
                };
            }));
            compositeBrick.addBrick(...bricks);
            return [compositeBrick];
        }
    }
    getOptionsByValue(value) {
        const { id, isReadOnly } = this.question;
        return {
            fieldName: id,
            inputType: 'number',
            value,
            isReadOnly,
            shouldRenderReadOnly: SurveyHelper.shouldRenderReadOnly(this.question, this.controller, isReadOnly),
            shouldRenderBorders: true,
        };
    }
    async generateInputBrick(point, options) {
        const shouldRenderReadOnly = SurveyHelper.shouldRenderReadOnly(this.question, this.controller, this.question.isReadOnly);
        const style = SurveyHelper.getPatchedTextStyle(this.controller, SurveyHelper.mergeObjects({}, this.style.input, shouldRenderReadOnly ? this.style.inputReadOnly : undefined));
        return await SurveyHelper.createCommentFlat(point, this.controller, { ...options, shouldRenderReadOnly }, style);
    }
}
FlatRepository.getInstance().register('slider', FlatSlider);

class FlatSignaturePad extends FlatQuestion {
    get signatureSize() {
        if (!this._signatureSize) {
            let width = SurveyHelper.pxToPt(this.question.signatureWidth);
            let height = SurveyHelper.pxToPt(this.question.signatureHeight);
            const availableWidth = SurveyHelper.getPageAvailableWidth(this.controller);
            if (width > availableWidth) {
                const newWidth = availableWidth;
                height *= newWidth / width;
                width = newWidth;
            }
            this._signatureSize = { width, height };
        }
        return this._signatureSize;
    }
    async generateBackgroundImage(point) {
        return await SurveyHelper.createImageFlat(point, this.question, this.controller, { link: this.question.backgroundImage, ...this.signatureSize, objectFit: 'cover' }, true);
    }
    getSignImageUrl() {
        return this.question.storeDataAsText || !this.question.loadedData ? this.question.value : this.question.loadedData;
    }
    async generateSign(point) {
        let brick;
        if (this.question.value) {
            brick = await SurveyHelper.createImageFlat(point, this.question, this.controller, { link: this.getSignImageUrl(), ...this.signatureSize }, false);
        }
        else {
            brick = new EmptyBrick(this.controller, SurveyHelper.createRect(point, this.signatureSize.width, this.signatureSize.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,
                };
                SurveyHelper.renderFlatBorders(this.controller, borderOptions, { ...this.style.input,
                    dashStyle: FlatSignaturePad.BORDER_STYLE == 'dashed' ? {
                        dashArray: [5],
                        dashPhase: 0
                    } : undefined
                });
            };
        }
        return brick;
    }
    async generateFlatsContent(point) {
        const compositeBrick = new CompositeBrick();
        if (this.question.backgroundImage) {
            compositeBrick.addBrick(await this.generateBackgroundImage(point));
        }
        compositeBrick.addBrick(await this.generateSign(point));
        return [compositeBrick];
    }
}
FlatSignaturePad.BORDER_STYLE = 'dashed';
FlatRepository.getInstance().register('signaturepad', FlatSignaturePad);

class FlatTextbox extends FlatQuestion {
    async generateFlatsContent(point) {
        const shouldRenderReadOnly = SurveyHelper.shouldRenderReadOnly(this.question, this.controller, this.question.isReadOnly);
        const style = SurveyHelper.getPatchedTextStyle(this.controller, SurveyHelper.mergeObjects({}, this.style.input, shouldRenderReadOnly ? this.style.inputReadOnly : undefined));
        const options = {
            fieldName: this.question.id,
            inputType: this.question.inputType,
            value: !!this.question.value ? this.question.inputValue : '',
            isReadOnly: this.question.isReadOnly,
            shouldRenderReadOnly: shouldRenderReadOnly,
            shouldRenderBorders: settings.readOnlyTextRenderMode === 'input',
            placeholder: SurveyHelper.getLocString(this.question.locPlaceHolder) || this.question.inputValue
        };
        return [await SurveyHelper.createCommentFlat(point, this.controller, { shouldRenderReadOnly, rows: FlatTextbox.MULTILINE_TEXT_ROWS_COUNT, ...options }, style)];
    }
}
FlatTextbox.MULTILINE_TEXT_ROWS_COUNT = 1;
FlatRepository.getInstance().register('text', FlatTextbox);

class FlatSurvey {
    popRowlines(flats) {
        while (flats.length > 0 && flats[flats.length - 1] instanceof RowlineBrick) {
            flats.pop();
        }
    }
    constructor(survey, controller, style) {
        this.survey = survey;
        this.controller = controller;
        this.style = style;
    }
    async generateFlatTitle(point) {
        const compositeFlat = new CompositeBrick();
        if (this.survey.showTitle) {
            const style = this.style;
            if (this.survey.title) {
                const textOptions = { ...style.title };
                const surveyTitleFlat = await SurveyHelper.createTextFlat(point, this.controller, this.survey.locTitle, textOptions);
                compositeFlat.addBrick(surveyTitleFlat);
                point = SurveyHelper.createPoint(surveyTitleFlat);
            }
            if (this.survey.description) {
                if (this.survey.title) {
                    point.yTop += style.spacing.titleDescriptionGap;
                }
                compositeFlat.addBrick(await SurveyHelper.createTextFlat(point, this.controller, this.survey.locDescription, { ...style.description }));
            }
        }
        return compositeFlat;
    }
    async generateFlatLogoImage(point) {
        const logoUrl = SurveyHelper.getLocString(this.survey.locLogo);
        const logoSize = await SurveyHelper.getCorrectedImageSize(this.controller, { imageLink: logoUrl, imageHeight: this.survey.logoHeight, imageWidth: this.survey.logoWidth, defaultImageWidth: '300px', defaultImageHeight: '200px' });
        const logoFlat = await SurveyHelper.createImageFlat(point, null, this.controller, { link: logoUrl,
            width: logoSize.width, height: logoSize.height });
        let shift = 0;
        if (this.survey.logoPosition === 'right') {
            shift = SurveyHelper.getPageAvailableWidth(this.controller) - logoFlat.width;
        }
        else if (this.survey.logoPosition !== 'left') {
            shift = SurveyHelper.getPageAvailableWidth(this.controller) / 2.0 - logoFlat.width / 2.0;
        }
        logoFlat.xLeft += shift;
        logoFlat.xRight += shift;
        return logoFlat;
    }
    async generateFlats() {
        const flats = [];
        const header = new ContainerBrick(this.controller, { ...this.controller.leftTopPoint, width: SurveyHelper.getPageAvailableWidth(this.controller) }, this.style.header);
        await header.setup(async (point, bricks) => {
            if (!this.survey.hasLogo) {
                const titleFlat = await this.generateFlatTitle(point);
                if (!titleFlat.isEmpty)
                    bricks.push(titleFlat);
            }
            else if (this.survey.isLogoBefore) {
                const logoFlat = await this.generateFlatLogoImage(point);
                bricks.push(logoFlat);
                const titlePoint = SurveyHelper.createPoint(logoFlat, this.survey.logoPosition === 'top', this.survey.logoPosition !== 'top');
                if (this.survey.logoPosition !== 'top') {
                    this.controller.pushMargins();
                    titlePoint.xLeft += this.controller.unitWidth;
                    this.controller.margins.left += logoFlat.width + this.controller.unitWidth;
                }
                else {
                    titlePoint.xLeft = point.xLeft;
                    titlePoint.yTop += this.controller.unitHeight / 2.0;
                }
                const titleFlat = await this.generateFlatTitle(titlePoint);
                if (this.survey.logoPosition !== 'top')
                    this.controller.popMargins();
                if (!titleFlat.isEmpty)
                    bricks.push(titleFlat);
            }
            else {
                if (this.survey.logoPosition === 'right') {
                    const logoFlat = await this.generateFlatLogoImage(point);
                    bricks.push(logoFlat);
                    this.controller.pushMargins();
                    this.controller.margins.right += logoFlat.width + this.controller.unitWidth;
                    const titleFlat = await this.generateFlatTitle(point);
                    if (!titleFlat.isEmpty)
                        bricks.unshift(titleFlat);
                    this.controller.popMargins();
                }
                else {
                    const titleFlat = await this.generateFlatTitle(point);
                    let logoPoint = point;
                    if (!titleFlat.isEmpty) {
                        bricks.push(titleFlat);
                        logoPoint = SurveyHelper.createPoint(titleFlat);
                        logoPoint.yTop += this.controller.unitHeight / 2.0;
                    }
                    const logoFlat = await this.generateFlatLogoImage(logoPoint);
                    if (bricks.length !== 0)
                        bricks.push(logoFlat);
                    else
                        bricks.push(logoFlat);
                }
            }
        });
        if (!header.isEmpty) {
            flats.push(header.getBricks());
        }
        let point = this.controller.leftTopPoint;
        if (flats.length !== 0) {
            point.yTop = SurveyHelper.createPoint(SurveyHelper.mergeRects(...flats[0])).yTop;
            flats[0].push(SurveyHelper.createRowlineFlat(point, this.controller));
            point.yTop += this.style.spacing.headerContentGap + SurveyHelper.EPSILON;
        }
        for (let i = 0; i < this.survey.visiblePages.length; i++) {
            this.survey.currentPage = this.survey.visiblePages[i];
            let pageFlats = await SurveyHelper.generatePageFlats(this.survey, this.controller, this.survey.currentPage, point);
            if (i === 0 && flats.length !== 0) {
                flats[0].push(...pageFlats);
            }
            else
                flats.push(pageFlats);
            this.popRowlines(flats[flats.length - 1]);
            point.yTop = this.controller.leftTopPoint.yTop;
        }
        return flats;
    }
}
FlatRepository.registerSurvey(FlatSurvey);

class FlatMatrix extends FlatQuestion {
    async generateFlatsContent(point) {
        const pageWidth = SurveyHelper.getPageAvailableWidth(this.controller);
        const rowTitleWidth = this.question.hasRows ? (this.question.rowTitleWidth ? SurveyHelper.parseWidth(this.question.rowTitleWidth, pageWidth) : this.style.columnMinWidth) : 0;
        const availableWidth = pageWidth - rowTitleWidth;
        const isVertical = this.question.renderAs === 'list' || this.controller.matrixRenderAs === 'list' ||
            this.style.columnMinWidth * this.question.visibleColumns.length + this.style.spacing.tableColumnGap * (this.question.visibleColumns.length - 1) > availableWidth;
        return new (isVertical ? FlatMatrixContentVertical : FlatMatrixContentHorizontal)(this.controller, this.survey, this.question, this.style).generateFlats(point);
    }
}
class FlatMatrixContent {
    constructor(controller, survey, question, style) {
        this.controller = controller;
        this.survey = survey;
        this.question = question;
        this.style = style;
        this.radioGroupWraps = {};
    }
    async generateFlatCell(point, contentCallback) {
        const container = new ContainerBrick(this.controller, { ...point, width: SurveyHelper.getPageAvailableWidth(this.controller) }, this.style.cell);
        await container.setup(async (point, bricks) => {
            await contentCallback(point, bricks);
        });
        return container;
    }
    async generateTextComposite(point, options) {
        const { item, row } = options;
        const currPoint = SurveyHelper.clone(point);
        const radioFlat = this.generateFlatItem(currPoint, options);
        currPoint.yTop = radioFlat.yBot + this.style.spacing.gapBetweenItemText;
        const cellTextFlat = await SurveyHelper.createTextFlat(currPoint, this.controller, this.question.getCellDisplayLocText(row.name, item));
        return new CompositeBrick(radioFlat, cellTextFlat);
    }
    getRadioGroupWrap(fieldName, row, rowIndex) {
        if (!this.radioGroupWraps[fieldName]) {
            this.radioGroupWraps[fieldName] = new RadioGroupWrap(this.controller, {
                readOnly: this.question.isReadOnly,
                fieldName: fieldName,
                updateOptions: (options) => { this.survey.getUpdatedRadioGroupWrapOptions(options, this.question, { row, rowIndex }); }
            });
        }
        return this.radioGroupWraps[fieldName];
    }
    getInputStyle(isReadOnly, isChecked, isMultiSelect) {
        const inputType = isMultiSelect ? 'checkbox' : 'radio';
        const style = SurveyHelper.mergeObjects({}, this.style.input, this.style[`${inputType}Input`]);
        if (isReadOnly) {
            SurveyHelper.mergeObjects(style, this.style.inputReadOnly, this.style[`${inputType}InputReadOnly`]);
            if (isChecked) {
                SurveyHelper.mergeObjects(style, this.style.inputReadOnlyChecked, this.style[`${inputType}InputReadOnlyChecked`]);
            }
        }
        return style;
    }
    generateFlatItem(point, options) {
        const { item, row, itemIndex, rowIndex } = options;
        const fieldName = this.question.id + 'row' + rowIndex;
        const isChecked = row.isChecked(item);
        const isReadOnly = this.question.isReadOnly;
        const shouldRenderReadOnly = isReadOnly && SurveyHelper.getReadonlyRenderAs(this.question, this.controller) !== 'acroform' || this.controller.compress;
        const style = SurveyHelper.getPatchedTextStyle(this.controller, this.getInputStyle(shouldRenderReadOnly, isChecked, this.question.isMultiSelect));
        const rect = SurveyHelper.createRect(point, style.width, style.height);
        if (this.question.isMultiSelect) {
            return new CheckItemBrick(this.controller, rect, {
                fieldName: fieldName + 'index' + itemIndex,
                checked: isChecked,
                shouldRenderReadOnly,
                readOnly: isReadOnly,
                updateOptions: (options) => {
                    this.survey.updateCheckItemAcroformOptions(options, this.question, { item, row, rowIndex });
                }
            }, style);
        }
        else {
            const radioGroupWrap = this.getRadioGroupWrap(fieldName, row, rowIndex);
            return new RadioItemBrick(this.controller, rect, radioGroupWrap, {
                checked: isChecked,
                index: itemIndex,
                shouldRenderReadOnly: shouldRenderReadOnly,
                updateOptions: options => this.survey.updateRadioItemAcroformOptions(options, this.question, { item, row, rowIndex }),
            }, style);
        }
    }
    getGapBetweenRows() {
        return this.style.spacing.tableRowGap;
    }
    async generateFlats(point) {
        const cells = [];
        let currPoint = SurveyHelper.clone(point);
        for (let i = 0; i < this.question.visibleRows.length; i++) {
            const flatsRow = await this.generateFlatsRow(currPoint, this.question.visibleRows[i], i);
            currPoint = SurveyHelper.createPoint(SurveyHelper.mergeRects(...flatsRow));
            currPoint.yTop += this.getGapBetweenRows();
            cells.push(...flatsRow);
        }
        return cells;
    }
}
class FlatMatrixContentVertical extends FlatMatrixContent {
    async generateItemComposite(point, options) {
        const currPoint = SurveyHelper.clone(point);
        const radioFlat = this.generateFlatItem(point, options);
        currPoint.xLeft = radioFlat.xRight + this.style.spacing.listChoiceTextGap;
        const radioText = await SurveyHelper.createTextFlat(currPoint, this.controller, options.item.locText, SurveyHelper.mergeObjects({}, this.style.columnTitle, this.style.listChoiceText));
        SurveyHelper.alignVerticallyBricks('center', radioFlat, radioText.unfold()[0]);
        radioText.updateRect();
        return new CompositeBrick(radioFlat, radioText);
    }
    getGapBetweenRows() {
        return this.style.spacing.listSectionGap;
    }
    async generateFlatsRow(point, row, rowIndex) {
        const currPoint = SurveyHelper.clone(point);
        const cell = await this.generateFlatCell(currPoint, async (point, bricks) => {
            const currPoint = SurveyHelper.clone(point);
            const rowTextFlat = await SurveyHelper.createTextFlat(point, this.controller, row.locText, SurveyHelper.mergeObjects({}, this.style.rowTitle, this.style.listSectionTitle));
            bricks.push(rowTextFlat);
            currPoint.yTop = rowTextFlat.yBot + this.style.spacing.listItemTitleContentGap;
            for (let i = 0; i < this.question.visibleColumns.length; i++) {
                const itemFlat = await ((this.question.hasCellText) ? this.generateTextComposite : this.generateItemComposite).call(this, currPoint, { item: this.question.visibleColumns[i], itemIndex: i, row, rowIndex });
                bricks.push(itemFlat);
                currPoint.yTop = itemFlat.yBot + this.style.spacing.listChoiceGap;
            }
        });
        return [cell, SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(cell), this.controller)];
    }
}
class FlatMatrixContentHorizontal extends FlatMatrixContent {
    setup() {
        const availableWidth = SurveyHelper.getPageAvailableWidth(this.controller);
        if (this.question.rowTitleWidth) {
            this.rowTitleWidth = SurveyHelper.parseWidth(this.question.rowTitleWidth, availableWidth);
        }
        else {
            this.rowTitleWidth = SurveyHelper.getColumnWidth(this.controller, this.question.visibleColumns.length + (this.question.hasRows ? 1 : 0), this.style.spacing.tableColumnGap);
        }
        this.controller.pushMargins();
        this.controller.margins.left += (this.rowTitleWidth + this.style.spacing.tableColumnGap);
        this.columnWidth = SurveyHelper.getColumnWidth(this.controller, this.question.visibleColumns.length, this.style.spacing.tableColumnGap);
        this.controller.popMargins();
    }
    async generateFlatsRow(point, row, rowIndex) {
        const cells = [];
        const currPoint = SurveyHelper.clone(point);
        if (this.question.hasRows) {
            this.controller.pushMargins();
            currPoint.xLeft = this.controller.margins.left;
            this.controller.margins.right += (SurveyHelper.getPageAvailableWidth(this.controller) - this.rowTitleWidth);
            cells.push(await this.generateFlatCell(currPoint, async (point, bricks) => {
                bricks.push(await SurveyHelper.createTextFlat(point, this.controller, row.locText, this.style.rowTitle));
            }));
            currPoint.xLeft += this.rowTitleWidth + this.style.spacing.tableColumnGap;
            this.controller.popMargins();
        }
        for (let i = 0; i < this.question.visibleColumns.length; i++) {
            this.controller.pushMargins();
            this.controller.margins.left = point.xLeft;
            const options = { item: this.question.visibleColumns[i], itemIndex: i, rowIndex, row };
            this.controller.margins.right += (SurveyHelper.getPageAvailableWidth(this.controller) - this.columnWidth);
            cells.push(await this.generateFlatCell(currPoint, async (point, bricks) => {
                if (this.question.hasCellText) {
                    bricks.push(await this.generateTextComposite(point, options));
                }
                else {
                    bricks.push(this.generateFlatItem(point, options));
                }
            }));
            currPoint.xLeft += this.columnWidth + this.style.spacing.tableColumnGap;
            this.controller.popMargins();
        }
        const rowRect = SurveyHelper.mergeRects(...cells);
        cells.forEach(cell => {
            cell.fitToHeight(rowRect.yBot - rowRect.yTop, true);
        });
        const compositeBrick = new CompositeBrick(...cells);
        return [compositeBrick, SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(compositeBrick), this.controller)];
    }
    async generateFlatsHeader(point) {
        const headers = [];
        const currPoint = SurveyHelper.clone(point);
        if (this.question.hasRows) {
            this.controller.margins.left = currPoint.xLeft;
            this.controller.margins.right += (SurveyHelper.getPageAvailableWidth(this.controller) - this.rowTitleWidth);
            headers.push(await this.generateFlatCell(currPoint, async (point, bricks) => {
                bricks.push(new EmptyBrick(this.controller, SurveyHelper.createRect(point, SurveyHelper.getPageAvailableWidth(this.controller), 1)));
            }));
            currPoint.xLeft += this.rowTitleWidth + this.style.spacing.tableColumnGap;
        }
        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(await this.generateFlatCell(currPoint, async (point, bricks) => {
                bricks.push(await SurveyHelper.createTextFlat(point, this.controller, this.question.visibleColumns[i].locText, { ...this.style.columnTitle }));
            }));
            currPoint.xLeft += this.columnWidth + this.style.spacing.tableColumnGap;
            this.controller.popMargins();
        }
        const rowRect = SurveyHelper.mergeRects(...headers);
        headers.forEach(header => {
            header.fitToHeight(rowRect.yBot - rowRect.yTop, true);
        });
        const compositeBrick = new CompositeBrick(...headers);
        return [compositeBrick, SurveyHelper.createRowlineFlat(SurveyHelper.createPoint(compositeBrick), this.controller)];
    }
    async generateFlats(point) {
        this.setup();
        const currPoint = SurveyHelper.clone(point);
        const headerFlats = await this.generateFlatsHeader(point);
        currPoint.yTop = SurveyHelper.mergeRects(...headerFlats).yBot + this.style.spacing.tableRowGap;
        const rowsFlats = await super.generateFlats(currPoint);
        return [...headerFlats, ...rowsFlats];
    }
}
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, style, isMultiple = true) {
        super(survey, question, controller, style);
        this.survey = survey;
        this.isMultiple = isMultiple;
        this.cellFlatQuestionHash = {};
    }
    get visibleRows() {
        if (!this.visibleRowsValue) {
            this.visibleRowsValue = this.question.renderedTable.rows.filter(row => row.visible);
        }
        return this.visibleRowsValue;
    }
    getFlatQuestion(survey, controller, question) {
        const id = question.uniqueId.toString();
        if (!this.cellFlatQuestionHash[id]) {
            this.cellFlatQuestionHash[id] = SurveyHelper.getFlatQuestion(survey, controller, question);
        }
        return this.cellFlatQuestionHash[id];
    }
    async generateFlatsCell(point, cell, location, isWide = true) {
        let cellStyle = this.style.cell;
        if (cell.hasTitle && location !== 'header') {
            cellStyle = SurveyHelper.mergeObjects({}, cellStyle /* todo , this.style.cellRowTitle*/);
            if (!isWide) {
                cellStyle = SurveyHelper.mergeObjects({}, cellStyle, this.style.listSectionTitleContainer);
            }
        }
        if (cell.hasTitle && location === 'header') {
            cellStyle = SurveyHelper.mergeObjects({}, cellStyle /*todo, this.style.cellColumnTitle*/);
            if (!isWide) {
                cellStyle = SurveyHelper.mergeObjects({}, cellStyle /*todo, this.style.cellVerticalColumnTitle*/);
            }
        }
        const container = new ContainerBrick(this.controller, { ...point, width: SurveyHelper.getPageAvailableWidth(this.controller) }, cellStyle);
        await container.setup(async (point, bricks) => {
            var _a, _b;
            if (cell.hasQuestion) {
                if (location == 'footer' && !cell.question.isAnswered) {
                    bricks.push(new EmptyBrick(this.controller, { ...point, yBot: point.yTop, xRight: point.xLeft + SurveyHelper.getPageAvailableWidth(this.controller) }));
                }
                else {
                    const questionFlatRenderer = this.getFlatQuestion(this.survey, this.controller, cell.question);
                    if (isWide && cell.isChoice) {
                        bricks.push(questionFlatRenderer
                            .generateFlatItem(point, cell.item, cell.choiceIndex, questionFlatRenderer.getItemStyle(cell.item).input));
                    }
                    else {
                        cell.question.titleLocation = 'matrix';
                        const currPoint = SurveyHelper.clone(point);
                        if (!isWide && this.question.renderedTable.showHeader && (location !== 'header') && ((_b = (_a = cell.cell) === null || _a === void 0 ? void 0 : _a.column) === null || _b === void 0 ? void 0 : _b.locTitle)) {
                            container.addBrick(await SurveyHelper.createTextFlat(currPoint, this.controller, cell.cell.column.locTitle, SurveyHelper.mergeObjects({}, this.style.columnTitle, this.style.listItemTitle)));
                            currPoint.yTop = container.yBot + this.style.spacing.listItemTitleContentGap;
                        }
                        bricks.push(...await questionFlatRenderer.generateFlats(currPoint));
                    }
                }
            }
            else if (cell.hasTitle) {
                if (location == 'header') {
                    bricks.push(await SurveyHelper.createTextFlat(point, this.controller, cell.locTitle, { ...this.style.columnTitle }));
                }
                else {
                    bricks.push(await SurveyHelper.createTextFlat(point, this.controller, cell.locTitle, SurveyHelper.mergeObjects({}, this.style.rowTitle, isWide ? undefined : this.style.listSectionTitle)));
                }
            }
            else {
                bricks.push(new EmptyBrick(this.controller, { ...point, yBot: point.yTop, xRight: point.xLeft + SurveyHelper.getPageAvailableWidth(this.controller) }));
            }
        });
        return container;
    }
    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);
    }
    async generateFlatsRowHorisontal(point, row, columnWidth) {
        const rowBricks = [];
        const currPoint = SurveyHelper.clone(point);
        let lastRightMargin = this.controller.paperWidth - this.controller.margins.left +
            this.style.spacing.tableColumnGap;
        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.style.spacing.tableColumnGap;
            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 = await this.generateFlatsCell(currPoint, row.cells[i], rowLocation);
            if (!cellContent.isEmpty) {
                rowBricks.push(cellContent);
            }
            cnt++;
        }
        const { yBot: rowYBot, yTop: rowYTop } = SurveyHelper.mergeRects(...rowBricks);
        const rowHeight = rowYBot - rowYTop;
        rowBricks.forEach(brick => {
            brick.fitToHeight(rowHeight);
        });
        this.controller.popMargins();
        return new CompositeBrick(...rowBricks);
    }
    async generateFlatsRowVertical(point, row) {
        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;
            composite.addBrick(await this.generateFlatsCell(currPoint, row.cells[i], rowLocation, false));
            currPoint.yTop = composite.yBot + this.style.spacing.tableRowGap;
        }
        return composite;
    }
    getColumnsAvalableWidth(colCount) {
        return SurveyHelper.getPageAvailableWidth(this.controller) -
            (colCount - 1) * this.style.spacing.tableColumnGap;
    }
    calculateColumnWidth(rows, colCount) {
        const availableWidth = this.getColumnsAvalableWidth(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.style.columnMinWidth;
        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;
    }
    async generateOneRow(point, row, isWide, columnWidth) {
        if (isWide) {
            return await this.generateFlatsRowHorisontal(point, row, columnWidth);
        }
        return await this.generateFlatsRowVertical(point, row);
    }
    async generateFlatsRows(point, rows, colCount, isWide) {
        const currPoint = SurveyHelper.clone(point);
        const rowsFlats = [];
        if (!rows || rows.length == 0)
            return;
        const columnWidths = this.calculateColumnWidth(rows, colCount);
        for (let i = 0; i < rows.length; i++) {
            let rowFlat = await this.generateOneRow(currPoint, rows[i], isWide, columnWidths);
            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 + this.style.spacing.tableRowGap;
            }
            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 panelPoint = SurveyHelper.clone(currPoint);
                const currComposite = new CompositeBrick();
                if (this.isMultiple && isWide) {
                    this.controller.pushMargins();
                    panelPoint.xLeft += columnWidths[0] + this.style.spacing.tableColumnGap;
                    this.controller.margins.left = panelPoint.xLeft;
                }
                const panelBricks = await SurveyHelper.generatePanelFlats(this.survey, this.controller, currentDetailPanel, panelPoint);
                if (this.isMultiple && isWide) {
                    this.controller.popMargins();
                    const panelRect = SurveyHelper.mergeRects(...panelBricks);
                    const emptyBrick = new EmptyBrick(this.controller, { ...currPoint, xRight: currPoint.xLeft + columnWidths[0], yBot: currPoint.yTop + panelRect.yBot - panelRect.yTop }, { borderMode: BorderMode.Middle, color: this.style.cell.backgroundColor, borderColor: this.style.cell.borderColor, borderRadius: this.style.cell.borderRadius, borderWidth: this.style.cell.borderWidth });
                    currComposite.addBrick(emptyBrick);
                }
                currComposite.addBrick(...panelBricks);
                currPoint.yTop = currComposite.yBot + this.style.spacing.tableRowGap;
                rowsFlats.push(currComposite);
            }
        }
        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.getColumnsAvalableWidth(colCount));
    }
    getRowsToRender(table, isVertical, isWide) {
        const rows = [];
        const renderedRows = this.visibleRows.filter(row => !row.isDetailRow);
        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;
        }
    }
    async generateFlatsContent(point) {
        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);
        const oldIsMobile = this.question.isMobile;
        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);
        const result = await this.generateFlatsRows(point, rows, colCount, isWide);
        this.question.isMobile = oldIsMobile;
        return result;
    }
}
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, style) {
        super(survey, question, controller, style, 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 {
    getVisibleRows() {
        return this.question.getRows().filter(row => row.isVisible);
    }
    async generateFlatCell(point, callback) {
        const container = new ContainerBrick(this.controller, { ...point, width: SurveyHelper.getPageAvailableWidth(this.controller) }, this.style.itemCell);
        await container.setup(callback);
        return container;
    }
    async generateFlatItemCells(point, item) {
        const colWidth = SurveyHelper.getPageAvailableWidth(this.controller);
        const bricks = [];
        this.controller.pushMargins();
        this.controller.margins.right = this.controller.paperWidth - this.controller.margins.left - colWidth * this.style.itemTitleWidthPercentage;
        bricks.push(await this.generateFlatCell(point, async (point, bricks) => {
            bricks.push(await SurveyHelper.
                createTextFlat(point, this.controller, item.locTitle, { ...this.style.itemTitle }));
        }));
        this.controller.popMargins();
        this.controller.pushMargins();
        this.controller.margins.left = point.xLeft + colWidth * this.style.itemTitleWidthPercentage + this.style.spacing.itemTitleGap;
        bricks.push(await this.generateFlatCell({ xLeft: this.controller.margins.left, yTop: point.yTop }, async (point, bricks) => {
            const flatMultipleTextItemQuestion = FlatRepository.getInstance().create(this.survey, item.editor, this.controller, this.survey.getElementStyle(item.editor), 'text');
            bricks.push(...await flatMultipleTextItemQuestion.generateFlatsContent(point));
        }));
        this.controller.popMargins();
        return bricks;
    }
    async generateFlatsContent(point) {
        const rowsFlats = [];
        const currPoint = SurveyHelper.clone(point);
        const rows = this.getVisibleRows();
        for (let i = 0; i < rows.length; i++) {
            this.controller.pushMargins();
            const currentRowFlats = [];
            for (let j = 0; j < rows[i].cells.length; j++) {
                this.controller.pushMargins();
                SurveyHelper.setColumnMargins(this.controller, this.question.colCount, j, this.style.spacing.itemColumnGap);
                currPoint.xLeft = this.controller.margins.left;
                currentRowFlats.push(...await this.generateFlatItemCells(currPoint, rows[i].cells[j].item));
                this.controller.popMargins();
            }
            this.controller.popMargins();
            currPoint.xLeft = point.xLeft;
            const rowRect = SurveyHelper.mergeRects(...currentRowFlats);
            currentRowFlats.forEach((flat) => { flat.fitToHeight(rowRect.yBot - rowRect.yTop); });
            currPoint.yTop = rowRect.yBot;
            rowsFlats.push(...currentRowFlats, SurveyHelper.createRowlineFlat(currPoint, this.controller));
            currPoint.yTop += this.style.spacing.itemGap;
        }
        return rowsFlats;
    }
}
FlatRepository.getInstance().register('multipletext', FlatMultipleText);

class CustomBrick extends PdfBrick {
    constructor(question, controller, renderFunc) {
        super(controller, renderFunc(controller.helperDoc, question, 0, 0));
        this.question = question;
        this.renderFunc = renderFunc;
    }
    async renderInteractive() {
        await new Promise((resolve) => {
            this.renderFunc(this.controller.doc, this.question, this.contentRect.xLeft, this.contentRect.yTop);
            resolve();
        });
    }
}

checkLibraryVersion(`${"3.0.2"}`, 'survey-pdf');

function rgbaToHex(rgbaString) {
    const parts = rgbaString.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*([0-9.]+)?\)$/);
    if (!parts) {
        return null; // Invalid RGBA string format
    }
    const r = parseInt(parts[1]);
    const g = parseInt(parts[2]);
    const b = parseInt(parts[3]);
    const a = parts[4] ? parseFloat(parts[4]) : null;
    const toHex = (c) => {
        const hex = c.toString(16);
        return hex.length === 1 ? '0' + hex : hex;
    };
    const alphaHex = a ? toHex(Math.round(a * 255)) : '';
    return `#${toHex(r)}${toHex(g)}${toHex(b)}${alphaHex}`;
}
function parseColorCssFunction(value) {
    const regex = /color\s*\(\s*srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s*(?:\/\s*([\d.]+))?\s*\)/i;
    const match = value.match(regex);
    if (match) {
        const r = parseFloat(match[1]);
        const g = parseFloat(match[2]);
        const b = parseFloat(match[3]);
        const alpha = match[4] ? parseFloat(match[4]) : 1;
        const r255 = Math.round(r * 255);
        const g255 = Math.round(g * 255);
        const b255 = Math.round(b * 255);
        return `rgba(${r255}, ${g255}, ${b255}, ${alpha})`;
    }
    else {
        return value;
    }
}

export { TextFieldBrick as $, FlatQuestionDefault as A, BaseImageUtils as B, CheckItemBrick as C, DocController as D, EmptyBrick as E, FlatBooleanCheckbox as F, FlatRadiogroup as G, FlatRanking as H, FlatRating as I, FlatRepository as J, FlatSelectBase as K, FlatSignaturePad as L, FlatSlider as M, FlatSurvey as N, FlatTextbox as O, HTMLBrick as P, HorizontalAlign as Q, ImageBrick as R, LinkBrick as S, PagePacker as T, PdfBrick as U, RadioItemBrick as V, RankingItemBrick as W, RowlineBrick as X, SurveyHelper as Y, SurveyPDF as Z, TextBrick as _, registerImageUtils as a, VerticalAlign as a0, parseColorCssFunction as a1, registerVariablesManagerCreator as b, CompositeBrick as c, CustomBrick as d, DocOptions as e, DrawCanvas as f, DropdownBrick as g, EventHandler as h, FlatCheckbox as i, FlatComment as j, FlatCustomModel as k, FlatDropdown as l, FlatExpression as m, FlatFile as n, FlatHTML as o, FlatImage as p, FlatImagePicker as q, rgbaToHex as r, FlatMatrix as s, FlatMatrixDynamic as t, FlatMatrixMultiple as u, FlatMultipleText as v, FlatPage as w, FlatPanel as x, FlatPanelDynamic as y, FlatQuestion as z };
//# sourceMappingURL=pdf-shared.mjs.map