\n htmlButton.children.push({\n name: \"a\",\n attributes: {\n id: \"link\" + this[$uid],\n href,\n newWindow: jsURL.newWindow,\n class: [\"xfaLink\"],\n style: {},\n },\n children: [],\n });\n }\n\n return HTMLResult.success(htmlButton);\n }\n}\n\nclass Calculate extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"calculate\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.override = getStringOption(attributes.override, [\n \"disabled\",\n \"error\",\n \"ignore\",\n \"warning\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.message = null;\n this.script = null;\n }\n}\n\nclass Caption extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"caption\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.placement = getStringOption(attributes.placement, [\n \"left\",\n \"bottom\",\n \"inline\",\n \"right\",\n \"top\",\n ]);\n this.presence = getStringOption(attributes.presence, [\n \"visible\",\n \"hidden\",\n \"inactive\",\n \"invisible\",\n ]);\n this.reserve = Math.ceil(getMeasurement(attributes.reserve));\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.font = null;\n this.margin = null;\n this.para = null;\n this.value = null;\n }\n\n [$setValue](value) {\n _setValue(this, value);\n }\n\n [$getExtra](availableSpace) {\n if (!this[$extra]) {\n let { width, height } = availableSpace;\n switch (this.placement) {\n case \"left\":\n case \"right\":\n case \"inline\":\n width = this.reserve <= 0 ? width : this.reserve;\n break;\n case \"top\":\n case \"bottom\":\n height = this.reserve <= 0 ? height : this.reserve;\n break;\n }\n\n this[$extra] = layoutNode(this, { width, height });\n }\n return this[$extra];\n }\n\n [$toHTML](availableSpace) {\n // TODO: incomplete.\n if (!this.value) {\n return HTMLResult.EMPTY;\n }\n\n this[$pushPara]();\n const value = this.value[$toHTML](availableSpace).html;\n\n if (!value) {\n this[$popPara]();\n return HTMLResult.EMPTY;\n }\n\n const savedReserve = this.reserve;\n if (this.reserve <= 0) {\n const { w, h } = this[$getExtra](availableSpace);\n switch (this.placement) {\n case \"left\":\n case \"right\":\n case \"inline\":\n this.reserve = w;\n break;\n case \"top\":\n case \"bottom\":\n this.reserve = h;\n break;\n }\n }\n\n const children = [];\n if (typeof value === \"string\") {\n children.push({\n name: \"#text\",\n value,\n });\n } else {\n children.push(value);\n }\n\n const style = toStyle(this, \"font\", \"margin\", \"visibility\");\n switch (this.placement) {\n case \"left\":\n case \"right\":\n if (this.reserve > 0) {\n style.width = measureToString(this.reserve);\n }\n break;\n case \"top\":\n case \"bottom\":\n if (this.reserve > 0) {\n style.height = measureToString(this.reserve);\n }\n break;\n }\n\n setPara(this, null, value);\n this[$popPara]();\n\n this.reserve = savedReserve;\n\n return HTMLResult.success({\n name: \"div\",\n attributes: {\n style,\n class: [\"xfaCaption\"],\n },\n children,\n });\n }\n}\n\nclass Certificate extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"certificate\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Certificates extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"certificates\", /* hasChildren = */ true);\n this.credentialServerPolicy = getStringOption(\n attributes.credentialServerPolicy,\n [\"optional\", \"required\"]\n );\n this.id = attributes.id || \"\";\n this.url = attributes.url || \"\";\n this.urlPolicy = attributes.urlPolicy || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.encryption = null;\n this.issuers = null;\n this.keyUsage = null;\n this.oids = null;\n this.signing = null;\n this.subjectDNs = null;\n }\n}\n\nclass CheckButton extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"checkButton\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.mark = getStringOption(attributes.mark, [\n \"default\",\n \"check\",\n \"circle\",\n \"cross\",\n \"diamond\",\n \"square\",\n \"star\",\n ]);\n this.shape = getStringOption(attributes.shape, [\"square\", \"round\"]);\n this.size = getMeasurement(attributes.size, \"10pt\");\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.border = null;\n this.extras = null;\n this.margin = null;\n }\n\n [$toHTML](availableSpace) {\n // TODO: border, shape and mark.\n\n const style = toStyle(\"margin\");\n const size = measureToString(this.size);\n\n style.width = style.height = size;\n\n let type;\n let className;\n let groupId;\n const field = this[$getParent]()[$getParent]();\n const items =\n (field.items.children.length &&\n field.items.children[0][$toHTML]().html) ||\n [];\n const exportedValue = {\n on: (items[0] !== undefined ? items[0] : \"on\").toString(),\n off: (items[1] !== undefined ? items[1] : \"off\").toString(),\n };\n\n const value = field.value?.[$text]() || \"off\";\n const checked = value === exportedValue.on || undefined;\n const container = field[$getSubformParent]();\n const fieldId = field[$uid];\n let dataId;\n\n if (container instanceof ExclGroup) {\n groupId = container[$uid];\n type = \"radio\";\n className = \"xfaRadio\";\n dataId = container[$data]?.[$uid] || container[$uid];\n } else {\n type = \"checkbox\";\n className = \"xfaCheckbox\";\n dataId = field[$data]?.[$uid] || field[$uid];\n }\n\n const input = {\n name: \"input\",\n attributes: {\n class: [className],\n style,\n fieldId,\n dataId,\n type,\n checked,\n xfaOn: exportedValue.on,\n xfaOff: exportedValue.off,\n \"aria-label\": ariaLabel(field),\n \"aria-required\": false,\n },\n };\n\n if (groupId) {\n input.attributes.name = groupId;\n }\n\n if (isRequired(field)) {\n input.attributes[\"aria-required\"] = true;\n input.attributes.required = true;\n }\n\n return HTMLResult.success({\n name: \"label\",\n attributes: {\n class: [\"xfaLabel\"],\n },\n children: [input],\n });\n }\n}\n\nclass ChoiceList extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"choiceList\", /* hasChildren = */ true);\n this.commitOn = getStringOption(attributes.commitOn, [\"select\", \"exit\"]);\n this.id = attributes.id || \"\";\n this.open = getStringOption(attributes.open, [\n \"userControl\",\n \"always\",\n \"multiSelect\",\n \"onEntry\",\n ]);\n this.textEntry = getInteger({\n data: attributes.textEntry,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.border = null;\n this.extras = null;\n this.margin = null;\n }\n\n [$toHTML](availableSpace) {\n // TODO: incomplete.\n const style = toStyle(this, \"border\", \"margin\");\n const ui = this[$getParent]();\n const field = ui[$getParent]();\n const fontSize = field.font?.size || 10;\n const optionStyle = {\n fontSize: `calc(${fontSize}px * var(--scale-factor))`,\n };\n const children = [];\n\n if (field.items.children.length > 0) {\n const items = field.items;\n let displayedIndex = 0;\n let saveIndex = 0;\n if (items.children.length === 2) {\n displayedIndex = items.children[0].save;\n saveIndex = 1 - displayedIndex;\n }\n const displayed = items.children[displayedIndex][$toHTML]().html;\n const values = items.children[saveIndex][$toHTML]().html;\n\n let selected = false;\n const value = field.value?.[$text]() || \"\";\n for (let i = 0, ii = displayed.length; i < ii; i++) {\n const option = {\n name: \"option\",\n attributes: {\n value: values[i] || displayed[i],\n style: optionStyle,\n },\n value: displayed[i],\n };\n if (values[i] === value) {\n option.attributes.selected = selected = true;\n }\n children.push(option);\n }\n\n if (!selected) {\n children.splice(0, 0, {\n name: \"option\",\n attributes: {\n hidden: true,\n selected: true,\n },\n value: \" \",\n });\n }\n }\n\n const selectAttributes = {\n class: [\"xfaSelect\"],\n fieldId: field[$uid],\n dataId: field[$data]?.[$uid] || field[$uid],\n style,\n \"aria-label\": ariaLabel(field),\n \"aria-required\": false,\n };\n\n if (isRequired(field)) {\n selectAttributes[\"aria-required\"] = true;\n selectAttributes.required = true;\n }\n\n if (this.open === \"multiSelect\") {\n selectAttributes.multiple = true;\n }\n\n return HTMLResult.success({\n name: \"label\",\n attributes: {\n class: [\"xfaLabel\"],\n },\n children: [\n {\n name: \"select\",\n children,\n attributes: selectAttributes,\n },\n ],\n });\n }\n}\n\nclass Color extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"color\", /* hasChildren = */ true);\n this.cSpace = getStringOption(attributes.cSpace, [\"SRGB\"]);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.value = attributes.value ? getColor(attributes.value) : \"\";\n this.extras = null;\n }\n\n [$hasSettableValue]() {\n return false;\n }\n\n [$toStyle]() {\n return this.value\n ? Util.makeHexColor(this.value.r, this.value.g, this.value.b)\n : null;\n }\n}\n\nclass Comb extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"comb\");\n this.id = attributes.id || \"\";\n this.numberOfCells = getInteger({\n data: attributes.numberOfCells,\n defaultValue: 0,\n validate: x => x >= 0,\n });\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Connect extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"connect\", /* hasChildren = */ true);\n this.connection = attributes.connection || \"\";\n this.id = attributes.id || \"\";\n this.ref = attributes.ref || \"\";\n this.usage = getStringOption(attributes.usage, [\n \"exportAndImport\",\n \"exportOnly\",\n \"importOnly\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.picture = null;\n }\n}\n\nclass ContentArea extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"contentArea\", /* hasChildren = */ true);\n this.h = getMeasurement(attributes.h);\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.relevant = getRelevant(attributes.relevant);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.w = getMeasurement(attributes.w);\n this.x = getMeasurement(attributes.x, \"0pt\");\n this.y = getMeasurement(attributes.y, \"0pt\");\n this.desc = null;\n this.extras = null;\n }\n\n [$toHTML](availableSpace) {\n // TODO: incomplete.\n const left = measureToString(this.x);\n const top = measureToString(this.y);\n\n const style = {\n left,\n top,\n width: measureToString(this.w),\n height: measureToString(this.h),\n };\n\n const classNames = [\"xfaContentarea\"];\n\n if (isPrintOnly(this)) {\n classNames.push(\"xfaPrintOnly\");\n }\n\n return HTMLResult.success({\n name: \"div\",\n children: [],\n attributes: {\n style,\n class: classNames,\n id: this[$uid],\n },\n });\n }\n}\n\nclass Corner extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"corner\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.inverted = getInteger({\n data: attributes.inverted,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.join = getStringOption(attributes.join, [\"square\", \"round\"]);\n this.presence = getStringOption(attributes.presence, [\n \"visible\",\n \"hidden\",\n \"inactive\",\n \"invisible\",\n ]);\n this.radius = getMeasurement(attributes.radius);\n this.stroke = getStringOption(attributes.stroke, [\n \"solid\",\n \"dashDot\",\n \"dashDotDot\",\n \"dashed\",\n \"dotted\",\n \"embossed\",\n \"etched\",\n \"lowered\",\n \"raised\",\n ]);\n this.thickness = getMeasurement(attributes.thickness, \"0.5pt\");\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.color = null;\n this.extras = null;\n }\n\n [$toStyle]() {\n // In using CSS it's only possible to handle radius\n // (at least with basic css).\n // Is there a real use (interest ?) of all these properties ?\n // Maybe it's possible to implement them using svg and border-image...\n // TODO: implement all the missing properties.\n const style = toStyle(this, \"visibility\");\n style.radius = measureToString(this.join === \"square\" ? 0 : this.radius);\n return style;\n }\n}\n\nclass DateElement extends ContentObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"date\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$finalize]() {\n const date = this[$content].trim();\n this[$content] = date ? new Date(date) : null;\n }\n\n [$toHTML](availableSpace) {\n return valueToHtml(this[$content] ? this[$content].toString() : \"\");\n }\n}\n\nclass DateTime extends ContentObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"dateTime\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$finalize]() {\n const date = this[$content].trim();\n this[$content] = date ? new Date(date) : null;\n }\n\n [$toHTML](availableSpace) {\n return valueToHtml(this[$content] ? this[$content].toString() : \"\");\n }\n}\n\nclass DateTimeEdit extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"dateTimeEdit\", /* hasChildren = */ true);\n this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, [\n \"auto\",\n \"off\",\n \"on\",\n ]);\n this.id = attributes.id || \"\";\n this.picker = getStringOption(attributes.picker, [\"host\", \"none\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.border = null;\n this.comb = null;\n this.extras = null;\n this.margin = null;\n }\n\n [$toHTML](availableSpace) {\n // TODO: incomplete.\n // When the picker is host we should use type=date for the input\n // but we need to put the buttons outside the text-field.\n const style = toStyle(this, \"border\", \"font\", \"margin\");\n const field = this[$getParent]()[$getParent]();\n const html = {\n name: \"input\",\n attributes: {\n type: \"text\",\n fieldId: field[$uid],\n dataId: field[$data]?.[$uid] || field[$uid],\n class: [\"xfaTextfield\"],\n style,\n \"aria-label\": ariaLabel(field),\n \"aria-required\": false,\n },\n };\n\n if (isRequired(field)) {\n html.attributes[\"aria-required\"] = true;\n html.attributes.required = true;\n }\n\n return HTMLResult.success({\n name: \"label\",\n attributes: {\n class: [\"xfaLabel\"],\n },\n children: [html],\n });\n }\n}\n\nclass Decimal extends ContentObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"decimal\");\n this.fracDigits = getInteger({\n data: attributes.fracDigits,\n defaultValue: 2,\n validate: x => true,\n });\n this.id = attributes.id || \"\";\n this.leadDigits = getInteger({\n data: attributes.leadDigits,\n defaultValue: -1,\n validate: x => true,\n });\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$finalize]() {\n const number = parseFloat(this[$content].trim());\n this[$content] = isNaN(number) ? null : number;\n }\n\n [$toHTML](availableSpace) {\n return valueToHtml(\n this[$content] !== null ? this[$content].toString() : \"\"\n );\n }\n}\n\nclass DefaultUi extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"defaultUi\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n }\n}\n\nclass Desc extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"desc\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.boolean = new XFAObjectArray();\n this.date = new XFAObjectArray();\n this.dateTime = new XFAObjectArray();\n this.decimal = new XFAObjectArray();\n this.exData = new XFAObjectArray();\n this.float = new XFAObjectArray();\n this.image = new XFAObjectArray();\n this.integer = new XFAObjectArray();\n this.text = new XFAObjectArray();\n this.time = new XFAObjectArray();\n }\n}\n\nclass DigestMethod extends OptionObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"digestMethod\", [\n \"\",\n \"SHA1\",\n \"SHA256\",\n \"SHA512\",\n \"RIPEMD160\",\n ]);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass DigestMethods extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"digestMethods\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.digestMethod = new XFAObjectArray();\n }\n}\n\nclass Draw extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"draw\", /* hasChildren = */ true);\n this.anchorType = getStringOption(attributes.anchorType, [\n \"topLeft\",\n \"bottomCenter\",\n \"bottomLeft\",\n \"bottomRight\",\n \"middleCenter\",\n \"middleLeft\",\n \"middleRight\",\n \"topCenter\",\n \"topRight\",\n ]);\n this.colSpan = getInteger({\n data: attributes.colSpan,\n defaultValue: 1,\n validate: n => n >= 1 || n === -1,\n });\n this.h = attributes.h ? getMeasurement(attributes.h) : \"\";\n this.hAlign = getStringOption(attributes.hAlign, [\n \"left\",\n \"center\",\n \"justify\",\n \"justifyAll\",\n \"radix\",\n \"right\",\n ]);\n this.id = attributes.id || \"\";\n this.locale = attributes.locale || \"\";\n this.maxH = getMeasurement(attributes.maxH, \"0pt\");\n this.maxW = getMeasurement(attributes.maxW, \"0pt\");\n this.minH = getMeasurement(attributes.minH, \"0pt\");\n this.minW = getMeasurement(attributes.minW, \"0pt\");\n this.name = attributes.name || \"\";\n this.presence = getStringOption(attributes.presence, [\n \"visible\",\n \"hidden\",\n \"inactive\",\n \"invisible\",\n ]);\n this.relevant = getRelevant(attributes.relevant);\n this.rotate = getInteger({\n data: attributes.rotate,\n defaultValue: 0,\n validate: x => x % 90 === 0,\n });\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.w = attributes.w ? getMeasurement(attributes.w) : \"\";\n this.x = getMeasurement(attributes.x, \"0pt\");\n this.y = getMeasurement(attributes.y, \"0pt\");\n this.assist = null;\n this.border = null;\n this.caption = null;\n this.desc = null;\n this.extras = null;\n this.font = null;\n this.keep = null;\n this.margin = null;\n this.para = null;\n this.traversal = null;\n this.ui = null;\n this.value = null;\n this.setProperty = new XFAObjectArray();\n }\n\n [$setValue](value) {\n _setValue(this, value);\n }\n\n [$toHTML](availableSpace) {\n setTabIndex(this);\n\n if (this.presence === \"hidden\" || this.presence === \"inactive\") {\n return HTMLResult.EMPTY;\n }\n\n fixDimensions(this);\n this[$pushPara]();\n\n // If at least one dimension is missing and we've a text\n // then we can guess it in laying out the text.\n const savedW = this.w;\n const savedH = this.h;\n const { w, h, isBroken } = layoutNode(this, availableSpace);\n if (w && this.w === \"\") {\n // If the parent layout is lr-tb with a w=100 and we already have a child\n // which takes 90 on the current line.\n // If we have a text with a length (in px) equal to 100 then it'll be\n // splitted into almost 10 chunks: so it won't be nice.\n // So if we've potentially more width to provide in some parent containers\n // let's increase it to give a chance to have a better rendering.\n if (isBroken && this[$getSubformParent]()[$isThereMoreWidth]()) {\n this[$popPara]();\n return HTMLResult.FAILURE;\n }\n\n this.w = w;\n }\n if (h && this.h === \"\") {\n this.h = h;\n }\n\n setFirstUnsplittable(this);\n if (!checkDimensions(this, availableSpace)) {\n this.w = savedW;\n this.h = savedH;\n this[$popPara]();\n return HTMLResult.FAILURE;\n }\n unsetFirstUnsplittable(this);\n\n const style = toStyle(\n this,\n \"font\",\n \"hAlign\",\n \"dimensions\",\n \"position\",\n \"presence\",\n \"rotate\",\n \"anchorType\",\n \"border\",\n \"margin\"\n );\n\n setMinMaxDimensions(this, style);\n\n if (style.margin) {\n style.padding = style.margin;\n delete style.margin;\n }\n\n const classNames = [\"xfaDraw\"];\n if (this.font) {\n classNames.push(\"xfaFont\");\n }\n if (isPrintOnly(this)) {\n classNames.push(\"xfaPrintOnly\");\n }\n\n const attributes = {\n style,\n id: this[$uid],\n class: classNames,\n };\n\n if (this.name) {\n attributes.xfaName = this.name;\n }\n\n const html = {\n name: \"div\",\n attributes,\n children: [],\n };\n\n applyAssist(this, attributes);\n\n const bbox = computeBbox(this, html, availableSpace);\n\n const value = this.value ? this.value[$toHTML](availableSpace).html : null;\n if (value === null) {\n this.w = savedW;\n this.h = savedH;\n this[$popPara]();\n return HTMLResult.success(createWrapper(this, html), bbox);\n }\n\n html.children.push(value);\n setPara(this, style, value);\n\n this.w = savedW;\n this.h = savedH;\n\n this[$popPara]();\n return HTMLResult.success(createWrapper(this, html), bbox);\n }\n}\n\nclass Edge extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"edge\", /* hasChildren = */ true);\n this.cap = getStringOption(attributes.cap, [\"square\", \"butt\", \"round\"]);\n this.id = attributes.id || \"\";\n this.presence = getStringOption(attributes.presence, [\n \"visible\",\n \"hidden\",\n \"inactive\",\n \"invisible\",\n ]);\n this.stroke = getStringOption(attributes.stroke, [\n \"solid\",\n \"dashDot\",\n \"dashDotDot\",\n \"dashed\",\n \"dotted\",\n \"embossed\",\n \"etched\",\n \"lowered\",\n \"raised\",\n ]);\n this.thickness = getMeasurement(attributes.thickness, \"0.5pt\");\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.color = null;\n this.extras = null;\n }\n\n [$toStyle]() {\n // TODO: dashDot & dashDotDot.\n const style = toStyle(this, \"visibility\");\n Object.assign(style, {\n linecap: this.cap,\n width: measureToString(this.thickness),\n color: this.color ? this.color[$toStyle]() : \"#000000\",\n style: \"\",\n });\n\n if (this.presence !== \"visible\") {\n style.style = \"none\";\n } else {\n switch (this.stroke) {\n case \"solid\":\n style.style = \"solid\";\n break;\n case \"dashDot\":\n style.style = \"dashed\";\n break;\n case \"dashDotDot\":\n style.style = \"dashed\";\n break;\n case \"dashed\":\n style.style = \"dashed\";\n break;\n case \"dotted\":\n style.style = \"dotted\";\n break;\n case \"embossed\":\n style.style = \"ridge\";\n break;\n case \"etched\":\n style.style = \"groove\";\n break;\n case \"lowered\":\n style.style = \"inset\";\n break;\n case \"raised\":\n style.style = \"outset\";\n break;\n }\n }\n return style;\n }\n}\n\nclass Encoding extends OptionObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"encoding\", [\n \"adbe.x509.rsa_sha1\",\n \"adbe.pkcs7.detached\",\n \"adbe.pkcs7.sha1\",\n ]);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Encodings extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"encodings\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.encoding = new XFAObjectArray();\n }\n}\n\nclass Encrypt extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"encrypt\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.certificate = null;\n }\n}\n\nclass EncryptData extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"encryptData\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.operation = getStringOption(attributes.operation, [\n \"encrypt\",\n \"decrypt\",\n ]);\n this.target = attributes.target || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.filter = null;\n this.manifest = null;\n }\n}\n\nclass Encryption extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"encryption\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.certificate = new XFAObjectArray();\n }\n}\n\nclass EncryptionMethod extends OptionObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"encryptionMethod\", [\n \"\",\n \"AES256-CBC\",\n \"TRIPLEDES-CBC\",\n \"AES128-CBC\",\n \"AES192-CBC\",\n ]);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass EncryptionMethods extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"encryptionMethods\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.encryptionMethod = new XFAObjectArray();\n }\n}\n\nclass Event extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"event\", /* hasChildren = */ true);\n this.activity = getStringOption(attributes.activity, [\n \"click\",\n \"change\",\n \"docClose\",\n \"docReady\",\n \"enter\",\n \"exit\",\n \"full\",\n \"indexChange\",\n \"initialize\",\n \"mouseDown\",\n \"mouseEnter\",\n \"mouseExit\",\n \"mouseUp\",\n \"postExecute\",\n \"postOpen\",\n \"postPrint\",\n \"postSave\",\n \"postSign\",\n \"postSubmit\",\n \"preExecute\",\n \"preOpen\",\n \"prePrint\",\n \"preSave\",\n \"preSign\",\n \"preSubmit\",\n \"ready\",\n \"validationState\",\n ]);\n this.id = attributes.id || \"\";\n this.listen = getStringOption(attributes.listen, [\n \"refOnly\",\n \"refAndDescendents\",\n ]);\n this.name = attributes.name || \"\";\n this.ref = attributes.ref || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n\n // One-of properties\n this.encryptData = null;\n this.execute = null;\n this.script = null;\n this.signData = null;\n this.submit = null;\n }\n}\n\nclass ExData extends ContentObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"exData\");\n this.contentType = attributes.contentType || \"\";\n this.href = attributes.href || \"\";\n this.id = attributes.id || \"\";\n this.maxLength = getInteger({\n data: attributes.maxLength,\n defaultValue: -1,\n validate: x => x >= -1,\n });\n this.name = attributes.name || \"\";\n this.rid = attributes.rid || \"\";\n this.transferEncoding = getStringOption(attributes.transferEncoding, [\n \"none\",\n \"base64\",\n \"package\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$isCDATAXml]() {\n return this.contentType === \"text/html\";\n }\n\n [$onChild](child) {\n if (\n this.contentType === \"text/html\" &&\n child[$namespaceId] === NamespaceIds.xhtml.id\n ) {\n this[$content] = child;\n return true;\n }\n\n if (this.contentType === \"text/xml\") {\n this[$content] = child;\n return true;\n }\n\n return false;\n }\n\n [$toHTML](availableSpace) {\n if (this.contentType !== \"text/html\" || !this[$content]) {\n // TODO: fix other cases.\n return HTMLResult.EMPTY;\n }\n\n return this[$content][$toHTML](availableSpace);\n }\n}\n\nclass ExObject extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"exObject\", /* hasChildren = */ true);\n this.archive = attributes.archive || \"\";\n this.classId = attributes.classId || \"\";\n this.codeBase = attributes.codeBase || \"\";\n this.codeType = attributes.codeType || \"\";\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.boolean = new XFAObjectArray();\n this.date = new XFAObjectArray();\n this.dateTime = new XFAObjectArray();\n this.decimal = new XFAObjectArray();\n this.exData = new XFAObjectArray();\n this.exObject = new XFAObjectArray();\n this.float = new XFAObjectArray();\n this.image = new XFAObjectArray();\n this.integer = new XFAObjectArray();\n this.text = new XFAObjectArray();\n this.time = new XFAObjectArray();\n }\n}\n\nclass ExclGroup extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"exclGroup\", /* hasChildren = */ true);\n this.access = getStringOption(attributes.access, [\n \"open\",\n \"nonInteractive\",\n \"protected\",\n \"readOnly\",\n ]);\n this.accessKey = attributes.accessKey || \"\";\n this.anchorType = getStringOption(attributes.anchorType, [\n \"topLeft\",\n \"bottomCenter\",\n \"bottomLeft\",\n \"bottomRight\",\n \"middleCenter\",\n \"middleLeft\",\n \"middleRight\",\n \"topCenter\",\n \"topRight\",\n ]);\n this.colSpan = getInteger({\n data: attributes.colSpan,\n defaultValue: 1,\n validate: n => n >= 1 || n === -1,\n });\n this.h = attributes.h ? getMeasurement(attributes.h) : \"\";\n this.hAlign = getStringOption(attributes.hAlign, [\n \"left\",\n \"center\",\n \"justify\",\n \"justifyAll\",\n \"radix\",\n \"right\",\n ]);\n this.id = attributes.id || \"\";\n this.layout = getStringOption(attributes.layout, [\n \"position\",\n \"lr-tb\",\n \"rl-row\",\n \"rl-tb\",\n \"row\",\n \"table\",\n \"tb\",\n ]);\n this.maxH = getMeasurement(attributes.maxH, \"0pt\");\n this.maxW = getMeasurement(attributes.maxW, \"0pt\");\n this.minH = getMeasurement(attributes.minH, \"0pt\");\n this.minW = getMeasurement(attributes.minW, \"0pt\");\n this.name = attributes.name || \"\";\n this.presence = getStringOption(attributes.presence, [\n \"visible\",\n \"hidden\",\n \"inactive\",\n \"invisible\",\n ]);\n this.relevant = getRelevant(attributes.relevant);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.w = attributes.w ? getMeasurement(attributes.w) : \"\";\n this.x = getMeasurement(attributes.x, \"0pt\");\n this.y = getMeasurement(attributes.y, \"0pt\");\n this.assist = null;\n this.bind = null;\n this.border = null;\n this.calculate = null;\n this.caption = null;\n this.desc = null;\n this.extras = null;\n this.margin = null;\n this.para = null;\n this.traversal = null;\n this.validate = null;\n this.connect = new XFAObjectArray();\n this.event = new XFAObjectArray();\n this.field = new XFAObjectArray();\n this.setProperty = new XFAObjectArray();\n }\n\n [$isBindable]() {\n return true;\n }\n\n [$hasSettableValue]() {\n return true;\n }\n\n [$setValue](value) {\n for (const field of this.field.children) {\n if (!field.value) {\n const nodeValue = new Value({});\n field[$appendChild](nodeValue);\n field.value = nodeValue;\n }\n\n field.value[$setValue](value);\n }\n }\n\n [$isThereMoreWidth]() {\n return (\n (this.layout.endsWith(\"-tb\") &&\n this[$extra].attempt === 0 &&\n this[$extra].numberInLine > 0) ||\n this[$getParent]()[$isThereMoreWidth]()\n );\n }\n\n [$isSplittable]() {\n // We cannot cache the result here because the contentArea\n // can change.\n const parent = this[$getSubformParent]();\n if (!parent[$isSplittable]()) {\n return false;\n }\n\n if (this[$extra]._isSplittable !== undefined) {\n return this[$extra]._isSplittable;\n }\n\n if (this.layout === \"position\" || this.layout.includes(\"row\")) {\n this[$extra]._isSplittable = false;\n return false;\n }\n\n if (parent.layout?.endsWith(\"-tb\") && parent[$extra].numberInLine !== 0) {\n // See comment in Subform::[$isSplittable] for an explanation.\n return false;\n }\n\n this[$extra]._isSplittable = true;\n return true;\n }\n\n [$flushHTML]() {\n return flushHTML(this);\n }\n\n [$addHTML](html, bbox) {\n addHTML(this, html, bbox);\n }\n\n [$getAvailableSpace]() {\n return getAvailableSpace(this);\n }\n\n [$toHTML](availableSpace) {\n setTabIndex(this);\n if (\n this.presence === \"hidden\" ||\n this.presence === \"inactive\" ||\n this.h === 0 ||\n this.w === 0\n ) {\n return HTMLResult.EMPTY;\n }\n\n fixDimensions(this);\n\n const children = [];\n const attributes = {\n id: this[$uid],\n class: [],\n };\n\n setAccess(this, attributes.class);\n\n if (!this[$extra]) {\n this[$extra] = Object.create(null);\n }\n\n Object.assign(this[$extra], {\n children,\n attributes,\n attempt: 0,\n line: null,\n numberInLine: 0,\n availableSpace: {\n width: Math.min(this.w || Infinity, availableSpace.width),\n height: Math.min(this.h || Infinity, availableSpace.height),\n },\n width: 0,\n height: 0,\n prevHeight: 0,\n currentWidth: 0,\n });\n\n const isSplittable = this[$isSplittable]();\n if (!isSplittable) {\n setFirstUnsplittable(this);\n }\n\n if (!checkDimensions(this, availableSpace)) {\n return HTMLResult.FAILURE;\n }\n const filter = new Set([\"field\"]);\n\n if (this.layout.includes(\"row\")) {\n const columnWidths = this[$getSubformParent]().columnWidths;\n if (Array.isArray(columnWidths) && columnWidths.length > 0) {\n this[$extra].columnWidths = columnWidths;\n this[$extra].currentColumn = 0;\n }\n }\n\n const style = toStyle(\n this,\n \"anchorType\",\n \"dimensions\",\n \"position\",\n \"presence\",\n \"border\",\n \"margin\",\n \"hAlign\"\n );\n const classNames = [\"xfaExclgroup\"];\n const cl = layoutClass(this);\n if (cl) {\n classNames.push(cl);\n }\n\n if (isPrintOnly(this)) {\n classNames.push(\"xfaPrintOnly\");\n }\n\n attributes.style = style;\n attributes.class = classNames;\n\n if (this.name) {\n attributes.xfaName = this.name;\n }\n\n this[$pushPara]();\n const isLrTb = this.layout === \"lr-tb\" || this.layout === \"rl-tb\";\n const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1;\n for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {\n if (isLrTb && this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) {\n // If the layout is lr-tb then having attempt equals to\n // MAX_ATTEMPTS_FOR_LRTB_LAYOUT-1 means that we're trying to layout\n // on the next line so this on is empty.\n this[$extra].numberInLine = 0;\n }\n const result = this[$childrenToHTML]({\n filter,\n include: true,\n });\n if (result.success) {\n break;\n }\n if (result.isBreak()) {\n this[$popPara]();\n return result;\n }\n if (\n isLrTb &&\n this[$extra].attempt === 0 &&\n this[$extra].numberInLine === 0 &&\n !this[$getTemplateRoot]()[$extra].noLayoutFailure\n ) {\n // See comment in Subform::[$toHTML].\n this[$extra].attempt = maxRun;\n break;\n }\n }\n\n this[$popPara]();\n\n if (!isSplittable) {\n unsetFirstUnsplittable(this);\n }\n\n if (this[$extra].attempt === maxRun) {\n if (!isSplittable) {\n delete this[$extra];\n }\n return HTMLResult.FAILURE;\n }\n\n let marginH = 0;\n let marginV = 0;\n if (this.margin) {\n marginH = this.margin.leftInset + this.margin.rightInset;\n marginV = this.margin.topInset + this.margin.bottomInset;\n }\n\n const width = Math.max(this[$extra].width + marginH, this.w || 0);\n const height = Math.max(this[$extra].height + marginV, this.h || 0);\n const bbox = [this.x, this.y, width, height];\n\n if (this.w === \"\") {\n style.width = measureToString(width);\n }\n if (this.h === \"\") {\n style.height = measureToString(height);\n }\n\n const html = {\n name: \"div\",\n attributes,\n children,\n };\n\n applyAssist(this, attributes);\n\n delete this[$extra];\n\n return HTMLResult.success(createWrapper(this, html), bbox);\n }\n}\n\nclass Execute extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"execute\");\n this.connection = attributes.connection || \"\";\n this.executeType = getStringOption(attributes.executeType, [\n \"import\",\n \"remerge\",\n ]);\n this.id = attributes.id || \"\";\n this.runAt = getStringOption(attributes.runAt, [\n \"client\",\n \"both\",\n \"server\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Extras extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"extras\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.boolean = new XFAObjectArray();\n this.date = new XFAObjectArray();\n this.dateTime = new XFAObjectArray();\n this.decimal = new XFAObjectArray();\n this.exData = new XFAObjectArray();\n this.extras = new XFAObjectArray();\n this.float = new XFAObjectArray();\n this.image = new XFAObjectArray();\n this.integer = new XFAObjectArray();\n this.text = new XFAObjectArray();\n this.time = new XFAObjectArray();\n }\n\n // (Spec) The XFA template grammar defines the extras and desc elements,\n // which can be used to add human-readable or machine-readable\n // data to a template.\n}\n\nclass Field extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"field\", /* hasChildren = */ true);\n this.access = getStringOption(attributes.access, [\n \"open\",\n \"nonInteractive\",\n \"protected\",\n \"readOnly\",\n ]);\n this.accessKey = attributes.accessKey || \"\";\n this.anchorType = getStringOption(attributes.anchorType, [\n \"topLeft\",\n \"bottomCenter\",\n \"bottomLeft\",\n \"bottomRight\",\n \"middleCenter\",\n \"middleLeft\",\n \"middleRight\",\n \"topCenter\",\n \"topRight\",\n ]);\n this.colSpan = getInteger({\n data: attributes.colSpan,\n defaultValue: 1,\n validate: n => n >= 1 || n === -1,\n });\n this.h = attributes.h ? getMeasurement(attributes.h) : \"\";\n this.hAlign = getStringOption(attributes.hAlign, [\n \"left\",\n \"center\",\n \"justify\",\n \"justifyAll\",\n \"radix\",\n \"right\",\n ]);\n this.id = attributes.id || \"\";\n this.locale = attributes.locale || \"\";\n this.maxH = getMeasurement(attributes.maxH, \"0pt\");\n this.maxW = getMeasurement(attributes.maxW, \"0pt\");\n this.minH = getMeasurement(attributes.minH, \"0pt\");\n this.minW = getMeasurement(attributes.minW, \"0pt\");\n this.name = attributes.name || \"\";\n this.presence = getStringOption(attributes.presence, [\n \"visible\",\n \"hidden\",\n \"inactive\",\n \"invisible\",\n ]);\n this.relevant = getRelevant(attributes.relevant);\n this.rotate = getInteger({\n data: attributes.rotate,\n defaultValue: 0,\n validate: x => x % 90 === 0,\n });\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.w = attributes.w ? getMeasurement(attributes.w) : \"\";\n this.x = getMeasurement(attributes.x, \"0pt\");\n this.y = getMeasurement(attributes.y, \"0pt\");\n this.assist = null;\n this.bind = null;\n this.border = null;\n this.calculate = null;\n this.caption = null;\n this.desc = null;\n this.extras = null;\n this.font = null;\n this.format = null;\n // For a choice list, one list is used to have display entries\n // and the other for the exported values\n this.items = new XFAObjectArray(2);\n this.keep = null;\n this.margin = null;\n this.para = null;\n this.traversal = null;\n this.ui = null;\n this.validate = null;\n this.value = null;\n this.bindItems = new XFAObjectArray();\n this.connect = new XFAObjectArray();\n this.event = new XFAObjectArray();\n this.setProperty = new XFAObjectArray();\n }\n\n [$isBindable]() {\n return true;\n }\n\n [$setValue](value) {\n _setValue(this, value);\n }\n\n [$toHTML](availableSpace) {\n setTabIndex(this);\n\n if (!this.ui) {\n // It's allowed to not have an ui, specs say:\n // If the UI element contains no children or is not present,\n // the application chooses a default user interface for the\n // container, based on the type of the container's content.\n\n this.ui = new Ui({});\n this.ui[$globalData] = this[$globalData];\n this[$appendChild](this.ui);\n let node;\n\n // The items element can have 2 element max and\n // according to the items specs it's likely a good\n // way to guess the correct ui type.\n switch (this.items.children.length) {\n case 0:\n node = new TextEdit({});\n this.ui.textEdit = node;\n break;\n case 1:\n node = new CheckButton({});\n this.ui.checkButton = node;\n break;\n case 2:\n node = new ChoiceList({});\n this.ui.choiceList = node;\n break;\n }\n this.ui[$appendChild](node);\n }\n\n if (\n !this.ui ||\n this.presence === \"hidden\" ||\n this.presence === \"inactive\" ||\n this.h === 0 ||\n this.w === 0\n ) {\n return HTMLResult.EMPTY;\n }\n\n if (this.caption) {\n // Maybe we already tried to layout this field with\n // another availableSpace, so to avoid to use the cached\n // value just delete it.\n delete this.caption[$extra];\n }\n\n this[$pushPara]();\n\n const caption = this.caption\n ? this.caption[$toHTML](availableSpace).html\n : null;\n const savedW = this.w;\n const savedH = this.h;\n let marginH = 0;\n let marginV = 0;\n if (this.margin) {\n marginH = this.margin.leftInset + this.margin.rightInset;\n marginV = this.margin.topInset + this.margin.bottomInset;\n }\n\n let borderDims = null;\n if (this.w === \"\" || this.h === \"\") {\n let width = null;\n let height = null;\n\n let uiW = 0;\n let uiH = 0;\n if (this.ui.checkButton) {\n uiW = uiH = this.ui.checkButton.size;\n } else {\n const { w, h } = layoutNode(this, availableSpace);\n if (w !== null) {\n uiW = w;\n uiH = h;\n } else {\n uiH = getMetrics(this.font, /* real = */ true).lineNoGap;\n }\n }\n\n borderDims = getBorderDims(this.ui[$getExtra]());\n uiW += borderDims.w;\n uiH += borderDims.h;\n\n if (this.caption) {\n const { w, h, isBroken } = this.caption[$getExtra](availableSpace);\n // See comment in Draw::[$toHTML] to have an explanation\n // about this line.\n if (isBroken && this[$getSubformParent]()[$isThereMoreWidth]()) {\n this[$popPara]();\n return HTMLResult.FAILURE;\n }\n\n width = w;\n height = h;\n\n switch (this.caption.placement) {\n case \"left\":\n case \"right\":\n case \"inline\":\n width += uiW;\n break;\n case \"top\":\n case \"bottom\":\n height += uiH;\n break;\n }\n } else {\n width = uiW;\n height = uiH;\n }\n\n if (width && this.w === \"\") {\n width += marginH;\n this.w = Math.min(\n this.maxW <= 0 ? Infinity : this.maxW,\n this.minW + 1 < width ? width : this.minW\n );\n }\n\n if (height && this.h === \"\") {\n height += marginV;\n this.h = Math.min(\n this.maxH <= 0 ? Infinity : this.maxH,\n this.minH + 1 < height ? height : this.minH\n );\n }\n }\n\n this[$popPara]();\n\n fixDimensions(this);\n\n setFirstUnsplittable(this);\n if (!checkDimensions(this, availableSpace)) {\n this.w = savedW;\n this.h = savedH;\n this[$popPara]();\n return HTMLResult.FAILURE;\n }\n unsetFirstUnsplittable(this);\n\n const style = toStyle(\n this,\n \"font\",\n \"dimensions\",\n \"position\",\n \"rotate\",\n \"anchorType\",\n \"presence\",\n \"margin\",\n \"hAlign\"\n );\n\n setMinMaxDimensions(this, style);\n\n const classNames = [\"xfaField\"];\n // If no font, font properties are inherited.\n if (this.font) {\n classNames.push(\"xfaFont\");\n }\n\n if (isPrintOnly(this)) {\n classNames.push(\"xfaPrintOnly\");\n }\n\n const attributes = {\n style,\n id: this[$uid],\n class: classNames,\n };\n\n if (style.margin) {\n style.padding = style.margin;\n delete style.margin;\n }\n\n setAccess(this, classNames);\n\n if (this.name) {\n attributes.xfaName = this.name;\n }\n\n const children = [];\n const html = {\n name: \"div\",\n attributes,\n children,\n };\n\n applyAssist(this, attributes);\n\n const borderStyle = this.border ? this.border[$toStyle]() : null;\n const bbox = computeBbox(this, html, availableSpace);\n const ui = this.ui[$toHTML]().html;\n if (!ui) {\n Object.assign(style, borderStyle);\n return HTMLResult.success(createWrapper(this, html), bbox);\n }\n\n if (this[$tabIndex]) {\n if (ui.children?.[0]) {\n ui.children[0].attributes.tabindex = this[$tabIndex];\n } else {\n ui.attributes.tabindex = this[$tabIndex];\n }\n }\n\n if (!ui.attributes.style) {\n ui.attributes.style = Object.create(null);\n }\n\n let aElement = null;\n\n if (this.ui.button) {\n if (ui.children.length === 1) {\n [aElement] = ui.children.splice(0, 1);\n }\n Object.assign(ui.attributes.style, borderStyle);\n } else {\n Object.assign(style, borderStyle);\n }\n\n children.push(ui);\n\n if (this.value) {\n if (this.ui.imageEdit) {\n ui.children.push(this.value[$toHTML]().html);\n } else if (!this.ui.button) {\n let value = \"\";\n if (this.value.exData) {\n value = this.value.exData[$text]();\n } else if (this.value.text) {\n value = this.value.text[$getExtra]();\n } else {\n const htmlValue = this.value[$toHTML]().html;\n if (htmlValue !== null) {\n value = htmlValue.children[0].value;\n }\n }\n if (this.ui.textEdit && this.value.text?.maxChars) {\n ui.children[0].attributes.maxLength = this.value.text.maxChars;\n }\n\n if (value) {\n if (this.ui.numericEdit) {\n value = parseFloat(value);\n value = isNaN(value) ? \"\" : value.toString();\n }\n\n if (ui.children[0].name === \"textarea\") {\n ui.children[0].attributes.textContent = value;\n } else {\n ui.children[0].attributes.value = value;\n }\n }\n }\n }\n\n if (!this.ui.imageEdit && ui.children?.[0] && this.h) {\n borderDims = borderDims || getBorderDims(this.ui[$getExtra]());\n\n let captionHeight = 0;\n if (this.caption && [\"top\", \"bottom\"].includes(this.caption.placement)) {\n captionHeight = this.caption.reserve;\n if (captionHeight <= 0) {\n captionHeight = this.caption[$getExtra](availableSpace).h;\n }\n const inputHeight = this.h - captionHeight - marginV - borderDims.h;\n ui.children[0].attributes.style.height = measureToString(inputHeight);\n } else {\n ui.children[0].attributes.style.height = \"100%\";\n }\n }\n\n if (aElement) {\n ui.children.push(aElement);\n }\n\n if (!caption) {\n if (ui.attributes.class) {\n // Even if no caption this class will help to center the ui.\n ui.attributes.class.push(\"xfaLeft\");\n }\n this.w = savedW;\n this.h = savedH;\n\n return HTMLResult.success(createWrapper(this, html), bbox);\n }\n\n if (this.ui.button) {\n if (style.padding) {\n delete style.padding;\n }\n if (caption.name === \"div\") {\n caption.name = \"span\";\n }\n ui.children.push(caption);\n return HTMLResult.success(html, bbox);\n } else if (this.ui.checkButton) {\n caption.attributes.class[0] = \"xfaCaptionForCheckButton\";\n }\n\n if (!ui.attributes.class) {\n ui.attributes.class = [];\n }\n\n ui.children.splice(0, 0, caption);\n\n switch (this.caption.placement) {\n case \"left\":\n ui.attributes.class.push(\"xfaLeft\");\n break;\n case \"right\":\n ui.attributes.class.push(\"xfaRight\");\n break;\n case \"top\":\n ui.attributes.class.push(\"xfaTop\");\n break;\n case \"bottom\":\n ui.attributes.class.push(\"xfaBottom\");\n break;\n case \"inline\":\n // TODO;\n ui.attributes.class.push(\"xfaLeft\");\n break;\n }\n\n this.w = savedW;\n this.h = savedH;\n return HTMLResult.success(createWrapper(this, html), bbox);\n }\n}\n\nclass Fill extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"fill\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.presence = getStringOption(attributes.presence, [\n \"visible\",\n \"hidden\",\n \"inactive\",\n \"invisible\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.color = null;\n this.extras = null;\n\n // One-of properties or none\n this.linear = null;\n this.pattern = null;\n this.radial = null;\n this.solid = null;\n this.stipple = null;\n }\n\n [$toStyle]() {\n const parent = this[$getParent]();\n const grandpa = parent[$getParent]();\n const ggrandpa = grandpa[$getParent]();\n const style = Object.create(null);\n\n // Use for color, i.e. #...\n let propName = \"color\";\n\n // Use for non-color, i.e. gradient, radial-gradient...\n let altPropName = propName;\n\n if (parent instanceof Border) {\n propName = \"background-color\";\n altPropName = \"background\";\n if (ggrandpa instanceof Ui) {\n // The default fill color is white.\n style.backgroundColor = \"white\";\n }\n }\n if (parent instanceof Rectangle || parent instanceof Arc) {\n propName = altPropName = \"fill\";\n style.fill = \"white\";\n }\n\n for (const name of Object.getOwnPropertyNames(this)) {\n if (name === \"extras\" || name === \"color\") {\n continue;\n }\n const obj = this[name];\n if (!(obj instanceof XFAObject)) {\n continue;\n }\n\n const color = obj[$toStyle](this.color);\n if (color) {\n style[color.startsWith(\"#\") ? propName : altPropName] = color;\n }\n return style;\n }\n\n if (this.color?.value) {\n const color = this.color[$toStyle]();\n style[color.startsWith(\"#\") ? propName : altPropName] = color;\n }\n\n return style;\n }\n}\n\nclass Filter extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"filter\", /* hasChildren = */ true);\n this.addRevocationInfo = getStringOption(attributes.addRevocationInfo, [\n \"\",\n \"required\",\n \"optional\",\n \"none\",\n ]);\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.version = getInteger({\n data: this.version,\n defaultValue: 5,\n validate: x => x >= 1 && x <= 5,\n });\n this.appearanceFilter = null;\n this.certificates = null;\n this.digestMethods = null;\n this.encodings = null;\n this.encryptionMethods = null;\n this.handler = null;\n this.lockDocument = null;\n this.mdp = null;\n this.reasons = null;\n this.timeStamp = null;\n }\n}\n\nclass Float extends ContentObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"float\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$finalize]() {\n const number = parseFloat(this[$content].trim());\n this[$content] = isNaN(number) ? null : number;\n }\n\n [$toHTML](availableSpace) {\n return valueToHtml(\n this[$content] !== null ? this[$content].toString() : \"\"\n );\n }\n}\n\nclass Font extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"font\", /* hasChildren = */ true);\n this.baselineShift = getMeasurement(attributes.baselineShift);\n this.fontHorizontalScale = getFloat({\n data: attributes.fontHorizontalScale,\n defaultValue: 100,\n validate: x => x >= 0,\n });\n this.fontVerticalScale = getFloat({\n data: attributes.fontVerticalScale,\n defaultValue: 100,\n validate: x => x >= 0,\n });\n this.id = attributes.id || \"\";\n this.kerningMode = getStringOption(attributes.kerningMode, [\n \"none\",\n \"pair\",\n ]);\n this.letterSpacing = getMeasurement(attributes.letterSpacing, \"0\");\n this.lineThrough = getInteger({\n data: attributes.lineThrough,\n defaultValue: 0,\n validate: x => x === 1 || x === 2,\n });\n this.lineThroughPeriod = getStringOption(attributes.lineThroughPeriod, [\n \"all\",\n \"word\",\n ]);\n this.overline = getInteger({\n data: attributes.overline,\n defaultValue: 0,\n validate: x => x === 1 || x === 2,\n });\n this.overlinePeriod = getStringOption(attributes.overlinePeriod, [\n \"all\",\n \"word\",\n ]);\n this.posture = getStringOption(attributes.posture, [\"normal\", \"italic\"]);\n this.size = getMeasurement(attributes.size, \"10pt\");\n this.typeface = attributes.typeface || \"Courier\";\n this.underline = getInteger({\n data: attributes.underline,\n defaultValue: 0,\n validate: x => x === 1 || x === 2,\n });\n this.underlinePeriod = getStringOption(attributes.underlinePeriod, [\n \"all\",\n \"word\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.weight = getStringOption(attributes.weight, [\"normal\", \"bold\"]);\n this.extras = null;\n this.fill = null;\n }\n\n [$clean](builder) {\n super[$clean](builder);\n this[$globalData].usedTypefaces.add(this.typeface);\n }\n\n [$toStyle]() {\n const style = toStyle(this, \"fill\");\n const color = style.color;\n if (color) {\n if (color === \"#000000\") {\n // Default font color.\n delete style.color;\n } else if (!color.startsWith(\"#\")) {\n // We've a gradient which is not possible for a font color\n // so use a workaround.\n style.background = color;\n style.backgroundClip = \"text\";\n style.color = \"transparent\";\n }\n }\n\n if (this.baselineShift) {\n style.verticalAlign = measureToString(this.baselineShift);\n }\n\n // TODO: fontHorizontalScale\n // TODO: fontVerticalScale\n\n style.fontKerning = this.kerningMode === \"none\" ? \"none\" : \"normal\";\n style.letterSpacing = measureToString(this.letterSpacing);\n\n if (this.lineThrough !== 0) {\n style.textDecoration = \"line-through\";\n if (this.lineThrough === 2) {\n style.textDecorationStyle = \"double\";\n }\n }\n\n // TODO: lineThroughPeriod\n\n if (this.overline !== 0) {\n style.textDecoration = \"overline\";\n if (this.overline === 2) {\n style.textDecorationStyle = \"double\";\n }\n }\n\n // TODO: overlinePeriod\n\n style.fontStyle = this.posture;\n style.fontSize = measureToString(0.99 * this.size);\n\n setFontFamily(this, this, this[$globalData].fontFinder, style);\n\n if (this.underline !== 0) {\n style.textDecoration = \"underline\";\n if (this.underline === 2) {\n style.textDecorationStyle = \"double\";\n }\n }\n\n // TODO: underlinePeriod\n\n style.fontWeight = this.weight;\n\n return style;\n }\n}\n\nclass Format extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"format\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.picture = null;\n }\n}\n\nclass Handler extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"handler\");\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Hyphenation extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"hyphenation\");\n this.excludeAllCaps = getInteger({\n data: attributes.excludeAllCaps,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.excludeInitialCap = getInteger({\n data: attributes.excludeInitialCap,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.hyphenate = getInteger({\n data: attributes.hyphenate,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.id = attributes.id || \"\";\n this.pushCharacterCount = getInteger({\n data: attributes.pushCharacterCount,\n defaultValue: 3,\n validate: x => x >= 0,\n });\n this.remainCharacterCount = getInteger({\n data: attributes.remainCharacterCount,\n defaultValue: 3,\n validate: x => x >= 0,\n });\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.wordCharacterCount = getInteger({\n data: attributes.wordCharacterCount,\n defaultValue: 7,\n validate: x => x >= 0,\n });\n }\n}\n\nclass Image extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"image\");\n this.aspect = getStringOption(attributes.aspect, [\n \"fit\",\n \"actual\",\n \"height\",\n \"none\",\n \"width\",\n ]);\n this.contentType = attributes.contentType || \"\";\n this.href = attributes.href || \"\";\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.transferEncoding = getStringOption(attributes.transferEncoding, [\n \"base64\",\n \"none\",\n \"package\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$toHTML]() {\n if (this.contentType && !MIMES.has(this.contentType.toLowerCase())) {\n return HTMLResult.EMPTY;\n }\n\n let buffer =\n this[$globalData].images && this[$globalData].images.get(this.href);\n if (!buffer && (this.href || !this[$content])) {\n // In general, we don't get remote data and use what we have\n // in the pdf itself, so no picture for non null href.\n return HTMLResult.EMPTY;\n }\n\n if (!buffer && this.transferEncoding === \"base64\") {\n buffer = stringToBytes(atob(this[$content]));\n }\n\n if (!buffer) {\n return HTMLResult.EMPTY;\n }\n\n if (!this.contentType) {\n for (const [header, type] of IMAGES_HEADERS) {\n if (\n buffer.length > header.length &&\n header.every((x, i) => x === buffer[i])\n ) {\n this.contentType = type;\n break;\n }\n }\n if (!this.contentType) {\n return HTMLResult.EMPTY;\n }\n }\n\n // TODO: Firefox doesn't support natively tiff (and tif) format.\n const blob = new Blob([buffer], { type: this.contentType });\n let style;\n switch (this.aspect) {\n case \"fit\":\n case \"actual\":\n // TODO: check what to do with actual.\n // Normally we should return {auto, auto} for it but\n // it implies some wrong rendering (see xfa_bug1716816.pdf).\n break;\n case \"height\":\n style = {\n height: \"100%\",\n objectFit: \"fill\",\n };\n break;\n case \"none\":\n style = {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"fill\",\n };\n break;\n case \"width\":\n style = {\n width: \"100%\",\n objectFit: \"fill\",\n };\n break;\n }\n const parent = this[$getParent]();\n return HTMLResult.success({\n name: \"img\",\n attributes: {\n class: [\"xfaImage\"],\n style,\n src: URL.createObjectURL(blob),\n alt: parent ? ariaLabel(parent[$getParent]()) : null,\n },\n });\n }\n}\n\nclass ImageEdit extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"imageEdit\", /* hasChildren = */ true);\n this.data = getStringOption(attributes.data, [\"link\", \"embed\"]);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.border = null;\n this.extras = null;\n this.margin = null;\n }\n\n [$toHTML](availableSpace) {\n if (this.data === \"embed\") {\n return HTMLResult.success({\n name: \"div\",\n children: [],\n attributes: {},\n });\n }\n\n return HTMLResult.EMPTY;\n }\n}\n\nclass Integer extends ContentObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"integer\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$finalize]() {\n const number = parseInt(this[$content].trim(), 10);\n this[$content] = isNaN(number) ? null : number;\n }\n\n [$toHTML](availableSpace) {\n return valueToHtml(\n this[$content] !== null ? this[$content].toString() : \"\"\n );\n }\n}\n\nclass Issuers extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"issuers\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.certificate = new XFAObjectArray();\n }\n}\n\nclass Items extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"items\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.presence = getStringOption(attributes.presence, [\n \"visible\",\n \"hidden\",\n \"inactive\",\n \"invisible\",\n ]);\n this.ref = attributes.ref || \"\";\n this.save = getInteger({\n data: attributes.save,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.boolean = new XFAObjectArray();\n this.date = new XFAObjectArray();\n this.dateTime = new XFAObjectArray();\n this.decimal = new XFAObjectArray();\n this.exData = new XFAObjectArray();\n this.float = new XFAObjectArray();\n this.image = new XFAObjectArray();\n this.integer = new XFAObjectArray();\n this.text = new XFAObjectArray();\n this.time = new XFAObjectArray();\n }\n\n [$toHTML]() {\n const output = [];\n for (const child of this[$getChildren]()) {\n output.push(child[$text]());\n }\n return HTMLResult.success(output);\n }\n}\n\nclass Keep extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"keep\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n const options = [\"none\", \"contentArea\", \"pageArea\"];\n this.intact = getStringOption(attributes.intact, options);\n this.next = getStringOption(attributes.next, options);\n this.previous = getStringOption(attributes.previous, options);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n }\n}\n\nclass KeyUsage extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"keyUsage\");\n const options = [\"\", \"yes\", \"no\"];\n this.crlSign = getStringOption(attributes.crlSign, options);\n this.dataEncipherment = getStringOption(\n attributes.dataEncipherment,\n options\n );\n this.decipherOnly = getStringOption(attributes.decipherOnly, options);\n this.digitalSignature = getStringOption(\n attributes.digitalSignature,\n options\n );\n this.encipherOnly = getStringOption(attributes.encipherOnly, options);\n this.id = attributes.id || \"\";\n this.keyAgreement = getStringOption(attributes.keyAgreement, options);\n this.keyCertSign = getStringOption(attributes.keyCertSign, options);\n this.keyEncipherment = getStringOption(attributes.keyEncipherment, options);\n this.nonRepudiation = getStringOption(attributes.nonRepudiation, options);\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Line extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"line\", /* hasChildren = */ true);\n this.hand = getStringOption(attributes.hand, [\"even\", \"left\", \"right\"]);\n this.id = attributes.id || \"\";\n this.slope = getStringOption(attributes.slope, [\"\\\\\", \"/\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.edge = null;\n }\n\n [$toHTML]() {\n const parent = this[$getParent]()[$getParent]();\n const edge = this.edge || new Edge({});\n const edgeStyle = edge[$toStyle]();\n const style = Object.create(null);\n const thickness = edge.presence === \"visible\" ? edge.thickness : 0;\n style.strokeWidth = measureToString(thickness);\n style.stroke = edgeStyle.color;\n let x1, y1, x2, y2;\n let width = \"100%\";\n let height = \"100%\";\n\n if (parent.w <= thickness) {\n [x1, y1, x2, y2] = [\"50%\", 0, \"50%\", \"100%\"];\n width = style.strokeWidth;\n } else if (parent.h <= thickness) {\n [x1, y1, x2, y2] = [0, \"50%\", \"100%\", \"50%\"];\n height = style.strokeWidth;\n } else if (this.slope === \"\\\\\") {\n [x1, y1, x2, y2] = [0, 0, \"100%\", \"100%\"];\n } else {\n [x1, y1, x2, y2] = [0, \"100%\", \"100%\", 0];\n }\n\n const line = {\n name: \"line\",\n attributes: {\n xmlns: SVG_NS,\n x1,\n y1,\n x2,\n y2,\n style,\n },\n };\n\n const svg = {\n name: \"svg\",\n children: [line],\n attributes: {\n xmlns: SVG_NS,\n width,\n height,\n style: {\n overflow: \"visible\",\n },\n },\n };\n\n if (hasMargin(parent)) {\n return HTMLResult.success({\n name: \"div\",\n attributes: {\n style: {\n display: \"inline\",\n width: \"100%\",\n height: \"100%\",\n },\n },\n children: [svg],\n });\n }\n\n svg.attributes.style.position = \"absolute\";\n return HTMLResult.success(svg);\n }\n}\n\nclass Linear extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"linear\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\n \"toRight\",\n \"toBottom\",\n \"toLeft\",\n \"toTop\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.color = null;\n this.extras = null;\n }\n\n [$toStyle](startColor) {\n startColor = startColor ? startColor[$toStyle]() : \"#FFFFFF\";\n const transf = this.type.replace(/([RBLT])/, \" $1\").toLowerCase();\n const endColor = this.color ? this.color[$toStyle]() : \"#000000\";\n return `linear-gradient(${transf}, ${startColor}, ${endColor})`;\n }\n}\n\nclass LockDocument extends ContentObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"lockDocument\");\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$finalize]() {\n this[$content] = getStringOption(this[$content], [\"auto\", \"0\", \"1\"]);\n }\n}\n\nclass Manifest extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"manifest\", /* hasChildren = */ true);\n this.action = getStringOption(attributes.action, [\n \"include\",\n \"all\",\n \"exclude\",\n ]);\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.ref = new XFAObjectArray();\n }\n}\n\nclass Margin extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"margin\", /* hasChildren = */ true);\n this.bottomInset = getMeasurement(attributes.bottomInset, \"0\");\n this.id = attributes.id || \"\";\n this.leftInset = getMeasurement(attributes.leftInset, \"0\");\n this.rightInset = getMeasurement(attributes.rightInset, \"0\");\n this.topInset = getMeasurement(attributes.topInset, \"0\");\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n }\n\n [$toStyle]() {\n return {\n margin:\n measureToString(this.topInset) +\n \" \" +\n measureToString(this.rightInset) +\n \" \" +\n measureToString(this.bottomInset) +\n \" \" +\n measureToString(this.leftInset),\n };\n }\n}\n\nclass Mdp extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"mdp\");\n this.id = attributes.id || \"\";\n this.permissions = getInteger({\n data: attributes.permissions,\n defaultValue: 2,\n validate: x => x === 1 || x === 3,\n });\n this.signatureType = getStringOption(attributes.signatureType, [\n \"filler\",\n \"author\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Medium extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"medium\");\n this.id = attributes.id || \"\";\n this.imagingBBox = getBBox(attributes.imagingBBox);\n this.long = getMeasurement(attributes.long);\n this.orientation = getStringOption(attributes.orientation, [\n \"portrait\",\n \"landscape\",\n ]);\n this.short = getMeasurement(attributes.short);\n this.stock = attributes.stock || \"\";\n this.trayIn = getStringOption(attributes.trayIn, [\n \"auto\",\n \"delegate\",\n \"pageFront\",\n ]);\n this.trayOut = getStringOption(attributes.trayOut, [\"auto\", \"delegate\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Message extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"message\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.text = new XFAObjectArray();\n }\n}\n\nclass NumericEdit extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"numericEdit\", /* hasChildren = */ true);\n this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, [\n \"auto\",\n \"off\",\n \"on\",\n ]);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.border = null;\n this.comb = null;\n this.extras = null;\n this.margin = null;\n }\n\n [$toHTML](availableSpace) {\n // TODO: incomplete.\n const style = toStyle(this, \"border\", \"font\", \"margin\");\n const field = this[$getParent]()[$getParent]();\n const html = {\n name: \"input\",\n attributes: {\n type: \"text\",\n fieldId: field[$uid],\n dataId: field[$data]?.[$uid] || field[$uid],\n class: [\"xfaTextfield\"],\n style,\n \"aria-label\": ariaLabel(field),\n \"aria-required\": false,\n },\n };\n\n if (isRequired(field)) {\n html.attributes[\"aria-required\"] = true;\n html.attributes.required = true;\n }\n\n return HTMLResult.success({\n name: \"label\",\n attributes: {\n class: [\"xfaLabel\"],\n },\n children: [html],\n });\n }\n}\n\nclass Occur extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"occur\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.initial =\n attributes.initial !== \"\"\n ? getInteger({\n data: attributes.initial,\n defaultValue: \"\",\n validate: x => true,\n })\n : \"\";\n this.max =\n attributes.max !== \"\"\n ? getInteger({\n data: attributes.max,\n defaultValue: 1,\n validate: x => true,\n })\n : \"\";\n this.min =\n attributes.min !== \"\"\n ? getInteger({\n data: attributes.min,\n defaultValue: 1,\n validate: x => true,\n })\n : \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n }\n\n [$clean]() {\n const parent = this[$getParent]();\n const originalMin = this.min;\n\n if (this.min === \"\") {\n this.min =\n parent instanceof PageArea || parent instanceof PageSet ? 0 : 1;\n }\n if (this.max === \"\") {\n if (originalMin === \"\") {\n this.max =\n parent instanceof PageArea || parent instanceof PageSet ? -1 : 1;\n } else {\n this.max = this.min;\n }\n }\n\n if (this.max !== -1 && this.max < this.min) {\n this.max = this.min;\n }\n\n if (this.initial === \"\") {\n this.initial = parent instanceof Template ? 1 : this.min;\n }\n }\n}\n\nclass Oid extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"oid\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Oids extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"oids\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.oid = new XFAObjectArray();\n }\n}\n\nclass Overflow extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"overflow\");\n this.id = attributes.id || \"\";\n this.leader = attributes.leader || \"\";\n this.target = attributes.target || \"\";\n this.trailer = attributes.trailer || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$getExtra]() {\n if (!this[$extra]) {\n const parent = this[$getParent]();\n const root = this[$getTemplateRoot]();\n const target = root[$searchNode](this.target, parent);\n const leader = root[$searchNode](this.leader, parent);\n const trailer = root[$searchNode](this.trailer, parent);\n this[$extra] = {\n target: target?.[0] || null,\n leader: leader?.[0] || null,\n trailer: trailer?.[0] || null,\n addLeader: false,\n addTrailer: false,\n };\n }\n return this[$extra];\n }\n}\n\nclass PageArea extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"pageArea\", /* hasChildren = */ true);\n this.blankOrNotBlank = getStringOption(attributes.blankOrNotBlank, [\n \"any\",\n \"blank\",\n \"notBlank\",\n ]);\n this.id = attributes.id || \"\";\n this.initialNumber = getInteger({\n data: attributes.initialNumber,\n defaultValue: 1,\n validate: x => true,\n });\n this.name = attributes.name || \"\";\n this.numbered = getInteger({\n data: attributes.numbered,\n defaultValue: 1,\n validate: x => true,\n });\n this.oddOrEven = getStringOption(attributes.oddOrEven, [\n \"any\",\n \"even\",\n \"odd\",\n ]);\n this.pagePosition = getStringOption(attributes.pagePosition, [\n \"any\",\n \"first\",\n \"last\",\n \"only\",\n \"rest\",\n ]);\n this.relevant = getRelevant(attributes.relevant);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.desc = null;\n this.extras = null;\n this.medium = null;\n this.occur = null;\n this.area = new XFAObjectArray();\n this.contentArea = new XFAObjectArray();\n this.draw = new XFAObjectArray();\n this.exclGroup = new XFAObjectArray();\n this.field = new XFAObjectArray();\n this.subform = new XFAObjectArray();\n }\n\n [$isUsable]() {\n if (!this[$extra]) {\n this[$extra] = {\n numberOfUse: 0,\n };\n return true;\n }\n return (\n !this.occur ||\n this.occur.max === -1 ||\n this[$extra].numberOfUse < this.occur.max\n );\n }\n\n [$cleanPage]() {\n delete this[$extra];\n }\n\n [$getNextPage]() {\n if (!this[$extra]) {\n this[$extra] = {\n numberOfUse: 0,\n };\n }\n\n const parent = this[$getParent]();\n if (parent.relation === \"orderedOccurrence\") {\n if (this[$isUsable]()) {\n this[$extra].numberOfUse += 1;\n return this;\n }\n }\n\n return parent[$getNextPage]();\n }\n\n [$getAvailableSpace]() {\n return this[$extra].space || { width: 0, height: 0 };\n }\n\n [$toHTML]() {\n // TODO: incomplete.\n if (!this[$extra]) {\n this[$extra] = {\n numberOfUse: 1,\n };\n }\n\n const children = [];\n this[$extra].children = children;\n\n const style = Object.create(null);\n if (this.medium && this.medium.short && this.medium.long) {\n style.width = measureToString(this.medium.short);\n style.height = measureToString(this.medium.long);\n this[$extra].space = {\n width: this.medium.short,\n height: this.medium.long,\n };\n if (this.medium.orientation === \"landscape\") {\n const x = style.width;\n style.width = style.height;\n style.height = x;\n this[$extra].space = {\n width: this.medium.long,\n height: this.medium.short,\n };\n }\n } else {\n warn(\"XFA - No medium specified in pageArea: please file a bug.\");\n }\n\n this[$childrenToHTML]({\n filter: new Set([\"area\", \"draw\", \"field\", \"subform\"]),\n include: true,\n });\n\n // contentarea must be the last container to be sure it is\n // on top of the others.\n this[$childrenToHTML]({\n filter: new Set([\"contentArea\"]),\n include: true,\n });\n\n return HTMLResult.success({\n name: \"div\",\n children,\n attributes: {\n class: [\"xfaPage\"],\n id: this[$uid],\n style,\n xfaName: this.name,\n },\n });\n }\n}\n\nclass PageSet extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"pageSet\", /* hasChildren = */ true);\n this.duplexImposition = getStringOption(attributes.duplexImposition, [\n \"longEdge\",\n \"shortEdge\",\n ]);\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.relation = getStringOption(attributes.relation, [\n \"orderedOccurrence\",\n \"duplexPaginated\",\n \"simplexPaginated\",\n ]);\n this.relevant = getRelevant(attributes.relevant);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.occur = null;\n this.pageArea = new XFAObjectArray();\n this.pageSet = new XFAObjectArray();\n }\n\n [$cleanPage]() {\n for (const page of this.pageArea.children) {\n page[$cleanPage]();\n }\n for (const page of this.pageSet.children) {\n page[$cleanPage]();\n }\n }\n\n [$isUsable]() {\n return (\n !this.occur ||\n this.occur.max === -1 ||\n this[$extra].numberOfUse < this.occur.max\n );\n }\n\n [$getNextPage]() {\n if (!this[$extra]) {\n this[$extra] = {\n numberOfUse: 1,\n pageIndex: -1,\n pageSetIndex: -1,\n };\n }\n\n if (this.relation === \"orderedOccurrence\") {\n if (this[$extra].pageIndex + 1 < this.pageArea.children.length) {\n this[$extra].pageIndex += 1;\n const pageArea = this.pageArea.children[this[$extra].pageIndex];\n return pageArea[$getNextPage]();\n }\n\n if (this[$extra].pageSetIndex + 1 < this.pageSet.children.length) {\n this[$extra].pageSetIndex += 1;\n return this.pageSet.children[this[$extra].pageSetIndex][$getNextPage]();\n }\n\n if (this[$isUsable]()) {\n this[$extra].numberOfUse += 1;\n this[$extra].pageIndex = -1;\n this[$extra].pageSetIndex = -1;\n return this[$getNextPage]();\n }\n\n const parent = this[$getParent]();\n if (parent instanceof PageSet) {\n return parent[$getNextPage]();\n }\n\n this[$cleanPage]();\n return this[$getNextPage]();\n }\n const pageNumber = this[$getTemplateRoot]()[$extra].pageNumber;\n const parity = pageNumber % 2 === 0 ? \"even\" : \"odd\";\n const position = pageNumber === 0 ? \"first\" : \"rest\";\n\n let page = this.pageArea.children.find(\n p => p.oddOrEven === parity && p.pagePosition === position\n );\n if (page) {\n return page;\n }\n\n page = this.pageArea.children.find(\n p => p.oddOrEven === \"any\" && p.pagePosition === position\n );\n if (page) {\n return page;\n }\n\n page = this.pageArea.children.find(\n p => p.oddOrEven === \"any\" && p.pagePosition === \"any\"\n );\n if (page) {\n return page;\n }\n\n return this.pageArea.children[0];\n }\n}\n\nclass Para extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"para\", /* hasChildren = */ true);\n this.hAlign = getStringOption(attributes.hAlign, [\n \"left\",\n \"center\",\n \"justify\",\n \"justifyAll\",\n \"radix\",\n \"right\",\n ]);\n this.id = attributes.id || \"\";\n this.lineHeight = attributes.lineHeight\n ? getMeasurement(attributes.lineHeight, \"0pt\")\n : \"\";\n this.marginLeft = attributes.marginLeft\n ? getMeasurement(attributes.marginLeft, \"0pt\")\n : \"\";\n this.marginRight = attributes.marginRight\n ? getMeasurement(attributes.marginRight, \"0pt\")\n : \"\";\n this.orphans = getInteger({\n data: attributes.orphans,\n defaultValue: 0,\n validate: x => x >= 0,\n });\n this.preserve = attributes.preserve || \"\";\n this.radixOffset = attributes.radixOffset\n ? getMeasurement(attributes.radixOffset, \"0pt\")\n : \"\";\n this.spaceAbove = attributes.spaceAbove\n ? getMeasurement(attributes.spaceAbove, \"0pt\")\n : \"\";\n this.spaceBelow = attributes.spaceBelow\n ? getMeasurement(attributes.spaceBelow, \"0pt\")\n : \"\";\n this.tabDefault = attributes.tabDefault\n ? getMeasurement(this.tabDefault)\n : \"\";\n this.tabStops = (attributes.tabStops || \"\")\n .trim()\n .split(/\\s+/)\n .map((x, i) => (i % 2 === 1 ? getMeasurement(x) : x));\n this.textIndent = attributes.textIndent\n ? getMeasurement(attributes.textIndent, \"0pt\")\n : \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.vAlign = getStringOption(attributes.vAlign, [\n \"top\",\n \"bottom\",\n \"middle\",\n ]);\n this.widows = getInteger({\n data: attributes.widows,\n defaultValue: 0,\n validate: x => x >= 0,\n });\n this.hyphenation = null;\n }\n\n [$toStyle]() {\n const style = toStyle(this, \"hAlign\");\n if (this.marginLeft !== \"\") {\n style.paddingLeft = measureToString(this.marginLeft);\n }\n if (this.marginRight !== \"\") {\n style.paddingight = measureToString(this.marginRight);\n }\n if (this.spaceAbove !== \"\") {\n style.paddingTop = measureToString(this.spaceAbove);\n }\n if (this.spaceBelow !== \"\") {\n style.paddingBottom = measureToString(this.spaceBelow);\n }\n if (this.textIndent !== \"\") {\n style.textIndent = measureToString(this.textIndent);\n fixTextIndent(style);\n }\n\n if (this.lineHeight > 0) {\n style.lineHeight = measureToString(this.lineHeight);\n }\n\n if (this.tabDefault !== \"\") {\n style.tabSize = measureToString(this.tabDefault);\n }\n\n if (this.tabStops.length > 0) {\n // TODO\n }\n\n if (this.hyphenatation) {\n Object.assign(style, this.hyphenatation[$toStyle]());\n }\n\n return style;\n }\n}\n\nclass PasswordEdit extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"passwordEdit\", /* hasChildren = */ true);\n this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, [\n \"auto\",\n \"off\",\n \"on\",\n ]);\n this.id = attributes.id || \"\";\n this.passwordChar = attributes.passwordChar || \"*\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.border = null;\n this.extras = null;\n this.margin = null;\n }\n}\n\nclass Pattern extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"pattern\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\n \"crossHatch\",\n \"crossDiagonal\",\n \"diagonalLeft\",\n \"diagonalRight\",\n \"horizontal\",\n \"vertical\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.color = null;\n this.extras = null;\n }\n\n [$toStyle](startColor) {\n startColor = startColor ? startColor[$toStyle]() : \"#FFFFFF\";\n const endColor = this.color ? this.color[$toStyle]() : \"#000000\";\n const width = 5;\n const cmd = \"repeating-linear-gradient\";\n const colors = `${startColor},${startColor} ${width}px,${endColor} ${width}px,${endColor} ${\n 2 * width\n }px`;\n switch (this.type) {\n case \"crossHatch\":\n return `${cmd}(to top,${colors}) ${cmd}(to right,${colors})`;\n case \"crossDiagonal\":\n return `${cmd}(45deg,${colors}) ${cmd}(-45deg,${colors})`;\n case \"diagonalLeft\":\n return `${cmd}(45deg,${colors})`;\n case \"diagonalRight\":\n return `${cmd}(-45deg,${colors})`;\n case \"horizontal\":\n return `${cmd}(to top,${colors})`;\n case \"vertical\":\n return `${cmd}(to right,${colors})`;\n }\n\n return \"\";\n }\n}\n\nclass Picture extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"picture\");\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Proto extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"proto\", /* hasChildren = */ true);\n this.appearanceFilter = new XFAObjectArray();\n this.arc = new XFAObjectArray();\n this.area = new XFAObjectArray();\n this.assist = new XFAObjectArray();\n this.barcode = new XFAObjectArray();\n this.bindItems = new XFAObjectArray();\n this.bookend = new XFAObjectArray();\n this.boolean = new XFAObjectArray();\n this.border = new XFAObjectArray();\n this.break = new XFAObjectArray();\n this.breakAfter = new XFAObjectArray();\n this.breakBefore = new XFAObjectArray();\n this.button = new XFAObjectArray();\n this.calculate = new XFAObjectArray();\n this.caption = new XFAObjectArray();\n this.certificate = new XFAObjectArray();\n this.certificates = new XFAObjectArray();\n this.checkButton = new XFAObjectArray();\n this.choiceList = new XFAObjectArray();\n this.color = new XFAObjectArray();\n this.comb = new XFAObjectArray();\n this.connect = new XFAObjectArray();\n this.contentArea = new XFAObjectArray();\n this.corner = new XFAObjectArray();\n this.date = new XFAObjectArray();\n this.dateTime = new XFAObjectArray();\n this.dateTimeEdit = new XFAObjectArray();\n this.decimal = new XFAObjectArray();\n this.defaultUi = new XFAObjectArray();\n this.desc = new XFAObjectArray();\n this.digestMethod = new XFAObjectArray();\n this.digestMethods = new XFAObjectArray();\n this.draw = new XFAObjectArray();\n this.edge = new XFAObjectArray();\n this.encoding = new XFAObjectArray();\n this.encodings = new XFAObjectArray();\n this.encrypt = new XFAObjectArray();\n this.encryptData = new XFAObjectArray();\n this.encryption = new XFAObjectArray();\n this.encryptionMethod = new XFAObjectArray();\n this.encryptionMethods = new XFAObjectArray();\n this.event = new XFAObjectArray();\n this.exData = new XFAObjectArray();\n this.exObject = new XFAObjectArray();\n this.exclGroup = new XFAObjectArray();\n this.execute = new XFAObjectArray();\n this.extras = new XFAObjectArray();\n this.field = new XFAObjectArray();\n this.fill = new XFAObjectArray();\n this.filter = new XFAObjectArray();\n this.float = new XFAObjectArray();\n this.font = new XFAObjectArray();\n this.format = new XFAObjectArray();\n this.handler = new XFAObjectArray();\n this.hyphenation = new XFAObjectArray();\n this.image = new XFAObjectArray();\n this.imageEdit = new XFAObjectArray();\n this.integer = new XFAObjectArray();\n this.issuers = new XFAObjectArray();\n this.items = new XFAObjectArray();\n this.keep = new XFAObjectArray();\n this.keyUsage = new XFAObjectArray();\n this.line = new XFAObjectArray();\n this.linear = new XFAObjectArray();\n this.lockDocument = new XFAObjectArray();\n this.manifest = new XFAObjectArray();\n this.margin = new XFAObjectArray();\n this.mdp = new XFAObjectArray();\n this.medium = new XFAObjectArray();\n this.message = new XFAObjectArray();\n this.numericEdit = new XFAObjectArray();\n this.occur = new XFAObjectArray();\n this.oid = new XFAObjectArray();\n this.oids = new XFAObjectArray();\n this.overflow = new XFAObjectArray();\n this.pageArea = new XFAObjectArray();\n this.pageSet = new XFAObjectArray();\n this.para = new XFAObjectArray();\n this.passwordEdit = new XFAObjectArray();\n this.pattern = new XFAObjectArray();\n this.picture = new XFAObjectArray();\n this.radial = new XFAObjectArray();\n this.reason = new XFAObjectArray();\n this.reasons = new XFAObjectArray();\n this.rectangle = new XFAObjectArray();\n this.ref = new XFAObjectArray();\n this.script = new XFAObjectArray();\n this.setProperty = new XFAObjectArray();\n this.signData = new XFAObjectArray();\n this.signature = new XFAObjectArray();\n this.signing = new XFAObjectArray();\n this.solid = new XFAObjectArray();\n this.speak = new XFAObjectArray();\n this.stipple = new XFAObjectArray();\n this.subform = new XFAObjectArray();\n this.subformSet = new XFAObjectArray();\n this.subjectDN = new XFAObjectArray();\n this.subjectDNs = new XFAObjectArray();\n this.submit = new XFAObjectArray();\n this.text = new XFAObjectArray();\n this.textEdit = new XFAObjectArray();\n this.time = new XFAObjectArray();\n this.timeStamp = new XFAObjectArray();\n this.toolTip = new XFAObjectArray();\n this.traversal = new XFAObjectArray();\n this.traverse = new XFAObjectArray();\n this.ui = new XFAObjectArray();\n this.validate = new XFAObjectArray();\n this.value = new XFAObjectArray();\n this.variables = new XFAObjectArray();\n }\n}\n\nclass Radial extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"radial\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"toEdge\", \"toCenter\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.color = null;\n this.extras = null;\n }\n\n [$toStyle](startColor) {\n startColor = startColor ? startColor[$toStyle]() : \"#FFFFFF\";\n const endColor = this.color ? this.color[$toStyle]() : \"#000000\";\n const colors =\n this.type === \"toEdge\"\n ? `${startColor},${endColor}`\n : `${endColor},${startColor}`;\n return `radial-gradient(circle at center, ${colors})`;\n }\n}\n\nclass Reason extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"reason\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Reasons extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"reasons\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.reason = new XFAObjectArray();\n }\n}\n\nclass Rectangle extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"rectangle\", /* hasChildren = */ true);\n this.hand = getStringOption(attributes.hand, [\"even\", \"left\", \"right\"]);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.corner = new XFAObjectArray(4);\n this.edge = new XFAObjectArray(4);\n this.fill = null;\n }\n\n [$toHTML]() {\n const edge = this.edge.children.length\n ? this.edge.children[0]\n : new Edge({});\n const edgeStyle = edge[$toStyle]();\n const style = Object.create(null);\n if (this.fill?.presence === \"visible\") {\n Object.assign(style, this.fill[$toStyle]());\n } else {\n style.fill = \"transparent\";\n }\n style.strokeWidth = measureToString(\n edge.presence === \"visible\" ? edge.thickness : 0\n );\n style.stroke = edgeStyle.color;\n\n const corner = this.corner.children.length\n ? this.corner.children[0]\n : new Corner({});\n const cornerStyle = corner[$toStyle]();\n\n const rect = {\n name: \"rect\",\n attributes: {\n xmlns: SVG_NS,\n width: \"100%\",\n height: \"100%\",\n x: 0,\n y: 0,\n rx: cornerStyle.radius,\n ry: cornerStyle.radius,\n style,\n },\n };\n\n const svg = {\n name: \"svg\",\n children: [rect],\n attributes: {\n xmlns: SVG_NS,\n style: {\n overflow: \"visible\",\n },\n width: \"100%\",\n height: \"100%\",\n },\n };\n\n const parent = this[$getParent]()[$getParent]();\n if (hasMargin(parent)) {\n return HTMLResult.success({\n name: \"div\",\n attributes: {\n style: {\n display: \"inline\",\n width: \"100%\",\n height: \"100%\",\n },\n },\n children: [svg],\n });\n }\n\n svg.attributes.style.position = \"absolute\";\n return HTMLResult.success(svg);\n }\n}\n\nclass RefElement extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"ref\");\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Script extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"script\");\n this.binding = attributes.binding || \"\";\n this.contentType = attributes.contentType || \"\";\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.runAt = getStringOption(attributes.runAt, [\n \"client\",\n \"both\",\n \"server\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass SetProperty extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"setProperty\");\n this.connection = attributes.connection || \"\";\n this.ref = attributes.ref || \"\";\n this.target = attributes.target || \"\";\n }\n}\n\nclass SignData extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"signData\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.operation = getStringOption(attributes.operation, [\n \"sign\",\n \"clear\",\n \"verify\",\n ]);\n this.ref = attributes.ref || \"\";\n this.target = attributes.target || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.filter = null;\n this.manifest = null;\n }\n}\n\nclass Signature extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"signature\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"PDF1.3\", \"PDF1.6\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.border = null;\n this.extras = null;\n this.filter = null;\n this.manifest = null;\n this.margin = null;\n }\n}\n\nclass Signing extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"signing\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.certificate = new XFAObjectArray();\n }\n}\n\nclass Solid extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"solid\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n }\n\n [$toStyle](startColor) {\n return startColor ? startColor[$toStyle]() : \"#FFFFFF\";\n }\n}\n\nclass Speak extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"speak\");\n this.disable = getInteger({\n data: attributes.disable,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.id = attributes.id || \"\";\n this.priority = getStringOption(attributes.priority, [\n \"custom\",\n \"caption\",\n \"name\",\n \"toolTip\",\n ]);\n this.rid = attributes.rid || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Stipple extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"stipple\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.rate = getInteger({\n data: attributes.rate,\n defaultValue: 50,\n validate: x => x >= 0 && x <= 100,\n });\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.color = null;\n this.extras = null;\n }\n\n [$toStyle](bgColor) {\n const alpha = this.rate / 100;\n return Util.makeHexColor(\n Math.round(bgColor.value.r * (1 - alpha) + this.value.r * alpha),\n Math.round(bgColor.value.g * (1 - alpha) + this.value.g * alpha),\n Math.round(bgColor.value.b * (1 - alpha) + this.value.b * alpha)\n );\n }\n}\n\nclass Subform extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"subform\", /* hasChildren = */ true);\n this.access = getStringOption(attributes.access, [\n \"open\",\n \"nonInteractive\",\n \"protected\",\n \"readOnly\",\n ]);\n this.allowMacro = getInteger({\n data: attributes.allowMacro,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.anchorType = getStringOption(attributes.anchorType, [\n \"topLeft\",\n \"bottomCenter\",\n \"bottomLeft\",\n \"bottomRight\",\n \"middleCenter\",\n \"middleLeft\",\n \"middleRight\",\n \"topCenter\",\n \"topRight\",\n ]);\n this.colSpan = getInteger({\n data: attributes.colSpan,\n defaultValue: 1,\n validate: n => n >= 1 || n === -1,\n });\n this.columnWidths = (attributes.columnWidths || \"\")\n .trim()\n .split(/\\s+/)\n .map(x => (x === \"-1\" ? -1 : getMeasurement(x)));\n this.h = attributes.h ? getMeasurement(attributes.h) : \"\";\n this.hAlign = getStringOption(attributes.hAlign, [\n \"left\",\n \"center\",\n \"justify\",\n \"justifyAll\",\n \"radix\",\n \"right\",\n ]);\n this.id = attributes.id || \"\";\n this.layout = getStringOption(attributes.layout, [\n \"position\",\n \"lr-tb\",\n \"rl-row\",\n \"rl-tb\",\n \"row\",\n \"table\",\n \"tb\",\n ]);\n this.locale = attributes.locale || \"\";\n this.maxH = getMeasurement(attributes.maxH, \"0pt\");\n this.maxW = getMeasurement(attributes.maxW, \"0pt\");\n this.mergeMode = getStringOption(attributes.mergeMode, [\n \"consumeData\",\n \"matchTemplate\",\n ]);\n this.minH = getMeasurement(attributes.minH, \"0pt\");\n this.minW = getMeasurement(attributes.minW, \"0pt\");\n this.name = attributes.name || \"\";\n this.presence = getStringOption(attributes.presence, [\n \"visible\",\n \"hidden\",\n \"inactive\",\n \"invisible\",\n ]);\n this.relevant = getRelevant(attributes.relevant);\n this.restoreState = getStringOption(attributes.restoreState, [\n \"manual\",\n \"auto\",\n ]);\n this.scope = getStringOption(attributes.scope, [\"name\", \"none\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.w = attributes.w ? getMeasurement(attributes.w) : \"\";\n this.x = getMeasurement(attributes.x, \"0pt\");\n this.y = getMeasurement(attributes.y, \"0pt\");\n this.assist = null;\n this.bind = null;\n this.bookend = null;\n this.border = null;\n this.break = null;\n this.calculate = null;\n this.desc = null;\n this.extras = null;\n this.keep = null;\n this.margin = null;\n this.occur = null;\n this.overflow = null;\n this.pageSet = null;\n this.para = null;\n this.traversal = null;\n this.validate = null;\n this.variables = null;\n this.area = new XFAObjectArray();\n this.breakAfter = new XFAObjectArray();\n this.breakBefore = new XFAObjectArray();\n this.connect = new XFAObjectArray();\n this.draw = new XFAObjectArray();\n this.event = new XFAObjectArray();\n this.exObject = new XFAObjectArray();\n this.exclGroup = new XFAObjectArray();\n this.field = new XFAObjectArray();\n this.proto = new XFAObjectArray();\n this.setProperty = new XFAObjectArray();\n this.subform = new XFAObjectArray();\n this.subformSet = new XFAObjectArray();\n }\n\n [$getSubformParent]() {\n const parent = this[$getParent]();\n if (parent instanceof SubformSet) {\n return parent[$getSubformParent]();\n }\n return parent;\n }\n\n [$isBindable]() {\n return true;\n }\n\n [$isThereMoreWidth]() {\n return (\n (this.layout.endsWith(\"-tb\") &&\n this[$extra].attempt === 0 &&\n this[$extra].numberInLine > 0) ||\n this[$getParent]()[$isThereMoreWidth]()\n );\n }\n\n *[$getContainedChildren]() {\n // This function is overriden in order to fake that subforms under\n // this set are in fact under parent subform.\n yield* getContainedChildren(this);\n }\n\n [$flushHTML]() {\n return flushHTML(this);\n }\n\n [$addHTML](html, bbox) {\n addHTML(this, html, bbox);\n }\n\n [$getAvailableSpace]() {\n return getAvailableSpace(this);\n }\n\n [$isSplittable]() {\n // We cannot cache the result here because the contentArea\n // can change.\n const parent = this[$getSubformParent]();\n if (!parent[$isSplittable]()) {\n return false;\n }\n\n if (this[$extra]._isSplittable !== undefined) {\n return this[$extra]._isSplittable;\n }\n\n if (this.layout === \"position\" || this.layout.includes(\"row\")) {\n this[$extra]._isSplittable = false;\n return false;\n }\n\n if (this.keep && this.keep.intact !== \"none\") {\n this[$extra]._isSplittable = false;\n return false;\n }\n\n if (parent.layout?.endsWith(\"-tb\") && parent[$extra].numberInLine !== 0) {\n // If parent can fit in w=100 and there's already an element which takes\n // 90 then we've 10 for this element. Suppose this element has a tb layout\n // and 5 elements have a width of 7 and the 6th has a width of 20:\n // then this element (and all its content) must move on the next line.\n // If this element is splittable then the first 5 children will stay\n // at the end of the line: we don't want that.\n return false;\n }\n\n this[$extra]._isSplittable = true;\n\n return true;\n }\n\n [$toHTML](availableSpace) {\n setTabIndex(this);\n\n if (this.break) {\n // break element is deprecated so plug it on one of its replacement\n // breakBefore or breakAfter.\n if (this.break.after !== \"auto\" || this.break.afterTarget !== \"\") {\n const node = new BreakAfter({\n targetType: this.break.after,\n target: this.break.afterTarget,\n startNew: this.break.startNew.toString(),\n });\n node[$globalData] = this[$globalData];\n this[$appendChild](node);\n this.breakAfter.push(node);\n }\n\n if (this.break.before !== \"auto\" || this.break.beforeTarget !== \"\") {\n const node = new BreakBefore({\n targetType: this.break.before,\n target: this.break.beforeTarget,\n startNew: this.break.startNew.toString(),\n });\n node[$globalData] = this[$globalData];\n this[$appendChild](node);\n this.breakBefore.push(node);\n }\n\n if (this.break.overflowTarget !== \"\") {\n const node = new Overflow({\n target: this.break.overflowTarget,\n leader: this.break.overflowLeader,\n trailer: this.break.overflowTrailer,\n });\n node[$globalData] = this[$globalData];\n this[$appendChild](node);\n this.overflow.push(node);\n }\n\n this[$removeChild](this.break);\n this.break = null;\n }\n\n if (this.presence === \"hidden\" || this.presence === \"inactive\") {\n return HTMLResult.EMPTY;\n }\n\n if (\n this.breakBefore.children.length > 1 ||\n this.breakAfter.children.length > 1\n ) {\n // Specs are always talking about the breakBefore element\n // and it doesn't really make sense to have several ones.\n warn(\n \"XFA - Several breakBefore or breakAfter in subforms: please file a bug.\"\n );\n }\n\n if (this.breakBefore.children.length >= 1) {\n const breakBefore = this.breakBefore.children[0];\n if (handleBreak(breakBefore)) {\n return HTMLResult.breakNode(breakBefore);\n }\n }\n\n if (this[$extra]?.afterBreakAfter) {\n return HTMLResult.EMPTY;\n }\n\n // TODO: incomplete.\n fixDimensions(this);\n const children = [];\n const attributes = {\n id: this[$uid],\n class: [],\n };\n\n setAccess(this, attributes.class);\n\n if (!this[$extra]) {\n this[$extra] = Object.create(null);\n }\n\n Object.assign(this[$extra], {\n children,\n line: null,\n attributes,\n attempt: 0,\n numberInLine: 0,\n availableSpace: {\n width: Math.min(this.w || Infinity, availableSpace.width),\n height: Math.min(this.h || Infinity, availableSpace.height),\n },\n width: 0,\n height: 0,\n prevHeight: 0,\n currentWidth: 0,\n });\n\n const root = this[$getTemplateRoot]();\n const savedNoLayoutFailure = root[$extra].noLayoutFailure;\n\n const isSplittable = this[$isSplittable]();\n if (!isSplittable) {\n setFirstUnsplittable(this);\n }\n\n if (!checkDimensions(this, availableSpace)) {\n return HTMLResult.FAILURE;\n }\n\n const filter = new Set([\n \"area\",\n \"draw\",\n \"exclGroup\",\n \"field\",\n \"subform\",\n \"subformSet\",\n ]);\n\n if (this.layout.includes(\"row\")) {\n const columnWidths = this[$getSubformParent]().columnWidths;\n if (Array.isArray(columnWidths) && columnWidths.length > 0) {\n this[$extra].columnWidths = columnWidths;\n this[$extra].currentColumn = 0;\n }\n }\n\n const style = toStyle(\n this,\n \"anchorType\",\n \"dimensions\",\n \"position\",\n \"presence\",\n \"border\",\n \"margin\",\n \"hAlign\"\n );\n const classNames = [\"xfaSubform\"];\n const cl = layoutClass(this);\n if (cl) {\n classNames.push(cl);\n }\n\n attributes.style = style;\n attributes.class = classNames;\n\n if (this.name) {\n attributes.xfaName = this.name;\n }\n\n if (this.overflow) {\n const overflowExtra = this.overflow[$getExtra]();\n if (overflowExtra.addLeader) {\n overflowExtra.addLeader = false;\n handleOverflow(this, overflowExtra.leader, availableSpace);\n }\n }\n\n this[$pushPara]();\n const isLrTb = this.layout === \"lr-tb\" || this.layout === \"rl-tb\";\n const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1;\n for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {\n if (isLrTb && this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) {\n // If the layout is lr-tb then having attempt equals to\n // MAX_ATTEMPTS_FOR_LRTB_LAYOUT-1 means that we're trying to layout\n // on the next line so this on is empty.\n this[$extra].numberInLine = 0;\n }\n const result = this[$childrenToHTML]({\n filter,\n include: true,\n });\n if (result.success) {\n break;\n }\n if (result.isBreak()) {\n this[$popPara]();\n return result;\n }\n if (\n isLrTb &&\n this[$extra].attempt === 0 &&\n this[$extra].numberInLine === 0 &&\n !root[$extra].noLayoutFailure\n ) {\n // We're failing to put the first element on the line so no\n // need to test on the next line.\n // The goal is not only to avoid some useless checks but to avoid\n // bugs too: if a descendant managed to put a node and failed\n // on the next one, going to the next step here will imply to\n // visit the descendant again, clear [$extra].children and restart\n // on the failing node, consequently the first node just disappears\n // because it has never been flushed.\n this[$extra].attempt = maxRun;\n break;\n }\n }\n\n this[$popPara]();\n if (!isSplittable) {\n unsetFirstUnsplittable(this);\n }\n root[$extra].noLayoutFailure = savedNoLayoutFailure;\n\n if (this[$extra].attempt === maxRun) {\n if (this.overflow) {\n this[$getTemplateRoot]()[$extra].overflowNode = this.overflow;\n }\n\n if (!isSplittable) {\n // Since a new try will happen in a new container with maybe\n // new dimensions, we invalidate already layed out components.\n delete this[$extra];\n }\n return HTMLResult.FAILURE;\n }\n\n if (this.overflow) {\n const overflowExtra = this.overflow[$getExtra]();\n if (overflowExtra.addTrailer) {\n overflowExtra.addTrailer = false;\n handleOverflow(this, overflowExtra.trailer, availableSpace);\n }\n }\n\n let marginH = 0;\n let marginV = 0;\n if (this.margin) {\n marginH = this.margin.leftInset + this.margin.rightInset;\n marginV = this.margin.topInset + this.margin.bottomInset;\n }\n\n const width = Math.max(this[$extra].width + marginH, this.w || 0);\n const height = Math.max(this[$extra].height + marginV, this.h || 0);\n const bbox = [this.x, this.y, width, height];\n\n if (this.w === \"\") {\n style.width = measureToString(width);\n }\n if (this.h === \"\") {\n style.height = measureToString(height);\n }\n\n if (\n (style.width === \"0px\" || style.height === \"0px\") &&\n children.length === 0\n ) {\n return HTMLResult.EMPTY;\n }\n\n const html = {\n name: \"div\",\n attributes,\n children,\n };\n\n applyAssist(this, attributes);\n\n const result = HTMLResult.success(createWrapper(this, html), bbox);\n\n if (this.breakAfter.children.length >= 1) {\n const breakAfter = this.breakAfter.children[0];\n if (handleBreak(breakAfter)) {\n this[$extra].afterBreakAfter = result;\n return HTMLResult.breakNode(breakAfter);\n }\n }\n\n delete this[$extra];\n\n return result;\n }\n}\n\nclass SubformSet extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"subformSet\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.relation = getStringOption(attributes.relation, [\n \"ordered\",\n \"choice\",\n \"unordered\",\n ]);\n this.relevant = getRelevant(attributes.relevant);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.bookend = null;\n this.break = null;\n this.desc = null;\n this.extras = null;\n this.occur = null;\n this.overflow = null;\n this.breakAfter = new XFAObjectArray();\n this.breakBefore = new XFAObjectArray();\n this.subform = new XFAObjectArray();\n this.subformSet = new XFAObjectArray();\n\n // TODO: need to handle break stuff and relation.\n }\n\n *[$getContainedChildren]() {\n // This function is overriden in order to fake that subforms under\n // this set are in fact under parent subform.\n yield* getContainedChildren(this);\n }\n\n [$getSubformParent]() {\n let parent = this[$getParent]();\n while (!(parent instanceof Subform)) {\n parent = parent[$getParent]();\n }\n return parent;\n }\n\n [$isBindable]() {\n return true;\n }\n}\n\nclass SubjectDN extends ContentObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"subjectDN\");\n this.delimiter = attributes.delimiter || \",\";\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$finalize]() {\n this[$content] = new Map(\n this[$content].split(this.delimiter).map(kv => {\n kv = kv.split(\"=\", 2);\n kv[0] = kv[0].trim();\n return kv;\n })\n );\n }\n}\n\nclass SubjectDNs extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"subjectDNs\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.subjectDN = new XFAObjectArray();\n }\n}\n\nclass Submit extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"submit\", /* hasChildren = */ true);\n this.embedPDF = getInteger({\n data: attributes.embedPDF,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.format = getStringOption(attributes.format, [\n \"xdp\",\n \"formdata\",\n \"pdf\",\n \"urlencoded\",\n \"xfd\",\n \"xml\",\n ]);\n this.id = attributes.id || \"\";\n this.target = attributes.target || \"\";\n this.textEncoding = getKeyword({\n data: attributes.textEncoding\n ? attributes.textEncoding.toLowerCase()\n : \"\",\n defaultValue: \"\",\n validate: k =>\n [\n \"utf-8\",\n \"big-five\",\n \"fontspecific\",\n \"gbk\",\n \"gb-18030\",\n \"gb-2312\",\n \"ksc-5601\",\n \"none\",\n \"shift-jis\",\n \"ucs-2\",\n \"utf-16\",\n ].includes(k) || k.match(/iso-8859-\\d{2}/),\n });\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.xdpContent = attributes.xdpContent || \"\";\n this.encrypt = null;\n this.encryptData = new XFAObjectArray();\n this.signData = new XFAObjectArray();\n }\n}\n\nclass Template extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"template\", /* hasChildren = */ true);\n this.baseProfile = getStringOption(attributes.baseProfile, [\n \"full\",\n \"interactiveForms\",\n ]);\n this.extras = null;\n\n // Spec is unclear:\n // A container element that describes a single subform capable of\n // enclosing other containers.\n // Can we have more than one subform ?\n this.subform = new XFAObjectArray();\n }\n\n [$finalize]() {\n if (this.subform.children.length === 0) {\n warn(\"XFA - No subforms in template node.\");\n }\n if (this.subform.children.length >= 2) {\n warn(\"XFA - Several subforms in template node: please file a bug.\");\n }\n this[$tabIndex] = DEFAULT_TAB_INDEX;\n }\n\n [$isSplittable]() {\n return true;\n }\n\n [$searchNode](expr, container) {\n if (expr.startsWith(\"#\")) {\n // This is an id.\n return [this[$ids].get(expr.slice(1))];\n }\n return searchNode(this, container, expr, true, true);\n }\n\n /**\n * This function is a generator because the conversion into\n * pages is done asynchronously and we want to save the state\n * of the function where we were in the previous iteration.\n */\n *[$toPages]() {\n if (!this.subform.children.length) {\n return HTMLResult.success({\n name: \"div\",\n children: [],\n });\n }\n this[$extra] = {\n overflowNode: null,\n firstUnsplittable: null,\n currentContentArea: null,\n currentPageArea: null,\n noLayoutFailure: false,\n pageNumber: 1,\n pagePosition: \"first\",\n oddOrEven: \"odd\",\n blankOrNotBlank: \"nonBlank\",\n paraStack: [],\n };\n\n const root = this.subform.children[0];\n root.pageSet[$cleanPage]();\n\n const pageAreas = root.pageSet.pageArea.children;\n const mainHtml = {\n name: \"div\",\n children: [],\n };\n\n let pageArea = null;\n let breakBefore = null;\n let breakBeforeTarget = null;\n if (root.breakBefore.children.length >= 1) {\n breakBefore = root.breakBefore.children[0];\n breakBeforeTarget = breakBefore.target;\n } else if (\n root.subform.children.length >= 1 &&\n root.subform.children[0].breakBefore.children.length >= 1\n ) {\n breakBefore = root.subform.children[0].breakBefore.children[0];\n breakBeforeTarget = breakBefore.target;\n } else if (root.break?.beforeTarget) {\n breakBefore = root.break;\n breakBeforeTarget = breakBefore.beforeTarget;\n } else if (\n root.subform.children.length >= 1 &&\n root.subform.children[0].break?.beforeTarget\n ) {\n breakBefore = root.subform.children[0].break;\n breakBeforeTarget = breakBefore.beforeTarget;\n }\n\n if (breakBefore) {\n const target = this[$searchNode](\n breakBeforeTarget,\n breakBefore[$getParent]()\n );\n if (target instanceof PageArea) {\n pageArea = target;\n // Consume breakBefore.\n breakBefore[$extra] = {};\n }\n }\n\n if (!pageArea) {\n pageArea = pageAreas[0];\n }\n\n pageArea[$extra] = {\n numberOfUse: 1,\n };\n\n const pageAreaParent = pageArea[$getParent]();\n pageAreaParent[$extra] = {\n numberOfUse: 1,\n pageIndex: pageAreaParent.pageArea.children.indexOf(pageArea),\n pageSetIndex: 0,\n };\n\n let targetPageArea;\n let leader = null;\n let trailer = null;\n let hasSomething = true;\n let hasSomethingCounter = 0;\n let startIndex = 0;\n\n while (true) {\n if (!hasSomething) {\n mainHtml.children.pop();\n // Nothing has been added in the previous page\n if (++hasSomethingCounter === MAX_EMPTY_PAGES) {\n warn(\"XFA - Something goes wrong: please file a bug.\");\n return mainHtml;\n }\n } else {\n hasSomethingCounter = 0;\n }\n\n targetPageArea = null;\n this[$extra].currentPageArea = pageArea;\n const page = pageArea[$toHTML]().html;\n mainHtml.children.push(page);\n\n if (leader) {\n this[$extra].noLayoutFailure = true;\n page.children.push(leader[$toHTML](pageArea[$extra].space).html);\n leader = null;\n }\n\n if (trailer) {\n this[$extra].noLayoutFailure = true;\n page.children.push(trailer[$toHTML](pageArea[$extra].space).html);\n trailer = null;\n }\n\n const contentAreas = pageArea.contentArea.children;\n const htmlContentAreas = page.children.filter(node =>\n node.attributes.class.includes(\"xfaContentarea\")\n );\n\n hasSomething = false;\n this[$extra].firstUnsplittable = null;\n this[$extra].noLayoutFailure = false;\n\n const flush = index => {\n const html = root[$flushHTML]();\n if (html) {\n hasSomething ||= html.children?.length > 0;\n htmlContentAreas[index].children.push(html);\n }\n };\n\n for (let i = startIndex, ii = contentAreas.length; i < ii; i++) {\n const contentArea = (this[$extra].currentContentArea = contentAreas[i]);\n const space = { width: contentArea.w, height: contentArea.h };\n startIndex = 0;\n\n if (leader) {\n htmlContentAreas[i].children.push(leader[$toHTML](space).html);\n leader = null;\n }\n\n if (trailer) {\n htmlContentAreas[i].children.push(trailer[$toHTML](space).html);\n trailer = null;\n }\n\n const html = root[$toHTML](space);\n if (html.success) {\n if (html.html) {\n hasSomething ||= html.html.children?.length > 0;\n htmlContentAreas[i].children.push(html.html);\n } else if (!hasSomething && mainHtml.children.length > 1) {\n mainHtml.children.pop();\n }\n return mainHtml;\n }\n\n if (html.isBreak()) {\n const node = html.breakNode;\n flush(i);\n\n if (node.targetType === \"auto\") {\n continue;\n }\n\n if (node.leader) {\n leader = this[$searchNode](node.leader, node[$getParent]());\n leader = leader ? leader[0] : null;\n }\n\n if (node.trailer) {\n trailer = this[$searchNode](node.trailer, node[$getParent]());\n trailer = trailer ? trailer[0] : null;\n }\n\n if (node.targetType === \"pageArea\") {\n targetPageArea = node[$extra].target;\n i = Infinity;\n } else if (!node[$extra].target) {\n // We stay on the same page.\n i = node[$extra].index;\n } else {\n targetPageArea = node[$extra].target;\n startIndex = node[$extra].index + 1;\n i = Infinity;\n }\n\n continue;\n }\n\n if (this[$extra].overflowNode) {\n const node = this[$extra].overflowNode;\n this[$extra].overflowNode = null;\n\n const overflowExtra = node[$getExtra]();\n const target = overflowExtra.target;\n overflowExtra.addLeader = overflowExtra.leader !== null;\n overflowExtra.addTrailer = overflowExtra.trailer !== null;\n\n flush(i);\n\n const currentIndex = i;\n\n i = Infinity;\n if (target instanceof PageArea) {\n // We must stop the contentAreas filling and go to the next page.\n targetPageArea = target;\n } else if (target instanceof ContentArea) {\n const index = contentAreas.indexOf(target);\n if (index !== -1) {\n if (index > currentIndex) {\n // In the next loop iteration `i` will be incremented, note the\n // `continue` just below, hence we need to subtract one here.\n i = index - 1;\n } else {\n // The targetted contentArea has already been filled\n // so create a new page.\n startIndex = index;\n }\n } else {\n targetPageArea = target[$getParent]();\n startIndex = targetPageArea.contentArea.children.indexOf(target);\n }\n }\n continue;\n }\n\n flush(i);\n }\n\n this[$extra].pageNumber += 1;\n if (targetPageArea) {\n if (targetPageArea[$isUsable]()) {\n targetPageArea[$extra].numberOfUse += 1;\n } else {\n targetPageArea = null;\n }\n }\n pageArea = targetPageArea || pageArea[$getNextPage]();\n yield null;\n }\n }\n}\n\nclass Text extends ContentObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"text\");\n this.id = attributes.id || \"\";\n this.maxChars = getInteger({\n data: attributes.maxChars,\n defaultValue: 0,\n validate: x => x >= 0,\n });\n this.name = attributes.name || \"\";\n this.rid = attributes.rid || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$acceptWhitespace]() {\n return true;\n }\n\n [$onChild](child) {\n if (child[$namespaceId] === NamespaceIds.xhtml.id) {\n this[$content] = child;\n return true;\n }\n warn(`XFA - Invalid content in Text: ${child[$nodeName]}.`);\n return false;\n }\n\n [$onText](str) {\n if (this[$content] instanceof XFAObject) {\n return;\n }\n super[$onText](str);\n }\n\n [$finalize]() {\n if (typeof this[$content] === \"string\") {\n this[$content] = this[$content].replaceAll(\"\\r\\n\", \"\\n\");\n }\n }\n\n [$getExtra]() {\n if (typeof this[$content] === \"string\") {\n return this[$content]\n .split(/[\\u2029\\u2028\\n]/)\n .reduce((acc, line) => {\n if (line) {\n acc.push(line);\n }\n return acc;\n }, [])\n .join(\"\\n\");\n }\n return this[$content][$text]();\n }\n\n [$toHTML](availableSpace) {\n if (typeof this[$content] === \"string\") {\n // \\u2028 is a line separator.\n // \\u2029 is a paragraph separator.\n const html = valueToHtml(this[$content]).html;\n\n if (this[$content].includes(\"\\u2029\")) {\n // We've plain text containing a paragraph separator\n // so convert it into a set of .\n html.name = \"div\";\n html.children = [];\n this[$content]\n .split(\"\\u2029\")\n .map(para =>\n // Convert a paragraph into a set of (for lines)\n // separated by
.\n para.split(/[\\u2028\\n]/).reduce((acc, line) => {\n acc.push(\n {\n name: \"span\",\n value: line,\n },\n {\n name: \"br\",\n }\n );\n return acc;\n }, [])\n )\n .forEach(lines => {\n html.children.push({\n name: \"p\",\n children: lines,\n });\n });\n } else if (/[\\u2028\\n]/.test(this[$content])) {\n html.name = \"div\";\n html.children = [];\n // Convert plain text into a set of (for lines)\n // separated by
.\n this[$content].split(/[\\u2028\\n]/).forEach(line => {\n html.children.push(\n {\n name: \"span\",\n value: line,\n },\n {\n name: \"br\",\n }\n );\n });\n }\n\n return HTMLResult.success(html);\n }\n\n return this[$content][$toHTML](availableSpace);\n }\n}\n\nclass TextEdit extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"textEdit\", /* hasChildren = */ true);\n this.allowRichText = getInteger({\n data: attributes.allowRichText,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.hScrollPolicy = getStringOption(attributes.hScrollPolicy, [\n \"auto\",\n \"off\",\n \"on\",\n ]);\n this.id = attributes.id || \"\";\n this.multiLine = getInteger({\n data: attributes.multiLine,\n defaultValue: \"\",\n validate: x => x === 0 || x === 1,\n });\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.vScrollPolicy = getStringOption(attributes.vScrollPolicy, [\n \"auto\",\n \"off\",\n \"on\",\n ]);\n this.border = null;\n this.comb = null;\n this.extras = null;\n this.margin = null;\n }\n\n [$toHTML](availableSpace) {\n // TODO: incomplete.\n const style = toStyle(this, \"border\", \"font\", \"margin\");\n let html;\n const field = this[$getParent]()[$getParent]();\n if (this.multiLine === \"\") {\n this.multiLine = field instanceof Draw ? 1 : 0;\n }\n if (this.multiLine === 1) {\n html = {\n name: \"textarea\",\n attributes: {\n dataId: field[$data]?.[$uid] || field[$uid],\n fieldId: field[$uid],\n class: [\"xfaTextfield\"],\n style,\n \"aria-label\": ariaLabel(field),\n \"aria-required\": false,\n },\n };\n } else {\n html = {\n name: \"input\",\n attributes: {\n type: \"text\",\n dataId: field[$data]?.[$uid] || field[$uid],\n fieldId: field[$uid],\n class: [\"xfaTextfield\"],\n style,\n \"aria-label\": ariaLabel(field),\n \"aria-required\": false,\n },\n };\n }\n\n if (isRequired(field)) {\n html.attributes[\"aria-required\"] = true;\n html.attributes.required = true;\n }\n\n return HTMLResult.success({\n name: \"label\",\n attributes: {\n class: [\"xfaLabel\"],\n },\n children: [html],\n });\n }\n}\n\nclass Time extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"time\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n\n [$finalize]() {\n // TODO: need to handle the string as a time and not as a date.\n const date = this[$content].trim();\n this[$content] = date ? new Date(date) : null;\n }\n\n [$toHTML](availableSpace) {\n return valueToHtml(this[$content] ? this[$content].toString() : \"\");\n }\n}\n\nclass TimeStamp extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"timeStamp\");\n this.id = attributes.id || \"\";\n this.server = attributes.server || \"\";\n this.type = getStringOption(attributes.type, [\"optional\", \"required\"]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass ToolTip extends StringObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"toolTip\");\n this.id = attributes.id || \"\";\n this.rid = attributes.rid || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Traversal extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"traversal\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.traverse = new XFAObjectArray();\n }\n}\n\nclass Traverse extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"traverse\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.operation = getStringOption(attributes.operation, [\n \"next\",\n \"back\",\n \"down\",\n \"first\",\n \"left\",\n \"right\",\n \"up\",\n ]);\n this.ref = attributes.ref || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.script = null;\n }\n\n get name() {\n // SOM expression: see page 94\n return this.operation;\n }\n\n [$isTransparent]() {\n return false;\n }\n}\n\nclass Ui extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"ui\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.picture = null;\n\n // One-of properties\n this.barcode = null;\n this.button = null;\n this.checkButton = null;\n this.choiceList = null;\n this.dateTimeEdit = null;\n this.defaultUi = null;\n this.imageEdit = null;\n this.numericEdit = null;\n this.passwordEdit = null;\n this.signature = null;\n this.textEdit = null;\n }\n\n [$getExtra]() {\n if (this[$extra] === undefined) {\n for (const name of Object.getOwnPropertyNames(this)) {\n if (name === \"extras\" || name === \"picture\") {\n continue;\n }\n const obj = this[name];\n if (!(obj instanceof XFAObject)) {\n continue;\n }\n\n this[$extra] = obj;\n return obj;\n }\n this[$extra] = null;\n }\n return this[$extra];\n }\n\n [$toHTML](availableSpace) {\n // TODO: picture.\n const obj = this[$getExtra]();\n if (obj) {\n return obj[$toHTML](availableSpace);\n }\n return HTMLResult.EMPTY;\n }\n}\n\nclass Validate extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"validate\", /* hasChildren = */ true);\n this.formatTest = getStringOption(attributes.formatTest, [\n \"warning\",\n \"disabled\",\n \"error\",\n ]);\n this.id = attributes.id || \"\";\n this.nullTest = getStringOption(attributes.nullTest, [\n \"disabled\",\n \"error\",\n \"warning\",\n ]);\n this.scriptTest = getStringOption(attributes.scriptTest, [\n \"error\",\n \"disabled\",\n \"warning\",\n ]);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.extras = null;\n this.message = null;\n this.picture = null;\n this.script = null;\n }\n}\n\nclass Value extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"value\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.override = getInteger({\n data: attributes.override,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.relevant = getRelevant(attributes.relevant);\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n\n // One-of properties\n this.arc = null;\n this.boolean = null;\n this.date = null;\n this.dateTime = null;\n this.decimal = null;\n this.exData = null;\n this.float = null;\n this.image = null;\n this.integer = null;\n this.line = null;\n this.rectangle = null;\n this.text = null;\n this.time = null;\n }\n\n [$setValue](value) {\n const parent = this[$getParent]();\n if (parent instanceof Field) {\n if (parent.ui?.imageEdit) {\n if (!this.image) {\n this.image = new Image({});\n this[$appendChild](this.image);\n }\n this.image[$content] = value[$content];\n return;\n }\n }\n\n const valueName = value[$nodeName];\n if (this[valueName] !== null) {\n this[valueName][$content] = value[$content];\n return;\n }\n\n // Reset all the properties.\n for (const name of Object.getOwnPropertyNames(this)) {\n const obj = this[name];\n if (obj instanceof XFAObject) {\n this[name] = null;\n this[$removeChild](obj);\n }\n }\n\n this[value[$nodeName]] = value;\n this[$appendChild](value);\n }\n\n [$text]() {\n if (this.exData) {\n if (typeof this.exData[$content] === \"string\") {\n return this.exData[$content].trim();\n }\n return this.exData[$content][$text]().trim();\n }\n for (const name of Object.getOwnPropertyNames(this)) {\n if (name === \"image\") {\n continue;\n }\n const obj = this[name];\n if (obj instanceof XFAObject) {\n return (obj[$content] || \"\").toString().trim();\n }\n }\n return null;\n }\n\n [$toHTML](availableSpace) {\n for (const name of Object.getOwnPropertyNames(this)) {\n const obj = this[name];\n if (!(obj instanceof XFAObject)) {\n continue;\n }\n\n return obj[$toHTML](availableSpace);\n }\n\n return HTMLResult.EMPTY;\n }\n}\n\nclass Variables extends XFAObject {\n constructor(attributes) {\n super(TEMPLATE_NS_ID, \"variables\", /* hasChildren = */ true);\n this.id = attributes.id || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n this.boolean = new XFAObjectArray();\n this.date = new XFAObjectArray();\n this.dateTime = new XFAObjectArray();\n this.decimal = new XFAObjectArray();\n this.exData = new XFAObjectArray();\n this.float = new XFAObjectArray();\n this.image = new XFAObjectArray();\n this.integer = new XFAObjectArray();\n this.manifest = new XFAObjectArray();\n this.script = new XFAObjectArray();\n this.text = new XFAObjectArray();\n this.time = new XFAObjectArray();\n }\n\n [$isTransparent]() {\n return true;\n }\n}\n\nclass TemplateNamespace {\n static [$buildXFAObject](name, attributes) {\n if (TemplateNamespace.hasOwnProperty(name)) {\n const node = TemplateNamespace[name](attributes);\n node[$setSetAttributes](attributes);\n return node;\n }\n return undefined;\n }\n\n static appearanceFilter(attrs) {\n return new AppearanceFilter(attrs);\n }\n\n static arc(attrs) {\n return new Arc(attrs);\n }\n\n static area(attrs) {\n return new Area(attrs);\n }\n\n static assist(attrs) {\n return new Assist(attrs);\n }\n\n static barcode(attrs) {\n return new Barcode(attrs);\n }\n\n static bind(attrs) {\n return new Bind(attrs);\n }\n\n static bindItems(attrs) {\n return new BindItems(attrs);\n }\n\n static bookend(attrs) {\n return new Bookend(attrs);\n }\n\n static boolean(attrs) {\n return new BooleanElement(attrs);\n }\n\n static border(attrs) {\n return new Border(attrs);\n }\n\n static break(attrs) {\n return new Break(attrs);\n }\n\n static breakAfter(attrs) {\n return new BreakAfter(attrs);\n }\n\n static breakBefore(attrs) {\n return new BreakBefore(attrs);\n }\n\n static button(attrs) {\n return new Button(attrs);\n }\n\n static calculate(attrs) {\n return new Calculate(attrs);\n }\n\n static caption(attrs) {\n return new Caption(attrs);\n }\n\n static certificate(attrs) {\n return new Certificate(attrs);\n }\n\n static certificates(attrs) {\n return new Certificates(attrs);\n }\n\n static checkButton(attrs) {\n return new CheckButton(attrs);\n }\n\n static choiceList(attrs) {\n return new ChoiceList(attrs);\n }\n\n static color(attrs) {\n return new Color(attrs);\n }\n\n static comb(attrs) {\n return new Comb(attrs);\n }\n\n static connect(attrs) {\n return new Connect(attrs);\n }\n\n static contentArea(attrs) {\n return new ContentArea(attrs);\n }\n\n static corner(attrs) {\n return new Corner(attrs);\n }\n\n static date(attrs) {\n return new DateElement(attrs);\n }\n\n static dateTime(attrs) {\n return new DateTime(attrs);\n }\n\n static dateTimeEdit(attrs) {\n return new DateTimeEdit(attrs);\n }\n\n static decimal(attrs) {\n return new Decimal(attrs);\n }\n\n static defaultUi(attrs) {\n return new DefaultUi(attrs);\n }\n\n static desc(attrs) {\n return new Desc(attrs);\n }\n\n static digestMethod(attrs) {\n return new DigestMethod(attrs);\n }\n\n static digestMethods(attrs) {\n return new DigestMethods(attrs);\n }\n\n static draw(attrs) {\n return new Draw(attrs);\n }\n\n static edge(attrs) {\n return new Edge(attrs);\n }\n\n static encoding(attrs) {\n return new Encoding(attrs);\n }\n\n static encodings(attrs) {\n return new Encodings(attrs);\n }\n\n static encrypt(attrs) {\n return new Encrypt(attrs);\n }\n\n static encryptData(attrs) {\n return new EncryptData(attrs);\n }\n\n static encryption(attrs) {\n return new Encryption(attrs);\n }\n\n static encryptionMethod(attrs) {\n return new EncryptionMethod(attrs);\n }\n\n static encryptionMethods(attrs) {\n return new EncryptionMethods(attrs);\n }\n\n static event(attrs) {\n return new Event(attrs);\n }\n\n static exData(attrs) {\n return new ExData(attrs);\n }\n\n static exObject(attrs) {\n return new ExObject(attrs);\n }\n\n static exclGroup(attrs) {\n return new ExclGroup(attrs);\n }\n\n static execute(attrs) {\n return new Execute(attrs);\n }\n\n static extras(attrs) {\n return new Extras(attrs);\n }\n\n static field(attrs) {\n return new Field(attrs);\n }\n\n static fill(attrs) {\n return new Fill(attrs);\n }\n\n static filter(attrs) {\n return new Filter(attrs);\n }\n\n static float(attrs) {\n return new Float(attrs);\n }\n\n static font(attrs) {\n return new Font(attrs);\n }\n\n static format(attrs) {\n return new Format(attrs);\n }\n\n static handler(attrs) {\n return new Handler(attrs);\n }\n\n static hyphenation(attrs) {\n return new Hyphenation(attrs);\n }\n\n static image(attrs) {\n return new Image(attrs);\n }\n\n static imageEdit(attrs) {\n return new ImageEdit(attrs);\n }\n\n static integer(attrs) {\n return new Integer(attrs);\n }\n\n static issuers(attrs) {\n return new Issuers(attrs);\n }\n\n static items(attrs) {\n return new Items(attrs);\n }\n\n static keep(attrs) {\n return new Keep(attrs);\n }\n\n static keyUsage(attrs) {\n return new KeyUsage(attrs);\n }\n\n static line(attrs) {\n return new Line(attrs);\n }\n\n static linear(attrs) {\n return new Linear(attrs);\n }\n\n static lockDocument(attrs) {\n return new LockDocument(attrs);\n }\n\n static manifest(attrs) {\n return new Manifest(attrs);\n }\n\n static margin(attrs) {\n return new Margin(attrs);\n }\n\n static mdp(attrs) {\n return new Mdp(attrs);\n }\n\n static medium(attrs) {\n return new Medium(attrs);\n }\n\n static message(attrs) {\n return new Message(attrs);\n }\n\n static numericEdit(attrs) {\n return new NumericEdit(attrs);\n }\n\n static occur(attrs) {\n return new Occur(attrs);\n }\n\n static oid(attrs) {\n return new Oid(attrs);\n }\n\n static oids(attrs) {\n return new Oids(attrs);\n }\n\n static overflow(attrs) {\n return new Overflow(attrs);\n }\n\n static pageArea(attrs) {\n return new PageArea(attrs);\n }\n\n static pageSet(attrs) {\n return new PageSet(attrs);\n }\n\n static para(attrs) {\n return new Para(attrs);\n }\n\n static passwordEdit(attrs) {\n return new PasswordEdit(attrs);\n }\n\n static pattern(attrs) {\n return new Pattern(attrs);\n }\n\n static picture(attrs) {\n return new Picture(attrs);\n }\n\n static proto(attrs) {\n return new Proto(attrs);\n }\n\n static radial(attrs) {\n return new Radial(attrs);\n }\n\n static reason(attrs) {\n return new Reason(attrs);\n }\n\n static reasons(attrs) {\n return new Reasons(attrs);\n }\n\n static rectangle(attrs) {\n return new Rectangle(attrs);\n }\n\n static ref(attrs) {\n return new RefElement(attrs);\n }\n\n static script(attrs) {\n return new Script(attrs);\n }\n\n static setProperty(attrs) {\n return new SetProperty(attrs);\n }\n\n static signData(attrs) {\n return new SignData(attrs);\n }\n\n static signature(attrs) {\n return new Signature(attrs);\n }\n\n static signing(attrs) {\n return new Signing(attrs);\n }\n\n static solid(attrs) {\n return new Solid(attrs);\n }\n\n static speak(attrs) {\n return new Speak(attrs);\n }\n\n static stipple(attrs) {\n return new Stipple(attrs);\n }\n\n static subform(attrs) {\n return new Subform(attrs);\n }\n\n static subformSet(attrs) {\n return new SubformSet(attrs);\n }\n\n static subjectDN(attrs) {\n return new SubjectDN(attrs);\n }\n\n static subjectDNs(attrs) {\n return new SubjectDNs(attrs);\n }\n\n static submit(attrs) {\n return new Submit(attrs);\n }\n\n static template(attrs) {\n return new Template(attrs);\n }\n\n static text(attrs) {\n return new Text(attrs);\n }\n\n static textEdit(attrs) {\n return new TextEdit(attrs);\n }\n\n static time(attrs) {\n return new Time(attrs);\n }\n\n static timeStamp(attrs) {\n return new TimeStamp(attrs);\n }\n\n static toolTip(attrs) {\n return new ToolTip(attrs);\n }\n\n static traversal(attrs) {\n return new Traversal(attrs);\n }\n\n static traverse(attrs) {\n return new Traverse(attrs);\n }\n\n static ui(attrs) {\n return new Ui(attrs);\n }\n\n static validate(attrs) {\n return new Validate(attrs);\n }\n\n static value(attrs) {\n return new Value(attrs);\n }\n\n static variables(attrs) {\n return new Variables(attrs);\n }\n}\n\nexport {\n BindItems,\n Field,\n Items,\n SetProperty,\n Template,\n TemplateNamespace,\n Text,\n Value,\n};\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n $appendChild,\n $clone,\n $consumed,\n $content,\n $data,\n $finalize,\n $getAttributeIt,\n $getChildren,\n $getDataValue,\n $getParent,\n $getRealChildrenByNameIt,\n $hasSettableValue,\n $indexOf,\n $insertAt,\n $isBindable,\n $isDataValue,\n $isDescendent,\n $namespaceId,\n $nodeName,\n $removeChild,\n $setValue,\n $text,\n} from \"./symbol_utils.js\";\nimport { BindItems, Field, Items, SetProperty, Text } from \"./template.js\";\nimport { createDataNode, searchNode } from \"./som.js\";\nimport { XFAAttribute, XFAObjectArray, XmlObject } from \"./xfa_object.js\";\nimport { NamespaceIds } from \"./namespaces.js\";\nimport { warn } from \"../../shared/util.js\";\n\nconst NS_DATASETS = NamespaceIds.datasets.id;\n\nfunction createText(content) {\n const node = new Text({});\n node[$content] = content;\n return node;\n}\n\nclass Binder {\n constructor(root) {\n this.root = root;\n this.datasets = root.datasets;\n this.data =\n root.datasets?.data || new XmlObject(NamespaceIds.datasets.id, \"data\");\n this.emptyMerge = this.data[$getChildren]().length === 0;\n\n this.root.form = this.form = root.template[$clone]();\n }\n\n _isConsumeData() {\n return !this.emptyMerge && this._mergeMode;\n }\n\n _isMatchTemplate() {\n return !this._isConsumeData();\n }\n\n bind() {\n this._bindElement(this.form, this.data);\n return this.form;\n }\n\n getData() {\n return this.data;\n }\n\n _bindValue(formNode, data, picture) {\n // Nodes must have the same \"type\": container or value.\n // Here we make the link between form node and\n // data node (through $data property): we'll use it\n // to save form data.\n\n formNode[$data] = data;\n if (formNode[$hasSettableValue]()) {\n if (data[$isDataValue]()) {\n const value = data[$getDataValue]();\n // TODO: use picture.\n formNode[$setValue](createText(value));\n } else if (\n formNode instanceof Field &&\n formNode.ui?.choiceList?.open === \"multiSelect\"\n ) {\n const value = data[$getChildren]()\n .map(child => child[$content].trim())\n .join(\"\\n\");\n formNode[$setValue](createText(value));\n } else if (this._isConsumeData()) {\n warn(`XFA - Nodes haven't the same type.`);\n }\n } else if (!data[$isDataValue]() || this._isMatchTemplate()) {\n this._bindElement(formNode, data);\n } else {\n warn(`XFA - Nodes haven't the same type.`);\n }\n }\n\n _findDataByNameToConsume(name, isValue, dataNode, global) {\n if (!name) {\n return null;\n }\n\n // Firstly, we try to find a node with the given name:\n // - in dataNode;\n // - if not found, then in parent;\n // - and if not in found, then in grand-parent.\n let generator, match;\n for (let i = 0; i < 3; i++) {\n generator = dataNode[$getRealChildrenByNameIt](\n name,\n /* allTransparent = */ false,\n /* skipConsumed = */ true\n );\n // Try to find a match of the same kind.\n while (true) {\n match = generator.next().value;\n if (!match) {\n break;\n }\n\n if (isValue === match[$isDataValue]()) {\n return match;\n }\n }\n if (\n dataNode[$namespaceId] === NamespaceIds.datasets.id &&\n dataNode[$nodeName] === \"data\"\n ) {\n break;\n }\n dataNode = dataNode[$getParent]();\n }\n\n if (!global) {\n return null;\n }\n\n // Secondly, if global try to find it just under the root of datasets\n // (which is the location of global variables).\n generator = this.data[$getRealChildrenByNameIt](\n name,\n /* allTransparent = */ true,\n /* skipConsumed = */ false\n );\n\n match = generator.next().value;\n if (match) {\n return match;\n }\n\n // Thirdly, try to find it in attributes.\n generator = this.data[$getAttributeIt](name, /* skipConsumed = */ true);\n match = generator.next().value;\n if (match?.[$isDataValue]()) {\n return match;\n }\n\n return null;\n }\n\n _setProperties(formNode, dataNode) {\n // For example:\n // \n // \n // \n // \n // \n\n if (!formNode.hasOwnProperty(\"setProperty\")) {\n return;\n }\n\n for (const { ref, target, connection } of formNode.setProperty.children) {\n if (connection) {\n // TODO: evaluate if we should implement this feature.\n // Skip for security reasons.\n continue;\n }\n if (!ref) {\n continue;\n }\n\n const nodes = searchNode(\n this.root,\n dataNode,\n ref,\n false /* = dotDotAllowed */,\n false /* = useCache */\n );\n if (!nodes) {\n warn(`XFA - Invalid reference: ${ref}.`);\n continue;\n }\n const [node] = nodes;\n\n if (!node[$isDescendent](this.data)) {\n warn(`XFA - Invalid node: must be a data node.`);\n continue;\n }\n\n const targetNodes = searchNode(\n this.root,\n formNode,\n target,\n false /* = dotDotAllowed */,\n false /* = useCache */\n );\n if (!targetNodes) {\n warn(`XFA - Invalid target: ${target}.`);\n continue;\n }\n const [targetNode] = targetNodes;\n\n if (!targetNode[$isDescendent](formNode)) {\n warn(`XFA - Invalid target: must be a property or subproperty.`);\n continue;\n }\n\n const targetParent = targetNode[$getParent]();\n if (\n targetNode instanceof SetProperty ||\n targetParent instanceof SetProperty\n ) {\n warn(\n `XFA - Invalid target: cannot be a setProperty or one of its properties.`\n );\n continue;\n }\n\n if (\n targetNode instanceof BindItems ||\n targetParent instanceof BindItems\n ) {\n warn(\n `XFA - Invalid target: cannot be a bindItems or one of its properties.`\n );\n continue;\n }\n\n const content = node[$text]();\n const name = targetNode[$nodeName];\n\n if (targetNode instanceof XFAAttribute) {\n const attrs = Object.create(null);\n attrs[name] = content;\n const obj = Reflect.construct(\n Object.getPrototypeOf(targetParent).constructor,\n [attrs]\n );\n targetParent[name] = obj[name];\n continue;\n }\n\n if (!targetNode.hasOwnProperty($content)) {\n warn(`XFA - Invalid node to use in setProperty`);\n continue;\n }\n\n targetNode[$data] = node;\n targetNode[$content] = content;\n targetNode[$finalize]();\n }\n }\n\n _bindItems(formNode, dataNode) {\n // For example:\n // \n // \n // \n // \n\n if (\n !formNode.hasOwnProperty(\"items\") ||\n !formNode.hasOwnProperty(\"bindItems\") ||\n formNode.bindItems.isEmpty()\n ) {\n return;\n }\n\n for (const item of formNode.items.children) {\n formNode[$removeChild](item);\n }\n\n formNode.items.clear();\n\n const labels = new Items({});\n const values = new Items({});\n\n formNode[$appendChild](labels);\n formNode.items.push(labels);\n\n formNode[$appendChild](values);\n formNode.items.push(values);\n\n for (const { ref, labelRef, valueRef, connection } of formNode.bindItems\n .children) {\n if (connection) {\n // TODO: evaluate if we should implement this feature.\n // Skip for security reasons.\n continue;\n }\n if (!ref) {\n continue;\n }\n\n const nodes = searchNode(\n this.root,\n dataNode,\n ref,\n false /* = dotDotAllowed */,\n false /* = useCache */\n );\n if (!nodes) {\n warn(`XFA - Invalid reference: ${ref}.`);\n continue;\n }\n for (const node of nodes) {\n if (!node[$isDescendent](this.datasets)) {\n warn(`XFA - Invalid ref (${ref}): must be a datasets child.`);\n continue;\n }\n\n const labelNodes = searchNode(\n this.root,\n node,\n labelRef,\n true /* = dotDotAllowed */,\n false /* = useCache */\n );\n if (!labelNodes) {\n warn(`XFA - Invalid label: ${labelRef}.`);\n continue;\n }\n const [labelNode] = labelNodes;\n\n if (!labelNode[$isDescendent](this.datasets)) {\n warn(`XFA - Invalid label: must be a datasets child.`);\n continue;\n }\n\n const valueNodes = searchNode(\n this.root,\n node,\n valueRef,\n true /* = dotDotAllowed */,\n false /* = useCache */\n );\n if (!valueNodes) {\n warn(`XFA - Invalid value: ${valueRef}.`);\n continue;\n }\n const [valueNode] = valueNodes;\n\n if (!valueNode[$isDescendent](this.datasets)) {\n warn(`XFA - Invalid value: must be a datasets child.`);\n continue;\n }\n\n const label = createText(labelNode[$text]());\n const value = createText(valueNode[$text]());\n\n labels[$appendChild](label);\n labels.text.push(label);\n\n values[$appendChild](value);\n values.text.push(value);\n }\n }\n }\n\n _bindOccurrences(formNode, matches, picture) {\n // Insert nodes which are not in the template but reflect\n // what we've in data tree.\n\n let baseClone;\n if (matches.length > 1) {\n // Clone before binding to avoid bad state.\n baseClone = formNode[$clone]();\n baseClone[$removeChild](baseClone.occur);\n baseClone.occur = null;\n }\n\n this._bindValue(formNode, matches[0], picture);\n this._setProperties(formNode, matches[0]);\n this._bindItems(formNode, matches[0]);\n\n if (matches.length === 1) {\n return;\n }\n\n const parent = formNode[$getParent]();\n const name = formNode[$nodeName];\n const pos = parent[$indexOf](formNode);\n\n for (let i = 1, ii = matches.length; i < ii; i++) {\n const match = matches[i];\n const clone = baseClone[$clone]();\n parent[name].push(clone);\n parent[$insertAt](pos + i, clone);\n\n this._bindValue(clone, match, picture);\n this._setProperties(clone, match);\n this._bindItems(clone, match);\n }\n }\n\n _createOccurrences(formNode) {\n if (!this.emptyMerge) {\n return;\n }\n\n const { occur } = formNode;\n if (!occur || occur.initial <= 1) {\n return;\n }\n\n const parent = formNode[$getParent]();\n const name = formNode[$nodeName];\n\n if (!(parent[name] instanceof XFAObjectArray)) {\n return;\n }\n\n let currentNumber;\n if (formNode.name) {\n currentNumber = parent[name].children.filter(\n e => e.name === formNode.name\n ).length;\n } else {\n currentNumber = parent[name].children.length;\n }\n\n const pos = parent[$indexOf](formNode) + 1;\n const ii = occur.initial - currentNumber;\n if (ii) {\n const nodeClone = formNode[$clone]();\n nodeClone[$removeChild](nodeClone.occur);\n nodeClone.occur = null;\n parent[name].push(nodeClone);\n parent[$insertAt](pos, nodeClone);\n\n for (let i = 1; i < ii; i++) {\n const clone = nodeClone[$clone]();\n parent[name].push(clone);\n parent[$insertAt](pos + i, clone);\n }\n }\n }\n\n _getOccurInfo(formNode) {\n const { name, occur } = formNode;\n if (!occur || !name) {\n return [1, 1];\n }\n const max = occur.max === -1 ? Infinity : occur.max;\n return [occur.min, max];\n }\n\n _setAndBind(formNode, dataNode) {\n this._setProperties(formNode, dataNode);\n this._bindItems(formNode, dataNode);\n this._bindElement(formNode, dataNode);\n }\n\n _bindElement(formNode, dataNode) {\n // Some nodes can be useless because min=0 so remove them\n // after the loop to avoid bad things.\n\n const uselessNodes = [];\n\n this._createOccurrences(formNode);\n\n for (const child of formNode[$getChildren]()) {\n if (child[$data]) {\n // Already bound.\n continue;\n }\n\n if (this._mergeMode === undefined && child[$nodeName] === \"subform\") {\n this._mergeMode = child.mergeMode === \"consumeData\";\n\n // XFA specs p. 182:\n // The highest-level subform and the data node representing\n // the current record are special; they are always\n // bound even if their names don't match.\n const dataChildren = dataNode[$getChildren]();\n if (dataChildren.length > 0) {\n this._bindOccurrences(child, [dataChildren[0]], null);\n } else if (this.emptyMerge) {\n const nsId =\n dataNode[$namespaceId] === NS_DATASETS\n ? -1\n : dataNode[$namespaceId];\n const dataChild = (child[$data] = new XmlObject(\n nsId,\n child.name || \"root\"\n ));\n dataNode[$appendChild](dataChild);\n this._bindElement(child, dataChild);\n }\n continue;\n }\n\n if (!child[$isBindable]()) {\n // The node cannot contain some new data so there is nothing\n // to create in the data node.\n continue;\n }\n\n let global = false;\n let picture = null;\n let ref = null;\n let match = null;\n if (child.bind) {\n switch (child.bind.match) {\n case \"none\":\n this._setAndBind(child, dataNode);\n continue;\n case \"global\":\n global = true;\n break;\n case \"dataRef\":\n if (!child.bind.ref) {\n warn(`XFA - ref is empty in node ${child[$nodeName]}.`);\n this._setAndBind(child, dataNode);\n continue;\n }\n ref = child.bind.ref;\n break;\n default:\n break;\n }\n if (child.bind.picture) {\n picture = child.bind.picture[$content];\n }\n }\n\n const [min, max] = this._getOccurInfo(child);\n\n if (ref) {\n // Don't use a cache for searching: nodes can change during binding.\n match = searchNode(\n this.root,\n dataNode,\n ref,\n true /* = dotDotAllowed */,\n false /* = useCache */\n );\n if (match === null) {\n // Nothing found: we must create some nodes in data in order\n // to have something to match with the given expression.\n // See http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.364.2157&rep=rep1&type=pdf#page=199\n match = createDataNode(this.data, dataNode, ref);\n if (!match) {\n // For example if the node contains a .(...) then it isn't\n // findable.\n // TODO: remove this when .(...) is implemented.\n continue;\n }\n if (this._isConsumeData()) {\n match[$consumed] = true;\n }\n\n // Don't bind the value in newly created node because it's empty.\n this._setAndBind(child, match);\n continue;\n } else {\n if (this._isConsumeData()) {\n // Filter out consumed nodes.\n match = match.filter(node => !node[$consumed]);\n }\n if (match.length > max) {\n match = match.slice(0, max);\n } else if (match.length === 0) {\n match = null;\n }\n if (match && this._isConsumeData()) {\n match.forEach(node => {\n node[$consumed] = true;\n });\n }\n }\n } else {\n if (!child.name) {\n this._setAndBind(child, dataNode);\n continue;\n }\n if (this._isConsumeData()) {\n // In consumeData mode, search for the next node with the given name.\n // occurs.max gives us the max number of node to match.\n const matches = [];\n while (matches.length < max) {\n const found = this._findDataByNameToConsume(\n child.name,\n child[$hasSettableValue](),\n dataNode,\n global\n );\n\n if (!found) {\n break;\n }\n found[$consumed] = true;\n matches.push(found);\n }\n match = matches.length > 0 ? matches : null;\n } else {\n // If we've an empty merge, there are no reason\n // to make multiple bind so skip consumed nodes.\n match = dataNode[$getRealChildrenByNameIt](\n child.name,\n /* allTransparent = */ false,\n /* skipConsumed = */ this.emptyMerge\n ).next().value;\n if (!match) {\n // If there is no match (no data) and `min === 0` then\n // the container is entirely excluded.\n // https://www.pdfa.org/norm-refs/XFA-3_3.pdf#G12.1428332\n if (min === 0) {\n uselessNodes.push(child);\n continue;\n }\n // We're in matchTemplate mode so create a node in data to reflect\n // what we've in template.\n const nsId =\n dataNode[$namespaceId] === NS_DATASETS\n ? -1\n : dataNode[$namespaceId];\n match = child[$data] = new XmlObject(nsId, child.name);\n if (this.emptyMerge) {\n match[$consumed] = true;\n }\n dataNode[$appendChild](match);\n\n // Don't bind the value in newly created node because it's empty.\n this._setAndBind(child, match);\n continue;\n }\n if (this.emptyMerge) {\n match[$consumed] = true;\n }\n match = [match];\n }\n }\n\n if (match) {\n this._bindOccurrences(child, match, picture);\n } else if (min > 0) {\n this._setAndBind(child, dataNode);\n } else {\n uselessNodes.push(child);\n }\n }\n\n uselessNodes.forEach(node => node[$getParent]()[$removeChild](node));\n }\n}\n\nexport { Binder };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n $getAttributes,\n $getChildren,\n $nodeName,\n $setValue,\n $toString,\n $uid,\n} from \"./symbol_utils.js\";\n\nclass DataHandler {\n constructor(root, data) {\n this.data = data;\n this.dataset = root.datasets || null;\n }\n\n serialize(storage) {\n const stack = [[-1, this.data[$getChildren]()]];\n\n while (stack.length > 0) {\n const last = stack.at(-1);\n const [i, children] = last;\n if (i + 1 === children.length) {\n stack.pop();\n continue;\n }\n\n const child = children[++last[0]];\n const storageEntry = storage.get(child[$uid]);\n if (storageEntry) {\n child[$setValue](storageEntry);\n } else {\n const attributes = child[$getAttributes]();\n for (const value of attributes.values()) {\n const entry = storage.get(value[$uid]);\n if (entry) {\n value[$setValue](entry);\n break;\n }\n }\n }\n\n const nodes = child[$getChildren]();\n if (nodes.length > 0) {\n stack.push([-1, nodes]);\n }\n }\n\n const buf = [\n ``,\n ];\n if (this.dataset) {\n // Dump nodes other than data: they can contains for example\n // some data for choice lists.\n for (const child of this.dataset[$getChildren]()) {\n if (child[$nodeName] !== \"data\") {\n child[$toString](buf);\n }\n }\n }\n this.data[$toString](buf);\n buf.push(\"\");\n\n return buf.join(\"\");\n }\n}\n\nexport { DataHandler };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { $buildXFAObject, NamespaceIds } from \"./namespaces.js\";\nimport { $content, $finalize } from \"./symbol_utils.js\";\nimport {\n ContentObject,\n IntegerObject,\n Option01,\n Option10,\n OptionObject,\n StringObject,\n XFAObject,\n XFAObjectArray,\n} from \"./xfa_object.js\";\nimport { getInteger, getStringOption } from \"./utils.js\";\nimport { shadow, warn } from \"../../shared/util.js\";\n\nconst CONFIG_NS_ID = NamespaceIds.config.id;\n\nclass Acrobat extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"acrobat\", /* hasChildren = */ true);\n this.acrobat7 = null;\n this.autoSave = null;\n this.common = null;\n this.validate = null;\n this.validateApprovalSignatures = null;\n this.submitUrl = new XFAObjectArray();\n }\n}\n\nclass Acrobat7 extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"acrobat7\", /* hasChildren = */ true);\n this.dynamicRender = null;\n }\n}\n\nclass ADBE_JSConsole extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"ADBE_JSConsole\", [\"delegate\", \"Enable\", \"Disable\"]);\n }\n}\n\nclass ADBE_JSDebugger extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"ADBE_JSDebugger\", [\"delegate\", \"Enable\", \"Disable\"]);\n }\n}\n\nclass AddSilentPrint extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"addSilentPrint\");\n }\n}\n\nclass AddViewerPreferences extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"addViewerPreferences\");\n }\n}\n\nclass AdjustData extends Option10 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"adjustData\");\n }\n}\n\nclass AdobeExtensionLevel extends IntegerObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"adobeExtensionLevel\", 0, n => n >= 1 && n <= 8);\n }\n}\n\nclass Agent extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"agent\", /* hasChildren = */ true);\n this.name = attributes.name ? attributes.name.trim() : \"\";\n this.common = new XFAObjectArray();\n }\n}\n\nclass AlwaysEmbed extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"alwaysEmbed\");\n }\n}\n\nclass Amd extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"amd\");\n }\n}\n\nclass Area extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"area\");\n this.level = getInteger({\n data: attributes.level,\n defaultValue: 0,\n validate: n => n >= 1 && n <= 3,\n });\n this.name = getStringOption(attributes.name, [\n \"\",\n \"barcode\",\n \"coreinit\",\n \"deviceDriver\",\n \"font\",\n \"general\",\n \"layout\",\n \"merge\",\n \"script\",\n \"signature\",\n \"sourceSet\",\n \"templateCache\",\n ]);\n }\n}\n\nclass Attributes extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"attributes\", [\"preserve\", \"delegate\", \"ignore\"]);\n }\n}\n\nclass AutoSave extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"autoSave\", [\"disabled\", \"enabled\"]);\n }\n}\n\nclass Base extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"base\");\n }\n}\n\nclass BatchOutput extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"batchOutput\");\n this.format = getStringOption(attributes.format, [\n \"none\",\n \"concat\",\n \"zip\",\n \"zipCompress\",\n ]);\n }\n}\n\nclass BehaviorOverride extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"behaviorOverride\");\n }\n\n [$finalize]() {\n this[$content] = new Map(\n this[$content]\n .trim()\n .split(/\\s+/)\n .filter(x => x.includes(\":\"))\n .map(x => x.split(\":\", 2))\n );\n }\n}\n\nclass Cache extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"cache\", /* hasChildren = */ true);\n this.templateCache = null;\n }\n}\n\nclass Change extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"change\");\n }\n}\n\nclass Common extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"common\", /* hasChildren = */ true);\n this.data = null;\n this.locale = null;\n this.localeSet = null;\n this.messaging = null;\n this.suppressBanner = null;\n this.template = null;\n this.validationMessaging = null;\n this.versionControl = null;\n this.log = new XFAObjectArray();\n }\n}\n\nclass Compress extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"compress\");\n this.scope = getStringOption(attributes.scope, [\"imageOnly\", \"document\"]);\n }\n}\n\nclass CompressLogicalStructure extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"compressLogicalStructure\");\n }\n}\n\nclass CompressObjectStream extends Option10 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"compressObjectStream\");\n }\n}\n\nclass Compression extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"compression\", /* hasChildren = */ true);\n this.compressLogicalStructure = null;\n this.compressObjectStream = null;\n this.level = null;\n this.type = null;\n }\n}\n\nclass Config extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"config\", /* hasChildren = */ true);\n this.acrobat = null;\n this.present = null;\n this.trace = null;\n this.agent = new XFAObjectArray();\n }\n}\n\nclass Conformance extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"conformance\", [\"A\", \"B\"]);\n }\n}\n\nclass ContentCopy extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"contentCopy\");\n }\n}\n\nclass Copies extends IntegerObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"copies\", 1, n => n >= 1);\n }\n}\n\nclass Creator extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"creator\");\n }\n}\n\nclass CurrentPage extends IntegerObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"currentPage\", 0, n => n >= 0);\n }\n}\n\nclass Data extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"data\", /* hasChildren = */ true);\n this.adjustData = null;\n this.attributes = null;\n this.incrementalLoad = null;\n this.outputXSL = null;\n this.range = null;\n this.record = null;\n this.startNode = null;\n this.uri = null;\n this.window = null;\n this.xsl = null;\n this.excludeNS = new XFAObjectArray();\n this.transform = new XFAObjectArray();\n }\n}\n\nclass Debug extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"debug\", /* hasChildren = */ true);\n this.uri = null;\n }\n}\n\nclass DefaultTypeface extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"defaultTypeface\");\n this.writingScript = getStringOption(attributes.writingScript, [\n \"*\",\n \"Arabic\",\n \"Cyrillic\",\n \"EastEuropeanRoman\",\n \"Greek\",\n \"Hebrew\",\n \"Japanese\",\n \"Korean\",\n \"Roman\",\n \"SimplifiedChinese\",\n \"Thai\",\n \"TraditionalChinese\",\n \"Vietnamese\",\n ]);\n }\n}\n\nclass Destination extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"destination\", [\n \"pdf\",\n \"pcl\",\n \"ps\",\n \"webClient\",\n \"zpl\",\n ]);\n }\n}\n\nclass DocumentAssembly extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"documentAssembly\");\n }\n}\n\nclass Driver extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"driver\", /* hasChildren = */ true);\n this.name = attributes.name ? attributes.name.trim() : \"\";\n this.fontInfo = null;\n this.xdc = null;\n }\n}\n\nclass DuplexOption extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"duplexOption\", [\n \"simplex\",\n \"duplexFlipLongEdge\",\n \"duplexFlipShortEdge\",\n ]);\n }\n}\n\nclass DynamicRender extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"dynamicRender\", [\"forbidden\", \"required\"]);\n }\n}\n\nclass Embed extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"embed\");\n }\n}\n\nclass Encrypt extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"encrypt\");\n }\n}\n\nclass Encryption extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"encryption\", /* hasChildren = */ true);\n this.encrypt = null;\n this.encryptionLevel = null;\n this.permissions = null;\n }\n}\n\nclass EncryptionLevel extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"encryptionLevel\", [\"40bit\", \"128bit\"]);\n }\n}\n\nclass Enforce extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"enforce\");\n }\n}\n\nclass Equate extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"equate\");\n\n this.force = getInteger({\n data: attributes.force,\n defaultValue: 1,\n validate: n => n === 0,\n });\n\n this.from = attributes.from || \"\";\n this.to = attributes.to || \"\";\n }\n}\n\nclass EquateRange extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"equateRange\");\n\n this.from = attributes.from || \"\";\n this.to = attributes.to || \"\";\n this._unicodeRange = attributes.unicodeRange || \"\";\n }\n\n get unicodeRange() {\n const ranges = [];\n const unicodeRegex = /U\\+([0-9a-fA-F]+)/;\n const unicodeRange = this._unicodeRange;\n for (let range of unicodeRange\n .split(\",\")\n .map(x => x.trim())\n .filter(x => !!x)) {\n range = range.split(\"-\", 2).map(x => {\n const found = x.match(unicodeRegex);\n if (!found) {\n return 0;\n }\n return parseInt(found[1], 16);\n });\n if (range.length === 1) {\n range.push(range[0]);\n }\n ranges.push(range);\n }\n return shadow(this, \"unicodeRange\", ranges);\n }\n}\n\nclass Exclude extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"exclude\");\n }\n\n [$finalize]() {\n this[$content] = this[$content]\n .trim()\n .split(/\\s+/)\n .filter(\n x =>\n x &&\n [\n \"calculate\",\n \"close\",\n \"enter\",\n \"exit\",\n \"initialize\",\n \"ready\",\n \"validate\",\n ].includes(x)\n );\n }\n}\n\nclass ExcludeNS extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"excludeNS\");\n }\n}\n\nclass FlipLabel extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"flipLabel\", [\"usePrinterSetting\", \"on\", \"off\"]);\n }\n}\n\nclass FontInfo extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"fontInfo\", /* hasChildren = */ true);\n this.embed = null;\n this.map = null;\n this.subsetBelow = null;\n this.alwaysEmbed = new XFAObjectArray();\n this.defaultTypeface = new XFAObjectArray();\n this.neverEmbed = new XFAObjectArray();\n }\n}\n\nclass FormFieldFilling extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"formFieldFilling\");\n }\n}\n\nclass GroupParent extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"groupParent\");\n }\n}\n\nclass IfEmpty extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"ifEmpty\", [\n \"dataValue\",\n \"dataGroup\",\n \"ignore\",\n \"remove\",\n ]);\n }\n}\n\nclass IncludeXDPContent extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"includeXDPContent\");\n }\n}\n\nclass IncrementalLoad extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"incrementalLoad\", [\"none\", \"forwardOnly\"]);\n }\n}\n\nclass IncrementalMerge extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"incrementalMerge\");\n }\n}\n\nclass Interactive extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"interactive\");\n }\n}\n\nclass Jog extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"jog\", [\"usePrinterSetting\", \"none\", \"pageSet\"]);\n }\n}\n\nclass LabelPrinter extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"labelPrinter\", /* hasChildren = */ true);\n this.name = getStringOption(attributes.name, [\"zpl\", \"dpl\", \"ipl\", \"tcpl\"]);\n this.batchOutput = null;\n this.flipLabel = null;\n this.fontInfo = null;\n this.xdc = null;\n }\n}\n\nclass Layout extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"layout\", [\"paginate\", \"panel\"]);\n }\n}\n\nclass Level extends IntegerObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"level\", 0, n => n > 0);\n }\n}\n\nclass Linearized extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"linearized\");\n }\n}\n\nclass Locale extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"locale\");\n }\n}\n\nclass LocaleSet extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"localeSet\");\n }\n}\n\nclass Log extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"log\", /* hasChildren = */ true);\n this.mode = null;\n this.threshold = null;\n this.to = null;\n this.uri = null;\n }\n}\n\n// Renamed in MapElement to avoid confusion with usual js Map.\nclass MapElement extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"map\", /* hasChildren = */ true);\n this.equate = new XFAObjectArray();\n this.equateRange = new XFAObjectArray();\n }\n}\n\nclass MediumInfo extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"mediumInfo\", /* hasChildren = */ true);\n this.map = null;\n }\n}\n\nclass Message extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"message\", /* hasChildren = */ true);\n this.msgId = null;\n this.severity = null;\n }\n}\n\nclass Messaging extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"messaging\", /* hasChildren = */ true);\n this.message = new XFAObjectArray();\n }\n}\n\nclass Mode extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"mode\", [\"append\", \"overwrite\"]);\n }\n}\n\nclass ModifyAnnots extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"modifyAnnots\");\n }\n}\n\nclass MsgId extends IntegerObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"msgId\", 1, n => n >= 1);\n }\n}\n\nclass NameAttr extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"nameAttr\");\n }\n}\n\nclass NeverEmbed extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"neverEmbed\");\n }\n}\n\nclass NumberOfCopies extends IntegerObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"numberOfCopies\", null, n => n >= 2 && n <= 5);\n }\n}\n\nclass OpenAction extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"openAction\", /* hasChildren = */ true);\n this.destination = null;\n }\n}\n\nclass Output extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"output\", /* hasChildren = */ true);\n this.to = null;\n this.type = null;\n this.uri = null;\n }\n}\n\nclass OutputBin extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"outputBin\");\n }\n}\n\nclass OutputXSL extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"outputXSL\", /* hasChildren = */ true);\n this.uri = null;\n }\n}\n\nclass Overprint extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"overprint\", [\"none\", \"both\", \"draw\", \"field\"]);\n }\n}\n\nclass Packets extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"packets\");\n }\n\n [$finalize]() {\n if (this[$content] === \"*\") {\n return;\n }\n this[$content] = this[$content]\n .trim()\n .split(/\\s+/)\n .filter(x =>\n [\"config\", \"datasets\", \"template\", \"xfdf\", \"xslt\"].includes(x)\n );\n }\n}\n\nclass PageOffset extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"pageOffset\");\n this.x = getInteger({\n data: attributes.x,\n defaultValue: \"useXDCSetting\",\n validate: n => true,\n });\n this.y = getInteger({\n data: attributes.y,\n defaultValue: \"useXDCSetting\",\n validate: n => true,\n });\n }\n}\n\nclass PageRange extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"pageRange\");\n }\n\n [$finalize]() {\n const numbers = this[$content]\n .trim()\n .split(/\\s+/)\n .map(x => parseInt(x, 10));\n const ranges = [];\n for (let i = 0, ii = numbers.length; i < ii; i += 2) {\n ranges.push(numbers.slice(i, i + 2));\n }\n this[$content] = ranges;\n }\n}\n\nclass Pagination extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"pagination\", [\n \"simplex\",\n \"duplexShortEdge\",\n \"duplexLongEdge\",\n ]);\n }\n}\n\nclass PaginationOverride extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"paginationOverride\", [\n \"none\",\n \"forceDuplex\",\n \"forceDuplexLongEdge\",\n \"forceDuplexShortEdge\",\n \"forceSimplex\",\n ]);\n }\n}\n\nclass Part extends IntegerObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"part\", 1, n => false);\n }\n}\n\nclass Pcl extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"pcl\", /* hasChildren = */ true);\n this.name = attributes.name || \"\";\n this.batchOutput = null;\n this.fontInfo = null;\n this.jog = null;\n this.mediumInfo = null;\n this.outputBin = null;\n this.pageOffset = null;\n this.staple = null;\n this.xdc = null;\n }\n}\n\nclass Pdf extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"pdf\", /* hasChildren = */ true);\n this.name = attributes.name || \"\";\n this.adobeExtensionLevel = null;\n this.batchOutput = null;\n this.compression = null;\n this.creator = null;\n this.encryption = null;\n this.fontInfo = null;\n this.interactive = null;\n this.linearized = null;\n this.openAction = null;\n this.pdfa = null;\n this.producer = null;\n this.renderPolicy = null;\n this.scriptModel = null;\n this.silentPrint = null;\n this.submitFormat = null;\n this.tagged = null;\n this.version = null;\n this.viewerPreferences = null;\n this.xdc = null;\n }\n}\n\nclass Pdfa extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"pdfa\", /* hasChildren = */ true);\n this.amd = null;\n this.conformance = null;\n this.includeXDPContent = null;\n this.part = null;\n }\n}\n\nclass Permissions extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"permissions\", /* hasChildren = */ true);\n this.accessibleContent = null;\n this.change = null;\n this.contentCopy = null;\n this.documentAssembly = null;\n this.formFieldFilling = null;\n this.modifyAnnots = null;\n this.plaintextMetadata = null;\n this.print = null;\n this.printHighQuality = null;\n }\n}\n\nclass PickTrayByPDFSize extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"pickTrayByPDFSize\");\n }\n}\n\nclass Picture extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"picture\");\n }\n\n // TODO: check the validity of the picture clause.\n // See page 1150 in the spec.\n}\n\nclass PlaintextMetadata extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"plaintextMetadata\");\n }\n}\n\nclass Presence extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"presence\", [\n \"preserve\",\n \"dissolve\",\n \"dissolveStructure\",\n \"ignore\",\n \"remove\",\n ]);\n }\n}\n\nclass Present extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"present\", /* hasChildren = */ true);\n this.behaviorOverride = null;\n this.cache = null;\n this.common = null;\n this.copies = null;\n this.destination = null;\n this.incrementalMerge = null;\n this.layout = null;\n this.output = null;\n this.overprint = null;\n this.pagination = null;\n this.paginationOverride = null;\n this.script = null;\n this.validate = null;\n this.xdp = null;\n this.driver = new XFAObjectArray();\n this.labelPrinter = new XFAObjectArray();\n this.pcl = new XFAObjectArray();\n this.pdf = new XFAObjectArray();\n this.ps = new XFAObjectArray();\n this.submitUrl = new XFAObjectArray();\n this.webClient = new XFAObjectArray();\n this.zpl = new XFAObjectArray();\n }\n}\n\nclass Print extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"print\");\n }\n}\n\nclass PrintHighQuality extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"printHighQuality\");\n }\n}\n\nclass PrintScaling extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"printScaling\", [\"appdefault\", \"noScaling\"]);\n }\n}\n\nclass PrinterName extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"printerName\");\n }\n}\n\nclass Producer extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"producer\");\n }\n}\n\nclass Ps extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"ps\", /* hasChildren = */ true);\n this.name = attributes.name || \"\";\n this.batchOutput = null;\n this.fontInfo = null;\n this.jog = null;\n this.mediumInfo = null;\n this.outputBin = null;\n this.staple = null;\n this.xdc = null;\n }\n}\n\nclass Range extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"range\");\n }\n\n [$finalize]() {\n this[$content] = this[$content]\n .trim()\n .split(/\\s*,\\s*/, 2)\n .map(range => range.split(\"-\").map(x => parseInt(x.trim(), 10)))\n .filter(range => range.every(x => !isNaN(x)))\n .map(range => {\n if (range.length === 1) {\n range.push(range[0]);\n }\n return range;\n });\n }\n}\n\nclass Record extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"record\");\n }\n\n [$finalize]() {\n this[$content] = this[$content].trim();\n const n = parseInt(this[$content], 10);\n if (!isNaN(n) && n >= 0) {\n this[$content] = n;\n }\n }\n}\n\nclass Relevant extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"relevant\");\n }\n\n [$finalize]() {\n this[$content] = this[$content].trim().split(/\\s+/);\n }\n}\n\nclass Rename extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"rename\");\n }\n\n [$finalize]() {\n this[$content] = this[$content].trim();\n // String must be a XFA name: same as XML one except that there\n // is no colon.\n if (\n this[$content].toLowerCase().startsWith(\"xml\") ||\n new RegExp(\"[\\\\p{L}_][\\\\p{L}\\\\d._\\\\p{M}-]*\", \"u\").test(this[$content])\n ) {\n warn(\"XFA - Rename: invalid XFA name\");\n }\n }\n}\n\nclass RenderPolicy extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"renderPolicy\", [\"server\", \"client\"]);\n }\n}\n\nclass RunScripts extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"runScripts\", [\"both\", \"client\", \"none\", \"server\"]);\n }\n}\n\nclass Script extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"script\", /* hasChildren = */ true);\n this.currentPage = null;\n this.exclude = null;\n this.runScripts = null;\n }\n}\n\nclass ScriptModel extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"scriptModel\", [\"XFA\", \"none\"]);\n }\n}\n\nclass Severity extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"severity\", [\n \"ignore\",\n \"error\",\n \"information\",\n \"trace\",\n \"warning\",\n ]);\n }\n}\n\nclass SilentPrint extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"silentPrint\", /* hasChildren = */ true);\n this.addSilentPrint = null;\n this.printerName = null;\n }\n}\n\nclass Staple extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"staple\");\n this.mode = getStringOption(attributes.mode, [\n \"usePrinterSetting\",\n \"on\",\n \"off\",\n ]);\n }\n}\n\nclass StartNode extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"startNode\");\n }\n}\n\nclass StartPage extends IntegerObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"startPage\", 0, n => true);\n }\n}\n\nclass SubmitFormat extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"submitFormat\", [\n \"html\",\n \"delegate\",\n \"fdf\",\n \"xml\",\n \"pdf\",\n ]);\n }\n}\n\nclass SubmitUrl extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"submitUrl\");\n }\n}\n\nclass SubsetBelow extends IntegerObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"subsetBelow\", 100, n => n >= 0 && n <= 100);\n }\n}\n\nclass SuppressBanner extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"suppressBanner\");\n }\n}\n\nclass Tagged extends Option01 {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"tagged\");\n }\n}\n\nclass Template extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"template\", /* hasChildren = */ true);\n this.base = null;\n this.relevant = null;\n this.startPage = null;\n this.uri = null;\n this.xsl = null;\n }\n}\n\nclass Threshold extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"threshold\", [\n \"trace\",\n \"error\",\n \"information\",\n \"warning\",\n ]);\n }\n}\n\nclass To extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"to\", [\n \"null\",\n \"memory\",\n \"stderr\",\n \"stdout\",\n \"system\",\n \"uri\",\n ]);\n }\n}\n\nclass TemplateCache extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"templateCache\");\n this.maxEntries = getInteger({\n data: attributes.maxEntries,\n defaultValue: 5,\n validate: n => n >= 0,\n });\n }\n}\n\nclass Trace extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"trace\", /* hasChildren = */ true);\n this.area = new XFAObjectArray();\n }\n}\n\nclass Transform extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"transform\", /* hasChildren = */ true);\n this.groupParent = null;\n this.ifEmpty = null;\n this.nameAttr = null;\n this.picture = null;\n this.presence = null;\n this.rename = null;\n this.whitespace = null;\n }\n}\n\nclass Type extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"type\", [\n \"none\",\n \"ascii85\",\n \"asciiHex\",\n \"ccittfax\",\n \"flate\",\n \"lzw\",\n \"runLength\",\n \"native\",\n \"xdp\",\n \"mergedXDP\",\n ]);\n }\n}\n\nclass Uri extends StringObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"uri\");\n }\n}\n\nclass Validate extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"validate\", [\n \"preSubmit\",\n \"prePrint\",\n \"preExecute\",\n \"preSave\",\n ]);\n }\n}\n\nclass ValidateApprovalSignatures extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"validateApprovalSignatures\");\n }\n\n [$finalize]() {\n this[$content] = this[$content]\n .trim()\n .split(/\\s+/)\n .filter(x => [\"docReady\", \"postSign\"].includes(x));\n }\n}\n\nclass ValidationMessaging extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"validationMessaging\", [\n \"allMessagesIndividually\",\n \"allMessagesTogether\",\n \"firstMessageOnly\",\n \"noMessages\",\n ]);\n }\n}\n\nclass Version extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"version\", [\"1.7\", \"1.6\", \"1.5\", \"1.4\", \"1.3\", \"1.2\"]);\n }\n}\n\nclass VersionControl extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"VersionControl\");\n this.outputBelow = getStringOption(attributes.outputBelow, [\n \"warn\",\n \"error\",\n \"update\",\n ]);\n this.sourceAbove = getStringOption(attributes.sourceAbove, [\n \"warn\",\n \"error\",\n ]);\n this.sourceBelow = getStringOption(attributes.sourceBelow, [\n \"update\",\n \"maintain\",\n ]);\n }\n}\n\nclass ViewerPreferences extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"viewerPreferences\", /* hasChildren = */ true);\n this.ADBE_JSConsole = null;\n this.ADBE_JSDebugger = null;\n this.addViewerPreferences = null;\n this.duplexOption = null;\n this.enforce = null;\n this.numberOfCopies = null;\n this.pageRange = null;\n this.pickTrayByPDFSize = null;\n this.printScaling = null;\n }\n}\n\nclass WebClient extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"webClient\", /* hasChildren = */ true);\n this.name = attributes.name ? attributes.name.trim() : \"\";\n this.fontInfo = null;\n this.xdc = null;\n }\n}\n\nclass Whitespace extends OptionObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"whitespace\", [\n \"preserve\",\n \"ltrim\",\n \"normalize\",\n \"rtrim\",\n \"trim\",\n ]);\n }\n}\n\nclass Window extends ContentObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"window\");\n }\n\n [$finalize]() {\n const pair = this[$content]\n .trim()\n .split(/\\s*,\\s*/, 2)\n .map(x => parseInt(x, 10));\n if (pair.some(x => isNaN(x))) {\n this[$content] = [0, 0];\n return;\n }\n if (pair.length === 1) {\n pair.push(pair[0]);\n }\n this[$content] = pair;\n }\n}\n\nclass Xdc extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"xdc\", /* hasChildren = */ true);\n this.uri = new XFAObjectArray();\n this.xsl = new XFAObjectArray();\n }\n}\n\nclass Xdp extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"xdp\", /* hasChildren = */ true);\n this.packets = null;\n }\n}\n\nclass Xsl extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"xsl\", /* hasChildren = */ true);\n this.debug = null;\n this.uri = null;\n }\n}\n\nclass Zpl extends XFAObject {\n constructor(attributes) {\n super(CONFIG_NS_ID, \"zpl\", /* hasChildren = */ true);\n this.name = attributes.name ? attributes.name.trim() : \"\";\n this.batchOutput = null;\n this.flipLabel = null;\n this.fontInfo = null;\n this.xdc = null;\n }\n}\n\nclass ConfigNamespace {\n static [$buildXFAObject](name, attributes) {\n if (ConfigNamespace.hasOwnProperty(name)) {\n return ConfigNamespace[name](attributes);\n }\n return undefined;\n }\n\n static acrobat(attrs) {\n return new Acrobat(attrs);\n }\n\n static acrobat7(attrs) {\n return new Acrobat7(attrs);\n }\n\n static ADBE_JSConsole(attrs) {\n return new ADBE_JSConsole(attrs);\n }\n\n static ADBE_JSDebugger(attrs) {\n return new ADBE_JSDebugger(attrs);\n }\n\n static addSilentPrint(attrs) {\n return new AddSilentPrint(attrs);\n }\n\n static addViewerPreferences(attrs) {\n return new AddViewerPreferences(attrs);\n }\n\n static adjustData(attrs) {\n return new AdjustData(attrs);\n }\n\n static adobeExtensionLevel(attrs) {\n return new AdobeExtensionLevel(attrs);\n }\n\n static agent(attrs) {\n return new Agent(attrs);\n }\n\n static alwaysEmbed(attrs) {\n return new AlwaysEmbed(attrs);\n }\n\n static amd(attrs) {\n return new Amd(attrs);\n }\n\n static area(attrs) {\n return new Area(attrs);\n }\n\n static attributes(attrs) {\n return new Attributes(attrs);\n }\n\n static autoSave(attrs) {\n return new AutoSave(attrs);\n }\n\n static base(attrs) {\n return new Base(attrs);\n }\n\n static batchOutput(attrs) {\n return new BatchOutput(attrs);\n }\n\n static behaviorOverride(attrs) {\n return new BehaviorOverride(attrs);\n }\n\n static cache(attrs) {\n return new Cache(attrs);\n }\n\n static change(attrs) {\n return new Change(attrs);\n }\n\n static common(attrs) {\n return new Common(attrs);\n }\n\n static compress(attrs) {\n return new Compress(attrs);\n }\n\n static compressLogicalStructure(attrs) {\n return new CompressLogicalStructure(attrs);\n }\n\n static compressObjectStream(attrs) {\n return new CompressObjectStream(attrs);\n }\n\n static compression(attrs) {\n return new Compression(attrs);\n }\n\n static config(attrs) {\n return new Config(attrs);\n }\n\n static conformance(attrs) {\n return new Conformance(attrs);\n }\n\n static contentCopy(attrs) {\n return new ContentCopy(attrs);\n }\n\n static copies(attrs) {\n return new Copies(attrs);\n }\n\n static creator(attrs) {\n return new Creator(attrs);\n }\n\n static currentPage(attrs) {\n return new CurrentPage(attrs);\n }\n\n static data(attrs) {\n return new Data(attrs);\n }\n\n static debug(attrs) {\n return new Debug(attrs);\n }\n\n static defaultTypeface(attrs) {\n return new DefaultTypeface(attrs);\n }\n\n static destination(attrs) {\n return new Destination(attrs);\n }\n\n static documentAssembly(attrs) {\n return new DocumentAssembly(attrs);\n }\n\n static driver(attrs) {\n return new Driver(attrs);\n }\n\n static duplexOption(attrs) {\n return new DuplexOption(attrs);\n }\n\n static dynamicRender(attrs) {\n return new DynamicRender(attrs);\n }\n\n static embed(attrs) {\n return new Embed(attrs);\n }\n\n static encrypt(attrs) {\n return new Encrypt(attrs);\n }\n\n static encryption(attrs) {\n return new Encryption(attrs);\n }\n\n static encryptionLevel(attrs) {\n return new EncryptionLevel(attrs);\n }\n\n static enforce(attrs) {\n return new Enforce(attrs);\n }\n\n static equate(attrs) {\n return new Equate(attrs);\n }\n\n static equateRange(attrs) {\n return new EquateRange(attrs);\n }\n\n static exclude(attrs) {\n return new Exclude(attrs);\n }\n\n static excludeNS(attrs) {\n return new ExcludeNS(attrs);\n }\n\n static flipLabel(attrs) {\n return new FlipLabel(attrs);\n }\n\n static fontInfo(attrs) {\n return new FontInfo(attrs);\n }\n\n static formFieldFilling(attrs) {\n return new FormFieldFilling(attrs);\n }\n\n static groupParent(attrs) {\n return new GroupParent(attrs);\n }\n\n static ifEmpty(attrs) {\n return new IfEmpty(attrs);\n }\n\n static includeXDPContent(attrs) {\n return new IncludeXDPContent(attrs);\n }\n\n static incrementalLoad(attrs) {\n return new IncrementalLoad(attrs);\n }\n\n static incrementalMerge(attrs) {\n return new IncrementalMerge(attrs);\n }\n\n static interactive(attrs) {\n return new Interactive(attrs);\n }\n\n static jog(attrs) {\n return new Jog(attrs);\n }\n\n static labelPrinter(attrs) {\n return new LabelPrinter(attrs);\n }\n\n static layout(attrs) {\n return new Layout(attrs);\n }\n\n static level(attrs) {\n return new Level(attrs);\n }\n\n static linearized(attrs) {\n return new Linearized(attrs);\n }\n\n static locale(attrs) {\n return new Locale(attrs);\n }\n\n static localeSet(attrs) {\n return new LocaleSet(attrs);\n }\n\n static log(attrs) {\n return new Log(attrs);\n }\n\n static map(attrs) {\n return new MapElement(attrs);\n }\n\n static mediumInfo(attrs) {\n return new MediumInfo(attrs);\n }\n\n static message(attrs) {\n return new Message(attrs);\n }\n\n static messaging(attrs) {\n return new Messaging(attrs);\n }\n\n static mode(attrs) {\n return new Mode(attrs);\n }\n\n static modifyAnnots(attrs) {\n return new ModifyAnnots(attrs);\n }\n\n static msgId(attrs) {\n return new MsgId(attrs);\n }\n\n static nameAttr(attrs) {\n return new NameAttr(attrs);\n }\n\n static neverEmbed(attrs) {\n return new NeverEmbed(attrs);\n }\n\n static numberOfCopies(attrs) {\n return new NumberOfCopies(attrs);\n }\n\n static openAction(attrs) {\n return new OpenAction(attrs);\n }\n\n static output(attrs) {\n return new Output(attrs);\n }\n\n static outputBin(attrs) {\n return new OutputBin(attrs);\n }\n\n static outputXSL(attrs) {\n return new OutputXSL(attrs);\n }\n\n static overprint(attrs) {\n return new Overprint(attrs);\n }\n\n static packets(attrs) {\n return new Packets(attrs);\n }\n\n static pageOffset(attrs) {\n return new PageOffset(attrs);\n }\n\n static pageRange(attrs) {\n return new PageRange(attrs);\n }\n\n static pagination(attrs) {\n return new Pagination(attrs);\n }\n\n static paginationOverride(attrs) {\n return new PaginationOverride(attrs);\n }\n\n static part(attrs) {\n return new Part(attrs);\n }\n\n static pcl(attrs) {\n return new Pcl(attrs);\n }\n\n static pdf(attrs) {\n return new Pdf(attrs);\n }\n\n static pdfa(attrs) {\n return new Pdfa(attrs);\n }\n\n static permissions(attrs) {\n return new Permissions(attrs);\n }\n\n static pickTrayByPDFSize(attrs) {\n return new PickTrayByPDFSize(attrs);\n }\n\n static picture(attrs) {\n return new Picture(attrs);\n }\n\n static plaintextMetadata(attrs) {\n return new PlaintextMetadata(attrs);\n }\n\n static presence(attrs) {\n return new Presence(attrs);\n }\n\n static present(attrs) {\n return new Present(attrs);\n }\n\n static print(attrs) {\n return new Print(attrs);\n }\n\n static printHighQuality(attrs) {\n return new PrintHighQuality(attrs);\n }\n\n static printScaling(attrs) {\n return new PrintScaling(attrs);\n }\n\n static printerName(attrs) {\n return new PrinterName(attrs);\n }\n\n static producer(attrs) {\n return new Producer(attrs);\n }\n\n static ps(attrs) {\n return new Ps(attrs);\n }\n\n static range(attrs) {\n return new Range(attrs);\n }\n\n static record(attrs) {\n return new Record(attrs);\n }\n\n static relevant(attrs) {\n return new Relevant(attrs);\n }\n\n static rename(attrs) {\n return new Rename(attrs);\n }\n\n static renderPolicy(attrs) {\n return new RenderPolicy(attrs);\n }\n\n static runScripts(attrs) {\n return new RunScripts(attrs);\n }\n\n static script(attrs) {\n return new Script(attrs);\n }\n\n static scriptModel(attrs) {\n return new ScriptModel(attrs);\n }\n\n static severity(attrs) {\n return new Severity(attrs);\n }\n\n static silentPrint(attrs) {\n return new SilentPrint(attrs);\n }\n\n static staple(attrs) {\n return new Staple(attrs);\n }\n\n static startNode(attrs) {\n return new StartNode(attrs);\n }\n\n static startPage(attrs) {\n return new StartPage(attrs);\n }\n\n static submitFormat(attrs) {\n return new SubmitFormat(attrs);\n }\n\n static submitUrl(attrs) {\n return new SubmitUrl(attrs);\n }\n\n static subsetBelow(attrs) {\n return new SubsetBelow(attrs);\n }\n\n static suppressBanner(attrs) {\n return new SuppressBanner(attrs);\n }\n\n static tagged(attrs) {\n return new Tagged(attrs);\n }\n\n static template(attrs) {\n return new Template(attrs);\n }\n\n static templateCache(attrs) {\n return new TemplateCache(attrs);\n }\n\n static threshold(attrs) {\n return new Threshold(attrs);\n }\n\n static to(attrs) {\n return new To(attrs);\n }\n\n static trace(attrs) {\n return new Trace(attrs);\n }\n\n static transform(attrs) {\n return new Transform(attrs);\n }\n\n static type(attrs) {\n return new Type(attrs);\n }\n\n static uri(attrs) {\n return new Uri(attrs);\n }\n\n static validate(attrs) {\n return new Validate(attrs);\n }\n\n static validateApprovalSignatures(attrs) {\n return new ValidateApprovalSignatures(attrs);\n }\n\n static validationMessaging(attrs) {\n return new ValidationMessaging(attrs);\n }\n\n static version(attrs) {\n return new Version(attrs);\n }\n\n static versionControl(attrs) {\n return new VersionControl(attrs);\n }\n\n static viewerPreferences(attrs) {\n return new ViewerPreferences(attrs);\n }\n\n static webClient(attrs) {\n return new WebClient(attrs);\n }\n\n static whitespace(attrs) {\n return new Whitespace(attrs);\n }\n\n static window(attrs) {\n return new Window(attrs);\n }\n\n static xdc(attrs) {\n return new Xdc(attrs);\n }\n\n static xdp(attrs) {\n return new Xdp(attrs);\n }\n\n static xsl(attrs) {\n return new Xsl(attrs);\n }\n\n static zpl(attrs) {\n return new Zpl(attrs);\n }\n}\n\nexport { ConfigNamespace };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { $buildXFAObject, NamespaceIds } from \"./namespaces.js\";\nimport { StringObject, XFAObject, XFAObjectArray } from \"./xfa_object.js\";\n\nconst CONNECTION_SET_NS_ID = NamespaceIds.connectionSet.id;\n\nclass ConnectionSet extends XFAObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"connectionSet\", /* hasChildren = */ true);\n this.wsdlConnection = new XFAObjectArray();\n this.xmlConnection = new XFAObjectArray();\n this.xsdConnection = new XFAObjectArray();\n }\n}\n\nclass EffectiveInputPolicy extends XFAObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"effectiveInputPolicy\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass EffectiveOutputPolicy extends XFAObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"effectiveOutputPolicy\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Operation extends StringObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"operation\");\n this.id = attributes.id || \"\";\n this.input = attributes.input || \"\";\n this.name = attributes.name || \"\";\n this.output = attributes.output || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass RootElement extends StringObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"rootElement\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass SoapAction extends StringObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"soapAction\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass SoapAddress extends StringObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"soapAddress\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass Uri extends StringObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"uri\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass WsdlAddress extends StringObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"wsdlAddress\");\n this.id = attributes.id || \"\";\n this.name = attributes.name || \"\";\n this.use = attributes.use || \"\";\n this.usehref = attributes.usehref || \"\";\n }\n}\n\nclass WsdlConnection extends XFAObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"wsdlConnection\", /* hasChildren = */ true);\n this.dataDescription = attributes.dataDescription || \"\";\n this.name = attributes.name || \"\";\n this.effectiveInputPolicy = null;\n this.effectiveOutputPolicy = null;\n this.operation = null;\n this.soapAction = null;\n this.soapAddress = null;\n this.wsdlAddress = null;\n }\n}\n\nclass XmlConnection extends XFAObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"xmlConnection\", /* hasChildren = */ true);\n this.dataDescription = attributes.dataDescription || \"\";\n this.name = attributes.name || \"\";\n this.uri = null;\n }\n}\n\nclass XsdConnection extends XFAObject {\n constructor(attributes) {\n super(CONNECTION_SET_NS_ID, \"xsdConnection\", /* hasChildren = */ true);\n this.dataDescription = attributes.dataDescription || \"\";\n this.name = attributes.name || \"\";\n this.rootElement = null;\n this.uri = null;\n }\n}\n\nclass ConnectionSetNamespace {\n static [$buildXFAObject](name, attributes) {\n if (ConnectionSetNamespace.hasOwnProperty(name)) {\n return ConnectionSetNamespace[name](attributes);\n }\n return undefined;\n }\n\n static connectionSet(attrs) {\n return new ConnectionSet(attrs);\n }\n\n static effectiveInputPolicy(attrs) {\n return new EffectiveInputPolicy(attrs);\n }\n\n static effectiveOutputPolicy(attrs) {\n return new EffectiveOutputPolicy(attrs);\n }\n\n static operation(attrs) {\n return new Operation(attrs);\n }\n\n static rootElement(attrs) {\n return new RootElement(attrs);\n }\n\n static soapAction(attrs) {\n return new SoapAction(attrs);\n }\n\n static soapAddress(attrs) {\n return new SoapAddress(attrs);\n }\n\n static uri(attrs) {\n return new Uri(attrs);\n }\n\n static wsdlAddress(attrs) {\n return new WsdlAddress(attrs);\n }\n\n static wsdlConnection(attrs) {\n return new WsdlConnection(attrs);\n }\n\n static xmlConnection(attrs) {\n return new XmlConnection(attrs);\n }\n\n static xsdConnection(attrs) {\n return new XsdConnection(attrs);\n }\n}\n\nexport { ConnectionSetNamespace };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n $appendChild,\n $isNsAgnostic,\n $namespaceId,\n $nodeName,\n $onChild,\n} from \"./symbol_utils.js\";\nimport { $buildXFAObject, NamespaceIds } from \"./namespaces.js\";\nimport { XFAObject, XmlObject } from \"./xfa_object.js\";\n\nconst DATASETS_NS_ID = NamespaceIds.datasets.id;\n\nclass Data extends XmlObject {\n constructor(attributes) {\n super(DATASETS_NS_ID, \"data\", attributes);\n }\n\n [$isNsAgnostic]() {\n return true;\n }\n}\n\nclass Datasets extends XFAObject {\n constructor(attributes) {\n super(DATASETS_NS_ID, \"datasets\", /* hasChildren = */ true);\n this.data = null;\n this.Signature = null;\n }\n\n [$onChild](child) {\n const name = child[$nodeName];\n if (\n (name === \"data\" && child[$namespaceId] === DATASETS_NS_ID) ||\n (name === \"Signature\" &&\n child[$namespaceId] === NamespaceIds.signature.id)\n ) {\n this[name] = child;\n }\n this[$appendChild](child);\n }\n}\n\nclass DatasetsNamespace {\n static [$buildXFAObject](name, attributes) {\n if (DatasetsNamespace.hasOwnProperty(name)) {\n return DatasetsNamespace[name](attributes);\n }\n return undefined;\n }\n\n static datasets(attributes) {\n return new Datasets(attributes);\n }\n\n static data(attributes) {\n return new Data(attributes);\n }\n}\n\nexport { DatasetsNamespace };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { $buildXFAObject, NamespaceIds } from \"./namespaces.js\";\nimport {\n ContentObject,\n StringObject,\n XFAObject,\n XFAObjectArray,\n} from \"./xfa_object.js\";\nimport { getInteger, getStringOption } from \"./utils.js\";\n\nconst LOCALE_SET_NS_ID = NamespaceIds.localeSet.id;\n\nclass CalendarSymbols extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"calendarSymbols\", /* hasChildren = */ true);\n this.name = \"gregorian\";\n this.dayNames = new XFAObjectArray(2);\n this.eraNames = null;\n this.meridiemNames = null;\n this.monthNames = new XFAObjectArray(2);\n }\n}\n\nclass CurrencySymbol extends StringObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"currencySymbol\");\n this.name = getStringOption(attributes.name, [\n \"symbol\",\n \"isoname\",\n \"decimal\",\n ]);\n }\n}\n\nclass CurrencySymbols extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"currencySymbols\", /* hasChildren = */ true);\n this.currencySymbol = new XFAObjectArray(3);\n }\n}\n\nclass DatePattern extends StringObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"datePattern\");\n this.name = getStringOption(attributes.name, [\n \"full\",\n \"long\",\n \"med\",\n \"short\",\n ]);\n }\n}\n\nclass DatePatterns extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"datePatterns\", /* hasChildren = */ true);\n this.datePattern = new XFAObjectArray(4);\n }\n}\n\nclass DateTimeSymbols extends ContentObject {\n // TODO: spec unclear about the format of the array.\n\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"dateTimeSymbols\");\n }\n}\n\nclass Day extends StringObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"day\");\n }\n}\n\nclass DayNames extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"dayNames\", /* hasChildren = */ true);\n this.abbr = getInteger({\n data: attributes.abbr,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.day = new XFAObjectArray(7);\n }\n}\n\nclass Era extends StringObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"era\");\n }\n}\n\nclass EraNames extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"eraNames\", /* hasChildren = */ true);\n this.era = new XFAObjectArray(2);\n }\n}\n\nclass Locale extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"locale\", /* hasChildren = */ true);\n this.desc = attributes.desc || \"\";\n this.name = \"isoname\";\n this.calendarSymbols = null;\n this.currencySymbols = null;\n this.datePatterns = null;\n this.dateTimeSymbols = null;\n this.numberPatterns = null;\n this.numberSymbols = null;\n this.timePatterns = null;\n this.typeFaces = null;\n }\n}\n\nclass LocaleSet extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"localeSet\", /* hasChildren = */ true);\n this.locale = new XFAObjectArray();\n }\n}\n\nclass Meridiem extends StringObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"meridiem\");\n }\n}\n\nclass MeridiemNames extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"meridiemNames\", /* hasChildren = */ true);\n this.meridiem = new XFAObjectArray(2);\n }\n}\n\nclass Month extends StringObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"month\");\n }\n}\n\nclass MonthNames extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"monthNames\", /* hasChildren = */ true);\n this.abbr = getInteger({\n data: attributes.abbr,\n defaultValue: 0,\n validate: x => x === 1,\n });\n this.month = new XFAObjectArray(12);\n }\n}\n\nclass NumberPattern extends StringObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"numberPattern\");\n this.name = getStringOption(attributes.name, [\n \"full\",\n \"long\",\n \"med\",\n \"short\",\n ]);\n }\n}\n\nclass NumberPatterns extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"numberPatterns\", /* hasChildren = */ true);\n this.numberPattern = new XFAObjectArray(4);\n }\n}\n\nclass NumberSymbol extends StringObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"numberSymbol\");\n this.name = getStringOption(attributes.name, [\n \"decimal\",\n \"grouping\",\n \"percent\",\n \"minus\",\n \"zero\",\n ]);\n }\n}\n\nclass NumberSymbols extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"numberSymbols\", /* hasChildren = */ true);\n this.numberSymbol = new XFAObjectArray(5);\n }\n}\n\nclass TimePattern extends StringObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"timePattern\");\n this.name = getStringOption(attributes.name, [\n \"full\",\n \"long\",\n \"med\",\n \"short\",\n ]);\n }\n}\n\nclass TimePatterns extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"timePatterns\", /* hasChildren = */ true);\n this.timePattern = new XFAObjectArray(4);\n }\n}\n\nclass TypeFace extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"typeFace\", /* hasChildren = */ true);\n this.name = attributes.name | \"\";\n }\n}\n\nclass TypeFaces extends XFAObject {\n constructor(attributes) {\n super(LOCALE_SET_NS_ID, \"typeFaces\", /* hasChildren = */ true);\n this.typeFace = new XFAObjectArray();\n }\n}\n\nclass LocaleSetNamespace {\n static [$buildXFAObject](name, attributes) {\n if (LocaleSetNamespace.hasOwnProperty(name)) {\n return LocaleSetNamespace[name](attributes);\n }\n return undefined;\n }\n\n static calendarSymbols(attrs) {\n return new CalendarSymbols(attrs);\n }\n\n static currencySymbol(attrs) {\n return new CurrencySymbol(attrs);\n }\n\n static currencySymbols(attrs) {\n return new CurrencySymbols(attrs);\n }\n\n static datePattern(attrs) {\n return new DatePattern(attrs);\n }\n\n static datePatterns(attrs) {\n return new DatePatterns(attrs);\n }\n\n static dateTimeSymbols(attrs) {\n return new DateTimeSymbols(attrs);\n }\n\n static day(attrs) {\n return new Day(attrs);\n }\n\n static dayNames(attrs) {\n return new DayNames(attrs);\n }\n\n static era(attrs) {\n return new Era(attrs);\n }\n\n static eraNames(attrs) {\n return new EraNames(attrs);\n }\n\n static locale(attrs) {\n return new Locale(attrs);\n }\n\n static localeSet(attrs) {\n return new LocaleSet(attrs);\n }\n\n static meridiem(attrs) {\n return new Meridiem(attrs);\n }\n\n static meridiemNames(attrs) {\n return new MeridiemNames(attrs);\n }\n\n static month(attrs) {\n return new Month(attrs);\n }\n\n static monthNames(attrs) {\n return new MonthNames(attrs);\n }\n\n static numberPattern(attrs) {\n return new NumberPattern(attrs);\n }\n\n static numberPatterns(attrs) {\n return new NumberPatterns(attrs);\n }\n\n static numberSymbol(attrs) {\n return new NumberSymbol(attrs);\n }\n\n static numberSymbols(attrs) {\n return new NumberSymbols(attrs);\n }\n\n static timePattern(attrs) {\n return new TimePattern(attrs);\n }\n\n static timePatterns(attrs) {\n return new TimePatterns(attrs);\n }\n\n static typeFace(attrs) {\n return new TypeFace(attrs);\n }\n\n static typeFaces(attrs) {\n return new TypeFaces(attrs);\n }\n}\n\nexport { LocaleSetNamespace };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { $buildXFAObject, NamespaceIds } from \"./namespaces.js\";\nimport { XFAObject } from \"./xfa_object.js\";\n\nconst SIGNATURE_NS_ID = NamespaceIds.signature.id;\n\nclass Signature extends XFAObject {\n constructor(attributes) {\n super(SIGNATURE_NS_ID, \"signature\", /* hasChildren = */ true);\n }\n}\n\nclass SignatureNamespace {\n static [$buildXFAObject](name, attributes) {\n if (SignatureNamespace.hasOwnProperty(name)) {\n return SignatureNamespace[name](attributes);\n }\n return undefined;\n }\n\n static signature(attributes) {\n return new Signature(attributes);\n }\n}\n\nexport { SignatureNamespace };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { $buildXFAObject, NamespaceIds } from \"./namespaces.js\";\nimport { XFAObject } from \"./xfa_object.js\";\n\nconst STYLESHEET_NS_ID = NamespaceIds.stylesheet.id;\n\nclass Stylesheet extends XFAObject {\n constructor(attributes) {\n super(STYLESHEET_NS_ID, \"stylesheet\", /* hasChildren = */ true);\n }\n}\n\nclass StylesheetNamespace {\n static [$buildXFAObject](name, attributes) {\n if (StylesheetNamespace.hasOwnProperty(name)) {\n return StylesheetNamespace[name](attributes);\n }\n return undefined;\n }\n\n static stylesheet(attributes) {\n return new Stylesheet(attributes);\n }\n}\n\nexport { StylesheetNamespace };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { $buildXFAObject, NamespaceIds } from \"./namespaces.js\";\nimport { $namespaceId, $nodeName, $onChildCheck } from \"./symbol_utils.js\";\nimport { XFAObject, XFAObjectArray } from \"./xfa_object.js\";\n\nconst XDP_NS_ID = NamespaceIds.xdp.id;\n\nclass Xdp extends XFAObject {\n constructor(attributes) {\n super(XDP_NS_ID, \"xdp\", /* hasChildren = */ true);\n this.uuid = attributes.uuid || \"\";\n this.timeStamp = attributes.timeStamp || \"\";\n this.config = null;\n this.connectionSet = null;\n this.datasets = null;\n this.localeSet = null;\n this.stylesheet = new XFAObjectArray();\n this.template = null;\n }\n\n [$onChildCheck](child) {\n const ns = NamespaceIds[child[$nodeName]];\n return ns && child[$namespaceId] === ns.id;\n }\n}\n\nclass XdpNamespace {\n static [$buildXFAObject](name, attributes) {\n if (XdpNamespace.hasOwnProperty(name)) {\n return XdpNamespace[name](attributes);\n }\n return undefined;\n }\n\n static xdp(attributes) {\n return new Xdp(attributes);\n }\n}\n\nexport { XdpNamespace };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n $acceptWhitespace,\n $childrenToHTML,\n $clean,\n $content,\n $extra,\n $getChildren,\n $getParent,\n $globalData,\n $nodeName,\n $onText,\n $pushGlyphs,\n $text,\n $toHTML,\n} from \"./symbol_utils.js\";\nimport { $buildXFAObject, NamespaceIds } from \"./namespaces.js\";\nimport {\n fixTextIndent,\n fixURL,\n measureToString,\n setFontFamily,\n} from \"./html_utils.js\";\nimport { getMeasurement, HTMLResult, stripQuotes } from \"./utils.js\";\nimport { XmlObject } from \"./xfa_object.js\";\n\nconst XHTML_NS_ID = NamespaceIds.xhtml.id;\nconst $richText = Symbol();\n\nconst VALID_STYLES = new Set([\n \"color\",\n \"font\",\n \"font-family\",\n \"font-size\",\n \"font-stretch\",\n \"font-style\",\n \"font-weight\",\n \"margin\",\n \"margin-bottom\",\n \"margin-left\",\n \"margin-right\",\n \"margin-top\",\n \"letter-spacing\",\n \"line-height\",\n \"orphans\",\n \"page-break-after\",\n \"page-break-before\",\n \"page-break-inside\",\n \"tab-interval\",\n \"tab-stop\",\n \"text-align\",\n \"text-decoration\",\n \"text-indent\",\n \"vertical-align\",\n \"widows\",\n \"kerning-mode\",\n \"xfa-font-horizontal-scale\",\n \"xfa-font-vertical-scale\",\n \"xfa-spacerun\",\n \"xfa-tab-stops\",\n]);\n\nconst StyleMapping = new Map([\n [\"page-break-after\", \"breakAfter\"],\n [\"page-break-before\", \"breakBefore\"],\n [\"page-break-inside\", \"breakInside\"],\n [\"kerning-mode\", value => (value === \"none\" ? \"none\" : \"normal\")],\n [\n \"xfa-font-horizontal-scale\",\n value =>\n `scaleX(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`,\n ],\n [\n \"xfa-font-vertical-scale\",\n value =>\n `scaleY(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`,\n ],\n [\"xfa-spacerun\", \"\"],\n [\"xfa-tab-stops\", \"\"],\n [\n \"font-size\",\n (value, original) => {\n value = original.fontSize = getMeasurement(value);\n return measureToString(0.99 * value);\n },\n ],\n [\"letter-spacing\", value => measureToString(getMeasurement(value))],\n [\"line-height\", value => measureToString(getMeasurement(value))],\n [\"margin\", value => measureToString(getMeasurement(value))],\n [\"margin-bottom\", value => measureToString(getMeasurement(value))],\n [\"margin-left\", value => measureToString(getMeasurement(value))],\n [\"margin-right\", value => measureToString(getMeasurement(value))],\n [\"margin-top\", value => measureToString(getMeasurement(value))],\n [\"text-indent\", value => measureToString(getMeasurement(value))],\n [\"font-family\", value => value],\n [\"vertical-align\", value => measureToString(getMeasurement(value))],\n]);\n\nconst spacesRegExp = /\\s+/g;\nconst crlfRegExp = /[\\r\\n]+/g;\nconst crlfForRichTextRegExp = /\\r\\n?/g;\n\nfunction mapStyle(styleStr, node, richText) {\n const style = Object.create(null);\n if (!styleStr) {\n return style;\n }\n const original = Object.create(null);\n for (const [key, value] of styleStr.split(\";\").map(s => s.split(\":\", 2))) {\n const mapping = StyleMapping.get(key);\n if (mapping === \"\") {\n continue;\n }\n let newValue = value;\n if (mapping) {\n newValue =\n typeof mapping === \"string\" ? mapping : mapping(value, original);\n }\n if (key.endsWith(\"scale\")) {\n style.transform = style.transform\n ? `${style[key]} ${newValue}`\n : newValue;\n } else {\n style[key.replaceAll(/-([a-zA-Z])/g, (_, x) => x.toUpperCase())] =\n newValue;\n }\n }\n\n if (style.fontFamily) {\n setFontFamily(\n {\n typeface: style.fontFamily,\n weight: style.fontWeight || \"normal\",\n posture: style.fontStyle || \"normal\",\n size: original.fontSize || 0,\n },\n node,\n node[$globalData].fontFinder,\n style\n );\n }\n\n if (\n richText &&\n style.verticalAlign &&\n style.verticalAlign !== \"0px\" &&\n style.fontSize\n ) {\n // A non-zero verticalAlign means that we've a sub/super-script and\n // consequently the font size must be decreased.\n // https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf#G11.2097514\n // And the two following factors to position the scripts have been\n // found here:\n // https://en.wikipedia.org/wiki/Subscript_and_superscript#Desktop_publishing\n const SUB_SUPER_SCRIPT_FACTOR = 0.583;\n const VERTICAL_FACTOR = 0.333;\n const fontSize = getMeasurement(style.fontSize);\n style.fontSize = measureToString(fontSize * SUB_SUPER_SCRIPT_FACTOR);\n style.verticalAlign = measureToString(\n Math.sign(getMeasurement(style.verticalAlign)) *\n fontSize *\n VERTICAL_FACTOR\n );\n }\n\n if (richText && style.fontSize) {\n style.fontSize = `calc(${style.fontSize} * var(--scale-factor))`;\n }\n\n fixTextIndent(style);\n return style;\n}\n\nfunction checkStyle(node) {\n if (!node.style) {\n return \"\";\n }\n\n // Remove any non-allowed keys.\n return node.style\n .trim()\n .split(/\\s*;\\s*/)\n .filter(s => !!s)\n .map(s => s.split(/\\s*:\\s*/, 2))\n .filter(([key, value]) => {\n if (key === \"font-family\") {\n node[$globalData].usedTypefaces.add(value);\n }\n return VALID_STYLES.has(key);\n })\n .map(kv => kv.join(\":\"))\n .join(\";\");\n}\n\nconst NoWhites = new Set([\"body\", \"html\"]);\n\nclass XhtmlObject extends XmlObject {\n constructor(attributes, name) {\n super(XHTML_NS_ID, name);\n this[$richText] = false;\n this.style = attributes.style || \"\";\n }\n\n [$clean](builder) {\n super[$clean](builder);\n this.style = checkStyle(this);\n }\n\n [$acceptWhitespace]() {\n return !NoWhites.has(this[$nodeName]);\n }\n\n [$onText](str, richText = false) {\n if (!richText) {\n str = str.replaceAll(crlfRegExp, \"\");\n if (!this.style.includes(\"xfa-spacerun:yes\")) {\n str = str.replaceAll(spacesRegExp, \" \");\n }\n } else {\n this[$richText] = true;\n }\n\n if (str) {\n this[$content] += str;\n }\n }\n\n [$pushGlyphs](measure, mustPop = true) {\n const xfaFont = Object.create(null);\n const margin = {\n top: NaN,\n bottom: NaN,\n left: NaN,\n right: NaN,\n };\n let lineHeight = null;\n for (const [key, value] of this.style\n .split(\";\")\n .map(s => s.split(\":\", 2))) {\n switch (key) {\n case \"font-family\":\n xfaFont.typeface = stripQuotes(value);\n break;\n case \"font-size\":\n xfaFont.size = getMeasurement(value);\n break;\n case \"font-weight\":\n xfaFont.weight = value;\n break;\n case \"font-style\":\n xfaFont.posture = value;\n break;\n case \"letter-spacing\":\n xfaFont.letterSpacing = getMeasurement(value);\n break;\n case \"margin\":\n const values = value.split(/ \\t/).map(x => getMeasurement(x));\n switch (values.length) {\n case 1:\n margin.top =\n margin.bottom =\n margin.left =\n margin.right =\n values[0];\n break;\n case 2:\n margin.top = margin.bottom = values[0];\n margin.left = margin.right = values[1];\n break;\n case 3:\n margin.top = values[0];\n margin.bottom = values[2];\n margin.left = margin.right = values[1];\n break;\n case 4:\n margin.top = values[0];\n margin.left = values[1];\n margin.bottom = values[2];\n margin.right = values[3];\n break;\n }\n break;\n case \"margin-top\":\n margin.top = getMeasurement(value);\n break;\n case \"margin-bottom\":\n margin.bottom = getMeasurement(value);\n break;\n case \"margin-left\":\n margin.left = getMeasurement(value);\n break;\n case \"margin-right\":\n margin.right = getMeasurement(value);\n break;\n case \"line-height\":\n lineHeight = getMeasurement(value);\n break;\n }\n }\n\n measure.pushData(xfaFont, margin, lineHeight);\n\n if (this[$content]) {\n measure.addString(this[$content]);\n } else {\n for (const child of this[$getChildren]()) {\n if (child[$nodeName] === \"#text\") {\n measure.addString(child[$content]);\n continue;\n }\n child[$pushGlyphs](measure);\n }\n }\n\n if (mustPop) {\n measure.popFont();\n }\n }\n\n [$toHTML](availableSpace) {\n const children = [];\n this[$extra] = {\n children,\n };\n\n this[$childrenToHTML]({});\n\n if (children.length === 0 && !this[$content]) {\n return HTMLResult.EMPTY;\n }\n\n let value;\n if (this[$richText]) {\n value = this[$content]\n ? this[$content].replaceAll(crlfForRichTextRegExp, \"\\n\")\n : undefined;\n } else {\n value = this[$content] || undefined;\n }\n\n return HTMLResult.success({\n name: this[$nodeName],\n attributes: {\n href: this.href,\n style: mapStyle(this.style, this, this[$richText]),\n },\n children,\n value,\n });\n }\n}\n\nclass A extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"a\");\n this.href = fixURL(attributes.href) || \"\";\n }\n}\n\nclass B extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"b\");\n }\n\n [$pushGlyphs](measure) {\n measure.pushFont({ weight: \"bold\" });\n super[$pushGlyphs](measure);\n measure.popFont();\n }\n}\n\nclass Body extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"body\");\n }\n\n [$toHTML](availableSpace) {\n const res = super[$toHTML](availableSpace);\n const { html } = res;\n if (!html) {\n return HTMLResult.EMPTY;\n }\n html.name = \"div\";\n html.attributes.class = [\"xfaRich\"];\n return res;\n }\n}\n\nclass Br extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"br\");\n }\n\n [$text]() {\n return \"\\n\";\n }\n\n [$pushGlyphs](measure) {\n measure.addString(\"\\n\");\n }\n\n [$toHTML](availableSpace) {\n return HTMLResult.success({\n name: \"br\",\n });\n }\n}\n\nclass Html extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"html\");\n }\n\n [$toHTML](availableSpace) {\n const children = [];\n this[$extra] = {\n children,\n };\n\n this[$childrenToHTML]({});\n if (children.length === 0) {\n return HTMLResult.success({\n name: \"div\",\n attributes: {\n class: [\"xfaRich\"],\n style: {},\n },\n value: this[$content] || \"\",\n });\n }\n\n if (children.length === 1) {\n const child = children[0];\n if (child.attributes?.class.includes(\"xfaRich\")) {\n return HTMLResult.success(child);\n }\n }\n\n return HTMLResult.success({\n name: \"div\",\n attributes: {\n class: [\"xfaRich\"],\n style: {},\n },\n children,\n });\n }\n}\n\nclass I extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"i\");\n }\n\n [$pushGlyphs](measure) {\n measure.pushFont({ posture: \"italic\" });\n super[$pushGlyphs](measure);\n measure.popFont();\n }\n}\n\nclass Li extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"li\");\n }\n}\n\nclass Ol extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"ol\");\n }\n}\n\nclass P extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"p\");\n }\n\n [$pushGlyphs](measure) {\n super[$pushGlyphs](measure, /* mustPop = */ false);\n measure.addString(\"\\n\");\n measure.addPara();\n measure.popFont();\n }\n\n [$text]() {\n const siblings = this[$getParent]()[$getChildren]();\n if (siblings.at(-1) === this) {\n return super[$text]();\n }\n return super[$text]() + \"\\n\";\n }\n}\n\nclass Span extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"span\");\n }\n}\n\nclass Sub extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"sub\");\n }\n}\n\nclass Sup extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"sup\");\n }\n}\n\nclass Ul extends XhtmlObject {\n constructor(attributes) {\n super(attributes, \"ul\");\n }\n}\n\nclass XhtmlNamespace {\n static [$buildXFAObject](name, attributes) {\n if (XhtmlNamespace.hasOwnProperty(name)) {\n return XhtmlNamespace[name](attributes);\n }\n return undefined;\n }\n\n static a(attributes) {\n return new A(attributes);\n }\n\n static b(attributes) {\n return new B(attributes);\n }\n\n static body(attributes) {\n return new Body(attributes);\n }\n\n static br(attributes) {\n return new Br(attributes);\n }\n\n static html(attributes) {\n return new Html(attributes);\n }\n\n static i(attributes) {\n return new I(attributes);\n }\n\n static li(attributes) {\n return new Li(attributes);\n }\n\n static ol(attributes) {\n return new Ol(attributes);\n }\n\n static p(attributes) {\n return new P(attributes);\n }\n\n static span(attributes) {\n return new Span(attributes);\n }\n\n static sub(attributes) {\n return new Sub(attributes);\n }\n\n static sup(attributes) {\n return new Sup(attributes);\n }\n\n static ul(attributes) {\n return new Ul(attributes);\n }\n}\n\nexport { XhtmlNamespace };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ConfigNamespace } from \"./config.js\";\nimport { ConnectionSetNamespace } from \"./connection_set.js\";\nimport { DatasetsNamespace } from \"./datasets.js\";\nimport { LocaleSetNamespace } from \"./locale_set.js\";\nimport { SignatureNamespace } from \"./signature.js\";\nimport { StylesheetNamespace } from \"./stylesheet.js\";\nimport { TemplateNamespace } from \"./template.js\";\nimport { XdpNamespace } from \"./xdp.js\";\nimport { XhtmlNamespace } from \"./xhtml.js\";\n\nconst NamespaceSetUp = {\n config: ConfigNamespace,\n connection: ConnectionSetNamespace,\n datasets: DatasetsNamespace,\n localeSet: LocaleSetNamespace,\n signature: SignatureNamespace,\n stylesheet: StylesheetNamespace,\n template: TemplateNamespace,\n xdp: XdpNamespace,\n xhtml: XhtmlNamespace,\n};\n\nexport { NamespaceSetUp };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { $buildXFAObject } from \"./namespaces.js\";\nimport { XmlObject } from \"./xfa_object.js\";\n\nclass UnknownNamespace {\n constructor(nsId) {\n this.namespaceId = nsId;\n }\n\n [$buildXFAObject](name, attributes) {\n return new XmlObject(this.namespaceId, name, attributes);\n }\n}\n\nexport { UnknownNamespace };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { $buildXFAObject, NamespaceIds } from \"./namespaces.js\";\nimport {\n $cleanup,\n $finalize,\n $ids,\n $isNsAgnostic,\n $nsAttributes,\n $onChild,\n $resolvePrototypes,\n $root,\n} from \"./symbol_utils.js\";\nimport { NamespaceSetUp } from \"./setup.js\";\nimport { Template } from \"./template.js\";\nimport { UnknownNamespace } from \"./unknown.js\";\nimport { warn } from \"../../shared/util.js\";\nimport { XFAObject } from \"./xfa_object.js\";\n\nclass Root extends XFAObject {\n constructor(ids) {\n super(-1, \"root\", Object.create(null));\n this.element = null;\n this[$ids] = ids;\n }\n\n [$onChild](child) {\n this.element = child;\n return true;\n }\n\n [$finalize]() {\n super[$finalize]();\n if (this.element.template instanceof Template) {\n // Set the root element in $ids using a symbol in order\n // to avoid conflict with real IDs.\n this[$ids].set($root, this.element);\n\n this.element.template[$resolvePrototypes](this[$ids]);\n this.element.template[$ids] = this[$ids];\n }\n }\n}\n\nclass Empty extends XFAObject {\n constructor() {\n super(-1, \"\", Object.create(null));\n }\n\n [$onChild](_) {\n return false;\n }\n}\n\nclass Builder {\n constructor(rootNameSpace = null) {\n this._namespaceStack = [];\n this._nsAgnosticLevel = 0;\n\n // Each prefix has its own stack\n this._namespacePrefixes = new Map();\n this._namespaces = new Map();\n this._nextNsId = Math.max(\n ...Object.values(NamespaceIds).map(({ id }) => id)\n );\n this._currentNamespace =\n rootNameSpace || new UnknownNamespace(++this._nextNsId);\n }\n\n buildRoot(ids) {\n return new Root(ids);\n }\n\n build({ nsPrefix, name, attributes, namespace, prefixes }) {\n const hasNamespaceDef = namespace !== null;\n if (hasNamespaceDef) {\n // Define the current namespace to use.\n this._namespaceStack.push(this._currentNamespace);\n this._currentNamespace = this._searchNamespace(namespace);\n }\n\n if (prefixes) {\n // The xml node may have namespace prefix definitions\n this._addNamespacePrefix(prefixes);\n }\n\n if (attributes.hasOwnProperty($nsAttributes)) {\n // Only support xfa-data namespace.\n const dataTemplate = NamespaceSetUp.datasets;\n const nsAttrs = attributes[$nsAttributes];\n let xfaAttrs = null;\n for (const [ns, attrs] of Object.entries(nsAttrs)) {\n const nsToUse = this._getNamespaceToUse(ns);\n if (nsToUse === dataTemplate) {\n xfaAttrs = { xfa: attrs };\n break;\n }\n }\n if (xfaAttrs) {\n attributes[$nsAttributes] = xfaAttrs;\n } else {\n delete attributes[$nsAttributes];\n }\n }\n\n const namespaceToUse = this._getNamespaceToUse(nsPrefix);\n const node =\n namespaceToUse?.[$buildXFAObject](name, attributes) || new Empty();\n\n if (node[$isNsAgnostic]()) {\n this._nsAgnosticLevel++;\n }\n\n // In case the node has some namespace things,\n // we must pop the different stacks.\n if (hasNamespaceDef || prefixes || node[$isNsAgnostic]()) {\n node[$cleanup] = {\n hasNamespace: hasNamespaceDef,\n prefixes,\n nsAgnostic: node[$isNsAgnostic](),\n };\n }\n\n return node;\n }\n\n isNsAgnostic() {\n return this._nsAgnosticLevel > 0;\n }\n\n _searchNamespace(nsName) {\n let ns = this._namespaces.get(nsName);\n if (ns) {\n return ns;\n }\n for (const [name, { check }] of Object.entries(NamespaceIds)) {\n if (check(nsName)) {\n ns = NamespaceSetUp[name];\n if (ns) {\n this._namespaces.set(nsName, ns);\n return ns;\n }\n // The namespace is known but not handled.\n break;\n }\n }\n\n ns = new UnknownNamespace(++this._nextNsId);\n this._namespaces.set(nsName, ns);\n return ns;\n }\n\n _addNamespacePrefix(prefixes) {\n for (const { prefix, value } of prefixes) {\n const namespace = this._searchNamespace(value);\n let prefixStack = this._namespacePrefixes.get(prefix);\n if (!prefixStack) {\n prefixStack = [];\n this._namespacePrefixes.set(prefix, prefixStack);\n }\n prefixStack.push(namespace);\n }\n }\n\n _getNamespaceToUse(prefix) {\n if (!prefix) {\n return this._currentNamespace;\n }\n const prefixStack = this._namespacePrefixes.get(prefix);\n if (prefixStack?.length > 0) {\n return prefixStack.at(-1);\n }\n\n warn(`Unknown namespace prefix: ${prefix}.`);\n return null;\n }\n\n clean(data) {\n const { hasNamespace, prefixes, nsAgnostic } = data;\n if (hasNamespace) {\n this._currentNamespace = this._namespaceStack.pop();\n }\n if (prefixes) {\n prefixes.forEach(({ prefix }) => {\n this._namespacePrefixes.get(prefix).pop();\n });\n }\n if (nsAgnostic) {\n this._nsAgnosticLevel--;\n }\n }\n}\n\nexport { Builder };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n $acceptWhitespace,\n $clean,\n $content,\n $finalize,\n $globalData,\n $isCDATAXml,\n $nsAttributes,\n $onChild,\n $onText,\n $setId,\n} from \"./symbol_utils.js\";\nimport { XMLParserBase, XMLParserErrorCode } from \"../xml_parser.js\";\nimport { Builder } from \"./builder.js\";\nimport { warn } from \"../../shared/util.js\";\n\nclass XFAParser extends XMLParserBase {\n constructor(rootNameSpace = null, richText = false) {\n super();\n this._builder = new Builder(rootNameSpace);\n this._stack = [];\n this._globalData = {\n usedTypefaces: new Set(),\n };\n this._ids = new Map();\n this._current = this._builder.buildRoot(this._ids);\n this._errorCode = XMLParserErrorCode.NoError;\n this._whiteRegex = /^\\s+$/;\n this._nbsps = /\\xa0+/g;\n this._richText = richText;\n }\n\n parse(data) {\n this.parseXml(data);\n\n if (this._errorCode !== XMLParserErrorCode.NoError) {\n return undefined;\n }\n\n this._current[$finalize]();\n\n return this._current.element;\n }\n\n onText(text) {\n // Normally by definition a   is unbreakable\n // but in real life Acrobat can break strings on  .\n text = text.replace(this._nbsps, match => match.slice(1) + \" \");\n if (this._richText || this._current[$acceptWhitespace]()) {\n this._current[$onText](text, this._richText);\n return;\n }\n\n if (this._whiteRegex.test(text)) {\n return;\n }\n this._current[$onText](text.trim());\n }\n\n onCdata(text) {\n this._current[$onText](text);\n }\n\n _mkAttributes(attributes, tagName) {\n // Transform attributes into an object and get out\n // namespaces information.\n let namespace = null;\n let prefixes = null;\n const attributeObj = Object.create({});\n for (const { name, value } of attributes) {\n if (name === \"xmlns\") {\n if (!namespace) {\n namespace = value;\n } else {\n warn(`XFA - multiple namespace definition in <${tagName}>`);\n }\n } else if (name.startsWith(\"xmlns:\")) {\n const prefix = name.substring(\"xmlns:\".length);\n if (!prefixes) {\n prefixes = [];\n }\n prefixes.push({ prefix, value });\n } else {\n const i = name.indexOf(\":\");\n if (i === -1) {\n attributeObj[name] = value;\n } else {\n // Attributes can have their own namespace.\n // For example in data, we can have \n let nsAttrs = attributeObj[$nsAttributes];\n if (!nsAttrs) {\n nsAttrs = attributeObj[$nsAttributes] = Object.create(null);\n }\n const [ns, attrName] = [name.slice(0, i), name.slice(i + 1)];\n const attrs = (nsAttrs[ns] ||= Object.create(null));\n attrs[attrName] = value;\n }\n }\n }\n\n return [namespace, prefixes, attributeObj];\n }\n\n _getNameAndPrefix(name, nsAgnostic) {\n const i = name.indexOf(\":\");\n if (i === -1) {\n return [name, null];\n }\n return [name.substring(i + 1), nsAgnostic ? \"\" : name.substring(0, i)];\n }\n\n onBeginElement(tagName, attributes, isEmpty) {\n const [namespace, prefixes, attributesObj] = this._mkAttributes(\n attributes,\n tagName\n );\n const [name, nsPrefix] = this._getNameAndPrefix(\n tagName,\n this._builder.isNsAgnostic()\n );\n const node = this._builder.build({\n nsPrefix,\n name,\n attributes: attributesObj,\n namespace,\n prefixes,\n });\n node[$globalData] = this._globalData;\n\n if (isEmpty) {\n // No children: just push the node into its parent.\n node[$finalize]();\n if (this._current[$onChild](node)) {\n node[$setId](this._ids);\n }\n node[$clean](this._builder);\n return;\n }\n\n this._stack.push(this._current);\n this._current = node;\n }\n\n onEndElement(name) {\n const node = this._current;\n if (node[$isCDATAXml]() && typeof node[$content] === \"string\") {\n const parser = new XFAParser();\n parser._globalData = this._globalData;\n const root = parser.parse(node[$content]);\n node[$content] = null;\n node[$onChild](root);\n }\n\n node[$finalize]();\n this._current = this._stack.pop();\n if (this._current[$onChild](node)) {\n node[$setId](this._ids);\n }\n node[$clean](this._builder);\n }\n\n onError(code) {\n this._errorCode = code;\n }\n}\n\nexport { XFAParser };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n $appendChild,\n $globalData,\n $nodeName,\n $text,\n $toHTML,\n $toPages,\n} from \"./symbol_utils.js\";\nimport { Binder } from \"./bind.js\";\nimport { DataHandler } from \"./data.js\";\nimport { FontFinder } from \"./fonts.js\";\nimport { stripQuotes } from \"./utils.js\";\nimport { warn } from \"../../shared/util.js\";\nimport { XFAParser } from \"./parser.js\";\nimport { XhtmlNamespace } from \"./xhtml.js\";\n\nclass XFAFactory {\n constructor(data) {\n try {\n this.root = new XFAParser().parse(XFAFactory._createDocument(data));\n const binder = new Binder(this.root);\n this.form = binder.bind();\n this.dataHandler = new DataHandler(this.root, binder.getData());\n this.form[$globalData].template = this.form;\n } catch (e) {\n warn(`XFA - an error occurred during parsing and binding: ${e}`);\n }\n }\n\n isValid() {\n return this.root && this.form;\n }\n\n /**\n * In order to avoid to block the event loop, the conversion\n * into pages is made asynchronously.\n */\n _createPagesHelper() {\n const iterator = this.form[$toPages]();\n return new Promise((resolve, reject) => {\n const nextIteration = () => {\n try {\n const value = iterator.next();\n if (value.done) {\n resolve(value.value);\n } else {\n setTimeout(nextIteration, 0);\n }\n } catch (e) {\n reject(e);\n }\n };\n setTimeout(nextIteration, 0);\n });\n }\n\n async _createPages() {\n try {\n this.pages = await this._createPagesHelper();\n this.dims = this.pages.children.map(c => {\n const { width, height } = c.attributes.style;\n return [0, 0, parseInt(width), parseInt(height)];\n });\n } catch (e) {\n warn(`XFA - an error occurred during layout: ${e}`);\n }\n }\n\n getBoundingBox(pageIndex) {\n return this.dims[pageIndex];\n }\n\n async getNumPages() {\n if (!this.pages) {\n await this._createPages();\n }\n return this.dims.length;\n }\n\n setImages(images) {\n this.form[$globalData].images = images;\n }\n\n setFonts(fonts) {\n this.form[$globalData].fontFinder = new FontFinder(fonts);\n const missingFonts = [];\n for (let typeface of this.form[$globalData].usedTypefaces) {\n typeface = stripQuotes(typeface);\n const font = this.form[$globalData].fontFinder.find(typeface);\n if (!font) {\n missingFonts.push(typeface);\n }\n }\n\n if (missingFonts.length > 0) {\n return missingFonts;\n }\n\n return null;\n }\n\n appendFonts(fonts, reallyMissingFonts) {\n this.form[$globalData].fontFinder.add(fonts, reallyMissingFonts);\n }\n\n async getPages() {\n if (!this.pages) {\n await this._createPages();\n }\n const pages = this.pages;\n this.pages = null;\n return pages;\n }\n\n serializeData(storage) {\n return this.dataHandler.serialize(storage);\n }\n\n static _createDocument(data) {\n if (!data[\"/xdp:xdp\"]) {\n return data[\"xdp:xdp\"];\n }\n return Object.values(data).join(\"\");\n }\n\n static getRichTextAsHtml(rc) {\n if (!rc || typeof rc !== \"string\") {\n return null;\n }\n\n try {\n let root = new XFAParser(XhtmlNamespace, /* richText */ true).parse(rc);\n if (![\"body\", \"xhtml\"].includes(root[$nodeName])) {\n // No body, so create one.\n const newRoot = XhtmlNamespace.body({});\n newRoot[$appendChild](root);\n root = newRoot;\n }\n\n const result = root[$toHTML]();\n if (!result.success) {\n return null;\n }\n\n const { html } = result;\n const { attributes } = html;\n if (attributes) {\n if (attributes.class) {\n attributes.class = attributes.class.filter(\n attr => !attr.startsWith(\"xfa\")\n );\n }\n attributes.dir = \"auto\";\n }\n\n return { html, str: root[$text]() };\n } catch (e) {\n warn(`XFA - an error occurred during parsing of rich text: ${e}`);\n }\n return null;\n }\n}\n\nexport { XFAFactory };\n","/* Copyright 2012 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AnnotationActionEventType,\n AnnotationBorderStyleType,\n AnnotationEditorType,\n AnnotationFieldFlag,\n AnnotationFlag,\n AnnotationReplyType,\n AnnotationType,\n assert,\n BASELINE_FACTOR,\n FeatureTest,\n getModificationDate,\n IDENTITY_MATRIX,\n info,\n LINE_DESCENT_FACTOR,\n LINE_FACTOR,\n OPS,\n RenderingIntentFlag,\n shadow,\n stringToPDFString,\n unreachable,\n Util,\n warn,\n} from \"../shared/util.js\";\nimport {\n collectActions,\n escapeString,\n getInheritableProperty,\n getRotationMatrix,\n isAscii,\n isNumberArray,\n lookupMatrix,\n lookupNormalRect,\n lookupRect,\n numberToString,\n stringToUTF16String,\n} from \"./core_utils.js\";\nimport {\n createDefaultAppearance,\n FakeUnicodeFont,\n getPdfColor,\n parseAppearanceStream,\n parseDefaultAppearance,\n} from \"./default_appearance.js\";\nimport { Dict, isName, isRefsEqual, Name, Ref, RefSet } from \"./primitives.js\";\nimport { Stream, StringStream } from \"./stream.js\";\nimport { BaseStream } from \"./base_stream.js\";\nimport { bidi } from \"./bidi.js\";\nimport { Catalog } from \"./catalog.js\";\nimport { ColorSpace } from \"./colorspace.js\";\nimport { FileSpec } from \"./file_spec.js\";\nimport { JpegStream } from \"./jpeg_stream.js\";\nimport { ObjectLoader } from \"./object_loader.js\";\nimport { OperatorList } from \"./operator_list.js\";\nimport { writeObject } from \"./writer.js\";\nimport { XFAFactory } from \"./xfa/factory.js\";\n\nclass AnnotationFactory {\n static createGlobals(pdfManager) {\n return Promise.all([\n pdfManager.ensureCatalog(\"acroForm\"),\n pdfManager.ensureDoc(\"xfaDatasets\"),\n pdfManager.ensureCatalog(\"structTreeRoot\"),\n // Only necessary to prevent the `Catalog.baseUrl`-getter, used\n // with some Annotations, from throwing and thus breaking parsing:\n pdfManager.ensureCatalog(\"baseUrl\"),\n // Only necessary to prevent the `Catalog.attachments`-getter, used\n // with \"GoToE\" actions, from throwing and thus breaking parsing:\n pdfManager.ensureCatalog(\"attachments\"),\n ]).then(\n // eslint-disable-next-line arrow-body-style\n ([acroForm, xfaDatasets, structTreeRoot, baseUrl, attachments]) => {\n return {\n pdfManager,\n acroForm: acroForm instanceof Dict ? acroForm : Dict.empty,\n xfaDatasets,\n structTreeRoot,\n baseUrl,\n attachments,\n };\n },\n reason => {\n warn(`createGlobals: \"${reason}\".`);\n return null;\n }\n );\n }\n\n /**\n * Create an `Annotation` object of the correct type for the given reference\n * to an annotation dictionary. This yields a promise that is resolved when\n * the `Annotation` object is constructed.\n *\n * @param {XRef} xref\n * @param {Object} ref\n * @params {Object} annotationGlobals\n * @param {Object} idFactory\n * @param {boolean} [collectFields]\n * @param {Object} [pageRef]\n * @returns {Promise} A promise that is resolved with an {Annotation}\n * instance.\n */\n static async create(\n xref,\n ref,\n annotationGlobals,\n idFactory,\n collectFields,\n pageRef\n ) {\n const pageIndex = collectFields\n ? await this._getPageIndex(xref, ref, annotationGlobals.pdfManager)\n : null;\n\n return annotationGlobals.pdfManager.ensure(this, \"_create\", [\n xref,\n ref,\n annotationGlobals,\n idFactory,\n collectFields,\n pageIndex,\n pageRef,\n ]);\n }\n\n /**\n * @private\n */\n static _create(\n xref,\n ref,\n annotationGlobals,\n idFactory,\n collectFields = false,\n pageIndex = null,\n pageRef = null\n ) {\n const dict = xref.fetchIfRef(ref);\n if (!(dict instanceof Dict)) {\n return undefined;\n }\n\n const { acroForm, pdfManager } = annotationGlobals;\n const id =\n ref instanceof Ref ? ref.toString() : `annot_${idFactory.createObjId()}`;\n\n // Determine the annotation's subtype.\n let subtype = dict.get(\"Subtype\");\n subtype = subtype instanceof Name ? subtype.name : null;\n\n // Return the right annotation object based on the subtype and field type.\n const parameters = {\n xref,\n ref,\n dict,\n subtype,\n id,\n annotationGlobals,\n collectFields,\n needAppearances:\n !collectFields && acroForm.get(\"NeedAppearances\") === true,\n pageIndex,\n evaluatorOptions: pdfManager.evaluatorOptions,\n pageRef,\n };\n\n switch (subtype) {\n case \"Link\":\n return new LinkAnnotation(parameters);\n\n case \"Text\":\n return new TextAnnotation(parameters);\n\n case \"Widget\":\n let fieldType = getInheritableProperty({ dict, key: \"FT\" });\n fieldType = fieldType instanceof Name ? fieldType.name : null;\n\n switch (fieldType) {\n case \"Tx\":\n return new TextWidgetAnnotation(parameters);\n case \"Btn\":\n return new ButtonWidgetAnnotation(parameters);\n case \"Ch\":\n return new ChoiceWidgetAnnotation(parameters);\n case \"Sig\":\n return new SignatureWidgetAnnotation(parameters);\n }\n warn(\n `Unimplemented widget field type \"${fieldType}\", ` +\n \"falling back to base field type.\"\n );\n return new WidgetAnnotation(parameters);\n\n case \"Popup\":\n return new PopupAnnotation(parameters);\n\n case \"FreeText\":\n return new FreeTextAnnotation(parameters);\n\n case \"Line\":\n return new LineAnnotation(parameters);\n\n case \"Square\":\n return new SquareAnnotation(parameters);\n\n case \"Circle\":\n return new CircleAnnotation(parameters);\n\n case \"PolyLine\":\n return new PolylineAnnotation(parameters);\n\n case \"Polygon\":\n return new PolygonAnnotation(parameters);\n\n case \"Caret\":\n return new CaretAnnotation(parameters);\n\n case \"Ink\":\n return new InkAnnotation(parameters);\n\n case \"Highlight\":\n return new HighlightAnnotation(parameters);\n\n case \"Underline\":\n return new UnderlineAnnotation(parameters);\n\n case \"Squiggly\":\n return new SquigglyAnnotation(parameters);\n\n case \"StrikeOut\":\n return new StrikeOutAnnotation(parameters);\n\n case \"Stamp\":\n return new StampAnnotation(parameters);\n\n case \"FileAttachment\":\n return new FileAttachmentAnnotation(parameters);\n\n default:\n if (!collectFields) {\n if (!subtype) {\n warn(\"Annotation is missing the required /Subtype.\");\n } else {\n warn(\n `Unimplemented annotation type \"${subtype}\", ` +\n \"falling back to base annotation.\"\n );\n }\n }\n return new Annotation(parameters);\n }\n }\n\n static async _getPageIndex(xref, ref, pdfManager) {\n try {\n const annotDict = await xref.fetchIfRefAsync(ref);\n if (!(annotDict instanceof Dict)) {\n return -1;\n }\n const pageRef = annotDict.getRaw(\"P\");\n if (pageRef instanceof Ref) {\n try {\n const pageIndex = await pdfManager.ensureCatalog(\"getPageIndex\", [\n pageRef,\n ]);\n return pageIndex;\n } catch (ex) {\n info(`_getPageIndex -- not a valid page reference: \"${ex}\".`);\n }\n }\n if (annotDict.has(\"Kids\")) {\n return -1; // Not an annotation reference.\n }\n // Fallback to, potentially, checking the annotations of all pages.\n // PLEASE NOTE: This could force the *entire* PDF document to load,\n // hence it absolutely cannot be done unconditionally.\n const numPages = await pdfManager.ensureDoc(\"numPages\");\n\n for (let pageIndex = 0; pageIndex < numPages; pageIndex++) {\n const page = await pdfManager.getPage(pageIndex);\n const annotations = await pdfManager.ensure(page, \"annotations\");\n\n for (const annotRef of annotations) {\n if (annotRef instanceof Ref && isRefsEqual(annotRef, ref)) {\n return pageIndex;\n }\n }\n }\n } catch (ex) {\n warn(`_getPageIndex: \"${ex}\".`);\n }\n return -1;\n }\n\n static generateImages(annotations, xref, isOffscreenCanvasSupported) {\n if (!isOffscreenCanvasSupported) {\n warn(\n \"generateImages: OffscreenCanvas is not supported, cannot save or print some annotations with images.\"\n );\n return null;\n }\n let imagePromises;\n for (const { bitmapId, bitmap } of annotations) {\n if (!bitmap) {\n continue;\n }\n imagePromises ||= new Map();\n imagePromises.set(bitmapId, StampAnnotation.createImage(bitmap, xref));\n }\n\n return imagePromises;\n }\n\n static async saveNewAnnotations(evaluator, task, annotations, imagePromises) {\n const xref = evaluator.xref;\n let baseFontRef;\n const dependencies = [];\n const promises = [];\n const { isOffscreenCanvasSupported } = evaluator.options;\n\n for (const annotation of annotations) {\n if (annotation.deleted) {\n continue;\n }\n switch (annotation.annotationType) {\n case AnnotationEditorType.FREETEXT:\n if (!baseFontRef) {\n const baseFont = new Dict(xref);\n baseFont.set(\"BaseFont\", Name.get(\"Helvetica\"));\n baseFont.set(\"Type\", Name.get(\"Font\"));\n baseFont.set(\"Subtype\", Name.get(\"Type1\"));\n baseFont.set(\"Encoding\", Name.get(\"WinAnsiEncoding\"));\n const buffer = [];\n baseFontRef = xref.getNewTemporaryRef();\n await writeObject(baseFontRef, baseFont, buffer, xref);\n dependencies.push({ ref: baseFontRef, data: buffer.join(\"\") });\n }\n promises.push(\n FreeTextAnnotation.createNewAnnotation(\n xref,\n annotation,\n dependencies,\n { evaluator, task, baseFontRef }\n )\n );\n break;\n case AnnotationEditorType.HIGHLIGHT:\n if (annotation.quadPoints) {\n promises.push(\n HighlightAnnotation.createNewAnnotation(\n xref,\n annotation,\n dependencies\n )\n );\n } else {\n promises.push(\n InkAnnotation.createNewAnnotation(xref, annotation, dependencies)\n );\n }\n break;\n case AnnotationEditorType.INK:\n promises.push(\n InkAnnotation.createNewAnnotation(xref, annotation, dependencies)\n );\n break;\n case AnnotationEditorType.STAMP:\n if (!isOffscreenCanvasSupported) {\n break;\n }\n const image = await imagePromises.get(annotation.bitmapId);\n if (image.imageStream) {\n const { imageStream, smaskStream } = image;\n const buffer = [];\n if (smaskStream) {\n const smaskRef = xref.getNewTemporaryRef();\n await writeObject(smaskRef, smaskStream, buffer, xref);\n dependencies.push({ ref: smaskRef, data: buffer.join(\"\") });\n imageStream.dict.set(\"SMask\", smaskRef);\n buffer.length = 0;\n }\n const imageRef = (image.imageRef = xref.getNewTemporaryRef());\n await writeObject(imageRef, imageStream, buffer, xref);\n dependencies.push({ ref: imageRef, data: buffer.join(\"\") });\n image.imageStream = image.smaskStream = null;\n }\n promises.push(\n StampAnnotation.createNewAnnotation(\n xref,\n annotation,\n dependencies,\n { image }\n )\n );\n break;\n }\n }\n\n return {\n annotations: await Promise.all(promises),\n dependencies,\n };\n }\n\n static async printNewAnnotations(\n annotationGlobals,\n evaluator,\n task,\n annotations,\n imagePromises\n ) {\n if (!annotations) {\n return null;\n }\n\n const { options, xref } = evaluator;\n const promises = [];\n for (const annotation of annotations) {\n if (annotation.deleted) {\n continue;\n }\n switch (annotation.annotationType) {\n case AnnotationEditorType.FREETEXT:\n promises.push(\n FreeTextAnnotation.createNewPrintAnnotation(\n annotationGlobals,\n xref,\n annotation,\n {\n evaluator,\n task,\n evaluatorOptions: options,\n }\n )\n );\n break;\n case AnnotationEditorType.HIGHLIGHT:\n if (annotation.quadPoints) {\n promises.push(\n HighlightAnnotation.createNewPrintAnnotation(\n annotationGlobals,\n xref,\n annotation,\n {\n evaluatorOptions: options,\n }\n )\n );\n } else {\n promises.push(\n InkAnnotation.createNewPrintAnnotation(\n annotationGlobals,\n xref,\n annotation,\n {\n evaluatorOptions: options,\n }\n )\n );\n }\n break;\n case AnnotationEditorType.INK:\n promises.push(\n InkAnnotation.createNewPrintAnnotation(\n annotationGlobals,\n xref,\n annotation,\n {\n evaluatorOptions: options,\n }\n )\n );\n break;\n case AnnotationEditorType.STAMP:\n if (!options.isOffscreenCanvasSupported) {\n break;\n }\n const image = await imagePromises.get(annotation.bitmapId);\n if (image.imageStream) {\n const { imageStream, smaskStream } = image;\n if (smaskStream) {\n imageStream.dict.set(\"SMask\", smaskStream);\n }\n image.imageRef = new JpegStream(imageStream, imageStream.length);\n image.imageStream = image.smaskStream = null;\n }\n promises.push(\n StampAnnotation.createNewPrintAnnotation(\n annotationGlobals,\n xref,\n annotation,\n {\n image,\n evaluatorOptions: options,\n }\n )\n );\n break;\n }\n }\n\n return Promise.all(promises);\n }\n}\n\nfunction getRgbColor(color, defaultColor = new Uint8ClampedArray(3)) {\n if (!Array.isArray(color)) {\n return defaultColor;\n }\n\n const rgbColor = defaultColor || new Uint8ClampedArray(3);\n switch (color.length) {\n case 0: // Transparent, which we indicate with a null value\n return null;\n\n case 1: // Convert grayscale to RGB\n ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0);\n return rgbColor;\n\n case 3: // Convert RGB percentages to RGB\n ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0);\n return rgbColor;\n\n case 4: // Convert CMYK to RGB\n ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0);\n return rgbColor;\n\n default:\n return defaultColor;\n }\n}\n\nfunction getPdfColorArray(color) {\n return Array.from(color, c => c / 255);\n}\n\nfunction getQuadPoints(dict, rect) {\n // The region is described as a number of quadrilaterals.\n // Each quadrilateral must consist of eight coordinates.\n const quadPoints = dict.getArray(\"QuadPoints\");\n if (\n !isNumberArray(quadPoints, null) ||\n quadPoints.length === 0 ||\n quadPoints.length % 8 > 0\n ) {\n return null;\n }\n\n const quadPointsLists = [];\n for (let i = 0, ii = quadPoints.length / 8; i < ii; i++) {\n // Each series of eight numbers represents the coordinates for one\n // quadrilateral in the order [x1, y1, x2, y2, x3, y3, x4, y4].\n // Convert this to an array of objects with x and y coordinates.\n let minX = Infinity,\n maxX = -Infinity,\n minY = Infinity,\n maxY = -Infinity;\n for (let j = i * 8, jj = i * 8 + 8; j < jj; j += 2) {\n const x = quadPoints[j];\n const y = quadPoints[j + 1];\n\n minX = Math.min(x, minX);\n maxX = Math.max(x, maxX);\n minY = Math.min(y, minY);\n maxY = Math.max(y, maxY);\n }\n // The quadpoints should be ignored if any coordinate in the array\n // lies outside the region specified by the rectangle. The rectangle\n // can be `null` for markup annotations since their rectangle may be\n // incorrect (fixes bug 1538111).\n if (\n rect !== null &&\n (minX < rect[0] || maxX > rect[2] || minY < rect[1] || maxY > rect[3])\n ) {\n return null;\n }\n // The PDF specification states in section 12.5.6.10 (figure 64) that the\n // order of the quadpoints should be bottom left, bottom right, top right\n // and top left. However, in practice PDF files use a different order,\n // namely bottom left, bottom right, top left and top right (this is also\n // mentioned on https://github.com/highkite/pdfAnnotate#QuadPoints), so\n // this is the actual order we should work with. However, the situation is\n // even worse since Adobe's own applications and other applications violate\n // the specification and create annotations with other orders, namely top\n // left, top right, bottom left and bottom right or even top left,\n // top right, bottom right and bottom left. To avoid inconsistency and\n // broken rendering, we normalize all lists to put the quadpoints in the\n // same standard order (see https://stackoverflow.com/a/10729881).\n quadPointsLists.push([\n { x: minX, y: maxY },\n { x: maxX, y: maxY },\n { x: minX, y: minY },\n { x: maxX, y: minY },\n ]);\n }\n return quadPointsLists;\n}\n\nfunction getTransformMatrix(rect, bbox, matrix) {\n // 12.5.5: Algorithm: Appearance streams\n const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox(\n bbox,\n matrix\n );\n if (minX === maxX || minY === maxY) {\n // From real-life file, bbox was [0, 0, 0, 0]. In this case,\n // just apply the transform for rect\n return [1, 0, 0, 1, rect[0], rect[1]];\n }\n\n const xRatio = (rect[2] - rect[0]) / (maxX - minX);\n const yRatio = (rect[3] - rect[1]) / (maxY - minY);\n return [\n xRatio,\n 0,\n 0,\n yRatio,\n rect[0] - minX * xRatio,\n rect[1] - minY * yRatio,\n ];\n}\n\nclass Annotation {\n constructor(params) {\n const { dict, xref, annotationGlobals } = params;\n\n this.setTitle(dict.get(\"T\"));\n this.setContents(dict.get(\"Contents\"));\n this.setModificationDate(dict.get(\"M\"));\n this.setFlags(dict.get(\"F\"));\n this.setRectangle(dict.getArray(\"Rect\"));\n this.setColor(dict.getArray(\"C\"));\n this.setBorderStyle(dict);\n this.setAppearance(dict);\n this.setOptionalContent(dict);\n\n const MK = dict.get(\"MK\");\n this.setBorderAndBackgroundColors(MK);\n this.setRotation(MK, dict);\n this.ref = params.ref instanceof Ref ? params.ref : null;\n\n this._streams = [];\n if (this.appearance) {\n this._streams.push(this.appearance);\n }\n\n // The annotation cannot be changed (neither its position/visibility nor its\n // contents), hence we can just display its appearance and don't generate\n // a HTML element for it.\n const isLocked = !!(this.flags & AnnotationFlag.LOCKED);\n const isContentLocked = !!(this.flags & AnnotationFlag.LOCKEDCONTENTS);\n\n if (annotationGlobals.structTreeRoot) {\n let structParent = dict.get(\"StructParent\");\n structParent =\n Number.isInteger(structParent) && structParent >= 0 ? structParent : -1;\n\n annotationGlobals.structTreeRoot.addAnnotationIdToPage(\n params.pageRef,\n structParent\n );\n }\n\n // Expose public properties using a data object.\n this.data = {\n annotationFlags: this.flags,\n borderStyle: this.borderStyle,\n color: this.color,\n backgroundColor: this.backgroundColor,\n borderColor: this.borderColor,\n rotation: this.rotation,\n contentsObj: this._contents,\n hasAppearance: !!this.appearance,\n id: params.id,\n modificationDate: this.modificationDate,\n rect: this.rectangle,\n subtype: params.subtype,\n hasOwnCanvas: false,\n noRotate: !!(this.flags & AnnotationFlag.NOROTATE),\n noHTML: isLocked && isContentLocked,\n };\n\n if (params.collectFields) {\n // Fields can act as container for other fields and have\n // some actions even if no Annotation inherit from them.\n // Those fields can be referenced by CO (calculation order).\n const kids = dict.get(\"Kids\");\n if (Array.isArray(kids)) {\n const kidIds = [];\n for (const kid of kids) {\n if (kid instanceof Ref) {\n kidIds.push(kid.toString());\n }\n }\n if (kidIds.length !== 0) {\n this.data.kidIds = kidIds;\n }\n }\n\n this.data.actions = collectActions(xref, dict, AnnotationActionEventType);\n this.data.fieldName = this._constructFieldName(dict);\n this.data.pageIndex = params.pageIndex;\n }\n\n this._isOffscreenCanvasSupported =\n params.evaluatorOptions.isOffscreenCanvasSupported;\n this._fallbackFontDict = null;\n this._needAppearances = false;\n }\n\n /**\n * @private\n */\n _hasFlag(flags, flag) {\n return !!(flags & flag);\n }\n\n /**\n * @private\n */\n _isViewable(flags) {\n return (\n !this._hasFlag(flags, AnnotationFlag.INVISIBLE) &&\n !this._hasFlag(flags, AnnotationFlag.NOVIEW)\n );\n }\n\n /**\n * @private\n */\n _isPrintable(flags) {\n // In Acrobat, hidden flag cancels the print one\n // (see annotation_hidden_print.pdf).\n return (\n this._hasFlag(flags, AnnotationFlag.PRINT) &&\n !this._hasFlag(flags, AnnotationFlag.HIDDEN) &&\n !this._hasFlag(flags, AnnotationFlag.INVISIBLE)\n );\n }\n\n /**\n * Check if the annotation must be displayed by taking into account\n * the value found in the annotationStorage which may have been set\n * through JS.\n *\n * @public\n * @memberof Annotation\n * @param {AnnotationStorage} [annotationStorage] - Storage for annotation\n * @param {boolean} [_renderForms] - if true widgets are rendered thanks to\n * the annotation layer.\n */\n mustBeViewed(annotationStorage, _renderForms) {\n const noView = annotationStorage?.get(this.data.id)?.noView;\n if (noView !== undefined) {\n return !noView;\n }\n return this.viewable && !this._hasFlag(this.flags, AnnotationFlag.HIDDEN);\n }\n\n /**\n * Check if the annotation must be printed by taking into account\n * the value found in the annotationStorage which may have been set\n * through JS.\n *\n * @public\n * @memberof Annotation\n * @param {AnnotationStorage} [annotationStorage] - Storage for annotation\n */\n mustBePrinted(annotationStorage) {\n const noPrint = annotationStorage?.get(this.data.id)?.noPrint;\n if (noPrint !== undefined) {\n return !noPrint;\n }\n return this.printable;\n }\n\n /**\n * @type {boolean}\n */\n get viewable() {\n if (this.data.quadPoints === null) {\n return false;\n }\n if (this.flags === 0) {\n return true;\n }\n return this._isViewable(this.flags);\n }\n\n /**\n * @type {boolean}\n */\n get printable() {\n if (this.data.quadPoints === null) {\n return false;\n }\n if (this.flags === 0) {\n return false;\n }\n return this._isPrintable(this.flags);\n }\n\n /**\n * @private\n */\n _parseStringHelper(data) {\n const str = typeof data === \"string\" ? stringToPDFString(data) : \"\";\n const dir = str && bidi(str).dir === \"rtl\" ? \"rtl\" : \"ltr\";\n\n return { str, dir };\n }\n\n setDefaultAppearance(params) {\n const { dict, annotationGlobals } = params;\n\n const defaultAppearance =\n getInheritableProperty({ dict, key: \"DA\" }) ||\n annotationGlobals.acroForm.get(\"DA\");\n this._defaultAppearance =\n typeof defaultAppearance === \"string\" ? defaultAppearance : \"\";\n this.data.defaultAppearanceData = parseDefaultAppearance(\n this._defaultAppearance\n );\n }\n\n /**\n * Set the title.\n *\n * @param {string} title - The title of the annotation, used e.g. with\n * PopupAnnotations.\n */\n setTitle(title) {\n this._title = this._parseStringHelper(title);\n }\n\n /**\n * Set the contents.\n *\n * @param {string} contents - Text to display for the annotation or, if the\n * type of annotation does not display text, a\n * description of the annotation's contents\n */\n setContents(contents) {\n this._contents = this._parseStringHelper(contents);\n }\n\n /**\n * Set the modification date.\n *\n * @public\n * @memberof Annotation\n * @param {string} modificationDate - PDF date string that indicates when the\n * annotation was last modified\n */\n setModificationDate(modificationDate) {\n this.modificationDate =\n typeof modificationDate === \"string\" ? modificationDate : null;\n }\n\n /**\n * Set the flags.\n *\n * @public\n * @memberof Annotation\n * @param {number} flags - Unsigned 32-bit integer specifying annotation\n * characteristics\n * @see {@link shared/util.js}\n */\n setFlags(flags) {\n this.flags = Number.isInteger(flags) && flags > 0 ? flags : 0;\n if (\n this.flags & AnnotationFlag.INVISIBLE &&\n this.constructor.name !== \"Annotation\"\n ) {\n // From the pdf spec v1.7, section 12.5.3 (Annotation Flags):\n // If set, do not display the annotation if it does not belong to one of\n // the standard annotation types and no annotation handler is available.\n //\n // So we can remove the flag in case we have a known annotation type.\n this.flags ^= AnnotationFlag.INVISIBLE;\n }\n }\n\n /**\n * Check if a provided flag is set.\n *\n * @public\n * @memberof Annotation\n * @param {number} flag - Hexadecimal representation for an annotation\n * characteristic\n * @returns {boolean}\n * @see {@link shared/util.js}\n */\n hasFlag(flag) {\n return this._hasFlag(this.flags, flag);\n }\n\n /**\n * Set the rectangle.\n *\n * @public\n * @memberof Annotation\n * @param {Array} rectangle - The rectangle array with exactly four entries\n */\n setRectangle(rectangle) {\n this.rectangle = lookupNormalRect(rectangle, [0, 0, 0, 0]);\n }\n\n /**\n * Set the color and take care of color space conversion.\n * The default value is black, in RGB color space.\n *\n * @public\n * @memberof Annotation\n * @param {Array} color - The color array containing either 0\n * (transparent), 1 (grayscale), 3 (RGB) or\n * 4 (CMYK) elements\n */\n setColor(color) {\n this.color = getRgbColor(color);\n }\n\n /**\n * Set the line endings; should only be used with specific annotation types.\n * @param {Array} lineEndings - The line endings array.\n */\n setLineEndings(lineEndings) {\n if (typeof PDFJSDev !== \"undefined\" && PDFJSDev.test(\"MOZCENTRAL\")) {\n throw new Error(\"Not implemented: setLineEndings\");\n }\n this.lineEndings = [\"None\", \"None\"]; // The default values.\n\n if (Array.isArray(lineEndings) && lineEndings.length === 2) {\n for (let i = 0; i < 2; i++) {\n const obj = lineEndings[i];\n\n if (obj instanceof Name) {\n switch (obj.name) {\n case \"None\":\n continue;\n case \"Square\":\n case \"Circle\":\n case \"Diamond\":\n case \"OpenArrow\":\n case \"ClosedArrow\":\n case \"Butt\":\n case \"ROpenArrow\":\n case \"RClosedArrow\":\n case \"Slash\":\n this.lineEndings[i] = obj.name;\n continue;\n }\n }\n warn(`Ignoring invalid lineEnding: ${obj}`);\n }\n }\n }\n\n setRotation(mk, dict) {\n this.rotation = 0;\n let angle = mk instanceof Dict ? mk.get(\"R\") || 0 : dict.get(\"Rotate\") || 0;\n if (Number.isInteger(angle) && angle !== 0) {\n angle %= 360;\n if (angle < 0) {\n angle += 360;\n }\n if (angle % 90 === 0) {\n this.rotation = angle;\n }\n }\n }\n\n /**\n * Set the color for background and border if any.\n * The default values are transparent.\n *\n * @public\n * @memberof Annotation\n * @param {Dict} mk - The MK dictionary\n */\n setBorderAndBackgroundColors(mk) {\n if (mk instanceof Dict) {\n this.borderColor = getRgbColor(mk.getArray(\"BC\"), null);\n this.backgroundColor = getRgbColor(mk.getArray(\"BG\"), null);\n } else {\n this.borderColor = this.backgroundColor = null;\n }\n }\n\n /**\n * Set the border style (as AnnotationBorderStyle object).\n *\n * @public\n * @memberof Annotation\n * @param {Dict} borderStyle - The border style dictionary\n */\n setBorderStyle(borderStyle) {\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(this.rectangle, \"setRectangle must have been called previously.\");\n }\n\n this.borderStyle = new AnnotationBorderStyle();\n if (!(borderStyle instanceof Dict)) {\n return;\n }\n if (borderStyle.has(\"BS\")) {\n const dict = borderStyle.get(\"BS\");\n\n if (dict instanceof Dict) {\n const dictType = dict.get(\"Type\");\n\n if (!dictType || isName(dictType, \"Border\")) {\n this.borderStyle.setWidth(dict.get(\"W\"), this.rectangle);\n this.borderStyle.setStyle(dict.get(\"S\"));\n this.borderStyle.setDashArray(dict.getArray(\"D\"));\n }\n }\n } else if (borderStyle.has(\"Border\")) {\n const array = borderStyle.getArray(\"Border\");\n if (Array.isArray(array) && array.length >= 3) {\n this.borderStyle.setHorizontalCornerRadius(array[0]);\n this.borderStyle.setVerticalCornerRadius(array[1]);\n this.borderStyle.setWidth(array[2], this.rectangle);\n\n if (array.length === 4) {\n // Dash array available\n this.borderStyle.setDashArray(array[3], /* forceStyle = */ true);\n }\n }\n } else {\n // There are no border entries in the dictionary. According to the\n // specification, we should draw a solid border of width 1 in that\n // case, but Adobe Reader did not implement that part of the\n // specification and instead draws no border at all, so we do the same.\n // See also https://github.com/mozilla/pdf.js/issues/6179.\n this.borderStyle.setWidth(0);\n }\n }\n\n /**\n * Set the (normal) appearance.\n *\n * @public\n * @memberof Annotation\n * @param {Dict} dict - The annotation's data dictionary\n */\n setAppearance(dict) {\n this.appearance = null;\n\n const appearanceStates = dict.get(\"AP\");\n if (!(appearanceStates instanceof Dict)) {\n return;\n }\n\n // In case the normal appearance is a stream, then it is used directly.\n const normalAppearanceState = appearanceStates.get(\"N\");\n if (normalAppearanceState instanceof BaseStream) {\n this.appearance = normalAppearanceState;\n return;\n }\n if (!(normalAppearanceState instanceof Dict)) {\n return;\n }\n\n // In case the normal appearance is a dictionary, the `AS` entry provides\n // the key of the stream in this dictionary.\n const as = dict.get(\"AS\");\n if (!(as instanceof Name) || !normalAppearanceState.has(as.name)) {\n return;\n }\n const appearance = normalAppearanceState.get(as.name);\n if (appearance instanceof BaseStream) {\n this.appearance = appearance;\n }\n }\n\n setOptionalContent(dict) {\n this.oc = null;\n\n const oc = dict.get(\"OC\");\n if (oc instanceof Name) {\n warn(\"setOptionalContent: Support for /Name-entry is not implemented.\");\n } else if (oc instanceof Dict) {\n this.oc = oc;\n }\n }\n\n loadResources(keys, appearance) {\n return appearance.dict.getAsync(\"Resources\").then(resources => {\n if (!resources) {\n return undefined;\n }\n\n const objectLoader = new ObjectLoader(resources, keys, resources.xref);\n return objectLoader.load().then(function () {\n return resources;\n });\n });\n }\n\n async getOperatorList(\n evaluator,\n task,\n intent,\n renderForms,\n annotationStorage\n ) {\n const { hasOwnCanvas, id, rect } = this.data;\n let appearance = this.appearance;\n const isUsingOwnCanvas = !!(\n hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY\n );\n if (isUsingOwnCanvas && (rect[0] === rect[2] || rect[1] === rect[3])) {\n // Empty annotation, don't draw anything.\n this.data.hasOwnCanvas = false;\n return {\n opList: new OperatorList(),\n separateForm: false,\n separateCanvas: false,\n };\n }\n if (!appearance) {\n if (!isUsingOwnCanvas) {\n return {\n opList: new OperatorList(),\n separateForm: false,\n separateCanvas: false,\n };\n }\n appearance = new StringStream(\"\");\n appearance.dict = new Dict();\n }\n\n const appearanceDict = appearance.dict;\n const resources = await this.loadResources(\n [\"ExtGState\", \"ColorSpace\", \"Pattern\", \"Shading\", \"XObject\", \"Font\"],\n appearance\n );\n const bbox = lookupRect(appearanceDict.getArray(\"BBox\"), [0, 0, 1, 1]);\n const matrix = lookupMatrix(\n appearanceDict.getArray(\"Matrix\"),\n IDENTITY_MATRIX\n );\n const transform = getTransformMatrix(rect, bbox, matrix);\n\n const opList = new OperatorList();\n\n let optionalContent;\n if (this.oc) {\n optionalContent = await evaluator.parseMarkedContentProps(\n this.oc,\n /* resources = */ null\n );\n }\n if (optionalContent !== undefined) {\n opList.addOp(OPS.beginMarkedContentProps, [\"OC\", optionalContent]);\n }\n\n opList.addOp(OPS.beginAnnotation, [\n id,\n rect,\n transform,\n matrix,\n isUsingOwnCanvas,\n ]);\n\n await evaluator.getOperatorList({\n stream: appearance,\n task,\n resources,\n operatorList: opList,\n fallbackFontDict: this._fallbackFontDict,\n });\n opList.addOp(OPS.endAnnotation, []);\n\n if (optionalContent !== undefined) {\n opList.addOp(OPS.endMarkedContent, []);\n }\n this.reset();\n return { opList, separateForm: false, separateCanvas: isUsingOwnCanvas };\n }\n\n async save(evaluator, task, annotationStorage) {\n return null;\n }\n\n get hasTextContent() {\n return false;\n }\n\n async extractTextContent(evaluator, task, viewBox) {\n if (!this.appearance) {\n return;\n }\n\n const resources = await this.loadResources(\n [\"ExtGState\", \"Font\", \"Properties\", \"XObject\"],\n this.appearance\n );\n\n const text = [];\n const buffer = [];\n let firstPosition = null;\n const sink = {\n desiredSize: Math.Infinity,\n ready: true,\n\n enqueue(chunk, size) {\n for (const item of chunk.items) {\n if (item.str === undefined) {\n continue;\n }\n firstPosition ||= item.transform.slice(-2);\n buffer.push(item.str);\n if (item.hasEOL) {\n text.push(buffer.join(\"\").trimEnd());\n buffer.length = 0;\n }\n }\n },\n };\n\n await evaluator.getTextContent({\n stream: this.appearance,\n task,\n resources,\n includeMarkedContent: true,\n keepWhiteSpace: true,\n sink,\n viewBox,\n });\n this.reset();\n\n if (buffer.length) {\n text.push(buffer.join(\"\").trimEnd());\n }\n\n if (text.length > 1 || text[0]) {\n const appearanceDict = this.appearance.dict;\n const bbox = lookupRect(appearanceDict.getArray(\"BBox\"), null);\n const matrix = lookupMatrix(appearanceDict.getArray(\"Matrix\"), null);\n\n this.data.textPosition = this._transformPoint(\n firstPosition,\n bbox,\n matrix\n );\n this.data.textContent = text;\n }\n }\n\n _transformPoint(coords, bbox, matrix) {\n const { rect } = this.data;\n bbox ||= [0, 0, 1, 1];\n matrix ||= [1, 0, 0, 1, 0, 0];\n const transform = getTransformMatrix(rect, bbox, matrix);\n transform[4] -= rect[0];\n transform[5] -= rect[1];\n coords = Util.applyTransform(coords, transform);\n return Util.applyTransform(coords, matrix);\n }\n\n /**\n * Get field data for usage in JS sandbox.\n *\n * Field object is defined here:\n * https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/js_api_reference.pdf#page=16\n *\n * @public\n * @memberof Annotation\n * @returns {Object | null}\n */\n getFieldObject() {\n if (this.data.kidIds) {\n return {\n id: this.data.id,\n actions: this.data.actions,\n name: this.data.fieldName,\n strokeColor: this.data.borderColor,\n fillColor: this.data.backgroundColor,\n type: \"\",\n kidIds: this.data.kidIds,\n page: this.data.pageIndex,\n rotation: this.rotation,\n };\n }\n return null;\n }\n\n /**\n * Reset the annotation.\n *\n * This involves resetting the various streams that are either cached on the\n * annotation instance or created during its construction.\n *\n * @public\n * @memberof Annotation\n */\n reset() {\n if (\n (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) &&\n this.appearance &&\n !this._streams.includes(this.appearance)\n ) {\n unreachable(\"The appearance stream should always be reset.\");\n }\n\n for (const stream of this._streams) {\n stream.reset();\n }\n }\n\n /**\n * Construct the (fully qualified) field name from the (partial) field\n * names of the field and its ancestors.\n *\n * @private\n * @memberof Annotation\n * @param {Dict} dict - Complete widget annotation dictionary\n * @returns {string}\n */\n _constructFieldName(dict) {\n // Both the `Parent` and `T` fields are optional. While at least one of\n // them should be provided, bad PDF generators may fail to do so.\n if (!dict.has(\"T\") && !dict.has(\"Parent\")) {\n warn(\"Unknown field name, falling back to empty field name.\");\n return \"\";\n }\n\n // If no parent exists, the partial and fully qualified names are equal.\n if (!dict.has(\"Parent\")) {\n return stringToPDFString(dict.get(\"T\"));\n }\n\n // Form the fully qualified field name by appending the partial name to\n // the parent's fully qualified name, separated by a period.\n const fieldName = [];\n if (dict.has(\"T\")) {\n fieldName.unshift(stringToPDFString(dict.get(\"T\")));\n }\n\n let loopDict = dict;\n const visited = new RefSet();\n if (dict.objId) {\n visited.put(dict.objId);\n }\n while (loopDict.has(\"Parent\")) {\n loopDict = loopDict.get(\"Parent\");\n if (\n !(loopDict instanceof Dict) ||\n (loopDict.objId && visited.has(loopDict.objId))\n ) {\n // Even though it is not allowed according to the PDF specification,\n // bad PDF generators may provide a `Parent` entry that is not a\n // dictionary, but `null` for example (issue 8143).\n //\n // If parent has been already visited, it means that we're\n // in an infinite loop.\n break;\n }\n if (loopDict.objId) {\n visited.put(loopDict.objId);\n }\n\n if (loopDict.has(\"T\")) {\n fieldName.unshift(stringToPDFString(loopDict.get(\"T\")));\n }\n }\n return fieldName.join(\".\");\n }\n}\n\n/**\n * Contains all data regarding an annotation's border style.\n */\nclass AnnotationBorderStyle {\n constructor() {\n this.width = 1;\n this.style = AnnotationBorderStyleType.SOLID;\n this.dashArray = [3];\n this.horizontalCornerRadius = 0;\n this.verticalCornerRadius = 0;\n }\n\n /**\n * Set the width.\n *\n * @public\n * @memberof AnnotationBorderStyle\n * @param {number} width - The width.\n * @param {Array} rect - The annotation `Rect` entry.\n */\n setWidth(width, rect = [0, 0, 0, 0]) {\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(\n isNumberArray(rect, 4),\n \"A valid `rect` parameter must be provided.\"\n );\n }\n\n // Some corrupt PDF generators may provide the width as a `Name`,\n // rather than as a number (fixes issue 10385).\n if (width instanceof Name) {\n this.width = 0; // This is consistent with the behaviour in Adobe Reader.\n return;\n }\n if (typeof width === \"number\") {\n if (width > 0) {\n const maxWidth = (rect[2] - rect[0]) / 2;\n const maxHeight = (rect[3] - rect[1]) / 2;\n\n // Ignore large `width`s, since they lead to the Annotation overflowing\n // the size set by the `Rect` entry thus causing the `annotationLayer`\n // to render it over the surrounding document (fixes bug1552113.pdf).\n if (\n maxWidth > 0 &&\n maxHeight > 0 &&\n (width > maxWidth || width > maxHeight)\n ) {\n warn(`AnnotationBorderStyle.setWidth - ignoring width: ${width}`);\n width = 1;\n }\n }\n this.width = width;\n }\n }\n\n /**\n * Set the style.\n *\n * @public\n * @memberof AnnotationBorderStyle\n * @param {Name} style - The annotation style.\n * @see {@link shared/util.js}\n */\n setStyle(style) {\n if (!(style instanceof Name)) {\n return;\n }\n switch (style.name) {\n case \"S\":\n this.style = AnnotationBorderStyleType.SOLID;\n break;\n\n case \"D\":\n this.style = AnnotationBorderStyleType.DASHED;\n break;\n\n case \"B\":\n this.style = AnnotationBorderStyleType.BEVELED;\n break;\n\n case \"I\":\n this.style = AnnotationBorderStyleType.INSET;\n break;\n\n case \"U\":\n this.style = AnnotationBorderStyleType.UNDERLINE;\n break;\n\n default:\n break;\n }\n }\n\n /**\n * Set the dash array.\n *\n * @public\n * @memberof AnnotationBorderStyle\n * @param {Array} dashArray - The dash array with at least one element\n * @param {boolean} [forceStyle]\n */\n setDashArray(dashArray, forceStyle = false) {\n // We validate the dash array, but we do not use it because CSS does not\n // allow us to change spacing of dashes. For more information, visit\n // http://www.w3.org/TR/css3-background/#the-border-style.\n if (Array.isArray(dashArray)) {\n // The PDF specification states that elements in the dash array, if\n // present, must be non-negative numbers and must not all equal zero.\n let isValid = true;\n let allZeros = true;\n for (const element of dashArray) {\n const validNumber = +element >= 0;\n if (!validNumber) {\n isValid = false;\n break;\n } else if (element > 0) {\n allZeros = false;\n }\n }\n if (dashArray.length === 0 || (isValid && !allZeros)) {\n this.dashArray = dashArray;\n\n if (forceStyle) {\n // Even though we cannot use the dash array in the display layer,\n // at least ensure that we use the correct border-style.\n this.setStyle(Name.get(\"D\"));\n }\n } else {\n this.width = 0; // Adobe behavior when the array is invalid.\n }\n } else if (dashArray) {\n this.width = 0; // Adobe behavior when the array is invalid.\n }\n }\n\n /**\n * Set the horizontal corner radius (from a Border dictionary).\n *\n * @public\n * @memberof AnnotationBorderStyle\n * @param {number} radius - The horizontal corner radius.\n */\n setHorizontalCornerRadius(radius) {\n if (Number.isInteger(radius)) {\n this.horizontalCornerRadius = radius;\n }\n }\n\n /**\n * Set the vertical corner radius (from a Border dictionary).\n *\n * @public\n * @memberof AnnotationBorderStyle\n * @param {number} radius - The vertical corner radius.\n */\n setVerticalCornerRadius(radius) {\n if (Number.isInteger(radius)) {\n this.verticalCornerRadius = radius;\n }\n }\n}\n\nclass MarkupAnnotation extends Annotation {\n constructor(params) {\n super(params);\n\n const { dict } = params;\n\n if (dict.has(\"IRT\")) {\n const rawIRT = dict.getRaw(\"IRT\");\n this.data.inReplyTo = rawIRT instanceof Ref ? rawIRT.toString() : null;\n\n const rt = dict.get(\"RT\");\n this.data.replyType =\n rt instanceof Name ? rt.name : AnnotationReplyType.REPLY;\n }\n let popupRef = null;\n\n if (this.data.replyType === AnnotationReplyType.GROUP) {\n // Subordinate annotations in a group should inherit\n // the group attributes from the primary annotation.\n const parent = dict.get(\"IRT\");\n\n this.setTitle(parent.get(\"T\"));\n this.data.titleObj = this._title;\n\n this.setContents(parent.get(\"Contents\"));\n this.data.contentsObj = this._contents;\n\n if (!parent.has(\"CreationDate\")) {\n this.data.creationDate = null;\n } else {\n this.setCreationDate(parent.get(\"CreationDate\"));\n this.data.creationDate = this.creationDate;\n }\n\n if (!parent.has(\"M\")) {\n this.data.modificationDate = null;\n } else {\n this.setModificationDate(parent.get(\"M\"));\n this.data.modificationDate = this.modificationDate;\n }\n\n popupRef = parent.getRaw(\"Popup\");\n\n if (!parent.has(\"C\")) {\n // Fall back to the default background color.\n this.data.color = null;\n } else {\n this.setColor(parent.getArray(\"C\"));\n this.data.color = this.color;\n }\n } else {\n this.data.titleObj = this._title;\n\n this.setCreationDate(dict.get(\"CreationDate\"));\n this.data.creationDate = this.creationDate;\n\n popupRef = dict.getRaw(\"Popup\");\n\n if (!dict.has(\"C\")) {\n // Fall back to the default background color.\n this.data.color = null;\n }\n }\n\n this.data.popupRef = popupRef instanceof Ref ? popupRef.toString() : null;\n\n if (dict.has(\"RC\")) {\n this.data.richText = XFAFactory.getRichTextAsHtml(dict.get(\"RC\"));\n }\n }\n\n /**\n * Set the creation date.\n *\n * @public\n * @memberof MarkupAnnotation\n * @param {string} creationDate - PDF date string that indicates when the\n * annotation was originally created\n */\n setCreationDate(creationDate) {\n this.creationDate = typeof creationDate === \"string\" ? creationDate : null;\n }\n\n _setDefaultAppearance({\n xref,\n extra,\n strokeColor,\n fillColor,\n blendMode,\n strokeAlpha,\n fillAlpha,\n pointsCallback,\n }) {\n let minX = Number.MAX_VALUE;\n let minY = Number.MAX_VALUE;\n let maxX = Number.MIN_VALUE;\n let maxY = Number.MIN_VALUE;\n\n const buffer = [\"q\"];\n if (extra) {\n buffer.push(extra);\n }\n if (strokeColor) {\n buffer.push(`${strokeColor[0]} ${strokeColor[1]} ${strokeColor[2]} RG`);\n }\n if (fillColor) {\n buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`);\n }\n\n let pointsArray = this.data.quadPoints;\n if (!pointsArray) {\n // If there are no quadpoints, the rectangle should be used instead.\n // Convert the rectangle definition to a points array similar to how the\n // quadpoints are defined.\n pointsArray = [\n [\n { x: this.rectangle[0], y: this.rectangle[3] },\n { x: this.rectangle[2], y: this.rectangle[3] },\n { x: this.rectangle[0], y: this.rectangle[1] },\n { x: this.rectangle[2], y: this.rectangle[1] },\n ],\n ];\n }\n\n for (const points of pointsArray) {\n const [mX, MX, mY, MY] = pointsCallback(buffer, points);\n minX = Math.min(minX, mX);\n maxX = Math.max(maxX, MX);\n minY = Math.min(minY, mY);\n maxY = Math.max(maxY, MY);\n }\n buffer.push(\"Q\");\n\n const formDict = new Dict(xref);\n const appearanceStreamDict = new Dict(xref);\n appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n\n const appearanceStream = new StringStream(buffer.join(\" \"));\n appearanceStream.dict = appearanceStreamDict;\n formDict.set(\"Fm0\", appearanceStream);\n\n const gsDict = new Dict(xref);\n if (blendMode) {\n gsDict.set(\"BM\", Name.get(blendMode));\n }\n if (typeof strokeAlpha === \"number\") {\n gsDict.set(\"CA\", strokeAlpha);\n }\n if (typeof fillAlpha === \"number\") {\n gsDict.set(\"ca\", fillAlpha);\n }\n\n const stateDict = new Dict(xref);\n stateDict.set(\"GS0\", gsDict);\n\n const resources = new Dict(xref);\n resources.set(\"ExtGState\", stateDict);\n resources.set(\"XObject\", formDict);\n\n const appearanceDict = new Dict(xref);\n appearanceDict.set(\"Resources\", resources);\n const bbox = (this.data.rect = [minX, minY, maxX, maxY]);\n appearanceDict.set(\"BBox\", bbox);\n\n this.appearance = new StringStream(\"/GS0 gs /Fm0 Do\");\n this.appearance.dict = appearanceDict;\n\n // This method is only called if there is no appearance for the annotation,\n // so `this.appearance` is not pushed yet in the `Annotation` constructor.\n this._streams.push(this.appearance, appearanceStream);\n }\n\n static async createNewAnnotation(xref, annotation, dependencies, params) {\n const annotationRef = (annotation.ref ||= xref.getNewTemporaryRef());\n const ap = await this.createNewAppearanceStream(annotation, xref, params);\n const buffer = [];\n let annotationDict;\n\n if (ap) {\n const apRef = xref.getNewTemporaryRef();\n annotationDict = this.createNewDict(annotation, xref, { apRef });\n await writeObject(apRef, ap, buffer, xref);\n dependencies.push({ ref: apRef, data: buffer.join(\"\") });\n } else {\n annotationDict = this.createNewDict(annotation, xref, {});\n }\n if (Number.isInteger(annotation.parentTreeId)) {\n annotationDict.set(\"StructParent\", annotation.parentTreeId);\n }\n\n buffer.length = 0;\n await writeObject(annotationRef, annotationDict, buffer, xref);\n\n return { ref: annotationRef, data: buffer.join(\"\") };\n }\n\n static async createNewPrintAnnotation(\n annotationGlobals,\n xref,\n annotation,\n params\n ) {\n const ap = await this.createNewAppearanceStream(annotation, xref, params);\n const annotationDict = this.createNewDict(annotation, xref, { ap });\n\n const newAnnotation = new this.prototype.constructor({\n dict: annotationDict,\n xref,\n annotationGlobals,\n evaluatorOptions: params.evaluatorOptions,\n });\n\n if (annotation.ref) {\n newAnnotation.ref = newAnnotation.refToReplace = annotation.ref;\n }\n\n return newAnnotation;\n }\n}\n\nclass WidgetAnnotation extends Annotation {\n constructor(params) {\n super(params);\n\n const { dict, xref, annotationGlobals } = params;\n const data = this.data;\n this._needAppearances = params.needAppearances;\n\n data.annotationType = AnnotationType.WIDGET;\n if (data.fieldName === undefined) {\n data.fieldName = this._constructFieldName(dict);\n }\n\n if (data.actions === undefined) {\n data.actions = collectActions(xref, dict, AnnotationActionEventType);\n }\n\n let fieldValue = getInheritableProperty({\n dict,\n key: \"V\",\n getArray: true,\n });\n data.fieldValue = this._decodeFormValue(fieldValue);\n\n const defaultFieldValue = getInheritableProperty({\n dict,\n key: \"DV\",\n getArray: true,\n });\n data.defaultFieldValue = this._decodeFormValue(defaultFieldValue);\n\n if (fieldValue === undefined && annotationGlobals.xfaDatasets) {\n // Try to figure out if we have something in the xfa dataset.\n const path = this._title.str;\n if (path) {\n this._hasValueFromXFA = true;\n data.fieldValue = fieldValue =\n annotationGlobals.xfaDatasets.getValue(path);\n }\n }\n\n // When no \"V\" entry exists, let the fieldValue fallback to the \"DV\" entry\n // (fixes issue13823.pdf).\n if (fieldValue === undefined && data.defaultFieldValue !== null) {\n data.fieldValue = data.defaultFieldValue;\n }\n\n data.alternativeText = stringToPDFString(dict.get(\"TU\") || \"\");\n\n this.setDefaultAppearance(params);\n\n data.hasAppearance ||=\n this._needAppearances &&\n data.fieldValue !== undefined &&\n data.fieldValue !== null;\n\n const fieldType = getInheritableProperty({ dict, key: \"FT\" });\n data.fieldType = fieldType instanceof Name ? fieldType.name : null;\n\n const localResources = getInheritableProperty({ dict, key: \"DR\" });\n const acroFormResources = annotationGlobals.acroForm.get(\"DR\");\n const appearanceResources = this.appearance?.dict.get(\"Resources\");\n\n this._fieldResources = {\n localResources,\n acroFormResources,\n appearanceResources,\n mergedResources: Dict.merge({\n xref,\n dictArray: [localResources, appearanceResources, acroFormResources],\n mergeSubDicts: true,\n }),\n };\n\n data.fieldFlags = getInheritableProperty({ dict, key: \"Ff\" });\n if (!Number.isInteger(data.fieldFlags) || data.fieldFlags < 0) {\n data.fieldFlags = 0;\n }\n\n data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);\n data.required = this.hasFieldFlag(AnnotationFieldFlag.REQUIRED);\n data.hidden =\n this._hasFlag(data.annotationFlags, AnnotationFlag.HIDDEN) ||\n this._hasFlag(data.annotationFlags, AnnotationFlag.NOVIEW);\n }\n\n /**\n * Decode the given form value.\n *\n * @private\n * @memberof WidgetAnnotation\n * @param {Array|Name|string} formValue - The (possibly encoded)\n * form value.\n * @returns {Array|string|null}\n */\n _decodeFormValue(formValue) {\n if (Array.isArray(formValue)) {\n return formValue\n .filter(item => typeof item === \"string\")\n .map(item => stringToPDFString(item));\n } else if (formValue instanceof Name) {\n return stringToPDFString(formValue.name);\n } else if (typeof formValue === \"string\") {\n return stringToPDFString(formValue);\n }\n return null;\n }\n\n /**\n * Check if a provided field flag is set.\n *\n * @public\n * @memberof WidgetAnnotation\n * @param {number} flag - Hexadecimal representation for an annotation\n * field characteristic\n * @returns {boolean}\n * @see {@link shared/util.js}\n */\n hasFieldFlag(flag) {\n return !!(this.data.fieldFlags & flag);\n }\n\n /** @inheritdoc */\n _isViewable(flags) {\n // We don't take into account the `NOVIEW` or `HIDDEN` flags here,\n // since the visibility can be changed by js code, hence in case\n // it's made viewable, we should render it (with visibility set to\n // hidden).\n // We don't take into account the `INVISIBLE` flag here, since we've a known\n // annotation type.\n return true;\n }\n\n /** @inheritdoc */\n mustBeViewed(annotationStorage, renderForms) {\n if (renderForms) {\n return this.viewable;\n }\n return (\n super.mustBeViewed(annotationStorage, renderForms) &&\n !this._hasFlag(this.flags, AnnotationFlag.NOVIEW)\n );\n }\n\n getRotationMatrix(annotationStorage) {\n let rotation = annotationStorage?.get(this.data.id)?.rotation;\n if (rotation === undefined) {\n rotation = this.rotation;\n }\n\n if (rotation === 0) {\n return IDENTITY_MATRIX;\n }\n\n const width = this.data.rect[2] - this.data.rect[0];\n const height = this.data.rect[3] - this.data.rect[1];\n\n return getRotationMatrix(rotation, width, height);\n }\n\n getBorderAndBackgroundAppearances(annotationStorage) {\n let rotation = annotationStorage?.get(this.data.id)?.rotation;\n if (rotation === undefined) {\n rotation = this.rotation;\n }\n\n if (!this.backgroundColor && !this.borderColor) {\n return \"\";\n }\n const width = this.data.rect[2] - this.data.rect[0];\n const height = this.data.rect[3] - this.data.rect[1];\n const rect =\n rotation === 0 || rotation === 180\n ? `0 0 ${width} ${height} re`\n : `0 0 ${height} ${width} re`;\n\n let str = \"\";\n if (this.backgroundColor) {\n str = `${getPdfColor(\n this.backgroundColor,\n /* isFill */ true\n )} ${rect} f `;\n }\n\n if (this.borderColor) {\n const borderWidth = this.borderStyle.width || 1;\n str += `${borderWidth} w ${getPdfColor(\n this.borderColor,\n /* isFill */ false\n )} ${rect} S `;\n }\n\n return str;\n }\n\n async getOperatorList(\n evaluator,\n task,\n intent,\n renderForms,\n annotationStorage\n ) {\n // Do not render form elements on the canvas when interactive forms are\n // enabled. The display layer is responsible for rendering them instead.\n if (\n renderForms &&\n !(this instanceof SignatureWidgetAnnotation) &&\n !this.data.noHTML &&\n !this.data.hasOwnCanvas\n ) {\n return {\n opList: new OperatorList(),\n separateForm: true,\n separateCanvas: false,\n };\n }\n\n if (!this._hasText) {\n return super.getOperatorList(\n evaluator,\n task,\n intent,\n renderForms,\n annotationStorage\n );\n }\n\n const content = await this._getAppearance(\n evaluator,\n task,\n intent,\n annotationStorage\n );\n if (this.appearance && content === null) {\n return super.getOperatorList(\n evaluator,\n task,\n intent,\n renderForms,\n annotationStorage\n );\n }\n\n const opList = new OperatorList();\n\n // Even if there is an appearance stream, ignore it. This is the\n // behaviour used by Adobe Reader.\n if (!this._defaultAppearance || content === null) {\n return { opList, separateForm: false, separateCanvas: false };\n }\n\n const isUsingOwnCanvas = !!(\n this.data.hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY\n );\n\n const matrix = [1, 0, 0, 1, 0, 0];\n const bbox = [\n 0,\n 0,\n this.data.rect[2] - this.data.rect[0],\n this.data.rect[3] - this.data.rect[1],\n ];\n const transform = getTransformMatrix(this.data.rect, bbox, matrix);\n\n let optionalContent;\n if (this.oc) {\n optionalContent = await evaluator.parseMarkedContentProps(\n this.oc,\n /* resources = */ null\n );\n }\n if (optionalContent !== undefined) {\n opList.addOp(OPS.beginMarkedContentProps, [\"OC\", optionalContent]);\n }\n\n opList.addOp(OPS.beginAnnotation, [\n this.data.id,\n this.data.rect,\n transform,\n this.getRotationMatrix(annotationStorage),\n isUsingOwnCanvas,\n ]);\n\n const stream = new StringStream(content);\n await evaluator.getOperatorList({\n stream,\n task,\n resources: this._fieldResources.mergedResources,\n operatorList: opList,\n });\n opList.addOp(OPS.endAnnotation, []);\n\n if (optionalContent !== undefined) {\n opList.addOp(OPS.endMarkedContent, []);\n }\n return { opList, separateForm: false, separateCanvas: isUsingOwnCanvas };\n }\n\n _getMKDict(rotation) {\n const mk = new Dict(null);\n if (rotation) {\n mk.set(\"R\", rotation);\n }\n if (this.borderColor) {\n mk.set(\"BC\", getPdfColorArray(this.borderColor));\n }\n if (this.backgroundColor) {\n mk.set(\"BG\", getPdfColorArray(this.backgroundColor));\n }\n return mk.size > 0 ? mk : null;\n }\n\n amendSavedDict(annotationStorage, dict) {}\n\n async save(evaluator, task, annotationStorage) {\n const storageEntry = annotationStorage?.get(this.data.id);\n let value = storageEntry?.value,\n rotation = storageEntry?.rotation;\n if (value === this.data.fieldValue || value === undefined) {\n if (!this._hasValueFromXFA && rotation === undefined) {\n return null;\n }\n value ||= this.data.fieldValue;\n }\n\n // Value can be an array (with choice list and multiple selections)\n if (\n rotation === undefined &&\n !this._hasValueFromXFA &&\n Array.isArray(value) &&\n Array.isArray(this.data.fieldValue) &&\n value.length === this.data.fieldValue.length &&\n value.every((x, i) => x === this.data.fieldValue[i])\n ) {\n return null;\n }\n\n if (rotation === undefined) {\n rotation = this.rotation;\n }\n\n let appearance = null;\n if (!this._needAppearances) {\n appearance = await this._getAppearance(\n evaluator,\n task,\n RenderingIntentFlag.SAVE,\n annotationStorage\n );\n if (appearance === null) {\n // Appearance didn't change.\n return null;\n }\n } else {\n // No need to create an appearance: the pdf has the flag /NeedAppearances\n // which means that it's up to the reader to produce an appearance.\n }\n\n let needAppearances = false;\n if (appearance?.needAppearances) {\n needAppearances = true;\n appearance = null;\n }\n\n const { xref } = evaluator;\n\n const originalDict = xref.fetchIfRef(this.ref);\n if (!(originalDict instanceof Dict)) {\n return null;\n }\n\n const dict = new Dict(xref);\n for (const key of originalDict.getKeys()) {\n if (key !== \"AP\") {\n dict.set(key, originalDict.getRaw(key));\n }\n }\n\n const xfa = {\n path: this.data.fieldName,\n value,\n };\n\n const encoder = val =>\n isAscii(val) ? val : stringToUTF16String(val, /* bigEndian = */ true);\n dict.set(\"V\", Array.isArray(value) ? value.map(encoder) : encoder(value));\n this.amendSavedDict(annotationStorage, dict);\n\n const maybeMK = this._getMKDict(rotation);\n if (maybeMK) {\n dict.set(\"MK\", maybeMK);\n }\n\n const buffer = [];\n const changes = [\n // data for the original object\n // V field changed + reference for new AP\n { ref: this.ref, data: \"\", xfa, needAppearances },\n ];\n if (appearance !== null) {\n const newRef = xref.getNewTemporaryRef();\n const AP = new Dict(xref);\n dict.set(\"AP\", AP);\n AP.set(\"N\", newRef);\n\n const resources = this._getSaveFieldResources(xref);\n const appearanceStream = new StringStream(appearance);\n const appearanceDict = (appearanceStream.dict = new Dict(xref));\n appearanceDict.set(\"Subtype\", Name.get(\"Form\"));\n appearanceDict.set(\"Resources\", resources);\n appearanceDict.set(\"BBox\", [\n 0,\n 0,\n this.data.rect[2] - this.data.rect[0],\n this.data.rect[3] - this.data.rect[1],\n ]);\n\n const rotationMatrix = this.getRotationMatrix(annotationStorage);\n if (rotationMatrix !== IDENTITY_MATRIX) {\n // The matrix isn't the identity one.\n appearanceDict.set(\"Matrix\", rotationMatrix);\n }\n\n await writeObject(newRef, appearanceStream, buffer, xref);\n\n changes.push(\n // data for the new AP\n {\n ref: newRef,\n data: buffer.join(\"\"),\n xfa: null,\n needAppearances: false,\n }\n );\n buffer.length = 0;\n }\n\n dict.set(\"M\", `D:${getModificationDate()}`);\n await writeObject(this.ref, dict, buffer, xref);\n\n changes[0].data = buffer.join(\"\");\n\n return changes;\n }\n\n async _getAppearance(evaluator, task, intent, annotationStorage) {\n const isPassword = this.hasFieldFlag(AnnotationFieldFlag.PASSWORD);\n if (isPassword) {\n return null;\n }\n const storageEntry = annotationStorage?.get(this.data.id);\n let value, rotation;\n if (storageEntry) {\n value = storageEntry.formattedValue || storageEntry.value;\n rotation = storageEntry.rotation;\n }\n\n if (\n rotation === undefined &&\n value === undefined &&\n !this._needAppearances\n ) {\n if (!this._hasValueFromXFA || this.appearance) {\n // The annotation hasn't been rendered so use the appearance.\n return null;\n }\n }\n\n // Empty or it has a trailing whitespace.\n const colors = this.getBorderAndBackgroundAppearances(annotationStorage);\n\n if (value === undefined) {\n // The annotation has its value in XFA datasets but not in the V field.\n value = this.data.fieldValue;\n if (!value) {\n return `/Tx BMC q ${colors}Q EMC`;\n }\n }\n\n if (Array.isArray(value) && value.length === 1) {\n value = value[0];\n }\n\n assert(typeof value === \"string\", \"Expected `value` to be a string.\");\n value = value.trim();\n\n if (this.data.combo) {\n // The value can be one of the exportValue or any other values.\n const option = this.data.options.find(\n ({ exportValue }) => value === exportValue\n );\n value = option?.displayValue || value;\n }\n\n if (value === \"\") {\n // the field is empty: nothing to render\n return `/Tx BMC q ${colors}Q EMC`;\n }\n\n if (rotation === undefined) {\n rotation = this.rotation;\n }\n\n let lineCount = -1;\n let lines;\n\n // We could have a text containing for example some sequences of chars and\n // their diacritics (e.g. \"é\".normalize(\"NFKD\") shows 1 char when it's 2).\n // Positioning diacritics is really something we don't want to do here.\n // So if a font has a glyph for a acute accent and one for \"e\" then we won't\n // get any encoding issues but we'll render \"e\" and then \"´\".\n // It's why we normalize the string. We use NFC to preserve the initial\n // string, (e.g. \"²\".normalize(\"NFC\") === \"²\"\n // but \"²\".normalize(\"NFKC\") === \"2\").\n //\n // TODO: it isn't a perfect solution, some chars like \"ẹ́\" will be\n // decomposed into two chars (\"ẹ\" and \"´\"), so we should detect such\n // situations and then use either FakeUnicodeFont or set the\n // /NeedAppearances flag.\n if (this.data.multiLine) {\n lines = value.split(/\\r\\n?|\\n/).map(line => line.normalize(\"NFC\"));\n lineCount = lines.length;\n } else {\n lines = [value.replace(/\\r\\n?|\\n/, \"\").normalize(\"NFC\")];\n }\n\n const defaultPadding = 1;\n const defaultHPadding = 2;\n let totalHeight = this.data.rect[3] - this.data.rect[1];\n let totalWidth = this.data.rect[2] - this.data.rect[0];\n\n if (rotation === 90 || rotation === 270) {\n [totalWidth, totalHeight] = [totalHeight, totalWidth];\n }\n\n if (!this._defaultAppearance) {\n // The DA is required and must be a string.\n // If there is no font named Helvetica in the resource dictionary,\n // the evaluator will fall back to a default font.\n // Doing so prevents exceptions and allows saving/printing\n // the file as expected.\n this.data.defaultAppearanceData = parseDefaultAppearance(\n (this._defaultAppearance = \"/Helvetica 0 Tf 0 g\")\n );\n }\n\n let font = await WidgetAnnotation._getFontData(\n evaluator,\n task,\n this.data.defaultAppearanceData,\n this._fieldResources.mergedResources\n );\n\n let defaultAppearance, fontSize, lineHeight;\n const encodedLines = [];\n let encodingError = false;\n for (const line of lines) {\n const encodedString = font.encodeString(line);\n if (encodedString.length > 1) {\n encodingError = true;\n }\n encodedLines.push(encodedString.join(\"\"));\n }\n\n if (encodingError && intent & RenderingIntentFlag.SAVE) {\n // We don't have a way to render the field, so we just rely on the\n // /NeedAppearances trick to let the different sofware correctly render\n // this pdf.\n return { needAppearances: true };\n }\n\n // We check that the font is able to encode the string.\n if (encodingError && this._isOffscreenCanvasSupported) {\n // If it can't then we fallback on fake unicode font (mapped to sans-serif\n // for the rendering).\n // It means that a printed form can be rendered differently (it depends on\n // the sans-serif font) but at least we've something to render.\n // In an ideal world the associated font should correctly handle the\n // possible chars but a user can add a smiley or whatever.\n // We could try to embed a font but it means that we must have access\n // to the raw font file.\n const fontFamily = this.data.comb ? \"monospace\" : \"sans-serif\";\n const fakeUnicodeFont = new FakeUnicodeFont(evaluator.xref, fontFamily);\n const resources = fakeUnicodeFont.createFontResources(lines.join(\"\"));\n const newFont = resources.getRaw(\"Font\");\n\n if (this._fieldResources.mergedResources.has(\"Font\")) {\n const oldFont = this._fieldResources.mergedResources.get(\"Font\");\n for (const key of newFont.getKeys()) {\n oldFont.set(key, newFont.getRaw(key));\n }\n } else {\n this._fieldResources.mergedResources.set(\"Font\", newFont);\n }\n\n const fontName = fakeUnicodeFont.fontName.name;\n font = await WidgetAnnotation._getFontData(\n evaluator,\n task,\n { fontName, fontSize: 0 },\n resources\n );\n\n for (let i = 0, ii = encodedLines.length; i < ii; i++) {\n encodedLines[i] = stringToUTF16String(lines[i]);\n }\n\n const savedDefaultAppearance = Object.assign(\n Object.create(null),\n this.data.defaultAppearanceData\n );\n this.data.defaultAppearanceData.fontSize = 0;\n this.data.defaultAppearanceData.fontName = fontName;\n\n [defaultAppearance, fontSize, lineHeight] = this._computeFontSize(\n totalHeight - 2 * defaultPadding,\n totalWidth - 2 * defaultHPadding,\n value,\n font,\n lineCount\n );\n\n this.data.defaultAppearanceData = savedDefaultAppearance;\n } else {\n if (!this._isOffscreenCanvasSupported) {\n warn(\n \"_getAppearance: OffscreenCanvas is not supported, annotation may not render correctly.\"\n );\n }\n\n [defaultAppearance, fontSize, lineHeight] = this._computeFontSize(\n totalHeight - 2 * defaultPadding,\n totalWidth - 2 * defaultHPadding,\n value,\n font,\n lineCount\n );\n }\n\n let descent = font.descent;\n if (isNaN(descent)) {\n descent = BASELINE_FACTOR * lineHeight;\n } else {\n descent = Math.max(\n BASELINE_FACTOR * lineHeight,\n Math.abs(descent) * fontSize\n );\n }\n\n // Take into account the space we have to compute the default vertical\n // padding.\n const defaultVPadding = Math.min(\n Math.floor((totalHeight - fontSize) / 2),\n defaultPadding\n );\n const alignment = this.data.textAlignment;\n\n if (this.data.multiLine) {\n return this._getMultilineAppearance(\n defaultAppearance,\n encodedLines,\n font,\n fontSize,\n totalWidth,\n totalHeight,\n alignment,\n defaultHPadding,\n defaultVPadding,\n descent,\n lineHeight,\n annotationStorage\n );\n }\n\n if (this.data.comb) {\n return this._getCombAppearance(\n defaultAppearance,\n font,\n encodedLines[0],\n fontSize,\n totalWidth,\n totalHeight,\n defaultHPadding,\n defaultVPadding,\n descent,\n lineHeight,\n annotationStorage\n );\n }\n\n const bottomPadding = defaultVPadding + descent;\n if (alignment === 0 || alignment > 2) {\n // Left alignment: nothing to do\n return (\n `/Tx BMC q ${colors}BT ` +\n defaultAppearance +\n ` 1 0 0 1 ${numberToString(defaultHPadding)} ${numberToString(\n bottomPadding\n )} Tm (${escapeString(encodedLines[0])}) Tj` +\n \" ET Q EMC\"\n );\n }\n\n const prevInfo = { shift: 0 };\n const renderedText = this._renderText(\n encodedLines[0],\n font,\n fontSize,\n totalWidth,\n alignment,\n prevInfo,\n defaultHPadding,\n bottomPadding\n );\n return (\n `/Tx BMC q ${colors}BT ` +\n defaultAppearance +\n ` 1 0 0 1 0 0 Tm ${renderedText}` +\n \" ET Q EMC\"\n );\n }\n\n static async _getFontData(evaluator, task, appearanceData, resources) {\n const operatorList = new OperatorList();\n const initialState = {\n font: null,\n clone() {\n return this;\n },\n };\n\n const { fontName, fontSize } = appearanceData;\n await evaluator.handleSetFont(\n resources,\n [fontName && Name.get(fontName), fontSize],\n /* fontRef = */ null,\n operatorList,\n task,\n initialState,\n /* fallbackFontDict = */ null\n );\n\n return initialState.font;\n }\n\n _getTextWidth(text, font) {\n return (\n font\n .charsToGlyphs(text)\n .reduce((width, glyph) => width + glyph.width, 0) / 1000\n );\n }\n\n _computeFontSize(height, width, text, font, lineCount) {\n let { fontSize } = this.data.defaultAppearanceData;\n let lineHeight = (fontSize || 12) * LINE_FACTOR,\n numberOfLines = Math.round(height / lineHeight);\n\n if (!fontSize) {\n // A zero value for size means that the font shall be auto-sized:\n // its size shall be computed as a function of the height of the\n // annotation rectangle (see 12.7.3.3).\n\n const roundWithTwoDigits = x => Math.floor(x * 100) / 100;\n\n if (lineCount === -1) {\n const textWidth = this._getTextWidth(text, font);\n fontSize = roundWithTwoDigits(\n Math.min(\n height / LINE_FACTOR,\n textWidth > width ? width / textWidth : Infinity\n )\n );\n numberOfLines = 1;\n } else {\n const lines = text.split(/\\r\\n?|\\n/);\n const cachedLines = [];\n for (const line of lines) {\n const encoded = font.encodeString(line).join(\"\");\n const glyphs = font.charsToGlyphs(encoded);\n const positions = font.getCharPositions(encoded);\n cachedLines.push({\n line: encoded,\n glyphs,\n positions,\n });\n }\n\n const isTooBig = fsize => {\n // Return true when the text doesn't fit the given height.\n let totalHeight = 0;\n for (const cache of cachedLines) {\n const chunks = this._splitLine(null, font, fsize, width, cache);\n totalHeight += chunks.length * fsize;\n if (totalHeight > height) {\n return true;\n }\n }\n return false;\n };\n\n // Hard to guess how many lines there are.\n // The field may have been sized to have 10 lines\n // and the user entered only 1 so if we get font size from\n // height and number of lines then we'll get something too big.\n // So we compute a fake number of lines based on height and\n // a font size equal to 12 (this is the default font size in\n // Acrobat).\n // Then we'll adjust font size to what we have really.\n numberOfLines = Math.max(numberOfLines, lineCount);\n\n while (true) {\n lineHeight = height / numberOfLines;\n fontSize = roundWithTwoDigits(lineHeight / LINE_FACTOR);\n\n if (isTooBig(fontSize)) {\n numberOfLines++;\n continue;\n }\n\n break;\n }\n }\n\n const { fontName, fontColor } = this.data.defaultAppearanceData;\n this._defaultAppearance = createDefaultAppearance({\n fontSize,\n fontName,\n fontColor,\n });\n }\n\n return [this._defaultAppearance, fontSize, height / numberOfLines];\n }\n\n _renderText(\n text,\n font,\n fontSize,\n totalWidth,\n alignment,\n prevInfo,\n hPadding,\n vPadding\n ) {\n // TODO: we need to take into account (if possible) how the text\n // is rendered. For example in arabic, the cumulated width of some\n // glyphs isn't equal to the width of the rendered glyphs because\n // of ligatures.\n let shift;\n if (alignment === 1) {\n // Center\n const width = this._getTextWidth(text, font) * fontSize;\n shift = (totalWidth - width) / 2;\n } else if (alignment === 2) {\n // Right\n const width = this._getTextWidth(text, font) * fontSize;\n shift = totalWidth - width - hPadding;\n } else {\n shift = hPadding;\n }\n const shiftStr = numberToString(shift - prevInfo.shift);\n prevInfo.shift = shift;\n vPadding = numberToString(vPadding);\n\n return `${shiftStr} ${vPadding} Td (${escapeString(text)}) Tj`;\n }\n\n /**\n * @private\n */\n _getSaveFieldResources(xref) {\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(\n this.data.defaultAppearanceData,\n \"Expected `_defaultAppearanceData` to have been set.\"\n );\n }\n const { localResources, appearanceResources, acroFormResources } =\n this._fieldResources;\n\n const fontName = this.data.defaultAppearanceData?.fontName;\n if (!fontName) {\n return localResources || Dict.empty;\n }\n\n for (const resources of [localResources, appearanceResources]) {\n if (resources instanceof Dict) {\n const localFont = resources.get(\"Font\");\n if (localFont instanceof Dict && localFont.has(fontName)) {\n return resources;\n }\n }\n }\n if (acroFormResources instanceof Dict) {\n const acroFormFont = acroFormResources.get(\"Font\");\n if (acroFormFont instanceof Dict && acroFormFont.has(fontName)) {\n const subFontDict = new Dict(xref);\n subFontDict.set(fontName, acroFormFont.getRaw(fontName));\n\n const subResourcesDict = new Dict(xref);\n subResourcesDict.set(\"Font\", subFontDict);\n\n return Dict.merge({\n xref,\n dictArray: [subResourcesDict, localResources],\n mergeSubDicts: true,\n });\n }\n }\n return localResources || Dict.empty;\n }\n\n getFieldObject() {\n return null;\n }\n}\n\nclass TextWidgetAnnotation extends WidgetAnnotation {\n constructor(params) {\n super(params);\n\n this.data.hasOwnCanvas = this.data.readOnly && !this.data.noHTML;\n this._hasText = true;\n\n const dict = params.dict;\n\n // The field value is always a string.\n if (typeof this.data.fieldValue !== \"string\") {\n this.data.fieldValue = \"\";\n }\n\n // Determine the alignment of text in the field.\n let alignment = getInheritableProperty({ dict, key: \"Q\" });\n if (!Number.isInteger(alignment) || alignment < 0 || alignment > 2) {\n alignment = null;\n }\n this.data.textAlignment = alignment;\n\n // Determine the maximum length of text in the field.\n let maximumLength = getInheritableProperty({ dict, key: \"MaxLen\" });\n if (!Number.isInteger(maximumLength) || maximumLength < 0) {\n maximumLength = 0;\n }\n this.data.maxLen = maximumLength;\n\n // Process field flags for the display layer.\n this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE);\n this.data.comb =\n this.hasFieldFlag(AnnotationFieldFlag.COMB) &&\n !this.hasFieldFlag(AnnotationFieldFlag.MULTILINE) &&\n !this.hasFieldFlag(AnnotationFieldFlag.PASSWORD) &&\n !this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) &&\n this.data.maxLen !== 0;\n this.data.doNotScroll = this.hasFieldFlag(AnnotationFieldFlag.DONOTSCROLL);\n }\n\n get hasTextContent() {\n return !!this.appearance && !this._needAppearances;\n }\n\n _getCombAppearance(\n defaultAppearance,\n font,\n text,\n fontSize,\n width,\n height,\n hPadding,\n vPadding,\n descent,\n lineHeight,\n annotationStorage\n ) {\n const combWidth = width / this.data.maxLen;\n // Empty or it has a trailing whitespace.\n const colors = this.getBorderAndBackgroundAppearances(annotationStorage);\n\n const buf = [];\n const positions = font.getCharPositions(text);\n for (const [start, end] of positions) {\n buf.push(`(${escapeString(text.substring(start, end))}) Tj`);\n }\n\n const renderedComb = buf.join(` ${numberToString(combWidth)} 0 Td `);\n return (\n `/Tx BMC q ${colors}BT ` +\n defaultAppearance +\n ` 1 0 0 1 ${numberToString(hPadding)} ${numberToString(\n vPadding + descent\n )} Tm ${renderedComb}` +\n \" ET Q EMC\"\n );\n }\n\n _getMultilineAppearance(\n defaultAppearance,\n lines,\n font,\n fontSize,\n width,\n height,\n alignment,\n hPadding,\n vPadding,\n descent,\n lineHeight,\n annotationStorage\n ) {\n const buf = [];\n const totalWidth = width - 2 * hPadding;\n const prevInfo = { shift: 0 };\n for (let i = 0, ii = lines.length; i < ii; i++) {\n const line = lines[i];\n const chunks = this._splitLine(line, font, fontSize, totalWidth);\n for (let j = 0, jj = chunks.length; j < jj; j++) {\n const chunk = chunks[j];\n const vShift =\n i === 0 && j === 0 ? -vPadding - (lineHeight - descent) : -lineHeight;\n buf.push(\n this._renderText(\n chunk,\n font,\n fontSize,\n width,\n alignment,\n prevInfo,\n hPadding,\n vShift\n )\n );\n }\n }\n\n // Empty or it has a trailing whitespace.\n const colors = this.getBorderAndBackgroundAppearances(annotationStorage);\n const renderedText = buf.join(\"\\n\");\n\n return (\n `/Tx BMC q ${colors}BT ` +\n defaultAppearance +\n ` 1 0 0 1 0 ${numberToString(height)} Tm ${renderedText}` +\n \" ET Q EMC\"\n );\n }\n\n _splitLine(line, font, fontSize, width, cache = {}) {\n line = cache.line || line;\n\n const glyphs = cache.glyphs || font.charsToGlyphs(line);\n\n if (glyphs.length <= 1) {\n // Nothing to split\n return [line];\n }\n\n const positions = cache.positions || font.getCharPositions(line);\n const scale = fontSize / 1000;\n const chunks = [];\n\n let lastSpacePosInStringStart = -1,\n lastSpacePosInStringEnd = -1,\n lastSpacePos = -1,\n startChunk = 0,\n currentWidth = 0;\n\n for (let i = 0, ii = glyphs.length; i < ii; i++) {\n const [start, end] = positions[i];\n const glyph = glyphs[i];\n const glyphWidth = glyph.width * scale;\n if (glyph.unicode === \" \") {\n if (currentWidth + glyphWidth > width) {\n // We can break here\n chunks.push(line.substring(startChunk, start));\n startChunk = start;\n currentWidth = glyphWidth;\n lastSpacePosInStringStart = -1;\n lastSpacePos = -1;\n } else {\n currentWidth += glyphWidth;\n lastSpacePosInStringStart = start;\n lastSpacePosInStringEnd = end;\n lastSpacePos = i;\n }\n } else if (currentWidth + glyphWidth > width) {\n // We must break to the last white position (if available)\n if (lastSpacePosInStringStart !== -1) {\n chunks.push(line.substring(startChunk, lastSpacePosInStringEnd));\n startChunk = lastSpacePosInStringEnd;\n i = lastSpacePos + 1;\n lastSpacePosInStringStart = -1;\n currentWidth = 0;\n } else {\n // Just break in the middle of the word\n chunks.push(line.substring(startChunk, start));\n startChunk = start;\n currentWidth = glyphWidth;\n }\n } else {\n currentWidth += glyphWidth;\n }\n }\n\n if (startChunk < line.length) {\n chunks.push(line.substring(startChunk, line.length));\n }\n\n return chunks;\n }\n\n getFieldObject() {\n return {\n id: this.data.id,\n value: this.data.fieldValue,\n defaultValue: this.data.defaultFieldValue || \"\",\n multiline: this.data.multiLine,\n password: this.hasFieldFlag(AnnotationFieldFlag.PASSWORD),\n charLimit: this.data.maxLen,\n comb: this.data.comb,\n editable: !this.data.readOnly,\n hidden: this.data.hidden,\n name: this.data.fieldName,\n rect: this.data.rect,\n actions: this.data.actions,\n page: this.data.pageIndex,\n strokeColor: this.data.borderColor,\n fillColor: this.data.backgroundColor,\n rotation: this.rotation,\n type: \"text\",\n };\n }\n}\n\nclass ButtonWidgetAnnotation extends WidgetAnnotation {\n constructor(params) {\n super(params);\n\n this.checkedAppearance = null;\n this.uncheckedAppearance = null;\n\n this.data.checkBox =\n !this.hasFieldFlag(AnnotationFieldFlag.RADIO) &&\n !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);\n this.data.radioButton =\n this.hasFieldFlag(AnnotationFieldFlag.RADIO) &&\n !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);\n this.data.pushButton = this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON);\n this.data.isTooltipOnly = false;\n\n if (this.data.checkBox) {\n this._processCheckBox(params);\n } else if (this.data.radioButton) {\n this._processRadioButton(params);\n } else if (this.data.pushButton) {\n this.data.hasOwnCanvas = true;\n this.data.noHTML = false;\n this._processPushButton(params);\n } else {\n warn(\"Invalid field flags for button widget annotation\");\n }\n }\n\n async getOperatorList(\n evaluator,\n task,\n intent,\n renderForms,\n annotationStorage\n ) {\n if (this.data.pushButton) {\n return super.getOperatorList(\n evaluator,\n task,\n intent,\n false, // we use normalAppearance to render the button\n annotationStorage\n );\n }\n\n let value = null;\n let rotation = null;\n if (annotationStorage) {\n const storageEntry = annotationStorage.get(this.data.id);\n value = storageEntry ? storageEntry.value : null;\n rotation = storageEntry ? storageEntry.rotation : null;\n }\n\n if (value === null && this.appearance) {\n // Nothing in the annotationStorage.\n // But we've a default appearance so use it.\n return super.getOperatorList(\n evaluator,\n task,\n intent,\n renderForms,\n annotationStorage\n );\n }\n\n if (value === null || value === undefined) {\n // There is no default appearance so use the one derived\n // from the field value.\n value = this.data.checkBox\n ? this.data.fieldValue === this.data.exportValue\n : this.data.fieldValue === this.data.buttonValue;\n }\n\n const appearance = value\n ? this.checkedAppearance\n : this.uncheckedAppearance;\n if (appearance) {\n const savedAppearance = this.appearance;\n const savedMatrix = lookupMatrix(\n appearance.dict.getArray(\"Matrix\"),\n IDENTITY_MATRIX\n );\n\n if (rotation) {\n appearance.dict.set(\n \"Matrix\",\n this.getRotationMatrix(annotationStorage)\n );\n }\n\n this.appearance = appearance;\n const operatorList = super.getOperatorList(\n evaluator,\n task,\n intent,\n renderForms,\n annotationStorage\n );\n this.appearance = savedAppearance;\n appearance.dict.set(\"Matrix\", savedMatrix);\n return operatorList;\n }\n\n // No appearance\n return {\n opList: new OperatorList(),\n separateForm: false,\n separateCanvas: false,\n };\n }\n\n async save(evaluator, task, annotationStorage) {\n if (this.data.checkBox) {\n return this._saveCheckbox(evaluator, task, annotationStorage);\n }\n\n if (this.data.radioButton) {\n return this._saveRadioButton(evaluator, task, annotationStorage);\n }\n\n // Nothing to save\n return null;\n }\n\n async _saveCheckbox(evaluator, task, annotationStorage) {\n if (!annotationStorage) {\n return null;\n }\n const storageEntry = annotationStorage.get(this.data.id);\n let rotation = storageEntry?.rotation,\n value = storageEntry?.value;\n\n if (rotation === undefined) {\n if (value === undefined) {\n return null;\n }\n\n const defaultValue = this.data.fieldValue === this.data.exportValue;\n if (defaultValue === value) {\n return null;\n }\n }\n\n const dict = evaluator.xref.fetchIfRef(this.ref);\n if (!(dict instanceof Dict)) {\n return null;\n }\n\n if (rotation === undefined) {\n rotation = this.rotation;\n }\n if (value === undefined) {\n value = this.data.fieldValue === this.data.exportValue;\n }\n\n const xfa = {\n path: this.data.fieldName,\n value: value ? this.data.exportValue : \"\",\n };\n\n const name = Name.get(value ? this.data.exportValue : \"Off\");\n dict.set(\"V\", name);\n dict.set(\"AS\", name);\n dict.set(\"M\", `D:${getModificationDate()}`);\n\n const maybeMK = this._getMKDict(rotation);\n if (maybeMK) {\n dict.set(\"MK\", maybeMK);\n }\n\n const buffer = [];\n await writeObject(this.ref, dict, buffer, evaluator.xref);\n\n return [{ ref: this.ref, data: buffer.join(\"\"), xfa }];\n }\n\n async _saveRadioButton(evaluator, task, annotationStorage) {\n if (!annotationStorage) {\n return null;\n }\n const storageEntry = annotationStorage.get(this.data.id);\n let rotation = storageEntry?.rotation,\n value = storageEntry?.value;\n\n if (rotation === undefined) {\n if (value === undefined) {\n return null;\n }\n\n const defaultValue = this.data.fieldValue === this.data.buttonValue;\n if (defaultValue === value) {\n return null;\n }\n }\n\n const dict = evaluator.xref.fetchIfRef(this.ref);\n if (!(dict instanceof Dict)) {\n return null;\n }\n\n if (value === undefined) {\n value = this.data.fieldValue === this.data.buttonValue;\n }\n\n if (rotation === undefined) {\n rotation = this.rotation;\n }\n\n const xfa = {\n path: this.data.fieldName,\n value: value ? this.data.buttonValue : \"\",\n };\n\n const name = Name.get(value ? this.data.buttonValue : \"Off\");\n const buffer = [];\n let parentData = null;\n\n if (value) {\n if (this.parent instanceof Ref) {\n const parent = evaluator.xref.fetch(this.parent);\n parent.set(\"V\", name);\n await writeObject(this.parent, parent, buffer, evaluator.xref);\n parentData = buffer.join(\"\");\n buffer.length = 0;\n } else if (this.parent instanceof Dict) {\n this.parent.set(\"V\", name);\n }\n }\n\n dict.set(\"AS\", name);\n dict.set(\"M\", `D:${getModificationDate()}`);\n\n const maybeMK = this._getMKDict(rotation);\n if (maybeMK) {\n dict.set(\"MK\", maybeMK);\n }\n\n await writeObject(this.ref, dict, buffer, evaluator.xref);\n const newRefs = [{ ref: this.ref, data: buffer.join(\"\"), xfa }];\n if (parentData) {\n newRefs.push({ ref: this.parent, data: parentData, xfa: null });\n }\n\n return newRefs;\n }\n\n _getDefaultCheckedAppearance(params, type) {\n const width = this.data.rect[2] - this.data.rect[0];\n const height = this.data.rect[3] - this.data.rect[1];\n const bbox = [0, 0, width, height];\n\n // Ratio used to have a mark slightly smaller than the bbox.\n const FONT_RATIO = 0.8;\n const fontSize = Math.min(width, height) * FONT_RATIO;\n\n // Char Metrics\n // Widths came from widths for ZapfDingbats.\n // Heights are guessed with Fontforge and FoxitDingbats.pfb.\n let metrics, char;\n if (type === \"check\") {\n // Char 33 (2713 in unicode)\n metrics = {\n width: 0.755 * fontSize,\n height: 0.705 * fontSize,\n };\n char = \"\\x33\";\n } else if (type === \"disc\") {\n // Char 6C (25CF in unicode)\n metrics = {\n width: 0.791 * fontSize,\n height: 0.705 * fontSize,\n };\n char = \"\\x6C\";\n } else {\n unreachable(`_getDefaultCheckedAppearance - unsupported type: ${type}`);\n }\n\n // Values to center the glyph in the bbox.\n const xShift = numberToString((width - metrics.width) / 2);\n const yShift = numberToString((height - metrics.height) / 2);\n\n const appearance = `q BT /PdfJsZaDb ${fontSize} Tf 0 g ${xShift} ${yShift} Td (${char}) Tj ET Q`;\n\n const appearanceStreamDict = new Dict(params.xref);\n appearanceStreamDict.set(\"FormType\", 1);\n appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n appearanceStreamDict.set(\"BBox\", bbox);\n appearanceStreamDict.set(\"Matrix\", [1, 0, 0, 1, 0, 0]);\n appearanceStreamDict.set(\"Length\", appearance.length);\n\n const resources = new Dict(params.xref);\n const font = new Dict(params.xref);\n font.set(\"PdfJsZaDb\", this.fallbackFontDict);\n resources.set(\"Font\", font);\n\n appearanceStreamDict.set(\"Resources\", resources);\n\n this.checkedAppearance = new StringStream(appearance);\n this.checkedAppearance.dict = appearanceStreamDict;\n\n this._streams.push(this.checkedAppearance);\n }\n\n _processCheckBox(params) {\n const customAppearance = params.dict.get(\"AP\");\n if (!(customAppearance instanceof Dict)) {\n return;\n }\n\n const normalAppearance = customAppearance.get(\"N\");\n if (!(normalAppearance instanceof Dict)) {\n return;\n }\n\n // See https://bugzilla.mozilla.org/show_bug.cgi?id=1722036.\n // If we've an AS and a V then take AS.\n const asValue = this._decodeFormValue(params.dict.get(\"AS\"));\n if (typeof asValue === \"string\") {\n this.data.fieldValue = asValue;\n }\n\n const yes =\n this.data.fieldValue !== null && this.data.fieldValue !== \"Off\"\n ? this.data.fieldValue\n : \"Yes\";\n\n const exportValues = normalAppearance.getKeys();\n if (exportValues.length === 0) {\n exportValues.push(\"Off\", yes);\n } else if (exportValues.length === 1) {\n if (exportValues[0] === \"Off\") {\n exportValues.push(yes);\n } else {\n exportValues.unshift(\"Off\");\n }\n } else if (exportValues.includes(yes)) {\n exportValues.length = 0;\n exportValues.push(\"Off\", yes);\n } else {\n const otherYes = exportValues.find(v => v !== \"Off\");\n exportValues.length = 0;\n exportValues.push(\"Off\", otherYes);\n }\n\n // Don't use a \"V\" entry pointing to a non-existent appearance state,\n // see e.g. bug1720411.pdf where it's an *empty* Name-instance.\n if (!exportValues.includes(this.data.fieldValue)) {\n this.data.fieldValue = \"Off\";\n }\n\n this.data.exportValue = exportValues[1];\n\n const checkedAppearance = normalAppearance.get(this.data.exportValue);\n this.checkedAppearance =\n checkedAppearance instanceof BaseStream ? checkedAppearance : null;\n const uncheckedAppearance = normalAppearance.get(\"Off\");\n this.uncheckedAppearance =\n uncheckedAppearance instanceof BaseStream ? uncheckedAppearance : null;\n\n if (this.checkedAppearance) {\n this._streams.push(this.checkedAppearance);\n } else {\n this._getDefaultCheckedAppearance(params, \"check\");\n }\n if (this.uncheckedAppearance) {\n this._streams.push(this.uncheckedAppearance);\n }\n this._fallbackFontDict = this.fallbackFontDict;\n if (this.data.defaultFieldValue === null) {\n this.data.defaultFieldValue = \"Off\";\n }\n }\n\n _processRadioButton(params) {\n this.data.buttonValue = null;\n\n // The parent field's `V` entry holds a `Name` object with the appearance\n // state of whichever child field is currently in the \"on\" state.\n const fieldParent = params.dict.get(\"Parent\");\n if (fieldParent instanceof Dict) {\n this.parent = params.dict.getRaw(\"Parent\");\n const fieldParentValue = fieldParent.get(\"V\");\n if (fieldParentValue instanceof Name) {\n this.data.fieldValue = this._decodeFormValue(fieldParentValue);\n }\n }\n\n // The button's value corresponds to its appearance state.\n const appearanceStates = params.dict.get(\"AP\");\n if (!(appearanceStates instanceof Dict)) {\n return;\n }\n const normalAppearance = appearanceStates.get(\"N\");\n if (!(normalAppearance instanceof Dict)) {\n return;\n }\n for (const key of normalAppearance.getKeys()) {\n if (key !== \"Off\") {\n this.data.buttonValue = this._decodeFormValue(key);\n break;\n }\n }\n\n const checkedAppearance = normalAppearance.get(this.data.buttonValue);\n this.checkedAppearance =\n checkedAppearance instanceof BaseStream ? checkedAppearance : null;\n const uncheckedAppearance = normalAppearance.get(\"Off\");\n this.uncheckedAppearance =\n uncheckedAppearance instanceof BaseStream ? uncheckedAppearance : null;\n\n if (this.checkedAppearance) {\n this._streams.push(this.checkedAppearance);\n } else {\n this._getDefaultCheckedAppearance(params, \"disc\");\n }\n if (this.uncheckedAppearance) {\n this._streams.push(this.uncheckedAppearance);\n }\n this._fallbackFontDict = this.fallbackFontDict;\n if (this.data.defaultFieldValue === null) {\n this.data.defaultFieldValue = \"Off\";\n }\n }\n\n _processPushButton(params) {\n const { dict, annotationGlobals } = params;\n\n if (!dict.has(\"A\") && !dict.has(\"AA\") && !this.data.alternativeText) {\n warn(\"Push buttons without action dictionaries are not supported\");\n return;\n }\n\n this.data.isTooltipOnly = !dict.has(\"A\") && !dict.has(\"AA\");\n\n Catalog.parseDestDictionary({\n destDict: dict,\n resultObj: this.data,\n docBaseUrl: annotationGlobals.baseUrl,\n docAttachments: annotationGlobals.attachments,\n });\n }\n\n getFieldObject() {\n let type = \"button\";\n let exportValues;\n if (this.data.checkBox) {\n type = \"checkbox\";\n exportValues = this.data.exportValue;\n } else if (this.data.radioButton) {\n type = \"radiobutton\";\n exportValues = this.data.buttonValue;\n }\n return {\n id: this.data.id,\n value: this.data.fieldValue || \"Off\",\n defaultValue: this.data.defaultFieldValue,\n exportValues,\n editable: !this.data.readOnly,\n name: this.data.fieldName,\n rect: this.data.rect,\n hidden: this.data.hidden,\n actions: this.data.actions,\n page: this.data.pageIndex,\n strokeColor: this.data.borderColor,\n fillColor: this.data.backgroundColor,\n rotation: this.rotation,\n type,\n };\n }\n\n get fallbackFontDict() {\n const dict = new Dict();\n dict.set(\"BaseFont\", Name.get(\"ZapfDingbats\"));\n dict.set(\"Type\", Name.get(\"FallbackType\"));\n dict.set(\"Subtype\", Name.get(\"FallbackType\"));\n dict.set(\"Encoding\", Name.get(\"ZapfDingbatsEncoding\"));\n\n return shadow(this, \"fallbackFontDict\", dict);\n }\n}\n\nclass ChoiceWidgetAnnotation extends WidgetAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n\n this.indices = dict.getArray(\"I\");\n this.hasIndices = Array.isArray(this.indices) && this.indices.length > 0;\n\n // Determine the options. The options array may consist of strings or\n // arrays. If the array consists of arrays, then the first element of\n // each array is the export value and the second element of each array is\n // the display value. If the array consists of strings, then these\n // represent both the export and display value. In this case, we convert\n // it to an array of arrays as well for convenience in the display layer.\n // Note that the specification does not state that the `Opt` field is\n // inheritable, but in practice PDF generators do make annotations\n // inherit the options from a parent annotation (issue 8094).\n this.data.options = [];\n\n const options = getInheritableProperty({ dict, key: \"Opt\" });\n if (Array.isArray(options)) {\n for (let i = 0, ii = options.length; i < ii; i++) {\n const option = xref.fetchIfRef(options[i]);\n const isOptionArray = Array.isArray(option);\n\n this.data.options[i] = {\n exportValue: this._decodeFormValue(\n isOptionArray ? xref.fetchIfRef(option[0]) : option\n ),\n displayValue: this._decodeFormValue(\n isOptionArray ? xref.fetchIfRef(option[1]) : option\n ),\n };\n }\n }\n\n if (!this.hasIndices) {\n // The field value can be `null` if no item is selected, a string if one\n // item is selected or an array of strings if multiple items are selected.\n // For consistency in the API and convenience in the display layer, we\n // always make the field value an array with zero, one or multiple items.\n if (typeof this.data.fieldValue === \"string\") {\n this.data.fieldValue = [this.data.fieldValue];\n } else if (!this.data.fieldValue) {\n this.data.fieldValue = [];\n }\n } else {\n // The specs say that we should have an indices array only with\n // multiselectable Choice and the \"V\" entry should have the\n // precedence, but Acrobat itself is using it whatever the\n // the \"V\" entry is (see bug 1770750).\n this.data.fieldValue = [];\n const ii = this.data.options.length;\n for (const i of this.indices) {\n if (Number.isInteger(i) && i >= 0 && i < ii) {\n this.data.fieldValue.push(this.data.options[i].exportValue);\n }\n }\n }\n\n // Process field flags for the display layer.\n this.data.combo = this.hasFieldFlag(AnnotationFieldFlag.COMBO);\n this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT);\n this._hasText = true;\n }\n\n getFieldObject() {\n const type = this.data.combo ? \"combobox\" : \"listbox\";\n const value =\n this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : null;\n return {\n id: this.data.id,\n value,\n defaultValue: this.data.defaultFieldValue,\n editable: !this.data.readOnly,\n name: this.data.fieldName,\n rect: this.data.rect,\n numItems: this.data.fieldValue.length,\n multipleSelection: this.data.multiSelect,\n hidden: this.data.hidden,\n actions: this.data.actions,\n items: this.data.options,\n page: this.data.pageIndex,\n strokeColor: this.data.borderColor,\n fillColor: this.data.backgroundColor,\n rotation: this.rotation,\n type,\n };\n }\n\n amendSavedDict(annotationStorage, dict) {\n if (!this.hasIndices) {\n return;\n }\n let values = annotationStorage?.get(this.data.id)?.value;\n if (!Array.isArray(values)) {\n values = [values];\n }\n const indices = [];\n const { options } = this.data;\n for (let i = 0, j = 0, ii = options.length; i < ii; i++) {\n if (options[i].exportValue === values[j]) {\n indices.push(i);\n j += 1;\n }\n }\n dict.set(\"I\", indices);\n }\n\n async _getAppearance(evaluator, task, intent, annotationStorage) {\n if (this.data.combo) {\n return super._getAppearance(evaluator, task, intent, annotationStorage);\n }\n\n let exportedValue, rotation;\n const storageEntry = annotationStorage?.get(this.data.id);\n if (storageEntry) {\n rotation = storageEntry.rotation;\n exportedValue = storageEntry.value;\n }\n\n if (\n rotation === undefined &&\n exportedValue === undefined &&\n !this._needAppearances\n ) {\n // The annotation hasn't been rendered so use the appearance\n return null;\n }\n\n if (exportedValue === undefined) {\n exportedValue = this.data.fieldValue;\n } else if (!Array.isArray(exportedValue)) {\n exportedValue = [exportedValue];\n }\n\n const defaultPadding = 1;\n const defaultHPadding = 2;\n let totalHeight = this.data.rect[3] - this.data.rect[1];\n let totalWidth = this.data.rect[2] - this.data.rect[0];\n\n if (rotation === 90 || rotation === 270) {\n [totalWidth, totalHeight] = [totalHeight, totalWidth];\n }\n\n const lineCount = this.data.options.length;\n const valueIndices = [];\n for (let i = 0; i < lineCount; i++) {\n const { exportValue } = this.data.options[i];\n if (exportedValue.includes(exportValue)) {\n valueIndices.push(i);\n }\n }\n\n if (!this._defaultAppearance) {\n // The DA is required and must be a string.\n // If there is no font named Helvetica in the resource dictionary,\n // the evaluator will fall back to a default font.\n // Doing so prevents exceptions and allows saving/printing\n // the file as expected.\n this.data.defaultAppearanceData = parseDefaultAppearance(\n (this._defaultAppearance = \"/Helvetica 0 Tf 0 g\")\n );\n }\n\n const font = await WidgetAnnotation._getFontData(\n evaluator,\n task,\n this.data.defaultAppearanceData,\n this._fieldResources.mergedResources\n );\n\n let defaultAppearance;\n let { fontSize } = this.data.defaultAppearanceData;\n if (!fontSize) {\n const lineHeight = (totalHeight - defaultPadding) / lineCount;\n let lineWidth = -1;\n let value;\n for (const { displayValue } of this.data.options) {\n const width = this._getTextWidth(displayValue, font);\n if (width > lineWidth) {\n lineWidth = width;\n value = displayValue;\n }\n }\n\n [defaultAppearance, fontSize] = this._computeFontSize(\n lineHeight,\n totalWidth - 2 * defaultHPadding,\n value,\n font,\n -1\n );\n } else {\n defaultAppearance = this._defaultAppearance;\n }\n\n const lineHeight = fontSize * LINE_FACTOR;\n const vPadding = (lineHeight - fontSize) / 2;\n const numberOfVisibleLines = Math.floor(totalHeight / lineHeight);\n\n let firstIndex = 0;\n if (valueIndices.length > 0) {\n const minIndex = Math.min(...valueIndices);\n const maxIndex = Math.max(...valueIndices);\n\n firstIndex = Math.max(0, maxIndex - numberOfVisibleLines + 1);\n if (firstIndex > minIndex) {\n firstIndex = minIndex;\n }\n }\n const end = Math.min(firstIndex + numberOfVisibleLines + 1, lineCount);\n\n const buf = [\"/Tx BMC q\", `1 1 ${totalWidth} ${totalHeight} re W n`];\n\n if (valueIndices.length) {\n // This value has been copied/pasted from annotation-choice-widget.pdf.\n // It corresponds to rgb(153, 193, 218).\n buf.push(\"0.600006 0.756866 0.854904 rg\");\n\n // Highlight the lines in filling a blue rectangle at the selected\n // positions.\n for (const index of valueIndices) {\n if (firstIndex <= index && index < end) {\n buf.push(\n `1 ${\n totalHeight - (index - firstIndex + 1) * lineHeight\n } ${totalWidth} ${lineHeight} re f`\n );\n }\n }\n }\n buf.push(\"BT\", defaultAppearance, `1 0 0 1 0 ${totalHeight} Tm`);\n\n const prevInfo = { shift: 0 };\n for (let i = firstIndex; i < end; i++) {\n const { displayValue } = this.data.options[i];\n const vpadding = i === firstIndex ? vPadding : 0;\n buf.push(\n this._renderText(\n displayValue,\n font,\n fontSize,\n totalWidth,\n 0,\n prevInfo,\n defaultHPadding,\n -lineHeight + vpadding\n )\n );\n }\n\n buf.push(\"ET Q EMC\");\n\n return buf.join(\"\\n\");\n }\n}\n\nclass SignatureWidgetAnnotation extends WidgetAnnotation {\n constructor(params) {\n super(params);\n\n // Unset the fieldValue since it's (most likely) a `Dict` which is\n // non-serializable and will thus cause errors when sending annotations\n // to the main-thread (issue 10347).\n this.data.fieldValue = null;\n this.data.hasOwnCanvas = this.data.noRotate;\n this.data.noHTML = !this.data.hasOwnCanvas;\n }\n\n getFieldObject() {\n return {\n id: this.data.id,\n value: null,\n page: this.data.pageIndex,\n type: \"signature\",\n };\n }\n}\n\nclass TextAnnotation extends MarkupAnnotation {\n constructor(params) {\n const DEFAULT_ICON_SIZE = 22; // px\n\n super(params);\n\n // No rotation for Text (see 12.5.6.4).\n this.data.noRotate = true;\n this.data.hasOwnCanvas = this.data.noRotate;\n this.data.noHTML = false;\n\n const { dict } = params;\n this.data.annotationType = AnnotationType.TEXT;\n\n if (this.data.hasAppearance) {\n this.data.name = \"NoIcon\";\n } else {\n this.data.rect[1] = this.data.rect[3] - DEFAULT_ICON_SIZE;\n this.data.rect[2] = this.data.rect[0] + DEFAULT_ICON_SIZE;\n this.data.name = dict.has(\"Name\") ? dict.get(\"Name\").name : \"Note\";\n }\n\n if (dict.has(\"State\")) {\n this.data.state = dict.get(\"State\") || null;\n this.data.stateModel = dict.get(\"StateModel\") || null;\n } else {\n this.data.state = null;\n this.data.stateModel = null;\n }\n }\n}\n\nclass LinkAnnotation extends Annotation {\n constructor(params) {\n super(params);\n\n const { dict, annotationGlobals } = params;\n this.data.annotationType = AnnotationType.LINK;\n\n // A link is never rendered on the main canvas so we must render its HTML\n // version.\n this.data.noHTML = false;\n\n const quadPoints = getQuadPoints(dict, this.rectangle);\n if (quadPoints) {\n this.data.quadPoints = quadPoints;\n }\n\n // The color entry for a link annotation is the color of the border.\n this.data.borderColor ||= this.data.color;\n\n Catalog.parseDestDictionary({\n destDict: dict,\n resultObj: this.data,\n docBaseUrl: annotationGlobals.baseUrl,\n docAttachments: annotationGlobals.attachments,\n });\n }\n}\n\nclass PopupAnnotation extends Annotation {\n constructor(params) {\n super(params);\n\n const { dict } = params;\n this.data.annotationType = AnnotationType.POPUP;\n\n // A pop-up is never rendered on the main canvas so we must render its HTML\n // version.\n this.data.noHTML = false;\n\n if (\n this.data.rect[0] === this.data.rect[2] ||\n this.data.rect[1] === this.data.rect[3]\n ) {\n this.data.rect = null;\n }\n\n let parentItem = dict.get(\"Parent\");\n if (!parentItem) {\n warn(\"Popup annotation has a missing or invalid parent annotation.\");\n return;\n }\n this.data.parentRect = lookupNormalRect(parentItem.getArray(\"Rect\"), null);\n\n const rt = parentItem.get(\"RT\");\n if (isName(rt, AnnotationReplyType.GROUP)) {\n // Subordinate annotations in a group should inherit\n // the group attributes from the primary annotation.\n parentItem = parentItem.get(\"IRT\");\n }\n\n if (!parentItem.has(\"M\")) {\n this.data.modificationDate = null;\n } else {\n this.setModificationDate(parentItem.get(\"M\"));\n this.data.modificationDate = this.modificationDate;\n }\n\n if (!parentItem.has(\"C\")) {\n // Fall back to the default background color.\n this.data.color = null;\n } else {\n this.setColor(parentItem.getArray(\"C\"));\n this.data.color = this.color;\n }\n\n // If the Popup annotation is not viewable, but the parent annotation is,\n // that is most likely a bug. Fallback to inherit the flags from the parent\n // annotation (this is consistent with the behaviour in Adobe Reader).\n if (!this.viewable) {\n const parentFlags = parentItem.get(\"F\");\n if (this._isViewable(parentFlags)) {\n this.setFlags(parentFlags);\n }\n }\n\n this.setTitle(parentItem.get(\"T\"));\n this.data.titleObj = this._title;\n\n this.setContents(parentItem.get(\"Contents\"));\n this.data.contentsObj = this._contents;\n\n if (parentItem.has(\"RC\")) {\n this.data.richText = XFAFactory.getRichTextAsHtml(parentItem.get(\"RC\"));\n }\n\n this.data.open = !!dict.get(\"Open\");\n }\n}\n\nclass FreeTextAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n // It uses its own canvas in order to be hidden if edited.\n // But if it has the noHTML flag, it means that we don't want to be able\n // to modify it so we can just draw it on the main canvas.\n this.data.hasOwnCanvas = !this.data.noHTML;\n // We want to be able to add mouse listeners to the annotation.\n this.data.noHTML = false;\n\n const { evaluatorOptions, xref } = params;\n this.data.annotationType = AnnotationType.FREETEXT;\n this.setDefaultAppearance(params);\n this._hasAppearance = !!this.appearance;\n\n if (this._hasAppearance) {\n const { fontColor, fontSize } = parseAppearanceStream(\n this.appearance,\n evaluatorOptions,\n xref\n );\n this.data.defaultAppearanceData.fontColor = fontColor;\n this.data.defaultAppearanceData.fontSize = fontSize || 10;\n } else {\n this.data.defaultAppearanceData.fontSize ||= 10;\n const { fontColor, fontSize } = this.data.defaultAppearanceData;\n if (this._contents.str) {\n this.data.textContent = this._contents.str\n .split(/\\r\\n?|\\n/)\n .map(line => line.trimEnd());\n const { coords, bbox, matrix } = FakeUnicodeFont.getFirstPositionInfo(\n this.rectangle,\n this.rotation,\n fontSize\n );\n this.data.textPosition = this._transformPoint(coords, bbox, matrix);\n }\n if (this._isOffscreenCanvasSupported) {\n const strokeAlpha = params.dict.get(\"CA\");\n const fakeUnicodeFont = new FakeUnicodeFont(xref, \"sans-serif\");\n this.appearance = fakeUnicodeFont.createAppearance(\n this._contents.str,\n this.rectangle,\n this.rotation,\n fontSize,\n fontColor,\n strokeAlpha\n );\n this._streams.push(this.appearance);\n } else {\n warn(\n \"FreeTextAnnotation: OffscreenCanvas is not supported, annotation may not render correctly.\"\n );\n }\n }\n }\n\n get hasTextContent() {\n return this._hasAppearance;\n }\n\n static createNewDict(annotation, xref, { apRef, ap }) {\n const { color, fontSize, rect, rotation, user, value } = annotation;\n const freetext = new Dict(xref);\n freetext.set(\"Type\", Name.get(\"Annot\"));\n freetext.set(\"Subtype\", Name.get(\"FreeText\"));\n freetext.set(\"CreationDate\", `D:${getModificationDate()}`);\n freetext.set(\"Rect\", rect);\n const da = `/Helv ${fontSize} Tf ${getPdfColor(color, /* isFill */ true)}`;\n freetext.set(\"DA\", da);\n freetext.set(\n \"Contents\",\n isAscii(value)\n ? value\n : stringToUTF16String(value, /* bigEndian = */ true)\n );\n freetext.set(\"F\", 4);\n freetext.set(\"Border\", [0, 0, 0]);\n freetext.set(\"Rotate\", rotation);\n\n if (user) {\n freetext.set(\n \"T\",\n isAscii(user) ? user : stringToUTF16String(user, /* bigEndian = */ true)\n );\n }\n\n if (apRef || ap) {\n const n = new Dict(xref);\n freetext.set(\"AP\", n);\n\n if (apRef) {\n n.set(\"N\", apRef);\n } else {\n n.set(\"N\", ap);\n }\n }\n\n return freetext;\n }\n\n static async createNewAppearanceStream(annotation, xref, params) {\n const { baseFontRef, evaluator, task } = params;\n const { color, fontSize, rect, rotation, value } = annotation;\n\n const resources = new Dict(xref);\n const font = new Dict(xref);\n\n if (baseFontRef) {\n font.set(\"Helv\", baseFontRef);\n } else {\n const baseFont = new Dict(xref);\n baseFont.set(\"BaseFont\", Name.get(\"Helvetica\"));\n baseFont.set(\"Type\", Name.get(\"Font\"));\n baseFont.set(\"Subtype\", Name.get(\"Type1\"));\n baseFont.set(\"Encoding\", Name.get(\"WinAnsiEncoding\"));\n font.set(\"Helv\", baseFont);\n }\n resources.set(\"Font\", font);\n\n const helv = await WidgetAnnotation._getFontData(\n evaluator,\n task,\n {\n fontName: \"Helv\",\n fontSize,\n },\n resources\n );\n\n const [x1, y1, x2, y2] = rect;\n let w = x2 - x1;\n let h = y2 - y1;\n\n if (rotation % 180 !== 0) {\n [w, h] = [h, w];\n }\n\n const lines = value.split(\"\\n\");\n const scale = fontSize / 1000;\n let totalWidth = -Infinity;\n const encodedLines = [];\n for (let line of lines) {\n const encoded = helv.encodeString(line);\n if (encoded.length > 1) {\n // The font doesn't contain all the chars.\n return null;\n }\n line = encoded.join(\"\");\n encodedLines.push(line);\n let lineWidth = 0;\n const glyphs = helv.charsToGlyphs(line);\n for (const glyph of glyphs) {\n lineWidth += glyph.width * scale;\n }\n totalWidth = Math.max(totalWidth, lineWidth);\n }\n\n let hscale = 1;\n if (totalWidth > w) {\n hscale = w / totalWidth;\n }\n let vscale = 1;\n const lineHeight = LINE_FACTOR * fontSize;\n const lineAscent = (LINE_FACTOR - LINE_DESCENT_FACTOR) * fontSize;\n const totalHeight = lineHeight * lines.length;\n if (totalHeight > h) {\n vscale = h / totalHeight;\n }\n const fscale = Math.min(hscale, vscale);\n const newFontSize = fontSize * fscale;\n let firstPoint, clipBox, matrix;\n switch (rotation) {\n case 0:\n matrix = [1, 0, 0, 1];\n clipBox = [rect[0], rect[1], w, h];\n firstPoint = [rect[0], rect[3] - lineAscent];\n break;\n case 90:\n matrix = [0, 1, -1, 0];\n clipBox = [rect[1], -rect[2], w, h];\n firstPoint = [rect[1], -rect[0] - lineAscent];\n break;\n case 180:\n matrix = [-1, 0, 0, -1];\n clipBox = [-rect[2], -rect[3], w, h];\n firstPoint = [-rect[2], -rect[1] - lineAscent];\n break;\n case 270:\n matrix = [0, -1, 1, 0];\n clipBox = [-rect[3], rect[0], w, h];\n firstPoint = [-rect[3], rect[2] - lineAscent];\n break;\n }\n\n const buffer = [\n \"q\",\n `${matrix.join(\" \")} 0 0 cm`,\n `${clipBox.join(\" \")} re W n`,\n `BT`,\n `${getPdfColor(color, /* isFill */ true)}`,\n `0 Tc /Helv ${numberToString(newFontSize)} Tf`,\n ];\n\n buffer.push(\n `${firstPoint.join(\" \")} Td (${escapeString(encodedLines[0])}) Tj`\n );\n const vShift = numberToString(lineHeight);\n for (let i = 1, ii = encodedLines.length; i < ii; i++) {\n const line = encodedLines[i];\n buffer.push(`0 -${vShift} Td (${escapeString(line)}) Tj`);\n }\n buffer.push(\"ET\", \"Q\");\n const appearance = buffer.join(\"\\n\");\n\n const appearanceStreamDict = new Dict(xref);\n appearanceStreamDict.set(\"FormType\", 1);\n appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n appearanceStreamDict.set(\"BBox\", rect);\n appearanceStreamDict.set(\"Resources\", resources);\n appearanceStreamDict.set(\"Matrix\", [1, 0, 0, 1, -rect[0], -rect[1]]);\n\n const ap = new StringStream(appearance);\n ap.dict = appearanceStreamDict;\n\n return ap;\n }\n}\n\nclass LineAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n this.data.annotationType = AnnotationType.LINE;\n this.data.hasOwnCanvas = this.data.noRotate;\n this.data.noHTML = false;\n\n const lineCoordinates = lookupRect(dict.getArray(\"L\"), [0, 0, 0, 0]);\n this.data.lineCoordinates = Util.normalizeRect(lineCoordinates);\n\n if (typeof PDFJSDev === \"undefined\" || !PDFJSDev.test(\"MOZCENTRAL\")) {\n this.setLineEndings(dict.getArray(\"LE\"));\n this.data.lineEndings = this.lineEndings;\n }\n\n if (!this.appearance) {\n // The default stroke color is black.\n const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n const strokeAlpha = dict.get(\"CA\");\n\n const interiorColor = getRgbColor(dict.getArray(\"IC\"), null);\n // The default fill color is transparent. Setting the fill colour is\n // necessary if/when we want to add support for non-default line endings.\n const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;\n const fillAlpha = fillColor ? strokeAlpha : null;\n\n const borderWidth = this.borderStyle.width || 1,\n borderAdjust = 2 * borderWidth;\n\n // If the /Rect-entry is empty/wrong, create a fallback rectangle so that\n // we get similar rendering/highlighting behaviour as in Adobe Reader.\n const bbox = [\n this.data.lineCoordinates[0] - borderAdjust,\n this.data.lineCoordinates[1] - borderAdjust,\n this.data.lineCoordinates[2] + borderAdjust,\n this.data.lineCoordinates[3] + borderAdjust,\n ];\n if (!Util.intersect(this.rectangle, bbox)) {\n this.rectangle = bbox;\n }\n\n this._setDefaultAppearance({\n xref,\n extra: `${borderWidth} w`,\n strokeColor,\n fillColor,\n strokeAlpha,\n fillAlpha,\n pointsCallback: (buffer, points) => {\n buffer.push(\n `${lineCoordinates[0]} ${lineCoordinates[1]} m`,\n `${lineCoordinates[2]} ${lineCoordinates[3]} l`,\n \"S\"\n );\n return [\n points[0].x - borderWidth,\n points[1].x + borderWidth,\n points[3].y - borderWidth,\n points[1].y + borderWidth,\n ];\n },\n });\n }\n }\n}\n\nclass SquareAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n this.data.annotationType = AnnotationType.SQUARE;\n this.data.hasOwnCanvas = this.data.noRotate;\n this.data.noHTML = false;\n\n if (!this.appearance) {\n // The default stroke color is black.\n const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n const strokeAlpha = dict.get(\"CA\");\n\n const interiorColor = getRgbColor(dict.getArray(\"IC\"), null);\n // The default fill color is transparent.\n const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;\n const fillAlpha = fillColor ? strokeAlpha : null;\n\n if (this.borderStyle.width === 0 && !fillColor) {\n // Prevent rendering a \"hairline\" border (fixes issue14164.pdf).\n return;\n }\n\n this._setDefaultAppearance({\n xref,\n extra: `${this.borderStyle.width} w`,\n strokeColor,\n fillColor,\n strokeAlpha,\n fillAlpha,\n pointsCallback: (buffer, points) => {\n const x = points[2].x + this.borderStyle.width / 2;\n const y = points[2].y + this.borderStyle.width / 2;\n const width = points[3].x - points[2].x - this.borderStyle.width;\n const height = points[1].y - points[3].y - this.borderStyle.width;\n buffer.push(`${x} ${y} ${width} ${height} re`);\n if (fillColor) {\n buffer.push(\"B\");\n } else {\n buffer.push(\"S\");\n }\n return [points[0].x, points[1].x, points[3].y, points[1].y];\n },\n });\n }\n }\n}\n\nclass CircleAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n this.data.annotationType = AnnotationType.CIRCLE;\n\n if (!this.appearance) {\n // The default stroke color is black.\n const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n const strokeAlpha = dict.get(\"CA\");\n\n const interiorColor = getRgbColor(dict.getArray(\"IC\"), null);\n // The default fill color is transparent.\n const fillColor = interiorColor ? getPdfColorArray(interiorColor) : null;\n const fillAlpha = fillColor ? strokeAlpha : null;\n\n if (this.borderStyle.width === 0 && !fillColor) {\n // Prevent rendering a \"hairline\" border (fixes issue14164.pdf).\n return;\n }\n\n // Circles are approximated by Bézier curves with four segments since\n // there is no circle primitive in the PDF specification. For the control\n // points distance, see https://stackoverflow.com/a/27863181.\n const controlPointsDistance = (4 / 3) * Math.tan(Math.PI / (2 * 4));\n\n this._setDefaultAppearance({\n xref,\n extra: `${this.borderStyle.width} w`,\n strokeColor,\n fillColor,\n strokeAlpha,\n fillAlpha,\n pointsCallback: (buffer, points) => {\n const x0 = points[0].x + this.borderStyle.width / 2;\n const y0 = points[0].y - this.borderStyle.width / 2;\n const x1 = points[3].x - this.borderStyle.width / 2;\n const y1 = points[3].y + this.borderStyle.width / 2;\n const xMid = x0 + (x1 - x0) / 2;\n const yMid = y0 + (y1 - y0) / 2;\n const xOffset = ((x1 - x0) / 2) * controlPointsDistance;\n const yOffset = ((y1 - y0) / 2) * controlPointsDistance;\n\n buffer.push(\n `${xMid} ${y1} m`,\n `${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`,\n `${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`,\n `${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`,\n `${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`,\n \"h\"\n );\n if (fillColor) {\n buffer.push(\"B\");\n } else {\n buffer.push(\"S\");\n }\n return [points[0].x, points[1].x, points[3].y, points[1].y];\n },\n });\n }\n }\n}\n\nclass PolylineAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n this.data.annotationType = AnnotationType.POLYLINE;\n this.data.hasOwnCanvas = this.data.noRotate;\n this.data.noHTML = false;\n this.data.vertices = [];\n\n if (\n (typeof PDFJSDev === \"undefined\" || !PDFJSDev.test(\"MOZCENTRAL\")) &&\n !(this instanceof PolygonAnnotation)\n ) {\n // Only meaningful for polyline annotations.\n this.setLineEndings(dict.getArray(\"LE\"));\n this.data.lineEndings = this.lineEndings;\n }\n\n // The vertices array is an array of numbers representing the alternating\n // horizontal and vertical coordinates, respectively, of each vertex.\n // Convert this to an array of objects with x and y coordinates.\n const rawVertices = dict.getArray(\"Vertices\");\n if (!isNumberArray(rawVertices, null)) {\n return;\n }\n for (let i = 0, ii = rawVertices.length; i < ii; i += 2) {\n this.data.vertices.push({\n x: rawVertices[i],\n y: rawVertices[i + 1],\n });\n }\n\n if (!this.appearance) {\n // The default stroke color is black.\n const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n const strokeAlpha = dict.get(\"CA\");\n\n const borderWidth = this.borderStyle.width || 1,\n borderAdjust = 2 * borderWidth;\n\n // If the /Rect-entry is empty/wrong, create a fallback rectangle so that\n // we get similar rendering/highlighting behaviour as in Adobe Reader.\n const bbox = [Infinity, Infinity, -Infinity, -Infinity];\n for (const vertex of this.data.vertices) {\n bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);\n bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);\n bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);\n bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);\n }\n if (!Util.intersect(this.rectangle, bbox)) {\n this.rectangle = bbox;\n }\n\n this._setDefaultAppearance({\n xref,\n extra: `${borderWidth} w`,\n strokeColor,\n strokeAlpha,\n pointsCallback: (buffer, points) => {\n const vertices = this.data.vertices;\n for (let i = 0, ii = vertices.length; i < ii; i++) {\n buffer.push(\n `${vertices[i].x} ${vertices[i].y} ${i === 0 ? \"m\" : \"l\"}`\n );\n }\n buffer.push(\"S\");\n return [points[0].x, points[1].x, points[3].y, points[1].y];\n },\n });\n }\n }\n}\n\nclass PolygonAnnotation extends PolylineAnnotation {\n constructor(params) {\n // Polygons are specific forms of polylines, so reuse their logic.\n super(params);\n\n this.data.annotationType = AnnotationType.POLYGON;\n }\n}\n\nclass CaretAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n this.data.annotationType = AnnotationType.CARET;\n }\n}\n\nclass InkAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n this.data.hasOwnCanvas = this.data.noRotate;\n this.data.noHTML = false;\n\n const { dict, xref } = params;\n this.data.annotationType = AnnotationType.INK;\n this.data.inkLists = [];\n\n const rawInkLists = dict.getArray(\"InkList\");\n if (!Array.isArray(rawInkLists)) {\n return;\n }\n for (let i = 0, ii = rawInkLists.length; i < ii; ++i) {\n // The raw ink lists array contains arrays of numbers representing\n // the alternating horizontal and vertical coordinates, respectively,\n // of each vertex. Convert this to an array of objects with x and y\n // coordinates.\n this.data.inkLists.push([]);\n if (!Array.isArray(rawInkLists[i])) {\n continue;\n }\n for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) {\n const x = xref.fetchIfRef(rawInkLists[i][j]),\n y = xref.fetchIfRef(rawInkLists[i][j + 1]);\n if (typeof x === \"number\" && typeof y === \"number\") {\n this.data.inkLists[i].push({ x, y });\n }\n }\n }\n\n if (!this.appearance) {\n // The default stroke color is black.\n const strokeColor = this.color ? getPdfColorArray(this.color) : [0, 0, 0];\n const strokeAlpha = dict.get(\"CA\");\n\n const borderWidth = this.borderStyle.width || 1,\n borderAdjust = 2 * borderWidth;\n\n // If the /Rect-entry is empty/wrong, create a fallback rectangle so that\n // we get similar rendering/highlighting behaviour as in Adobe Reader.\n const bbox = [Infinity, Infinity, -Infinity, -Infinity];\n for (const inkLists of this.data.inkLists) {\n for (const vertex of inkLists) {\n bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);\n bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);\n bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);\n bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);\n }\n }\n if (!Util.intersect(this.rectangle, bbox)) {\n this.rectangle = bbox;\n }\n\n this._setDefaultAppearance({\n xref,\n extra: `${borderWidth} w`,\n strokeColor,\n strokeAlpha,\n pointsCallback: (buffer, points) => {\n // According to the specification, see \"12.5.6.13 Ink Annotations\":\n // When drawn, the points shall be connected by straight lines or\n // curves in an implementation-dependent way.\n // In order to simplify things, we utilize straight lines for now.\n for (const inkList of this.data.inkLists) {\n for (let i = 0, ii = inkList.length; i < ii; i++) {\n buffer.push(\n `${inkList[i].x} ${inkList[i].y} ${i === 0 ? \"m\" : \"l\"}`\n );\n }\n buffer.push(\"S\");\n }\n return [points[0].x, points[1].x, points[3].y, points[1].y];\n },\n });\n }\n }\n\n static createNewDict(annotation, xref, { apRef, ap }) {\n const { color, opacity, paths, outlines, rect, rotation, thickness } =\n annotation;\n const ink = new Dict(xref);\n ink.set(\"Type\", Name.get(\"Annot\"));\n ink.set(\"Subtype\", Name.get(\"Ink\"));\n ink.set(\"CreationDate\", `D:${getModificationDate()}`);\n ink.set(\"Rect\", rect);\n ink.set(\"InkList\", outlines?.points || paths.map(p => p.points));\n ink.set(\"F\", 4);\n ink.set(\"Rotate\", rotation);\n\n if (outlines) {\n // Free highlight.\n // There's nothing about this in the spec, but it's used when highlighting\n // in Edge's viewer. Acrobat takes into account this parameter to indicate\n // that the Ink is used for highlighting.\n ink.set(\"IT\", Name.get(\"InkHighlight\"));\n }\n\n // Line thickness.\n const bs = new Dict(xref);\n ink.set(\"BS\", bs);\n bs.set(\"W\", thickness);\n\n // Color.\n ink.set(\n \"C\",\n Array.from(color, c => c / 255)\n );\n\n // Opacity.\n ink.set(\"CA\", opacity);\n\n const n = new Dict(xref);\n ink.set(\"AP\", n);\n\n if (apRef) {\n n.set(\"N\", apRef);\n } else {\n n.set(\"N\", ap);\n }\n\n return ink;\n }\n\n static async createNewAppearanceStream(annotation, xref, params) {\n if (annotation.outlines) {\n return this.createNewAppearanceStreamForHighlight(\n annotation,\n xref,\n params\n );\n }\n const { color, rect, paths, thickness, opacity } = annotation;\n\n const appearanceBuffer = [\n `${thickness} w 1 J 1 j`,\n `${getPdfColor(color, /* isFill */ false)}`,\n ];\n\n if (opacity !== 1) {\n appearanceBuffer.push(\"/R0 gs\");\n }\n\n const buffer = [];\n for (const { bezier } of paths) {\n buffer.length = 0;\n buffer.push(\n `${numberToString(bezier[0])} ${numberToString(bezier[1])} m`\n );\n if (bezier.length === 2) {\n buffer.push(\n `${numberToString(bezier[0])} ${numberToString(bezier[1])} l S`\n );\n } else {\n for (let i = 2, ii = bezier.length; i < ii; i += 6) {\n const curve = bezier\n .slice(i, i + 6)\n .map(numberToString)\n .join(\" \");\n buffer.push(`${curve} c`);\n }\n buffer.push(\"S\");\n }\n appearanceBuffer.push(buffer.join(\"\\n\"));\n }\n const appearance = appearanceBuffer.join(\"\\n\");\n\n const appearanceStreamDict = new Dict(xref);\n appearanceStreamDict.set(\"FormType\", 1);\n appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n appearanceStreamDict.set(\"BBox\", rect);\n appearanceStreamDict.set(\"Length\", appearance.length);\n\n if (opacity !== 1) {\n const resources = new Dict(xref);\n const extGState = new Dict(xref);\n const r0 = new Dict(xref);\n r0.set(\"CA\", opacity);\n r0.set(\"Type\", Name.get(\"ExtGState\"));\n extGState.set(\"R0\", r0);\n resources.set(\"ExtGState\", extGState);\n appearanceStreamDict.set(\"Resources\", resources);\n }\n\n const ap = new StringStream(appearance);\n ap.dict = appearanceStreamDict;\n\n return ap;\n }\n\n static async createNewAppearanceStreamForHighlight(annotation, xref, params) {\n const {\n color,\n rect,\n outlines: { outline },\n opacity,\n } = annotation;\n const appearanceBuffer = [\n `${getPdfColor(color, /* isFill */ true)}`,\n \"/R0 gs\",\n ];\n\n appearanceBuffer.push(\n `${numberToString(outline[4])} ${numberToString(outline[5])} m`\n );\n for (let i = 6, ii = outline.length; i < ii; i += 6) {\n if (isNaN(outline[i]) || outline[i] === null) {\n appearanceBuffer.push(\n `${numberToString(outline[i + 4])} ${numberToString(\n outline[i + 5]\n )} l`\n );\n } else {\n const curve = outline\n .slice(i, i + 6)\n .map(numberToString)\n .join(\" \");\n appearanceBuffer.push(`${curve} c`);\n }\n }\n appearanceBuffer.push(\"h f\");\n const appearance = appearanceBuffer.join(\"\\n\");\n\n const appearanceStreamDict = new Dict(xref);\n appearanceStreamDict.set(\"FormType\", 1);\n appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n appearanceStreamDict.set(\"BBox\", rect);\n appearanceStreamDict.set(\"Length\", appearance.length);\n\n const resources = new Dict(xref);\n const extGState = new Dict(xref);\n resources.set(\"ExtGState\", extGState);\n appearanceStreamDict.set(\"Resources\", resources);\n const r0 = new Dict(xref);\n extGState.set(\"R0\", r0);\n r0.set(\"BM\", Name.get(\"Multiply\"));\n\n if (opacity !== 1) {\n r0.set(\"ca\", opacity);\n r0.set(\"Type\", Name.get(\"ExtGState\"));\n }\n\n const ap = new StringStream(appearance);\n ap.dict = appearanceStreamDict;\n\n return ap;\n }\n}\n\nclass HighlightAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n this.data.annotationType = AnnotationType.HIGHLIGHT;\n\n const quadPoints = (this.data.quadPoints = getQuadPoints(dict, null));\n if (quadPoints) {\n const resources = this.appearance?.dict.get(\"Resources\");\n\n if (!this.appearance || !resources?.has(\"ExtGState\")) {\n if (this.appearance) {\n // Workaround for cases where there's no /ExtGState-entry directly\n // available, e.g. when the appearance stream contains a /XObject of\n // the /Form-type, since that causes the highlighting to completely\n // obscure the PDF content below it (fixes issue13242.pdf).\n warn(\"HighlightAnnotation - ignoring built-in appearance stream.\");\n }\n // Default color is yellow in Acrobat Reader\n const fillColor = this.color ? getPdfColorArray(this.color) : [1, 1, 0];\n const fillAlpha = dict.get(\"CA\");\n\n this._setDefaultAppearance({\n xref,\n fillColor,\n blendMode: \"Multiply\",\n fillAlpha,\n pointsCallback: (buffer, points) => {\n buffer.push(\n `${points[0].x} ${points[0].y} m`,\n `${points[1].x} ${points[1].y} l`,\n `${points[3].x} ${points[3].y} l`,\n `${points[2].x} ${points[2].y} l`,\n \"f\"\n );\n return [points[0].x, points[1].x, points[3].y, points[1].y];\n },\n });\n }\n } else {\n this.data.popupRef = null;\n }\n }\n\n static createNewDict(annotation, xref, { apRef, ap }) {\n const { color, opacity, rect, rotation, user, quadPoints } = annotation;\n const highlight = new Dict(xref);\n highlight.set(\"Type\", Name.get(\"Annot\"));\n highlight.set(\"Subtype\", Name.get(\"Highlight\"));\n highlight.set(\"CreationDate\", `D:${getModificationDate()}`);\n highlight.set(\"Rect\", rect);\n highlight.set(\"F\", 4);\n highlight.set(\"Border\", [0, 0, 0]);\n highlight.set(\"Rotate\", rotation);\n highlight.set(\"QuadPoints\", quadPoints);\n\n // Color.\n highlight.set(\n \"C\",\n Array.from(color, c => c / 255)\n );\n\n // Opacity.\n highlight.set(\"CA\", opacity);\n\n if (user) {\n highlight.set(\n \"T\",\n isAscii(user) ? user : stringToUTF16String(user, /* bigEndian = */ true)\n );\n }\n\n if (apRef || ap) {\n const n = new Dict(xref);\n highlight.set(\"AP\", n);\n n.set(\"N\", apRef || ap);\n }\n\n return highlight;\n }\n\n static async createNewAppearanceStream(annotation, xref, params) {\n const { color, rect, outlines, opacity } = annotation;\n\n const appearanceBuffer = [\n `${getPdfColor(color, /* isFill */ true)}`,\n \"/R0 gs\",\n ];\n\n const buffer = [];\n for (const outline of outlines) {\n buffer.length = 0;\n buffer.push(\n `${numberToString(outline[0])} ${numberToString(outline[1])} m`\n );\n for (let i = 2, ii = outline.length; i < ii; i += 2) {\n buffer.push(\n `${numberToString(outline[i])} ${numberToString(outline[i + 1])} l`\n );\n }\n buffer.push(\"h\");\n appearanceBuffer.push(buffer.join(\"\\n\"));\n }\n appearanceBuffer.push(\"f*\");\n const appearance = appearanceBuffer.join(\"\\n\");\n\n const appearanceStreamDict = new Dict(xref);\n appearanceStreamDict.set(\"FormType\", 1);\n appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n appearanceStreamDict.set(\"BBox\", rect);\n appearanceStreamDict.set(\"Length\", appearance.length);\n\n const resources = new Dict(xref);\n const extGState = new Dict(xref);\n resources.set(\"ExtGState\", extGState);\n appearanceStreamDict.set(\"Resources\", resources);\n const r0 = new Dict(xref);\n extGState.set(\"R0\", r0);\n r0.set(\"BM\", Name.get(\"Multiply\"));\n\n if (opacity !== 1) {\n r0.set(\"ca\", opacity);\n r0.set(\"Type\", Name.get(\"ExtGState\"));\n }\n\n const ap = new StringStream(appearance);\n ap.dict = appearanceStreamDict;\n\n return ap;\n }\n}\n\nclass UnderlineAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n this.data.annotationType = AnnotationType.UNDERLINE;\n\n const quadPoints = (this.data.quadPoints = getQuadPoints(dict, null));\n if (quadPoints) {\n if (!this.appearance) {\n // Default color is black\n const strokeColor = this.color\n ? getPdfColorArray(this.color)\n : [0, 0, 0];\n const strokeAlpha = dict.get(\"CA\");\n\n // The values 0.571 and 1.3 below corresponds to what Acrobat is doing.\n this._setDefaultAppearance({\n xref,\n extra: \"[] 0 d 0.571 w\",\n strokeColor,\n strokeAlpha,\n pointsCallback: (buffer, points) => {\n buffer.push(\n `${points[2].x} ${points[2].y + 1.3} m`,\n `${points[3].x} ${points[3].y + 1.3} l`,\n \"S\"\n );\n return [points[0].x, points[1].x, points[3].y, points[1].y];\n },\n });\n }\n } else {\n this.data.popupRef = null;\n }\n }\n}\n\nclass SquigglyAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n this.data.annotationType = AnnotationType.SQUIGGLY;\n\n const quadPoints = (this.data.quadPoints = getQuadPoints(dict, null));\n if (quadPoints) {\n if (!this.appearance) {\n // Default color is black\n const strokeColor = this.color\n ? getPdfColorArray(this.color)\n : [0, 0, 0];\n const strokeAlpha = dict.get(\"CA\");\n\n this._setDefaultAppearance({\n xref,\n extra: \"[] 0 d 1 w\",\n strokeColor,\n strokeAlpha,\n pointsCallback: (buffer, points) => {\n const dy = (points[0].y - points[2].y) / 6;\n let shift = dy;\n let x = points[2].x;\n const y = points[2].y;\n const xEnd = points[3].x;\n buffer.push(`${x} ${y + shift} m`);\n do {\n x += 2;\n shift = shift === 0 ? dy : 0;\n buffer.push(`${x} ${y + shift} l`);\n } while (x < xEnd);\n buffer.push(\"S\");\n return [points[2].x, xEnd, y - 2 * dy, y + 2 * dy];\n },\n });\n }\n } else {\n this.data.popupRef = null;\n }\n }\n}\n\nclass StrikeOutAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n this.data.annotationType = AnnotationType.STRIKEOUT;\n\n const quadPoints = (this.data.quadPoints = getQuadPoints(dict, null));\n if (quadPoints) {\n if (!this.appearance) {\n // Default color is black\n const strokeColor = this.color\n ? getPdfColorArray(this.color)\n : [0, 0, 0];\n const strokeAlpha = dict.get(\"CA\");\n\n this._setDefaultAppearance({\n xref,\n extra: \"[] 0 d 1 w\",\n strokeColor,\n strokeAlpha,\n pointsCallback: (buffer, points) => {\n buffer.push(\n `${(points[0].x + points[2].x) / 2} ` +\n `${(points[0].y + points[2].y) / 2} m`,\n `${(points[1].x + points[3].x) / 2} ` +\n `${(points[1].y + points[3].y) / 2} l`,\n \"S\"\n );\n return [points[0].x, points[1].x, points[3].y, points[1].y];\n },\n });\n }\n } else {\n this.data.popupRef = null;\n }\n }\n}\n\nclass StampAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n this.data.annotationType = AnnotationType.STAMP;\n this.data.hasOwnCanvas = this.data.noRotate;\n this.data.noHTML = false;\n }\n\n static async createImage(bitmap, xref) {\n // TODO: when printing, we could have a specific internal colorspace\n // (e.g. something like DeviceRGBA) in order avoid any conversion (i.e. no\n // jpeg, no rgba to rgb conversion, etc...)\n\n const { width, height } = bitmap;\n const canvas = new OffscreenCanvas(width, height);\n const ctx = canvas.getContext(\"2d\", { alpha: true });\n\n // Draw the image and get the data in order to extract the transparency.\n ctx.drawImage(bitmap, 0, 0);\n const data = ctx.getImageData(0, 0, width, height).data;\n const buf32 = new Uint32Array(data.buffer);\n const hasAlpha = buf32.some(\n FeatureTest.isLittleEndian\n ? x => x >>> 24 !== 0xff\n : x => (x & 0xff) !== 0xff\n );\n\n if (hasAlpha) {\n // Redraw the image on a white background in order to remove the thin gray\n // line which can appear when exporting to jpeg.\n ctx.fillStyle = \"white\";\n ctx.fillRect(0, 0, width, height);\n ctx.drawImage(bitmap, 0, 0);\n }\n\n const jpegBufferPromise = canvas\n .convertToBlob({ type: \"image/jpeg\", quality: 1 })\n .then(blob => blob.arrayBuffer());\n\n const xobjectName = Name.get(\"XObject\");\n const imageName = Name.get(\"Image\");\n const image = new Dict(xref);\n image.set(\"Type\", xobjectName);\n image.set(\"Subtype\", imageName);\n image.set(\"BitsPerComponent\", 8);\n image.set(\"ColorSpace\", Name.get(\"DeviceRGB\"));\n image.set(\"Filter\", Name.get(\"DCTDecode\"));\n image.set(\"BBox\", [0, 0, width, height]);\n image.set(\"Width\", width);\n image.set(\"Height\", height);\n\n let smaskStream = null;\n if (hasAlpha) {\n const alphaBuffer = new Uint8Array(buf32.length);\n if (FeatureTest.isLittleEndian) {\n for (let i = 0, ii = buf32.length; i < ii; i++) {\n alphaBuffer[i] = buf32[i] >>> 24;\n }\n } else {\n for (let i = 0, ii = buf32.length; i < ii; i++) {\n alphaBuffer[i] = buf32[i] & 0xff;\n }\n }\n\n const smask = new Dict(xref);\n smask.set(\"Type\", xobjectName);\n smask.set(\"Subtype\", imageName);\n smask.set(\"BitsPerComponent\", 8);\n smask.set(\"ColorSpace\", Name.get(\"DeviceGray\"));\n smask.set(\"Width\", width);\n smask.set(\"Height\", height);\n\n smaskStream = new Stream(alphaBuffer, 0, 0, smask);\n }\n const imageStream = new Stream(await jpegBufferPromise, 0, 0, image);\n\n return {\n imageStream,\n smaskStream,\n width,\n height,\n };\n }\n\n static createNewDict(annotation, xref, { apRef, ap }) {\n const { rect, rotation, user } = annotation;\n const stamp = new Dict(xref);\n stamp.set(\"Type\", Name.get(\"Annot\"));\n stamp.set(\"Subtype\", Name.get(\"Stamp\"));\n stamp.set(\"CreationDate\", `D:${getModificationDate()}`);\n stamp.set(\"Rect\", rect);\n stamp.set(\"F\", 4);\n stamp.set(\"Border\", [0, 0, 0]);\n stamp.set(\"Rotate\", rotation);\n\n if (user) {\n stamp.set(\n \"T\",\n isAscii(user) ? user : stringToUTF16String(user, /* bigEndian = */ true)\n );\n }\n\n if (apRef || ap) {\n const n = new Dict(xref);\n stamp.set(\"AP\", n);\n\n if (apRef) {\n n.set(\"N\", apRef);\n } else {\n n.set(\"N\", ap);\n }\n }\n\n return stamp;\n }\n\n static async createNewAppearanceStream(annotation, xref, params) {\n const { rotation } = annotation;\n const { imageRef, width, height } = params.image;\n const resources = new Dict(xref);\n const xobject = new Dict(xref);\n resources.set(\"XObject\", xobject);\n xobject.set(\"Im0\", imageRef);\n const appearance = `q ${width} 0 0 ${height} 0 0 cm /Im0 Do Q`;\n\n const appearanceStreamDict = new Dict(xref);\n appearanceStreamDict.set(\"FormType\", 1);\n appearanceStreamDict.set(\"Subtype\", Name.get(\"Form\"));\n appearanceStreamDict.set(\"Type\", Name.get(\"XObject\"));\n appearanceStreamDict.set(\"BBox\", [0, 0, width, height]);\n appearanceStreamDict.set(\"Resources\", resources);\n\n if (rotation) {\n const matrix = getRotationMatrix(rotation, width, height);\n appearanceStreamDict.set(\"Matrix\", matrix);\n }\n\n const ap = new StringStream(appearance);\n ap.dict = appearanceStreamDict;\n\n return ap;\n }\n}\n\nclass FileAttachmentAnnotation extends MarkupAnnotation {\n constructor(params) {\n super(params);\n\n const { dict, xref } = params;\n const file = new FileSpec(dict.get(\"FS\"), xref);\n\n this.data.annotationType = AnnotationType.FILEATTACHMENT;\n this.data.hasOwnCanvas = this.data.noRotate;\n this.data.noHTML = false;\n this.data.file = file.serializable;\n\n const name = dict.get(\"Name\");\n this.data.name =\n name instanceof Name ? stringToPDFString(name.name) : \"PushPin\";\n\n const fillAlpha = dict.get(\"ca\");\n this.data.fillAlpha =\n typeof fillAlpha === \"number\" && fillAlpha >= 0 && fillAlpha <= 1\n ? fillAlpha\n : null;\n }\n}\n\nexport {\n Annotation,\n AnnotationBorderStyle,\n AnnotationFactory,\n getQuadPoints,\n MarkupAnnotation,\n PopupAnnotation,\n WidgetAnnotation,\n};\n","/* Copyright 2022 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { stringToUTF8String, warn } from \"../shared/util.js\";\nimport { parseXFAPath } from \"./core_utils.js\";\nimport { SimpleXMLParser } from \"./xml_parser.js\";\n\nfunction decodeString(str) {\n try {\n return stringToUTF8String(str);\n } catch (ex) {\n warn(`UTF-8 decoding failed: \"${ex}\".`);\n return str;\n }\n}\n\nclass DatasetXMLParser extends SimpleXMLParser {\n constructor(options) {\n super(options);\n this.node = null;\n }\n\n onEndElement(name) {\n const node = super.onEndElement(name);\n if (node && name === \"xfa:datasets\") {\n this.node = node;\n\n // We don't need anything else, so just kill the parser.\n throw new Error(\"Aborting DatasetXMLParser.\");\n }\n }\n}\n\nclass DatasetReader {\n constructor(data) {\n if (data.datasets) {\n this.node = new SimpleXMLParser({ hasAttributes: true }).parseFromString(\n data.datasets\n ).documentElement;\n } else {\n const parser = new DatasetXMLParser({ hasAttributes: true });\n try {\n parser.parseFromString(data[\"xdp:xdp\"]);\n } catch {}\n this.node = parser.node;\n }\n }\n\n getValue(path) {\n if (!this.node || !path) {\n return \"\";\n }\n const node = this.node.searchNode(parseXFAPath(path), 0);\n\n if (!node) {\n return \"\";\n }\n\n const first = node.firstChild;\n if (first?.nodeName === \"value\") {\n return node.children.map(child => decodeString(child.textContent));\n }\n\n return decodeString(node.textContent);\n }\n}\n\nexport { DatasetReader };\n","/* Copyright 2021 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n assert,\n bytesToString,\n FormatError,\n info,\n InvalidPDFException,\n warn,\n} from \"../shared/util.js\";\nimport { CIRCULAR_REF, Cmd, Dict, isCmd, Ref, RefSet } from \"./primitives.js\";\nimport { Lexer, Parser } from \"./parser.js\";\nimport {\n MissingDataException,\n ParserEOFException,\n XRefEntryException,\n XRefParseException,\n} from \"./core_utils.js\";\nimport { BaseStream } from \"./base_stream.js\";\nimport { CipherTransformFactory } from \"./crypto.js\";\n\nclass XRef {\n #firstXRefStmPos = null;\n\n constructor(stream, pdfManager) {\n this.stream = stream;\n this.pdfManager = pdfManager;\n this.entries = [];\n this._xrefStms = new Set();\n this._cacheMap = new Map(); // Prepare the XRef cache.\n this._pendingRefs = new RefSet();\n this._newPersistentRefNum = null;\n this._newTemporaryRefNum = null;\n this._persistentRefsCache = null;\n }\n\n getNewPersistentRef(obj) {\n // When printing we don't care that much about the ref number by itself, it\n // can increase for ever and it allows to keep some re-usable refs.\n if (this._newPersistentRefNum === null) {\n this._newPersistentRefNum = this.entries.length || 1;\n }\n const num = this._newPersistentRefNum++;\n this._cacheMap.set(num, obj);\n return Ref.get(num, 0);\n }\n\n getNewTemporaryRef() {\n // When saving we want to have some minimal numbers.\n // Those refs are only created in order to be written in the final pdf\n // stream.\n if (this._newTemporaryRefNum === null) {\n this._newTemporaryRefNum = this.entries.length || 1;\n if (this._newPersistentRefNum) {\n this._persistentRefsCache = new Map();\n for (\n let i = this._newTemporaryRefNum;\n i < this._newPersistentRefNum;\n i++\n ) {\n // We *temporarily* clear the cache, see `resetNewTemporaryRef` below,\n // to avoid any conflict with the refs created during saving.\n this._persistentRefsCache.set(i, this._cacheMap.get(i));\n this._cacheMap.delete(i);\n }\n }\n }\n return Ref.get(this._newTemporaryRefNum++, 0);\n }\n\n resetNewTemporaryRef() {\n // Called once saving is finished.\n this._newTemporaryRefNum = null;\n if (this._persistentRefsCache) {\n for (const [num, obj] of this._persistentRefsCache) {\n this._cacheMap.set(num, obj);\n }\n }\n this._persistentRefsCache = null;\n }\n\n setStartXRef(startXRef) {\n // Store the starting positions of xref tables as we process them\n // so we can recover from missing data errors\n this.startXRefQueue = [startXRef];\n }\n\n parse(recoveryMode = false) {\n let trailerDict;\n if (!recoveryMode) {\n trailerDict = this.readXRef();\n } else {\n warn(\"Indexing all PDF objects\");\n trailerDict = this.indexObjects();\n }\n trailerDict.assignXref(this);\n this.trailer = trailerDict;\n\n let encrypt;\n try {\n encrypt = trailerDict.get(\"Encrypt\");\n } catch (ex) {\n if (ex instanceof MissingDataException) {\n throw ex;\n }\n warn(`XRef.parse - Invalid \"Encrypt\" reference: \"${ex}\".`);\n }\n if (encrypt instanceof Dict) {\n const ids = trailerDict.get(\"ID\");\n const fileId = ids?.length ? ids[0] : \"\";\n // The 'Encrypt' dictionary itself should not be encrypted, and by\n // setting `suppressEncryption` we can prevent an infinite loop inside\n // of `XRef_fetchUncompressed` if the dictionary contains indirect\n // objects (fixes issue7665.pdf).\n encrypt.suppressEncryption = true;\n this.encrypt = new CipherTransformFactory(\n encrypt,\n fileId,\n this.pdfManager.password\n );\n }\n\n // Get the root dictionary (catalog) object, and do some basic validation.\n let root;\n try {\n root = trailerDict.get(\"Root\");\n } catch (ex) {\n if (ex instanceof MissingDataException) {\n throw ex;\n }\n warn(`XRef.parse - Invalid \"Root\" reference: \"${ex}\".`);\n }\n if (root instanceof Dict) {\n try {\n const pages = root.get(\"Pages\");\n if (pages instanceof Dict) {\n this.root = root;\n return;\n }\n } catch (ex) {\n if (ex instanceof MissingDataException) {\n throw ex;\n }\n warn(`XRef.parse - Invalid \"Pages\" reference: \"${ex}\".`);\n }\n }\n\n if (!recoveryMode) {\n throw new XRefParseException();\n }\n // Even recovery failed, there's nothing more we can do here.\n throw new InvalidPDFException(\"Invalid Root reference.\");\n }\n\n processXRefTable(parser) {\n if (!(\"tableState\" in this)) {\n // Stores state of the table as we process it so we can resume\n // from middle of table in case of missing data error\n this.tableState = {\n entryNum: 0,\n streamPos: parser.lexer.stream.pos,\n parserBuf1: parser.buf1,\n parserBuf2: parser.buf2,\n };\n }\n\n const obj = this.readXRefTable(parser);\n\n // Sanity check\n if (!isCmd(obj, \"trailer\")) {\n throw new FormatError(\n \"Invalid XRef table: could not find trailer dictionary\"\n );\n }\n // Read trailer dictionary, e.g.\n // trailer\n // << /Size 22\n // /Root 20R\n // /Info 10R\n // /ID [ <81b14aafa313db63dbd6f981e49f94f4> ]\n // >>\n // The parser goes through the entire stream << ... >> and provides\n // a getter interface for the key-value table\n let dict = parser.getObj();\n\n // The pdflib PDF generator can generate a nested trailer dictionary\n if (!(dict instanceof Dict) && dict.dict) {\n dict = dict.dict;\n }\n if (!(dict instanceof Dict)) {\n throw new FormatError(\n \"Invalid XRef table: could not parse trailer dictionary\"\n );\n }\n delete this.tableState;\n\n return dict;\n }\n\n readXRefTable(parser) {\n // Example of cross-reference table:\n // xref\n // 0 1 <-- subsection header (first obj #, obj count)\n // 0000000000 65535 f <-- actual object (offset, generation #, f/n)\n // 23 2 <-- subsection header ... and so on ...\n // 0000025518 00002 n\n // 0000025635 00000 n\n // trailer\n // ...\n\n const stream = parser.lexer.stream;\n const tableState = this.tableState;\n stream.pos = tableState.streamPos;\n parser.buf1 = tableState.parserBuf1;\n parser.buf2 = tableState.parserBuf2;\n\n // Outer loop is over subsection headers\n let obj;\n\n while (true) {\n if (!(\"firstEntryNum\" in tableState) || !(\"entryCount\" in tableState)) {\n if (isCmd((obj = parser.getObj()), \"trailer\")) {\n break;\n }\n tableState.firstEntryNum = obj;\n tableState.entryCount = parser.getObj();\n }\n\n let first = tableState.firstEntryNum;\n const count = tableState.entryCount;\n if (!Number.isInteger(first) || !Number.isInteger(count)) {\n throw new FormatError(\n \"Invalid XRef table: wrong types in subsection header\"\n );\n }\n // Inner loop is over objects themselves\n for (let i = tableState.entryNum; i < count; i++) {\n tableState.streamPos = stream.pos;\n tableState.entryNum = i;\n tableState.parserBuf1 = parser.buf1;\n tableState.parserBuf2 = parser.buf2;\n\n const entry = {};\n entry.offset = parser.getObj();\n entry.gen = parser.getObj();\n const type = parser.getObj();\n\n if (type instanceof Cmd) {\n switch (type.cmd) {\n case \"f\":\n entry.free = true;\n break;\n case \"n\":\n entry.uncompressed = true;\n break;\n }\n }\n\n // Validate entry obj\n if (\n !Number.isInteger(entry.offset) ||\n !Number.isInteger(entry.gen) ||\n !(entry.free || entry.uncompressed)\n ) {\n throw new FormatError(\n `Invalid entry in XRef subsection: ${first}, ${count}`\n );\n }\n\n // The first xref table entry, i.e. obj 0, should be free. Attempting\n // to adjust an incorrect first obj # (fixes issue 3248 and 7229).\n if (i === 0 && entry.free && first === 1) {\n first = 0;\n }\n\n if (!this.entries[i + first]) {\n this.entries[i + first] = entry;\n }\n }\n\n tableState.entryNum = 0;\n tableState.streamPos = stream.pos;\n tableState.parserBuf1 = parser.buf1;\n tableState.parserBuf2 = parser.buf2;\n delete tableState.firstEntryNum;\n delete tableState.entryCount;\n }\n\n // Sanity check: as per spec, first object must be free\n if (this.entries[0] && !this.entries[0].free) {\n throw new FormatError(\"Invalid XRef table: unexpected first object\");\n }\n return obj;\n }\n\n processXRefStream(stream) {\n if (!(\"streamState\" in this)) {\n // Stores state of the stream as we process it so we can resume\n // from middle of stream in case of missing data error\n const streamParameters = stream.dict;\n const byteWidths = streamParameters.get(\"W\");\n let range = streamParameters.get(\"Index\");\n if (!range) {\n range = [0, streamParameters.get(\"Size\")];\n }\n\n this.streamState = {\n entryRanges: range,\n byteWidths,\n entryNum: 0,\n streamPos: stream.pos,\n };\n }\n this.readXRefStream(stream);\n delete this.streamState;\n\n return stream.dict;\n }\n\n readXRefStream(stream) {\n const streamState = this.streamState;\n stream.pos = streamState.streamPos;\n\n const [typeFieldWidth, offsetFieldWidth, generationFieldWidth] =\n streamState.byteWidths;\n\n const entryRanges = streamState.entryRanges;\n while (entryRanges.length > 0) {\n const [first, n] = entryRanges;\n\n if (!Number.isInteger(first) || !Number.isInteger(n)) {\n throw new FormatError(`Invalid XRef range fields: ${first}, ${n}`);\n }\n if (\n !Number.isInteger(typeFieldWidth) ||\n !Number.isInteger(offsetFieldWidth) ||\n !Number.isInteger(generationFieldWidth)\n ) {\n throw new FormatError(\n `Invalid XRef entry fields length: ${first}, ${n}`\n );\n }\n for (let i = streamState.entryNum; i < n; ++i) {\n streamState.entryNum = i;\n streamState.streamPos = stream.pos;\n\n let type = 0,\n offset = 0,\n generation = 0;\n for (let j = 0; j < typeFieldWidth; ++j) {\n const typeByte = stream.getByte();\n if (typeByte === -1) {\n throw new FormatError(\"Invalid XRef byteWidths 'type'.\");\n }\n type = (type << 8) | typeByte;\n }\n // if type field is absent, its default value is 1\n if (typeFieldWidth === 0) {\n type = 1;\n }\n for (let j = 0; j < offsetFieldWidth; ++j) {\n const offsetByte = stream.getByte();\n if (offsetByte === -1) {\n throw new FormatError(\"Invalid XRef byteWidths 'offset'.\");\n }\n offset = (offset << 8) | offsetByte;\n }\n for (let j = 0; j < generationFieldWidth; ++j) {\n const generationByte = stream.getByte();\n if (generationByte === -1) {\n throw new FormatError(\"Invalid XRef byteWidths 'generation'.\");\n }\n generation = (generation << 8) | generationByte;\n }\n const entry = {};\n entry.offset = offset;\n entry.gen = generation;\n switch (type) {\n case 0:\n entry.free = true;\n break;\n case 1:\n entry.uncompressed = true;\n break;\n case 2:\n break;\n default:\n throw new FormatError(`Invalid XRef entry type: ${type}`);\n }\n if (!this.entries[first + i]) {\n this.entries[first + i] = entry;\n }\n }\n\n streamState.entryNum = 0;\n streamState.streamPos = stream.pos;\n entryRanges.splice(0, 2);\n }\n }\n\n indexObjects() {\n // Simple scan through the PDF content to find objects,\n // trailers and XRef streams.\n const TAB = 0x9,\n LF = 0xa,\n CR = 0xd,\n SPACE = 0x20;\n const PERCENT = 0x25,\n LT = 0x3c;\n\n function readToken(data, offset) {\n let token = \"\",\n ch = data[offset];\n while (ch !== LF && ch !== CR && ch !== LT) {\n if (++offset >= data.length) {\n break;\n }\n token += String.fromCharCode(ch);\n ch = data[offset];\n }\n return token;\n }\n function skipUntil(data, offset, what) {\n const length = what.length,\n dataLength = data.length;\n let skipped = 0;\n // finding byte sequence\n while (offset < dataLength) {\n let i = 0;\n while (i < length && data[offset + i] === what[i]) {\n ++i;\n }\n if (i >= length) {\n break; // sequence found\n }\n offset++;\n skipped++;\n }\n return skipped;\n }\n const gEndobjRegExp = /\\b(endobj|\\d+\\s+\\d+\\s+obj|xref|trailer\\s*<<)\\b/g;\n const gStartxrefRegExp = /\\b(startxref|\\d+\\s+\\d+\\s+obj)\\b/g;\n const objRegExp = /^(\\d+)\\s+(\\d+)\\s+obj\\b/;\n\n const trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]);\n const startxrefBytes = new Uint8Array([\n 115, 116, 97, 114, 116, 120, 114, 101, 102,\n ]);\n const xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);\n\n // Clear out any existing entries, since they may be bogus.\n this.entries.length = 0;\n this._cacheMap.clear();\n\n const stream = this.stream;\n stream.pos = 0;\n const buffer = stream.getBytes(),\n bufferStr = bytesToString(buffer),\n length = buffer.length;\n let position = stream.start;\n const trailers = [],\n xrefStms = [];\n while (position < length) {\n let ch = buffer[position];\n if (ch === TAB || ch === LF || ch === CR || ch === SPACE) {\n ++position;\n continue;\n }\n if (ch === PERCENT) {\n // %-comment\n do {\n ++position;\n if (position >= length) {\n break;\n }\n ch = buffer[position];\n } while (ch !== LF && ch !== CR);\n continue;\n }\n const token = readToken(buffer, position);\n let m;\n if (\n token.startsWith(\"xref\") &&\n (token.length === 4 || /\\s/.test(token[4]))\n ) {\n position += skipUntil(buffer, position, trailerBytes);\n trailers.push(position);\n position += skipUntil(buffer, position, startxrefBytes);\n } else if ((m = objRegExp.exec(token))) {\n const num = m[1] | 0,\n gen = m[2] | 0;\n\n const startPos = position + token.length;\n let contentLength,\n updateEntries = false;\n if (!this.entries[num]) {\n updateEntries = true;\n } else if (this.entries[num].gen === gen) {\n // Before overwriting an existing entry, ensure that the new one won't\n // cause *immediate* errors when it's accessed (fixes issue13783.pdf).\n try {\n const parser = new Parser({\n lexer: new Lexer(stream.makeSubStream(startPos)),\n });\n parser.getObj();\n updateEntries = true;\n } catch (ex) {\n if (ex instanceof ParserEOFException) {\n warn(`indexObjects -- checking object (${token}): \"${ex}\".`);\n } else {\n // The error may come from the `Parser`-instance being initialized\n // without an `XRef`-instance (we don't have a usable one yet).\n updateEntries = true;\n }\n }\n }\n if (updateEntries) {\n this.entries[num] = {\n offset: position - stream.start,\n gen,\n uncompressed: true,\n };\n }\n\n // Find the next \"obj\" string, rather than \"endobj\", to ensure that\n // we won't skip over a new 'obj' operator in corrupt files where\n // 'endobj' operators are missing (fixes issue9105_reduced.pdf).\n gEndobjRegExp.lastIndex = startPos;\n const match = gEndobjRegExp.exec(bufferStr);\n\n if (match) {\n const endPos = gEndobjRegExp.lastIndex + 1;\n contentLength = endPos - position;\n\n if (match[1] !== \"endobj\") {\n warn(\n `indexObjects: Found \"${match[1]}\" inside of another \"obj\", ` +\n 'caused by missing \"endobj\" -- trying to recover.'\n );\n contentLength -= match[1].length + 1;\n }\n } else {\n contentLength = length - position;\n }\n const content = buffer.subarray(position, position + contentLength);\n\n // checking XRef stream suspect\n // (it shall have '/XRef' and next char is not a letter)\n const xrefTagOffset = skipUntil(content, 0, xrefBytes);\n if (xrefTagOffset < contentLength && content[xrefTagOffset + 5] < 64) {\n xrefStms.push(position - stream.start);\n this._xrefStms.add(position - stream.start); // Avoid recursion\n }\n\n position += contentLength;\n } else if (\n token.startsWith(\"trailer\") &&\n (token.length === 7 || /\\s/.test(token[7]))\n ) {\n trailers.push(position);\n\n const startPos = position + token.length;\n let contentLength;\n // Attempt to handle (some) corrupt documents, where no 'startxref'\n // operators are present (fixes issue15590.pdf).\n gStartxrefRegExp.lastIndex = startPos;\n const match = gStartxrefRegExp.exec(bufferStr);\n\n if (match) {\n const endPos = gStartxrefRegExp.lastIndex + 1;\n contentLength = endPos - position;\n\n if (match[1] !== \"startxref\") {\n warn(\n `indexObjects: Found \"${match[1]}\" after \"trailer\", ` +\n 'caused by missing \"startxref\" -- trying to recover.'\n );\n contentLength -= match[1].length + 1;\n }\n } else {\n contentLength = length - position;\n }\n position += contentLength;\n } else {\n position += token.length + 1;\n }\n }\n // reading XRef streams\n for (const xrefStm of xrefStms) {\n this.startXRefQueue.push(xrefStm);\n this.readXRef(/* recoveryMode */ true);\n }\n\n const trailerDicts = [];\n // Pre-parsing the trailers to check if the document is possibly encrypted.\n let isEncrypted = false;\n for (const trailer of trailers) {\n stream.pos = trailer;\n const parser = new Parser({\n lexer: new Lexer(stream),\n xref: this,\n allowStreams: true,\n recoveryMode: true,\n });\n const obj = parser.getObj();\n if (!isCmd(obj, \"trailer\")) {\n continue;\n }\n // read the trailer dictionary\n const dict = parser.getObj();\n if (!(dict instanceof Dict)) {\n continue;\n }\n trailerDicts.push(dict);\n\n if (dict.has(\"Encrypt\")) {\n isEncrypted = true;\n }\n }\n\n // finding main trailer\n let trailerDict, trailerError;\n for (const dict of [...trailerDicts, \"genFallback\", ...trailerDicts]) {\n if (dict === \"genFallback\") {\n if (!trailerError) {\n break; // No need to fallback if there were no validation errors.\n }\n this._generationFallback = true;\n continue;\n }\n // Do some basic validation of the trailer/root dictionary candidate.\n let validPagesDict = false;\n try {\n const rootDict = dict.get(\"Root\");\n if (!(rootDict instanceof Dict)) {\n continue;\n }\n const pagesDict = rootDict.get(\"Pages\");\n if (!(pagesDict instanceof Dict)) {\n continue;\n }\n const pagesCount = pagesDict.get(\"Count\");\n if (Number.isInteger(pagesCount)) {\n validPagesDict = true;\n }\n // The top-level /Pages dictionary isn't obviously corrupt.\n } catch (ex) {\n trailerError = ex;\n continue;\n }\n // taking the first one with 'ID'\n if (\n validPagesDict &&\n (!isEncrypted || dict.has(\"Encrypt\")) &&\n dict.has(\"ID\")\n ) {\n return dict;\n }\n // The current dictionary is a candidate, but continue searching.\n trailerDict = dict;\n }\n // No trailer with 'ID', taking last one (if exists).\n if (trailerDict) {\n return trailerDict;\n }\n // No trailer dictionary found, taking the \"top\"-dictionary (if exists).\n if (this.topDict) {\n return this.topDict;\n }\n // nothing helps\n throw new InvalidPDFException(\"Invalid PDF structure.\");\n }\n\n readXRef(recoveryMode = false) {\n const stream = this.stream;\n // Keep track of already parsed XRef tables, to prevent an infinite loop\n // when parsing corrupt PDF files where e.g. the /Prev entries create a\n // circular dependency between tables (fixes bug1393476.pdf).\n const startXRefParsedCache = new Set();\n\n while (this.startXRefQueue.length) {\n try {\n const startXRef = this.startXRefQueue[0];\n\n if (startXRefParsedCache.has(startXRef)) {\n warn(\"readXRef - skipping XRef table since it was already parsed.\");\n this.startXRefQueue.shift();\n continue;\n }\n startXRefParsedCache.add(startXRef);\n\n stream.pos = startXRef + stream.start;\n\n const parser = new Parser({\n lexer: new Lexer(stream),\n xref: this,\n allowStreams: true,\n });\n let obj = parser.getObj();\n let dict;\n\n // Get dictionary\n if (isCmd(obj, \"xref\")) {\n // Parse end-of-file XRef\n dict = this.processXRefTable(parser);\n if (!this.topDict) {\n this.topDict = dict;\n }\n\n // Recursively get other XRefs 'XRefStm', if any\n obj = dict.get(\"XRefStm\");\n if (Number.isInteger(obj) && !this._xrefStms.has(obj)) {\n // ignore previously loaded xref streams\n // (possible infinite recursion)\n this._xrefStms.add(obj);\n this.startXRefQueue.push(obj);\n this.#firstXRefStmPos ??= obj;\n }\n } else if (Number.isInteger(obj)) {\n // Parse in-stream XRef\n if (\n !Number.isInteger(parser.getObj()) ||\n !isCmd(parser.getObj(), \"obj\") ||\n !((obj = parser.getObj()) instanceof BaseStream)\n ) {\n throw new FormatError(\"Invalid XRef stream\");\n }\n dict = this.processXRefStream(obj);\n if (!this.topDict) {\n this.topDict = dict;\n }\n if (!dict) {\n throw new FormatError(\"Failed to read XRef stream\");\n }\n } else {\n throw new FormatError(\"Invalid XRef stream header\");\n }\n\n // Recursively get previous dictionary, if any\n obj = dict.get(\"Prev\");\n if (Number.isInteger(obj)) {\n this.startXRefQueue.push(obj);\n } else if (obj instanceof Ref) {\n // The spec says Prev must not be a reference, i.e. \"/Prev NNN\"\n // This is a fallback for non-compliant PDFs, i.e. \"/Prev NNN 0 R\"\n this.startXRefQueue.push(obj.num);\n }\n } catch (e) {\n if (e instanceof MissingDataException) {\n throw e;\n }\n info(\"(while reading XRef): \" + e);\n }\n this.startXRefQueue.shift();\n }\n\n if (this.topDict) {\n return this.topDict;\n }\n if (recoveryMode) {\n return undefined;\n }\n throw new XRefParseException();\n }\n\n get lastXRefStreamPos() {\n return (\n this.#firstXRefStmPos ??\n (this._xrefStms.size > 0 ? Math.max(...this._xrefStms) : null)\n );\n }\n\n getEntry(i) {\n const xrefEntry = this.entries[i];\n if (xrefEntry && !xrefEntry.free && xrefEntry.offset) {\n return xrefEntry;\n }\n return null;\n }\n\n fetchIfRef(obj, suppressEncryption = false) {\n if (obj instanceof Ref) {\n return this.fetch(obj, suppressEncryption);\n }\n return obj;\n }\n\n fetch(ref, suppressEncryption = false) {\n if (!(ref instanceof Ref)) {\n throw new Error(\"ref object is not a reference\");\n }\n const num = ref.num;\n\n // The XRef cache is populated with objects which are obtained through\n // `Parser.getObj`, and indirectly via `Lexer.getObj`. Neither of these\n // methods should ever return `undefined` (note the `assert` calls below).\n const cacheEntry = this._cacheMap.get(num);\n if (cacheEntry !== undefined) {\n // In documents with Object Streams, it's possible that cached `Dict`s\n // have not been assigned an `objId` yet (see e.g. issue3115r.pdf).\n if (cacheEntry instanceof Dict && !cacheEntry.objId) {\n cacheEntry.objId = ref.toString();\n }\n return cacheEntry;\n }\n let xrefEntry = this.getEntry(num);\n\n if (xrefEntry === null) {\n // The referenced entry can be free.\n this._cacheMap.set(num, xrefEntry);\n return xrefEntry;\n }\n // Prevent circular references, in corrupt PDF documents, from hanging the\n // worker-thread. This relies, implicitly, on the parsing being synchronous.\n if (this._pendingRefs.has(ref)) {\n this._pendingRefs.remove(ref);\n\n warn(`Ignoring circular reference: ${ref}.`);\n return CIRCULAR_REF;\n }\n this._pendingRefs.put(ref);\n\n try {\n xrefEntry = xrefEntry.uncompressed\n ? this.fetchUncompressed(ref, xrefEntry, suppressEncryption)\n : this.fetchCompressed(ref, xrefEntry, suppressEncryption);\n this._pendingRefs.remove(ref);\n } catch (ex) {\n this._pendingRefs.remove(ref);\n throw ex;\n }\n if (xrefEntry instanceof Dict) {\n xrefEntry.objId = ref.toString();\n } else if (xrefEntry instanceof BaseStream) {\n xrefEntry.dict.objId = ref.toString();\n }\n return xrefEntry;\n }\n\n fetchUncompressed(ref, xrefEntry, suppressEncryption = false) {\n const gen = ref.gen;\n let num = ref.num;\n if (xrefEntry.gen !== gen) {\n const msg = `Inconsistent generation in XRef: ${ref}`;\n // Try falling back to a *previous* generation (fixes issue15577.pdf).\n if (this._generationFallback && xrefEntry.gen < gen) {\n warn(msg);\n return this.fetchUncompressed(\n Ref.get(num, xrefEntry.gen),\n xrefEntry,\n suppressEncryption\n );\n }\n throw new XRefEntryException(msg);\n }\n const stream = this.stream.makeSubStream(\n xrefEntry.offset + this.stream.start\n );\n const parser = new Parser({\n lexer: new Lexer(stream),\n xref: this,\n allowStreams: true,\n });\n const obj1 = parser.getObj();\n const obj2 = parser.getObj();\n const obj3 = parser.getObj();\n\n if (obj1 !== num || obj2 !== gen || !(obj3 instanceof Cmd)) {\n throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);\n }\n if (obj3.cmd !== \"obj\") {\n // some bad PDFs use \"obj1234\" and really mean 1234\n if (obj3.cmd.startsWith(\"obj\")) {\n num = parseInt(obj3.cmd.substring(3), 10);\n if (!Number.isNaN(num)) {\n return num;\n }\n }\n throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);\n }\n xrefEntry =\n this.encrypt && !suppressEncryption\n ? parser.getObj(this.encrypt.createCipherTransform(num, gen))\n : parser.getObj();\n if (!(xrefEntry instanceof BaseStream)) {\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(\n xrefEntry !== undefined,\n 'fetchUncompressed: The \"xrefEntry\" cannot be undefined.'\n );\n }\n this._cacheMap.set(num, xrefEntry);\n }\n return xrefEntry;\n }\n\n fetchCompressed(ref, xrefEntry, suppressEncryption = false) {\n const tableOffset = xrefEntry.offset;\n const stream = this.fetch(Ref.get(tableOffset, 0));\n if (!(stream instanceof BaseStream)) {\n throw new FormatError(\"bad ObjStm stream\");\n }\n const first = stream.dict.get(\"First\");\n const n = stream.dict.get(\"N\");\n if (!Number.isInteger(first) || !Number.isInteger(n)) {\n throw new FormatError(\"invalid first and n parameters for ObjStm stream\");\n }\n let parser = new Parser({\n lexer: new Lexer(stream),\n xref: this,\n allowStreams: true,\n });\n const nums = new Array(n);\n const offsets = new Array(n);\n // read the object numbers to populate cache\n for (let i = 0; i < n; ++i) {\n const num = parser.getObj();\n if (!Number.isInteger(num)) {\n throw new FormatError(\n `invalid object number in the ObjStm stream: ${num}`\n );\n }\n const offset = parser.getObj();\n if (!Number.isInteger(offset)) {\n throw new FormatError(\n `invalid object offset in the ObjStm stream: ${offset}`\n );\n }\n nums[i] = num;\n offsets[i] = offset;\n }\n\n const start = (stream.start || 0) + first;\n const entries = new Array(n);\n // read stream objects for cache\n for (let i = 0; i < n; ++i) {\n const length = i < n - 1 ? offsets[i + 1] - offsets[i] : undefined;\n if (length < 0) {\n throw new FormatError(\"Invalid offset in the ObjStm stream.\");\n }\n parser = new Parser({\n lexer: new Lexer(\n stream.makeSubStream(start + offsets[i], length, stream.dict)\n ),\n xref: this,\n allowStreams: true,\n });\n\n const obj = parser.getObj();\n entries[i] = obj;\n if (obj instanceof BaseStream) {\n continue;\n }\n const num = nums[i],\n entry = this.entries[num];\n if (entry && entry.offset === tableOffset && entry.gen === i) {\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(\n obj !== undefined,\n 'fetchCompressed: The \"obj\" cannot be undefined.'\n );\n }\n this._cacheMap.set(num, obj);\n }\n }\n xrefEntry = entries[xrefEntry.gen];\n if (xrefEntry === undefined) {\n throw new XRefEntryException(`Bad (compressed) XRef entry: ${ref}`);\n }\n return xrefEntry;\n }\n\n async fetchIfRefAsync(obj, suppressEncryption) {\n if (obj instanceof Ref) {\n return this.fetchAsync(obj, suppressEncryption);\n }\n return obj;\n }\n\n async fetchAsync(ref, suppressEncryption) {\n try {\n return this.fetch(ref, suppressEncryption);\n } catch (ex) {\n if (!(ex instanceof MissingDataException)) {\n throw ex;\n }\n await this.pdfManager.requestRange(ex.begin, ex.end);\n return this.fetchAsync(ref, suppressEncryption);\n }\n }\n\n getCatalogObj() {\n return this.root;\n }\n}\n\nexport { XRef };\n","/* Copyright 2012 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AnnotationEditorPrefix,\n assert,\n FormatError,\n info,\n InvalidPDFException,\n isArrayEqual,\n PageActionEventType,\n RenderingIntentFlag,\n shadow,\n stringToBytes,\n stringToPDFString,\n stringToUTF8String,\n unreachable,\n Util,\n warn,\n} from \"../shared/util.js\";\nimport {\n AnnotationFactory,\n PopupAnnotation,\n WidgetAnnotation,\n} from \"./annotation.js\";\nimport {\n collectActions,\n getInheritableProperty,\n getNewAnnotationsMap,\n isWhiteSpace,\n lookupNormalRect,\n MissingDataException,\n PDF_VERSION_REGEXP,\n validateCSSFont,\n XRefEntryException,\n XRefParseException,\n} from \"./core_utils.js\";\nimport {\n Dict,\n isName,\n isRefsEqual,\n Name,\n Ref,\n RefSet,\n RefSetCache,\n} from \"./primitives.js\";\nimport { getXfaFontDict, getXfaFontName } from \"./xfa_fonts.js\";\nimport { BaseStream } from \"./base_stream.js\";\nimport { calculateMD5 } from \"./crypto.js\";\nimport { Catalog } from \"./catalog.js\";\nimport { clearGlobalCaches } from \"./cleanup_helper.js\";\nimport { DatasetReader } from \"./dataset_reader.js\";\nimport { Linearization } from \"./parser.js\";\nimport { NullStream } from \"./stream.js\";\nimport { ObjectLoader } from \"./object_loader.js\";\nimport { OperatorList } from \"./operator_list.js\";\nimport { PartialEvaluator } from \"./evaluator.js\";\nimport { StreamsSequenceStream } from \"./decode_stream.js\";\nimport { StructTreePage } from \"./struct_tree.js\";\nimport { writeObject } from \"./writer.js\";\nimport { XFAFactory } from \"./xfa/factory.js\";\nimport { XRef } from \"./xref.js\";\n\nconst DEFAULT_USER_UNIT = 1.0;\nconst LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];\n\nclass Page {\n constructor({\n pdfManager,\n xref,\n pageIndex,\n pageDict,\n ref,\n globalIdFactory,\n fontCache,\n builtInCMapCache,\n standardFontDataCache,\n globalImageCache,\n systemFontCache,\n nonBlendModesSet,\n xfaFactory,\n }) {\n this.pdfManager = pdfManager;\n this.pageIndex = pageIndex;\n this.pageDict = pageDict;\n this.xref = xref;\n this.ref = ref;\n this.fontCache = fontCache;\n this.builtInCMapCache = builtInCMapCache;\n this.standardFontDataCache = standardFontDataCache;\n this.globalImageCache = globalImageCache;\n this.systemFontCache = systemFontCache;\n this.nonBlendModesSet = nonBlendModesSet;\n this.evaluatorOptions = pdfManager.evaluatorOptions;\n this.resourcesPromise = null;\n this.xfaFactory = xfaFactory;\n\n const idCounters = {\n obj: 0,\n };\n this._localIdFactory = class extends globalIdFactory {\n static createObjId() {\n return `p${pageIndex}_${++idCounters.obj}`;\n }\n\n static getPageObjId() {\n return `p${ref.toString()}`;\n }\n };\n }\n\n /**\n * @private\n */\n _getInheritableProperty(key, getArray = false) {\n const value = getInheritableProperty({\n dict: this.pageDict,\n key,\n getArray,\n stopWhenFound: false,\n });\n if (!Array.isArray(value)) {\n return value;\n }\n if (value.length === 1 || !(value[0] instanceof Dict)) {\n return value[0];\n }\n return Dict.merge({ xref: this.xref, dictArray: value });\n }\n\n get content() {\n return this.pageDict.getArray(\"Contents\");\n }\n\n get resources() {\n // For robustness: The spec states that a \\Resources entry has to be\n // present, but can be empty. Some documents still omit it; in this case\n // we return an empty dictionary.\n const resources = this._getInheritableProperty(\"Resources\");\n\n return shadow(\n this,\n \"resources\",\n resources instanceof Dict ? resources : Dict.empty\n );\n }\n\n _getBoundingBox(name) {\n if (this.xfaData) {\n return this.xfaData.bbox;\n }\n const box = lookupNormalRect(\n this._getInheritableProperty(name, /* getArray = */ true),\n null\n );\n\n if (box) {\n if (box[2] - box[0] > 0 && box[3] - box[1] > 0) {\n return box;\n }\n warn(`Empty, or invalid, /${name} entry.`);\n }\n return null;\n }\n\n get mediaBox() {\n // Reset invalid media box to letter size.\n return shadow(\n this,\n \"mediaBox\",\n this._getBoundingBox(\"MediaBox\") || LETTER_SIZE_MEDIABOX\n );\n }\n\n get cropBox() {\n // Reset invalid crop box to media box.\n return shadow(\n this,\n \"cropBox\",\n this._getBoundingBox(\"CropBox\") || this.mediaBox\n );\n }\n\n get userUnit() {\n let obj = this.pageDict.get(\"UserUnit\");\n if (typeof obj !== \"number\" || obj <= 0) {\n obj = DEFAULT_USER_UNIT;\n }\n return shadow(this, \"userUnit\", obj);\n }\n\n get view() {\n // From the spec, 6th ed., p.963:\n // \"The crop, bleed, trim, and art boxes should not ordinarily\n // extend beyond the boundaries of the media box. If they do, they are\n // effectively reduced to their intersection with the media box.\"\n const { cropBox, mediaBox } = this;\n\n if (cropBox !== mediaBox && !isArrayEqual(cropBox, mediaBox)) {\n const box = Util.intersect(cropBox, mediaBox);\n if (box && box[2] - box[0] > 0 && box[3] - box[1] > 0) {\n return shadow(this, \"view\", box);\n }\n warn(\"Empty /CropBox and /MediaBox intersection.\");\n }\n return shadow(this, \"view\", mediaBox);\n }\n\n get rotate() {\n let rotate = this._getInheritableProperty(\"Rotate\") || 0;\n\n // Normalize rotation so it's a multiple of 90 and between 0 and 270.\n if (rotate % 90 !== 0) {\n rotate = 0;\n } else if (rotate >= 360) {\n rotate %= 360;\n } else if (rotate < 0) {\n // The spec doesn't cover negatives. Assume it's counterclockwise\n // rotation. The following is the other implementation of modulo.\n rotate = ((rotate % 360) + 360) % 360;\n }\n return shadow(this, \"rotate\", rotate);\n }\n\n /**\n * @private\n */\n _onSubStreamError(reason, objId) {\n if (this.evaluatorOptions.ignoreErrors) {\n warn(`getContentStream - ignoring sub-stream (${objId}): \"${reason}\".`);\n return;\n }\n throw reason;\n }\n\n /**\n * @returns {Promise}\n */\n getContentStream() {\n return this.pdfManager.ensure(this, \"content\").then(content => {\n if (content instanceof BaseStream) {\n return content;\n }\n if (Array.isArray(content)) {\n return new StreamsSequenceStream(\n content,\n this._onSubStreamError.bind(this)\n );\n }\n // Replace non-existent page content with empty content.\n return new NullStream();\n });\n }\n\n get xfaData() {\n return shadow(\n this,\n \"xfaData\",\n this.xfaFactory\n ? { bbox: this.xfaFactory.getBoundingBox(this.pageIndex) }\n : null\n );\n }\n\n #replaceIdByRef(annotations, deletedAnnotations, existingAnnotations) {\n for (const annotation of annotations) {\n if (annotation.id) {\n const ref = Ref.fromString(annotation.id);\n if (!ref) {\n warn(`A non-linked annotation cannot be modified: ${annotation.id}`);\n continue;\n }\n if (annotation.deleted) {\n deletedAnnotations.put(ref, ref);\n continue;\n }\n existingAnnotations?.put(ref);\n annotation.ref = ref;\n delete annotation.id;\n }\n }\n }\n\n async saveNewAnnotations(handler, task, annotations, imagePromises) {\n if (this.xfaFactory) {\n throw new Error(\"XFA: Cannot save new annotations.\");\n }\n\n const partialEvaluator = new PartialEvaluator({\n xref: this.xref,\n handler,\n pageIndex: this.pageIndex,\n idFactory: this._localIdFactory,\n fontCache: this.fontCache,\n builtInCMapCache: this.builtInCMapCache,\n standardFontDataCache: this.standardFontDataCache,\n globalImageCache: this.globalImageCache,\n systemFontCache: this.systemFontCache,\n options: this.evaluatorOptions,\n });\n\n const deletedAnnotations = new RefSetCache();\n const existingAnnotations = new RefSet();\n this.#replaceIdByRef(annotations, deletedAnnotations, existingAnnotations);\n\n const pageDict = this.pageDict;\n const annotationsArray = this.annotations.filter(\n a => !(a instanceof Ref && deletedAnnotations.has(a))\n );\n const newData = await AnnotationFactory.saveNewAnnotations(\n partialEvaluator,\n task,\n annotations,\n imagePromises\n );\n\n for (const { ref } of newData.annotations) {\n // Don't add an existing annotation ref to the annotations array.\n if (ref instanceof Ref && !existingAnnotations.has(ref)) {\n annotationsArray.push(ref);\n }\n }\n\n const savedDict = pageDict.get(\"Annots\");\n pageDict.set(\"Annots\", annotationsArray);\n const buffer = [];\n await writeObject(this.ref, pageDict, buffer, this.xref);\n if (savedDict) {\n pageDict.set(\"Annots\", savedDict);\n }\n\n const objects = newData.dependencies;\n objects.push(\n { ref: this.ref, data: buffer.join(\"\") },\n ...newData.annotations\n );\n for (const deletedRef of deletedAnnotations) {\n objects.push({ ref: deletedRef, data: null });\n }\n\n return objects;\n }\n\n save(handler, task, annotationStorage) {\n const partialEvaluator = new PartialEvaluator({\n xref: this.xref,\n handler,\n pageIndex: this.pageIndex,\n idFactory: this._localIdFactory,\n fontCache: this.fontCache,\n builtInCMapCache: this.builtInCMapCache,\n standardFontDataCache: this.standardFontDataCache,\n globalImageCache: this.globalImageCache,\n systemFontCache: this.systemFontCache,\n options: this.evaluatorOptions,\n });\n\n // Fetch the page's annotations and save the content\n // in case of interactive form fields.\n return this._parsedAnnotations.then(function (annotations) {\n const newRefsPromises = [];\n for (const annotation of annotations) {\n if (!annotation.mustBePrinted(annotationStorage)) {\n continue;\n }\n newRefsPromises.push(\n annotation\n .save(partialEvaluator, task, annotationStorage)\n .catch(function (reason) {\n warn(\n \"save - ignoring annotation data during \" +\n `\"${task.name}\" task: \"${reason}\".`\n );\n return null;\n })\n );\n }\n\n return Promise.all(newRefsPromises).then(function (newRefs) {\n return newRefs.filter(newRef => !!newRef);\n });\n });\n }\n\n loadResources(keys) {\n // TODO: add async `_getInheritableProperty` and remove this.\n this.resourcesPromise ||= this.pdfManager.ensure(this, \"resources\");\n\n return this.resourcesPromise.then(() => {\n const objectLoader = new ObjectLoader(this.resources, keys, this.xref);\n return objectLoader.load();\n });\n }\n\n getOperatorList({\n handler,\n sink,\n task,\n intent,\n cacheKey,\n annotationStorage = null,\n }) {\n const contentStreamPromise = this.getContentStream();\n const resourcesPromise = this.loadResources([\n \"ColorSpace\",\n \"ExtGState\",\n \"Font\",\n \"Pattern\",\n \"Properties\",\n \"Shading\",\n \"XObject\",\n ]);\n\n const partialEvaluator = new PartialEvaluator({\n xref: this.xref,\n handler,\n pageIndex: this.pageIndex,\n idFactory: this._localIdFactory,\n fontCache: this.fontCache,\n builtInCMapCache: this.builtInCMapCache,\n standardFontDataCache: this.standardFontDataCache,\n globalImageCache: this.globalImageCache,\n systemFontCache: this.systemFontCache,\n options: this.evaluatorOptions,\n });\n\n const newAnnotsByPage = !this.xfaFactory\n ? getNewAnnotationsMap(annotationStorage)\n : null;\n const newAnnots = newAnnotsByPage?.get(this.pageIndex);\n let newAnnotationsPromise = Promise.resolve(null);\n let deletedAnnotations = null;\n\n if (newAnnots) {\n const annotationGlobalsPromise =\n this.pdfManager.ensureDoc(\"annotationGlobals\");\n let imagePromises;\n\n // An annotation can contain a reference to a bitmap, but this bitmap\n // is defined in another annotation. So we need to find this annotation\n // and generate the bitmap.\n const missingBitmaps = new Set();\n for (const { bitmapId, bitmap } of newAnnots) {\n if (bitmapId && !bitmap && !missingBitmaps.has(bitmapId)) {\n missingBitmaps.add(bitmapId);\n }\n }\n\n const { isOffscreenCanvasSupported } = this.evaluatorOptions;\n if (missingBitmaps.size > 0) {\n const annotationWithBitmaps = newAnnots.slice();\n for (const [key, annotation] of annotationStorage) {\n if (!key.startsWith(AnnotationEditorPrefix)) {\n continue;\n }\n if (annotation.bitmap && missingBitmaps.has(annotation.bitmapId)) {\n annotationWithBitmaps.push(annotation);\n }\n }\n // The array annotationWithBitmaps cannot be empty: the check above\n // makes sure to have at least one annotation containing the bitmap.\n imagePromises = AnnotationFactory.generateImages(\n annotationWithBitmaps,\n this.xref,\n isOffscreenCanvasSupported\n );\n } else {\n imagePromises = AnnotationFactory.generateImages(\n newAnnots,\n this.xref,\n isOffscreenCanvasSupported\n );\n }\n\n deletedAnnotations = new RefSet();\n this.#replaceIdByRef(newAnnots, deletedAnnotations, null);\n\n newAnnotationsPromise = annotationGlobalsPromise.then(\n annotationGlobals => {\n if (!annotationGlobals) {\n return null;\n }\n\n return AnnotationFactory.printNewAnnotations(\n annotationGlobals,\n partialEvaluator,\n task,\n newAnnots,\n imagePromises\n );\n }\n );\n }\n\n const pageListPromise = Promise.all([\n contentStreamPromise,\n resourcesPromise,\n ]).then(([contentStream]) => {\n const opList = new OperatorList(intent, sink);\n\n handler.send(\"StartRenderPage\", {\n transparency: partialEvaluator.hasBlendModes(\n this.resources,\n this.nonBlendModesSet\n ),\n pageIndex: this.pageIndex,\n cacheKey,\n });\n\n return partialEvaluator\n .getOperatorList({\n stream: contentStream,\n task,\n resources: this.resources,\n operatorList: opList,\n })\n .then(function () {\n return opList;\n });\n });\n\n // Fetch the page's annotations and add their operator lists to the\n // page's operator list to render them.\n return Promise.all([\n pageListPromise,\n this._parsedAnnotations,\n newAnnotationsPromise,\n ]).then(function ([pageOpList, annotations, newAnnotations]) {\n if (newAnnotations) {\n // Some annotations can already exist (if it has the refToReplace\n // property). In this case, we replace the old annotation by the new\n // one.\n annotations = annotations.filter(\n a => !(a.ref && deletedAnnotations.has(a.ref))\n );\n for (let i = 0, ii = newAnnotations.length; i < ii; i++) {\n const newAnnotation = newAnnotations[i];\n if (newAnnotation.refToReplace) {\n const j = annotations.findIndex(\n a => a.ref && isRefsEqual(a.ref, newAnnotation.refToReplace)\n );\n if (j >= 0) {\n annotations.splice(j, 1, newAnnotation);\n newAnnotations.splice(i--, 1);\n ii--;\n }\n }\n }\n annotations = annotations.concat(newAnnotations);\n }\n if (\n annotations.length === 0 ||\n intent & RenderingIntentFlag.ANNOTATIONS_DISABLE\n ) {\n pageOpList.flush(/* lastChunk = */ true);\n return { length: pageOpList.totalLength };\n }\n const renderForms = !!(intent & RenderingIntentFlag.ANNOTATIONS_FORMS),\n intentAny = !!(intent & RenderingIntentFlag.ANY),\n intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),\n intentPrint = !!(intent & RenderingIntentFlag.PRINT);\n\n // Collect the operator list promises for the annotations. Each promise\n // is resolved with the complete operator list for a single annotation.\n const opListPromises = [];\n for (const annotation of annotations) {\n if (\n intentAny ||\n (intentDisplay &&\n annotation.mustBeViewed(annotationStorage, renderForms)) ||\n (intentPrint && annotation.mustBePrinted(annotationStorage))\n ) {\n opListPromises.push(\n annotation\n .getOperatorList(\n partialEvaluator,\n task,\n intent,\n renderForms,\n annotationStorage\n )\n .catch(function (reason) {\n warn(\n \"getOperatorList - ignoring annotation data during \" +\n `\"${task.name}\" task: \"${reason}\".`\n );\n return {\n opList: null,\n separateForm: false,\n separateCanvas: false,\n };\n })\n );\n }\n }\n\n return Promise.all(opListPromises).then(function (opLists) {\n let form = false,\n canvas = false;\n\n for (const { opList, separateForm, separateCanvas } of opLists) {\n pageOpList.addOpList(opList);\n\n form ||= separateForm;\n canvas ||= separateCanvas;\n }\n pageOpList.flush(\n /* lastChunk = */ true,\n /* separateAnnots = */ { form, canvas }\n );\n return { length: pageOpList.totalLength };\n });\n });\n }\n\n async extractTextContent({\n handler,\n task,\n includeMarkedContent,\n disableNormalization,\n sink,\n }) {\n const contentStreamPromise = this.getContentStream();\n const resourcesPromise = this.loadResources([\n \"ExtGState\",\n \"Font\",\n \"Properties\",\n \"XObject\",\n ]);\n const langPromise = this.pdfManager.ensureCatalog(\"lang\");\n\n const [contentStream, , lang] = await Promise.all([\n contentStreamPromise,\n resourcesPromise,\n langPromise,\n ]);\n const partialEvaluator = new PartialEvaluator({\n xref: this.xref,\n handler,\n pageIndex: this.pageIndex,\n idFactory: this._localIdFactory,\n fontCache: this.fontCache,\n builtInCMapCache: this.builtInCMapCache,\n standardFontDataCache: this.standardFontDataCache,\n globalImageCache: this.globalImageCache,\n systemFontCache: this.systemFontCache,\n options: this.evaluatorOptions,\n });\n\n return partialEvaluator.getTextContent({\n stream: contentStream,\n task,\n resources: this.resources,\n includeMarkedContent,\n disableNormalization,\n sink,\n viewBox: this.view,\n lang,\n });\n }\n\n async getStructTree() {\n const structTreeRoot =\n await this.pdfManager.ensureCatalog(\"structTreeRoot\");\n if (!structTreeRoot) {\n return null;\n }\n // Ensure that the structTree will contain the page's annotations.\n await this._parsedAnnotations;\n\n const structTree = await this.pdfManager.ensure(this, \"_parseStructTree\", [\n structTreeRoot,\n ]);\n return structTree.serializable;\n }\n\n /**\n * @private\n */\n _parseStructTree(structTreeRoot) {\n const tree = new StructTreePage(structTreeRoot, this.pageDict);\n tree.parse(this.ref);\n return tree;\n }\n\n async getAnnotationsData(handler, task, intent) {\n const annotations = await this._parsedAnnotations;\n if (annotations.length === 0) {\n return annotations;\n }\n\n const annotationsData = [],\n textContentPromises = [];\n let partialEvaluator;\n\n const intentAny = !!(intent & RenderingIntentFlag.ANY),\n intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),\n intentPrint = !!(intent & RenderingIntentFlag.PRINT);\n\n for (const annotation of annotations) {\n // Get the annotation even if it's hidden because\n // JS can change its display.\n const isVisible = intentAny || (intentDisplay && annotation.viewable);\n if (isVisible || (intentPrint && annotation.printable)) {\n annotationsData.push(annotation.data);\n }\n\n if (annotation.hasTextContent && isVisible) {\n partialEvaluator ||= new PartialEvaluator({\n xref: this.xref,\n handler,\n pageIndex: this.pageIndex,\n idFactory: this._localIdFactory,\n fontCache: this.fontCache,\n builtInCMapCache: this.builtInCMapCache,\n standardFontDataCache: this.standardFontDataCache,\n globalImageCache: this.globalImageCache,\n systemFontCache: this.systemFontCache,\n options: this.evaluatorOptions,\n });\n\n textContentPromises.push(\n annotation\n .extractTextContent(partialEvaluator, task, [\n -Infinity,\n -Infinity,\n Infinity,\n Infinity,\n ])\n .catch(function (reason) {\n warn(\n `getAnnotationsData - ignoring textContent during \"${task.name}\" task: \"${reason}\".`\n );\n })\n );\n }\n }\n\n await Promise.all(textContentPromises);\n return annotationsData;\n }\n\n get annotations() {\n const annots = this._getInheritableProperty(\"Annots\");\n return shadow(this, \"annotations\", Array.isArray(annots) ? annots : []);\n }\n\n get _parsedAnnotations() {\n const promise = this.pdfManager\n .ensure(this, \"annotations\")\n .then(async annots => {\n if (annots.length === 0) {\n return annots;\n }\n const annotationGlobals =\n await this.pdfManager.ensureDoc(\"annotationGlobals\");\n if (!annotationGlobals) {\n return [];\n }\n\n const annotationPromises = [];\n for (const annotationRef of annots) {\n annotationPromises.push(\n AnnotationFactory.create(\n this.xref,\n annotationRef,\n annotationGlobals,\n this._localIdFactory,\n /* collectFields */ false,\n this.ref\n ).catch(function (reason) {\n warn(`_parsedAnnotations: \"${reason}\".`);\n return null;\n })\n );\n }\n\n const sortedAnnotations = [];\n let popupAnnotations, widgetAnnotations;\n // Ensure that PopupAnnotations are handled last, since they depend on\n // their parent Annotation in the display layer; fixes issue 11362.\n for (const annotation of await Promise.all(annotationPromises)) {\n if (!annotation) {\n continue;\n }\n if (annotation instanceof WidgetAnnotation) {\n (widgetAnnotations ||= []).push(annotation);\n continue;\n }\n if (annotation instanceof PopupAnnotation) {\n (popupAnnotations ||= []).push(annotation);\n continue;\n }\n sortedAnnotations.push(annotation);\n }\n if (widgetAnnotations) {\n sortedAnnotations.push(...widgetAnnotations);\n }\n if (popupAnnotations) {\n sortedAnnotations.push(...popupAnnotations);\n }\n\n return sortedAnnotations;\n });\n\n return shadow(this, \"_parsedAnnotations\", promise);\n }\n\n get jsActions() {\n const actions = collectActions(\n this.xref,\n this.pageDict,\n PageActionEventType\n );\n return shadow(this, \"jsActions\", actions);\n }\n}\n\nconst PDF_HEADER_SIGNATURE = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d]);\nconst STARTXREF_SIGNATURE = new Uint8Array([\n 0x73, 0x74, 0x61, 0x72, 0x74, 0x78, 0x72, 0x65, 0x66,\n]);\nconst ENDOBJ_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a]);\n\nconst FINGERPRINT_FIRST_BYTES = 1024;\nconst EMPTY_FINGERPRINT =\n \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\";\n\nfunction find(stream, signature, limit = 1024, backwards = false) {\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(limit > 0, 'The \"limit\" must be a positive integer.');\n }\n const signatureLength = signature.length;\n\n const scanBytes = stream.peekBytes(limit);\n const scanLength = scanBytes.length - signatureLength;\n\n if (scanLength <= 0) {\n return false;\n }\n if (backwards) {\n const signatureEnd = signatureLength - 1;\n\n let pos = scanBytes.length - 1;\n while (pos >= signatureEnd) {\n let j = 0;\n while (\n j < signatureLength &&\n scanBytes[pos - j] === signature[signatureEnd - j]\n ) {\n j++;\n }\n if (j >= signatureLength) {\n // `signature` found.\n stream.pos += pos - signatureEnd;\n return true;\n }\n pos--;\n }\n } else {\n // forwards\n let pos = 0;\n while (pos <= scanLength) {\n let j = 0;\n while (j < signatureLength && scanBytes[pos + j] === signature[j]) {\n j++;\n }\n if (j >= signatureLength) {\n // `signature` found.\n stream.pos += pos;\n return true;\n }\n pos++;\n }\n }\n return false;\n}\n\n/**\n * The `PDFDocument` class holds all the (worker-thread) data of the PDF file.\n */\nclass PDFDocument {\n constructor(pdfManager, stream) {\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(\n stream instanceof BaseStream,\n 'PDFDocument: Invalid \"stream\" argument.'\n );\n }\n if (stream.length <= 0) {\n throw new InvalidPDFException(\n \"The PDF file is empty, i.e. its size is zero bytes.\"\n );\n }\n\n this.pdfManager = pdfManager;\n this.stream = stream;\n this.xref = new XRef(stream, pdfManager);\n this._pagePromises = new Map();\n this._version = null;\n\n const idCounters = {\n font: 0,\n };\n this._globalIdFactory = class {\n static getDocId() {\n return `g_${pdfManager.docId}`;\n }\n\n static createFontId() {\n return `f${++idCounters.font}`;\n }\n\n static createObjId() {\n unreachable(\"Abstract method `createObjId` called.\");\n }\n\n static getPageObjId() {\n unreachable(\"Abstract method `getPageObjId` called.\");\n }\n };\n }\n\n parse(recoveryMode) {\n this.xref.parse(recoveryMode);\n this.catalog = new Catalog(this.pdfManager, this.xref);\n }\n\n get linearization() {\n let linearization = null;\n try {\n linearization = Linearization.create(this.stream);\n } catch (err) {\n if (err instanceof MissingDataException) {\n throw err;\n }\n info(err);\n }\n return shadow(this, \"linearization\", linearization);\n }\n\n get startXRef() {\n const stream = this.stream;\n let startXRef = 0;\n\n if (this.linearization) {\n // Find the end of the first object.\n stream.reset();\n if (find(stream, ENDOBJ_SIGNATURE)) {\n stream.skip(6);\n\n let ch = stream.peekByte();\n while (isWhiteSpace(ch)) {\n stream.pos++;\n ch = stream.peekByte();\n }\n startXRef = stream.pos - stream.start;\n }\n } else {\n // Find `startxref` by checking backwards from the end of the file.\n const step = 1024;\n const startXRefLength = STARTXREF_SIGNATURE.length;\n let found = false,\n pos = stream.end;\n\n while (!found && pos > 0) {\n pos -= step - startXRefLength;\n if (pos < 0) {\n pos = 0;\n }\n stream.pos = pos;\n found = find(stream, STARTXREF_SIGNATURE, step, true);\n }\n\n if (found) {\n stream.skip(9);\n let ch;\n do {\n ch = stream.getByte();\n } while (isWhiteSpace(ch));\n let str = \"\";\n while (ch >= /* Space = */ 0x20 && ch <= /* '9' = */ 0x39) {\n str += String.fromCharCode(ch);\n ch = stream.getByte();\n }\n startXRef = parseInt(str, 10);\n if (isNaN(startXRef)) {\n startXRef = 0;\n }\n }\n }\n return shadow(this, \"startXRef\", startXRef);\n }\n\n // Find the header, get the PDF format version and setup the\n // stream to start from the header.\n checkHeader() {\n const stream = this.stream;\n stream.reset();\n\n if (!find(stream, PDF_HEADER_SIGNATURE)) {\n // May not be a PDF file, but don't throw an error and let\n // parsing continue.\n return;\n }\n stream.moveStart();\n\n // Skip over the \"%PDF-\" prefix, since it was found above.\n stream.skip(PDF_HEADER_SIGNATURE.length);\n // Read the PDF format version.\n let version = \"\",\n ch;\n while (\n (ch = stream.getByte()) > /* Space = */ 0x20 &&\n version.length < /* MAX_PDF_VERSION_LENGTH = */ 7\n ) {\n version += String.fromCharCode(ch);\n }\n\n if (PDF_VERSION_REGEXP.test(version)) {\n this._version = version;\n } else {\n warn(`Invalid PDF header version: ${version}`);\n }\n }\n\n parseStartXRef() {\n this.xref.setStartXRef(this.startXRef);\n }\n\n get numPages() {\n let num = 0;\n if (this.catalog.hasActualNumPages) {\n num = this.catalog.numPages;\n } else if (this.xfaFactory) {\n // num is a Promise.\n num = this.xfaFactory.getNumPages();\n } else if (this.linearization) {\n num = this.linearization.numPages;\n } else {\n num = this.catalog.numPages;\n }\n return shadow(this, \"numPages\", num);\n }\n\n /**\n * @private\n */\n _hasOnlyDocumentSignatures(fields, recursionDepth = 0) {\n const RECURSION_LIMIT = 10;\n\n if (!Array.isArray(fields)) {\n return false;\n }\n return fields.every(field => {\n field = this.xref.fetchIfRef(field);\n if (!(field instanceof Dict)) {\n return false;\n }\n if (field.has(\"Kids\")) {\n if (++recursionDepth > RECURSION_LIMIT) {\n warn(\"_hasOnlyDocumentSignatures: maximum recursion depth reached\");\n return false;\n }\n return this._hasOnlyDocumentSignatures(\n field.get(\"Kids\"),\n recursionDepth\n );\n }\n const isSignature = isName(field.get(\"FT\"), \"Sig\");\n const rectangle = field.get(\"Rect\");\n const isInvisible =\n Array.isArray(rectangle) && rectangle.every(value => value === 0);\n return isSignature && isInvisible;\n });\n }\n\n get _xfaStreams() {\n const acroForm = this.catalog.acroForm;\n if (!acroForm) {\n return null;\n }\n\n const xfa = acroForm.get(\"XFA\");\n const entries = {\n \"xdp:xdp\": \"\",\n template: \"\",\n datasets: \"\",\n config: \"\",\n connectionSet: \"\",\n localeSet: \"\",\n stylesheet: \"\",\n \"/xdp:xdp\": \"\",\n };\n if (xfa instanceof BaseStream && !xfa.isEmpty) {\n entries[\"xdp:xdp\"] = xfa;\n return entries;\n }\n\n if (!Array.isArray(xfa) || xfa.length === 0) {\n return null;\n }\n\n for (let i = 0, ii = xfa.length; i < ii; i += 2) {\n let name;\n if (i === 0) {\n name = \"xdp:xdp\";\n } else if (i === ii - 2) {\n name = \"/xdp:xdp\";\n } else {\n name = xfa[i];\n }\n\n if (!entries.hasOwnProperty(name)) {\n continue;\n }\n const data = this.xref.fetchIfRef(xfa[i + 1]);\n if (!(data instanceof BaseStream) || data.isEmpty) {\n continue;\n }\n entries[name] = data;\n }\n return entries;\n }\n\n get xfaDatasets() {\n const streams = this._xfaStreams;\n if (!streams) {\n return shadow(this, \"xfaDatasets\", null);\n }\n for (const key of [\"datasets\", \"xdp:xdp\"]) {\n const stream = streams[key];\n if (!stream) {\n continue;\n }\n try {\n const str = stringToUTF8String(stream.getString());\n const data = { [key]: str };\n return shadow(this, \"xfaDatasets\", new DatasetReader(data));\n } catch {\n warn(\"XFA - Invalid utf-8 string.\");\n break;\n }\n }\n return shadow(this, \"xfaDatasets\", null);\n }\n\n get xfaData() {\n const streams = this._xfaStreams;\n if (!streams) {\n return null;\n }\n const data = Object.create(null);\n for (const [key, stream] of Object.entries(streams)) {\n if (!stream) {\n continue;\n }\n try {\n data[key] = stringToUTF8String(stream.getString());\n } catch {\n warn(\"XFA - Invalid utf-8 string.\");\n return null;\n }\n }\n return data;\n }\n\n get xfaFactory() {\n let data;\n if (\n this.pdfManager.enableXfa &&\n this.catalog.needsRendering &&\n this.formInfo.hasXfa &&\n !this.formInfo.hasAcroForm\n ) {\n data = this.xfaData;\n }\n return shadow(this, \"xfaFactory\", data ? new XFAFactory(data) : null);\n }\n\n get isPureXfa() {\n return this.xfaFactory ? this.xfaFactory.isValid() : false;\n }\n\n get htmlForXfa() {\n return this.xfaFactory ? this.xfaFactory.getPages() : null;\n }\n\n async loadXfaImages() {\n const xfaImagesDict = await this.pdfManager.ensureCatalog(\"xfaImages\");\n if (!xfaImagesDict) {\n return;\n }\n\n const keys = xfaImagesDict.getKeys();\n const objectLoader = new ObjectLoader(xfaImagesDict, keys, this.xref);\n await objectLoader.load();\n\n const xfaImages = new Map();\n for (const key of keys) {\n const stream = xfaImagesDict.get(key);\n if (stream instanceof BaseStream) {\n xfaImages.set(key, stream.getBytes());\n }\n }\n\n this.xfaFactory.setImages(xfaImages);\n }\n\n async loadXfaFonts(handler, task) {\n const acroForm = await this.pdfManager.ensureCatalog(\"acroForm\");\n if (!acroForm) {\n return;\n }\n const resources = await acroForm.getAsync(\"DR\");\n if (!(resources instanceof Dict)) {\n return;\n }\n const objectLoader = new ObjectLoader(resources, [\"Font\"], this.xref);\n await objectLoader.load();\n\n const fontRes = resources.get(\"Font\");\n if (!(fontRes instanceof Dict)) {\n return;\n }\n\n const options = Object.assign(\n Object.create(null),\n this.pdfManager.evaluatorOptions\n );\n options.useSystemFonts = false;\n\n const partialEvaluator = new PartialEvaluator({\n xref: this.xref,\n handler,\n pageIndex: -1,\n idFactory: this._globalIdFactory,\n fontCache: this.catalog.fontCache,\n builtInCMapCache: this.catalog.builtInCMapCache,\n standardFontDataCache: this.catalog.standardFontDataCache,\n options,\n });\n const operatorList = new OperatorList();\n const pdfFonts = [];\n const initialState = {\n get font() {\n return pdfFonts.at(-1);\n },\n set font(font) {\n pdfFonts.push(font);\n },\n clone() {\n return this;\n },\n };\n\n const fonts = new Map();\n fontRes.forEach((fontName, font) => {\n fonts.set(fontName, font);\n });\n const promises = [];\n\n for (const [fontName, font] of fonts) {\n const descriptor = font.get(\"FontDescriptor\");\n if (!(descriptor instanceof Dict)) {\n continue;\n }\n let fontFamily = descriptor.get(\"FontFamily\");\n // For example, \"Wingdings 3\" is not a valid font name in the css specs.\n fontFamily = fontFamily.replaceAll(/[ ]+(\\d)/g, \"$1\");\n const fontWeight = descriptor.get(\"FontWeight\");\n\n // Angle is expressed in degrees counterclockwise in PDF\n // when it's clockwise in CSS\n // (see https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle)\n const italicAngle = -descriptor.get(\"ItalicAngle\");\n const cssFontInfo = { fontFamily, fontWeight, italicAngle };\n\n if (!validateCSSFont(cssFontInfo)) {\n continue;\n }\n promises.push(\n partialEvaluator\n .handleSetFont(\n resources,\n [Name.get(fontName), 1],\n /* fontRef = */ null,\n operatorList,\n task,\n initialState,\n /* fallbackFontDict = */ null,\n /* cssFontInfo = */ cssFontInfo\n )\n .catch(function (reason) {\n warn(`loadXfaFonts: \"${reason}\".`);\n return null;\n })\n );\n }\n\n await Promise.all(promises);\n const missingFonts = this.xfaFactory.setFonts(pdfFonts);\n\n if (!missingFonts) {\n return;\n }\n\n options.ignoreErrors = true;\n promises.length = 0;\n pdfFonts.length = 0;\n\n const reallyMissingFonts = new Set();\n for (const missing of missingFonts) {\n if (!getXfaFontName(`${missing}-Regular`)) {\n // No substitution available: we'll fallback on Myriad.\n reallyMissingFonts.add(missing);\n }\n }\n\n if (reallyMissingFonts.size) {\n missingFonts.push(\"PdfJS-Fallback\");\n }\n\n for (const missing of missingFonts) {\n if (reallyMissingFonts.has(missing)) {\n continue;\n }\n for (const fontInfo of [\n { name: \"Regular\", fontWeight: 400, italicAngle: 0 },\n { name: \"Bold\", fontWeight: 700, italicAngle: 0 },\n { name: \"Italic\", fontWeight: 400, italicAngle: 12 },\n { name: \"BoldItalic\", fontWeight: 700, italicAngle: 12 },\n ]) {\n const name = `${missing}-${fontInfo.name}`;\n const dict = getXfaFontDict(name);\n\n promises.push(\n partialEvaluator\n .handleSetFont(\n resources,\n [Name.get(name), 1],\n /* fontRef = */ null,\n operatorList,\n task,\n initialState,\n /* fallbackFontDict = */ dict,\n /* cssFontInfo = */ {\n fontFamily: missing,\n fontWeight: fontInfo.fontWeight,\n italicAngle: fontInfo.italicAngle,\n }\n )\n .catch(function (reason) {\n warn(`loadXfaFonts: \"${reason}\".`);\n return null;\n })\n );\n }\n }\n\n await Promise.all(promises);\n this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts);\n }\n\n async serializeXfaData(annotationStorage) {\n return this.xfaFactory\n ? this.xfaFactory.serializeData(annotationStorage)\n : null;\n }\n\n /**\n * The specification states in section 7.5.2 that the version from\n * the catalog, if present, should overwrite the version from the header.\n */\n get version() {\n return this.catalog.version || this._version;\n }\n\n get formInfo() {\n const formInfo = {\n hasFields: false,\n hasAcroForm: false,\n hasXfa: false,\n hasSignatures: false,\n };\n const acroForm = this.catalog.acroForm;\n if (!acroForm) {\n return shadow(this, \"formInfo\", formInfo);\n }\n\n try {\n const fields = acroForm.get(\"Fields\");\n const hasFields = Array.isArray(fields) && fields.length > 0;\n formInfo.hasFields = hasFields; // Used by the `fieldObjects` getter.\n\n // The document contains XFA data if the `XFA` entry is a non-empty\n // array or stream.\n const xfa = acroForm.get(\"XFA\");\n formInfo.hasXfa =\n (Array.isArray(xfa) && xfa.length > 0) ||\n (xfa instanceof BaseStream && !xfa.isEmpty);\n\n // The document contains AcroForm data if the `Fields` entry is a\n // non-empty array and it doesn't consist of only document signatures.\n // This second check is required for files that don't actually contain\n // AcroForm data (only XFA data), but that use the `Fields` entry to\n // store (invisible) document signatures. This can be detected using\n // the first bit of the `SigFlags` integer (see Table 219 in the\n // specification).\n const sigFlags = acroForm.get(\"SigFlags\");\n const hasSignatures = !!(sigFlags & 0x1);\n const hasOnlyDocumentSignatures =\n hasSignatures && this._hasOnlyDocumentSignatures(fields);\n formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures;\n formInfo.hasSignatures = hasSignatures;\n } catch (ex) {\n if (ex instanceof MissingDataException) {\n throw ex;\n }\n warn(`Cannot fetch form information: \"${ex}\".`);\n }\n return shadow(this, \"formInfo\", formInfo);\n }\n\n get documentInfo() {\n const docInfo = {\n PDFFormatVersion: this.version,\n Language: this.catalog.lang,\n EncryptFilterName: this.xref.encrypt\n ? this.xref.encrypt.filterName\n : null,\n IsLinearized: !!this.linearization,\n IsAcroFormPresent: this.formInfo.hasAcroForm,\n IsXFAPresent: this.formInfo.hasXfa,\n IsCollectionPresent: !!this.catalog.collection,\n IsSignaturesPresent: this.formInfo.hasSignatures,\n };\n\n let infoDict;\n try {\n infoDict = this.xref.trailer.get(\"Info\");\n } catch (err) {\n if (err instanceof MissingDataException) {\n throw err;\n }\n info(\"The document information dictionary is invalid.\");\n }\n if (!(infoDict instanceof Dict)) {\n return shadow(this, \"documentInfo\", docInfo);\n }\n\n for (const key of infoDict.getKeys()) {\n const value = infoDict.get(key);\n\n switch (key) {\n case \"Title\":\n case \"Author\":\n case \"Subject\":\n case \"Keywords\":\n case \"Creator\":\n case \"Producer\":\n case \"CreationDate\":\n case \"ModDate\":\n if (typeof value === \"string\") {\n docInfo[key] = stringToPDFString(value);\n continue;\n }\n break;\n case \"Trapped\":\n if (value instanceof Name) {\n docInfo[key] = value;\n continue;\n }\n break;\n default:\n // For custom values, only accept white-listed types to prevent\n // errors that would occur when trying to send non-serializable\n // objects to the main-thread (for example `Dict` or `Stream`).\n let customValue;\n switch (typeof value) {\n case \"string\":\n customValue = stringToPDFString(value);\n break;\n case \"number\":\n case \"boolean\":\n customValue = value;\n break;\n default:\n if (value instanceof Name) {\n customValue = value;\n }\n break;\n }\n\n if (customValue === undefined) {\n warn(`Bad value, for custom key \"${key}\", in Info: ${value}.`);\n continue;\n }\n if (!docInfo.Custom) {\n docInfo.Custom = Object.create(null);\n }\n docInfo.Custom[key] = customValue;\n continue;\n }\n warn(`Bad value, for key \"${key}\", in Info: ${value}.`);\n }\n return shadow(this, \"documentInfo\", docInfo);\n }\n\n get fingerprints() {\n function validate(data) {\n return (\n typeof data === \"string\" &&\n data.length > 0 &&\n data !== EMPTY_FINGERPRINT\n );\n }\n\n function hexString(hash) {\n const buf = [];\n for (const num of hash) {\n const hex = num.toString(16);\n buf.push(hex.padStart(2, \"0\"));\n }\n return buf.join(\"\");\n }\n\n const idArray = this.xref.trailer.get(\"ID\");\n let hashOriginal, hashModified;\n if (Array.isArray(idArray) && validate(idArray[0])) {\n hashOriginal = stringToBytes(idArray[0]);\n\n if (idArray[1] !== idArray[0] && validate(idArray[1])) {\n hashModified = stringToBytes(idArray[1]);\n }\n } else {\n hashOriginal = calculateMD5(\n this.stream.getByteRange(0, FINGERPRINT_FIRST_BYTES),\n 0,\n FINGERPRINT_FIRST_BYTES\n );\n }\n\n return shadow(this, \"fingerprints\", [\n hexString(hashOriginal),\n hashModified ? hexString(hashModified) : null,\n ]);\n }\n\n async _getLinearizationPage(pageIndex) {\n const { catalog, linearization, xref } = this;\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(\n linearization?.pageFirst === pageIndex,\n \"_getLinearizationPage - invalid pageIndex argument.\"\n );\n }\n\n const ref = Ref.get(linearization.objectNumberFirst, 0);\n try {\n const obj = await xref.fetchAsync(ref);\n // Ensure that the object that was found is actually a Page dictionary.\n if (obj instanceof Dict) {\n let type = obj.getRaw(\"Type\");\n if (type instanceof Ref) {\n type = await xref.fetchAsync(type);\n }\n if (\n isName(type, \"Page\") ||\n (!obj.has(\"Type\") && !obj.has(\"Kids\") && obj.has(\"Contents\"))\n ) {\n if (!catalog.pageKidsCountCache.has(ref)) {\n catalog.pageKidsCountCache.put(ref, 1); // Cache the Page reference.\n }\n // Help improve performance of the `Catalog.getPageIndex` method.\n if (!catalog.pageIndexCache.has(ref)) {\n catalog.pageIndexCache.put(ref, 0);\n }\n\n return [obj, ref];\n }\n }\n throw new FormatError(\n \"The Linearization dictionary doesn't point to a valid Page dictionary.\"\n );\n } catch (reason) {\n warn(`_getLinearizationPage: \"${reason.message}\".`);\n return catalog.getPageDict(pageIndex);\n }\n }\n\n getPage(pageIndex) {\n const cachedPromise = this._pagePromises.get(pageIndex);\n if (cachedPromise) {\n return cachedPromise;\n }\n const { catalog, linearization, xfaFactory } = this;\n\n let promise;\n if (xfaFactory) {\n promise = Promise.resolve([Dict.empty, null]);\n } else if (linearization?.pageFirst === pageIndex) {\n promise = this._getLinearizationPage(pageIndex);\n } else {\n promise = catalog.getPageDict(pageIndex);\n }\n // eslint-disable-next-line arrow-body-style\n promise = promise.then(([pageDict, ref]) => {\n return new Page({\n pdfManager: this.pdfManager,\n xref: this.xref,\n pageIndex,\n pageDict,\n ref,\n globalIdFactory: this._globalIdFactory,\n fontCache: catalog.fontCache,\n builtInCMapCache: catalog.builtInCMapCache,\n standardFontDataCache: catalog.standardFontDataCache,\n globalImageCache: catalog.globalImageCache,\n systemFontCache: catalog.systemFontCache,\n nonBlendModesSet: catalog.nonBlendModesSet,\n xfaFactory,\n });\n });\n\n this._pagePromises.set(pageIndex, promise);\n return promise;\n }\n\n async checkFirstPage(recoveryMode = false) {\n if (recoveryMode) {\n return;\n }\n try {\n await this.getPage(0);\n } catch (reason) {\n if (reason instanceof XRefEntryException) {\n // Clear out the various caches to ensure that we haven't stored any\n // inconsistent and/or incorrect state, since that could easily break\n // subsequent `this.getPage` calls.\n this._pagePromises.delete(0);\n await this.cleanup();\n\n throw new XRefParseException();\n }\n }\n }\n\n async checkLastPage(recoveryMode = false) {\n const { catalog, pdfManager } = this;\n\n catalog.setActualNumPages(); // Ensure that it's always reset.\n let numPages;\n\n try {\n await Promise.all([\n pdfManager.ensureDoc(\"xfaFactory\"),\n pdfManager.ensureDoc(\"linearization\"),\n pdfManager.ensureCatalog(\"numPages\"),\n ]);\n\n if (this.xfaFactory) {\n return; // The Page count is always calculated for XFA-documents.\n } else if (this.linearization) {\n numPages = this.linearization.numPages;\n } else {\n numPages = catalog.numPages;\n }\n\n if (!Number.isInteger(numPages)) {\n throw new FormatError(\"Page count is not an integer.\");\n } else if (numPages <= 1) {\n return;\n }\n await this.getPage(numPages - 1);\n } catch (reason) {\n // Clear out the various caches to ensure that we haven't stored any\n // inconsistent and/or incorrect state, since that could easily break\n // subsequent `this.getPage` calls.\n this._pagePromises.delete(numPages - 1);\n await this.cleanup();\n\n if (reason instanceof XRefEntryException && !recoveryMode) {\n throw new XRefParseException();\n }\n warn(`checkLastPage - invalid /Pages tree /Count: ${numPages}.`);\n\n let pagesTree;\n try {\n pagesTree = await catalog.getAllPageDicts(recoveryMode);\n } catch (reasonAll) {\n if (reasonAll instanceof XRefEntryException && !recoveryMode) {\n throw new XRefParseException();\n }\n catalog.setActualNumPages(1);\n return;\n }\n\n for (const [pageIndex, [pageDict, ref]] of pagesTree) {\n let promise;\n if (pageDict instanceof Error) {\n promise = Promise.reject(pageDict);\n\n // Prevent \"uncaught exception: Object\"-messages in the console.\n promise.catch(() => {});\n } else {\n promise = Promise.resolve(\n new Page({\n pdfManager,\n xref: this.xref,\n pageIndex,\n pageDict,\n ref,\n globalIdFactory: this._globalIdFactory,\n fontCache: catalog.fontCache,\n builtInCMapCache: catalog.builtInCMapCache,\n standardFontDataCache: catalog.standardFontDataCache,\n globalImageCache: catalog.globalImageCache,\n systemFontCache: catalog.systemFontCache,\n nonBlendModesSet: catalog.nonBlendModesSet,\n xfaFactory: null,\n })\n );\n }\n\n this._pagePromises.set(pageIndex, promise);\n }\n catalog.setActualNumPages(pagesTree.size);\n }\n }\n\n fontFallback(id, handler) {\n return this.catalog.fontFallback(id, handler);\n }\n\n async cleanup(manuallyTriggered = false) {\n return this.catalog\n ? this.catalog.cleanup(manuallyTriggered)\n : clearGlobalCaches();\n }\n\n async #collectFieldObjects(\n name,\n fieldRef,\n promises,\n annotationGlobals,\n visitedRefs\n ) {\n const { xref } = this;\n\n if (!(fieldRef instanceof Ref) || visitedRefs.has(fieldRef)) {\n return;\n }\n visitedRefs.put(fieldRef);\n const field = await xref.fetchAsync(fieldRef);\n if (!(field instanceof Dict)) {\n return;\n }\n if (field.has(\"T\")) {\n const partName = stringToPDFString(await field.getAsync(\"T\"));\n name = name === \"\" ? partName : `${name}.${partName}`;\n } else {\n let obj = field;\n while (true) {\n obj = obj.getRaw(\"Parent\");\n if (obj instanceof Ref) {\n if (visitedRefs.has(obj)) {\n break;\n }\n obj = await xref.fetchAsync(obj);\n }\n if (!(obj instanceof Dict)) {\n break;\n }\n if (obj.has(\"T\")) {\n const partName = stringToPDFString(await obj.getAsync(\"T\"));\n name = name === \"\" ? partName : `${name}.${partName}`;\n break;\n }\n }\n }\n\n if (!promises.has(name)) {\n promises.set(name, []);\n }\n promises.get(name).push(\n AnnotationFactory.create(\n xref,\n fieldRef,\n annotationGlobals,\n /* idFactory = */ null,\n /* collectFields */ true,\n /* pageRef */ null\n )\n .then(annotation => annotation?.getFieldObject())\n .catch(function (reason) {\n warn(`#collectFieldObjects: \"${reason}\".`);\n return null;\n })\n );\n\n if (!field.has(\"Kids\")) {\n return;\n }\n const kids = await field.getAsync(\"Kids\");\n if (Array.isArray(kids)) {\n for (const kid of kids) {\n await this.#collectFieldObjects(\n name,\n kid,\n promises,\n annotationGlobals,\n visitedRefs\n );\n }\n }\n }\n\n get fieldObjects() {\n if (!this.formInfo.hasFields) {\n return shadow(this, \"fieldObjects\", Promise.resolve(null));\n }\n\n const promise = Promise.all([\n this.pdfManager.ensureDoc(\"annotationGlobals\"),\n this.pdfManager.ensureCatalog(\"acroForm\"),\n ]).then(async ([annotationGlobals, acroForm]) => {\n if (!annotationGlobals) {\n return null;\n }\n\n const visitedRefs = new RefSet();\n const allFields = Object.create(null);\n const fieldPromises = new Map();\n for (const fieldRef of await acroForm.getAsync(\"Fields\")) {\n await this.#collectFieldObjects(\n \"\",\n fieldRef,\n fieldPromises,\n annotationGlobals,\n visitedRefs\n );\n }\n\n const allPromises = [];\n for (const [name, promises] of fieldPromises) {\n allPromises.push(\n Promise.all(promises).then(fields => {\n fields = fields.filter(field => !!field);\n if (fields.length > 0) {\n allFields[name] = fields;\n }\n })\n );\n }\n\n await Promise.all(allPromises);\n return allFields;\n });\n\n return shadow(this, \"fieldObjects\", promise);\n }\n\n get hasJSActions() {\n const promise = this.pdfManager.ensureDoc(\"_parseHasJSActions\");\n return shadow(this, \"hasJSActions\", promise);\n }\n\n /**\n * @private\n */\n async _parseHasJSActions() {\n const [catalogJsActions, fieldObjects] = await Promise.all([\n this.pdfManager.ensureCatalog(\"jsActions\"),\n this.pdfManager.ensureDoc(\"fieldObjects\"),\n ]);\n\n if (catalogJsActions) {\n return true;\n }\n if (fieldObjects) {\n return Object.values(fieldObjects).some(fieldObject =>\n fieldObject.some(object => object.actions !== null)\n );\n }\n return false;\n }\n\n get calculationOrderIds() {\n const acroForm = this.catalog.acroForm;\n if (!acroForm?.has(\"CO\")) {\n return shadow(this, \"calculationOrderIds\", null);\n }\n\n const calculationOrder = acroForm.get(\"CO\");\n if (!Array.isArray(calculationOrder) || calculationOrder.length === 0) {\n return shadow(this, \"calculationOrderIds\", null);\n }\n\n const ids = [];\n for (const id of calculationOrder) {\n if (id instanceof Ref) {\n ids.push(id.toString());\n }\n }\n if (ids.length === 0) {\n return shadow(this, \"calculationOrderIds\", null);\n }\n return shadow(this, \"calculationOrderIds\", ids);\n }\n\n get annotationGlobals() {\n return shadow(\n this,\n \"annotationGlobals\",\n AnnotationFactory.createGlobals(this.pdfManager)\n );\n }\n}\n\nexport { Page, PDFDocument };\n","/* Copyright 2012 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createValidAbsoluteUrl,\n FeatureTest,\n unreachable,\n warn,\n} from \"../shared/util.js\";\nimport { ChunkedStreamManager } from \"./chunked_stream.js\";\nimport { MissingDataException } from \"./core_utils.js\";\nimport { PDFDocument } from \"./document.js\";\nimport { Stream } from \"./stream.js\";\n\nfunction parseDocBaseUrl(url) {\n if (url) {\n const absoluteUrl = createValidAbsoluteUrl(url);\n if (absoluteUrl) {\n return absoluteUrl.href;\n }\n warn(`Invalid absolute docBaseUrl: \"${url}\".`);\n }\n return null;\n}\n\nclass BasePdfManager {\n constructor(args) {\n if (this.constructor === BasePdfManager) {\n unreachable(\"Cannot initialize BasePdfManager.\");\n }\n this._docBaseUrl = parseDocBaseUrl(args.docBaseUrl);\n this._docId = args.docId;\n this._password = args.password;\n this.enableXfa = args.enableXfa;\n\n // Check `OffscreenCanvas` support once, rather than repeatedly throughout\n // the worker-thread code.\n args.evaluatorOptions.isOffscreenCanvasSupported &&=\n FeatureTest.isOffscreenCanvasSupported;\n this.evaluatorOptions = Object.freeze(args.evaluatorOptions);\n }\n\n get docId() {\n return this._docId;\n }\n\n get password() {\n return this._password;\n }\n\n get docBaseUrl() {\n return this._docBaseUrl;\n }\n\n get catalog() {\n return this.pdfDocument.catalog;\n }\n\n ensureDoc(prop, args) {\n return this.ensure(this.pdfDocument, prop, args);\n }\n\n ensureXRef(prop, args) {\n return this.ensure(this.pdfDocument.xref, prop, args);\n }\n\n ensureCatalog(prop, args) {\n return this.ensure(this.pdfDocument.catalog, prop, args);\n }\n\n getPage(pageIndex) {\n return this.pdfDocument.getPage(pageIndex);\n }\n\n fontFallback(id, handler) {\n return this.pdfDocument.fontFallback(id, handler);\n }\n\n loadXfaFonts(handler, task) {\n return this.pdfDocument.loadXfaFonts(handler, task);\n }\n\n loadXfaImages() {\n return this.pdfDocument.loadXfaImages();\n }\n\n serializeXfaData(annotationStorage) {\n return this.pdfDocument.serializeXfaData(annotationStorage);\n }\n\n cleanup(manuallyTriggered = false) {\n return this.pdfDocument.cleanup(manuallyTriggered);\n }\n\n async ensure(obj, prop, args) {\n unreachable(\"Abstract method `ensure` called\");\n }\n\n requestRange(begin, end) {\n unreachable(\"Abstract method `requestRange` called\");\n }\n\n requestLoadedStream(noFetch = false) {\n unreachable(\"Abstract method `requestLoadedStream` called\");\n }\n\n sendProgressiveData(chunk) {\n unreachable(\"Abstract method `sendProgressiveData` called\");\n }\n\n updatePassword(password) {\n this._password = password;\n }\n\n terminate(reason) {\n unreachable(\"Abstract method `terminate` called\");\n }\n}\n\nclass LocalPdfManager extends BasePdfManager {\n constructor(args) {\n super(args);\n\n const stream = new Stream(args.source);\n this.pdfDocument = new PDFDocument(this, stream);\n this._loadedStreamPromise = Promise.resolve(stream);\n }\n\n async ensure(obj, prop, args) {\n const value = obj[prop];\n if (typeof value === \"function\") {\n return value.apply(obj, args);\n }\n return value;\n }\n\n requestRange(begin, end) {\n return Promise.resolve();\n }\n\n requestLoadedStream(noFetch = false) {\n return this._loadedStreamPromise;\n }\n\n terminate(reason) {}\n}\n\nclass NetworkPdfManager extends BasePdfManager {\n constructor(args) {\n super(args);\n\n this.streamManager = new ChunkedStreamManager(args.source, {\n msgHandler: args.handler,\n length: args.length,\n disableAutoFetch: args.disableAutoFetch,\n rangeChunkSize: args.rangeChunkSize,\n });\n this.pdfDocument = new PDFDocument(this, this.streamManager.getStream());\n }\n\n async ensure(obj, prop, args) {\n try {\n const value = obj[prop];\n if (typeof value === \"function\") {\n return value.apply(obj, args);\n }\n return value;\n } catch (ex) {\n if (!(ex instanceof MissingDataException)) {\n throw ex;\n }\n await this.requestRange(ex.begin, ex.end);\n return this.ensure(obj, prop, args);\n }\n }\n\n requestRange(begin, end) {\n return this.streamManager.requestRange(begin, end);\n }\n\n requestLoadedStream(noFetch = false) {\n return this.streamManager.requestAllChunks(noFetch);\n }\n\n sendProgressiveData(chunk) {\n this.streamManager.onReceiveData({ chunk });\n }\n\n terminate(reason) {\n this.streamManager.abort(reason);\n }\n}\n\nexport { LocalPdfManager, NetworkPdfManager };\n","/* Copyright 2018 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AbortException,\n assert,\n MissingPDFException,\n PasswordException,\n UnexpectedResponseException,\n UnknownErrorException,\n unreachable,\n} from \"./util.js\";\n\nconst CallbackKind = {\n UNKNOWN: 0,\n DATA: 1,\n ERROR: 2,\n};\n\nconst StreamKind = {\n UNKNOWN: 0,\n CANCEL: 1,\n CANCEL_COMPLETE: 2,\n CLOSE: 3,\n ENQUEUE: 4,\n ERROR: 5,\n PULL: 6,\n PULL_COMPLETE: 7,\n START_COMPLETE: 8,\n};\n\nfunction wrapReason(reason) {\n if (\n !(\n reason instanceof Error ||\n (typeof reason === \"object\" && reason !== null)\n )\n ) {\n unreachable(\n 'wrapReason: Expected \"reason\" to be a (possibly cloned) Error.'\n );\n }\n switch (reason.name) {\n case \"AbortException\":\n return new AbortException(reason.message);\n case \"MissingPDFException\":\n return new MissingPDFException(reason.message);\n case \"PasswordException\":\n return new PasswordException(reason.message, reason.code);\n case \"UnexpectedResponseException\":\n return new UnexpectedResponseException(reason.message, reason.status);\n case \"UnknownErrorException\":\n return new UnknownErrorException(reason.message, reason.details);\n default:\n return new UnknownErrorException(reason.message, reason.toString());\n }\n}\n\nclass MessageHandler {\n constructor(sourceName, targetName, comObj) {\n this.sourceName = sourceName;\n this.targetName = targetName;\n this.comObj = comObj;\n this.callbackId = 1;\n this.streamId = 1;\n this.streamSinks = Object.create(null);\n this.streamControllers = Object.create(null);\n this.callbackCapabilities = Object.create(null);\n this.actionHandler = Object.create(null);\n\n this._onComObjOnMessage = event => {\n const data = event.data;\n if (data.targetName !== this.sourceName) {\n return;\n }\n if (data.stream) {\n this.#processStreamMessage(data);\n return;\n }\n if (data.callback) {\n const callbackId = data.callbackId;\n const capability = this.callbackCapabilities[callbackId];\n if (!capability) {\n throw new Error(`Cannot resolve callback ${callbackId}`);\n }\n delete this.callbackCapabilities[callbackId];\n\n if (data.callback === CallbackKind.DATA) {\n capability.resolve(data.data);\n } else if (data.callback === CallbackKind.ERROR) {\n capability.reject(wrapReason(data.reason));\n } else {\n throw new Error(\"Unexpected callback case\");\n }\n return;\n }\n const action = this.actionHandler[data.action];\n if (!action) {\n throw new Error(`Unknown action from worker: ${data.action}`);\n }\n if (data.callbackId) {\n const cbSourceName = this.sourceName;\n const cbTargetName = data.sourceName;\n\n new Promise(function (resolve) {\n resolve(action(data.data));\n }).then(\n function (result) {\n comObj.postMessage({\n sourceName: cbSourceName,\n targetName: cbTargetName,\n callback: CallbackKind.DATA,\n callbackId: data.callbackId,\n data: result,\n });\n },\n function (reason) {\n comObj.postMessage({\n sourceName: cbSourceName,\n targetName: cbTargetName,\n callback: CallbackKind.ERROR,\n callbackId: data.callbackId,\n reason: wrapReason(reason),\n });\n }\n );\n return;\n }\n if (data.streamId) {\n this.#createStreamSink(data);\n return;\n }\n action(data.data);\n };\n comObj.addEventListener(\"message\", this._onComObjOnMessage);\n }\n\n on(actionName, handler) {\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(\n typeof handler === \"function\",\n 'MessageHandler.on: Expected \"handler\" to be a function.'\n );\n }\n const ah = this.actionHandler;\n if (ah[actionName]) {\n throw new Error(`There is already an actionName called \"${actionName}\"`);\n }\n ah[actionName] = handler;\n }\n\n /**\n * Sends a message to the comObj to invoke the action with the supplied data.\n * @param {string} actionName - Action to call.\n * @param {JSON} data - JSON data to send.\n * @param {Array} [transfers] - List of transfers/ArrayBuffers.\n */\n send(actionName, data, transfers) {\n this.comObj.postMessage(\n {\n sourceName: this.sourceName,\n targetName: this.targetName,\n action: actionName,\n data,\n },\n transfers\n );\n }\n\n /**\n * Sends a message to the comObj to invoke the action with the supplied data.\n * Expects that the other side will callback with the response.\n * @param {string} actionName - Action to call.\n * @param {JSON} data - JSON data to send.\n * @param {Array} [transfers] - List of transfers/ArrayBuffers.\n * @returns {Promise} Promise to be resolved with response data.\n */\n sendWithPromise(actionName, data, transfers) {\n const callbackId = this.callbackId++;\n const capability = Promise.withResolvers();\n this.callbackCapabilities[callbackId] = capability;\n try {\n this.comObj.postMessage(\n {\n sourceName: this.sourceName,\n targetName: this.targetName,\n action: actionName,\n callbackId,\n data,\n },\n transfers\n );\n } catch (ex) {\n capability.reject(ex);\n }\n return capability.promise;\n }\n\n /**\n * Sends a message to the comObj to invoke the action with the supplied data.\n * Expect that the other side will callback to signal 'start_complete'.\n * @param {string} actionName - Action to call.\n * @param {JSON} data - JSON data to send.\n * @param {Object} queueingStrategy - Strategy to signal backpressure based on\n * internal queue.\n * @param {Array} [transfers] - List of transfers/ArrayBuffers.\n * @returns {ReadableStream} ReadableStream to read data in chunks.\n */\n sendWithStream(actionName, data, queueingStrategy, transfers) {\n const streamId = this.streamId++,\n sourceName = this.sourceName,\n targetName = this.targetName,\n comObj = this.comObj;\n\n return new ReadableStream(\n {\n start: controller => {\n const startCapability = Promise.withResolvers();\n this.streamControllers[streamId] = {\n controller,\n startCall: startCapability,\n pullCall: null,\n cancelCall: null,\n isClosed: false,\n };\n comObj.postMessage(\n {\n sourceName,\n targetName,\n action: actionName,\n streamId,\n data,\n desiredSize: controller.desiredSize,\n },\n transfers\n );\n // Return Promise for Async process, to signal success/failure.\n return startCapability.promise;\n },\n\n pull: controller => {\n const pullCapability = Promise.withResolvers();\n this.streamControllers[streamId].pullCall = pullCapability;\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.PULL,\n streamId,\n desiredSize: controller.desiredSize,\n });\n // Returning Promise will not call \"pull\"\n // again until current pull is resolved.\n return pullCapability.promise;\n },\n\n cancel: reason => {\n assert(reason instanceof Error, \"cancel must have a valid reason\");\n const cancelCapability = Promise.withResolvers();\n this.streamControllers[streamId].cancelCall = cancelCapability;\n this.streamControllers[streamId].isClosed = true;\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.CANCEL,\n streamId,\n reason: wrapReason(reason),\n });\n // Return Promise to signal success or failure.\n return cancelCapability.promise;\n },\n },\n queueingStrategy\n );\n }\n\n #createStreamSink(data) {\n const streamId = data.streamId,\n sourceName = this.sourceName,\n targetName = data.sourceName,\n comObj = this.comObj;\n const self = this,\n action = this.actionHandler[data.action];\n\n const streamSink = {\n enqueue(chunk, size = 1, transfers) {\n if (this.isCancelled) {\n return;\n }\n const lastDesiredSize = this.desiredSize;\n this.desiredSize -= size;\n // Enqueue decreases the desiredSize property of sink,\n // so when it changes from positive to negative,\n // set ready as unresolved promise.\n if (lastDesiredSize > 0 && this.desiredSize <= 0) {\n this.sinkCapability = Promise.withResolvers();\n this.ready = this.sinkCapability.promise;\n }\n comObj.postMessage(\n {\n sourceName,\n targetName,\n stream: StreamKind.ENQUEUE,\n streamId,\n chunk,\n },\n transfers\n );\n },\n\n close() {\n if (this.isCancelled) {\n return;\n }\n this.isCancelled = true;\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.CLOSE,\n streamId,\n });\n delete self.streamSinks[streamId];\n },\n\n error(reason) {\n assert(reason instanceof Error, \"error must have a valid reason\");\n if (this.isCancelled) {\n return;\n }\n this.isCancelled = true;\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.ERROR,\n streamId,\n reason: wrapReason(reason),\n });\n },\n\n sinkCapability: Promise.withResolvers(),\n onPull: null,\n onCancel: null,\n isCancelled: false,\n desiredSize: data.desiredSize,\n ready: null,\n };\n\n streamSink.sinkCapability.resolve();\n streamSink.ready = streamSink.sinkCapability.promise;\n this.streamSinks[streamId] = streamSink;\n\n new Promise(function (resolve) {\n resolve(action(data.data, streamSink));\n }).then(\n function () {\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.START_COMPLETE,\n streamId,\n success: true,\n });\n },\n function (reason) {\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.START_COMPLETE,\n streamId,\n reason: wrapReason(reason),\n });\n }\n );\n }\n\n #processStreamMessage(data) {\n const streamId = data.streamId,\n sourceName = this.sourceName,\n targetName = data.sourceName,\n comObj = this.comObj;\n const streamController = this.streamControllers[streamId],\n streamSink = this.streamSinks[streamId];\n\n switch (data.stream) {\n case StreamKind.START_COMPLETE:\n if (data.success) {\n streamController.startCall.resolve();\n } else {\n streamController.startCall.reject(wrapReason(data.reason));\n }\n break;\n case StreamKind.PULL_COMPLETE:\n if (data.success) {\n streamController.pullCall.resolve();\n } else {\n streamController.pullCall.reject(wrapReason(data.reason));\n }\n break;\n case StreamKind.PULL:\n // Ignore any pull after close is called.\n if (!streamSink) {\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.PULL_COMPLETE,\n streamId,\n success: true,\n });\n break;\n }\n // Pull increases the desiredSize property of sink, so when it changes\n // from negative to positive, set ready property as resolved promise.\n if (streamSink.desiredSize <= 0 && data.desiredSize > 0) {\n streamSink.sinkCapability.resolve();\n }\n // Reset desiredSize property of sink on every pull.\n streamSink.desiredSize = data.desiredSize;\n\n new Promise(function (resolve) {\n resolve(streamSink.onPull?.());\n }).then(\n function () {\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.PULL_COMPLETE,\n streamId,\n success: true,\n });\n },\n function (reason) {\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.PULL_COMPLETE,\n streamId,\n reason: wrapReason(reason),\n });\n }\n );\n break;\n case StreamKind.ENQUEUE:\n assert(streamController, \"enqueue should have stream controller\");\n if (streamController.isClosed) {\n break;\n }\n streamController.controller.enqueue(data.chunk);\n break;\n case StreamKind.CLOSE:\n assert(streamController, \"close should have stream controller\");\n if (streamController.isClosed) {\n break;\n }\n streamController.isClosed = true;\n streamController.controller.close();\n this.#deleteStreamController(streamController, streamId);\n break;\n case StreamKind.ERROR:\n assert(streamController, \"error should have stream controller\");\n streamController.controller.error(wrapReason(data.reason));\n this.#deleteStreamController(streamController, streamId);\n break;\n case StreamKind.CANCEL_COMPLETE:\n if (data.success) {\n streamController.cancelCall.resolve();\n } else {\n streamController.cancelCall.reject(wrapReason(data.reason));\n }\n this.#deleteStreamController(streamController, streamId);\n break;\n case StreamKind.CANCEL:\n if (!streamSink) {\n break;\n }\n\n new Promise(function (resolve) {\n resolve(streamSink.onCancel?.(wrapReason(data.reason)));\n }).then(\n function () {\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.CANCEL_COMPLETE,\n streamId,\n success: true,\n });\n },\n function (reason) {\n comObj.postMessage({\n sourceName,\n targetName,\n stream: StreamKind.CANCEL_COMPLETE,\n streamId,\n reason: wrapReason(reason),\n });\n }\n );\n streamSink.sinkCapability.reject(wrapReason(data.reason));\n streamSink.isCancelled = true;\n delete this.streamSinks[streamId];\n break;\n default:\n throw new Error(\"Unexpected stream case\");\n }\n }\n\n async #deleteStreamController(streamController, streamId) {\n // Delete the `streamController` only when the start, pull, and cancel\n // capabilities have settled, to prevent `TypeError`s.\n await Promise.allSettled([\n streamController.startCall?.promise,\n streamController.pullCall?.promise,\n streamController.cancelCall?.promise,\n ]);\n delete this.streamControllers[streamId];\n }\n\n destroy() {\n this.comObj.removeEventListener(\"message\", this._onComObjOnMessage);\n }\n}\n\nexport { MessageHandler };\n","/* Copyright 2019 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { assert } from \"../shared/util.js\";\n\n/** @implements {IPDFStream} */\nclass PDFWorkerStream {\n constructor(msgHandler) {\n this._msgHandler = msgHandler;\n this._contentLength = null;\n this._fullRequestReader = null;\n this._rangeRequestReaders = [];\n }\n\n getFullReader() {\n assert(\n !this._fullRequestReader,\n \"PDFWorkerStream.getFullReader can only be called once.\"\n );\n this._fullRequestReader = new PDFWorkerStreamReader(this._msgHandler);\n return this._fullRequestReader;\n }\n\n getRangeReader(begin, end) {\n const reader = new PDFWorkerStreamRangeReader(begin, end, this._msgHandler);\n this._rangeRequestReaders.push(reader);\n return reader;\n }\n\n cancelAllRequests(reason) {\n this._fullRequestReader?.cancel(reason);\n\n for (const reader of this._rangeRequestReaders.slice(0)) {\n reader.cancel(reason);\n }\n }\n}\n\n/** @implements {IPDFStreamReader} */\nclass PDFWorkerStreamReader {\n constructor(msgHandler) {\n this._msgHandler = msgHandler;\n this.onProgress = null;\n\n this._contentLength = null;\n this._isRangeSupported = false;\n this._isStreamingSupported = false;\n\n const readableStream = this._msgHandler.sendWithStream(\"GetReader\");\n this._reader = readableStream.getReader();\n\n this._headersReady = this._msgHandler\n .sendWithPromise(\"ReaderHeadersReady\")\n .then(data => {\n this._isStreamingSupported = data.isStreamingSupported;\n this._isRangeSupported = data.isRangeSupported;\n this._contentLength = data.contentLength;\n });\n }\n\n get headersReady() {\n return this._headersReady;\n }\n\n get contentLength() {\n return this._contentLength;\n }\n\n get isStreamingSupported() {\n return this._isStreamingSupported;\n }\n\n get isRangeSupported() {\n return this._isRangeSupported;\n }\n\n async read() {\n const { value, done } = await this._reader.read();\n if (done) {\n return { value: undefined, done: true };\n }\n // `value` is wrapped into Uint8Array, we need to\n // unwrap it to ArrayBuffer for further processing.\n return { value: value.buffer, done: false };\n }\n\n cancel(reason) {\n this._reader.cancel(reason);\n }\n}\n\n/** @implements {IPDFStreamRangeReader} */\nclass PDFWorkerStreamRangeReader {\n constructor(begin, end, msgHandler) {\n this._msgHandler = msgHandler;\n this.onProgress = null;\n\n const readableStream = this._msgHandler.sendWithStream(\"GetRangeReader\", {\n begin,\n end,\n });\n this._reader = readableStream.getReader();\n }\n\n get isStreamingSupported() {\n return false;\n }\n\n async read() {\n const { value, done } = await this._reader.read();\n if (done) {\n return { value: undefined, done: true };\n }\n return { value: value.buffer, done: false };\n }\n\n cancel(reason) {\n this._reader.cancel(reason);\n }\n}\n\nexport { PDFWorkerStream };\n","/* Copyright 2012 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AbortException,\n assert,\n getVerbosityLevel,\n info,\n InvalidPDFException,\n isNodeJS,\n MissingPDFException,\n PasswordException,\n setVerbosityLevel,\n stringToPDFString,\n UnexpectedResponseException,\n UnknownErrorException,\n VerbosityLevel,\n warn,\n} from \"../shared/util.js\";\nimport {\n arrayBuffersToBytes,\n getNewAnnotationsMap,\n XRefParseException,\n} from \"./core_utils.js\";\nimport { Dict, isDict, Ref } from \"./primitives.js\";\nimport { LocalPdfManager, NetworkPdfManager } from \"./pdf_manager.js\";\nimport { AnnotationFactory } from \"./annotation.js\";\nimport { clearGlobalCaches } from \"./cleanup_helper.js\";\nimport { incrementalUpdate } from \"./writer.js\";\nimport { MessageHandler } from \"../shared/message_handler.js\";\nimport { PDFWorkerStream } from \"./worker_stream.js\";\nimport { StructTreeRoot } from \"./struct_tree.js\";\n\nclass WorkerTask {\n constructor(name) {\n this.name = name;\n this.terminated = false;\n this._capability = Promise.withResolvers();\n }\n\n get finished() {\n return this._capability.promise;\n }\n\n finish() {\n this._capability.resolve();\n }\n\n terminate() {\n this.terminated = true;\n }\n\n ensureNotTerminated() {\n if (this.terminated) {\n throw new Error(\"Worker task was terminated\");\n }\n }\n}\n\nclass WorkerMessageHandler {\n static setup(handler, port) {\n let testMessageProcessed = false;\n handler.on(\"test\", function (data) {\n if (testMessageProcessed) {\n return; // we already processed 'test' message once\n }\n testMessageProcessed = true;\n\n // Ensure that `TypedArray`s can be sent to the worker.\n handler.send(\"test\", data instanceof Uint8Array);\n });\n\n handler.on(\"configure\", function (data) {\n setVerbosityLevel(data.verbosity);\n });\n\n handler.on(\"GetDocRequest\", function (data) {\n return WorkerMessageHandler.createDocumentHandler(data, port);\n });\n }\n\n static createDocumentHandler(docParams, port) {\n // This context is actually holds references on pdfManager and handler,\n // until the latter is destroyed.\n let pdfManager;\n let terminated = false;\n let cancelXHRs = null;\n const WorkerTasks = new Set();\n const verbosity = getVerbosityLevel();\n\n const { docId, apiVersion } = docParams;\n const workerVersion =\n typeof PDFJSDev !== \"undefined\" && !PDFJSDev.test(\"TESTING\")\n ? PDFJSDev.eval(\"BUNDLE_VERSION\")\n : null;\n if (apiVersion !== workerVersion) {\n throw new Error(\n `The API version \"${apiVersion}\" does not match ` +\n `the Worker version \"${workerVersion}\".`\n );\n }\n\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"GENERIC\")) {\n // Fail early, and predictably, rather than having (some) fonts fail to\n // load/render with slightly cryptic error messages in environments where\n // the `Array.prototype` has been *incorrectly* extended.\n //\n // PLEASE NOTE: We do *not* want to slow down font parsing by adding\n // `hasOwnProperty` checks all over the code-base.\n const enumerableProperties = [];\n for (const property in []) {\n enumerableProperties.push(property);\n }\n if (enumerableProperties.length) {\n throw new Error(\n \"The `Array.prototype` contains unexpected enumerable properties: \" +\n enumerableProperties.join(\", \") +\n \"; thus breaking e.g. `for...in` iteration of `Array`s.\"\n );\n }\n }\n const workerHandlerName = docId + \"_worker\";\n let handler = new MessageHandler(workerHandlerName, docId, port);\n\n function ensureNotTerminated() {\n if (terminated) {\n throw new Error(\"Worker was terminated\");\n }\n }\n\n function startWorkerTask(task) {\n WorkerTasks.add(task);\n }\n\n function finishWorkerTask(task) {\n task.finish();\n WorkerTasks.delete(task);\n }\n\n async function loadDocument(recoveryMode) {\n await pdfManager.ensureDoc(\"checkHeader\");\n await pdfManager.ensureDoc(\"parseStartXRef\");\n await pdfManager.ensureDoc(\"parse\", [recoveryMode]);\n\n // Check that at least the first page can be successfully loaded,\n // since otherwise the XRef table is definitely not valid.\n await pdfManager.ensureDoc(\"checkFirstPage\", [recoveryMode]);\n // Check that the last page can be successfully loaded, to ensure that\n // `numPages` is correct, and fallback to walking the entire /Pages-tree.\n await pdfManager.ensureDoc(\"checkLastPage\", [recoveryMode]);\n\n const isPureXfa = await pdfManager.ensureDoc(\"isPureXfa\");\n if (isPureXfa) {\n const task = new WorkerTask(\"loadXfaFonts\");\n startWorkerTask(task);\n await Promise.all([\n pdfManager\n .loadXfaFonts(handler, task)\n .catch(reason => {\n // Ignore errors, to allow the document to load.\n })\n .then(() => finishWorkerTask(task)),\n pdfManager.loadXfaImages(),\n ]);\n }\n\n const [numPages, fingerprints] = await Promise.all([\n pdfManager.ensureDoc(\"numPages\"),\n pdfManager.ensureDoc(\"fingerprints\"),\n ]);\n\n // Get htmlForXfa after numPages to avoid to create HTML twice.\n const htmlForXfa = isPureXfa\n ? await pdfManager.ensureDoc(\"htmlForXfa\")\n : null;\n\n return { numPages, fingerprints, htmlForXfa };\n }\n\n function getPdfManager({\n data,\n password,\n disableAutoFetch,\n rangeChunkSize,\n length,\n docBaseUrl,\n enableXfa,\n evaluatorOptions,\n }) {\n const pdfManagerArgs = {\n source: null,\n disableAutoFetch,\n docBaseUrl,\n docId,\n enableXfa,\n evaluatorOptions,\n handler,\n length,\n password,\n rangeChunkSize,\n };\n const pdfManagerCapability = Promise.withResolvers();\n let newPdfManager;\n\n if (data) {\n try {\n pdfManagerArgs.source = data;\n\n newPdfManager = new LocalPdfManager(pdfManagerArgs);\n pdfManagerCapability.resolve(newPdfManager);\n } catch (ex) {\n pdfManagerCapability.reject(ex);\n }\n return pdfManagerCapability.promise;\n }\n\n let pdfStream,\n cachedChunks = [];\n try {\n pdfStream = new PDFWorkerStream(handler);\n } catch (ex) {\n pdfManagerCapability.reject(ex);\n return pdfManagerCapability.promise;\n }\n\n const fullRequest = pdfStream.getFullReader();\n fullRequest.headersReady\n .then(function () {\n if (!fullRequest.isRangeSupported) {\n return;\n }\n pdfManagerArgs.source = pdfStream;\n pdfManagerArgs.length = fullRequest.contentLength;\n // We don't need auto-fetch when streaming is enabled.\n pdfManagerArgs.disableAutoFetch ||= fullRequest.isStreamingSupported;\n\n newPdfManager = new NetworkPdfManager(pdfManagerArgs);\n // There may be a chance that `newPdfManager` is not initialized for\n // the first few runs of `readchunk` block of code. Be sure to send\n // all cached chunks, if any, to chunked_stream via pdf_manager.\n for (const chunk of cachedChunks) {\n newPdfManager.sendProgressiveData(chunk);\n }\n\n cachedChunks = [];\n pdfManagerCapability.resolve(newPdfManager);\n cancelXHRs = null;\n })\n .catch(function (reason) {\n pdfManagerCapability.reject(reason);\n cancelXHRs = null;\n });\n\n let loaded = 0;\n const flushChunks = function () {\n const pdfFile = arrayBuffersToBytes(cachedChunks);\n if (length && pdfFile.length !== length) {\n warn(\"reported HTTP length is different from actual\");\n }\n // the data is array, instantiating directly from it\n try {\n pdfManagerArgs.source = pdfFile;\n\n newPdfManager = new LocalPdfManager(pdfManagerArgs);\n pdfManagerCapability.resolve(newPdfManager);\n } catch (ex) {\n pdfManagerCapability.reject(ex);\n }\n cachedChunks = [];\n };\n new Promise(function (resolve, reject) {\n const readChunk = function ({ value, done }) {\n try {\n ensureNotTerminated();\n if (done) {\n if (!newPdfManager) {\n flushChunks();\n }\n cancelXHRs = null;\n return;\n }\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n assert(\n value instanceof ArrayBuffer,\n \"readChunk (getPdfManager) - expected an ArrayBuffer.\"\n );\n }\n loaded += value.byteLength;\n\n if (!fullRequest.isStreamingSupported) {\n handler.send(\"DocProgress\", {\n loaded,\n total: Math.max(loaded, fullRequest.contentLength || 0),\n });\n }\n\n if (newPdfManager) {\n newPdfManager.sendProgressiveData(value);\n } else {\n cachedChunks.push(value);\n }\n fullRequest.read().then(readChunk, reject);\n } catch (e) {\n reject(e);\n }\n };\n fullRequest.read().then(readChunk, reject);\n }).catch(function (e) {\n pdfManagerCapability.reject(e);\n cancelXHRs = null;\n });\n\n cancelXHRs = function (reason) {\n pdfStream.cancelAllRequests(reason);\n };\n\n return pdfManagerCapability.promise;\n }\n\n function setupDoc(data) {\n function onSuccess(doc) {\n ensureNotTerminated();\n handler.send(\"GetDoc\", { pdfInfo: doc });\n }\n\n function onFailure(ex) {\n ensureNotTerminated();\n\n if (ex instanceof PasswordException) {\n const task = new WorkerTask(`PasswordException: response ${ex.code}`);\n startWorkerTask(task);\n\n handler\n .sendWithPromise(\"PasswordRequest\", ex)\n .then(function ({ password }) {\n finishWorkerTask(task);\n pdfManager.updatePassword(password);\n pdfManagerReady();\n })\n .catch(function () {\n finishWorkerTask(task);\n handler.send(\"DocException\", ex);\n });\n } else if (\n ex instanceof InvalidPDFException ||\n ex instanceof MissingPDFException ||\n ex instanceof UnexpectedResponseException ||\n ex instanceof UnknownErrorException\n ) {\n handler.send(\"DocException\", ex);\n } else {\n handler.send(\n \"DocException\",\n new UnknownErrorException(ex.message, ex.toString())\n );\n }\n }\n\n function pdfManagerReady() {\n ensureNotTerminated();\n\n loadDocument(false).then(onSuccess, function (reason) {\n ensureNotTerminated();\n\n // Try again with recoveryMode == true\n if (!(reason instanceof XRefParseException)) {\n onFailure(reason);\n return;\n }\n pdfManager.requestLoadedStream().then(function () {\n ensureNotTerminated();\n\n loadDocument(true).then(onSuccess, onFailure);\n });\n });\n }\n\n ensureNotTerminated();\n\n getPdfManager(data)\n .then(function (newPdfManager) {\n if (terminated) {\n // We were in a process of setting up the manager, but it got\n // terminated in the middle.\n newPdfManager.terminate(\n new AbortException(\"Worker was terminated.\")\n );\n throw new Error(\"Worker was terminated\");\n }\n pdfManager = newPdfManager;\n\n pdfManager.requestLoadedStream(/* noFetch = */ true).then(stream => {\n handler.send(\"DataLoaded\", { length: stream.bytes.byteLength });\n });\n })\n .then(pdfManagerReady, onFailure);\n }\n\n handler.on(\"GetPage\", function (data) {\n return pdfManager.getPage(data.pageIndex).then(function (page) {\n return Promise.all([\n pdfManager.ensure(page, \"rotate\"),\n pdfManager.ensure(page, \"ref\"),\n pdfManager.ensure(page, \"userUnit\"),\n pdfManager.ensure(page, \"view\"),\n ]).then(function ([rotate, ref, userUnit, view]) {\n return {\n rotate,\n ref,\n refStr: ref?.toString() ?? null,\n userUnit,\n view,\n };\n });\n });\n });\n\n handler.on(\"GetPageIndex\", function (data) {\n const pageRef = Ref.get(data.num, data.gen);\n return pdfManager.ensureCatalog(\"getPageIndex\", [pageRef]);\n });\n\n handler.on(\"GetDestinations\", function (data) {\n return pdfManager.ensureCatalog(\"destinations\");\n });\n\n handler.on(\"GetDestination\", function (data) {\n return pdfManager.ensureCatalog(\"getDestination\", [data.id]);\n });\n\n handler.on(\"GetPageLabels\", function (data) {\n return pdfManager.ensureCatalog(\"pageLabels\");\n });\n\n handler.on(\"GetPageLayout\", function (data) {\n return pdfManager.ensureCatalog(\"pageLayout\");\n });\n\n handler.on(\"GetPageMode\", function (data) {\n return pdfManager.ensureCatalog(\"pageMode\");\n });\n\n handler.on(\"GetViewerPreferences\", function (data) {\n return pdfManager.ensureCatalog(\"viewerPreferences\");\n });\n\n handler.on(\"GetOpenAction\", function (data) {\n return pdfManager.ensureCatalog(\"openAction\");\n });\n\n handler.on(\"GetAttachments\", function (data) {\n return pdfManager.ensureCatalog(\"attachments\");\n });\n\n handler.on(\"GetDocJSActions\", function (data) {\n return pdfManager.ensureCatalog(\"jsActions\");\n });\n\n handler.on(\"GetPageJSActions\", function ({ pageIndex }) {\n return pdfManager.getPage(pageIndex).then(function (page) {\n return pdfManager.ensure(page, \"jsActions\");\n });\n });\n\n handler.on(\"GetOutline\", function (data) {\n return pdfManager.ensureCatalog(\"documentOutline\");\n });\n\n handler.on(\"GetOptionalContentConfig\", function (data) {\n return pdfManager.ensureCatalog(\"optionalContentConfig\");\n });\n\n handler.on(\"GetPermissions\", function (data) {\n return pdfManager.ensureCatalog(\"permissions\");\n });\n\n handler.on(\"GetMetadata\", function (data) {\n return Promise.all([\n pdfManager.ensureDoc(\"documentInfo\"),\n pdfManager.ensureCatalog(\"metadata\"),\n ]);\n });\n\n handler.on(\"GetMarkInfo\", function (data) {\n return pdfManager.ensureCatalog(\"markInfo\");\n });\n\n handler.on(\"GetData\", function (data) {\n return pdfManager.requestLoadedStream().then(function (stream) {\n return stream.bytes;\n });\n });\n\n handler.on(\"GetAnnotations\", function ({ pageIndex, intent }) {\n return pdfManager.getPage(pageIndex).then(function (page) {\n const task = new WorkerTask(`GetAnnotations: page ${pageIndex}`);\n startWorkerTask(task);\n\n return page.getAnnotationsData(handler, task, intent).then(\n data => {\n finishWorkerTask(task);\n return data;\n },\n reason => {\n finishWorkerTask(task);\n throw reason;\n }\n );\n });\n });\n\n handler.on(\"GetFieldObjects\", function (data) {\n return pdfManager.ensureDoc(\"fieldObjects\");\n });\n\n handler.on(\"HasJSActions\", function (data) {\n return pdfManager.ensureDoc(\"hasJSActions\");\n });\n\n handler.on(\"GetCalculationOrderIds\", function (data) {\n return pdfManager.ensureDoc(\"calculationOrderIds\");\n });\n\n handler.on(\n \"SaveDocument\",\n async function ({ isPureXfa, numPages, annotationStorage, filename }) {\n const globalPromises = [\n pdfManager.requestLoadedStream(),\n pdfManager.ensureCatalog(\"acroForm\"),\n pdfManager.ensureCatalog(\"acroFormRef\"),\n pdfManager.ensureDoc(\"startXRef\"),\n pdfManager.ensureDoc(\"xref\"),\n pdfManager.ensureDoc(\"linearization\"),\n pdfManager.ensureCatalog(\"structTreeRoot\"),\n ];\n const promises = [];\n\n const newAnnotationsByPage = !isPureXfa\n ? getNewAnnotationsMap(annotationStorage)\n : null;\n const [\n stream,\n acroForm,\n acroFormRef,\n startXRef,\n xref,\n linearization,\n _structTreeRoot,\n ] = await Promise.all(globalPromises);\n const catalogRef = xref.trailer.getRaw(\"Root\") || null;\n let structTreeRoot;\n\n if (newAnnotationsByPage) {\n if (!_structTreeRoot) {\n if (\n await StructTreeRoot.canCreateStructureTree({\n catalogRef,\n pdfManager,\n newAnnotationsByPage,\n })\n ) {\n structTreeRoot = null;\n }\n } else if (\n await _structTreeRoot.canUpdateStructTree({\n pdfManager,\n xref,\n newAnnotationsByPage,\n })\n ) {\n structTreeRoot = _structTreeRoot;\n }\n\n const imagePromises = AnnotationFactory.generateImages(\n annotationStorage.values(),\n xref,\n pdfManager.evaluatorOptions.isOffscreenCanvasSupported\n );\n const newAnnotationPromises =\n structTreeRoot === undefined ? promises : [];\n for (const [pageIndex, annotations] of newAnnotationsByPage) {\n newAnnotationPromises.push(\n pdfManager.getPage(pageIndex).then(page => {\n const task = new WorkerTask(`Save (editor): page ${pageIndex}`);\n return page\n .saveNewAnnotations(handler, task, annotations, imagePromises)\n .finally(function () {\n finishWorkerTask(task);\n });\n })\n );\n }\n if (structTreeRoot === null) {\n // No structTreeRoot exists, so we need to create one.\n promises.push(\n Promise.all(newAnnotationPromises).then(async newRefs => {\n await StructTreeRoot.createStructureTree({\n newAnnotationsByPage,\n xref,\n catalogRef,\n pdfManager,\n newRefs,\n });\n return newRefs;\n })\n );\n } else if (structTreeRoot) {\n promises.push(\n Promise.all(newAnnotationPromises).then(async newRefs => {\n await structTreeRoot.updateStructureTree({\n newAnnotationsByPage,\n pdfManager,\n newRefs,\n });\n return newRefs;\n })\n );\n }\n }\n\n if (isPureXfa) {\n promises.push(pdfManager.serializeXfaData(annotationStorage));\n } else {\n for (let pageIndex = 0; pageIndex < numPages; pageIndex++) {\n promises.push(\n pdfManager.getPage(pageIndex).then(function (page) {\n const task = new WorkerTask(`Save: page ${pageIndex}`);\n return page\n .save(handler, task, annotationStorage)\n .finally(function () {\n finishWorkerTask(task);\n });\n })\n );\n }\n }\n const refs = await Promise.all(promises);\n\n let newRefs = [];\n let xfaData = null;\n if (isPureXfa) {\n xfaData = refs[0];\n if (!xfaData) {\n return stream.bytes;\n }\n } else {\n newRefs = refs.flat(2);\n\n if (newRefs.length === 0) {\n // No new refs so just return the initial bytes\n return stream.bytes;\n }\n }\n\n const needAppearances =\n acroFormRef &&\n acroForm instanceof Dict &&\n newRefs.some(ref => ref.needAppearances);\n\n const xfa = (acroForm instanceof Dict && acroForm.get(\"XFA\")) || null;\n let xfaDatasetsRef = null;\n let hasXfaDatasetsEntry = false;\n if (Array.isArray(xfa)) {\n for (let i = 0, ii = xfa.length; i < ii; i += 2) {\n if (xfa[i] === \"datasets\") {\n xfaDatasetsRef = xfa[i + 1];\n hasXfaDatasetsEntry = true;\n }\n }\n if (xfaDatasetsRef === null) {\n xfaDatasetsRef = xref.getNewTemporaryRef();\n }\n } else if (xfa) {\n // TODO: Support XFA streams.\n warn(\"Unsupported XFA type.\");\n }\n\n let newXrefInfo = Object.create(null);\n if (xref.trailer) {\n // Get string info from Info in order to compute fileId.\n const infoObj = Object.create(null);\n const xrefInfo = xref.trailer.get(\"Info\") || null;\n if (xrefInfo instanceof Dict) {\n xrefInfo.forEach((key, value) => {\n if (typeof value === \"string\") {\n infoObj[key] = stringToPDFString(value);\n }\n });\n }\n\n newXrefInfo = {\n rootRef: catalogRef,\n encryptRef: xref.trailer.getRaw(\"Encrypt\") || null,\n newRef: xref.getNewTemporaryRef(),\n infoRef: xref.trailer.getRaw(\"Info\") || null,\n info: infoObj,\n fileIds: xref.trailer.get(\"ID\") || null,\n startXRef: linearization\n ? startXRef\n : xref.lastXRefStreamPos ?? startXRef,\n filename,\n };\n }\n\n return incrementalUpdate({\n originalData: stream.bytes,\n xrefInfo: newXrefInfo,\n newRefs,\n xref,\n hasXfa: !!xfa,\n xfaDatasetsRef,\n hasXfaDatasetsEntry,\n needAppearances,\n acroFormRef,\n acroForm,\n xfaData,\n // Use the same kind of XRef as the previous one.\n useXrefStream: isDict(xref.topDict, \"XRef\"),\n }).finally(() => {\n xref.resetNewTemporaryRef();\n });\n }\n );\n\n handler.on(\"GetOperatorList\", function (data, sink) {\n const pageIndex = data.pageIndex;\n pdfManager.getPage(pageIndex).then(function (page) {\n const task = new WorkerTask(`GetOperatorList: page ${pageIndex}`);\n startWorkerTask(task);\n\n // NOTE: Keep this condition in sync with the `info` helper function.\n const start = verbosity >= VerbosityLevel.INFOS ? Date.now() : 0;\n\n // Pre compile the pdf page and fetch the fonts/images.\n page\n .getOperatorList({\n handler,\n sink,\n task,\n intent: data.intent,\n cacheKey: data.cacheKey,\n annotationStorage: data.annotationStorage,\n })\n .then(\n function (operatorListInfo) {\n finishWorkerTask(task);\n\n if (start) {\n info(\n `page=${pageIndex + 1} - getOperatorList: time=` +\n `${Date.now() - start}ms, len=${operatorListInfo.length}`\n );\n }\n sink.close();\n },\n function (reason) {\n finishWorkerTask(task);\n if (task.terminated) {\n return; // ignoring errors from the terminated thread\n }\n sink.error(reason);\n\n // TODO: Should `reason` be re-thrown here (currently that casues\n // \"Uncaught exception: ...\" messages in the console)?\n }\n );\n });\n });\n\n handler.on(\"GetTextContent\", function (data, sink) {\n const { pageIndex, includeMarkedContent, disableNormalization } = data;\n\n pdfManager.getPage(pageIndex).then(function (page) {\n const task = new WorkerTask(\"GetTextContent: page \" + pageIndex);\n startWorkerTask(task);\n\n // NOTE: Keep this condition in sync with the `info` helper function.\n const start = verbosity >= VerbosityLevel.INFOS ? Date.now() : 0;\n\n page\n .extractTextContent({\n handler,\n task,\n sink,\n includeMarkedContent,\n disableNormalization,\n })\n .then(\n function () {\n finishWorkerTask(task);\n\n if (start) {\n info(\n `page=${pageIndex + 1} - getTextContent: time=` +\n `${Date.now() - start}ms`\n );\n }\n sink.close();\n },\n function (reason) {\n finishWorkerTask(task);\n if (task.terminated) {\n return; // ignoring errors from the terminated thread\n }\n sink.error(reason);\n\n // TODO: Should `reason` be re-thrown here (currently that casues\n // \"Uncaught exception: ...\" messages in the console)?\n }\n );\n });\n });\n\n handler.on(\"GetStructTree\", function (data) {\n return pdfManager.getPage(data.pageIndex).then(function (page) {\n return pdfManager.ensure(page, \"getStructTree\");\n });\n });\n\n handler.on(\"FontFallback\", function (data) {\n return pdfManager.fontFallback(data.id, handler);\n });\n\n handler.on(\"Cleanup\", function (data) {\n return pdfManager.cleanup(/* manuallyTriggered = */ true);\n });\n\n handler.on(\"Terminate\", function (data) {\n terminated = true;\n\n const waitOn = [];\n if (pdfManager) {\n pdfManager.terminate(new AbortException(\"Worker was terminated.\"));\n\n const cleanupPromise = pdfManager.cleanup();\n waitOn.push(cleanupPromise);\n\n pdfManager = null;\n } else {\n clearGlobalCaches();\n }\n if (cancelXHRs) {\n cancelXHRs(new AbortException(\"Worker was terminated.\"));\n }\n\n for (const task of WorkerTasks) {\n waitOn.push(task.finished);\n task.terminate();\n }\n\n return Promise.all(waitOn).then(function () {\n // Notice that even if we destroying handler, resolved response promise\n // must be sent back.\n handler.destroy();\n handler = null;\n });\n });\n\n handler.on(\"Ready\", function (data) {\n setupDoc(docParams);\n docParams = null; // we don't need docParams anymore -- saving memory.\n });\n\n if (typeof PDFJSDev === \"undefined\" || PDFJSDev.test(\"TESTING\")) {\n handler.on(\"GetXFADatasets\", function (data) {\n return pdfManager.ensureDoc(\"xfaDatasets\");\n });\n handler.on(\"GetXRefPrevValue\", function (data) {\n return pdfManager\n .ensureXRef(\"trailer\")\n .then(trailer => trailer.get(\"Prev\"));\n });\n handler.on(\"GetStartXRefPos\", function (data) {\n return pdfManager.ensureDoc(\"startXRef\");\n });\n handler.on(\"GetAnnotArray\", function (data) {\n return pdfManager.getPage(data.pageIndex).then(function (page) {\n return page.annotations.map(a => a.toString());\n });\n });\n }\n\n return workerHandlerName;\n }\n\n static initializeFromPort(port) {\n const handler = new MessageHandler(\"worker\", \"main\", port);\n WorkerMessageHandler.setup(handler, port);\n handler.send(\"ready\", null);\n }\n}\n\nfunction isMessagePort(maybePort) {\n return (\n typeof maybePort.postMessage === \"function\" && \"onmessage\" in maybePort\n );\n}\n\n// Worker thread (and not Node.js)?\nif (\n typeof window === \"undefined\" &&\n !isNodeJS &&\n typeof self !== \"undefined\" &&\n isMessagePort(self)\n) {\n WorkerMessageHandler.initializeFromPort(self);\n}\n\nexport { WorkerMessageHandler, WorkerTask };\n","/* Copyright 2012 Mozilla Foundation\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { WorkerMessageHandler } from \"./core/worker.js\";\n\n/* eslint-disable-next-line no-unused-vars */\nconst pdfjsVersion =\n typeof PDFJSDev !== \"undefined\" ? PDFJSDev.eval(\"BUNDLE_VERSION\") : void 0;\n/* eslint-disable-next-line no-unused-vars */\nconst pdfjsBuild =\n typeof PDFJSDev !== \"undefined\" ? PDFJSDev.eval(\"BUNDLE_BUILD\") : void 0;\n\nexport { WorkerMessageHandler };\n"],"names":["isNodeJS","process","versions","nw","electron","type","IDENTITY_MATRIX","FONT_IDENTITY_MATRIX","MAX_IMAGE_SIZE_TO_CACHE","LINE_FACTOR","LINE_DESCENT_FACTOR","BASELINE_FACTOR","RenderingIntentFlag","ANY","DISPLAY","PRINT","SAVE","ANNOTATIONS_FORMS","ANNOTATIONS_STORAGE","ANNOTATIONS_DISABLE","OPLIST","AnnotationMode","DISABLE","ENABLE","ENABLE_FORMS","ENABLE_STORAGE","AnnotationEditorPrefix","AnnotationEditorType","NONE","FREETEXT","HIGHLIGHT","STAMP","INK","AnnotationEditorParamsType","RESIZE","CREATE","FREETEXT_SIZE","FREETEXT_COLOR","FREETEXT_OPACITY","INK_COLOR","INK_THICKNESS","INK_OPACITY","HIGHLIGHT_COLOR","HIGHLIGHT_DEFAULT_COLOR","HIGHLIGHT_THICKNESS","HIGHLIGHT_FREE","HIGHLIGHT_SHOW_ALL","PermissionFlag","MODIFY_CONTENTS","COPY","MODIFY_ANNOTATIONS","FILL_INTERACTIVE_FORMS","COPY_FOR_ACCESSIBILITY","ASSEMBLE","PRINT_HIGH_QUALITY","TextRenderingMode","FILL","STROKE","FILL_STROKE","INVISIBLE","FILL_ADD_TO_PATH","STROKE_ADD_TO_PATH","FILL_STROKE_ADD_TO_PATH","ADD_TO_PATH","FILL_STROKE_MASK","ADD_TO_PATH_FLAG","ImageKind","GRAYSCALE_1BPP","RGB_24BPP","RGBA_32BPP","AnnotationType","TEXT","LINK","LINE","SQUARE","CIRCLE","POLYGON","POLYLINE","UNDERLINE","SQUIGGLY","STRIKEOUT","CARET","POPUP","FILEATTACHMENT","SOUND","MOVIE","WIDGET","SCREEN","PRINTERMARK","TRAPNET","WATERMARK","THREED","REDACT","AnnotationReplyType","GROUP","REPLY","AnnotationFlag","HIDDEN","NOZOOM","NOROTATE","NOVIEW","READONLY","LOCKED","TOGGLENOVIEW","LOCKEDCONTENTS","AnnotationFieldFlag","REQUIRED","NOEXPORT","MULTILINE","PASSWORD","NOTOGGLETOOFF","RADIO","PUSHBUTTON","COMBO","EDIT","SORT","FILESELECT","MULTISELECT","DONOTSPELLCHECK","DONOTSCROLL","COMB","RICHTEXT","RADIOSINUNISON","COMMITONSELCHANGE","AnnotationBorderStyleType","SOLID","DASHED","BEVELED","INSET","AnnotationActionEventType","E","X","D","U","Fo","Bl","PO","PC","PV","PI","K","F","V","C","DocumentActionEventType","WC","WS","DS","WP","DP","PageActionEventType","O","VerbosityLevel","ERRORS","WARNINGS","INFOS","CMapCompressionType","BINARY","OPS","dependency","setLineWidth","setLineCap","setLineJoin","setMiterLimit","setDash","setRenderingIntent","setFlatness","setGState","save","restore","transform","moveTo","lineTo","curveTo","curveTo2","curveTo3","closePath","rectangle","stroke","closeStroke","fill","eoFill","fillStroke","eoFillStroke","closeFillStroke","closeEOFillStroke","endPath","clip","eoClip","beginText","endText","setCharSpacing","setWordSpacing","setHScale","setLeading","setFont","setTextRenderingMode","setTextRise","moveText","setLeadingMoveText","setTextMatrix","nextLine","showText","showSpacedText","nextLineShowText","nextLineSetSpacingShowText","setCharWidth","setCharWidthAndBounds","setStrokeColorSpace","setFillColorSpace","setStrokeColor","setStrokeColorN","setFillColor","setFillColorN","setStrokeGray","setFillGray","setStrokeRGBColor","setFillRGBColor","setStrokeCMYKColor","setFillCMYKColor","shadingFill","beginInlineImage","beginImageData","endInlineImage","paintXObject","markPoint","markPointProps","beginMarkedContent","beginMarkedContentProps","endMarkedContent","beginCompat","endCompat","paintFormXObjectBegin","paintFormXObjectEnd","beginGroup","endGroup","beginAnnotation","endAnnotation","paintImageMaskXObject","paintImageMaskXObjectGroup","paintImageXObject","paintInlineImageXObject","paintInlineImageXObjectGroup","paintImageXObjectRepeat","paintImageMaskXObjectRepeat","paintSolidColorImageMask","constructPath","PasswordResponses","NEED_PASSWORD","INCORRECT_PASSWORD","verbosity","setVerbosityLevel","level","Number","isInteger","getVerbosityLevel","info","msg","console","log","warn","unreachable","Error","assert","cond","_isValidProtocol","url","protocol","createValidAbsoluteUrl","baseUrl","options","addDefaultProtocol","startsWith","dots","match","length","tryConvertEncoding","stringToUTF8String","absoluteUrl","URL","shadow","obj","prop","value","nonSerializable","Object","defineProperty","enumerable","configurable","writable","BaseException","BaseExceptionClosure","message","name","constructor","prototype","PasswordException","code","UnknownErrorException","details","InvalidPDFException","MissingPDFException","UnexpectedResponseException","status","FormatError","AbortException","bytesToString","bytes","undefined","MAX_ARGUMENT_COUNT","String","fromCharCode","apply","strBuf","i","chunkEnd","Math","min","chunk","subarray","push","join","stringToBytes","str","Uint8Array","charCodeAt","string32","objectSize","keys","objectFromMap","map","create","key","isLittleEndian","buffer8","view32","Uint32Array","buffer","isEvalSupported","Function","FeatureTest","isOffscreenCanvasSupported","OffscreenCanvas","platform","navigator","isMac","includes","isCSSRoundSupported","globalThis","CSS","supports","hexNumbers","Array","from","n","toString","padStart","Util","makeHexColor","r","g","b","scaleMinMax","minMax","temp","m1","m2","applyTransform","p","m","xt","yt","applyInverseTransform","d","getAxialAlignedBoundingBox","p1","p2","slice","p3","p4","max","inverseTransform","singularValueDecompose2dScale","transpose","a","c","first","second","sqrt","sx","sy","normalizeRect","rect","intersect","rect1","rect2","xLow","xHigh","yLow","yHigh","getExtremumOnCurve","#getExtremumOnCurve","x0","x1","x2","x3","y0","y1","y2","y3","t","mt","tt","ttt","x","y","getExtremum","#getExtremum","abs","delta","sqrtDelta","a2","bezierBoundingBox","PDFStringTranslateTable","stringToPDFString","encoding","decoder","TextDecoder","fatal","decoded","decode","replaceAll","ex","ii","charCode","charAt","decodeURIComponent","escape","utf8StringToString","unescape","encodeURIComponent","isArrayEqual","arr1","arr2","getModificationDate","date","Date","getUTCFullYear","getUTCMonth","getUTCDate","getUTCHours","getUTCMinutes","getUTCSeconds","NormalizeRegex","NormalizationMap","normalizeUnicode","Map","_","normalize","get","getUuid","crypto","randomUUID","buf","getRandomValues","floor","random","AnnotationPrefix","FontRenderOps","BEZIER_CURVE_TO","MOVE_TO","LINE_TO","QUADRATIC_CURVE_TO","RESTORE","SCALE","TRANSFORM","TRANSLATE","CIRCULAR_REF","Symbol","EOF","CmdCache","NameCache","RefCache","clearPrimitiveCaches","Name","Cmd","cmd","nonSerializableClosure","Dict","xref","_map","objId","suppressEncryption","__nonSerializable__","assignXref","newXref","size","key1","key2","key3","Ref","fetch","getAsync","fetchAsync","getArray","isArray","getRaw","getKeys","getRawValues","values","set","has","forEach","callback","empty","emptyDict","merge","dictArray","mergeSubDicts","mergedDict","properties","dict","entries","property","subDict","clear","clone","num","gen","fromString","ref","exec","parseInt","RefSet","parent","_set","Set","put","add","remove","delete","iterator","RefSetCache","putAlias","aliasRef","items","isName","v","isCmd","isDict","isRefsEqual","v1","v2","BaseStream","isEmpty","isDataLoaded","getByte","getBytes","peekByte","peekedByte","pos","peekBytes","getUint16","b0","b1","getInt32","b2","b3","getByteRange","begin","end","getString","skip","reset","moveStart","makeSubStream","start","getBaseStreams","PDF_VERSION_REGEXP","getLookupTableFactory","initializer","lookup","MissingDataException","ParserEOFException","XRefEntryException","XRefParseException","arrayBuffersToBytes","arr","dataLength","byteLength","data","item","getInheritableProperty","stopWhenFound","visited","ROMAN_NUMBER_MAP","toRomanNumerals","number","lowerCase","romanBuf","romanStr","toLowerCase","log2","ceil","readInt8","offset","readUint16","readUint32","isWhiteSpace","ch","isBooleanArray","len","every","isNumberArray","lookupMatrix","fallback","lookupRect","lookupNormalRect","parseXFAPath","path","positionPattern","split","component","escapePDFName","char","substring","escapeString","_collectJS","entry","list","parents","element","js","collectActions","eventType","actions","additionalActionsDicts","additionalActions","action","actionDict","Action","XMLEntities","codePointIter","codePointAt","encodeToXmlString","entity","toUpperCase","validateFontName","fontFamily","mustWarn","re","RegExp","test","ident","validateCSSFont","cssFontInfo","DEFAULT_CSS_FONT_OBLIQUE","DEFAULT_CSS_FONT_WEIGHT","CSS_FONT_WEIGHT_VALUES","fontWeight","italicAngle","weight","angle","parseFloat","isNaN","recoverJsURL","URL_OPEN_METHODS","regex","jsUrl","newWindow","numberToString","roundedValue","round","toFixed","getNewAnnotationsMap","annotationStorage","newAnnotationsByPage","annotations","pageIndex","isAscii","stringToUTF16HexString","stringToUTF16String","bigEndian","getRotationMatrix","rotation","width","height","getSizeInBytes","Stream","arrayBuffer","strEnd","StringStream","NullStream","ChunkedStream","chunkSize","manager","_loadedChunks","numChunks","progressiveDataLength","lastSuccessfulEnsureByteChunk","getMissingChunks","chunks","numChunksLoaded","onReceiveData","beginChunk","endChunk","curChunk","onReceiveProgressiveData","position","ensureByte","ensureRange","nextEmptyChunk","hasChunk","ChunkedStreamSubstream","missingChunks","subStream","ChunkedStreamManager","pdfNetworkStream","args","rangeChunkSize","stream","disableAutoFetch","msgHandler","currRequestId","_chunksNeededByRequest","_requestsByChunk","_promisesByRequest","aborted","_loadedStreamCapability","Promise","withResolvers","sendRequest","rangeReader","getRangeReader","isStreamingSupported","onProgress","bind","loaded","resolve","reject","readChunk","done","chunkData","read","then","e","requestAllChunks","noFetch","_requestChunks","promise","requestId","chunksNeeded","capability","chunksToRequest","requestIds","groupedChunksToRequest","groupChunks","groupedChunk","catch","reason","getStream","requestRange","getBeginChunk","getEndChunk","requestRanges","ranges","range","sort","groupedChunks","prevChunk","send","total","isProgressive","loadedRequests","lastChunk","onError","err","abort","cancelAllRequests","resizeRgbImage","src","dest","w1","h1","w2","h2","alpha01","COMPONENTS","xRatio","yRatio","newIndex","oldIndex","xScaled","Uint16Array","w1Scanline","py","j","ColorSpace","numComps","getRgb","srcOffset","rgb","Uint8ClampedArray","getRgbItem","destOffset","getRgbBuffer","count","bits","getOutputLength","inputLength","isPassthrough","isDefaultDecode","decodeMap","bpc","fillRgb","originalWidth","originalHeight","actualHeight","comps","rgbBuf","numComponentColors","needsResizing","allColors","colorMap","destPos","rgbPos","usesZeroToOneRange","_cache","cacheKey","localColorSpaceCache","parsedColorSpace","csName","csRef","getCached","localColorSpace","getByRef","getByName","parseAsync","cs","resources","pdfFunctionFactory","_parse","parse","cachedColorSpace","fetchIfRef","singletons","gray","cmyk","PatternCS","colorSpaces","resourcesCS","mode","params","baseCS","whitePoint","blackPoint","gamma","CalGrayCS","matrix","CalRGBCS","alt","altCS","hiVal","IndexedCS","tintFn","AlternateCS","LabCS","DeviceGrayCS","DeviceRgbCS","DeviceCmykCS","base","tmpBuf","Float32Array","scale","baseNumComps","baseBuf","scaled","tinted","highVal","outputDelta","lookupPos","q","toRgb","#toRgb","srcScale","k","XW","YW","ZW","XB","YB","ZB","G","A","AG","L","val","BRADFORD_SCALE_MATRIX","BRADFORD_SCALE_INVERSE_MATRIX","SRGB_D65_XYZ_TO_RGB_MATRIX","FLAT_WHITEPOINT_MATRIX","tempNormalizeMatrix","tempConvertMatrix1","tempConvertMatrix2","DECODE_L_CONSTANT","GR","GG","GB","MXA","MYA","MZA","MXB","MYB","MZB","MXC","MYC","MZC","matrixProduct","#matrixProduct","result","toFlat","#toFlat","sourceWhitePoint","LMS","toD65","#toD65","D65X","D65Y","D65Z","sRGBTransferFunction","#sRGBTransferFunction","color","adjustToRange","#adjustToRange","decodeL","#decodeL","compensateBlackPoint","#compensateBlackPoint","sourceBlackPoint","XYZ_Flat","zeroDecodeL","X_DST","X_SRC","Y_DST","Y_SRC","Z_DST","Z_SRC","X_Scale","X_Offset","Y_Scale","Y_Offset","Z_Scale","Z_Offset","normalizeWhitePointToFlat","#normalizeWhitePointToFlat","XYZ_In","LMS_Flat","normalizeWhitePointToD65","#normalizeWhitePointToD65","LMS_D65","B","AGR","BGG","CGB","Y","Z","XYZ","XYZ_Black","XYZ_D65","SRGB","amin","amax","bmin","bmax","fn_g","#fn_g","#decode","high1","low2","high2","maxVal","Ls","as","bs","M","N","hexToInt","hexToStr","addHex","incHex","MAX_NUM_SIZE","MAX_ENCODED_NUM_SIZE","BinaryCMapStream","readByte","readNumber","last","readSigned","readHex","readHexNumber","stack","sp","bufferSize","readHexSigned","sign","readString","BinaryCMapReader","cMap","extend","header","vertical","useCMap","tmp","sequence","dataSize","ucs2DataSize","subitemsCount","addCodespaceRange","mapOne","mapCidRange","mapBfRange","emptyBuffer","DecodeStream","maybeMinBufferLength","_rawMinBufferLength","bufferLength","eof","minBufferLength","readBlock","ensureBuffer","requested","buffer2","ignoreColorSpace","bufEnd","StreamsSequenceStream","streams","maybeLength","_onError","shift","newLength","baseStreamsBuf","baseStreams","Ascii85Stream","input","TILDA_CHAR","Z_LOWER_CHAR","AsciiHexStream","firstDigit","UPSTREAM_BLOCK_SIZE","maxDecodeLength","digit","ccittEOL","ccittEOF","twoDimPass","twoDimHoriz","twoDimVert0","twoDimVertR1","twoDimVertL1","twoDimVertR2","twoDimVertL2","twoDimVertR3","twoDimVertL3","twoDimTable","whiteTable1","whiteTable2","blackTable1","blackTable2","blackTable3","CCITTFaxDecoder","source","next","eoline","EndOfLine","byteAlign","EncodedByteAlign","columns","Columns","rows","Rows","eoblock","EndOfBlock","black","BlackIs1","codingLine","refLine","codingPos","row","nextLine2D","inputBits","inputBuf","outputBits","rowsDone","code1","_lookBits","_eatBits","readNextChar","refPos","blackPixels","code2","code3","_getTwoDimCode","_addPixels","_getBlackCode","_getWhiteCode","_addPixelsNeg","gotEOL","a1","_findTableCode","table","limit","limitValue","CCITTFaxStream","ccittFaxDecoder","codeLenCodeMap","Int32Array","lengthDecode","distDecode","fixedLitCodeTab","fixedDistCodeTab","FlateStream","cmf","flg","codeSize","codeBuf","getBits","getCode","codes","maxLen","codeLen","codeVal","generateHuffmanTable","lengths","endsStreamOnError","#endsStreamOnError","hdr","blockLen","check","block","litCodeTable","distCodeTable","numLitCodes","numDistCodes","numCodeLenCodes","codeLenCodeLengths","codeLenCodeTab","codeLengths","bitsLength","bitsOffset","what","repeatLength","dist","QeTable","qe","nmps","nlps","switchFlag","ArithmeticDecoder","bp","dataEnd","chigh","clow","byteIn","ct","readBit","contexts","cx_index","cx_mps","qeTableIcx","qeIcx","Jbig2Error","ContextCache","getContexts","id","Int8Array","DecodingContext","contextCache","cache","MAX_INT_32","MIN_INT_32","decodeInteger","procedure","prev","readBits","bit","signedValue","decodeIAID","codeLength","SegmentTypes","CodingTemplates","RefinementTemplates","coding","reference","ReusedContexts","RefinementReusedContexts","decodeBitmapTemplate0","decodingContext","bitmap","contextLabel","pixel","row1","row2","OLD_PIXEL_MASK","decodeBitmap","mmr","templateIndex","prediction","at","Reader","decodeMMRBitmap","useskip","template","concat","templateLength","templateX","templateY","changingTemplateEntries","reuseMask","minX","maxX","minY","changingEntriesLength","changingTemplateX","changingTemplateY","changingTemplateBit","sbb_left","sbb_top","sbb_right","pseudoPixelContext","ltp","i0","j0","sltp","decodeRefinement","referenceBitmap","offsetX","offsetY","codingTemplate","codingTemplateLength","codingTemplateX","codingTemplateY","referenceTemplate","referenceTemplateLength","referenceTemplateX","referenceTemplateY","referenceWidth","referenceHeight","decodeSymbolDictionary","huffman","refinement","symbols","numberOfNewSymbols","numberOfExportedSymbols","huffmanTables","refinementTemplateIndex","refinementAt","huffmanInput","newSymbols","currentHeight","symbolCodeLength","tableB1","symbolWidths","getStandardTable","deltaHeight","tableDeltaHeight","currentWidth","totalWidth","firstSymbol","deltaWidth","tableDeltaWidth","numberOfInstances","decodeTextRegion","symbolId","rdx","rdy","symbol","bitmapSize","tableBitmapSize","collectiveBitmap","readUncompressedBitmap","originalEnd","bitmapEnd","numberOfSymbolsDecoded","xMin","xMax","bitmapWidth","symbolBitmap","exportedSymbols","flags","currentFlag","totalSymbolsLength","runLength","defaultPixelValue","numberOfSymbolInstances","stripSize","inputSymbols","transposed","dsOffset","referenceCorner","combinationOperator","logStripSize","stripT","tableDeltaT","firstS","deltaT","deltaFirstS","tableFirstS","currentS","currentT","symbolIDTable","applyRefinement","symbolWidth","symbolHeight","rdw","rdh","increment","offsetT","offsetS","s2","t2","symbolRow","maxWidth","deltaS","tableDeltaS","decodePatternDictionary","patternWidth","patternHeight","maxPatternIndex","collectiveWidth","patterns","patternBitmap","decodeHalftoneRegion","regionWidth","regionHeight","enableSkip","gridWidth","gridHeight","gridOffsetX","gridOffsetY","gridVectorX","gridVectorY","regionBitmap","numberOfPatterns","pattern0","bitsPerValue","grayScaleBitPlanes","mmrInput","mg","ng","patternIndex","patternRow","regionRow","regionX","regionY","readSegmentHeader","segmentHeader","segmentType","typeName","deferredNonRetain","pageAssociationFieldSize","referredFlags","referredToCount","retainBits","referredToSegmentNumberSize","referredTo","pageAssociation","genericRegionInfo","readRegionSegmentInformation","genericRegionSegmentFlags","RegionSegmentInformationFieldLength","genericRegionMmr","searchPatternLength","searchPattern","headerEnd","readSegments","segments","segment","randomAccess","processSegment","visitor","atLength","dictionary","dictionaryFlags","huffmanDHSelector","huffmanDWSelector","bitmapSizeSelector","aggregationInstancesSelector","bitmapCodingContextUsed","bitmapCodingContextRetained","refinementTemplate","textRegion","textRegionSegmentFlags","textRegionHuffmanFlags","huffmanFS","huffmanDS","huffmanDT","huffmanRefinementDW","huffmanRefinementDH","huffmanRefinementDX","huffmanRefinementDY","huffmanRefinementSizeSelector","patternDictionary","patternDictionaryFlags","halftoneRegion","halftoneRegionFlags","genericRegion","pageInfo","resolutionX","resolutionY","pageSegmentFlags","lossless","requiresBuffer","combinationOperatorOverride","callbackName","processSegments","parseJbig2Chunks","SimpleSegmentVisitor","parseJbig2","onPageInformation","currentPageInfo","rowSize","drawBitmap","regionInfo","mask0","offset0","mask","onImmediateGenericRegion","region","onImmediateLosslessGenericRegion","arguments","onSymbolDictionary","currentSegment","referredSegments","getSymbolDictionaryHuffmanTables","customTables","referredSegment","referredSymbols","onImmediateTextRegion","getTextRegionHuffmanTables","onImmediateLosslessTextRegion","onPatternDictionary","onImmediateHalftoneRegion","onImmediateLosslessHalftoneRegion","onTables","decodeTablesSegment","HuffmanLine","lineData","isOOB","rangeLow","prefixLength","rangeLength","prefixCode","isLowerRange","HuffmanTreeNode","line","children","isLeaf","buildTree","node","decodeNode","reader","htOffset","HuffmanTable","lines","prefixCodesDone","assignPrefixCodes","rootNode","linesLength","prefixLengthMax","histogram","currentLength","firstCode","currentCode","currentTemp","lowestValue","highestValue","prefixSizeBits","rangeSizeBits","currentRangeLow","standardTablesCache","currentByte","numBits","getCustomHuffmanTable","index","currentIndex","numberOfSymbols","runCodesTable","repeatedLength","numberOfRepeats","customIndex","tableAggregateInstances","endOfBlock","lookForEOFLimit","Jbig2Image","parseChunks","Jbig2Stream","jbig2Image","globalsStream","globals","convertToRGBA","kind","convertBlackAndWhiteToRGBA","convertRGBToRGBA","srcPos","nonBlackColor","inverseDecode","zeroMapping","oneMapping","widthInSource","widthRemainder","srcLength","elem","len32","src32","s1","s3","jj","grayToRGBA","JpegError","DNLMarkerError","scanLines","EOIMarkerError","dctZigZag","dctCos1","dctSin1","dctCos3","dctSin3","dctCos6","dctSin6","dctSqrt2","dctSqrt1d2","buildHuffmanTable","pop","getBlockBufferOffset","col","blocksPerLine","decodeScan","frame","components","resetInterval","spectralStart","spectralEnd","successivePrev","successive","parseDNLMarker","mcusPerLine","progressive","startOffset","bitsData","bitsCount","nextByte","maybeScanLines","blockRow","precision","decodeHuffman","tree","receive","receiveAndExtend","decodeBaseline","blockOffset","huffmanTableDC","diff","blockData","pred","rs","huffmanTableAC","s","z","decodeDCFirst","decodeDCSuccessive","eobrun","decodeACFirst","successiveACState","successiveACNextValue","decodeACSuccessive","offsetZ","decodeMcu","mcu","mcuRow","mcuCol","blockCol","h","decodeBlock","componentsLength","decodeFn","fileMarker","mcuExpected","blocksPerColumn","mcusPerColumn","mcuToRead","findNextFileMarker","invalid","partialMsg","marker","quantizeAndInverse","blockBufferOffset","qt","quantizationTable","v0","v3","v4","v5","v6","v7","p0","p5","p6","p7","buildComponentData","computationBuffer","Int16Array","currentPos","startPos","maxPos","newPos","currentMarker","newMarker","JpegImage","decodeTransform","colorTransform","_decodeTransform","_colorTransform","dnlScanLines","readDataBlock","endOffset","array","prepareComponents","samplesPerLine","maxH","maxV","blocksPerLineForMcu","blocksPerColumnForMcu","blocksBufferSize","jfif","adobe","numSOSMarkers","quantizationTables","huffmanTablesAC","huffmanTablesDC","markerLoop","l","appData","version","major","minor","densityUnits","xDensity","yDensity","thumbWidth","thumbHeight","thumbData","flags0","flags1","transformCode","quantizationTablesLength","quantizationTablesEnd","quantizationTableSpec","tableData","extended","sofScanLines","componentIds","componentsCount","componentId","qId","quantizationId","huffmanLength","huffmanTableSpec","codeLengthSum","huffmanValues","selectorsCount","componentIndex","tableSpec","successiveApproximation","processed","nextFileMarker","output","scaleX","scaleY","numComponents","_getLinearizedBlockData","isSourcePDF","componentScaleX","componentScaleY","blocksPerScanline","xScaleBlockOffset","mask3LSB","lastComponentScaleX","_isColorConversionNeeded","_convertYccToRgb","Cb","Cr","_convertYccToRgba","out","_convertYcckToRgb","_convertYcckToRgba","_convertYcckToCmyk","_convertCmykToRgb","_convertCmykToRgba","getData","forceRGBA","forceRGB","rgbaData","grayColor","JpegStream","jpegOptions","decodeArr","bitsPerComponent","decodeArrLength","transformNeeded","maxValue","jpegImage","drawWidth","drawHeight","OpenJPEG","_scriptName","document","currentScript","moduleArg","moduleRtn","Module","assign","readyPromiseResolve","readyPromiseReject","readyPromise","ENVIRONMENT_IS_WEB","ENVIRONMENT_IS_WORKER","ptr","_malloc","HEAPU8","ret","_jp2_decode","_free","errorMessages","imageData","moduleOverrides","arguments_","thisProgram","quit_","toThrow","scriptDirectory","read_","readAsync","readBinary","self","location","href","substr","replace","lastIndexOf","xhr","XMLHttpRequest","open","responseText","responseType","response","onload","onerror","error","wasmBinary","intArrayFromBase64","atob","tryParseAsDataURI","filename","isDataURI","dataURIPrefix","wasmMemory","ABORT","HEAP8","HEAP16","HEAPU16","HEAP32","HEAPU32","HEAPF32","HEAPF64","updateMemoryViews","Float64Array","__ATPRERUN__","__ATINIT__","__ATPOSTRUN__","runtimeInitialized","preRun","addOnPreRun","callRuntimeCallbacks","initRuntime","postRun","addOnPostRun","cb","unshift","addOnInit","runDependencies","runDependencyWatcher","dependenciesFulfilled","addRunDependency","removeRunDependency","clearInterval","findWasmBinary","f","wasmBinaryFile","getBinarySync","file","binary","instantiateSync","module","WebAssembly","instance","Instance","getWasmImports","wasmImports","createWasm","receiveInstance","wasmExports","exports","callbacks","noExitRuntime","__emscripten_memcpy_js","copyWithin","getHeapMax","growMemory","pages","grow","_emscripten_resize_heap","requestedSize","oldSize","maxHeapSize","alignUp","multiple","cutDown","overGrownHeapSize","newSize","replacement","ENV","getExecutableName","getEnvStrings","strings","lang","languages","env","stringToAscii","_environ_get","__environ","environ_buf","bufSize","string","_environ_sizes_get","penviron_count","penviron_buf_size","printCharBuffers","UTF8Decoder","UTF8ArrayToString","heapOrArray","idx","maxBytesToRead","endIdx","endPtr","u0","u1","u2","printChar","curr","UTF8ToString","_fd_write","fd","iov","iovcnt","pnum","_jsPrintWarning","message_ptr","_setImageData","array_ptr","array_size","_storeErrorMessage","___wasm_call_ctors","__emscripten_stack_restore","__emscripten_stack_alloc","_emscripten_stack_get_current","calledRun","runCaller","run","doRun","setTimeout","JpxError","JpxImage","cleanup","parseImageProperties","newByte","oldByte","Xsiz","Ysiz","XOsiz","YOsiz","Csiz","JpxStream","LZWStream","earlyChange","cachedData","bitsCached","maxLzwDictionarySize","lzwState","nextCode","dictionaryValues","dictionaryLengths","dictionaryPrevCodes","currentSequence","currentSequenceLength","lastCode","blockSize","decodedSizeDelta","estimatedDecodedSize","prevCode","decodedLength","currentBufferLength","hasPrev","PredictorStream","predictor","readBlockTiff","readBlockPng","colors","pixBytes","rowBytes","rawBytes","inbuf","outbuf","inbits","outbits","bytesPerPixel","sum","compArray","bitMask","kk","prevRow","up","upLeft","left","pa","pb","pc","RunLengthStream","repeatHeader","MAX_LENGTH_TO_CACHE","getInlineImageCacheKey","Parser","lexer","allowStreams","recoveryMode","imageCache","_imageId","refill","buf1","getObj","buf2","tryShift","cipherTransform","makeInlineImage","makeStream","decryptString","findDefaultInlineStreamEnd","I","SPACE","LF","CR","NUL","knownCommands","state","maybeEIPos","followingBytes","tmpLexer","Lexer","_hexStringWarn","numArgs","nextObj","knownCommand","variableArgs","findDCTDecodeInlineStreamEnd","foundEOI","markerLength","inlineStreamSkipEI","findASCII85DecodeInlineStreamEnd","TILDE","GT","tildePos","maybeEI","findASCIIHexDecodeInlineStreamEnd","dictMap","dictLength","beginInlineImagePos","filter","Filter","filterName","filterZero","initialStreamPos","cacheEntry","imageStream","createStream","findStreamLength","#findStreamLength","SCAN_BLOCK_LENGTH","signatureLength","END_SIGNATURE","endLength","PARTIAL_SIGNATURE","normalLength","scanBytes","scanLength","found","part","partLen","lastByte","skipToNextLine","nextChar","makeFilter","filterArray","paramsArray","specialChars","toHexDigit","_hexStringNumWarn","currentChar","peekChar","getNumber","eNotation","divideBy","baseValue","powerValue","powerValueSign","currentDigit","numParen","charBuffered","getName","previousCh","MAX_HEX_STRING_NUM_WARN","getHexString","isFirstHex","secondDigit","comment","nextCh","knownCommandFound","possibleCommand","Linearization","getInt","linDict","allowZeroValue","getHints","hints","hintsLength","hint","parser","obj1","obj2","obj3","objectNumberFirst","endFirst","numPages","mainXRefEntriesOffset","pageFirst","BUILT_IN_CMAPS","MAX_MAP_RANGE","CMap","builtInCMap","codespaceRanges","numCodespaceRanges","low","high","dstLow","nextCharCode","mapBfRangeToArray","dst","contains","charCodeOf","indexOf","getMap","readCharCode","nn","codespaceRange","charcode","getCharCodeLength","isIdentityCMap","IdentityCMap","strToInt","expectString","expectInt","parseBfChar","parseBfRange","parseCidChar","parseCidRange","parseCodespaceRange","parseWMode","parseCMapName","parseCMap","fetchBuiltInCMap","previous","embeddedUseCMap","objLoop","extendCMap","createBuiltInCMap","useCodespaceRanges","cMapData","compressionType","CMapFactory","parsedCMap","ISOAdobeCharset","ExpertCharset","ExpertSubsetCharset","ExpertEncoding","MacExpertEncoding","MacRomanEncoding","StandardEncoding","WinAnsiEncoding","SymbolSetEncoding","ZapfDingbatsEncoding","getEncoding","encodingName","MAX_SUBR_NESTING","CFFStandardStrings","NUM_STANDARD_CFF_STRINGS","CharstringValidationData","stackClearing","stem","resetStack","undefStack","CharstringValidationData12","stackDelta","stackFn","CFFParser","seacAnalysisEnabled","cff","CFF","parseHeader","nameIndex","parseIndex","endPos","topDictIndex","stringIndex","globalSubrIndex","topDictParsed","parseDict","topDict","createDict","CFFTopDict","names","parseNameIndex","parseStringIndex","parsePrivateDict","isCIDFont","hasName","charStringOffset","charStringIndex","fontMatrix","fontBBox","ascent","descent","ascentScaled","charset","fdArrayIndex","dictRaw","fontDict","fdArray","parseCharsets","fdSelect","parseFDSelect","parseEncoding","charStringsAndSeacs","parseCharStrings","charStrings","localSubrIndex","privateDict","subrsIndex","seacs","widths","bytesLength","hdrSize","offSize","CFFHeader","parseOperand","parseFloatOperand","NaN","operands","cffIndex","CFFIndex","offsets","offsetSize","offsetStart","offsetEnd","CFFStrings","Type","cffDict","setByKey","parseCharString","callDepth","stackSize","validationCommand","seac","bias","subrNumber","valid","hasVStems","firstStackClearing","charstring","localSubrToUse","privateDictToUse","fdIndex","getFDIndex","nominalWidth","defaultWidth","emptyPrivateDictionary","parentDict","CFFPrivateDict","privateOffset","removeByName","privateDictEnd","dictData","setByName","subrsOffset","relativeOffset","cid","CFFCharset","CFFCharsetPredefinedTypes","ISO_ADOBE","EXPERT","EXPERT_SUBSET","format","raw","predefined","readSupplement","supplementsCount","sid","baseEncoding","dataStart","glyphsCount","rangesCount","gid","CFFEncoding","CFFFDSelect","duplicateFirstGlyph","glyphZero","hasGlyphId","glyph","getSID","objects","CFFDict","tables","keyToNameMap","nameToKeyMap","defaults","types","opcodes","order","createTables","layout","CFFTopDictLayout","CFFPrivateDictLayout","glyphIndex","CFFOffsetTracker","isTracking","track","setEntryLocation","dataOffset","offset1","offset2","offset3","offset4","CFFCompiler","compile","compileHeader","compileNameIndex","xuid","compiled","compileTopDicts","topDictTracker","trackers","compileStringIndex","compileIndex","compileEncoding","compileCharset","compileCharStrings","compileFDSelect","fontDictTrackers","compilePrivateDicts","encodeNumber","encodeInteger","encodeFloat","EncodeFloatRegExp","epsilon","nibbles","sanitizedName","dicts","removeCidKeys","fontDictTracker","fontDictData","compileDict","privateDictTracker","privateDictData","outputLength","subrs","offsetTracker","charStringsIndex","numGlyphs","numGlyphsLessNotDef","charsetIndex","numCharsets","warned","compileTypedArray","lastFD","currentFD","numRanges","lastOffset","getGlyphsUnicode","AE","AEacute","AEmacron","AEsmall","Aacute","Aacutesmall","Abreve","Abreveacute","Abrevecyrillic","Abrevedotbelow","Abrevegrave","Abrevehookabove","Abrevetilde","Acaron","Acircle","Acircumflex","Acircumflexacute","Acircumflexdotbelow","Acircumflexgrave","Acircumflexhookabove","Acircumflexsmall","Acircumflextilde","Acute","Acutesmall","Acyrillic","Adblgrave","Adieresis","Adieresiscyrillic","Adieresismacron","Adieresissmall","Adotbelow","Adotmacron","Agrave","Agravesmall","Ahookabove","Aiecyrillic","Ainvertedbreve","Alpha","Alphatonos","Amacron","Amonospace","Aogonek","Aring","Aringacute","Aringbelow","Aringsmall","Asmall","Atilde","Atildesmall","Aybarmenian","Bcircle","Bdotaccent","Bdotbelow","Becyrillic","Benarmenian","Beta","Bhook","Blinebelow","Bmonospace","Brevesmall","Bsmall","Btopbar","Caarmenian","Cacute","Caron","Caronsmall","Ccaron","Ccedilla","Ccedillaacute","Ccedillasmall","Ccircle","Ccircumflex","Cdot","Cdotaccent","Cedillasmall","Chaarmenian","Cheabkhasiancyrillic","Checyrillic","Chedescenderabkhasiancyrillic","Chedescendercyrillic","Chedieresiscyrillic","Cheharmenian","Chekhakassiancyrillic","Cheverticalstrokecyrillic","Chi","Chook","Circumflexsmall","Cmonospace","Coarmenian","Csmall","DZ","DZcaron","Daarmenian","Dafrican","Dcaron","Dcedilla","Dcircle","Dcircumflexbelow","Dcroat","Ddotaccent","Ddotbelow","Decyrillic","Deicoptic","Delta","Deltagreek","Dhook","Dieresis","DieresisAcute","DieresisGrave","Dieresissmall","Digammagreek","Djecyrillic","Dlinebelow","Dmonospace","Dotaccentsmall","Dslash","Dsmall","Dtopbar","Dz","Dzcaron","Dzeabkhasiancyrillic","Dzecyrillic","Dzhecyrillic","Eacute","Eacutesmall","Ebreve","Ecaron","Ecedillabreve","Echarmenian","Ecircle","Ecircumflex","Ecircumflexacute","Ecircumflexbelow","Ecircumflexdotbelow","Ecircumflexgrave","Ecircumflexhookabove","Ecircumflexsmall","Ecircumflextilde","Ecyrillic","Edblgrave","Edieresis","Edieresissmall","Edot","Edotaccent","Edotbelow","Efcyrillic","Egrave","Egravesmall","Eharmenian","Ehookabove","Eightroman","Einvertedbreve","Eiotifiedcyrillic","Elcyrillic","Elevenroman","Emacron","Emacronacute","Emacrongrave","Emcyrillic","Emonospace","Encyrillic","Endescendercyrillic","Eng","Enghecyrillic","Enhookcyrillic","Eogonek","Eopen","Epsilon","Epsilontonos","Ercyrillic","Ereversed","Ereversedcyrillic","Escyrillic","Esdescendercyrillic","Esh","Esmall","Eta","Etarmenian","Etatonos","Eth","Ethsmall","Etilde","Etildebelow","Euro","Ezh","Ezhcaron","Ezhreversed","Fcircle","Fdotaccent","Feharmenian","Feicoptic","Fhook","Fitacyrillic","Fiveroman","Fmonospace","Fourroman","Fsmall","GBsquare","Gacute","Gamma","Gammaafrican","Gangiacoptic","Gbreve","Gcaron","Gcedilla","Gcircle","Gcircumflex","Gcommaaccent","Gdot","Gdotaccent","Gecyrillic","Ghadarmenian","Ghemiddlehookcyrillic","Ghestrokecyrillic","Gheupturncyrillic","Ghook","Gimarmenian","Gjecyrillic","Gmacron","Gmonospace","Grave","Gravesmall","Gsmall","Gsmallhook","Gstroke","H","H18533","H18543","H18551","H22073","HPsquare","Haabkhasiancyrillic","Hadescendercyrillic","Hardsigncyrillic","Hbar","Hbrevebelow","Hcedilla","Hcircle","Hcircumflex","Hdieresis","Hdotaccent","Hdotbelow","Hmonospace","Hoarmenian","Horicoptic","Hsmall","Hungarumlaut","Hungarumlautsmall","Hzsquare","IAcyrillic","IJ","IUcyrillic","Iacute","Iacutesmall","Ibreve","Icaron","Icircle","Icircumflex","Icircumflexsmall","Icyrillic","Idblgrave","Idieresis","Idieresisacute","Idieresiscyrillic","Idieresissmall","Idot","Idotaccent","Idotbelow","Iebrevecyrillic","Iecyrillic","Ifraktur","Igrave","Igravesmall","Ihookabove","Iicyrillic","Iinvertedbreve","Iishortcyrillic","Imacron","Imacroncyrillic","Imonospace","Iniarmenian","Iocyrillic","Iogonek","Iota","Iotaafrican","Iotadieresis","Iotatonos","Ismall","Istroke","Itilde","Itildebelow","Izhitsacyrillic","Izhitsadblgravecyrillic","J","Jaarmenian","Jcircle","Jcircumflex","Jecyrillic","Jheharmenian","Jmonospace","Jsmall","KBsquare","KKsquare","Kabashkircyrillic","Kacute","Kacyrillic","Kadescendercyrillic","Kahookcyrillic","Kappa","Kastrokecyrillic","Kaverticalstrokecyrillic","Kcaron","Kcedilla","Kcircle","Kcommaaccent","Kdotbelow","Keharmenian","Kenarmenian","Khacyrillic","Kheicoptic","Khook","Kjecyrillic","Klinebelow","Kmonospace","Koppacyrillic","Koppagreek","Ksicyrillic","Ksmall","LJ","LL","Lacute","Lambda","Lcaron","Lcedilla","Lcircle","Lcircumflexbelow","Lcommaaccent","Ldot","Ldotaccent","Ldotbelow","Ldotbelowmacron","Liwnarmenian","Lj","Ljecyrillic","Llinebelow","Lmonospace","Lslash","Lslashsmall","Lsmall","MBsquare","Macron","Macronsmall","Macute","Mcircle","Mdotaccent","Mdotbelow","Menarmenian","Mmonospace","Msmall","Mturned","Mu","NJ","Nacute","Ncaron","Ncedilla","Ncircle","Ncircumflexbelow","Ncommaaccent","Ndotaccent","Ndotbelow","Nhookleft","Nineroman","Nj","Njecyrillic","Nlinebelow","Nmonospace","Nowarmenian","Nsmall","Ntilde","Ntildesmall","Nu","OE","OEsmall","Oacute","Oacutesmall","Obarredcyrillic","Obarreddieresiscyrillic","Obreve","Ocaron","Ocenteredtilde","Ocircle","Ocircumflex","Ocircumflexacute","Ocircumflexdotbelow","Ocircumflexgrave","Ocircumflexhookabove","Ocircumflexsmall","Ocircumflextilde","Ocyrillic","Odblacute","Odblgrave","Odieresis","Odieresiscyrillic","Odieresissmall","Odotbelow","Ogoneksmall","Ograve","Ogravesmall","Oharmenian","Ohm","Ohookabove","Ohorn","Ohornacute","Ohorndotbelow","Ohorngrave","Ohornhookabove","Ohorntilde","Ohungarumlaut","Oi","Oinvertedbreve","Omacron","Omacronacute","Omacrongrave","Omega","Omegacyrillic","Omegagreek","Omegaroundcyrillic","Omegatitlocyrillic","Omegatonos","Omicron","Omicrontonos","Omonospace","Oneroman","Oogonek","Oogonekmacron","Oopen","Oslash","Oslashacute","Oslashsmall","Osmall","Ostrokeacute","Otcyrillic","Otilde","Otildeacute","Otildedieresis","Otildesmall","P","Pacute","Pcircle","Pdotaccent","Pecyrillic","Peharmenian","Pemiddlehookcyrillic","Phi","Phook","Pi","Piwrarmenian","Pmonospace","Psi","Psicyrillic","Psmall","Q","Qcircle","Qmonospace","Qsmall","R","Raarmenian","Racute","Rcaron","Rcedilla","Rcircle","Rcommaaccent","Rdblgrave","Rdotaccent","Rdotbelow","Rdotbelowmacron","Reharmenian","Rfraktur","Rho","Ringsmall","Rinvertedbreve","Rlinebelow","Rmonospace","Rsmall","Rsmallinverted","Rsmallinvertedsuperior","S","SF010000","SF020000","SF030000","SF040000","SF050000","SF060000","SF070000","SF080000","SF090000","SF100000","SF110000","SF190000","SF200000","SF210000","SF220000","SF230000","SF240000","SF250000","SF260000","SF270000","SF280000","SF360000","SF370000","SF380000","SF390000","SF400000","SF410000","SF420000","SF430000","SF440000","SF450000","SF460000","SF470000","SF480000","SF490000","SF500000","SF510000","SF520000","SF530000","SF540000","Sacute","Sacutedotaccent","Sampigreek","Scaron","Scarondotaccent","Scaronsmall","Scedilla","Schwa","Schwacyrillic","Schwadieresiscyrillic","Scircle","Scircumflex","Scommaaccent","Sdotaccent","Sdotbelow","Sdotbelowdotaccent","Seharmenian","Sevenroman","Shaarmenian","Shacyrillic","Shchacyrillic","Sheicoptic","Shhacyrillic","Shimacoptic","Sigma","Sixroman","Smonospace","Softsigncyrillic","Ssmall","Stigmagreek","T","Tau","Tbar","Tcaron","Tcedilla","Tcircle","Tcircumflexbelow","Tcommaaccent","Tdotaccent","Tdotbelow","Tecyrillic","Tedescendercyrillic","Tenroman","Tetsecyrillic","Theta","Thook","Thorn","Thornsmall","Threeroman","Tildesmall","Tiwnarmenian","Tlinebelow","Tmonospace","Toarmenian","Tonefive","Tonesix","Tonetwo","Tretroflexhook","Tsecyrillic","Tshecyrillic","Tsmall","Twelveroman","Tworoman","Uacute","Uacutesmall","Ubreve","Ucaron","Ucircle","Ucircumflex","Ucircumflexbelow","Ucircumflexsmall","Ucyrillic","Udblacute","Udblgrave","Udieresis","Udieresisacute","Udieresisbelow","Udieresiscaron","Udieresiscyrillic","Udieresisgrave","Udieresismacron","Udieresissmall","Udotbelow","Ugrave","Ugravesmall","Uhookabove","Uhorn","Uhornacute","Uhorndotbelow","Uhorngrave","Uhornhookabove","Uhorntilde","Uhungarumlaut","Uhungarumlautcyrillic","Uinvertedbreve","Ukcyrillic","Umacron","Umacroncyrillic","Umacrondieresis","Umonospace","Uogonek","Upsilon","Upsilon1","Upsilonacutehooksymbolgreek","Upsilonafrican","Upsilondieresis","Upsilondieresishooksymbolgreek","Upsilonhooksymbol","Upsilontonos","Uring","Ushortcyrillic","Usmall","Ustraightcyrillic","Ustraightstrokecyrillic","Utilde","Utildeacute","Utildebelow","Vcircle","Vdotbelow","Vecyrillic","Vewarmenian","Vhook","Vmonospace","Voarmenian","Vsmall","Vtilde","W","Wacute","Wcircle","Wcircumflex","Wdieresis","Wdotaccent","Wdotbelow","Wgrave","Wmonospace","Wsmall","Xcircle","Xdieresis","Xdotaccent","Xeharmenian","Xi","Xmonospace","Xsmall","Yacute","Yacutesmall","Yatcyrillic","Ycircle","Ycircumflex","Ydieresis","Ydieresissmall","Ydotaccent","Ydotbelow","Yericyrillic","Yerudieresiscyrillic","Ygrave","Yhook","Yhookabove","Yiarmenian","Yicyrillic","Yiwnarmenian","Ymonospace","Ysmall","Ytilde","Yusbigcyrillic","Yusbigiotifiedcyrillic","Yuslittlecyrillic","Yuslittleiotifiedcyrillic","Zaarmenian","Zacute","Zcaron","Zcaronsmall","Zcircle","Zcircumflex","Zdot","Zdotaccent","Zdotbelow","Zecyrillic","Zedescendercyrillic","Zedieresiscyrillic","Zeta","Zhearmenian","Zhebrevecyrillic","Zhecyrillic","Zhedescendercyrillic","Zhedieresiscyrillic","Zlinebelow","Zmonospace","Zsmall","Zstroke","aabengali","aacute","aadeva","aagujarati","aagurmukhi","aamatragurmukhi","aarusquare","aavowelsignbengali","aavowelsigndeva","aavowelsigngujarati","abbreviationmarkarmenian","abbreviationsigndeva","abengali","abopomofo","abreve","abreveacute","abrevecyrillic","abrevedotbelow","abrevegrave","abrevehookabove","abrevetilde","acaron","acircle","acircumflex","acircumflexacute","acircumflexdotbelow","acircumflexgrave","acircumflexhookabove","acircumflextilde","acute","acutebelowcmb","acutecmb","acutecomb","acutedeva","acutelowmod","acutetonecmb","acyrillic","adblgrave","addakgurmukhi","adeva","adieresis","adieresiscyrillic","adieresismacron","adotbelow","adotmacron","ae","aeacute","aekorean","aemacron","afii00208","afii08941","afii10017","afii10018","afii10019","afii10020","afii10021","afii10022","afii10023","afii10024","afii10025","afii10026","afii10027","afii10028","afii10029","afii10030","afii10031","afii10032","afii10033","afii10034","afii10035","afii10036","afii10037","afii10038","afii10039","afii10040","afii10041","afii10042","afii10043","afii10044","afii10045","afii10046","afii10047","afii10048","afii10049","afii10050","afii10051","afii10052","afii10053","afii10054","afii10055","afii10056","afii10057","afii10058","afii10059","afii10060","afii10061","afii10062","afii10063","afii10064","afii10065","afii10066","afii10067","afii10068","afii10069","afii10070","afii10071","afii10072","afii10073","afii10074","afii10075","afii10076","afii10077","afii10078","afii10079","afii10080","afii10081","afii10082","afii10083","afii10084","afii10085","afii10086","afii10087","afii10088","afii10089","afii10090","afii10091","afii10092","afii10093","afii10094","afii10095","afii10096","afii10097","afii10098","afii10099","afii10100","afii10101","afii10102","afii10103","afii10104","afii10105","afii10106","afii10107","afii10108","afii10109","afii10110","afii10145","afii10146","afii10147","afii10148","afii10192","afii10193","afii10194","afii10195","afii10196","afii10831","afii10832","afii10846","afii299","afii300","afii301","afii57381","afii57388","afii57392","afii57393","afii57394","afii57395","afii57396","afii57397","afii57398","afii57399","afii57400","afii57401","afii57403","afii57407","afii57409","afii57410","afii57411","afii57412","afii57413","afii57414","afii57415","afii57416","afii57417","afii57418","afii57419","afii57420","afii57421","afii57422","afii57423","afii57424","afii57425","afii57426","afii57427","afii57428","afii57429","afii57430","afii57431","afii57432","afii57433","afii57434","afii57440","afii57441","afii57442","afii57443","afii57444","afii57445","afii57446","afii57448","afii57449","afii57450","afii57451","afii57452","afii57453","afii57454","afii57455","afii57456","afii57457","afii57458","afii57470","afii57505","afii57506","afii57507","afii57508","afii57509","afii57511","afii57512","afii57513","afii57514","afii57519","afii57534","afii57636","afii57645","afii57658","afii57664","afii57665","afii57666","afii57667","afii57668","afii57669","afii57670","afii57671","afii57672","afii57673","afii57674","afii57675","afii57676","afii57677","afii57678","afii57679","afii57680","afii57681","afii57682","afii57683","afii57684","afii57685","afii57686","afii57687","afii57688","afii57689","afii57690","afii57694","afii57695","afii57700","afii57705","afii57716","afii57717","afii57718","afii57723","afii57793","afii57794","afii57795","afii57796","afii57797","afii57798","afii57799","afii57800","afii57801","afii57802","afii57803","afii57804","afii57806","afii57807","afii57839","afii57841","afii57842","afii57929","afii61248","afii61289","afii61352","afii61573","afii61574","afii61575","afii61664","afii63167","afii64937","agrave","agujarati","agurmukhi","ahiragana","ahookabove","aibengali","aibopomofo","aideva","aiecyrillic","aigujarati","aigurmukhi","aimatragurmukhi","ainarabic","ainfinalarabic","aininitialarabic","ainmedialarabic","ainvertedbreve","aivowelsignbengali","aivowelsigndeva","aivowelsigngujarati","akatakana","akatakanahalfwidth","akorean","alef","alefarabic","alefdageshhebrew","aleffinalarabic","alefhamzaabovearabic","alefhamzaabovefinalarabic","alefhamzabelowarabic","alefhamzabelowfinalarabic","alefhebrew","aleflamedhebrew","alefmaddaabovearabic","alefmaddaabovefinalarabic","alefmaksuraarabic","alefmaksurafinalarabic","alefmaksurainitialarabic","alefmaksuramedialarabic","alefpatahhebrew","alefqamatshebrew","aleph","allequal","alpha","alphatonos","amacron","amonospace","ampersand","ampersandmonospace","ampersandsmall","amsquare","anbopomofo","angbopomofo","angbracketleft","angbracketright","angkhankhuthai","anglebracketleft","anglebracketleftvertical","anglebracketright","anglebracketrightvertical","angleleft","angleright","angstrom","anoteleia","anudattadeva","anusvarabengali","anusvaradeva","anusvaragujarati","aogonek","apaatosquare","aparen","apostrophearmenian","apostrophemod","apple","approaches","approxequal","approxequalorimage","approximatelyequal","araeaekorean","araeakorean","arc","arighthalfring","aring","aringacute","aringbelow","arrowboth","arrowdashdown","arrowdashleft","arrowdashright","arrowdashup","arrowdblboth","arrowdbldown","arrowdblleft","arrowdblright","arrowdblup","arrowdown","arrowdownleft","arrowdownright","arrowdownwhite","arrowheaddownmod","arrowheadleftmod","arrowheadrightmod","arrowheadupmod","arrowhorizex","arrowleft","arrowleftdbl","arrowleftdblstroke","arrowleftoverright","arrowleftwhite","arrowright","arrowrightdblstroke","arrowrightheavy","arrowrightoverleft","arrowrightwhite","arrowtableft","arrowtabright","arrowup","arrowupdn","arrowupdnbse","arrowupdownbase","arrowupleft","arrowupleftofdown","arrowupright","arrowupwhite","arrowvertex","asciicircum","asciicircummonospace","asciitilde","asciitildemonospace","ascript","ascriptturned","asmallhiragana","asmallkatakana","asmallkatakanahalfwidth","asterisk","asteriskaltonearabic","asteriskarabic","asteriskmath","asteriskmonospace","asterisksmall","asterism","asuperior","asymptoticallyequal","atilde","atmonospace","atsmall","aturned","aubengali","aubopomofo","audeva","augujarati","augurmukhi","aulengthmarkbengali","aumatragurmukhi","auvowelsignbengali","auvowelsigndeva","auvowelsigngujarati","avagrahadeva","aybarmenian","ayin","ayinaltonehebrew","ayinhebrew","babengali","backslash","backslashmonospace","badeva","bagujarati","bagurmukhi","bahiragana","bahtthai","bakatakana","bar","barmonospace","bbopomofo","bcircle","bdotaccent","bdotbelow","beamedsixteenthnotes","because","becyrillic","beharabic","behfinalarabic","behinitialarabic","behiragana","behmedialarabic","behmeeminitialarabic","behmeemisolatedarabic","behnoonfinalarabic","bekatakana","benarmenian","bet","beta","betasymbolgreek","betdagesh","betdageshhebrew","bethebrew","betrafehebrew","bhabengali","bhadeva","bhagujarati","bhagurmukhi","bhook","bihiragana","bikatakana","bilabialclick","bindigurmukhi","birusquare","blackcircle","blackdiamond","blackdownpointingtriangle","blackleftpointingpointer","blackleftpointingtriangle","blacklenticularbracketleft","blacklenticularbracketleftvertical","blacklenticularbracketright","blacklenticularbracketrightvertical","blacklowerlefttriangle","blacklowerrighttriangle","blackrectangle","blackrightpointingpointer","blackrightpointingtriangle","blacksmallsquare","blacksmilingface","blacksquare","blackstar","blackupperlefttriangle","blackupperrighttriangle","blackuppointingsmalltriangle","blackuppointingtriangle","blank","blinebelow","bmonospace","bobaimaithai","bohiragana","bokatakana","bparen","bqsquare","braceex","braceleft","braceleftbt","braceleftmid","braceleftmonospace","braceleftsmall","bracelefttp","braceleftvertical","braceright","bracerightbt","bracerightmid","bracerightmonospace","bracerightsmall","bracerighttp","bracerightvertical","bracketleft","bracketleftbt","bracketleftex","bracketleftmonospace","bracketlefttp","bracketright","bracketrightbt","bracketrightex","bracketrightmonospace","bracketrighttp","breve","brevebelowcmb","brevecmb","breveinvertedbelowcmb","breveinvertedcmb","breveinverteddoublecmb","bridgebelowcmb","bridgeinvertedbelowcmb","brokenbar","bstroke","bsuperior","btopbar","buhiragana","bukatakana","bullet","bulletinverse","bulletoperator","bullseye","caarmenian","cabengali","cacute","cadeva","cagujarati","cagurmukhi","calsquare","candrabindubengali","candrabinducmb","candrabindudeva","candrabindugujarati","capslock","careof","caron","caronbelowcmb","caroncmb","carriagereturn","cbopomofo","ccaron","ccedilla","ccedillaacute","ccircle","ccircumflex","ccurl","cdot","cdotaccent","cdsquare","cedilla","cedillacmb","cent","centigrade","centinferior","centmonospace","centoldstyle","centsuperior","chaarmenian","chabengali","chadeva","chagujarati","chagurmukhi","chbopomofo","cheabkhasiancyrillic","checkmark","checyrillic","chedescenderabkhasiancyrillic","chedescendercyrillic","chedieresiscyrillic","cheharmenian","chekhakassiancyrillic","cheverticalstrokecyrillic","chi","chieuchacirclekorean","chieuchaparenkorean","chieuchcirclekorean","chieuchkorean","chieuchparenkorean","chochangthai","chochanthai","chochingthai","chochoethai","chook","cieucacirclekorean","cieucaparenkorean","cieuccirclekorean","cieuckorean","cieucparenkorean","cieucuparenkorean","circle","circlecopyrt","circlemultiply","circleot","circleplus","circlepostalmark","circlewithlefthalfblack","circlewithrighthalfblack","circumflex","circumflexbelowcmb","circumflexcmb","clickalveolar","clickdental","clicklateral","clickretroflex","club","clubsuitblack","clubsuitwhite","cmcubedsquare","cmonospace","cmsquaredsquare","coarmenian","colon","colonmonetary","colonmonospace","colonsign","colonsmall","colontriangularhalfmod","colontriangularmod","comma","commaabovecmb","commaaboverightcmb","commaaccent","commaarabic","commaarmenian","commainferior","commamonospace","commareversedabovecmb","commareversedmod","commasmall","commasuperior","commaturnedabovecmb","commaturnedmod","compass","congruent","contourintegral","control","controlACK","controlBEL","controlBS","controlCAN","controlCR","controlDC1","controlDC2","controlDC3","controlDC4","controlDEL","controlDLE","controlEM","controlENQ","controlEOT","controlESC","controlETB","controlETX","controlFF","controlFS","controlGS","controlHT","controlLF","controlNAK","controlNULL","controlRS","controlSI","controlSO","controlSOT","controlSTX","controlSUB","controlSYN","controlUS","controlVT","copyright","copyrightsans","copyrightserif","cornerbracketleft","cornerbracketlefthalfwidth","cornerbracketleftvertical","cornerbracketright","cornerbracketrighthalfwidth","cornerbracketrightvertical","corporationsquare","cosquare","coverkgsquare","cparen","cruzeiro","cstretched","curlyand","curlyor","currency","cyrBreve","cyrFlex","cyrbreve","cyrflex","daarmenian","dabengali","dadarabic","dadeva","dadfinalarabic","dadinitialarabic","dadmedialarabic","dagesh","dageshhebrew","dagger","daggerdbl","dagujarati","dagurmukhi","dahiragana","dakatakana","dalarabic","dalet","daletdagesh","daletdageshhebrew","dalethebrew","dalfinalarabic","dammaarabic","dammalowarabic","dammatanaltonearabic","dammatanarabic","danda","dargahebrew","dargalefthebrew","dasiapneumatacyrilliccmb","dblGrave","dblanglebracketleft","dblanglebracketleftvertical","dblanglebracketright","dblanglebracketrightvertical","dblarchinvertedbelowcmb","dblarrowleft","dblarrowright","dbldanda","dblgrave","dblgravecmb","dblintegral","dbllowline","dbllowlinecmb","dbloverlinecmb","dblprimemod","dblverticalbar","dblverticallineabovecmb","dbopomofo","dbsquare","dcaron","dcedilla","dcircle","dcircumflexbelow","dcroat","ddabengali","ddadeva","ddagujarati","ddagurmukhi","ddalarabic","ddalfinalarabic","dddhadeva","ddhabengali","ddhadeva","ddhagujarati","ddhagurmukhi","ddotaccent","ddotbelow","decimalseparatorarabic","decimalseparatorpersian","decyrillic","degree","dehihebrew","dehiragana","deicoptic","dekatakana","deleteleft","deleteright","deltaturned","denominatorminusonenumeratorbengali","dezh","dhabengali","dhadeva","dhagujarati","dhagurmukhi","dhook","dialytikatonos","dialytikatonoscmb","diamond","diamondsuitwhite","dieresis","dieresisacute","dieresisbelowcmb","dieresiscmb","dieresisgrave","dieresistonos","dihiragana","dikatakana","dittomark","divide","divides","divisionslash","djecyrillic","dkshade","dlinebelow","dlsquare","dmacron","dmonospace","dnblock","dochadathai","dodekthai","dohiragana","dokatakana","dollar","dollarinferior","dollarmonospace","dollaroldstyle","dollarsmall","dollarsuperior","dong","dorusquare","dotaccent","dotaccentcmb","dotbelowcmb","dotbelowcomb","dotkatakana","dotlessi","dotlessj","dotlessjstrokehook","dotmath","dottedcircle","doubleyodpatah","doubleyodpatahhebrew","downtackbelowcmb","downtackmod","dparen","dsuperior","dtail","dtopbar","duhiragana","dukatakana","dz","dzaltone","dzcaron","dzcurl","dzeabkhasiancyrillic","dzecyrillic","dzhecyrillic","eacute","earth","ebengali","ebopomofo","ebreve","ecandradeva","ecandragujarati","ecandravowelsigndeva","ecandravowelsigngujarati","ecaron","ecedillabreve","echarmenian","echyiwnarmenian","ecircle","ecircumflex","ecircumflexacute","ecircumflexbelow","ecircumflexdotbelow","ecircumflexgrave","ecircumflexhookabove","ecircumflextilde","ecyrillic","edblgrave","edeva","edieresis","edot","edotaccent","edotbelow","eegurmukhi","eematragurmukhi","efcyrillic","egrave","egujarati","eharmenian","ehbopomofo","ehiragana","ehookabove","eibopomofo","eight","eightarabic","eightbengali","eightcircle","eightcircleinversesansserif","eightdeva","eighteencircle","eighteenparen","eighteenperiod","eightgujarati","eightgurmukhi","eighthackarabic","eighthangzhou","eighthnotebeamed","eightideographicparen","eightinferior","eightmonospace","eightoldstyle","eightparen","eightperiod","eightpersian","eightroman","eightsuperior","eightthai","einvertedbreve","eiotifiedcyrillic","ekatakana","ekatakanahalfwidth","ekonkargurmukhi","ekorean","elcyrillic","elevencircle","elevenparen","elevenperiod","elevenroman","ellipsis","ellipsisvertical","emacron","emacronacute","emacrongrave","emcyrillic","emdash","emdashvertical","emonospace","emphasismarkarmenian","emptyset","enbopomofo","encyrillic","endash","endashvertical","endescendercyrillic","eng","engbopomofo","enghecyrillic","enhookcyrillic","enspace","eogonek","eokorean","eopen","eopenclosed","eopenreversed","eopenreversedclosed","eopenreversedhook","eparen","epsilontonos","equal","equalmonospace","equalsmall","equalsuperior","equivalence","erbopomofo","ercyrillic","ereversed","ereversedcyrillic","escyrillic","esdescendercyrillic","esh","eshcurl","eshortdeva","eshortvowelsigndeva","eshreversedloop","eshsquatreversed","esmallhiragana","esmallkatakana","esmallkatakanahalfwidth","estimated","esuperior","eta","etarmenian","etatonos","eth","etilde","etildebelow","etnahtafoukhhebrew","etnahtafoukhlefthebrew","etnahtahebrew","etnahtalefthebrew","eturned","eukorean","euro","evowelsignbengali","evowelsigndeva","evowelsigngujarati","exclam","exclamarmenian","exclamdbl","exclamdown","exclamdownsmall","exclammonospace","exclamsmall","existential","ezh","ezhcaron","ezhcurl","ezhreversed","ezhtail","fadeva","fagurmukhi","fahrenheit","fathaarabic","fathalowarabic","fathatanarabic","fbopomofo","fcircle","fdotaccent","feharabic","feharmenian","fehfinalarabic","fehinitialarabic","fehmedialarabic","feicoptic","female","ff","f_f","ffi","f_f_i","ffl","f_f_l","fi","f_i","fifteencircle","fifteenparen","fifteenperiod","figuredash","filledbox","filledrect","finalkaf","finalkafdagesh","finalkafdageshhebrew","finalkafhebrew","finalmem","finalmemhebrew","finalnun","finalnunhebrew","finalpe","finalpehebrew","finaltsadi","finaltsadihebrew","firsttonechinese","fisheye","fitacyrillic","five","fivearabic","fivebengali","fivecircle","fivecircleinversesansserif","fivedeva","fiveeighths","fivegujarati","fivegurmukhi","fivehackarabic","fivehangzhou","fiveideographicparen","fiveinferior","fivemonospace","fiveoldstyle","fiveparen","fiveperiod","fivepersian","fiveroman","fivesuperior","fivethai","fl","f_l","florin","fmonospace","fmsquare","fofanthai","fofathai","fongmanthai","forall","four","fourarabic","fourbengali","fourcircle","fourcircleinversesansserif","fourdeva","fourgujarati","fourgurmukhi","fourhackarabic","fourhangzhou","fourideographicparen","fourinferior","fourmonospace","fournumeratorbengali","fouroldstyle","fourparen","fourperiod","fourpersian","fourroman","foursuperior","fourteencircle","fourteenparen","fourteenperiod","fourthai","fourthtonechinese","fparen","fraction","franc","gabengali","gacute","gadeva","gafarabic","gaffinalarabic","gafinitialarabic","gafmedialarabic","gagujarati","gagurmukhi","gahiragana","gakatakana","gammalatinsmall","gammasuperior","gangiacoptic","gbopomofo","gbreve","gcaron","gcedilla","gcircle","gcircumflex","gcommaaccent","gdot","gdotaccent","gecyrillic","gehiragana","gekatakana","geometricallyequal","gereshaccenthebrew","gereshhebrew","gereshmuqdamhebrew","germandbls","gershayimaccenthebrew","gershayimhebrew","getamark","ghabengali","ghadarmenian","ghadeva","ghagujarati","ghagurmukhi","ghainarabic","ghainfinalarabic","ghaininitialarabic","ghainmedialarabic","ghemiddlehookcyrillic","ghestrokecyrillic","gheupturncyrillic","ghhadeva","ghhagurmukhi","ghook","ghzsquare","gihiragana","gikatakana","gimarmenian","gimel","gimeldagesh","gimeldageshhebrew","gimelhebrew","gjecyrillic","glottalinvertedstroke","glottalstop","glottalstopinverted","glottalstopmod","glottalstopreversed","glottalstopreversedmod","glottalstopreversedsuperior","glottalstopstroke","glottalstopstrokereversed","gmacron","gmonospace","gohiragana","gokatakana","gparen","gpasquare","gradient","grave","gravebelowcmb","gravecmb","gravecomb","gravedeva","gravelowmod","gravemonospace","gravetonecmb","greater","greaterequal","greaterequalorless","greatermonospace","greaterorequivalent","greaterorless","greateroverequal","greatersmall","gscript","gstroke","guhiragana","guillemotleft","guillemotright","guilsinglleft","guilsinglright","gukatakana","guramusquare","gysquare","haabkhasiancyrillic","haaltonearabic","habengali","hadescendercyrillic","hadeva","hagujarati","hagurmukhi","haharabic","hahfinalarabic","hahinitialarabic","hahiragana","hahmedialarabic","haitusquare","hakatakana","hakatakanahalfwidth","halantgurmukhi","hamzaarabic","hamzalowarabic","hangulfiller","hardsigncyrillic","harpoonleftbarbup","harpoonrightbarbup","hasquare","hatafpatah","hatafpatah16","hatafpatah23","hatafpatah2f","hatafpatahhebrew","hatafpatahnarrowhebrew","hatafpatahquarterhebrew","hatafpatahwidehebrew","hatafqamats","hatafqamats1b","hatafqamats28","hatafqamats34","hatafqamatshebrew","hatafqamatsnarrowhebrew","hatafqamatsquarterhebrew","hatafqamatswidehebrew","hatafsegol","hatafsegol17","hatafsegol24","hatafsegol30","hatafsegolhebrew","hatafsegolnarrowhebrew","hatafsegolquarterhebrew","hatafsegolwidehebrew","hbar","hbopomofo","hbrevebelow","hcedilla","hcircle","hcircumflex","hdieresis","hdotaccent","hdotbelow","he","heart","heartsuitblack","heartsuitwhite","hedagesh","hedageshhebrew","hehaltonearabic","heharabic","hehebrew","hehfinalaltonearabic","hehfinalalttwoarabic","hehfinalarabic","hehhamzaabovefinalarabic","hehhamzaaboveisolatedarabic","hehinitialaltonearabic","hehinitialarabic","hehiragana","hehmedialaltonearabic","hehmedialarabic","heiseierasquare","hekatakana","hekatakanahalfwidth","hekutaarusquare","henghook","herutusquare","het","hethebrew","hhook","hhooksuperior","hieuhacirclekorean","hieuhaparenkorean","hieuhcirclekorean","hieuhkorean","hieuhparenkorean","hihiragana","hikatakana","hikatakanahalfwidth","hiriq","hiriq14","hiriq21","hiriq2d","hiriqhebrew","hiriqnarrowhebrew","hiriqquarterhebrew","hiriqwidehebrew","hlinebelow","hmonospace","hoarmenian","hohipthai","hohiragana","hokatakana","hokatakanahalfwidth","holam","holam19","holam26","holam32","holamhebrew","holamnarrowhebrew","holamquarterhebrew","holamwidehebrew","honokhukthai","hookabovecomb","hookcmb","hookpalatalizedbelowcmb","hookretroflexbelowcmb","hoonsquare","horicoptic","horizontalbar","horncmb","hotsprings","house","hparen","hsuperior","hturned","huhiragana","huiitosquare","hukatakana","hukatakanahalfwidth","hungarumlaut","hungarumlautcmb","hv","hyphen","hypheninferior","hyphenmonospace","hyphensmall","hyphensuperior","hyphentwo","iacute","iacyrillic","ibengali","ibopomofo","ibreve","icaron","icircle","icircumflex","icyrillic","idblgrave","ideographearthcircle","ideographfirecircle","ideographicallianceparen","ideographiccallparen","ideographiccentrecircle","ideographicclose","ideographiccomma","ideographiccommaleft","ideographiccongratulationparen","ideographiccorrectcircle","ideographicearthparen","ideographicenterpriseparen","ideographicexcellentcircle","ideographicfestivalparen","ideographicfinancialcircle","ideographicfinancialparen","ideographicfireparen","ideographichaveparen","ideographichighcircle","ideographiciterationmark","ideographiclaborcircle","ideographiclaborparen","ideographicleftcircle","ideographiclowcircle","ideographicmedicinecircle","ideographicmetalparen","ideographicmoonparen","ideographicnameparen","ideographicperiod","ideographicprintcircle","ideographicreachparen","ideographicrepresentparen","ideographicresourceparen","ideographicrightcircle","ideographicsecretcircle","ideographicselfparen","ideographicsocietyparen","ideographicspace","ideographicspecialparen","ideographicstockparen","ideographicstudyparen","ideographicsunparen","ideographicsuperviseparen","ideographicwaterparen","ideographicwoodparen","ideographiczero","ideographmetalcircle","ideographmooncircle","ideographnamecircle","ideographsuncircle","ideographwatercircle","ideographwoodcircle","ideva","idieresis","idieresisacute","idieresiscyrillic","idotbelow","iebrevecyrillic","iecyrillic","ieungacirclekorean","ieungaparenkorean","ieungcirclekorean","ieungkorean","ieungparenkorean","igrave","igujarati","igurmukhi","ihiragana","ihookabove","iibengali","iicyrillic","iideva","iigujarati","iigurmukhi","iimatragurmukhi","iinvertedbreve","iishortcyrillic","iivowelsignbengali","iivowelsigndeva","iivowelsigngujarati","ij","ikatakana","ikatakanahalfwidth","ikorean","ilde","iluyhebrew","imacron","imacroncyrillic","imageorapproximatelyequal","imatragurmukhi","imonospace","infinity","iniarmenian","integral","integralbottom","integralbt","integralex","integraltop","integraltp","intersection","intisquare","invbullet","invcircle","invsmileface","iocyrillic","iogonek","iota","iotadieresis","iotadieresistonos","iotalatin","iotatonos","iparen","irigurmukhi","ismallhiragana","ismallkatakana","ismallkatakanahalfwidth","issharbengali","istroke","isuperior","iterationhiragana","iterationkatakana","itilde","itildebelow","iubopomofo","iucyrillic","ivowelsignbengali","ivowelsigndeva","ivowelsigngujarati","izhitsacyrillic","izhitsadblgravecyrillic","jaarmenian","jabengali","jadeva","jagujarati","jagurmukhi","jbopomofo","jcaron","jcircle","jcircumflex","jcrossedtail","jdotlessstroke","jecyrillic","jeemarabic","jeemfinalarabic","jeeminitialarabic","jeemmedialarabic","jeharabic","jehfinalarabic","jhabengali","jhadeva","jhagujarati","jhagurmukhi","jheharmenian","jis","jmonospace","jparen","jsuperior","kabashkircyrillic","kabengali","kacute","kacyrillic","kadescendercyrillic","kadeva","kaf","kafarabic","kafdagesh","kafdageshhebrew","kaffinalarabic","kafhebrew","kafinitialarabic","kafmedialarabic","kafrafehebrew","kagujarati","kagurmukhi","kahiragana","kahookcyrillic","kakatakana","kakatakanahalfwidth","kappa","kappasymbolgreek","kapyeounmieumkorean","kapyeounphieuphkorean","kapyeounpieupkorean","kapyeounssangpieupkorean","karoriisquare","kashidaautoarabic","kashidaautonosidebearingarabic","kasmallkatakana","kasquare","kasraarabic","kasratanarabic","kastrokecyrillic","katahiraprolongmarkhalfwidth","kaverticalstrokecyrillic","kbopomofo","kcalsquare","kcaron","kcedilla","kcircle","kcommaaccent","kdotbelow","keharmenian","kehiragana","kekatakana","kekatakanahalfwidth","kenarmenian","kesmallkatakana","kgreenlandic","khabengali","khacyrillic","khadeva","khagujarati","khagurmukhi","khaharabic","khahfinalarabic","khahinitialarabic","khahmedialarabic","kheicoptic","khhadeva","khhagurmukhi","khieukhacirclekorean","khieukhaparenkorean","khieukhcirclekorean","khieukhkorean","khieukhparenkorean","khokhaithai","khokhonthai","khokhuatthai","khokhwaithai","khomutthai","khook","khorakhangthai","khzsquare","kihiragana","kikatakana","kikatakanahalfwidth","kiroguramusquare","kiromeetorusquare","kirosquare","kiyeokacirclekorean","kiyeokaparenkorean","kiyeokcirclekorean","kiyeokkorean","kiyeokparenkorean","kiyeoksioskorean","kjecyrillic","klinebelow","klsquare","kmcubedsquare","kmonospace","kmsquaredsquare","kohiragana","kohmsquare","kokaithai","kokatakana","kokatakanahalfwidth","kooposquare","koppacyrillic","koreanstandardsymbol","koroniscmb","kparen","kpasquare","ksicyrillic","ktsquare","kturned","kuhiragana","kukatakana","kukatakanahalfwidth","kvsquare","kwsquare","labengali","lacute","ladeva","lagujarati","lagurmukhi","lakkhangyaothai","lamaleffinalarabic","lamalefhamzaabovefinalarabic","lamalefhamzaaboveisolatedarabic","lamalefhamzabelowfinalarabic","lamalefhamzabelowisolatedarabic","lamalefisolatedarabic","lamalefmaddaabovefinalarabic","lamalefmaddaaboveisolatedarabic","lamarabic","lambda","lambdastroke","lamed","lameddagesh","lameddageshhebrew","lamedhebrew","lamfinalarabic","lamhahinitialarabic","laminitialarabic","lamjeeminitialarabic","lamkhahinitialarabic","lamlamhehisolatedarabic","lammedialarabic","lammeemhahinitialarabic","lammeeminitialarabic","largecircle","lbar","lbelt","lbopomofo","lcaron","lcedilla","lcircle","lcircumflexbelow","lcommaaccent","ldot","ldotaccent","ldotbelow","ldotbelowmacron","leftangleabovecmb","lefttackbelowcmb","less","lessequal","lessequalorgreater","lessmonospace","lessorequivalent","lessorgreater","lessoverequal","lesssmall","lezh","lfblock","lhookretroflex","lira","liwnarmenian","lj","ljecyrillic","ll","lladeva","llagujarati","llinebelow","llladeva","llvocalicbengali","llvocalicdeva","llvocalicvowelsignbengali","llvocalicvowelsigndeva","lmiddletilde","lmonospace","lmsquare","lochulathai","logicaland","logicalnot","logicalnotreversed","logicalor","lolingthai","longs","lowlinecenterline","lowlinecmb","lowlinedashed","lozenge","lparen","lslash","lsquare","lsuperior","ltshade","luthai","lvocalicbengali","lvocalicdeva","lvocalicvowelsignbengali","lvocalicvowelsigndeva","lxsquare","mabengali","macron","macronbelowcmb","macroncmb","macronlowmod","macronmonospace","macute","madeva","magujarati","magurmukhi","mahapakhhebrew","mahapakhlefthebrew","mahiragana","maichattawalowleftthai","maichattawalowrightthai","maichattawathai","maichattawaupperleftthai","maieklowleftthai","maieklowrightthai","maiekthai","maiekupperleftthai","maihanakatleftthai","maihanakatthai","maitaikhuleftthai","maitaikhuthai","maitholowleftthai","maitholowrightthai","maithothai","maithoupperleftthai","maitrilowleftthai","maitrilowrightthai","maitrithai","maitriupperleftthai","maiyamokthai","makatakana","makatakanahalfwidth","male","mansyonsquare","maqafhebrew","mars","masoracirclehebrew","masquare","mbopomofo","mbsquare","mcircle","mcubedsquare","mdotaccent","mdotbelow","meemarabic","meemfinalarabic","meeminitialarabic","meemmedialarabic","meemmeeminitialarabic","meemmeemisolatedarabic","meetorusquare","mehiragana","meizierasquare","mekatakana","mekatakanahalfwidth","mem","memdagesh","memdageshhebrew","memhebrew","menarmenian","merkhahebrew","merkhakefulahebrew","merkhakefulalefthebrew","merkhalefthebrew","mhook","mhzsquare","middledotkatakanahalfwidth","middot","mieumacirclekorean","mieumaparenkorean","mieumcirclekorean","mieumkorean","mieumpansioskorean","mieumparenkorean","mieumpieupkorean","mieumsioskorean","mihiragana","mikatakana","mikatakanahalfwidth","minus","minusbelowcmb","minuscircle","minusmod","minusplus","minute","miribaarusquare","mirisquare","mlonglegturned","mlsquare","mmcubedsquare","mmonospace","mmsquaredsquare","mohiragana","mohmsquare","mokatakana","mokatakanahalfwidth","molsquare","momathai","moverssquare","moverssquaredsquare","mparen","mpasquare","mssquare","msuperior","mturned","mu","mu1","muasquare","muchgreater","muchless","mufsquare","mugreek","mugsquare","muhiragana","mukatakana","mukatakanahalfwidth","mulsquare","multiply","mumsquare","munahhebrew","munahlefthebrew","musicalnote","musicalnotedbl","musicflatsign","musicsharpsign","mussquare","muvsquare","muwsquare","mvmegasquare","mvsquare","mwmegasquare","mwsquare","nabengali","nabla","nacute","nadeva","nagujarati","nagurmukhi","nahiragana","nakatakana","nakatakanahalfwidth","napostrophe","nasquare","nbopomofo","nbspace","ncaron","ncedilla","ncircle","ncircumflexbelow","ncommaaccent","ndotaccent","ndotbelow","nehiragana","nekatakana","nekatakanahalfwidth","newsheqelsign","nfsquare","ngabengali","ngadeva","ngagujarati","ngagurmukhi","ngonguthai","nhiragana","nhookleft","nhookretroflex","nieunacirclekorean","nieunaparenkorean","nieuncieuckorean","nieuncirclekorean","nieunhieuhkorean","nieunkorean","nieunpansioskorean","nieunparenkorean","nieunsioskorean","nieuntikeutkorean","nihiragana","nikatakana","nikatakanahalfwidth","nikhahitleftthai","nikhahitthai","nine","ninearabic","ninebengali","ninecircle","ninecircleinversesansserif","ninedeva","ninegujarati","ninegurmukhi","ninehackarabic","ninehangzhou","nineideographicparen","nineinferior","ninemonospace","nineoldstyle","nineparen","nineperiod","ninepersian","nineroman","ninesuperior","nineteencircle","nineteenparen","nineteenperiod","ninethai","nj","njecyrillic","nkatakana","nkatakanahalfwidth","nlegrightlong","nlinebelow","nmonospace","nmsquare","nnabengali","nnadeva","nnagujarati","nnagurmukhi","nnnadeva","nohiragana","nokatakana","nokatakanahalfwidth","nonbreakingspace","nonenthai","nonuthai","noonarabic","noonfinalarabic","noonghunnaarabic","noonghunnafinalarabic","nooninitialarabic","noonjeeminitialarabic","noonjeemisolatedarabic","noonmedialarabic","noonmeeminitialarabic","noonmeemisolatedarabic","noonnoonfinalarabic","notcontains","notelement","notelementof","notequal","notgreater","notgreaternorequal","notgreaternorless","notidentical","notless","notlessnorequal","notparallel","notprecedes","notsubset","notsucceeds","notsuperset","nowarmenian","nparen","nssquare","nsuperior","ntilde","nu","nuhiragana","nukatakana","nukatakanahalfwidth","nuktabengali","nuktadeva","nuktagujarati","nuktagurmukhi","numbersign","numbersignmonospace","numbersignsmall","numeralsigngreek","numeralsignlowergreek","numero","nun","nundagesh","nundageshhebrew","nunhebrew","nvsquare","nwsquare","nyabengali","nyadeva","nyagujarati","nyagurmukhi","o","oacute","oangthai","obarred","obarredcyrillic","obarreddieresiscyrillic","obengali","obopomofo","obreve","ocandradeva","ocandragujarati","ocandravowelsigndeva","ocandravowelsigngujarati","ocaron","ocircle","ocircumflex","ocircumflexacute","ocircumflexdotbelow","ocircumflexgrave","ocircumflexhookabove","ocircumflextilde","ocyrillic","odblacute","odblgrave","odeva","odieresis","odieresiscyrillic","odotbelow","oe","oekorean","ogonek","ogonekcmb","ograve","ogujarati","oharmenian","ohiragana","ohookabove","ohorn","ohornacute","ohorndotbelow","ohorngrave","ohornhookabove","ohorntilde","ohungarumlaut","oi","oinvertedbreve","okatakana","okatakanahalfwidth","okorean","olehebrew","omacron","omacronacute","omacrongrave","omdeva","omega","omega1","omegacyrillic","omegalatinclosed","omegaroundcyrillic","omegatitlocyrillic","omegatonos","omgujarati","omicron","omicrontonos","omonospace","one","onearabic","onebengali","onecircle","onecircleinversesansserif","onedeva","onedotenleader","oneeighth","onefitted","onegujarati","onegurmukhi","onehackarabic","onehalf","onehangzhou","oneideographicparen","oneinferior","onemonospace","onenumeratorbengali","oneoldstyle","oneparen","oneperiod","onepersian","onequarter","oneroman","onesuperior","onethai","onethird","oogonek","oogonekmacron","oogurmukhi","oomatragurmukhi","oopen","oparen","openbullet","option","ordfeminine","ordmasculine","orthogonal","oshortdeva","oshortvowelsigndeva","oslash","oslashacute","osmallhiragana","osmallkatakana","osmallkatakanahalfwidth","ostrokeacute","osuperior","otcyrillic","otilde","otildeacute","otildedieresis","oubopomofo","overline","overlinecenterline","overlinecmb","overlinedashed","overlinedblwavy","overlinewavy","overscore","ovowelsignbengali","ovowelsigndeva","ovowelsigngujarati","paampssquare","paasentosquare","pabengali","pacute","padeva","pagedown","pageup","pagujarati","pagurmukhi","pahiragana","paiyannoithai","pakatakana","palatalizationcyrilliccmb","palochkacyrillic","pansioskorean","paragraph","parallel","parenleft","parenleftaltonearabic","parenleftbt","parenleftex","parenleftinferior","parenleftmonospace","parenleftsmall","parenleftsuperior","parenlefttp","parenleftvertical","parenright","parenrightaltonearabic","parenrightbt","parenrightex","parenrightinferior","parenrightmonospace","parenrightsmall","parenrightsuperior","parenrighttp","parenrightvertical","partialdiff","paseqhebrew","pashtahebrew","pasquare","patah","patah11","patah1d","patah2a","patahhebrew","patahnarrowhebrew","patahquarterhebrew","patahwidehebrew","pazerhebrew","pbopomofo","pcircle","pdotaccent","pe","pecyrillic","pedagesh","pedageshhebrew","peezisquare","pefinaldageshhebrew","peharabic","peharmenian","pehebrew","pehfinalarabic","pehinitialarabic","pehiragana","pehmedialarabic","pekatakana","pemiddlehookcyrillic","perafehebrew","percent","percentarabic","percentmonospace","percentsmall","period","periodarmenian","periodcentered","periodhalfwidth","periodinferior","periodmonospace","periodsmall","periodsuperior","perispomenigreekcmb","perpendicular","perthousand","peseta","pfsquare","phabengali","phadeva","phagujarati","phagurmukhi","phi","phi1","phieuphacirclekorean","phieuphaparenkorean","phieuphcirclekorean","phieuphkorean","phieuphparenkorean","philatin","phinthuthai","phisymbolgreek","phook","phophanthai","phophungthai","phosamphaothai","pi","pieupacirclekorean","pieupaparenkorean","pieupcieuckorean","pieupcirclekorean","pieupkiyeokkorean","pieupkorean","pieupparenkorean","pieupsioskiyeokkorean","pieupsioskorean","pieupsiostikeutkorean","pieupthieuthkorean","pieuptikeutkorean","pihiragana","pikatakana","pisymbolgreek","piwrarmenian","planckover2pi","planckover2pi1","plus","plusbelowcmb","pluscircle","plusminus","plusmod","plusmonospace","plussmall","plussuperior","pmonospace","pmsquare","pohiragana","pointingindexdownwhite","pointingindexleftwhite","pointingindexrightwhite","pointingindexupwhite","pokatakana","poplathai","postalmark","postalmarkface","pparen","precedes","prescription","primemod","primereversed","product","projective","prolongedkana","propellor","propersubset","propersuperset","proportion","proportional","psi","psicyrillic","psilipneumatacyrilliccmb","pssquare","puhiragana","pukatakana","pvsquare","pwsquare","qadeva","qadmahebrew","qafarabic","qaffinalarabic","qafinitialarabic","qafmedialarabic","qamats","qamats10","qamats1a","qamats1c","qamats27","qamats29","qamats33","qamatsde","qamatshebrew","qamatsnarrowhebrew","qamatsqatanhebrew","qamatsqatannarrowhebrew","qamatsqatanquarterhebrew","qamatsqatanwidehebrew","qamatsquarterhebrew","qamatswidehebrew","qarneyparahebrew","qbopomofo","qcircle","qhook","qmonospace","qof","qofdagesh","qofdageshhebrew","qofhebrew","qparen","quarternote","qubuts","qubuts18","qubuts25","qubuts31","qubutshebrew","qubutsnarrowhebrew","qubutsquarterhebrew","qubutswidehebrew","question","questionarabic","questionarmenian","questiondown","questiondownsmall","questiongreek","questionmonospace","questionsmall","quotedbl","quotedblbase","quotedblleft","quotedblmonospace","quotedblprime","quotedblprimereversed","quotedblright","quoteleft","quoteleftreversed","quotereversed","quoteright","quoterightn","quotesinglbase","quotesingle","quotesinglemonospace","raarmenian","rabengali","racute","radeva","radical","radicalex","radoverssquare","radoverssquaredsquare","radsquare","rafe","rafehebrew","ragujarati","ragurmukhi","rahiragana","rakatakana","rakatakanahalfwidth","ralowerdiagonalbengali","ramiddlediagonalbengali","ramshorn","ratio","rbopomofo","rcaron","rcedilla","rcircle","rcommaaccent","rdblgrave","rdotaccent","rdotbelow","rdotbelowmacron","referencemark","reflexsubset","reflexsuperset","registered","registersans","registerserif","reharabic","reharmenian","rehfinalarabic","rehiragana","rekatakana","rekatakanahalfwidth","resh","reshdageshhebrew","reshhebrew","reversedtilde","reviahebrew","reviamugrashhebrew","revlogicalnot","rfishhook","rfishhookreversed","rhabengali","rhadeva","rho","rhook","rhookturned","rhookturnedsuperior","rhosymbolgreek","rhotichookmod","rieulacirclekorean","rieulaparenkorean","rieulcirclekorean","rieulhieuhkorean","rieulkiyeokkorean","rieulkiyeoksioskorean","rieulkorean","rieulmieumkorean","rieulpansioskorean","rieulparenkorean","rieulphieuphkorean","rieulpieupkorean","rieulpieupsioskorean","rieulsioskorean","rieulthieuthkorean","rieultikeutkorean","rieulyeorinhieuhkorean","rightangle","righttackbelowcmb","righttriangle","rihiragana","rikatakana","rikatakanahalfwidth","ring","ringbelowcmb","ringcmb","ringhalfleft","ringhalfleftarmenian","ringhalfleftbelowcmb","ringhalfleftcentered","ringhalfright","ringhalfrightbelowcmb","ringhalfrightcentered","rinvertedbreve","rittorusquare","rlinebelow","rlongleg","rlonglegturned","rmonospace","rohiragana","rokatakana","rokatakanahalfwidth","roruathai","rparen","rrabengali","rradeva","rragurmukhi","rreharabic","rrehfinalarabic","rrvocalicbengali","rrvocalicdeva","rrvocalicgujarati","rrvocalicvowelsignbengali","rrvocalicvowelsigndeva","rrvocalicvowelsigngujarati","rsuperior","rtblock","rturned","rturnedsuperior","ruhiragana","rukatakana","rukatakanahalfwidth","rupeemarkbengali","rupeesignbengali","rupiah","ruthai","rvocalicbengali","rvocalicdeva","rvocalicgujarati","rvocalicvowelsignbengali","rvocalicvowelsigndeva","rvocalicvowelsigngujarati","sabengali","sacute","sacutedotaccent","sadarabic","sadeva","sadfinalarabic","sadinitialarabic","sadmedialarabic","sagujarati","sagurmukhi","sahiragana","sakatakana","sakatakanahalfwidth","sallallahoualayhewasallamarabic","samekh","samekhdagesh","samekhdageshhebrew","samekhhebrew","saraaathai","saraaethai","saraaimaimalaithai","saraaimaimuanthai","saraamthai","saraathai","saraethai","saraiileftthai","saraiithai","saraileftthai","saraithai","saraothai","saraueeleftthai","saraueethai","saraueleftthai","sarauethai","sarauthai","sarauuthai","sbopomofo","scaron","scarondotaccent","scedilla","schwa","schwacyrillic","schwadieresiscyrillic","schwahook","scircle","scircumflex","scommaaccent","sdotaccent","sdotbelow","sdotbelowdotaccent","seagullbelowcmb","secondtonechinese","section","seenarabic","seenfinalarabic","seeninitialarabic","seenmedialarabic","segol","segol13","segol1f","segol2c","segolhebrew","segolnarrowhebrew","segolquarterhebrew","segoltahebrew","segolwidehebrew","seharmenian","sehiragana","sekatakana","sekatakanahalfwidth","semicolon","semicolonarabic","semicolonmonospace","semicolonsmall","semivoicedmarkkana","semivoicedmarkkanahalfwidth","sentisquare","sentosquare","seven","sevenarabic","sevenbengali","sevencircle","sevencircleinversesansserif","sevendeva","seveneighths","sevengujarati","sevengurmukhi","sevenhackarabic","sevenhangzhou","sevenideographicparen","seveninferior","sevenmonospace","sevenoldstyle","sevenparen","sevenperiod","sevenpersian","sevenroman","sevensuperior","seventeencircle","seventeenparen","seventeenperiod","seventhai","sfthyphen","shaarmenian","shabengali","shacyrillic","shaddaarabic","shaddadammaarabic","shaddadammatanarabic","shaddafathaarabic","shaddakasraarabic","shaddakasratanarabic","shade","shadedark","shadelight","shademedium","shadeva","shagujarati","shagurmukhi","shalshelethebrew","shbopomofo","shchacyrillic","sheenarabic","sheenfinalarabic","sheeninitialarabic","sheenmedialarabic","sheicoptic","sheqel","sheqelhebrew","sheva","sheva115","sheva15","sheva22","sheva2e","shevahebrew","shevanarrowhebrew","shevaquarterhebrew","shevawidehebrew","shhacyrillic","shimacoptic","shin","shindagesh","shindageshhebrew","shindageshshindot","shindageshshindothebrew","shindageshsindot","shindageshsindothebrew","shindothebrew","shinhebrew","shinshindot","shinshindothebrew","shinsindot","shinsindothebrew","shook","sigma","sigma1","sigmafinal","sigmalunatesymbolgreek","sihiragana","sikatakana","sikatakanahalfwidth","siluqhebrew","siluqlefthebrew","similar","sindothebrew","siosacirclekorean","siosaparenkorean","sioscieuckorean","sioscirclekorean","sioskiyeokkorean","sioskorean","siosnieunkorean","siosparenkorean","siospieupkorean","siostikeutkorean","six","sixarabic","sixbengali","sixcircle","sixcircleinversesansserif","sixdeva","sixgujarati","sixgurmukhi","sixhackarabic","sixhangzhou","sixideographicparen","sixinferior","sixmonospace","sixoldstyle","sixparen","sixperiod","sixpersian","sixroman","sixsuperior","sixteencircle","sixteencurrencydenominatorbengali","sixteenparen","sixteenperiod","sixthai","slash","slashmonospace","slong","slongdotaccent","smileface","smonospace","sofpasuqhebrew","softhyphen","softsigncyrillic","sohiragana","sokatakana","sokatakanahalfwidth","soliduslongoverlaycmb","solidusshortoverlaycmb","sorusithai","sosalathai","sosothai","sosuathai","space","spacehackarabic","spade","spadesuitblack","spadesuitwhite","sparen","squarebelowcmb","squarecc","squarecm","squarediagonalcrosshatchfill","squarehorizontalfill","squarekg","squarekm","squarekmcapital","squareln","squarelog","squaremg","squaremil","squaremm","squaremsquared","squareorthogonalcrosshatchfill","squareupperlefttolowerrightfill","squareupperrighttolowerleftfill","squareverticalfill","squarewhitewithsmallblack","srsquare","ssabengali","ssadeva","ssagujarati","ssangcieuckorean","ssanghieuhkorean","ssangieungkorean","ssangkiyeokkorean","ssangnieunkorean","ssangpieupkorean","ssangsioskorean","ssangtikeutkorean","ssuperior","sterling","sterlingmonospace","strokelongoverlaycmb","strokeshortoverlaycmb","subset","subsetnotequal","subsetorequal","succeeds","suchthat","suhiragana","sukatakana","sukatakanahalfwidth","sukunarabic","summation","sun","superset","supersetnotequal","supersetorequal","svsquare","syouwaerasquare","tabengali","tackdown","tackleft","tadeva","tagujarati","tagurmukhi","taharabic","tahfinalarabic","tahinitialarabic","tahiragana","tahmedialarabic","taisyouerasquare","takatakana","takatakanahalfwidth","tatweelarabic","tau","tav","tavdages","tavdagesh","tavdageshhebrew","tavhebrew","tbar","tbopomofo","tcaron","tccurl","tcedilla","tcheharabic","tchehfinalarabic","tchehinitialarabic","tchehmedialarabic","tcircle","tcircumflexbelow","tcommaaccent","tdieresis","tdotaccent","tdotbelow","tecyrillic","tedescendercyrillic","teharabic","tehfinalarabic","tehhahinitialarabic","tehhahisolatedarabic","tehinitialarabic","tehiragana","tehjeeminitialarabic","tehjeemisolatedarabic","tehmarbutaarabic","tehmarbutafinalarabic","tehmedialarabic","tehmeeminitialarabic","tehmeemisolatedarabic","tehnoonfinalarabic","tekatakana","tekatakanahalfwidth","telephone","telephoneblack","telishagedolahebrew","telishaqetanahebrew","tencircle","tenideographicparen","tenparen","tenperiod","tenroman","tesh","tet","tetdagesh","tetdageshhebrew","tethebrew","tetsecyrillic","tevirhebrew","tevirlefthebrew","thabengali","thadeva","thagujarati","thagurmukhi","thalarabic","thalfinalarabic","thanthakhatlowleftthai","thanthakhatlowrightthai","thanthakhatthai","thanthakhatupperleftthai","theharabic","thehfinalarabic","thehinitialarabic","thehmedialarabic","thereexists","therefore","theta","theta1","thetasymbolgreek","thieuthacirclekorean","thieuthaparenkorean","thieuthcirclekorean","thieuthkorean","thieuthparenkorean","thirteencircle","thirteenparen","thirteenperiod","thonangmonthothai","thook","thophuthaothai","thorn","thothahanthai","thothanthai","thothongthai","thothungthai","thousandcyrillic","thousandsseparatorarabic","thousandsseparatorpersian","three","threearabic","threebengali","threecircle","threecircleinversesansserif","threedeva","threeeighths","threegujarati","threegurmukhi","threehackarabic","threehangzhou","threeideographicparen","threeinferior","threemonospace","threenumeratorbengali","threeoldstyle","threeparen","threeperiod","threepersian","threequarters","threequartersemdash","threeroman","threesuperior","threethai","thzsquare","tihiragana","tikatakana","tikatakanahalfwidth","tikeutacirclekorean","tikeutaparenkorean","tikeutcirclekorean","tikeutkorean","tikeutparenkorean","tilde","tildebelowcmb","tildecmb","tildecomb","tildedoublecmb","tildeoperator","tildeoverlaycmb","tildeverticalcmb","timescircle","tipehahebrew","tipehalefthebrew","tippigurmukhi","titlocyrilliccmb","tiwnarmenian","tlinebelow","tmonospace","toarmenian","tohiragana","tokatakana","tokatakanahalfwidth","tonebarextrahighmod","tonebarextralowmod","tonebarhighmod","tonebarlowmod","tonebarmidmod","tonefive","tonesix","tonetwo","tonos","tonsquare","topatakthai","tortoiseshellbracketleft","tortoiseshellbracketleftsmall","tortoiseshellbracketleftvertical","tortoiseshellbracketright","tortoiseshellbracketrightsmall","tortoiseshellbracketrightvertical","totaothai","tpalatalhook","tparen","trademark","trademarksans","trademarkserif","tretroflexhook","triagdn","triaglf","triagrt","triagup","ts","tsadi","tsadidagesh","tsadidageshhebrew","tsadihebrew","tsecyrillic","tsere","tsere12","tsere1e","tsere2b","tserehebrew","tserenarrowhebrew","tserequarterhebrew","tserewidehebrew","tshecyrillic","tsuperior","ttabengali","ttadeva","ttagujarati","ttagurmukhi","tteharabic","ttehfinalarabic","ttehinitialarabic","ttehmedialarabic","tthabengali","tthadeva","tthagujarati","tthagurmukhi","tturned","tuhiragana","tukatakana","tukatakanahalfwidth","tusmallhiragana","tusmallkatakana","tusmallkatakanahalfwidth","twelvecircle","twelveparen","twelveperiod","twelveroman","twentycircle","twentyhangzhou","twentyparen","twentyperiod","two","twoarabic","twobengali","twocircle","twocircleinversesansserif","twodeva","twodotenleader","twodotleader","twodotleadervertical","twogujarati","twogurmukhi","twohackarabic","twohangzhou","twoideographicparen","twoinferior","twomonospace","twonumeratorbengali","twooldstyle","twoparen","twoperiod","twopersian","tworoman","twostroke","twosuperior","twothai","twothirds","u","uacute","ubar","ubengali","ubopomofo","ubreve","ucaron","ucircle","ucircumflex","ucircumflexbelow","ucyrillic","udattadeva","udblacute","udblgrave","udeva","udieresis","udieresisacute","udieresisbelow","udieresiscaron","udieresiscyrillic","udieresisgrave","udieresismacron","udotbelow","ugrave","ugujarati","ugurmukhi","uhiragana","uhookabove","uhorn","uhornacute","uhorndotbelow","uhorngrave","uhornhookabove","uhorntilde","uhungarumlaut","uhungarumlautcyrillic","uinvertedbreve","ukatakana","ukatakanahalfwidth","ukcyrillic","ukorean","umacron","umacroncyrillic","umacrondieresis","umatragurmukhi","umonospace","underscore","underscoredbl","underscoremonospace","underscorevertical","underscorewavy","union","universal","uogonek","uparen","upblock","upperdothebrew","upsilon","upsilondieresis","upsilondieresistonos","upsilonlatin","upsilontonos","uptackbelowcmb","uptackmod","uragurmukhi","uring","ushortcyrillic","usmallhiragana","usmallkatakana","usmallkatakanahalfwidth","ustraightcyrillic","ustraightstrokecyrillic","utilde","utildeacute","utildebelow","uubengali","uudeva","uugujarati","uugurmukhi","uumatragurmukhi","uuvowelsignbengali","uuvowelsigndeva","uuvowelsigngujarati","uvowelsignbengali","uvowelsigndeva","uvowelsigngujarati","vadeva","vagujarati","vagurmukhi","vakatakana","vav","vavdagesh","vavdagesh65","vavdageshhebrew","vavhebrew","vavholam","vavholamhebrew","vavvavhebrew","vavyodhebrew","vcircle","vdotbelow","vecyrillic","veharabic","vehfinalarabic","vehinitialarabic","vehmedialarabic","vekatakana","venus","verticalbar","verticallineabovecmb","verticallinebelowcmb","verticallinelowmod","verticallinemod","vewarmenian","vhook","vikatakana","viramabengali","viramadeva","viramagujarati","visargabengali","visargadeva","visargagujarati","vmonospace","voarmenian","voicediterationhiragana","voicediterationkatakana","voicedmarkkana","voicedmarkkanahalfwidth","vokatakana","vparen","vtilde","vturned","vuhiragana","vukatakana","w","wacute","waekorean","wahiragana","wakatakana","wakatakanahalfwidth","wakorean","wasmallhiragana","wasmallkatakana","wattosquare","wavedash","wavyunderscorevertical","wawarabic","wawfinalarabic","wawhamzaabovearabic","wawhamzaabovefinalarabic","wbsquare","wcircle","wcircumflex","wdieresis","wdotaccent","wdotbelow","wehiragana","weierstrass","wekatakana","wekorean","weokorean","wgrave","whitebullet","whitecircle","whitecircleinverse","whitecornerbracketleft","whitecornerbracketleftvertical","whitecornerbracketright","whitecornerbracketrightvertical","whitediamond","whitediamondcontainingblacksmalldiamond","whitedownpointingsmalltriangle","whitedownpointingtriangle","whiteleftpointingsmalltriangle","whiteleftpointingtriangle","whitelenticularbracketleft","whitelenticularbracketright","whiterightpointingsmalltriangle","whiterightpointingtriangle","whitesmallsquare","whitesmilingface","whitesquare","whitestar","whitetelephone","whitetortoiseshellbracketleft","whitetortoiseshellbracketright","whiteuppointingsmalltriangle","whiteuppointingtriangle","wihiragana","wikatakana","wikorean","wmonospace","wohiragana","wokatakana","wokatakanahalfwidth","won","wonmonospace","wowaenthai","wparen","wring","wsuperior","wturned","wynn","xabovecmb","xbopomofo","xcircle","xdieresis","xdotaccent","xeharmenian","xi","xmonospace","xparen","xsuperior","yaadosquare","yabengali","yacute","yadeva","yaekorean","yagujarati","yagurmukhi","yahiragana","yakatakana","yakatakanahalfwidth","yakorean","yamakkanthai","yasmallhiragana","yasmallkatakana","yasmallkatakanahalfwidth","yatcyrillic","ycircle","ycircumflex","ydieresis","ydotaccent","ydotbelow","yeharabic","yehbarreearabic","yehbarreefinalarabic","yehfinalarabic","yehhamzaabovearabic","yehhamzaabovefinalarabic","yehhamzaaboveinitialarabic","yehhamzaabovemedialarabic","yehinitialarabic","yehmedialarabic","yehmeeminitialarabic","yehmeemisolatedarabic","yehnoonfinalarabic","yehthreedotsbelowarabic","yekorean","yen","yenmonospace","yeokorean","yeorinhieuhkorean","yerahbenyomohebrew","yerahbenyomolefthebrew","yericyrillic","yerudieresiscyrillic","yesieungkorean","yesieungpansioskorean","yesieungsioskorean","yetivhebrew","ygrave","yhook","yhookabove","yiarmenian","yicyrillic","yikorean","yinyang","yiwnarmenian","ymonospace","yod","yoddagesh","yoddageshhebrew","yodhebrew","yodyodhebrew","yodyodpatahhebrew","yohiragana","yoikorean","yokatakana","yokatakanahalfwidth","yokorean","yosmallhiragana","yosmallkatakana","yosmallkatakanahalfwidth","yotgreek","yoyaekorean","yoyakorean","yoyakthai","yoyingthai","yparen","ypogegrammeni","ypogegrammenigreekcmb","yr","yring","ysuperior","ytilde","yturned","yuhiragana","yuikorean","yukatakana","yukatakanahalfwidth","yukorean","yusbigcyrillic","yusbigiotifiedcyrillic","yuslittlecyrillic","yuslittleiotifiedcyrillic","yusmallhiragana","yusmallkatakana","yusmallkatakanahalfwidth","yuyekorean","yuyeokorean","yyabengali","yyadeva","zaarmenian","zacute","zadeva","zagurmukhi","zaharabic","zahfinalarabic","zahinitialarabic","zahiragana","zahmedialarabic","zainarabic","zainfinalarabic","zakatakana","zaqefgadolhebrew","zaqefqatanhebrew","zarqahebrew","zayin","zayindagesh","zayindageshhebrew","zayinhebrew","zbopomofo","zcaron","zcircle","zcircumflex","zcurl","zdot","zdotaccent","zdotbelow","zecyrillic","zedescendercyrillic","zedieresiscyrillic","zehiragana","zekatakana","zero","zeroarabic","zerobengali","zerodeva","zerogujarati","zerogurmukhi","zerohackarabic","zeroinferior","zeromonospace","zerooldstyle","zeropersian","zerosuperior","zerothai","zerowidthjoiner","zerowidthnonjoiner","zerowidthspace","zeta","zhbopomofo","zhearmenian","zhebrevecyrillic","zhecyrillic","zhedescendercyrillic","zhedieresiscyrillic","zihiragana","zikatakana","zinorhebrew","zlinebelow","zmonospace","zohiragana","zokatakana","zparen","zretroflexhook","zstroke","zuhiragana","zukatakana","angbracketleftbig","angbracketleftBig","angbracketleftbigg","angbracketleftBigg","angbracketrightBig","angbracketrightbig","angbracketrightBigg","angbracketrightbigg","arrowhookleft","arrowhookright","arrowlefttophalf","arrowleftbothalf","arrownortheast","arrownorthwest","arrowrighttophalf","arrowrightbothalf","arrowsoutheast","arrowsouthwest","backslashbig","backslashBig","backslashBigg","backslashbigg","bardbl","bracehtipdownleft","bracehtipdownright","bracehtipupleft","bracehtipupright","braceleftBig","braceleftbig","braceleftbigg","braceleftBigg","bracerightBig","bracerightbig","bracerightbigg","bracerightBigg","bracketleftbig","bracketleftBig","bracketleftbigg","bracketleftBigg","bracketrightBig","bracketrightbig","bracketrightbigg","bracketrightBigg","ceilingleftbig","ceilingleftBig","ceilingleftBigg","ceilingleftbigg","ceilingrightbig","ceilingrightBig","ceilingrightbigg","ceilingrightBigg","circledotdisplay","circledottext","circlemultiplydisplay","circlemultiplytext","circleplusdisplay","circleplustext","contintegraldisplay","contintegraltext","coproductdisplay","coproducttext","floorleftBig","floorleftbig","floorleftbigg","floorleftBigg","floorrightbig","floorrightBig","floorrightBigg","floorrightbigg","hatwide","hatwider","hatwidest","intercal","integraldisplay","integraltext","intersectiondisplay","intersectiontext","logicalanddisplay","logicalandtext","logicalordisplay","logicalortext","parenleftBig","parenleftbig","parenleftBigg","parenleftbigg","parenrightBig","parenrightbig","parenrightBigg","parenrightbigg","prime","productdisplay","producttext","radicalbig","radicalBig","radicalBigg","radicalbigg","radicalbt","radicaltp","radicalvertex","slashbig","slashBig","slashBigg","slashbigg","summationdisplay","summationtext","tildewide","tildewider","tildewidest","uniondisplay","unionmultidisplay","unionmultitext","unionsqdisplay","unionsqtext","uniontext","vextenddouble","vextendsingle","getDingbatsGlyphsUnicode","a202","a3","a4","a5","a119","a118","a117","a11","a12","a13","a14","a15","a16","a105","a17","a18","a19","a20","a21","a22","a23","a24","a25","a26","a27","a28","a6","a7","a8","a9","a10","a29","a30","a31","a32","a33","a34","a35","a36","a37","a38","a39","a40","a41","a42","a43","a44","a45","a46","a47","a48","a49","a50","a51","a52","a53","a54","a55","a56","a57","a58","a59","a60","a61","a62","a63","a64","a65","a66","a67","a68","a69","a70","a71","a72","a73","a74","a203","a75","a204","a76","a77","a78","a79","a81","a82","a83","a84","a97","a98","a99","a100","a101","a102","a103","a104","a106","a107","a108","a112","a111","a110","a109","a120","a121","a122","a123","a124","a125","a126","a127","a128","a129","a130","a131","a132","a133","a134","a135","a136","a137","a138","a139","a140","a141","a142","a143","a144","a145","a146","a147","a148","a149","a150","a151","a152","a153","a154","a155","a156","a157","a158","a159","a160","a161","a163","a164","a196","a165","a192","a166","a167","a168","a169","a170","a171","a172","a173","a162","a174","a175","a176","a177","a178","a179","a193","a180","a199","a181","a200","a182","a201","a183","a184","a197","a185","a194","a198","a186","a195","a187","a188","a189","a190","a191","a89","a90","a93","a94","a91","a92","a205","a85","a206","a86","a87","a88","a95","a96","getSpecialPUASymbols","mapSpecialUnicodeValues","getUnicodeForGlyph","glyphsUnicodeMap","unicode","nameLen","hexStr","UnicodeRanges","getUnicodeRangeFor","lastPosition","SpecialCharRegExp","CategoryCache","getCharUnicodeCategory","cachedCategory","groups","category","isWhitespace","isZeroWidthDiacritic","isInvisibleFormatMark","clearUnicodeCaches","SEAC_ANALYSIS_ENABLED","FontFlags","FixedPitch","Serif","Symbolic","Script","Nonsymbolic","Italic","AllCap","SmallCap","ForceBold","MacStandardGlyphOrdering","recoverGlyphName","type1FontGlyphMapping","builtInEncoding","glyphNames","charCodeToGlyphId","glyphId","isSymbolicFont","isInternalFont","baseEncodingName","differences","glyphName","standardGlyphName","normalizeFontName","getStdFontMap","Helvetica","Courier","ZapfDingbats","ArialNarrow","ArialBlack","Arial","ArialMT","ArialUnicodeMS","CourierNew","CourierNewPSMT","TimesNewRoman","TimesNewRomanPS","TimesNewRomanPSMT","getFontNameToFileMap","getNonStdFontMap","Calibri","CenturyGothic","ComicSansMS","Impact","LucidaConsole","NuptialScript","SegoeUISymbol","getSerifFonts","Albertus","Aldus","Alexandria","Algerian","Antiqua","Apex","Arno","Aster","Aurora","Baskerville","Bell","Bembo","Benguiat","Bodoni","Bookman","Calisto","Calvert","Capitals","Cambria","Cartier","Caslon","Catull","Centaur","Chaparral","Cheltenham","Clarendon","Clearface","Cochin","Colonna","Constantia","Corona","Ecotype","Egyptienne","Elephant","Excelsior","Fairfield","Folkard","Footlight","FreeSerif","Garamond","Gentium","Georgia","Gloucester","Granjon","Heather","Hercules","Hiroshige","Imprint","Janson","Joanna","Korinna","Lexicon","LiberationSerif","Literaturnaya","Lucida","Melior","Memphis","Miller","Minion","Modern","Palatino","Perpetua","Plantin","Playbill","Renault","Requiem","Rockwell","Roman","Sabon","Scala","Seagull","Sistina","Souvenir","STIX","Sylfaen","Times","Trajan","Utopia","Versailles","Wanted","Weiss","Windsor","XITS","getSymbolsFonts","Dingbats","Wingdings","getGlyphMapForStandardFonts","getSupplementalGlyphMapForArialBlack","getSupplementalGlyphMapForCalibri","getStandardFontName","fontName","stdFontMap","isKnownFontName","ToUnicodeMap","cmap","amend","IdentityToUnicodeMap","firstChar","lastChar","CFFFont","compiler","loadedName","_createBuiltInEncoding","getCharset","getGlyphMapping","cidToGidMap","charsets","composite","invCidToGidMap","defaultEncoding","encodings","getUint32","getInt16","getInt8","getFloat214","getSubroutineBias","numSubrs","parseCmap","segCount","idDelta","idOffset","ids","parseCff","glyphs","gsubrs","isCFFCIDFont","parseGlyfTable","glyf","loca","isGlyphLocationsLong","itemSize","itemDecode","lookupCmap","compileGlyf","cmds","font","quadraticCurveTo","xa","ya","numberOfContours","arg1","arg2","scale01","scale10","subglyph","endPtsOfContours","instructionLength","numberOfPoints","points","repeat","startPoint","endPoint","contour","compileCharString","charStringCode","bezierCurveTo","stems","stackClean","xb","yb","subrCode","subrsBias","achar","bchar","glyphNameMap","gsubrsBias","NOOP","Commands","newArgs","arg","CompiledFont","compiledGlyphs","compiledCharCodeToGlyphId","getPathJs","fn","compileEx","compileGlyph","compileGlyphImpl","hasBuiltPath","TrueTypeCompiled","Type2Compiled","cffInfo","FontRendererFactory","indexToLocFormat","unitsPerEm","numTables","tag","getMetrics","getFontBasicMetrics","capHeight","xHeight","ON_CURVE_POINT","X_SHORT_VECTOR","Y_SHORT_VECTOR","REPEAT_FLAG","X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR","Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR","OVERLAP_SIMPLE","ARG_1_AND_2_ARE_WORDS","ARGS_ARE_XY_VALUES","WE_HAVE_A_SCALE","MORE_COMPONENTS","WE_HAVE_AN_X_AND_Y_SCALE","WE_HAVE_A_TWO_BY_TWO","WE_HAVE_INSTRUCTIONS","GlyfTable","glyfTable","locaTable","DataView","byteOffset","Glyph","getSize","reduce","write","totalSize","ArrayBuffer","isLocationLong","setUint32","setUint16","locaIndex","factors","simple","composites","GlyphHeader","CompositeGlyph","SimpleGlyph","spos","factor","xMiddle","yMin","yMax","setInt16","Contour","xCoordinates","yCoordinates","contours","instructions","endPt","numberOfPt","flag","getUint8","allXCoordinates","pointFlags","endPtsOfContoursIndex","lastCoordinate","lastX","lastY","setUint8","argument1","argument2","transf","writeInt16","writeInt32","writeData","OTF_HEADER_SIZE","OTF_TABLE_ENTRY_SIZE","OpenTypeFileBuilder","sfnt","getSearchParams","entriesCount","entrySize","maxPower2","searchRange","rangeShift","toArray","tablesNames","tableName","tableOffsets","paddedLength","searchParams","checksum","quad","addTable","HINTING_ENABLED","COMMAND_MAP","hstem","vstem","vmoveto","rlineto","hlineto","vlineto","rrcurveto","callsubr","flex","drop","endchar","rmoveto","hmoveto","vhcurveto","hvcurveto","Type1CharString","lsb","flexing","convert","encoded","wx","sbx","executeCommand","dy","asb","splice","sby","num2","num1","flexArgs","howManyArgs","command","keepStack","stackLength","EEXEC_ENCRYPT_KEY","CHAR_STRS_ENCRYPT_KEY","isHexDigit","decrypt","discardNumber","c1","c2","decrypted","decryptAscii","digit1","digit2","isSpecial","Type1Parser","encrypted","isBinary","readNumberArray","getToken","token","readInt","readBoolean","prevChar","readCharStrings","lenIV","extractFontProgram","charstrings","privateData","program","blueArray","charString","charStringObject","extractFontHeader","encodingArg","findBlock","streamBytes","signature","startIndex","streamBytesLength","getHeaderBlock","suggestedLength","EEXEC_SIGNATURE","streamStartPos","headerBytes","headerBytesLength","actualLength","getEexecBlock","eexecBytes","Type1Font","PFB_HEADER_SIZE","headerBlockLength","length1","eexecBlockLength","length2","pfbHeader","pfbHeaderPresent","headerBlock","headerBlockParser","eexecBlock","eexecBlockParser","type2Charstrings","getType2Charstrings","getType2Subrs","wrap","getSeacs","charstringsLen","seacMap","type1Charstrings","type1Charstring","type1Subrs","type2Subrs","bbox","charsetArray","fields","field","subrIndex","PRIVATE_USE_AREAS","PDF_GLYPH_SPACE_UNITS","EXPORT_DATA_PROPERTIES","EXPORT_DATA_EXTRA_PROPERTIES","adjustWidths","glyphsWidths","adjustTrueTypeToUnicode","nameRecords","hasIncludedToUnicodeMap","hasEncoding","toUnicode","isWinNameRecord","adjustType1ToUnicode","amendFallbackToUnicode","fallbackToUnicode","originalCharCode","fontChar","accent","vmetric","operatorListId","isSpace","isInFont","int16","writeSignedInt16","signedInt16","writeUint32","int32","string16","safeString16","isTrueTypeFile","isTrueTypeCollectionFile","isOpenTypeFile","isType1File","isCFFFile","getFontFileType","subtype","fileType","fileSubtype","applyStandardFontGlyphMap","glyphMap","buildToFontChar","toFontChar","isMacNameRecord","language","convertCidString","shouldThrow","adjustMapping","hasGlyph","newGlyphZeroId","newMap","toUnicodeExtraMap","usedGlyphIds","privateUseAreaIndex","privateUseOffetStart","nextAvailableFontCharCode","privateUseOffetEnd","isInPrivateArea","fontCharCode","getRanges","fontGetRangesSort","codeIndices","createCmapTable","bmpLength","trailingRangesCount","startCount","endCount","idDeltas","idRangeOffsets","glyphsIds","contiguous","startCode","format314","format31012","header31012","validateOS2Table","os2","selection","usWinAscent","createOS2Table","override","ulUnicodeRange1","ulUnicodeRange2","ulUnicodeRange3","ulUnicodeRange4","firstCharIndex","lastCharIndex","typoAscent","typoDescent","winAscent","winDescent","fixedPitch","createPostTable","createPostscriptName","createNameTable","proto","stringsUnicode","strBufUnicode","platforms","namesRecordCount","nameTable","strOffset","strs","nameRecord","Font","psName","mimetype","disableFontFace","isType3Font","missingFile","_charsCache","_glyphCache","isSerifFont","isSimulatedFlags","baseName","serifFonts","namePart","isMonospace","systemFontInfo","matches","isInvalidPDFjsFont","fallbackName","guessFallback","css","lineHeight","cidEncoding","vmetrics","defaultVMetrics","fallbackToSystemFont","checkAndRepair","isOpenType","renderer","exportData","extraProperties","exportDataProperties","nonStdFontMap","isStandardFont","isMappedToStandardFont","fontBasicMetricsMap","metrics","bold","italic","isNarrow","remeasure","unicodeCharCode","VALID_TABLES","readTables","head","hhea","hmtx","maxp","post","readTableEntry","previousPosition","readOpenTypeHeader","ttf","entrySelector","readTrueTypeCollectionHeader","ttc","ttcTag","majorVersion","minorVersion","numFonts","offsetTable","dsigTag","dsigLength","dsigOffset","readTrueTypeCollectionData","fontNameParts","fallbackData","potentialHeader","potentialTables","readNameTable","nameEntry","readCmapTable","platformId","encodingId","mappings","hasShortCmap","potentialTable","canBreak","useTable","correctlySorted","nextBytes","nextPlatformId","subHeaderKeys","maxSubHeaderKey","subHeaderKey","subHeaders","entryCount","idRangePos","segIndex","offsetsCount","offsetIndex","rangeOffset","nGroups","startCharCode","endCharCode","glyphCode","sanitizeMetrics","headTable","dupFirstEntry","caretOffset","numOfMetrics","macStyle","numOfSidebearings","numMissing","sanitizeGlyph","sourceStart","sourceEnd","destStart","hintsValid","glyphProfile","sizeOfInstructions","contoursCount","flagsCount","instructionsStart","instructionsLength","instructionsEnd","coordinatesLength","xLength","yLength","xyLength","glyphDataLength","sanitizeHead","locaLength","numGlyphsPlusOne","sanitizeGlyphLocations","maxSizeOfInstructions","itemEncode","fontItemDecodeLong","fontItemEncodeLong","fontItemDecode","fontItemEncode","numGlyphsOut","locaDataSize","locaData","oldGlyfData","oldGlyfDataLength","newGlyfData","locaEntries","nextOffset","missingGlyphs","writeOffset","simpleGlyph","firstEntryLength","readPostScriptTable","propertiesObj","maxpNumGlyphs","glyphNameIndexes","customNames","stringLength","records","FORMAT_0_HEADER_LENGTH","numRecords","stringsStart","NAME_RECORD_LENGTH","record","TTOpsStackDeltas","sanitizeTTProgram","ttContext","funcId","lastEndf","lastDeff","callstack","functionsCalled","tooComplexToFollowFunctions","inFDEF","ifLevel","inELSE","op","functionsUsed","functionsStackDeltas","newStackLength","functionsDefined","stackTop","content","foldTTTable","checkInvalidFunctions","maxFunctionDefs","sanitizeTTPrograms","fpgm","prep","cvt","cvtData","ttcData","cffFile","isTrueType","isComposite","scaleFactors","advanceWidth","maxZones","glyphsInfo","metricsOverride","lineGap","isCidToGidMapEmpty","cmapTable","cmapPlatformId","cmapEncodingId","cmapMappings","forcePostTable","unicodeOrCharCode","mapping","glyphZeroId","newMapping","namePrototype","builder","tableTag","newCharCodeToGlyphId","getCharCodes","charCodes","createCharCode","baseGlyphName","accentGlyphName","baseGlyphId","accentGlyphId","accentOffset","baseFontCharCode","accentFontCharCode","tan","fontFieldsHmtx","cffWidths","_charToGlyph","widthCode","fromCodePoint","charsToGlyphs","chars","getCharPositions","positions","glyphCacheValues","encodeString","buffers","currentBuf","hasCurrentBufErrors","getCharCode","charCodeLength","ErrorFont","ShadingType","FUNCTION_BASED","AXIAL","RADIAL","FREE_FORM_MESH","LATTICE_FORM_MESH","COONS_PATCH_MESH","TENSOR_PATCH_MESH","Pattern","parseShading","shading","res","RadialAxialShading","MeshShading","DummyShading","BaseShading","SMALL_NUMBER","getIR","shadingType","coordsLen","coordsArr","t0","t1","domainArr","extendStart","extendEnd","extendArr","r1","r2","distance","hypot","fnObj","createFromArray","NUMBER_OF_SAMPLES","step","colorStops","rgbColor","iBase","rgbBase","cssColorBase","iPrev","rgbPrev","maxSlopeR","maxSlopeG","maxSlopeB","minSlopeR","minSlopeG","minSlopeB","slopesExist","cssColor","background","r0","MeshStreamReader","context","tmpCompsBuf","csNumComps","colorSpace","tmpCsCompsBuf","colorFn","hasData","align","readFlag","bitsPerFlag","readCoordinate","bitsPerCoordinate","yi","readComponents","ci","bCache","buildB","lut","t_","getB","clearPatternCaches","MIN_SPLIT_PATCH_CHUNKS_AMOUNT","MAX_SPLIT_PATCH_CHUNKS_AMOUNT","TRIANGLE_DENSITY","coords","figures","decodeContext","patchMesh","_decodeType4Shading","verticesPerRow","_decodeType5Shading","_decodeType6Shading","_decodeType7Shading","_updateBounds","_buildFigureFromPatch","_packData","operators","ps","verticesLeft","coord","tmp1","tmp2","tmp3","tmp4","figure","figureMinX","figureMinY","figureMaxX","figureMaxY","splitXBy","bounds","splitYBy","figureCoords","figureColors","cl","cr","c0","c3","bRow","bCol","newColor","maxY","coordsPacked","xy","colorsPacked","getTilingPatternIR","operatorList","xstep","ystep","paintType","tilingType","CalibriBoldFactors","CalibriBoldMetrics","CalibriBoldItalicFactors","CalibriBoldItalicMetrics","CalibriItalicFactors","CalibriItalicMetrics","CalibriRegularFactors","CalibriRegularMetrics","HelveticaBoldFactors","HelveticaBoldMetrics","HelveticaBoldItalicFactors","HelveticaBoldItalicMetrics","HelveticaItalicFactors","HelveticaItalicMetrics","HelveticaRegularFactors","HelveticaRegularMetrics","LiberationSansBoldWidths","LiberationSansBoldMapping","LiberationSansBoldItalicWidths","LiberationSansBoldItalicMapping","LiberationSansItalicWidths","LiberationSansItalicMapping","LiberationSansRegularWidths","LiberationSansRegularMapping","MyriadProBoldFactors","MyriadProBoldMetrics","MyriadProBoldItalicFactors","MyriadProBoldItalicMetrics","MyriadProItalicFactors","MyriadProItalicMetrics","MyriadProRegularFactors","MyriadProRegularMetrics","SegoeuiBoldFactors","SegoeuiBoldMetrics","SegoeuiBoldItalicFactors","SegoeuiBoldItalicMetrics","SegoeuiItalicFactors","SegoeuiItalicMetrics","SegoeuiRegularFactors","SegoeuiRegularMetrics","getXFAFontMap","baseWidths","baseMapping","getXfaFontName","fontMap","getXfaFontWidths","rescaledBaseWidths","currentArray","newWidths","charUnicode","unicode1","unicode2","getXfaFontDict","descriptor","systemInfo","PostScriptParser","nextToken","accept","expect","PostScriptTokenTypes","LBRACE","parseBlock","RBRACE","NUMBER","OPERATOR","parseCondition","conditionLocation","IF","jumpLocation","endOfTrue","IFELSE","PostScriptToken","opCache","getOperator","PostScriptLexer","BaseLocalCache","_onlyRefs","onlyRefs","_nameRefMap","_imageMap","_imageCache","LocalImageCache","LocalColorSpaceCache","LocalFunctionCache","LocalGStateCache","LocalTilingPatternCache","RegionalImageCache","GlobalImageCache","NUM_PAGES_THRESHOLD","MIN_IMAGES_TO_CACHE","MAX_BYTE_SIZE","decodeFailedSet","_refCache","byteSize","#byteSize","cacheLimitReached","#cacheLimitReached","shouldCache","pageIndexSet","addDecodeFailed","hasDecodeFailed","addByteSize","setData","onlyData","PDFFunctionFactory","cachedFunction","parsedFunction","PDFFunction","parseArray","fnRef","localFunction","_localFunctionCache","toNumberArray","getSampleArray","outputSize","bps","sampleMul","strBytes","strIdx","typeNum","constructSampled","constructInterpolated","constructStiched","constructPostScript","fnArray","toMultiArray","interpolate","xmin","xmax","ymin","ymax","domain","inputSize","encode","samples","constructSampledFn","cubeVertices","cubeN","cubeVertex","domain_2i","domain_2i_1","size_i","e0","n0","n1","rj","constructInterpolatedFn","fns","constructStichedFn","constructStichedFromIRClip","dmin","dmax","rmin","rmax","PostScriptCompiler","numOutputs","numInputs","evaluator","PostScriptEvaluator","MAX_CACHE_SIZE","cache_available","constructPostScriptFn","cachedValue","execute","stackIndex","bound","isPDFFunction","fnDict","PostScriptStack","MAX_STACK_SIZE","initialStack","copy","roll","counter","operator","atan2","cos","log10","sin","AstNode","visit","AstArgument","visitArgument","AstLiteral","visitLiteral","AstBinaryOperation","visitBinaryOperation","AstMin","visitMin","AstVariable","visitVariable","AstVariableDefinition","variable","visitVariableDefinition","ExpressionBuilderVisitor","parts","literal","operation","definition","buildAddOperation","buildMulOperation","buildSubOperation","buildMinOperation","lastRegister","ast1","ast2","tmpVar","instruction","statementBuilder","expr","baseTypes","arabicTypes","isOdd","isEven","findUnequal","setValues","reverseValues","createBidiText","isLTR","dir","bidi","startLevel","strLength","numBidi","charType","levels","sor","eor","lastType","before","after","highestLevel","lowestOddLevel","NORMAL","style","BOLD","ITALIC","BOLDITALIC","substitutionMap","local","ultimate","alias","fontAliases","getStyleToAppend","getFamilyName","keywords","tok","generateFont","localFontPath","useFallback","usePath","append","extra","substitution","aliasAppend","fallbackInfo","fallbackUltimate","getFontSubstitution","systemFontCache","idFactory","baseFontName","standardFontName","substitutionInfo","subst","mustAddBaseFont","getDocId","createFontId","MIN_IMAGE_DIM","MAX_IMAGE_DIM","MAX_ERROR","ImageResizer","imgData","isMask","_imgData","_isMask","needsToBeResized","_goodSquareLength","MAX_DIM","area","_hasMaxArea","MAX_AREA","_areGoodDims","_guessMax","maxArea","setMaxArea","canvas","ctx","getContext","fillRect","opacity","getImageData","tolerance","defaultHeight","middle","createImage","_createImage","_encodeBMP","blob","Blob","bitmapPromise","createImageBitmap","minFactor","firstFactor","steps","newWidth","newHeight","prevWidth","prevHeight","drawImage","transferToImageBitmap","bitPerPixel","colorTable","maskTable","compression","rowLen","newData","extraLen","view","headerLength","fileLength","bmpData","setInt32","SEED","MASK_HIGH","MASK_LOW","MurmurHash3_64","seed","update","isView","blockCounts","tailLength","dataUint32","k1","k2","C1","C2","C1_LOW","C2_LOW","hexdigest","addState","parentState","pattern","checkFn","iterateFn","processFn","InitialState","iterateInlineImageGroup","iFirstSave","iCurr","foundInlineImageGroup","MIN_IMAGES_IN_INLINE_IMAGES_BLOCK","MAX_IMAGES_IN_INLINE_IMAGES_BLOCK","MAX_WIDTH","IMAGE_PADDING","argsArray","iFirstTransform","iFirstPIIXO","maxLineHeight","currentX","currentY","img","imgWidth","imgHeight","imgRowSize","putImageData","ImageData","iterateImageMaskGroup","foundImageMaskGroup","MIN_IMAGES_IN_MASKS_BLOCK","MAX_IMAGES_IN_MASKS_BLOCK","MAX_SAME_IMAGES_IN_MASKS_BLOCK","iFirstPIMXO","isSameImage","iTransform","transformArgs","firstPIMXOArg0","firstTransformArg0","firstTransformArg1","firstTransformArg2","firstTransformArg3","iPIMXO","images","maskParams","iterateImageGroup","iFirstPIXO","firstPIXOArg0","MIN_IMAGES_IN_BLOCK","MAX_IMAGES_IN_BLOCK","iterateShowTextGroup","iFirstSetFont","firstSetFontArg0","firstSetFontArg1","MIN_CHARS_IN_BLOCK","MAX_CHARS_IN_BLOCK","iFirstBeginText","iFirstSetTextMatrix","iFirstShowText","iFirstEndText","iFirst","iEndText","NullOptimizer","queue","_optimize","flush","QueueOptimizer","lastProcessed","iterate","OperatorList","CHUNK_SIZE","CHUNK_SIZE_ABOUT","intent","streamSink","_streamSink","optimizer","dependencies","_totalLength","_resolved","ready","totalLength","addOp","addImageOps","optionalContent","addDependency","addDependencies","addOpList","opList","_transfers","transfers","cached","separateAnnots","enqueue","decodeAndClamp","addend","coefficient","resizeImageMask","PDFImage","image","isInline","smask","jpxDecode","imageMask","matte","needsDecode","decodeCoefficients","decodeAddends","isIndexed","maskDict","buildImage","smaskData","maskData","createRawMask","imgArray","imageIsFromDecodeStream","computedLength","haveFullData","createMask","isSingleOpaquePixel","createImageData","decodeBuffer","getComponents","bufferPos","rowComps","loop1End","loop2End","remainingBits","fillOpacity","rgbaBuf","alphaBuf","sw","sh","fillGrayBuffer","imageOffset","maskOffset","undoPreblend","matteRgb","matteR","matteG","matteB","mustBeResized","getImageBytes","createBitmap","imageLength","isHandled","rgba","internal","maybeUndoPreblend","canvasImgData","imageBytes","DefaultPartialEvaluatorOptions","freeze","maxImageSize","ignoreErrors","canvasMaxAreaInBytes","fontExtraProperties","useSystemFonts","cMapUrl","standardFontDataUrl","PatternType","TILING","SHADING","TEXT_CHUNK_BATCH_SIZE","deferred","normalizeBlendMode","parsingArray","maybeBM","incrementCachedImageMaskCount","TimeSlotManager","TIME_SLOT_DURATION_MS","CHECK_TIME_EVERY","checked","endTime","now","PartialEvaluator","handler","fontCache","builtInCMapCache","standardFontDataCache","globalImageCache","type3FontRefs","_regionalImageCache","_fetchBuiltInCMapBound","_pdfFunctionFactory","parsingType3Font","newOptions","newEvaluator","hasBlendModes","nonBlendModesSet","nodes","graphicStates","graphicState","bm","xObjects","xObject","xResources","ok","statusText","sendWithPromise","fetchStandardFontData","standardFontNameToFileName","buildFormXObject","xobj","task","initialState","groupOptions","parseMarkedContentProps","group","isolated","knockout","groupSubtype","parseColorSpace","backdrop","getOperatorList","_sendImgData","cacheGlobally","buildPaintImageXObject","localImageCache","imageRef","bitStrideLength","cacheData","createObjId","dataLen","SMALL_IMAGE_DIMENSIONS","imageObj","localLength","handleSMask","stateManager","smaskContent","smaskOptions","transferObj","transferFn","transferMap","handleTransferFunction","tr","transferArray","transferMaps","numFns","numEffectfulFns","handleTilingType","patternDict","localTilingPatternCache","tilingOpList","patternResources","operatorListIR","tilingPatternIR","handleSetFont","fontArgs","fontRef","fallbackFontDict","translated","loadFont","loadType3Data","type3Dependencies","TranslatedFont","evaluatorOptions","handleText","isAddToPathSet","textRenderingMode","fillColorSpace","buildFontPaths","ensureStateFont","gState","localGStateCache","gStateRef","isSimpleGState","gStateObj","errorFont","fontRes","preEvaluatedFont","preEvaluateFont","hash","fontRefIsRef","fontID","aliasFontRef","translateFont","translatedFont","buildPath","parsingText","lastIndex","Infinity","opArgs","localShadingPatternCache","patternIR","handleColorN","patternName","rawPattern","localTilingPattern","_parseVisibilityExpression","nestingCounter","currentResult","MAX_NESTING","object","nestedResult","contentProperties","optionalContentType","expression","optionalContentGroups","groupIds","ocg","policy","EvalState","xobjs","StateManager","preprocessor","EvaluatorPreprocessor","timeSlotManager","closePendingRestoreOPS","argument","savedStatesDepth","promiseBody","all","ensureNotTerminated","stop","isValidName","localImage","resolveXObject","rejectXObject","globalImage","fontSize","combinedGlyphs","arrItem","strokeColorSpace","shadingRes","patternId","localGStateObj","resolveGState","rejectGState","extGState","getTextContent","includeMarkedContent","sink","seenStyles","viewBox","markedContentData","disableNormalization","keepWhiteSpace","TextState","textContent","styles","textContentItem","initialized","totalHeight","prevTransform","textAdvanceScale","spaceInFlowMin","spaceInFlowMax","trackingSpaceMin","negativeSpaceMax","notASpace","hasEOL","twoLastChars","twoLastCharsPos","saveLastChar","nextPos","shouldAddWhitepsace","resetLastChars","TRACKING_SPACE_FACTOR","NOT_A_SPACE_FACTOR","NEGATIVE_SPACE_FACTOR","SPACE_IN_FLOW_MIN_FACTOR","SPACE_IN_FLOW_MAX_FACTOR","VERTICAL_SHIFT_RATIO","showSpacedTextBuffer","emptyXObjectCache","emptyGStateCache","textState","pushWhitespace","getCurrentTextTransform","tsm","textHScale","textRise","isCharBBox","glyphHeight","ctm","textMatrix","ensureTextContentItem","fontSubstitution","fontSubstitutionLoadedName","trm","scaleLineX","textLineMatrix","scaleCtmX","updateAdvanceScale","scaleFactor","runBidiTransform","textChunk","text","bidiResult","applyInverseRotation","compareWithLastPosition","glyphWidth","currentTransform","posX","posY","lastPosX","lastPosY","rotate","advanceY","advanceX","textOrientation","appendEOL","flushTextContentItem","addFakeSpaces","buildTextContentItem","extraSpacing","charSpacing","translateTextMatrix","scaledDim","wordSpacing","glyphUnicode","enqueueChunk","batch","previousState","fontNameArg","fontSizeArg","leading","translateTextLineMatrix","carriageReturn","setTextLineMatrix","spaceFactor","elements","currentState","xObjStateManager","sinkWrapper","enqueueInvoked","desiredSize","gStateFont","mcid","getPageObjId","extractDataStructures","cidToGidBytes","toUnicodePromise","readToUnicode","cidSystemInfo","registry","ordering","supplement","diffEncoding","nonEmbeddedFont","isSymbolsFontName","isNonsymbolicFont","builtToUnicode","buildToUnicode","readCidToGidMap","_simpleFontToUnicode","forceGlyphs","codeStr","ucs2CMapName","ucs2CMap","ucs2","cmapObj","glyphsData","glyphID","extractWidths","glyphsVMetrics","dw","dw2","missingWidth","getBaseFontMetrics","buildCharCodeToWidth","firstWidth","fontNameWoStyle","monospace","lookupName","Metrics","glyphWidths","widthsByGlyphName","baseDict","df","diffLength","diffBuf","diffEntry","uint8array","widthsBuf","compositeWidths","subWidthsBuf","newProperties","baseFont","fontNameStr","baseFontStr","fontFile","length3","glyphScaleFactors","subtypeEntry","_evaluatorOptions","type3Loaded","sent","type3Evaluator","loadCharProcsPromise","charProcs","fontResources","charProcOperatorList","fontBBoxSize","glyphStream","_removeType3ColorOperators","dummyOperatorList","_bbox","charBBox","charBBoxSize","gStateKey","stateStack","old","opMap","ri","gs","cm","BT","ET","Tc","Tw","Tz","TL","Tf","Tr","Ts","Td","TD","Tm","Tj","TJ","d0","d1","CS","SC","SCN","sc","scn","RG","rg","BI","ID","EI","Do","MP","BMC","BDC","EMC","BX","EX","BM","BD","true","fa","fal","fals","false","nul","null","MAX_INVALID_PATH_OPS","nonProcessedArgs","_isPathOp","_numInvalidPathOPS","opSpec","argsLength","preprocessCommand","DefaultAppearanceEvaluator","fontColor","parseDefaultAppearance","AppearanceStreamEvaluator","breakLoop","_localColorSpaceCache","parseAppearanceStream","getPdfColor","isFill","createDefaultAppearance","FakeUnicodeFont","ctxMeasure","_fontNameId","fontDescriptorRef","_fontDescriptorRef","fontDescriptor","getNewPersistentRef","descendantFontRef","descendantFont","currentWidths","baseFontRef","_createContext","createFontResources","measureText","getFirstPositionInfo","lineDescent","createAppearance","bgColor","strokeAlpha","lineWidth","hscale","vscale","maxHeight","fscale","newFontSize","vShift","appearance","appearanceStreamDict","ap","NameOrNumberTree","root","_type","getAll","kids","kid","kidsOrEntries","loopCount","MAX_LEVELS","limits","currentKey","NameTree","NumberTree","clearGlobalCaches","pickPlatformItem","stripPath","FileSpec","contentAvailable","skipContent","fs","_contentRef","fileObj","description","desc","serializable","rawFilename","XMLParserErrorCode","NoError","EndOfDocument","UnterminatedCdat","UnterminatedXmlDeclaration","UnterminatedDoctypeDeclaration","UnterminatedComment","MalformedElement","OutOfMemory","UnterminatedAttributeValue","UnterminatedElement","ElementNeverBegun","isWhitespaceString","XMLParserBase","_resolveEntities","onResolveEntity","_parseContent","attributes","skipWs","attrName","attrValue","attrEndChar","attrEndIndex","parsed","_parseProcessingInstruction","attrStart","parseXml","ch2","onEndElement","onPi","onComment","onCdata","q2","complexDoctype","doctypeContent","onDoctype","isClosed","onBeginElement","onText","SimpleDOMNode","nodeName","nodeValue","firstChild","childNodes","nextSibling","parentNode","child","hasChildNodes","searchNode","paths","siblingPos","dump","attribute","SimpleXMLParser","hasAttributes","lowerCaseName","_currentFragment","_stack","_errorCode","_hasAttributes","_lowerCaseName","parseFromString","documentElement","lastElement","childNode","MetadataParser","_repair","xmlDocument","_metadataMap","_data","d2","d3","charBuf","_getSequence","_parseArray","seqNode","trim","rdf","parsedData","rawData","DecryptStream","nextChunk","hasMoreData","ARCFourCipher","keyLength","encryptBlock","decryptBlock","encrypt","calculateMD5","calculateMD5Closure","h0","h3","padded","rotateArg","Word64","highInteger","lowInteger","and","word","xor","or","shiftRight","places","shiftLeft","rotateRight","not","lowAdd","highAdd","copyTo","calculateSHA256","calculateSHA256Closure","rotr","maj","sigmaPrime","littleSigma","littleSigmaPrime","h4","h5","h6","h7","calculateSHA512","calculateSHA512Closure","mode384","calculateSHA384","NullCipher","AESBaseCipher","_s","_inv_s","_mix","_mixCol","bufferPosition","_expandKey","cipherKey","_decrypt","_keySize","_cyclesOfRepetition","s0","_encrypt","_decryptBlock2","finalize","sourceLength","iv","plain","_key","lastBlock","psLen","cipher","AES128Cipher","_rcon","rcon","t3","t4","AES256Cipher","PDF17","checkOwnerPassword","password","ownerValidationSalt","userBytes","ownerPassword","hashData","checkUserPassword","userValidationSalt","userPassword","getOwnerKey","ownerKeySalt","ownerEncryption","getUserKey","userKeySalt","userEncryption","PDF20","_hash","combinedLength","combinedArray","remainder","CipherTransform","stringCipherConstructor","streamCipherConstructor","StringCipherConstructor","StreamCipherConstructor","cipherTransformDecryptStream","encryptString","strLen","pad","CipherTransformFactory","defaultPasswordBytes","createEncryptionKey20","#createEncryptionKey20","revision","uBytes","perms","passwordLength","pdfAlgorithm","prepareKeyData","#prepareKeyData","fileId","encryptMetadata","hashDataSize","keyLengthInBytes","encryptionKey","checkData","derivedKey","decodeUserPassword","#decodeUserPassword","buildObjectKey","#buildObjectKey","isAes","buildCipherConstructor","#buildCipherConstructor","cf","cryptFilter","cfm","algorithm","cfDict","streamCryptoName","handlerDict","ownerBytes","fileIdBytes","passwordBytes","decodedPassword","stmf","strf","eff","createCipherTransform","cipherConstructor","writeObject","writeDict","writeStream","writeArray","writeValue","fetchIfRefAsync","isFilterZeroFlateDecode","MIN_LENGTH_FOR_COMPRESSING","CompressionStream","writer","getWriter","close","Response","readable","newFilter","newParams","writeInt","writeString","computeMD5","filesize","xrefInfo","time","md5Buffer","md5BufferLen","writeXFADataForAcroform","newRefs","xml","xfa","nodePath","updateAcroform","acroForm","acroFormRef","hasXfa","hasXfaDatasetsEntry","xfaDatasetsRef","needAppearances","newXfa","updateXFA","xfaData","datasets","getXRefTable","baseOffset","indexes","getIndexes","indexesPosition","computeIDs","getXRefStreamTable","xrefTableData","maxOffset","maxGen","maxGenSize","sizes","structSize","objOffset","newRef","fileIds","md5","getTrailerDict","useXrefStream","startXRef","refForXrefTable","rootRef","infoRef","encryptRef","incrementalUpdate","originalData","MAX_DEPTH","StructElementType","PAGE_CONTENT","STREAM_CONTENT","OBJECT","ANNOTATION","ELEMENT","StructTreeRoot","rootDict","roleMap","structParentIds","init","readRoleMap","addIdToPage","#addIdToPage","pageRef","addAnnotationIdToPage","roleMapDict","canCreateStructureTree","catalogRef","pdfManager","nextKey","hasNothingToUpdate","getPage","accessibilityData","parentTreeId","createStructureTree","catalog","cloneDict","structTreeRootRef","getNewTemporaryRef","structTreeRoot","parentTreeRef","parentTree","nums","writeKids","canUpdateStructTree","numberTree","pageDict","collectParents","structTreeParent","updateStructureTree","numsRef","newNextkey","#writeKids","objr","isPageRef","title","expanded","actualText","tagRef","tagDict","updateParentTag","newTagRef","fallbackKids","objDict","#collectParents","idToElements","structTreeParentId","elems","parentArray","updateElement","pageKid","kidRef","parentRef","#updateParentTag","cachedParentDict","parentKidsRaw","cachedParentKids","parentKidsRef","StructElementNode","parseKids","role","nameObj","pageObjId","objRef","parseKid","StructElement","kidDict","refObjId","StructTreePage","addNode","elemId","addTopLevelNode","nodeToSerializable","kidElement","isValidExplicitDest","page","zoom","allowNull","fetchDest","fetchRemoteDest","JSON","stringify","Catalog","_catDict","getCatalogObj","toplevelPagesDict","_actualNumPages","pageKidsCountCache","pageIndexCache","needsRendering","collection","metadata","streamRef","markInfo","_readMarkInfo","Marked","UserProperties","Suspects","structTree","_readStructTreeRoot","rawObj","pagesObj","documentOutline","_readDocumentOutline","blackColor","outlineDict","parseDestDictionary","destDict","resultObj","docBaseUrl","docAttachments","attachments","outlineItem","attachment","unsafeUrl","setOCGState","permissions","_readPermissions","trailer","optionalContentConfig","config","defaultConfig","groupsData","groupRefs","groupRef","readOptionalContentGroup","readOptionalContentConfig","#readOptionalContentGroup","usage","print","usageObj","printState","viewState","#readOptionalContentConfig","contentGroupRefs","parseOnOff","refs","onParsed","parseOrder","nestedLevels","parsedOrderRefs","nestedOrder","parseNestedOrder","hiddenGroups","MAX_NESTED_LEVELS","nestedName","creator","baseState","on","off","setActualNumPages","hasActualNumPages","_pagesCount","destinations","_readDests","dests","getDestination","allDest","pageLabels","_readPageLabels","prefix","currentLabel","labelDict","st","LIMIT","A_UPPER_CASE","A_LOWER_CASE","baseCharCode","letterIndex","character","pageLayout","pageMode","viewerPreferences","prefs","prefValue","isValid","openAction","nameTree","xfaImages","_collectJavaScript","javaScript","appendIfJavaScriptDict","jsDict","jsActions","fontFallback","translatedFonts","manuallyTriggered","getPageDict","nodesToVisit","visitedNodes","pagesRef","currentPageIndex","currentNode","getAllPageDicts","posInKids","addPageDict","addPageError","queueItem","kidObj","getPageIndex","cachedPageIndex","pagesBeforeRef","kidPromises","uri","actionType","actionName","include","resetForm","urlDict","remoteDest","target","relationship","attachmentDest","namedAction","preserveRB","stateArr","jsAction","jsURL","mayHaveChildren","addChildren","rawValue","ObjectLoader","refSet","load","_walk","nodesToRevisit","pendingRequests","foundMissingData","$acceptWhitespace","$addHTML","$appendChild","$childrenToHTML","$clean","$cleanPage","$cleanup","$clone","$consumed","$content","$data","$dump","$extra","$finalize","$flushHTML","$getAttributeIt","$getAttributes","$getAvailableSpace","$getChildrenByClass","$getChildrenByName","$getChildrenByNameIt","$getDataValue","$getExtra","$getRealChildrenByNameIt","$getChildren","$getContainedChildren","$getNextPage","$getSubformParent","$getParent","$getTemplateRoot","$globalData","$hasSettableValue","$ids","$indexOf","$insertAt","$isCDATAXml","$isBindable","$isDataValue","$isDescendent","$isNsAgnostic","$isSplittable","$isThereMoreWidth","$isTransparent","$isUsable","$lastAttribute","$namespaceId","$nodeName","$nsAttributes","$onChild","$onChildCheck","$onText","$pushGlyphs","$popPara","$pushPara","$removeChild","$root","$resolvePrototypes","$searchNode","$setId","$setSetAttributes","$setValue","$tabIndex","$text","$toPages","$toHTML","$toString","$toStyle","$uid","$buildXFAObject","NamespaceIds","ns","connectionSet","form","localeSet","pdf","sourceSet","stylesheet","xdc","xdp","xfdf","xhtml","xmpmeta","dimConverters","pt","mm","in","px","measurementPattern","stripQuotes","getInteger","defaultValue","validate","getFloat","getKeyword","getStringOption","getMeasurement","def","valueStr","unit","conv","getRatio","den","getRelevant","excluded","viewname","getColor","getBBox","HTMLResult","FAILURE","EMPTY","success","html","breakNode","isBreak","FontFinder","pdfFonts","fonts","defaultFont","reallyMissingFonts","pdfFont","addPdfFont","regular","bolditalic","myriad","missing","endsWith","getDefault","find","maybe","family","selectFont","xfaFont","typeface","posture","real","fontFinder","lineNoGap","WIDTH_FACTOR","FontInfo","margin","paraMargin","top","bottom","right","letterSpacing","FontSelector","defaultXfaFont","defaultParaMargin","defaultLineHeight","pushData","lastFont","fontInfo","popFont","topFont","TextMeasure","fontSelector","extraHeight","addPara","addString","fontLineHeight","noGap","firstLineHeight","fallbackWidth","encodedLine","compute","lastSpacePos","lastSpaceWidth","currentLineWidth","currentLineHeight","isBroken","isFirstLine","isEOL","namePattern","indexPattern","dot","dotDot","dotHash","dotBracket","dotParen","shortcuts","current","host","dataWindow","event","somCache","WeakMap","parseExpression","dotDotAllowed","noExpr","cacheName","formCalc","container","useCache","isQualified","isXFAObject","isXFAObjectArray","isFinite","flat","createDataNode","some","createNodes","_applyPrototype","_attributes","_attributeNames","_children","_cloneAttribute","_dataValue","_defaultValue","_filteredChildrenGenerator","_getPrototype","_getUnsetAttributes","_hasChildren","_max","_options","_parent","_resolvePrototypesHelper","_setAttributes","_validator","uid","NS_DATASETS","XFAObject","nsId","hasChildren","XmlObject","XFAObjectArray","hasOwnProperty","para","paraStack","clean","getPrototypeOf","getOwnPropertyNames","dumped","availableSpace","failingNode","generator","protoAttributes","allAttr","setAttr","ancestors","use","usehref","somExpression","protoProto","newAncestors","unsetAttrName","protoValue","$symbol","getOwnPropertySymbols","clonedChild","allTransparent","XFAAttribute","dataNode","tagName","utf8TagName","utf8Name","skipConsumed","hasNS","$ns","$name","ContentObject","OptionObject","StringObject","IntegerObject","validator","Option01","Option10","measureToString","converters","anchorType","dimensions","colSpan","columnWidths","currentColumn","transformOrigin","presence","visibility","display","hAlign","textAlign","alignSelf","setMinMaxDimensions","minW","minWidth","maxW","minH","minHeight","layoutText","measure","layoutNode","marginH","marginV","leftInset","rightInset","topInset","bottomInset","spaceAbove","spaceBelow","marginLeft","marginRight","exData","contentType","computeBbox","fixDimensions","layoutClass","toStyle","newStyle","createWrapper","wrapper","class","border","insets","insetsH","insetsW","hand","classNames","isPrintOnly","fixTextIndent","indent","textIndent","padding","setAccess","access","relevant","getCurrentPara","setPara","nodeStyle","valueStyle","flexDirection","vAlign","justifyContent","paraStyle","setFontFamily","fixURL","createLine","flushHTML","htmlFromFailing","addHTML","attempt","numberInLine","getAvailableSpace","getTransformedBBox","centerX","centerY","checkDimensions","firstUnsplittable","ERROR","noLayoutFailure","currentContentArea","TEMPLATE_NS_ID","SVG_NS","MAX_ATTEMPTS_FOR_LRTB_LAYOUT","MAX_EMPTY_PAGES","DEFAULT_TAB_INDEX","HEADING_PATTERN","MIMES","IMAGES_HEADERS","getBorderDims","borderExtra","hasMargin","_setValue","templateNode","Value","getContainedChildren","SubformSet","isRequired","nullTest","setTabIndex","traversal","applyAssist","assist","assistTitle","ariaRole","ariaLevel","ariaLabel","speak","toolTip","valueToHtml","setFirstUnsplittable","unsetFirstUnsplittable","handleBreak","targetType","currentPageArea","PageArea","startNew","ContentArea","pageArea","nextPageArea","contentAreas","contentArea","indexForCurrent","indexForTarget","handleOverflow","extraNode","saved","savedMethod","AppearanceFilter","Arc","circular","startAngle","sweepAngle","edge","Edge","edgeStyle","strokeWidth","thickness","xmlns","overflow","cx","cy","rx","ry","largeArc","vectorEffect","preserveAspectRatio","svg","Area","extras","draw","exObject","exclGroup","subform","subformSet","xfaName","Assist","Barcode","charEncoding","dataColumnCount","dataPrep","dataRowCount","endChar","errorCorrectionLevel","moduleHeight","moduleWidth","printCheckDigit","rowColumnRatio","startChar","textLocation","truncate","upsMode","wideNarrowRatio","Bind","picture","BindItems","connection","labelRef","valueRef","Bookend","leader","BooleanElement","Border","break","corner","edges","defaultEdge","edgeStyles","radius","cornerStyles","borderRadius","borderStyle","borderWidth","borderColor","Break","afterTarget","beforeTarget","bookendLeader","bookendTrailer","overflowLeader","overflowTarget","overflowTrailer","BreakAfter","script","BreakBefore","Button","highlight","grandpa","htmlButton","activity","Calculate","Caption","placement","reserve","savedReserve","Certificate","Certificates","credentialServerPolicy","urlPolicy","encryption","issuers","keyUsage","oids","signing","subjectDNs","CheckButton","mark","shape","className","groupId","exportedValue","fieldId","dataId","ExclGroup","xfaOn","xfaOff","required","ChoiceList","commitOn","textEntry","ui","optionStyle","displayedIndex","saveIndex","displayed","selected","hidden","selectAttributes","Color","cSpace","Comb","numberOfCells","Connect","Corner","inverted","DateElement","DateTime","DateTimeEdit","hScrollPolicy","picker","comb","Decimal","fracDigits","leadDigits","DefaultUi","Desc","boolean","dateTime","decimal","float","integer","DigestMethod","DigestMethods","digestMethod","Draw","locale","caption","keep","setProperty","savedW","savedH","cap","linecap","Encoding","Encodings","Encrypt","certificate","EncryptData","manifest","Encryption","EncryptionMethod","EncryptionMethods","encryptionMethod","Event","listen","encryptData","signData","submit","ExData","maxLength","rid","transferEncoding","ExObject","archive","classId","codeBase","codeType","accessKey","calculate","connect","_isSplittable","isSplittable","isLrTb","maxRun","Execute","executeType","runAt","Extras","Field","bindItems","Ui","TextEdit","textEdit","checkButton","choiceList","borderDims","uiW","uiH","tabindex","aElement","button","imageEdit","htmlValue","maxChars","numericEdit","captionHeight","inputHeight","Fill","linear","radial","solid","stipple","ggrandpa","propName","altPropName","backgroundColor","Rectangle","addRevocationInfo","appearanceFilter","certificates","digestMethods","encryptionMethods","lockDocument","mdp","reasons","timeStamp","Float","baselineShift","fontHorizontalScale","fontVerticalScale","kerningMode","lineThrough","lineThroughPeriod","overlinePeriod","underline","underlinePeriod","usedTypefaces","backgroundClip","verticalAlign","fontKerning","textDecoration","textDecorationStyle","fontStyle","Format","Handler","Hyphenation","excludeAllCaps","excludeInitialCap","hyphenate","pushCharacterCount","remainCharacterCount","wordCharacterCount","Image","aspect","objectFit","createObjectURL","ImageEdit","Integer","Issuers","Items","Keep","intact","KeyUsage","crlSign","dataEncipherment","decipherOnly","digitalSignature","encipherOnly","keyAgreement","keyCertSign","keyEncipherment","nonRepudiation","Line","slope","Linear","startColor","endColor","LockDocument","Manifest","Margin","Mdp","signatureType","Medium","imagingBBox","long","orientation","short","stock","trayIn","trayOut","Message","NumericEdit","Occur","initial","originalMin","PageSet","Template","Oid","Oids","oid","Overflow","addLeader","addTrailer","blankOrNotBlank","initialNumber","numbered","oddOrEven","pagePosition","medium","occur","numberOfUse","relation","duplexImposition","pageSet","pageSetIndex","pageNumber","parity","Para","orphans","preserve","radixOffset","tabDefault","tabStops","widows","hyphenation","paddingLeft","paddingight","paddingTop","paddingBottom","tabSize","hyphenatation","PasswordEdit","passwordChar","Picture","Proto","barcode","bookend","breakAfter","breakBefore","dateTimeEdit","defaultUi","passwordEdit","subjectDN","traverse","variables","Radial","Reason","Reasons","cornerStyle","RefElement","binding","SetProperty","SignData","Signature","Signing","Solid","Speak","disable","priority","Stipple","rate","Subform","allowMacro","mergeMode","restoreState","scope","afterBreakAfter","savedNoLayoutFailure","overflowExtra","overflowNode","SubjectDN","delimiter","kv","SubjectDNs","Submit","embedPDF","textEncoding","xdpContent","baseProfile","pageAreas","mainHtml","breakBeforeTarget","pageAreaParent","targetPageArea","hasSomething","hasSomethingCounter","htmlContentAreas","Text","acc","allowRichText","multiLine","vScrollPolicy","Time","TimeStamp","server","ToolTip","Traversal","Traverse","Validate","formatTest","scriptTest","valueName","Variables","TemplateNamespace","attrs","createText","Binder","emptyMerge","_isConsumeData","_mergeMode","_isMatchTemplate","_bindElement","_bindValue","formNode","_findDataByNameToConsume","isValue","global","_setProperties","targetNodes","targetNode","targetParent","Reflect","construct","_bindItems","labels","labelNodes","labelNode","valueNodes","valueNode","label","_bindOccurrences","baseClone","_createOccurrences","currentNumber","nodeClone","_getOccurInfo","_setAndBind","uselessNodes","dataChildren","dataChild","DataHandler","dataset","serialize","storage","storageEntry","CONFIG_NS_ID","Acrobat","acrobat7","autoSave","common","validateApprovalSignatures","submitUrl","Acrobat7","dynamicRender","ADBE_JSConsole","ADBE_JSDebugger","AddSilentPrint","AddViewerPreferences","AdjustData","AdobeExtensionLevel","Agent","AlwaysEmbed","Amd","Attributes","AutoSave","Base","BatchOutput","BehaviorOverride","Cache","templateCache","Change","Common","messaging","suppressBanner","validationMessaging","versionControl","Compress","CompressLogicalStructure","CompressObjectStream","Compression","compressLogicalStructure","compressObjectStream","Config","acrobat","present","trace","agent","Conformance","ContentCopy","Copies","Creator","CurrentPage","Data","adjustData","incrementalLoad","outputXSL","startNode","window","xsl","excludeNS","Debug","DefaultTypeface","writingScript","Destination","DocumentAssembly","Driver","DuplexOption","DynamicRender","Embed","encryptionLevel","EncryptionLevel","Enforce","Equate","force","to","EquateRange","_unicodeRange","unicodeRange","unicodeRegex","Exclude","ExcludeNS","FlipLabel","embed","subsetBelow","alwaysEmbed","defaultTypeface","neverEmbed","FormFieldFilling","GroupParent","IfEmpty","IncludeXDPContent","IncrementalLoad","IncrementalMerge","Interactive","Jog","LabelPrinter","batchOutput","flipLabel","Layout","Level","Linearized","Locale","LocaleSet","Log","threshold","MapElement","equate","equateRange","MediumInfo","msgId","severity","Messaging","Mode","ModifyAnnots","MsgId","NameAttr","NeverEmbed","NumberOfCopies","OpenAction","destination","Output","OutputBin","OutputXSL","Overprint","Packets","PageOffset","PageRange","numbers","Pagination","PaginationOverride","Part","Pcl","jog","mediumInfo","outputBin","pageOffset","staple","Pdf","adobeExtensionLevel","interactive","linearized","pdfa","producer","renderPolicy","scriptModel","silentPrint","submitFormat","tagged","Pdfa","amd","conformance","includeXDPContent","Permissions","accessibleContent","change","contentCopy","documentAssembly","formFieldFilling","modifyAnnots","plaintextMetadata","printHighQuality","PickTrayByPDFSize","PlaintextMetadata","Presence","Present","behaviorOverride","copies","incrementalMerge","overprint","pagination","paginationOverride","driver","labelPrinter","pcl","webClient","zpl","Print","PrintHighQuality","PrintScaling","PrinterName","Producer","Ps","Range","Record","Relevant","Rename","RenderPolicy","RunScripts","currentPage","exclude","runScripts","ScriptModel","Severity","SilentPrint","addSilentPrint","printerName","Staple","StartNode","StartPage","SubmitFormat","SubmitUrl","SubsetBelow","SuppressBanner","Tagged","startPage","Threshold","To","TemplateCache","maxEntries","Trace","Transform","groupParent","ifEmpty","nameAttr","rename","whitespace","Uri","ValidateApprovalSignatures","ValidationMessaging","Version","VersionControl","outputBelow","sourceAbove","sourceBelow","ViewerPreferences","addViewerPreferences","duplexOption","enforce","numberOfCopies","pageRange","pickTrayByPDFSize","printScaling","WebClient","Whitespace","Window","pair","Xdc","Xdp","packets","Xsl","debug","Zpl","ConfigNamespace","compress","CONNECTION_SET_NS_ID","ConnectionSet","wsdlConnection","xmlConnection","xsdConnection","EffectiveInputPolicy","EffectiveOutputPolicy","Operation","RootElement","SoapAction","SoapAddress","WsdlAddress","WsdlConnection","dataDescription","effectiveInputPolicy","effectiveOutputPolicy","soapAction","soapAddress","wsdlAddress","XmlConnection","XsdConnection","rootElement","ConnectionSetNamespace","DATASETS_NS_ID","Datasets","DatasetsNamespace","LOCALE_SET_NS_ID","CalendarSymbols","dayNames","eraNames","meridiemNames","monthNames","CurrencySymbol","CurrencySymbols","currencySymbol","DatePattern","DatePatterns","datePattern","DateTimeSymbols","Day","DayNames","abbr","day","Era","EraNames","era","calendarSymbols","currencySymbols","datePatterns","dateTimeSymbols","numberPatterns","numberSymbols","timePatterns","typeFaces","Meridiem","MeridiemNames","meridiem","Month","MonthNames","month","NumberPattern","NumberPatterns","numberPattern","NumberSymbol","NumberSymbols","numberSymbol","TimePattern","TimePatterns","timePattern","TypeFace","TypeFaces","typeFace","LocaleSetNamespace","SIGNATURE_NS_ID","SignatureNamespace","STYLESHEET_NS_ID","Stylesheet","StylesheetNamespace","XDP_NS_ID","uuid","XdpNamespace","XHTML_NS_ID","$richText","VALID_STYLES","StyleMapping","original","spacesRegExp","crlfRegExp","crlfForRichTextRegExp","mapStyle","styleStr","richText","newValue","SUB_SUPER_SCRIPT_FACTOR","VERTICAL_FACTOR","checkStyle","NoWhites","XhtmlObject","mustPop","pushFont","Body","Br","Html","Li","Ol","siblings","Span","Sub","Sup","Ul","XhtmlNamespace","body","br","li","ol","span","sub","sup","ul","NamespaceSetUp","UnknownNamespace","namespaceId","Root","Empty","Builder","rootNameSpace","_namespaceStack","_nsAgnosticLevel","_namespacePrefixes","_namespaces","_nextNsId","_currentNamespace","buildRoot","build","nsPrefix","namespace","prefixes","hasNamespaceDef","_searchNamespace","_addNamespacePrefix","dataTemplate","nsAttrs","xfaAttrs","nsToUse","_getNamespaceToUse","namespaceToUse","hasNamespace","nsAgnostic","isNsAgnostic","nsName","prefixStack","XFAParser","_builder","_globalData","_ids","_current","_whiteRegex","_nbsps","_richText","_mkAttributes","attributeObj","_getNameAndPrefix","attributesObj","XFAFactory","_createDocument","binder","dataHandler","_createPagesHelper","nextIteration","_createPages","dims","getBoundingBox","getNumPages","setImages","setFonts","missingFonts","appendFonts","getPages","serializeData","getRichTextAsHtml","rc","newRoot","attr","AnnotationFactory","createGlobals","ensureCatalog","ensureDoc","xfaDatasets","annotationGlobals","collectFields","_getPageIndex","ensure","_create","parameters","LinkAnnotation","TextAnnotation","fieldType","TextWidgetAnnotation","ButtonWidgetAnnotation","ChoiceWidgetAnnotation","SignatureWidgetAnnotation","WidgetAnnotation","PopupAnnotation","FreeTextAnnotation","LineAnnotation","SquareAnnotation","CircleAnnotation","PolylineAnnotation","PolygonAnnotation","CaretAnnotation","InkAnnotation","HighlightAnnotation","UnderlineAnnotation","SquigglyAnnotation","StrikeOutAnnotation","StampAnnotation","FileAttachmentAnnotation","Annotation","annotDict","annotRef","generateImages","imagePromises","bitmapId","saveNewAnnotations","promises","annotation","deleted","annotationType","createNewAnnotation","quadPoints","smaskStream","smaskRef","printNewAnnotations","createNewPrintAnnotation","getRgbColor","defaultColor","getPdfColorArray","getQuadPoints","quadPointsLists","getTransformMatrix","setTitle","setContents","setModificationDate","setFlags","setRectangle","setColor","setBorderStyle","setAppearance","setOptionalContent","MK","setBorderAndBackgroundColors","setRotation","_streams","isLocked","isContentLocked","structParent","annotationFlags","contentsObj","_contents","hasAppearance","modificationDate","hasOwnCanvas","noRotate","noHTML","kidIds","fieldName","_constructFieldName","_isOffscreenCanvasSupported","_fallbackFontDict","_needAppearances","_hasFlag","_isViewable","_isPrintable","mustBeViewed","_renderForms","noView","viewable","mustBePrinted","noPrint","printable","_parseStringHelper","setDefaultAppearance","defaultAppearance","_defaultAppearance","defaultAppearanceData","_title","contents","hasFlag","setLineEndings","lineEndings","mk","AnnotationBorderStyle","dictType","setWidth","setStyle","setDashArray","setHorizontalCornerRadius","setVerticalCornerRadius","appearanceStates","normalAppearanceState","oc","loadResources","objectLoader","renderForms","isUsingOwnCanvas","separateForm","separateCanvas","appearanceDict","hasTextContent","extractTextContent","firstPosition","trimEnd","textPosition","_transformPoint","getFieldObject","strokeColor","fillColor","loopDict","dashArray","horizontalCornerRadius","verticalCornerRadius","forceStyle","allZeros","validNumber","MarkupAnnotation","rawIRT","inReplyTo","rt","replyType","popupRef","titleObj","creationDate","setCreationDate","_setDefaultAppearance","blendMode","fillAlpha","pointsCallback","MAX_VALUE","MIN_VALUE","pointsArray","mX","MX","mY","MY","formDict","appearanceStream","gsDict","stateDict","annotationRef","createNewAppearanceStream","annotationDict","apRef","createNewDict","newAnnotation","refToReplace","fieldValue","_decodeFormValue","defaultFieldValue","_hasValueFromXFA","getValue","alternativeText","localResources","acroFormResources","appearanceResources","_fieldResources","mergedResources","fieldFlags","readOnly","hasFieldFlag","formValue","getBorderAndBackgroundAppearances","_hasText","_getAppearance","_getMKDict","amendSavedDict","originalDict","encoder","maybeMK","changes","AP","_getSaveFieldResources","rotationMatrix","isPassword","formattedValue","combo","exportValue","displayValue","lineCount","defaultPadding","defaultHPadding","_getFontData","encodedLines","encodingError","encodedString","fakeUnicodeFont","newFont","oldFont","savedDefaultAppearance","_computeFontSize","defaultVPadding","alignment","textAlignment","_getMultilineAppearance","_getCombAppearance","bottomPadding","prevInfo","renderedText","_renderText","appearanceData","_getTextWidth","numberOfLines","roundWithTwoDigits","textWidth","cachedLines","isTooBig","fsize","_splitLine","hPadding","vPadding","shiftStr","localFont","acroFormFont","subFontDict","subResourcesDict","maximumLength","doNotScroll","combWidth","renderedComb","lastSpacePosInStringStart","lastSpacePosInStringEnd","startChunk","multiline","charLimit","editable","checkedAppearance","uncheckedAppearance","checkBox","radioButton","pushButton","isTooltipOnly","_processCheckBox","_processRadioButton","_processPushButton","buttonValue","savedAppearance","savedMatrix","_saveCheckbox","_saveRadioButton","parentData","_getDefaultCheckedAppearance","FONT_RATIO","xShift","yShift","customAppearance","normalAppearance","asValue","yes","exportValues","otherYes","fieldParent","fieldParentValue","indices","hasIndices","isOptionArray","multiSelect","numItems","multipleSelection","valueIndices","numberOfVisibleLines","firstIndex","minIndex","maxIndex","vpadding","DEFAULT_ICON_SIZE","stateModel","parentItem","parentRect","parentFlags","_hasAppearance","user","freetext","da","helv","lineAscent","firstPoint","clipBox","lineCoordinates","interiorColor","borderAdjust","controlPointsDistance","xMid","yMid","xOffset","yOffset","vertices","rawVertices","vertex","inkLists","rawInkLists","inkList","outlines","ink","createNewAppearanceStreamForHighlight","appearanceBuffer","bezier","curve","outline","xEnd","buf32","hasAlpha","fillStyle","jpegBufferPromise","convertToBlob","quality","xobjectName","imageName","alphaBuffer","stamp","xobject","decodeString","DatasetXMLParser","DatasetReader","XRef","firstXRefStmPos","_xrefStms","_cacheMap","_pendingRefs","_newPersistentRefNum","_newTemporaryRefNum","_persistentRefsCache","resetNewTemporaryRef","setStartXRef","startXRefQueue","trailerDict","readXRef","indexObjects","processXRefTable","tableState","entryNum","streamPos","parserBuf1","parserBuf2","readXRefTable","firstEntryNum","free","uncompressed","processXRefStream","streamParameters","byteWidths","streamState","entryRanges","readXRefStream","typeFieldWidth","offsetFieldWidth","generationFieldWidth","generation","typeByte","offsetByte","generationByte","TAB","PERCENT","LT","readToken","skipUntil","skipped","gEndobjRegExp","gStartxrefRegExp","objRegExp","trailerBytes","startxrefBytes","xrefBytes","bufferStr","trailers","xrefStms","contentLength","updateEntries","xrefTagOffset","xrefStm","trailerDicts","isEncrypted","trailerError","_generationFallback","validPagesDict","pagesDict","pagesCount","startXRefParsedCache","lastXRefStreamPos","getEntry","xrefEntry","fetchUncompressed","fetchCompressed","tableOffset","DEFAULT_USER_UNIT","LETTER_SIZE_MEDIABOX","Page","globalIdFactory","xfaFactory","resourcesPromise","idCounters","_localIdFactory","_getInheritableProperty","_getBoundingBox","box","mediaBox","cropBox","userUnit","_onSubStreamError","getContentStream","replaceIdByRef","#replaceIdByRef","deletedAnnotations","existingAnnotations","partialEvaluator","annotationsArray","savedDict","deletedRef","_parsedAnnotations","newRefsPromises","contentStreamPromise","newAnnotsByPage","newAnnots","newAnnotationsPromise","annotationGlobalsPromise","missingBitmaps","annotationWithBitmaps","pageListPromise","contentStream","transparency","pageOpList","newAnnotations","findIndex","intentAny","intentDisplay","intentPrint","opListPromises","opLists","langPromise","getStructTree","_parseStructTree","getAnnotationsData","annotationsData","textContentPromises","isVisible","annots","annotationPromises","sortedAnnotations","popupAnnotations","widgetAnnotations","PDF_HEADER_SIGNATURE","STARTXREF_SIGNATURE","ENDOBJ_SIGNATURE","FINGERPRINT_FIRST_BYTES","EMPTY_FINGERPRINT","backwards","signatureEnd","PDFDocument","_pagePromises","_version","_globalIdFactory","docId","linearization","startXRefLength","checkHeader","parseStartXRef","_hasOnlyDocumentSignatures","recursionDepth","RECURSION_LIMIT","isSignature","isInvisible","_xfaStreams","enableXfa","formInfo","hasAcroForm","isPureXfa","htmlForXfa","loadXfaImages","xfaImagesDict","loadXfaFonts","serializeXfaData","hasFields","hasSignatures","sigFlags","hasOnlyDocumentSignatures","documentInfo","docInfo","PDFFormatVersion","Language","EncryptFilterName","IsLinearized","IsAcroFormPresent","IsXFAPresent","IsCollectionPresent","IsSignaturesPresent","infoDict","customValue","Custom","fingerprints","hexString","hex","idArray","hashOriginal","hashModified","_getLinearizationPage","cachedPromise","checkFirstPage","checkLastPage","pagesTree","reasonAll","collectFieldObjects","#collectFieldObjects","fieldRef","visitedRefs","partName","fieldObjects","allFields","fieldPromises","allPromises","hasJSActions","_parseHasJSActions","catalogJsActions","fieldObject","calculationOrderIds","calculationOrder","parseDocBaseUrl","BasePdfManager","_docBaseUrl","_docId","_password","pdfDocument","ensureXRef","requestLoadedStream","sendProgressiveData","updatePassword","terminate","LocalPdfManager","_loadedStreamPromise","NetworkPdfManager","streamManager","CallbackKind","UNKNOWN","DATA","StreamKind","CANCEL","CANCEL_COMPLETE","CLOSE","ENQUEUE","PULL","PULL_COMPLETE","START_COMPLETE","wrapReason","MessageHandler","sourceName","targetName","comObj","callbackId","streamId","streamSinks","streamControllers","callbackCapabilities","actionHandler","_onComObjOnMessage","processStreamMessage","cbSourceName","cbTargetName","postMessage","createStreamSink","addEventListener","ah","sendWithStream","queueingStrategy","ReadableStream","controller","startCapability","startCall","pullCall","cancelCall","pull","pullCapability","cancel","cancelCapability","#createStreamSink","isCancelled","lastDesiredSize","sinkCapability","onPull","onCancel","#processStreamMessage","streamController","deleteStreamController","#deleteStreamController","allSettled","destroy","removeEventListener","PDFWorkerStream","_msgHandler","_contentLength","_fullRequestReader","_rangeRequestReaders","getFullReader","PDFWorkerStreamReader","PDFWorkerStreamRangeReader","_isRangeSupported","_isStreamingSupported","readableStream","_reader","getReader","_headersReady","isRangeSupported","headersReady","WorkerTask","terminated","_capability","finished","finish","WorkerMessageHandler","setup","port","testMessageProcessed","createDocumentHandler","docParams","cancelXHRs","WorkerTasks","apiVersion","workerVersion","enumerableProperties","workerHandlerName","startWorkerTask","finishWorkerTask","loadDocument","getPdfManager","pdfManagerArgs","pdfManagerCapability","newPdfManager","pdfStream","cachedChunks","fullRequest","flushChunks","pdfFile","setupDoc","onSuccess","doc","pdfInfo","onFailure","pdfManagerReady","refStr","globalPromises","_structTreeRoot","newAnnotationPromises","finally","newXrefInfo","infoObj","operatorListInfo","waitOn","cleanupPromise","initializeFromPort","isMessagePort","maybePort","pdfjsVersion","pdfjsBuild"],"sourceRoot":""}
\ No newline at end of file
diff --git a/web/cmaps/78-EUC-H.bcmap b/web/cmaps/78-EUC-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..2655fc70ae706c7ba52a5d647cbfdfad6072c697
Binary files /dev/null and b/web/cmaps/78-EUC-H.bcmap differ
diff --git a/web/cmaps/78-EUC-V.bcmap b/web/cmaps/78-EUC-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..f1ed8538287499647d923d7d8f517a00cdac4e3a
Binary files /dev/null and b/web/cmaps/78-EUC-V.bcmap differ
diff --git a/web/cmaps/78-H.bcmap b/web/cmaps/78-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..39e89d3339c74cbe06e7e4f76d60bf3556b0d4b6
Binary files /dev/null and b/web/cmaps/78-H.bcmap differ
diff --git a/web/cmaps/78-RKSJ-H.bcmap b/web/cmaps/78-RKSJ-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..e4167cb51f66c60ef7d9500b450303b5da175574
Binary files /dev/null and b/web/cmaps/78-RKSJ-H.bcmap differ
diff --git a/web/cmaps/78-RKSJ-V.bcmap b/web/cmaps/78-RKSJ-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..50b1646e94bba61b3242a680fe3023337e191123
Binary files /dev/null and b/web/cmaps/78-RKSJ-V.bcmap differ
diff --git a/web/cmaps/78-V.bcmap b/web/cmaps/78-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..d7af99b5e2ae9a21d534f1965c35a2b572143322
Binary files /dev/null and b/web/cmaps/78-V.bcmap differ
diff --git a/web/cmaps/78ms-RKSJ-H.bcmap b/web/cmaps/78ms-RKSJ-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..37077d01e26f9ee2427592f6deebb145d628e731
Binary files /dev/null and b/web/cmaps/78ms-RKSJ-H.bcmap differ
diff --git a/web/cmaps/78ms-RKSJ-V.bcmap b/web/cmaps/78ms-RKSJ-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..acf23231aea22e1a95761f7eafd35f1d42ea6b84
Binary files /dev/null and b/web/cmaps/78ms-RKSJ-V.bcmap differ
diff --git a/web/cmaps/83pv-RKSJ-H.bcmap b/web/cmaps/83pv-RKSJ-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..2359bc529d160857cce4c1d1bfca1322290205c1
Binary files /dev/null and b/web/cmaps/83pv-RKSJ-H.bcmap differ
diff --git a/web/cmaps/90ms-RKSJ-H.bcmap b/web/cmaps/90ms-RKSJ-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..af8293829c90ce63cc4c5eda0318003785ffcba1
Binary files /dev/null and b/web/cmaps/90ms-RKSJ-H.bcmap differ
diff --git a/web/cmaps/90ms-RKSJ-V.bcmap b/web/cmaps/90ms-RKSJ-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..780549de19de05b6cbea4ccd4737351bc9ff6104
Binary files /dev/null and b/web/cmaps/90ms-RKSJ-V.bcmap differ
diff --git a/web/cmaps/90msp-RKSJ-H.bcmap b/web/cmaps/90msp-RKSJ-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..bfd3119c62d9976dde9b1e59c572c678cf5811a0
Binary files /dev/null and b/web/cmaps/90msp-RKSJ-H.bcmap differ
diff --git a/web/cmaps/90msp-RKSJ-V.bcmap b/web/cmaps/90msp-RKSJ-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..25ef14ab4af42f4b70ccac76cddac8f3b22d8813
Binary files /dev/null and b/web/cmaps/90msp-RKSJ-V.bcmap differ
diff --git a/web/cmaps/90pv-RKSJ-H.bcmap b/web/cmaps/90pv-RKSJ-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..02f713bb838a8cd46f5b262c934d0edc8c6e8fe9
Binary files /dev/null and b/web/cmaps/90pv-RKSJ-H.bcmap differ
diff --git a/web/cmaps/90pv-RKSJ-V.bcmap b/web/cmaps/90pv-RKSJ-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..d08e0cc5d98b2d933ab848c69dd6c504fbf1787d
Binary files /dev/null and b/web/cmaps/90pv-RKSJ-V.bcmap differ
diff --git a/web/cmaps/Add-H.bcmap b/web/cmaps/Add-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..59442acafb613b0c090314379f9f4c2fa134a1a1
Binary files /dev/null and b/web/cmaps/Add-H.bcmap differ
diff --git a/web/cmaps/Add-RKSJ-H.bcmap b/web/cmaps/Add-RKSJ-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..a3065e441a0e1f1a65e9109ec9bc4f826fccac24
Binary files /dev/null and b/web/cmaps/Add-RKSJ-H.bcmap differ
diff --git a/web/cmaps/Add-RKSJ-V.bcmap b/web/cmaps/Add-RKSJ-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..040014cfc0880371c20a89212942727c5dc30a78
Binary files /dev/null and b/web/cmaps/Add-RKSJ-V.bcmap differ
diff --git a/web/cmaps/Add-V.bcmap b/web/cmaps/Add-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..2f816d320f08b8671498299c4d00e4564d2ece6c
Binary files /dev/null and b/web/cmaps/Add-V.bcmap differ
diff --git a/web/cmaps/Adobe-CNS1-0.bcmap b/web/cmaps/Adobe-CNS1-0.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..88ec04af49d1e8ab6038e3b36dd28daf4dcf0119
Binary files /dev/null and b/web/cmaps/Adobe-CNS1-0.bcmap differ
diff --git a/web/cmaps/Adobe-CNS1-1.bcmap b/web/cmaps/Adobe-CNS1-1.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..03a501477c91d8156723f0c274a37d397ed65bad
Binary files /dev/null and b/web/cmaps/Adobe-CNS1-1.bcmap differ
diff --git a/web/cmaps/Adobe-CNS1-2.bcmap b/web/cmaps/Adobe-CNS1-2.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..2aa95141f9f5802818e34b0aa626e34e7cfee805
Binary files /dev/null and b/web/cmaps/Adobe-CNS1-2.bcmap differ
diff --git a/web/cmaps/Adobe-CNS1-3.bcmap b/web/cmaps/Adobe-CNS1-3.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..86d8b8c79cfa3907281aa3f25a46f178b87aedfa
Binary files /dev/null and b/web/cmaps/Adobe-CNS1-3.bcmap differ
diff --git a/web/cmaps/Adobe-CNS1-4.bcmap b/web/cmaps/Adobe-CNS1-4.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..f50fc6c14e67a228c4ba9a61b1357c16410e8228
Binary files /dev/null and b/web/cmaps/Adobe-CNS1-4.bcmap differ
diff --git a/web/cmaps/Adobe-CNS1-5.bcmap b/web/cmaps/Adobe-CNS1-5.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..6caf4a83146a60a2db652647b9cfed5fb71bd97c
Binary files /dev/null and b/web/cmaps/Adobe-CNS1-5.bcmap differ
diff --git a/web/cmaps/Adobe-CNS1-6.bcmap b/web/cmaps/Adobe-CNS1-6.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..b77fb0705c28d647d0bf7b25246af566a791b2ec
Binary files /dev/null and b/web/cmaps/Adobe-CNS1-6.bcmap differ
diff --git a/web/cmaps/Adobe-CNS1-UCS2.bcmap b/web/cmaps/Adobe-CNS1-UCS2.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..69d79a2c2c2b00207ab27b68ebf4404aa17c6f2c
Binary files /dev/null and b/web/cmaps/Adobe-CNS1-UCS2.bcmap differ
diff --git a/web/cmaps/Adobe-GB1-0.bcmap b/web/cmaps/Adobe-GB1-0.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..36101083fa8fd22399728ccf533c48e830781de0
Binary files /dev/null and b/web/cmaps/Adobe-GB1-0.bcmap differ
diff --git a/web/cmaps/Adobe-GB1-1.bcmap b/web/cmaps/Adobe-GB1-1.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..707bb1065c76d69551c287141cb258519132ef8e
Binary files /dev/null and b/web/cmaps/Adobe-GB1-1.bcmap differ
diff --git a/web/cmaps/Adobe-GB1-2.bcmap b/web/cmaps/Adobe-GB1-2.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..f7648cc3ff02c44e9594ccbd71deec742e253c2f
Binary files /dev/null and b/web/cmaps/Adobe-GB1-2.bcmap differ
diff --git a/web/cmaps/Adobe-GB1-3.bcmap b/web/cmaps/Adobe-GB1-3.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..852145890ede3dc04339de284993da270d62311d
Binary files /dev/null and b/web/cmaps/Adobe-GB1-3.bcmap differ
diff --git a/web/cmaps/Adobe-GB1-4.bcmap b/web/cmaps/Adobe-GB1-4.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..e40c63ab1e5c240841f3b465ade920d60be6328f
Binary files /dev/null and b/web/cmaps/Adobe-GB1-4.bcmap differ
diff --git a/web/cmaps/Adobe-GB1-5.bcmap b/web/cmaps/Adobe-GB1-5.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..d7623b500232b6e62f29c2c232e6c2469a527314
Binary files /dev/null and b/web/cmaps/Adobe-GB1-5.bcmap differ
diff --git a/web/cmaps/Adobe-GB1-UCS2.bcmap b/web/cmaps/Adobe-GB1-UCS2.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..7586525936cc5398b86d3752a4eb45b15825b25c
Binary files /dev/null and b/web/cmaps/Adobe-GB1-UCS2.bcmap differ
diff --git a/web/cmaps/Adobe-Japan1-0.bcmap b/web/cmaps/Adobe-Japan1-0.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..f0e94ec196757e8660b567d8ece7f99869927913
Binary files /dev/null and b/web/cmaps/Adobe-Japan1-0.bcmap differ
diff --git a/web/cmaps/Adobe-Japan1-1.bcmap b/web/cmaps/Adobe-Japan1-1.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..dad42c5ad7dad57954fc9a051ee7e222e83bee45
Binary files /dev/null and b/web/cmaps/Adobe-Japan1-1.bcmap differ
diff --git a/web/cmaps/Adobe-Japan1-2.bcmap b/web/cmaps/Adobe-Japan1-2.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..090819a064533f20aa68f562275556397683ae81
Binary files /dev/null and b/web/cmaps/Adobe-Japan1-2.bcmap differ
diff --git a/web/cmaps/Adobe-Japan1-3.bcmap b/web/cmaps/Adobe-Japan1-3.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..087dfc155860e65d2dc828dd432ffe88239fde23
Binary files /dev/null and b/web/cmaps/Adobe-Japan1-3.bcmap differ
diff --git a/web/cmaps/Adobe-Japan1-4.bcmap b/web/cmaps/Adobe-Japan1-4.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..46aa9bffe576e9a8b714646aed7f9e1a4e99dfe2
Binary files /dev/null and b/web/cmaps/Adobe-Japan1-4.bcmap differ
diff --git a/web/cmaps/Adobe-Japan1-5.bcmap b/web/cmaps/Adobe-Japan1-5.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..5b4b65cc6292a5ba7d89e976565bf08814bb88b6
Binary files /dev/null and b/web/cmaps/Adobe-Japan1-5.bcmap differ
diff --git a/web/cmaps/Adobe-Japan1-6.bcmap b/web/cmaps/Adobe-Japan1-6.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..e77d699ab6a1bfd1e3eab6db428222e108342aec
Binary files /dev/null and b/web/cmaps/Adobe-Japan1-6.bcmap differ
diff --git a/web/cmaps/Adobe-Japan1-UCS2.bcmap b/web/cmaps/Adobe-Japan1-UCS2.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..128a14107750da2939d920c3e0802798c36f2e18
Binary files /dev/null and b/web/cmaps/Adobe-Japan1-UCS2.bcmap differ
diff --git a/web/cmaps/Adobe-Korea1-0.bcmap b/web/cmaps/Adobe-Korea1-0.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..cef1a9985191f53c4a8a35811d1caeecdd1a1820
Binary files /dev/null and b/web/cmaps/Adobe-Korea1-0.bcmap differ
diff --git a/web/cmaps/Adobe-Korea1-1.bcmap b/web/cmaps/Adobe-Korea1-1.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..11ffa36df8404ab970df2a24b2d80b1dc6348436
Binary files /dev/null and b/web/cmaps/Adobe-Korea1-1.bcmap differ
diff --git a/web/cmaps/Adobe-Korea1-2.bcmap b/web/cmaps/Adobe-Korea1-2.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..3172308c79d57147cdbe05930228043faa48ca54
Binary files /dev/null and b/web/cmaps/Adobe-Korea1-2.bcmap differ
diff --git a/web/cmaps/Adobe-Korea1-UCS2.bcmap b/web/cmaps/Adobe-Korea1-UCS2.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..f3371c0cbd14c89a7d2b5a72e4e4f9cdc6d6017e
Binary files /dev/null and b/web/cmaps/Adobe-Korea1-UCS2.bcmap differ
diff --git a/web/cmaps/B5-H.bcmap b/web/cmaps/B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..beb4d228104cd985130e6eed7ccabdb63e94ce27
Binary files /dev/null and b/web/cmaps/B5-H.bcmap differ
diff --git a/web/cmaps/B5-V.bcmap b/web/cmaps/B5-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..2d4f87d5035f03485616d4986b6373fa0820d0af
Binary files /dev/null and b/web/cmaps/B5-V.bcmap differ
diff --git a/web/cmaps/B5pc-H.bcmap b/web/cmaps/B5pc-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..ce0013167f852a873b639e301088c094d468a750
Binary files /dev/null and b/web/cmaps/B5pc-H.bcmap differ
diff --git a/web/cmaps/B5pc-V.bcmap b/web/cmaps/B5pc-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..73b99ff2fbca40e7ca5501f61e3f2f29c1fc1af6
Binary files /dev/null and b/web/cmaps/B5pc-V.bcmap differ
diff --git a/web/cmaps/CNS-EUC-H.bcmap b/web/cmaps/CNS-EUC-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..61d1d0cb001dd484630e52eb7e47eaabbdee62cd
Binary files /dev/null and b/web/cmaps/CNS-EUC-H.bcmap differ
diff --git a/web/cmaps/CNS-EUC-V.bcmap b/web/cmaps/CNS-EUC-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..1a393a51e079d1b5e7898423463fac5e87170da2
Binary files /dev/null and b/web/cmaps/CNS-EUC-V.bcmap differ
diff --git a/web/cmaps/CNS1-H.bcmap b/web/cmaps/CNS1-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..f738e218ae2b4e9c33a1833a49fe20027e869aa1
Binary files /dev/null and b/web/cmaps/CNS1-H.bcmap differ
diff --git a/web/cmaps/CNS1-V.bcmap b/web/cmaps/CNS1-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..9c3169f0d9a083b462427bf4e46a25296ebbd862
Binary files /dev/null and b/web/cmaps/CNS1-V.bcmap differ
diff --git a/web/cmaps/CNS2-H.bcmap b/web/cmaps/CNS2-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..c89b3527fe57ad3b32061f36b73c756ead3fb071
Binary files /dev/null and b/web/cmaps/CNS2-H.bcmap differ
diff --git a/web/cmaps/CNS2-V.bcmap b/web/cmaps/CNS2-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..7588cec83e271feb04e3c7f3a6ee3a4c153f2052
--- /dev/null
+++ b/web/cmaps/CNS2-V.bcmap
@@ -0,0 +1,3 @@
+RCopyright 1990-2009 Adobe Systems Incorporated.
+All rights reserved.
+See ./LICENSECNS2-H
\ No newline at end of file
diff --git a/web/cmaps/ETHK-B5-H.bcmap b/web/cmaps/ETHK-B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..cb29415de4f5a669c1b47e34ab889b5fdee6e428
Binary files /dev/null and b/web/cmaps/ETHK-B5-H.bcmap differ
diff --git a/web/cmaps/ETHK-B5-V.bcmap b/web/cmaps/ETHK-B5-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..f09aec6318dbec88491e3e488526882eaa930f37
Binary files /dev/null and b/web/cmaps/ETHK-B5-V.bcmap differ
diff --git a/web/cmaps/ETen-B5-H.bcmap b/web/cmaps/ETen-B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..c2d77462d298cdb261f5e2eed5218fcba35cbe4e
Binary files /dev/null and b/web/cmaps/ETen-B5-H.bcmap differ
diff --git a/web/cmaps/ETen-B5-V.bcmap b/web/cmaps/ETen-B5-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..89bff159ec62cc86b7c4a3258ee0455cab10b76a
Binary files /dev/null and b/web/cmaps/ETen-B5-V.bcmap differ
diff --git a/web/cmaps/ETenms-B5-H.bcmap b/web/cmaps/ETenms-B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..a7d69db5e32646c3fecf51bd03dc1a61c8189fae
--- /dev/null
+++ b/web/cmaps/ETenms-B5-H.bcmap
@@ -0,0 +1,3 @@
+RCopyright 1990-2009 Adobe Systems Incorporated.
+All rights reserved.
+See ./LICENSE ETen-B5-H` ^
\ No newline at end of file
diff --git a/web/cmaps/ETenms-B5-V.bcmap b/web/cmaps/ETenms-B5-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..adc5d618d6912cb3a67e4745b63764120f93d17f
Binary files /dev/null and b/web/cmaps/ETenms-B5-V.bcmap differ
diff --git a/web/cmaps/EUC-H.bcmap b/web/cmaps/EUC-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..e92ea5b3b99b1f20d31c9760481de0472e72685b
Binary files /dev/null and b/web/cmaps/EUC-H.bcmap differ
diff --git a/web/cmaps/EUC-V.bcmap b/web/cmaps/EUC-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..7a7c183228dfdc5c236b7914ca68298520ac60a1
Binary files /dev/null and b/web/cmaps/EUC-V.bcmap differ
diff --git a/web/cmaps/Ext-H.bcmap b/web/cmaps/Ext-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..3b5cde44dbaf7f78c716c3705590ae28e9bed265
Binary files /dev/null and b/web/cmaps/Ext-H.bcmap differ
diff --git a/web/cmaps/Ext-RKSJ-H.bcmap b/web/cmaps/Ext-RKSJ-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..ea4d2d97b8bc1df2abebce0d4f8c58789f723eb1
Binary files /dev/null and b/web/cmaps/Ext-RKSJ-H.bcmap differ
diff --git a/web/cmaps/Ext-RKSJ-V.bcmap b/web/cmaps/Ext-RKSJ-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..3457c2770913963731993654c838df133deabbb3
Binary files /dev/null and b/web/cmaps/Ext-RKSJ-V.bcmap differ
diff --git a/web/cmaps/Ext-V.bcmap b/web/cmaps/Ext-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..4999ca40412b66e87c56870c80cff32212483ee8
Binary files /dev/null and b/web/cmaps/Ext-V.bcmap differ
diff --git a/web/cmaps/GB-EUC-H.bcmap b/web/cmaps/GB-EUC-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..e39908b9844939a3c6d6baccced5771b8c1b1b2d
Binary files /dev/null and b/web/cmaps/GB-EUC-H.bcmap differ
diff --git a/web/cmaps/GB-EUC-V.bcmap b/web/cmaps/GB-EUC-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..d5be5446aa40898742183202ce0624b8acee5234
Binary files /dev/null and b/web/cmaps/GB-EUC-V.bcmap differ
diff --git a/web/cmaps/GB-H.bcmap b/web/cmaps/GB-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..39189c54e363ebe2ea542addd6b629bdacd0e8e9
--- /dev/null
+++ b/web/cmaps/GB-H.bcmap
@@ -0,0 +1,4 @@
+RCopyright 1990-2009 Adobe Systems Incorporated.
+All rights reserved.
+See ./LICENSE!!]aX!!]`21> pz$]"Rd-U7*
4%+ Z {/%<9Kb1]."`],"]
+"]h"]F"]$"]"]`"]>"]"]z"]X"]6"]"]r"]P"]."]"]j"]H"]&"]"]b"]@"]"]|"]Z"]8"]"]t"]R"]0"]"]l"]J"]("]"]d"]B"] "X~']W"]5"]"]q"]O"]-"]"]i"]G"]%"]"]a"]?"]"]{"]Y"]7"]"]s"]Q"]/"]
"]k"]I"]'"]"]c"]A"]"]}"]["]9
\ No newline at end of file
diff --git a/web/cmaps/GB-V.bcmap b/web/cmaps/GB-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..310834512ffe49cbb7ca903abc2dc1aaa934e6f4
Binary files /dev/null and b/web/cmaps/GB-V.bcmap differ
diff --git a/web/cmaps/GBK-EUC-H.bcmap b/web/cmaps/GBK-EUC-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..05fff7e8254c995031783fb3b4892d58a6b176ac
Binary files /dev/null and b/web/cmaps/GBK-EUC-H.bcmap differ
diff --git a/web/cmaps/GBK-EUC-V.bcmap b/web/cmaps/GBK-EUC-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..0cdf6bed6d450473f92e691e9015e4028113607e
Binary files /dev/null and b/web/cmaps/GBK-EUC-V.bcmap differ
diff --git a/web/cmaps/GBK2K-H.bcmap b/web/cmaps/GBK2K-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..46f6ba5967cdfb381f001eb1193f17b43d943962
Binary files /dev/null and b/web/cmaps/GBK2K-H.bcmap differ
diff --git a/web/cmaps/GBK2K-V.bcmap b/web/cmaps/GBK2K-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..d9a9479843eda25fb0a8fd69fb302740813d925f
Binary files /dev/null and b/web/cmaps/GBK2K-V.bcmap differ
diff --git a/web/cmaps/GBKp-EUC-H.bcmap b/web/cmaps/GBKp-EUC-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..5cb0af687ee20a10ecc367892ae49d7b1e74acd1
Binary files /dev/null and b/web/cmaps/GBKp-EUC-H.bcmap differ
diff --git a/web/cmaps/GBKp-EUC-V.bcmap b/web/cmaps/GBKp-EUC-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..bca93b8efbb18a13e15025ad41d23db8267d2577
Binary files /dev/null and b/web/cmaps/GBKp-EUC-V.bcmap differ
diff --git a/web/cmaps/GBT-EUC-H.bcmap b/web/cmaps/GBT-EUC-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..4b4e2d32294538b5093ed3870bb9de37abf21599
Binary files /dev/null and b/web/cmaps/GBT-EUC-H.bcmap differ
diff --git a/web/cmaps/GBT-EUC-V.bcmap b/web/cmaps/GBT-EUC-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..38f706699f395dcdad5c6ad93d1a9b6fe9f66c78
Binary files /dev/null and b/web/cmaps/GBT-EUC-V.bcmap differ
diff --git a/web/cmaps/GBT-H.bcmap b/web/cmaps/GBT-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..8437ac33771536813228e3f9c1cb6c35af3acc72
Binary files /dev/null and b/web/cmaps/GBT-H.bcmap differ
diff --git a/web/cmaps/GBT-V.bcmap b/web/cmaps/GBT-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..697ab4a8e756204de9d089c8c136579973c9c0c6
Binary files /dev/null and b/web/cmaps/GBT-V.bcmap differ
diff --git a/web/cmaps/GBTpc-EUC-H.bcmap b/web/cmaps/GBTpc-EUC-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..f6e50e89363483070b25106b94f729ae38587d28
Binary files /dev/null and b/web/cmaps/GBTpc-EUC-H.bcmap differ
diff --git a/web/cmaps/GBTpc-EUC-V.bcmap b/web/cmaps/GBTpc-EUC-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..6c0d71a2d04473870c60dc59342400fe1b4ccc48
Binary files /dev/null and b/web/cmaps/GBTpc-EUC-V.bcmap differ
diff --git a/web/cmaps/GBpc-EUC-H.bcmap b/web/cmaps/GBpc-EUC-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..c9edf67cf6d640607080ad2775c14760df77dd96
Binary files /dev/null and b/web/cmaps/GBpc-EUC-H.bcmap differ
diff --git a/web/cmaps/GBpc-EUC-V.bcmap b/web/cmaps/GBpc-EUC-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..31450c97f640ff40bd79677cd3e97f4485453d9b
Binary files /dev/null and b/web/cmaps/GBpc-EUC-V.bcmap differ
diff --git a/web/cmaps/H.bcmap b/web/cmaps/H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..7b24ea4629d0d4cc9f0cd5852edde324156ef0b1
Binary files /dev/null and b/web/cmaps/H.bcmap differ
diff --git a/web/cmaps/HKdla-B5-H.bcmap b/web/cmaps/HKdla-B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..7d30c0500520d563d0e5891c8f4781c61ddcca5e
Binary files /dev/null and b/web/cmaps/HKdla-B5-H.bcmap differ
diff --git a/web/cmaps/HKdla-B5-V.bcmap b/web/cmaps/HKdla-B5-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..78946940d68cdd2ea1bec1230b1eb0b19b8d5deb
Binary files /dev/null and b/web/cmaps/HKdla-B5-V.bcmap differ
diff --git a/web/cmaps/HKdlb-B5-H.bcmap b/web/cmaps/HKdlb-B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..d829a231015161e107123e211d4a78110daab6bf
Binary files /dev/null and b/web/cmaps/HKdlb-B5-H.bcmap differ
diff --git a/web/cmaps/HKdlb-B5-V.bcmap b/web/cmaps/HKdlb-B5-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..2b572b50a47f09a51c777efabcaf6a9e3faa26c2
Binary files /dev/null and b/web/cmaps/HKdlb-B5-V.bcmap differ
diff --git a/web/cmaps/HKgccs-B5-H.bcmap b/web/cmaps/HKgccs-B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..971a4f23f791f75d4e604ad717735ee55529eda5
Binary files /dev/null and b/web/cmaps/HKgccs-B5-H.bcmap differ
diff --git a/web/cmaps/HKgccs-B5-V.bcmap b/web/cmaps/HKgccs-B5-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..d353ca256b54236a4acefafdbc08e5b719892014
Binary files /dev/null and b/web/cmaps/HKgccs-B5-V.bcmap differ
diff --git a/web/cmaps/HKm314-B5-H.bcmap b/web/cmaps/HKm314-B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..576dc01112bd7f28c30804661f546ece203c53d8
Binary files /dev/null and b/web/cmaps/HKm314-B5-H.bcmap differ
diff --git a/web/cmaps/HKm314-B5-V.bcmap b/web/cmaps/HKm314-B5-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..0e96d0e228e0608f77f035655140c6a235d4ea56
Binary files /dev/null and b/web/cmaps/HKm314-B5-V.bcmap differ
diff --git a/web/cmaps/HKm471-B5-H.bcmap b/web/cmaps/HKm471-B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..11d170c75ed8696f0705f9fb9f5afcf3b0aff4c9
Binary files /dev/null and b/web/cmaps/HKm471-B5-H.bcmap differ
diff --git a/web/cmaps/HKm471-B5-V.bcmap b/web/cmaps/HKm471-B5-V.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..54959bf9e776c990460f2582adee80d317062d20
Binary files /dev/null and b/web/cmaps/HKm471-B5-V.bcmap differ
diff --git a/web/cmaps/HKscs-B5-H.bcmap b/web/cmaps/HKscs-B5-H.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..6ef7857ad17cefc4ab714d209716002317c23baa
Binary files /dev/null and b/web/cmaps/HKscs-B5-H.bcmap differ
diff --git a/web/cmaps/Hankaku.bcmap b/web/cmaps/Hankaku.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..4b8ec7fcef466bc4090dc13c8edb354cb94d0b67
Binary files /dev/null and b/web/cmaps/Hankaku.bcmap differ
diff --git a/web/cmaps/Hiragana.bcmap b/web/cmaps/Hiragana.bcmap
new file mode 100644
index 0000000000000000000000000000000000000000..17e983e77264c62ee149d1c8ec576a23e8f90eda
Binary files /dev/null and b/web/cmaps/Hiragana.bcmap differ
diff --git a/web/compressed.tracemonkey-pldi-09.pdf b/web/compressed.tracemonkey-pldi-09.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..ab7caa68fa89f0584fffbf88f0cebea66c8b935b
--- /dev/null
+++ b/web/compressed.tracemonkey-pldi-09.pdf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3662ff519e485810520552bf301d8c3b2b917fd2f83303f4965d7abed367e113
+size 1016315
diff --git a/web/debugger.css b/web/debugger.css
new file mode 100644
index 0000000000000000000000000000000000000000..b9d9f8190686ec2a320aa52920fb334196fe02d4
--- /dev/null
+++ b/web/debugger.css
@@ -0,0 +1,111 @@
+/* Copyright 2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+:root {
+ --panel-width: 300px;
+}
+
+#PDFBug,
+#PDFBug :is(input, button, select) {
+ font: message-box;
+}
+#PDFBug {
+ background-color: rgb(255 255 255);
+ border: 1px solid rgb(102 102 102);
+ position: fixed;
+ top: 32px;
+ right: 0;
+ bottom: 0;
+ font-size: 10px;
+ padding: 0;
+ width: var(--panel-width);
+}
+#PDFBug .controls {
+ background: rgb(238 238 238);
+ border-bottom: 1px solid rgb(102 102 102);
+ padding: 3px;
+}
+#PDFBug .panels {
+ inset: 27px 0 0;
+ overflow: auto;
+ position: absolute;
+}
+#PDFBug .panels > div {
+ padding: 5px;
+}
+#PDFBug button.active {
+ font-weight: bold;
+}
+.debuggerShowText,
+.debuggerHideText:hover {
+ background-color: rgb(255 255 0 / 0.25);
+}
+#PDFBug .stats {
+ font-family: courier;
+ font-size: 10px;
+ white-space: pre;
+}
+#PDFBug .stats .title {
+ font-weight: bold;
+}
+#PDFBug table {
+ font-size: 10px;
+ white-space: pre;
+}
+#PDFBug table.showText {
+ border-collapse: collapse;
+ text-align: center;
+}
+#PDFBug table.showText,
+#PDFBug table.showText :is(tr, td) {
+ border: 1px solid black;
+ padding: 1px;
+}
+#PDFBug table.showText td.advance {
+ color: grey;
+}
+
+#viewer.textLayer-visible .textLayer {
+ opacity: 1;
+}
+
+#viewer.textLayer-visible .canvasWrapper {
+ background-color: rgb(128 255 128);
+}
+
+#viewer.textLayer-visible .canvasWrapper canvas {
+ mix-blend-mode: screen;
+}
+
+#viewer.textLayer-visible .textLayer span {
+ background-color: rgb(255 255 0 / 0.1);
+ color: rgb(0 0 0);
+ border: solid 1px rgb(255 0 0 / 0.5);
+ box-sizing: border-box;
+}
+
+#viewer.textLayer-visible .textLayer span[aria-owns] {
+ background-color: rgb(255 0 0 / 0.3);
+}
+
+#viewer.textLayer-hover .textLayer span:hover {
+ background-color: rgb(255 255 255);
+ color: rgb(0 0 0);
+}
+
+#viewer.textLayer-shadow .textLayer span {
+ background-color: rgb(255 255 255 / 0.6);
+ color: rgb(0 0 0);
+}
diff --git a/web/debugger.mjs b/web/debugger.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..59c1871b3eb3226294884db461476b5976a6366a
--- /dev/null
+++ b/web/debugger.mjs
@@ -0,0 +1,623 @@
+/* Copyright 2012 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const { OPS } = globalThis.pdfjsLib || (await import("pdfjs-lib"));
+
+const opMap = Object.create(null);
+for (const key in OPS) {
+ opMap[OPS[key]] = key;
+}
+
+const FontInspector = (function FontInspectorClosure() {
+ let fonts;
+ let active = false;
+ const fontAttribute = "data-font-name";
+ function removeSelection() {
+ const divs = document.querySelectorAll(`span[${fontAttribute}]`);
+ for (const div of divs) {
+ div.className = "";
+ }
+ }
+ function resetSelection() {
+ const divs = document.querySelectorAll(`span[${fontAttribute}]`);
+ for (const div of divs) {
+ div.className = "debuggerHideText";
+ }
+ }
+ function selectFont(fontName, show) {
+ const divs = document.querySelectorAll(
+ `span[${fontAttribute}=${fontName}]`
+ );
+ for (const div of divs) {
+ div.className = show ? "debuggerShowText" : "debuggerHideText";
+ }
+ }
+ function textLayerClick(e) {
+ if (
+ !e.target.dataset.fontName ||
+ e.target.tagName.toUpperCase() !== "SPAN"
+ ) {
+ return;
+ }
+ const fontName = e.target.dataset.fontName;
+ const selects = document.getElementsByTagName("input");
+ for (const select of selects) {
+ if (select.dataset.fontName !== fontName) {
+ continue;
+ }
+ select.checked = !select.checked;
+ selectFont(fontName, select.checked);
+ select.scrollIntoView();
+ }
+ }
+ return {
+ // Properties/functions needed by PDFBug.
+ id: "FontInspector",
+ name: "Font Inspector",
+ panel: null,
+ manager: null,
+ init() {
+ const panel = this.panel;
+ const tmp = document.createElement("button");
+ tmp.addEventListener("click", resetSelection);
+ tmp.textContent = "Refresh";
+ panel.append(tmp);
+
+ fonts = document.createElement("div");
+ panel.append(fonts);
+ },
+ cleanup() {
+ fonts.textContent = "";
+ },
+ enabled: false,
+ get active() {
+ return active;
+ },
+ set active(value) {
+ active = value;
+ if (active) {
+ document.body.addEventListener("click", textLayerClick, true);
+ resetSelection();
+ } else {
+ document.body.removeEventListener("click", textLayerClick, true);
+ removeSelection();
+ }
+ },
+ // FontInspector specific functions.
+ fontAdded(fontObj, url) {
+ function properties(obj, list) {
+ const moreInfo = document.createElement("table");
+ for (const entry of list) {
+ const tr = document.createElement("tr");
+ const td1 = document.createElement("td");
+ td1.textContent = entry;
+ tr.append(td1);
+ const td2 = document.createElement("td");
+ td2.textContent = obj[entry].toString();
+ tr.append(td2);
+ moreInfo.append(tr);
+ }
+ return moreInfo;
+ }
+
+ const moreInfo = fontObj.css
+ ? properties(fontObj, ["baseFontName"])
+ : properties(fontObj, ["name", "type"]);
+
+ const fontName = fontObj.loadedName;
+ const font = document.createElement("div");
+ const name = document.createElement("span");
+ name.textContent = fontName;
+ let download;
+ if (!fontObj.css) {
+ download = document.createElement("a");
+ if (url) {
+ url = /url\(['"]?([^)"']+)/.exec(url);
+ download.href = url[1];
+ } else if (fontObj.data) {
+ download.href = URL.createObjectURL(
+ new Blob([fontObj.data], { type: fontObj.mimetype })
+ );
+ }
+ download.textContent = "Download";
+ }
+
+ const logIt = document.createElement("a");
+ logIt.href = "";
+ logIt.textContent = "Log";
+ logIt.addEventListener("click", function (event) {
+ event.preventDefault();
+ console.log(fontObj);
+ });
+ const select = document.createElement("input");
+ select.setAttribute("type", "checkbox");
+ select.dataset.fontName = fontName;
+ select.addEventListener("click", function () {
+ selectFont(fontName, select.checked);
+ });
+ if (download) {
+ font.append(select, name, " ", download, " ", logIt, moreInfo);
+ } else {
+ font.append(select, name, " ", logIt, moreInfo);
+ }
+ fonts.append(font);
+ // Somewhat of a hack, should probably add a hook for when the text layer
+ // is done rendering.
+ setTimeout(() => {
+ if (this.active) {
+ resetSelection();
+ }
+ }, 2000);
+ },
+ };
+})();
+
+// Manages all the page steppers.
+const StepperManager = (function StepperManagerClosure() {
+ let steppers = [];
+ let stepperDiv = null;
+ let stepperControls = null;
+ let stepperChooser = null;
+ let breakPoints = Object.create(null);
+ return {
+ // Properties/functions needed by PDFBug.
+ id: "Stepper",
+ name: "Stepper",
+ panel: null,
+ manager: null,
+ init() {
+ const self = this;
+ stepperControls = document.createElement("div");
+ stepperChooser = document.createElement("select");
+ stepperChooser.addEventListener("change", function (event) {
+ self.selectStepper(this.value);
+ });
+ stepperControls.append(stepperChooser);
+ stepperDiv = document.createElement("div");
+ this.panel.append(stepperControls, stepperDiv);
+ if (sessionStorage.getItem("pdfjsBreakPoints")) {
+ breakPoints = JSON.parse(sessionStorage.getItem("pdfjsBreakPoints"));
+ }
+ },
+ cleanup() {
+ stepperChooser.textContent = "";
+ stepperDiv.textContent = "";
+ steppers = [];
+ },
+ enabled: false,
+ active: false,
+ // Stepper specific functions.
+ create(pageIndex) {
+ const debug = document.createElement("div");
+ debug.id = "stepper" + pageIndex;
+ debug.hidden = true;
+ debug.className = "stepper";
+ stepperDiv.append(debug);
+ const b = document.createElement("option");
+ b.textContent = "Page " + (pageIndex + 1);
+ b.value = pageIndex;
+ stepperChooser.append(b);
+ const initBreakPoints = breakPoints[pageIndex] || [];
+ const stepper = new Stepper(debug, pageIndex, initBreakPoints);
+ steppers.push(stepper);
+ if (steppers.length === 1) {
+ this.selectStepper(pageIndex, false);
+ }
+ return stepper;
+ },
+ selectStepper(pageIndex, selectPanel) {
+ pageIndex |= 0;
+ if (selectPanel) {
+ this.manager.selectPanel(this);
+ }
+ for (const stepper of steppers) {
+ stepper.panel.hidden = stepper.pageIndex !== pageIndex;
+ }
+ for (const option of stepperChooser.options) {
+ option.selected = (option.value | 0) === pageIndex;
+ }
+ },
+ saveBreakPoints(pageIndex, bps) {
+ breakPoints[pageIndex] = bps;
+ sessionStorage.setItem("pdfjsBreakPoints", JSON.stringify(breakPoints));
+ },
+ };
+})();
+
+// The stepper for each page's operatorList.
+class Stepper {
+ // Shorter way to create element and optionally set textContent.
+ #c(tag, textContent) {
+ const d = document.createElement(tag);
+ if (textContent) {
+ d.textContent = textContent;
+ }
+ return d;
+ }
+
+ #simplifyArgs(args) {
+ if (typeof args === "string") {
+ const MAX_STRING_LENGTH = 75;
+ return args.length <= MAX_STRING_LENGTH
+ ? args
+ : args.substring(0, MAX_STRING_LENGTH) + "...";
+ }
+ if (typeof args !== "object" || args === null) {
+ return args;
+ }
+ if ("length" in args) {
+ // array
+ const MAX_ITEMS = 10,
+ simpleArgs = [];
+ let i, ii;
+ for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) {
+ simpleArgs.push(this.#simplifyArgs(args[i]));
+ }
+ if (i < args.length) {
+ simpleArgs.push("...");
+ }
+ return simpleArgs;
+ }
+ const simpleObj = {};
+ for (const key in args) {
+ simpleObj[key] = this.#simplifyArgs(args[key]);
+ }
+ return simpleObj;
+ }
+
+ constructor(panel, pageIndex, initialBreakPoints) {
+ this.panel = panel;
+ this.breakPoint = 0;
+ this.nextBreakPoint = null;
+ this.pageIndex = pageIndex;
+ this.breakPoints = initialBreakPoints;
+ this.currentIdx = -1;
+ this.operatorListIdx = 0;
+ this.indentLevel = 0;
+ }
+
+ init(operatorList) {
+ const panel = this.panel;
+ const content = this.#c("div", "c=continue, s=step");
+ const table = this.#c("table");
+ content.append(table);
+ table.cellSpacing = 0;
+ const headerRow = this.#c("tr");
+ table.append(headerRow);
+ headerRow.append(
+ this.#c("th", "Break"),
+ this.#c("th", "Idx"),
+ this.#c("th", "fn"),
+ this.#c("th", "args")
+ );
+ panel.append(content);
+ this.table = table;
+ this.updateOperatorList(operatorList);
+ }
+
+ updateOperatorList(operatorList) {
+ const self = this;
+
+ function cboxOnClick() {
+ const x = +this.dataset.idx;
+ if (this.checked) {
+ self.breakPoints.push(x);
+ } else {
+ self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
+ }
+ StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
+ }
+
+ const MAX_OPERATORS_COUNT = 15000;
+ if (this.operatorListIdx > MAX_OPERATORS_COUNT) {
+ return;
+ }
+
+ const chunk = document.createDocumentFragment();
+ const operatorsToDisplay = Math.min(
+ MAX_OPERATORS_COUNT,
+ operatorList.fnArray.length
+ );
+ for (let i = this.operatorListIdx; i < operatorsToDisplay; i++) {
+ const line = this.#c("tr");
+ line.className = "line";
+ line.dataset.idx = i;
+ chunk.append(line);
+ const checked = this.breakPoints.includes(i);
+ const args = operatorList.argsArray[i] || [];
+
+ const breakCell = this.#c("td");
+ const cbox = this.#c("input");
+ cbox.type = "checkbox";
+ cbox.className = "points";
+ cbox.checked = checked;
+ cbox.dataset.idx = i;
+ cbox.onclick = cboxOnClick;
+
+ breakCell.append(cbox);
+ line.append(breakCell, this.#c("td", i.toString()));
+ const fn = opMap[operatorList.fnArray[i]];
+ let decArgs = args;
+ if (fn === "showText") {
+ const glyphs = args[0];
+ const charCodeRow = this.#c("tr");
+ const fontCharRow = this.#c("tr");
+ const unicodeRow = this.#c("tr");
+ for (const glyph of glyphs) {
+ if (typeof glyph === "object" && glyph !== null) {
+ charCodeRow.append(this.#c("td", glyph.originalCharCode));
+ fontCharRow.append(this.#c("td", glyph.fontChar));
+ unicodeRow.append(this.#c("td", glyph.unicode));
+ } else {
+ // null or number
+ const advanceEl = this.#c("td", glyph);
+ advanceEl.classList.add("advance");
+ charCodeRow.append(advanceEl);
+ fontCharRow.append(this.#c("td"));
+ unicodeRow.append(this.#c("td"));
+ }
+ }
+ decArgs = this.#c("td");
+ const table = this.#c("table");
+ table.classList.add("showText");
+ decArgs.append(table);
+ table.append(charCodeRow, fontCharRow, unicodeRow);
+ } else if (fn === "restore" && this.indentLevel > 0) {
+ this.indentLevel--;
+ }
+ line.append(this.#c("td", " ".repeat(this.indentLevel * 2) + fn));
+ if (fn === "save") {
+ this.indentLevel++;
+ }
+
+ if (decArgs instanceof HTMLElement) {
+ line.append(decArgs);
+ } else {
+ line.append(this.#c("td", JSON.stringify(this.#simplifyArgs(decArgs))));
+ }
+ }
+ if (operatorsToDisplay < operatorList.fnArray.length) {
+ const lastCell = this.#c("td", "...");
+ lastCell.colspan = 4;
+ chunk.append(lastCell);
+ }
+ this.operatorListIdx = operatorList.fnArray.length;
+ this.table.append(chunk);
+ }
+
+ getNextBreakPoint() {
+ this.breakPoints.sort(function (a, b) {
+ return a - b;
+ });
+ for (const breakPoint of this.breakPoints) {
+ if (breakPoint > this.currentIdx) {
+ return breakPoint;
+ }
+ }
+ return null;
+ }
+
+ breakIt(idx, callback) {
+ StepperManager.selectStepper(this.pageIndex, true);
+ this.currentIdx = idx;
+
+ const listener = evt => {
+ switch (evt.keyCode) {
+ case 83: // step
+ document.removeEventListener("keydown", listener);
+ this.nextBreakPoint = this.currentIdx + 1;
+ this.goTo(-1);
+ callback();
+ break;
+ case 67: // continue
+ document.removeEventListener("keydown", listener);
+ this.nextBreakPoint = this.getNextBreakPoint();
+ this.goTo(-1);
+ callback();
+ break;
+ }
+ };
+ document.addEventListener("keydown", listener);
+ this.goTo(idx);
+ }
+
+ goTo(idx) {
+ const allRows = this.panel.getElementsByClassName("line");
+ for (const row of allRows) {
+ if ((row.dataset.idx | 0) === idx) {
+ row.style.backgroundColor = "rgb(251,250,207)";
+ row.scrollIntoView();
+ } else {
+ row.style.backgroundColor = null;
+ }
+ }
+ }
+}
+
+const Stats = (function Stats() {
+ let stats = [];
+ function clear(node) {
+ node.textContent = ""; // Remove any `node` contents from the DOM.
+ }
+ function getStatIndex(pageNumber) {
+ for (const [i, stat] of stats.entries()) {
+ if (stat.pageNumber === pageNumber) {
+ return i;
+ }
+ }
+ return false;
+ }
+ return {
+ // Properties/functions needed by PDFBug.
+ id: "Stats",
+ name: "Stats",
+ panel: null,
+ manager: null,
+ init() {},
+ enabled: false,
+ active: false,
+ // Stats specific functions.
+ add(pageNumber, stat) {
+ if (!stat) {
+ return;
+ }
+ const statsIndex = getStatIndex(pageNumber);
+ if (statsIndex !== false) {
+ stats[statsIndex].div.remove();
+ stats.splice(statsIndex, 1);
+ }
+ const wrapper = document.createElement("div");
+ wrapper.className = "stats";
+ const title = document.createElement("div");
+ title.className = "title";
+ title.textContent = "Page: " + pageNumber;
+ const statsDiv = document.createElement("div");
+ statsDiv.textContent = stat.toString();
+ wrapper.append(title, statsDiv);
+ stats.push({ pageNumber, div: wrapper });
+ stats.sort(function (a, b) {
+ return a.pageNumber - b.pageNumber;
+ });
+ clear(this.panel);
+ for (const entry of stats) {
+ this.panel.append(entry.div);
+ }
+ },
+ cleanup() {
+ stats = [];
+ clear(this.panel);
+ },
+ };
+})();
+
+// Manages all the debugging tools.
+class PDFBug {
+ static #buttons = [];
+
+ static #activePanel = null;
+
+ static tools = [FontInspector, StepperManager, Stats];
+
+ static enable(ids) {
+ const all = ids.length === 1 && ids[0] === "all";
+ const tools = this.tools;
+ for (const tool of tools) {
+ if (all || ids.includes(tool.id)) {
+ tool.enabled = true;
+ }
+ }
+ if (!all) {
+ // Sort the tools by the order they are enabled.
+ tools.sort(function (a, b) {
+ let indexA = ids.indexOf(a.id);
+ indexA = indexA < 0 ? tools.length : indexA;
+ let indexB = ids.indexOf(b.id);
+ indexB = indexB < 0 ? tools.length : indexB;
+ return indexA - indexB;
+ });
+ }
+ }
+
+ static init(container, ids) {
+ this.loadCSS();
+ this.enable(ids);
+ /*
+ * Basic Layout:
+ * PDFBug
+ * Controls
+ * Panels
+ * Panel
+ * Panel
+ * ...
+ */
+ const ui = document.createElement("div");
+ ui.id = "PDFBug";
+
+ const controls = document.createElement("div");
+ controls.setAttribute("class", "controls");
+ ui.append(controls);
+
+ const panels = document.createElement("div");
+ panels.setAttribute("class", "panels");
+ ui.append(panels);
+
+ container.append(ui);
+ container.style.right = "var(--panel-width)";
+
+ // Initialize all the debugging tools.
+ for (const tool of this.tools) {
+ const panel = document.createElement("div");
+ const panelButton = document.createElement("button");
+ panelButton.textContent = tool.name;
+ panelButton.addEventListener("click", event => {
+ event.preventDefault();
+ this.selectPanel(tool);
+ });
+ controls.append(panelButton);
+ panels.append(panel);
+ tool.panel = panel;
+ tool.manager = this;
+ if (tool.enabled) {
+ tool.init();
+ } else {
+ panel.textContent =
+ `${tool.name} is disabled. To enable add "${tool.id}" to ` +
+ "the pdfBug parameter and refresh (separate multiple by commas).";
+ }
+ this.#buttons.push(panelButton);
+ }
+ this.selectPanel(0);
+ }
+
+ static loadCSS() {
+ const { url } = import.meta;
+
+ const link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = url.replace(/\.mjs$/, ".css");
+
+ document.head.append(link);
+ }
+
+ static cleanup() {
+ for (const tool of this.tools) {
+ if (tool.enabled) {
+ tool.cleanup();
+ }
+ }
+ }
+
+ static selectPanel(index) {
+ if (typeof index !== "number") {
+ index = this.tools.indexOf(index);
+ }
+ if (index === this.#activePanel) {
+ return;
+ }
+ this.#activePanel = index;
+ for (const [j, tool] of this.tools.entries()) {
+ const isActive = j === index;
+ this.#buttons[j].classList.toggle("active", isActive);
+ tool.active = isActive;
+ tool.panel.hidden = !isActive;
+ }
+ }
+}
+
+globalThis.FontInspector = FontInspector;
+globalThis.StepperManager = StepperManager;
+globalThis.Stats = Stats;
+
+export { PDFBug };
diff --git a/web/frame.html b/web/frame.html
new file mode 100644
index 0000000000000000000000000000000000000000..5e03130868f0aedb199e8b4a1b34f57889719cc6
--- /dev/null
+++ b/web/frame.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/images/altText_add.svg b/web/images/altText_add.svg
new file mode 100644
index 0000000000000000000000000000000000000000..3451b536ce7253b9341022f1fc2d7138f7840f79
--- /dev/null
+++ b/web/images/altText_add.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/altText_done.svg b/web/images/altText_done.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f54924ebf92890ce3148ade2313f471cc385b710
--- /dev/null
+++ b/web/images/altText_done.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/annotation-check.svg b/web/images/annotation-check.svg
new file mode 100644
index 0000000000000000000000000000000000000000..71cd16df576f158da70e8007c946cf25ad25e397
--- /dev/null
+++ b/web/images/annotation-check.svg
@@ -0,0 +1,11 @@
+
+
diff --git a/web/images/annotation-comment.svg b/web/images/annotation-comment.svg
new file mode 100644
index 0000000000000000000000000000000000000000..86f1f17249f0a2e49e54a6610fa928c91815c2d7
--- /dev/null
+++ b/web/images/annotation-comment.svg
@@ -0,0 +1,16 @@
+
+
diff --git a/web/images/annotation-help.svg b/web/images/annotation-help.svg
new file mode 100644
index 0000000000000000000000000000000000000000..00938fefe048b07fede7186d235179fe6ac2482c
--- /dev/null
+++ b/web/images/annotation-help.svg
@@ -0,0 +1,26 @@
+
+
diff --git a/web/images/annotation-insert.svg b/web/images/annotation-insert.svg
new file mode 100644
index 0000000000000000000000000000000000000000..519ef6826e56b178cbad9d91adf0b7a870696602
--- /dev/null
+++ b/web/images/annotation-insert.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/web/images/annotation-key.svg b/web/images/annotation-key.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8d09d5378d5be7d1350e08c3ece3d87e2e711a96
--- /dev/null
+++ b/web/images/annotation-key.svg
@@ -0,0 +1,11 @@
+
+
diff --git a/web/images/annotation-newparagraph.svg b/web/images/annotation-newparagraph.svg
new file mode 100644
index 0000000000000000000000000000000000000000..38d2497da9112076d3e88876d03aedbb1437a1a2
--- /dev/null
+++ b/web/images/annotation-newparagraph.svg
@@ -0,0 +1,11 @@
+
+
diff --git a/web/images/annotation-noicon.svg b/web/images/annotation-noicon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c07d1080832bebea22ad1cf45fe3bac16ca90642
--- /dev/null
+++ b/web/images/annotation-noicon.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/web/images/annotation-note.svg b/web/images/annotation-note.svg
new file mode 100644
index 0000000000000000000000000000000000000000..70173651c7e2a3d53492f234e7444c9d775b3b2c
--- /dev/null
+++ b/web/images/annotation-note.svg
@@ -0,0 +1,42 @@
+
+
diff --git a/web/images/annotation-paperclip.svg b/web/images/annotation-paperclip.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2bed2250aee5b1e1aa4b884ae5eb984ad6aa1c91
--- /dev/null
+++ b/web/images/annotation-paperclip.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/web/images/annotation-paragraph.svg b/web/images/annotation-paragraph.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6ae5212b75d6855aa2cf7964de2b27650a6a2b80
--- /dev/null
+++ b/web/images/annotation-paragraph.svg
@@ -0,0 +1,16 @@
+
+
diff --git a/web/images/annotation-pushpin.svg b/web/images/annotation-pushpin.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6e0896cf4ac0e9d2368ea4cfb11fe106b3855dbf
--- /dev/null
+++ b/web/images/annotation-pushpin.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/web/images/cursor-editorFreeHighlight.svg b/web/images/cursor-editorFreeHighlight.svg
new file mode 100644
index 0000000000000000000000000000000000000000..513f6bdfdaa928912c1220fee3d95f2978c8cdf0
--- /dev/null
+++ b/web/images/cursor-editorFreeHighlight.svg
@@ -0,0 +1,6 @@
+
diff --git a/web/images/cursor-editorFreeText.svg b/web/images/cursor-editorFreeText.svg
new file mode 100644
index 0000000000000000000000000000000000000000..de2838ef102b5b7666fa192bc7d620b9b0fffb3b
--- /dev/null
+++ b/web/images/cursor-editorFreeText.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/cursor-editorInk.svg b/web/images/cursor-editorInk.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1dadb5c0155e91dd36fdaf6a0e270627fd314f8a
--- /dev/null
+++ b/web/images/cursor-editorInk.svg
@@ -0,0 +1,4 @@
+
diff --git a/web/images/cursor-editorTextHighlight.svg b/web/images/cursor-editorTextHighlight.svg
new file mode 100644
index 0000000000000000000000000000000000000000..800340cb5b18aba3528973b7a9e846ddda450ba4
--- /dev/null
+++ b/web/images/cursor-editorTextHighlight.svg
@@ -0,0 +1,8 @@
+
diff --git a/web/images/editor-toolbar-delete.svg b/web/images/editor-toolbar-delete.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f84520d85a3406933e8f5d5d66f7c56c90a375ed
--- /dev/null
+++ b/web/images/editor-toolbar-delete.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/web/images/findbarButton-next.svg b/web/images/findbarButton-next.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8cb39bec6f1a491aa6ee2fabf6c20a9432b2ee6f
--- /dev/null
+++ b/web/images/findbarButton-next.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/findbarButton-previous.svg b/web/images/findbarButton-previous.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b610879dafcce529a81fcba83d34262cb6460612
--- /dev/null
+++ b/web/images/findbarButton-previous.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/gv-toolbarButton-download.svg b/web/images/gv-toolbarButton-download.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d56cf3ce70e11286d2b8f33483db252e85a5d30e
--- /dev/null
+++ b/web/images/gv-toolbarButton-download.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/loading-icon.gif b/web/images/loading-icon.gif
new file mode 100644
index 0000000000000000000000000000000000000000..1c72ebb554be018511ae972c3f2361dff02dce02
Binary files /dev/null and b/web/images/loading-icon.gif differ
diff --git a/web/images/loading.svg b/web/images/loading.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0a15ff6885de8c9e8423e98dd066840d56f3df19
--- /dev/null
+++ b/web/images/loading.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/images/secondaryToolbarButton-documentProperties.svg b/web/images/secondaryToolbarButton-documentProperties.svg
new file mode 100644
index 0000000000000000000000000000000000000000..dd3917b91d981d9b522c7c822cc2113d6a758f70
--- /dev/null
+++ b/web/images/secondaryToolbarButton-documentProperties.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-firstPage.svg b/web/images/secondaryToolbarButton-firstPage.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f5c917f12a67772571b35880e90beecc58b30db4
--- /dev/null
+++ b/web/images/secondaryToolbarButton-firstPage.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-handTool.svg b/web/images/secondaryToolbarButton-handTool.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b7073b598957c43288998b17fa5d424e4a782517
--- /dev/null
+++ b/web/images/secondaryToolbarButton-handTool.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-lastPage.svg b/web/images/secondaryToolbarButton-lastPage.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c04f65079c59c6e966e51fc920b4d9c4db4db339
--- /dev/null
+++ b/web/images/secondaryToolbarButton-lastPage.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-rotateCcw.svg b/web/images/secondaryToolbarButton-rotateCcw.svg
new file mode 100644
index 0000000000000000000000000000000000000000..da73a1b16be7ec3f6b96ff3db884c8b6b853b3a9
--- /dev/null
+++ b/web/images/secondaryToolbarButton-rotateCcw.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-rotateCw.svg b/web/images/secondaryToolbarButton-rotateCw.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c41ce7365eb4ca48558b71cca2d5d4e4a9b99a6f
--- /dev/null
+++ b/web/images/secondaryToolbarButton-rotateCw.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-scrollHorizontal.svg b/web/images/secondaryToolbarButton-scrollHorizontal.svg
new file mode 100644
index 0000000000000000000000000000000000000000..fb440b9465316f3511d680fad8aa08516a959797
--- /dev/null
+++ b/web/images/secondaryToolbarButton-scrollHorizontal.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-scrollPage.svg b/web/images/secondaryToolbarButton-scrollPage.svg
new file mode 100644
index 0000000000000000000000000000000000000000..64a9f500732d7072a9c5e771adaf5dfbba5cbdc8
--- /dev/null
+++ b/web/images/secondaryToolbarButton-scrollPage.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-scrollVertical.svg b/web/images/secondaryToolbarButton-scrollVertical.svg
new file mode 100644
index 0000000000000000000000000000000000000000..dc7e80520072e254cb4cca66841ae4028ecef9da
--- /dev/null
+++ b/web/images/secondaryToolbarButton-scrollVertical.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-scrollWrapped.svg b/web/images/secondaryToolbarButton-scrollWrapped.svg
new file mode 100644
index 0000000000000000000000000000000000000000..75fe26bcfcc5149376058eea001acc44d3f319fb
--- /dev/null
+++ b/web/images/secondaryToolbarButton-scrollWrapped.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-selectTool.svg b/web/images/secondaryToolbarButton-selectTool.svg
new file mode 100644
index 0000000000000000000000000000000000000000..94d51410dd43142275323c6fd3ae87859412f92a
--- /dev/null
+++ b/web/images/secondaryToolbarButton-selectTool.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-spreadEven.svg b/web/images/secondaryToolbarButton-spreadEven.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ce201e33c0a5dd9815e1067a3c523bcea353d59a
--- /dev/null
+++ b/web/images/secondaryToolbarButton-spreadEven.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-spreadNone.svg b/web/images/secondaryToolbarButton-spreadNone.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e8d487fa389e25b6aa35db299197d1c816ae5646
--- /dev/null
+++ b/web/images/secondaryToolbarButton-spreadNone.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/secondaryToolbarButton-spreadOdd.svg b/web/images/secondaryToolbarButton-spreadOdd.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9211a427bfee600ad9c02cfe3c77bb108928c6f9
--- /dev/null
+++ b/web/images/secondaryToolbarButton-spreadOdd.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-bookmark.svg b/web/images/toolbarButton-bookmark.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c4c37c90582dd951e90804502902989cb4c8503e
--- /dev/null
+++ b/web/images/toolbarButton-bookmark.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-currentOutlineItem.svg b/web/images/toolbarButton-currentOutlineItem.svg
new file mode 100644
index 0000000000000000000000000000000000000000..01e67623bf2ab17b21193966cd3ef9ff17c87cb3
--- /dev/null
+++ b/web/images/toolbarButton-currentOutlineItem.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-download.svg b/web/images/toolbarButton-download.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e2e850adf73d0cf2f499a3c33c9d4988676ad018
--- /dev/null
+++ b/web/images/toolbarButton-download.svg
@@ -0,0 +1,4 @@
+
diff --git a/web/images/toolbarButton-editorFreeText.svg b/web/images/toolbarButton-editorFreeText.svg
new file mode 100644
index 0000000000000000000000000000000000000000..13a67bd9b6ab2106f5b4db06ce3a55263e2dbc4b
--- /dev/null
+++ b/web/images/toolbarButton-editorFreeText.svg
@@ -0,0 +1,5 @@
+
diff --git a/web/images/toolbarButton-editorHighlight.svg b/web/images/toolbarButton-editorHighlight.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b3cd7fda9c960be975f276950a7b7c25966d0c8d
--- /dev/null
+++ b/web/images/toolbarButton-editorHighlight.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/web/images/toolbarButton-editorInk.svg b/web/images/toolbarButton-editorInk.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b579eec7ee58f1f9d43736ff1e671f542d942444
--- /dev/null
+++ b/web/images/toolbarButton-editorInk.svg
@@ -0,0 +1,4 @@
+
diff --git a/web/images/toolbarButton-editorStamp.svg b/web/images/toolbarButton-editorStamp.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a1fef4922d97f073c59dfe409db557b2e968408a
--- /dev/null
+++ b/web/images/toolbarButton-editorStamp.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/web/images/toolbarButton-menuArrow.svg b/web/images/toolbarButton-menuArrow.svg
new file mode 100644
index 0000000000000000000000000000000000000000..82ffeaabb9e497590979506a13f761b908ca6bd7
--- /dev/null
+++ b/web/images/toolbarButton-menuArrow.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-openFile.svg b/web/images/toolbarButton-openFile.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e773781d607247c6807cd7fe17c7fb7fddd74119
--- /dev/null
+++ b/web/images/toolbarButton-openFile.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-pageDown.svg b/web/images/toolbarButton-pageDown.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1fc12e733d9b957f359ec561063750dffab65073
--- /dev/null
+++ b/web/images/toolbarButton-pageDown.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-pageUp.svg b/web/images/toolbarButton-pageUp.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0936b9a578f89dd940767529a755c1c470e8a49c
--- /dev/null
+++ b/web/images/toolbarButton-pageUp.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-presentationMode.svg b/web/images/toolbarButton-presentationMode.svg
new file mode 100644
index 0000000000000000000000000000000000000000..901d5672bcb34191c425679a1b35db14e0f5abd1
--- /dev/null
+++ b/web/images/toolbarButton-presentationMode.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-print.svg b/web/images/toolbarButton-print.svg
new file mode 100644
index 0000000000000000000000000000000000000000..97a390474d93e6c34b17479f1781fc5266f72f0b
--- /dev/null
+++ b/web/images/toolbarButton-print.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-search.svg b/web/images/toolbarButton-search.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0cc7ae21ac16844b81e3861f0bd517fb2cb58a9b
--- /dev/null
+++ b/web/images/toolbarButton-search.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-secondaryToolbarToggle.svg b/web/images/toolbarButton-secondaryToolbarToggle.svg
new file mode 100644
index 0000000000000000000000000000000000000000..cace863728ade3088e92292458c28d9d0022c42f
--- /dev/null
+++ b/web/images/toolbarButton-secondaryToolbarToggle.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-sidebarToggle.svg b/web/images/toolbarButton-sidebarToggle.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1d8d0e4b23aab16ccd62f0e1dbb69088db84bc06
--- /dev/null
+++ b/web/images/toolbarButton-sidebarToggle.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-viewAttachments.svg b/web/images/toolbarButton-viewAttachments.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ab73f6e6e071e95c3c90b94b219c9c94a49e307f
--- /dev/null
+++ b/web/images/toolbarButton-viewAttachments.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-viewLayers.svg b/web/images/toolbarButton-viewLayers.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1d726682642bf968612180bfba62a2edd0882cfb
--- /dev/null
+++ b/web/images/toolbarButton-viewLayers.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-viewOutline.svg b/web/images/toolbarButton-viewOutline.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7ed1bd97f2f5b57aef1e73046e1df0d2b3ccf6ef
--- /dev/null
+++ b/web/images/toolbarButton-viewOutline.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-viewThumbnail.svg b/web/images/toolbarButton-viewThumbnail.svg
new file mode 100644
index 0000000000000000000000000000000000000000..040d123266a20cc089b2af50a0a39b9760e2d493
--- /dev/null
+++ b/web/images/toolbarButton-viewThumbnail.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-zoomIn.svg b/web/images/toolbarButton-zoomIn.svg
new file mode 100644
index 0000000000000000000000000000000000000000..30ec51a2ffdff5fc1f7bb6088bd9fa492c428a09
--- /dev/null
+++ b/web/images/toolbarButton-zoomIn.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/toolbarButton-zoomOut.svg b/web/images/toolbarButton-zoomOut.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f273b5995cdb234502a123a7e384af651f1e47bd
--- /dev/null
+++ b/web/images/toolbarButton-zoomOut.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/images/treeitem-collapsed.svg b/web/images/treeitem-collapsed.svg
new file mode 100644
index 0000000000000000000000000000000000000000..831cddfc869518822c6d70e289ea5fe4b175cb94
--- /dev/null
+++ b/web/images/treeitem-collapsed.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/images/treeitem-expanded.svg b/web/images/treeitem-expanded.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2d45f0c8d18dd66c1cbc6756b97299051b9886e2
--- /dev/null
+++ b/web/images/treeitem-expanded.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/locale/ach/viewer.ftl b/web/locale/ach/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..36769b7084c2c11912f9a7aaeaf0b46be768c158
--- /dev/null
+++ b/web/locale/ach/viewer.ftl
@@ -0,0 +1,225 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pot buk mukato
+pdfjs-previous-button-label = Mukato
+pdfjs-next-button =
+ .title = Pot buk malubo
+pdfjs-next-button-label = Malubo
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pot buk
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = pi { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } me { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Jwik Matidi
+pdfjs-zoom-out-button-label = Jwik Matidi
+pdfjs-zoom-in-button =
+ .title = Kwot Madit
+pdfjs-zoom-in-button-label = Kwot Madit
+pdfjs-zoom-select =
+ .title = Kwoti
+pdfjs-presentation-mode-button =
+ .title = Lokke i kit me tyer
+pdfjs-presentation-mode-button-label = Kit me tyer
+pdfjs-open-file-button =
+ .title = Yab Pwail
+pdfjs-open-file-button-label = Yab
+pdfjs-print-button =
+ .title = Go
+pdfjs-print-button-label = Go
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Gintic
+pdfjs-tools-button-label = Gintic
+pdfjs-first-page-button =
+ .title = Cit i pot buk mukwongo
+pdfjs-first-page-button-label = Cit i pot buk mukwongo
+pdfjs-last-page-button =
+ .title = Cit i pot buk magiko
+pdfjs-last-page-button-label = Cit i pot buk magiko
+pdfjs-page-rotate-cw-button =
+ .title = Wire i tung lacuc
+pdfjs-page-rotate-cw-button-label = Wire i tung lacuc
+pdfjs-page-rotate-ccw-button =
+ .title = Wire i tung lacam
+pdfjs-page-rotate-ccw-button-label = Wire i tung lacam
+pdfjs-cursor-text-select-tool-button =
+ .title = Cak gitic me yero coc
+pdfjs-cursor-text-select-tool-button-label = Gitic me yero coc
+pdfjs-cursor-hand-tool-button =
+ .title = Cak gitic me cing
+pdfjs-cursor-hand-tool-button-label = Gitic cing
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Jami me gin acoya…
+pdfjs-document-properties-button-label = Jami me gin acoya…
+pdfjs-document-properties-file-name = Nying pwail:
+pdfjs-document-properties-file-size = Dit pa pwail:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Wiye:
+pdfjs-document-properties-author = Ngat mucoyo:
+pdfjs-document-properties-subject = Subjek:
+pdfjs-document-properties-keywords = Lok mapire tek:
+pdfjs-document-properties-creation-date = Nino dwe me cwec:
+pdfjs-document-properties-modification-date = Nino dwe me yub:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Lacwec:
+pdfjs-document-properties-producer = Layub PDF:
+pdfjs-document-properties-version = Kit PDF:
+pdfjs-document-properties-page-count = Kwan me pot buk:
+pdfjs-document-properties-page-size = Dit pa potbuk:
+pdfjs-document-properties-page-size-unit-inches = i
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = atir
+pdfjs-document-properties-page-size-orientation-landscape = arii
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Waraga
+pdfjs-document-properties-page-size-name-legal = Cik
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+pdfjs-document-properties-linearized-yes = Eyo
+pdfjs-document-properties-linearized-no = Pe
+pdfjs-document-properties-close-button = Lor
+
+## Print
+
+pdfjs-print-progress-message = Yubo coc me agoya…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Juki
+pdfjs-printing-not-supported = Ciko: Layeny ma pe teno goyo liweng.
+pdfjs-printing-not-ready = Ciko: PDF pe ocane weng me agoya.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Lok gintic ma inget
+pdfjs-toggle-sidebar-button-label = Lok gintic ma inget
+pdfjs-document-outline-button =
+ .title = Nyut Wiyewiye me Gin acoya (dii-kiryo me yaro/kano jami weng)
+pdfjs-document-outline-button-label = Pek pa gin acoya
+pdfjs-attachments-button =
+ .title = Nyut twec
+pdfjs-attachments-button-label = Twec
+pdfjs-thumbs-button =
+ .title = Nyut cal
+pdfjs-thumbs-button-label = Cal
+pdfjs-findbar-button =
+ .title = Nong iye gin acoya
+pdfjs-findbar-button-label = Nong
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pot buk { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Cal me pot buk { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Nong
+ .placeholder = Nong i dokumen…
+pdfjs-find-previous-button =
+ .title = Nong timme pa lok mukato
+pdfjs-find-previous-button-label = Mukato
+pdfjs-find-next-button =
+ .title = Nong timme pa lok malubo
+pdfjs-find-next-button-label = Malubo
+pdfjs-find-highlight-checkbox = Ket Lanyut I Weng
+pdfjs-find-match-case-checkbox-label = Lok marwate
+pdfjs-find-reached-top = Oo iwi gin acoya, omede ki i tere
+pdfjs-find-reached-bottom = Oo i agiki me gin acoya, omede ki iwiye
+pdfjs-find-not-found = Lok pe ononge
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Lac me iye pot buk
+pdfjs-page-scale-fit = Porre me pot buk
+pdfjs-page-scale-auto = Kwot pire kene
+pdfjs-page-scale-actual = Dite kikome
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Bal otime kun cano PDF.
+pdfjs-invalid-file-error = Pwail me PDF ma pe atir onyo obale woko.
+pdfjs-missing-file-error = Pwail me PDF tye ka rem.
+pdfjs-unexpected-response-error = Lagam mape kigeno pa lapok tic.
+pdfjs-rendering-error = Bal otime i kare me nyuto pot buk.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Lok angea manok]
+
+## Password
+
+pdfjs-password-label = Ket mung me donyo me yabo pwail me PDF man.
+pdfjs-password-invalid = Mung me donyo pe atir. Tim ber i tem doki.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Juki
+pdfjs-web-fonts-disabled = Kijuko dit pa coc me kakube woko: pe romo tic ki dit pa coc me PDF ma kiketo i kine.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/af/viewer.ftl b/web/locale/af/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..7c4346fe018b0014b326c1a74c280e2b7b6417a2
--- /dev/null
+++ b/web/locale/af/viewer.ftl
@@ -0,0 +1,212 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Vorige bladsy
+pdfjs-previous-button-label = Vorige
+pdfjs-next-button =
+ .title = Volgende bladsy
+pdfjs-next-button-label = Volgende
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Bladsy
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = van { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } van { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoem uit
+pdfjs-zoom-out-button-label = Zoem uit
+pdfjs-zoom-in-button =
+ .title = Zoem in
+pdfjs-zoom-in-button-label = Zoem in
+pdfjs-zoom-select =
+ .title = Zoem
+pdfjs-presentation-mode-button =
+ .title = Wissel na voorleggingsmodus
+pdfjs-presentation-mode-button-label = Voorleggingsmodus
+pdfjs-open-file-button =
+ .title = Open lêer
+pdfjs-open-file-button-label = Open
+pdfjs-print-button =
+ .title = Druk
+pdfjs-print-button-label = Druk
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Nutsgoed
+pdfjs-tools-button-label = Nutsgoed
+pdfjs-first-page-button =
+ .title = Gaan na eerste bladsy
+pdfjs-first-page-button-label = Gaan na eerste bladsy
+pdfjs-last-page-button =
+ .title = Gaan na laaste bladsy
+pdfjs-last-page-button-label = Gaan na laaste bladsy
+pdfjs-page-rotate-cw-button =
+ .title = Roteer kloksgewys
+pdfjs-page-rotate-cw-button-label = Roteer kloksgewys
+pdfjs-page-rotate-ccw-button =
+ .title = Roteer anti-kloksgewys
+pdfjs-page-rotate-ccw-button-label = Roteer anti-kloksgewys
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktiveer gereedskap om teks te merk
+pdfjs-cursor-text-select-tool-button-label = Teksmerkgereedskap
+pdfjs-cursor-hand-tool-button =
+ .title = Aktiveer handjie
+pdfjs-cursor-hand-tool-button-label = Handjie
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumenteienskappe…
+pdfjs-document-properties-button-label = Dokumenteienskappe…
+pdfjs-document-properties-file-name = Lêernaam:
+pdfjs-document-properties-file-size = Lêergrootte:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } kG ({ $size_b } grepe)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MG ({ $size_b } grepe)
+pdfjs-document-properties-title = Titel:
+pdfjs-document-properties-author = Outeur:
+pdfjs-document-properties-subject = Onderwerp:
+pdfjs-document-properties-keywords = Sleutelwoorde:
+pdfjs-document-properties-creation-date = Skeppingsdatum:
+pdfjs-document-properties-modification-date = Wysigingsdatum:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Skepper:
+pdfjs-document-properties-producer = PDF-vervaardiger:
+pdfjs-document-properties-version = PDF-weergawe:
+pdfjs-document-properties-page-count = Aantal bladsye:
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+
+##
+
+pdfjs-document-properties-close-button = Sluit
+
+## Print
+
+pdfjs-print-progress-message = Berei tans dokument voor om te druk…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Kanselleer
+pdfjs-printing-not-supported = Waarskuwing: Dié blaaier ondersteun nie drukwerk ten volle nie.
+pdfjs-printing-not-ready = Waarskuwing: Die PDF is nog nie volledig gelaai vir drukwerk nie.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Sypaneel aan/af
+pdfjs-toggle-sidebar-button-label = Sypaneel aan/af
+pdfjs-document-outline-button =
+ .title = Wys dokumentskema (dubbelklik om alle items oop/toe te vou)
+pdfjs-document-outline-button-label = Dokumentoorsig
+pdfjs-attachments-button =
+ .title = Wys aanhegsels
+pdfjs-attachments-button-label = Aanhegsels
+pdfjs-thumbs-button =
+ .title = Wys duimnaels
+pdfjs-thumbs-button-label = Duimnaels
+pdfjs-findbar-button =
+ .title = Soek in dokument
+pdfjs-findbar-button-label = Vind
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Bladsy { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Duimnael van bladsy { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Vind
+ .placeholder = Soek in dokument…
+pdfjs-find-previous-button =
+ .title = Vind die vorige voorkoms van die frase
+pdfjs-find-previous-button-label = Vorige
+pdfjs-find-next-button =
+ .title = Vind die volgende voorkoms van die frase
+pdfjs-find-next-button-label = Volgende
+pdfjs-find-highlight-checkbox = Verlig almal
+pdfjs-find-match-case-checkbox-label = Kassensitief
+pdfjs-find-reached-top = Bokant van dokument is bereik; gaan voort van onder af
+pdfjs-find-reached-bottom = Einde van dokument is bereik; gaan voort van bo af
+pdfjs-find-not-found = Frase nie gevind nie
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Bladsywydte
+pdfjs-page-scale-fit = Pas bladsy
+pdfjs-page-scale-auto = Outomatiese zoem
+pdfjs-page-scale-actual = Werklike grootte
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = 'n Fout het voorgekom met die laai van die PDF.
+pdfjs-invalid-file-error = Ongeldige of korrupte PDF-lêer.
+pdfjs-missing-file-error = PDF-lêer is weg.
+pdfjs-unexpected-response-error = Onverwagse antwoord van bediener.
+pdfjs-rendering-error = 'n Fout het voorgekom toe die bladsy weergegee is.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type }-annotasie]
+
+## Password
+
+pdfjs-password-label = Gee die wagwoord om dié PDF-lêer mee te open.
+pdfjs-password-invalid = Ongeldige wagwoord. Probeer gerus weer.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Kanselleer
+pdfjs-web-fonts-disabled = Webfonte is gedeaktiveer: kan nie PDF-fonte wat ingebed is, gebruik nie.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/an/viewer.ftl b/web/locale/an/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..673314778ad8b7747c4708fda6feda05130b4582
--- /dev/null
+++ b/web/locale/an/viewer.ftl
@@ -0,0 +1,257 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pachina anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Pachina siguient
+pdfjs-next-button-label = Siguient
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pachina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Achiquir
+pdfjs-zoom-out-button-label = Achiquir
+pdfjs-zoom-in-button =
+ .title = Agrandir
+pdfjs-zoom-in-button-label = Agrandir
+pdfjs-zoom-select =
+ .title = Grandaria
+pdfjs-presentation-mode-button =
+ .title = Cambear t'o modo de presentación
+pdfjs-presentation-mode-button-label = Modo de presentación
+pdfjs-open-file-button =
+ .title = Ubrir o fichero
+pdfjs-open-file-button-label = Ubrir
+pdfjs-print-button =
+ .title = Imprentar
+pdfjs-print-button-label = Imprentar
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Ferramientas
+pdfjs-tools-button-label = Ferramientas
+pdfjs-first-page-button =
+ .title = Ir ta la primer pachina
+pdfjs-first-page-button-label = Ir ta la primer pachina
+pdfjs-last-page-button =
+ .title = Ir ta la zaguer pachina
+pdfjs-last-page-button-label = Ir ta la zaguer pachina
+pdfjs-page-rotate-cw-button =
+ .title = Chirar enta la dreita
+pdfjs-page-rotate-cw-button-label = Chira enta la dreita
+pdfjs-page-rotate-ccw-button =
+ .title = Chirar enta la zurda
+pdfjs-page-rotate-ccw-button-label = Chirar enta la zurda
+pdfjs-cursor-text-select-tool-button =
+ .title = Activar la ferramienta de selección de texto
+pdfjs-cursor-text-select-tool-button-label = Ferramienta de selección de texto
+pdfjs-cursor-hand-tool-button =
+ .title = Activar la ferramienta man
+pdfjs-cursor-hand-tool-button-label = Ferramienta man
+pdfjs-scroll-vertical-button =
+ .title = Usar lo desplazamiento vertical
+pdfjs-scroll-vertical-button-label = Desplazamiento vertical
+pdfjs-scroll-horizontal-button =
+ .title = Usar lo desplazamiento horizontal
+pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Activaar lo desplazamiento contino
+pdfjs-scroll-wrapped-button-label = Desplazamiento contino
+pdfjs-spread-none-button =
+ .title = No unir vistas de pachinas
+pdfjs-spread-none-button-label = Una pachina nomás
+pdfjs-spread-odd-button =
+ .title = Mostrar vista de pachinas, con as impars a la zurda
+pdfjs-spread-odd-button-label = Doble pachina, impar a la zurda
+pdfjs-spread-even-button =
+ .title = Amostrar vista de pachinas, con as pars a la zurda
+pdfjs-spread-even-button-label = Doble pachina, para a la zurda
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propiedatz d'o documento...
+pdfjs-document-properties-button-label = Propiedatz d'o documento...
+pdfjs-document-properties-file-name = Nombre de fichero:
+pdfjs-document-properties-file-size = Grandaria d'o fichero:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Titol:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Afer:
+pdfjs-document-properties-keywords = Parolas clau:
+pdfjs-document-properties-creation-date = Calendata de creyación:
+pdfjs-document-properties-modification-date = Calendata de modificación:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creyador:
+pdfjs-document-properties-producer = Creyador de PDF:
+pdfjs-document-properties-version = Versión de PDF:
+pdfjs-document-properties-page-count = Numero de pachinas:
+pdfjs-document-properties-page-size = Mida de pachina:
+pdfjs-document-properties-page-size-unit-inches = pulgadas
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertical
+pdfjs-document-properties-page-size-orientation-landscape = horizontal
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Carta
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } x { $height } { $unit } { $orientation }
+pdfjs-document-properties-page-size-dimension-name-string = { $width } x { $height } { $unit } { $name }, { $orientation }
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista web rapida:
+pdfjs-document-properties-linearized-yes = Sí
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Zarrar
+
+## Print
+
+pdfjs-print-progress-message = Se ye preparando la documentación pa imprentar…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancelar
+pdfjs-printing-not-supported = Pare cuenta: Iste navegador no maneya totalment as impresions.
+pdfjs-printing-not-ready = Aviso: Encara no se ha cargau completament o PDF ta imprentar-lo.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Amostrar u amagar a barra lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Cambiar barra lateral (lo documento contiene esquema/adchuntos/capas)
+pdfjs-toggle-sidebar-button-label = Amostrar a barra lateral
+pdfjs-document-outline-button =
+ .title = Amostrar esquema d'o documento (fer doble clic pa expandir/compactar totz los items)
+pdfjs-document-outline-button-label = Esquema d'o documento
+pdfjs-attachments-button =
+ .title = Amostrar os adchuntos
+pdfjs-attachments-button-label = Adchuntos
+pdfjs-layers-button =
+ .title = Amostrar capas (doble clic para reiniciar totas las capas a lo estau per defecto)
+pdfjs-layers-button-label = Capas
+pdfjs-thumbs-button =
+ .title = Amostrar as miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-findbar-button =
+ .title = Trobar en o documento
+pdfjs-findbar-button-label = Trobar
+pdfjs-additional-layers = Capas adicionals
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pachina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura d'a pachina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Trobar
+ .placeholder = Trobar en o documento…
+pdfjs-find-previous-button =
+ .title = Trobar l'anterior coincidencia d'a frase
+pdfjs-find-previous-button-label = Anterior
+pdfjs-find-next-button =
+ .title = Trobar a siguient coincidencia d'a frase
+pdfjs-find-next-button-label = Siguient
+pdfjs-find-highlight-checkbox = Resaltar-lo tot
+pdfjs-find-match-case-checkbox-label = Coincidencia de mayusclas/minusclas
+pdfjs-find-entire-word-checkbox-label = Parolas completas
+pdfjs-find-reached-top = S'ha plegau a l'inicio d'o documento, se contina dende baixo
+pdfjs-find-reached-bottom = S'ha plegau a la fin d'o documento, se contina dende alto
+pdfjs-find-not-found = No s'ha trobau a frase
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Amplaria d'a pachina
+pdfjs-page-scale-fit = Achuste d'a pachina
+pdfjs-page-scale-auto = Grandaria automatica
+pdfjs-page-scale-actual = Grandaria actual
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = S'ha produciu una error en cargar o PDF.
+pdfjs-invalid-file-error = O PDF no ye valido u ye estorbau.
+pdfjs-missing-file-error = No i ha fichero PDF.
+pdfjs-unexpected-response-error = Respuesta a lo servicio inasperada.
+pdfjs-rendering-error = Ha ocurriu una error en renderizar a pachina.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotación { $type }]
+
+## Password
+
+pdfjs-password-label = Introduzca a clau ta ubrir iste fichero PDF.
+pdfjs-password-invalid = Clau invalida. Torna a intentar-lo.
+pdfjs-password-ok-button = Acceptar
+pdfjs-password-cancel-button = Cancelar
+pdfjs-web-fonts-disabled = As fuents web son desactivadas: no se puet incrustar fichers PDF.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ar/viewer.ftl b/web/locale/ar/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..97d6da57fc474a9b2a5b287e1a5c710837aeeb6c
--- /dev/null
+++ b/web/locale/ar/viewer.ftl
@@ -0,0 +1,404 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = الصفحة السابقة
+pdfjs-previous-button-label = السابقة
+pdfjs-next-button =
+ .title = الصفحة التالية
+pdfjs-next-button-label = التالية
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = صفحة
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = من { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } من { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = بعّد
+pdfjs-zoom-out-button-label = بعّد
+pdfjs-zoom-in-button =
+ .title = قرّب
+pdfjs-zoom-in-button-label = قرّب
+pdfjs-zoom-select =
+ .title = التقريب
+pdfjs-presentation-mode-button =
+ .title = انتقل لوضع العرض التقديمي
+pdfjs-presentation-mode-button-label = وضع العرض التقديمي
+pdfjs-open-file-button =
+ .title = افتح ملفًا
+pdfjs-open-file-button-label = افتح
+pdfjs-print-button =
+ .title = اطبع
+pdfjs-print-button-label = اطبع
+pdfjs-save-button =
+ .title = احفظ
+pdfjs-save-button-label = احفظ
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = نزّل
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = نزّل
+pdfjs-bookmark-button =
+ .title = الصفحة الحالية (عرض URL من الصفحة الحالية)
+pdfjs-bookmark-button-label = الصفحة الحالية
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = الأدوات
+pdfjs-tools-button-label = الأدوات
+pdfjs-first-page-button =
+ .title = انتقل إلى الصفحة الأولى
+pdfjs-first-page-button-label = انتقل إلى الصفحة الأولى
+pdfjs-last-page-button =
+ .title = انتقل إلى الصفحة الأخيرة
+pdfjs-last-page-button-label = انتقل إلى الصفحة الأخيرة
+pdfjs-page-rotate-cw-button =
+ .title = أدر باتجاه عقارب الساعة
+pdfjs-page-rotate-cw-button-label = أدر باتجاه عقارب الساعة
+pdfjs-page-rotate-ccw-button =
+ .title = أدر بعكس اتجاه عقارب الساعة
+pdfjs-page-rotate-ccw-button-label = أدر بعكس اتجاه عقارب الساعة
+pdfjs-cursor-text-select-tool-button =
+ .title = فعّل أداة اختيار النص
+pdfjs-cursor-text-select-tool-button-label = أداة اختيار النص
+pdfjs-cursor-hand-tool-button =
+ .title = فعّل أداة اليد
+pdfjs-cursor-hand-tool-button-label = أداة اليد
+pdfjs-scroll-page-button =
+ .title = استخدم تمرير الصفحة
+pdfjs-scroll-page-button-label = تمرير الصفحة
+pdfjs-scroll-vertical-button =
+ .title = استخدم التمرير الرأسي
+pdfjs-scroll-vertical-button-label = التمرير الرأسي
+pdfjs-scroll-horizontal-button =
+ .title = استخدم التمرير الأفقي
+pdfjs-scroll-horizontal-button-label = التمرير الأفقي
+pdfjs-scroll-wrapped-button =
+ .title = استخدم التمرير الملتف
+pdfjs-scroll-wrapped-button-label = التمرير الملتف
+pdfjs-spread-none-button =
+ .title = لا تدمج هوامش الصفحات مع بعضها البعض
+pdfjs-spread-none-button-label = بلا هوامش
+pdfjs-spread-odd-button =
+ .title = ادمج هوامش الصفحات الفردية
+pdfjs-spread-odd-button-label = هوامش الصفحات الفردية
+pdfjs-spread-even-button =
+ .title = ادمج هوامش الصفحات الزوجية
+pdfjs-spread-even-button-label = هوامش الصفحات الزوجية
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = خصائص المستند…
+pdfjs-document-properties-button-label = خصائص المستند…
+pdfjs-document-properties-file-name = اسم الملف:
+pdfjs-document-properties-file-size = حجم الملف:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } ك.بايت ({ $size_b } بايت)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } م.بايت ({ $size_b } بايت)
+pdfjs-document-properties-title = العنوان:
+pdfjs-document-properties-author = المؤلف:
+pdfjs-document-properties-subject = الموضوع:
+pdfjs-document-properties-keywords = الكلمات الأساسية:
+pdfjs-document-properties-creation-date = تاريخ الإنشاء:
+pdfjs-document-properties-modification-date = تاريخ التعديل:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }، { $time }
+pdfjs-document-properties-creator = المنشئ:
+pdfjs-document-properties-producer = منتج PDF:
+pdfjs-document-properties-version = إصدارة PDF:
+pdfjs-document-properties-page-count = عدد الصفحات:
+pdfjs-document-properties-page-size = مقاس الورقة:
+pdfjs-document-properties-page-size-unit-inches = بوصة
+pdfjs-document-properties-page-size-unit-millimeters = ملم
+pdfjs-document-properties-page-size-orientation-portrait = طوليّ
+pdfjs-document-properties-page-size-orientation-landscape = عرضيّ
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = خطاب
+pdfjs-document-properties-page-size-name-legal = قانونيّ
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }، { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = العرض السريع عبر الوِب:
+pdfjs-document-properties-linearized-yes = نعم
+pdfjs-document-properties-linearized-no = لا
+pdfjs-document-properties-close-button = أغلق
+
+## Print
+
+pdfjs-print-progress-message = يُحضّر المستند للطباعة…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }٪
+pdfjs-print-progress-close-button = ألغِ
+pdfjs-printing-not-supported = تحذير: لا يدعم هذا المتصفح الطباعة بشكل كامل.
+pdfjs-printing-not-ready = تحذير: ملف PDF لم يُحمّل كاملًا للطباعة.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = بدّل ظهور الشريط الجانبي
+pdfjs-toggle-sidebar-notification-button =
+ .title = بدّل ظهور الشريط الجانبي (يحتوي المستند على مخطط أو مرفقات أو طبقات)
+pdfjs-toggle-sidebar-button-label = بدّل ظهور الشريط الجانبي
+pdfjs-document-outline-button =
+ .title = اعرض فهرس المستند (نقر مزدوج لتمديد أو تقليص كل العناصر)
+pdfjs-document-outline-button-label = مخطط المستند
+pdfjs-attachments-button =
+ .title = اعرض المرفقات
+pdfjs-attachments-button-label = المُرفقات
+pdfjs-layers-button =
+ .title = اعرض الطبقات (انقر مرتين لتصفير كل الطبقات إلى الحالة المبدئية)
+pdfjs-layers-button-label = الطبقات
+pdfjs-thumbs-button =
+ .title = اعرض مُصغرات
+pdfjs-thumbs-button-label = مُصغّرات
+pdfjs-current-outline-item-button =
+ .title = ابحث عن عنصر المخطّط التفصيلي الحالي
+pdfjs-current-outline-item-button-label = عنصر المخطّط التفصيلي الحالي
+pdfjs-findbar-button =
+ .title = ابحث في المستند
+pdfjs-findbar-button-label = ابحث
+pdfjs-additional-layers = الطبقات الإضافية
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = صفحة { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = مصغّرة صفحة { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = ابحث
+ .placeholder = ابحث في المستند…
+pdfjs-find-previous-button =
+ .title = ابحث عن التّواجد السّابق للعبارة
+pdfjs-find-previous-button-label = السابق
+pdfjs-find-next-button =
+ .title = ابحث عن التّواجد التّالي للعبارة
+pdfjs-find-next-button-label = التالي
+pdfjs-find-highlight-checkbox = أبرِز الكل
+pdfjs-find-match-case-checkbox-label = طابق حالة الأحرف
+pdfjs-find-match-diacritics-checkbox-label = طابِق الحركات
+pdfjs-find-entire-word-checkbox-label = كلمات كاملة
+pdfjs-find-reached-top = تابعت من الأسفل بعدما وصلت إلى بداية المستند
+pdfjs-find-reached-bottom = تابعت من الأعلى بعدما وصلت إلى نهاية المستند
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [zero] لا مطابقة
+ [one] { $current } من أصل { $total } مطابقة
+ [two] { $current } من أصل { $total } مطابقة
+ [few] { $current } من أصل { $total } مطابقة
+ [many] { $current } من أصل { $total } مطابقة
+ *[other] { $current } من أصل { $total } مطابقة
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [zero] { $limit } مطابقة
+ [one] أكثر من { $limit } مطابقة
+ [two] أكثر من { $limit } مطابقة
+ [few] أكثر من { $limit } مطابقة
+ [many] أكثر من { $limit } مطابقة
+ *[other] أكثر من { $limit } مطابقات
+ }
+pdfjs-find-not-found = لا وجود للعبارة
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = عرض الصفحة
+pdfjs-page-scale-fit = ملائمة الصفحة
+pdfjs-page-scale-auto = تقريب تلقائي
+pdfjs-page-scale-actual = الحجم الفعلي
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }٪
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = صفحة { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = حدث عطل أثناء تحميل ملف PDF.
+pdfjs-invalid-file-error = ملف PDF تالف أو غير صحيح.
+pdfjs-missing-file-error = ملف PDF غير موجود.
+pdfjs-unexpected-response-error = استجابة خادوم غير متوقعة.
+pdfjs-rendering-error = حدث خطأ أثناء عرض الصفحة.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }، { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [تعليق { $type }]
+
+## Password
+
+pdfjs-password-label = أدخل لكلمة السر لفتح هذا الملف.
+pdfjs-password-invalid = كلمة سر خطأ. من فضلك أعد المحاولة.
+pdfjs-password-ok-button = حسنا
+pdfjs-password-cancel-button = ألغِ
+pdfjs-web-fonts-disabled = خطوط الوب مُعطّلة: تعذّر استخدام خطوط PDF المُضمّنة.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = نص
+pdfjs-editor-free-text-button-label = نص
+pdfjs-editor-ink-button =
+ .title = ارسم
+pdfjs-editor-ink-button-label = ارسم
+pdfjs-editor-stamp-button =
+ .title = أضِف أو حرّر الصور
+pdfjs-editor-stamp-button-label = أضِف أو حرّر الصور
+pdfjs-editor-highlight-button =
+ .title = أبرِز
+pdfjs-editor-highlight-button-label = أبرِز
+pdfjs-highlight-floating-button =
+ .title = أبرِز
+pdfjs-highlight-floating-button1 =
+ .title = أبرِز
+ .aria-label = أبرِز
+pdfjs-highlight-floating-button-label = أبرِز
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = أزِل الرسم
+pdfjs-editor-remove-freetext-button =
+ .title = أزِل النص
+pdfjs-editor-remove-stamp-button =
+ .title = أزِل الصورة
+pdfjs-editor-remove-highlight-button =
+ .title = أزِل الإبراز
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = اللون
+pdfjs-editor-free-text-size-input = الحجم
+pdfjs-editor-ink-color-input = اللون
+pdfjs-editor-ink-thickness-input = السماكة
+pdfjs-editor-ink-opacity-input = العتامة
+pdfjs-editor-stamp-add-image-button =
+ .title = أضِف صورة
+pdfjs-editor-stamp-add-image-button-label = أضِف صورة
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = السماكة
+pdfjs-editor-free-highlight-thickness-title =
+ .title = غيّر السُمك عند إبراز عناصر أُخرى غير النص
+pdfjs-free-text =
+ .aria-label = محرِّر النص
+pdfjs-free-text-default-content = ابدأ الكتابة…
+pdfjs-ink =
+ .aria-label = محرِّر الرسم
+pdfjs-ink-canvas =
+ .aria-label = صورة أنشأها المستخدم
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = نص بديل
+pdfjs-editor-alt-text-edit-button-label = تحرير النص البديل
+pdfjs-editor-alt-text-dialog-label = اختر خيار
+pdfjs-editor-alt-text-dialog-description = يساعد النص البديل عندما لا يتمكن الأشخاص من رؤية الصورة أو عندما لا يتم تحميلها.
+pdfjs-editor-alt-text-add-description-label = أضِف وصف
+pdfjs-editor-alt-text-add-description-description = استهدف جملتين تصفان الموضوع أو الإعداد أو الإجراءات.
+pdfjs-editor-alt-text-mark-decorative-label = علّمها على أنها زخرفية
+pdfjs-editor-alt-text-mark-decorative-description = يُستخدم هذا في الصور المزخرفة، مثل الحدود أو العلامات المائية.
+pdfjs-editor-alt-text-cancel-button = ألغِ
+pdfjs-editor-alt-text-save-button = احفظ
+pdfjs-editor-alt-text-decorative-tooltip = عُلّمت على أنها زخرفية
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = على سبيل المثال، "يجلس شاب على الطاولة لتناول وجبة"
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = الزاوية اليُسرى العُليا — غيّر الحجم
+pdfjs-editor-resizer-label-top-middle = أعلى الوسط - غيّر الحجم
+pdfjs-editor-resizer-label-top-right = الزاوية اليُمنى العُليا - غيّر الحجم
+pdfjs-editor-resizer-label-middle-right = اليمين الأوسط - غيّر الحجم
+pdfjs-editor-resizer-label-bottom-right = الزاوية اليُمنى السُفلى - غيّر الحجم
+pdfjs-editor-resizer-label-bottom-middle = أسفل الوسط - غيّر الحجم
+pdfjs-editor-resizer-label-bottom-left = الزاوية اليُسرى السُفلية - غيّر الحجم
+pdfjs-editor-resizer-label-middle-left = مُنتصف اليسار - غيّر الحجم
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = أبرِز اللون
+pdfjs-editor-colorpicker-button =
+ .title = غيّر اللون
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = اختيارات الألوان
+pdfjs-editor-colorpicker-yellow =
+ .title = أصفر
+pdfjs-editor-colorpicker-green =
+ .title = أخضر
+pdfjs-editor-colorpicker-blue =
+ .title = أزرق
+pdfjs-editor-colorpicker-pink =
+ .title = وردي
+pdfjs-editor-colorpicker-red =
+ .title = أحمر
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = أظهِر الكل
+pdfjs-editor-highlight-show-all-button =
+ .title = أظهِر الكل
diff --git a/web/locale/ast/viewer.ftl b/web/locale/ast/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..2503cafceeab4079cc3098c15fb755bda9b8a02c
--- /dev/null
+++ b/web/locale/ast/viewer.ftl
@@ -0,0 +1,201 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Páxina anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Páxina siguiente
+pdfjs-next-button-label = Siguiente
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Páxina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Alloñar
+pdfjs-zoom-out-button-label = Alloña
+pdfjs-zoom-in-button =
+ .title = Averar
+pdfjs-zoom-in-button-label = Avera
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Cambiar al mou de presentación
+pdfjs-presentation-mode-button-label = Mou de presentación
+pdfjs-open-file-button-label = Abrir
+pdfjs-print-button =
+ .title = Imprentar
+pdfjs-print-button-label = Imprentar
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Ferramientes
+pdfjs-tools-button-label = Ferramientes
+pdfjs-first-page-button-label = Dir a la primer páxina
+pdfjs-last-page-button-label = Dir a la última páxina
+pdfjs-page-rotate-cw-button =
+ .title = Voltia a la derecha
+pdfjs-page-rotate-cw-button-label = Voltiar a la derecha
+pdfjs-page-rotate-ccw-button =
+ .title = Voltia a la esquierda
+pdfjs-page-rotate-ccw-button-label = Voltiar a la esquierda
+pdfjs-cursor-text-select-tool-button =
+ .title = Activa la ferramienta d'esbilla de testu
+pdfjs-cursor-text-select-tool-button-label = Ferramienta d'esbilla de testu
+pdfjs-cursor-hand-tool-button =
+ .title = Activa la ferramienta de mano
+pdfjs-cursor-hand-tool-button-label = Ferramienta de mano
+pdfjs-scroll-vertical-button =
+ .title = Usa'l desplazamientu vertical
+pdfjs-scroll-vertical-button-label = Desplazamientu vertical
+pdfjs-scroll-horizontal-button =
+ .title = Usa'l desplazamientu horizontal
+pdfjs-scroll-horizontal-button-label = Desplazamientu horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Usa'l desplazamientu continuu
+pdfjs-scroll-wrapped-button-label = Desplazamientu continuu
+pdfjs-spread-none-button-label = Fueyes individuales
+pdfjs-spread-odd-button-label = Fueyes pares
+pdfjs-spread-even-button-label = Fueyes impares
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propiedaes del documentu…
+pdfjs-document-properties-button-label = Propiedaes del documentu…
+pdfjs-document-properties-file-name = Nome del ficheru:
+pdfjs-document-properties-file-size = Tamañu del ficheru:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Títulu:
+pdfjs-document-properties-keywords = Pallabres clave:
+pdfjs-document-properties-creation-date = Data de creación:
+pdfjs-document-properties-modification-date = Data de modificación:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-producer = Productor del PDF:
+pdfjs-document-properties-version = Versión del PDF:
+pdfjs-document-properties-page-count = Númberu de páxines:
+pdfjs-document-properties-page-size = Tamañu de páxina:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertical
+pdfjs-document-properties-page-size-orientation-landscape = horizontal
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista web rápida:
+pdfjs-document-properties-linearized-yes = Sí
+pdfjs-document-properties-linearized-no = Non
+pdfjs-document-properties-close-button = Zarrar
+
+## Print
+
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Encaboxar
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Alternar la barra llateral
+pdfjs-attachments-button =
+ .title = Amosar los axuntos
+pdfjs-attachments-button-label = Axuntos
+pdfjs-layers-button-label = Capes
+pdfjs-thumbs-button =
+ .title = Amosar les miniatures
+pdfjs-thumbs-button-label = Miniatures
+pdfjs-findbar-button-label = Atopar
+pdfjs-additional-layers = Capes adicionales
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Páxina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-previous-button-label = Anterior
+pdfjs-find-next-button-label = Siguiente
+pdfjs-find-entire-word-checkbox-label = Pallabres completes
+pdfjs-find-reached-top = Algamóse'l comienzu de la páxina, síguese dende abaxo
+pdfjs-find-reached-bottom = Algamóse la fin del documentu, síguese dende arriba
+
+## Predefined zoom values
+
+pdfjs-page-scale-auto = Zoom automáticu
+pdfjs-page-scale-actual = Tamañu real
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Páxina { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Asocedió un fallu mentanto se cargaba'l PDF.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+
+## Password
+
+pdfjs-password-ok-button = Aceptar
+pdfjs-password-cancel-button = Encaboxar
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/az/viewer.ftl b/web/locale/az/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..773aae4d62743873e0a80771342a020fd6701c6d
--- /dev/null
+++ b/web/locale/az/viewer.ftl
@@ -0,0 +1,257 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Əvvəlki səhifə
+pdfjs-previous-button-label = Əvvəlkini tap
+pdfjs-next-button =
+ .title = Növbəti səhifə
+pdfjs-next-button-label = İrəli
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Səhifə
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = / { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Uzaqlaş
+pdfjs-zoom-out-button-label = Uzaqlaş
+pdfjs-zoom-in-button =
+ .title = Yaxınlaş
+pdfjs-zoom-in-button-label = Yaxınlaş
+pdfjs-zoom-select =
+ .title = Yaxınlaşdırma
+pdfjs-presentation-mode-button =
+ .title = Təqdimat Rejiminə Keç
+pdfjs-presentation-mode-button-label = Təqdimat Rejimi
+pdfjs-open-file-button =
+ .title = Fayl Aç
+pdfjs-open-file-button-label = Aç
+pdfjs-print-button =
+ .title = Yazdır
+pdfjs-print-button-label = Yazdır
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Alətlər
+pdfjs-tools-button-label = Alətlər
+pdfjs-first-page-button =
+ .title = İlk Səhifəyə get
+pdfjs-first-page-button-label = İlk Səhifəyə get
+pdfjs-last-page-button =
+ .title = Son Səhifəyə get
+pdfjs-last-page-button-label = Son Səhifəyə get
+pdfjs-page-rotate-cw-button =
+ .title = Saat İstiqamətində Fırlat
+pdfjs-page-rotate-cw-button-label = Saat İstiqamətində Fırlat
+pdfjs-page-rotate-ccw-button =
+ .title = Saat İstiqamətinin Əksinə Fırlat
+pdfjs-page-rotate-ccw-button-label = Saat İstiqamətinin Əksinə Fırlat
+pdfjs-cursor-text-select-tool-button =
+ .title = Yazı seçmə alətini aktivləşdir
+pdfjs-cursor-text-select-tool-button-label = Yazı seçmə aləti
+pdfjs-cursor-hand-tool-button =
+ .title = Əl alətini aktivləşdir
+pdfjs-cursor-hand-tool-button-label = Əl aləti
+pdfjs-scroll-vertical-button =
+ .title = Şaquli sürüşdürmə işlət
+pdfjs-scroll-vertical-button-label = Şaquli sürüşdürmə
+pdfjs-scroll-horizontal-button =
+ .title = Üfüqi sürüşdürmə işlət
+pdfjs-scroll-horizontal-button-label = Üfüqi sürüşdürmə
+pdfjs-scroll-wrapped-button =
+ .title = Bükülü sürüşdürmə işlət
+pdfjs-scroll-wrapped-button-label = Bükülü sürüşdürmə
+pdfjs-spread-none-button =
+ .title = Yan-yana birləşdirilmiş səhifələri işlətmə
+pdfjs-spread-none-button-label = Birləşdirmə
+pdfjs-spread-odd-button =
+ .title = Yan-yana birləşdirilmiş səhifələri tək nömrəli səhifələrdən başlat
+pdfjs-spread-odd-button-label = Tək nömrəli
+pdfjs-spread-even-button =
+ .title = Yan-yana birləşdirilmiş səhifələri cüt nömrəli səhifələrdən başlat
+pdfjs-spread-even-button-label = Cüt nömrəli
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Sənəd xüsusiyyətləri…
+pdfjs-document-properties-button-label = Sənəd xüsusiyyətləri…
+pdfjs-document-properties-file-name = Fayl adı:
+pdfjs-document-properties-file-size = Fayl ölçüsü:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bayt)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bayt)
+pdfjs-document-properties-title = Başlık:
+pdfjs-document-properties-author = Müəllif:
+pdfjs-document-properties-subject = Mövzu:
+pdfjs-document-properties-keywords = Açar sözlər:
+pdfjs-document-properties-creation-date = Yaradılış Tarixi :
+pdfjs-document-properties-modification-date = Dəyişdirilmə Tarixi :
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Yaradan:
+pdfjs-document-properties-producer = PDF yaradıcısı:
+pdfjs-document-properties-version = PDF versiyası:
+pdfjs-document-properties-page-count = Səhifə sayı:
+pdfjs-document-properties-page-size = Səhifə Ölçüsü:
+pdfjs-document-properties-page-size-unit-inches = inç
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portret
+pdfjs-document-properties-page-size-orientation-landscape = albom
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Məktub
+pdfjs-document-properties-page-size-name-legal = Hüquqi
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = Bəli
+pdfjs-document-properties-linearized-no = Xeyr
+pdfjs-document-properties-close-button = Qapat
+
+## Print
+
+pdfjs-print-progress-message = Sənəd çap üçün hazırlanır…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Ləğv et
+pdfjs-printing-not-supported = Xəbərdarlıq: Çap bu səyyah tərəfindən tam olaraq dəstəklənmir.
+pdfjs-printing-not-ready = Xəbərdarlıq: PDF çap üçün tam yüklənməyib.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Yan Paneli Aç/Bağla
+pdfjs-toggle-sidebar-notification-button =
+ .title = Yan paneli çevir (sənəddə icmal/bağlamalar/laylar mövcuddur)
+pdfjs-toggle-sidebar-button-label = Yan Paneli Aç/Bağla
+pdfjs-document-outline-button =
+ .title = Sənədin eskizini göstər (bütün bəndləri açmaq/yığmaq üçün iki dəfə klikləyin)
+pdfjs-document-outline-button-label = Sənəd strukturu
+pdfjs-attachments-button =
+ .title = Bağlamaları göstər
+pdfjs-attachments-button-label = Bağlamalar
+pdfjs-layers-button =
+ .title = Layları göstər (bütün layları ilkin halına sıfırlamaq üçün iki dəfə klikləyin)
+pdfjs-layers-button-label = Laylar
+pdfjs-thumbs-button =
+ .title = Kiçik şəkilləri göstər
+pdfjs-thumbs-button-label = Kiçik şəkillər
+pdfjs-findbar-button =
+ .title = Sənəddə Tap
+pdfjs-findbar-button-label = Tap
+pdfjs-additional-layers = Əlavə laylar
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Səhifə{ $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } səhifəsinin kiçik vəziyyəti
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Tap
+ .placeholder = Sənəddə tap…
+pdfjs-find-previous-button =
+ .title = Bir öncəki uyğun gələn sözü tapır
+pdfjs-find-previous-button-label = Geri
+pdfjs-find-next-button =
+ .title = Bir sonrakı uyğun gələn sözü tapır
+pdfjs-find-next-button-label = İrəli
+pdfjs-find-highlight-checkbox = İşarələ
+pdfjs-find-match-case-checkbox-label = Böyük/kiçik hərfə həssaslıq
+pdfjs-find-entire-word-checkbox-label = Tam sözlər
+pdfjs-find-reached-top = Sənədin yuxarısına çatdı, aşağıdan davam edir
+pdfjs-find-reached-bottom = Sənədin sonuna çatdı, yuxarıdan davam edir
+pdfjs-find-not-found = Uyğunlaşma tapılmadı
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Səhifə genişliyi
+pdfjs-page-scale-fit = Səhifəni sığdır
+pdfjs-page-scale-auto = Avtomatik yaxınlaşdır
+pdfjs-page-scale-actual = Hazırkı Həcm
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF yüklenərkən bir səhv yarandı.
+pdfjs-invalid-file-error = Səhv və ya zədələnmiş olmuş PDF fayl.
+pdfjs-missing-file-error = PDF fayl yoxdur.
+pdfjs-unexpected-response-error = Gözlənilməz server cavabı.
+pdfjs-rendering-error = Səhifə göstərilərkən səhv yarandı.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotasiyası]
+
+## Password
+
+pdfjs-password-label = Bu PDF faylı açmaq üçün parolu daxil edin.
+pdfjs-password-invalid = Parol səhvdir. Bir daha yoxlayın.
+pdfjs-password-ok-button = Tamam
+pdfjs-password-cancel-button = Ləğv et
+pdfjs-web-fonts-disabled = Web Şriftlər söndürülüb: yerləşdirilmiş PDF şriftlərini istifadə etmək mümkün deyil.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/be/viewer.ftl b/web/locale/be/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..ee1f430104d1b6ecc7c2e518eb8360707283aeb9
--- /dev/null
+++ b/web/locale/be/viewer.ftl
@@ -0,0 +1,404 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Папярэдняя старонка
+pdfjs-previous-button-label = Папярэдняя
+pdfjs-next-button =
+ .title = Наступная старонка
+pdfjs-next-button-label = Наступная
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Старонка
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = з { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } з { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Паменшыць
+pdfjs-zoom-out-button-label = Паменшыць
+pdfjs-zoom-in-button =
+ .title = Павялічыць
+pdfjs-zoom-in-button-label = Павялічыць
+pdfjs-zoom-select =
+ .title = Павялічэнне тэксту
+pdfjs-presentation-mode-button =
+ .title = Пераключыцца ў рэжым паказу
+pdfjs-presentation-mode-button-label = Рэжым паказу
+pdfjs-open-file-button =
+ .title = Адкрыць файл
+pdfjs-open-file-button-label = Адкрыць
+pdfjs-print-button =
+ .title = Друкаваць
+pdfjs-print-button-label = Друкаваць
+pdfjs-save-button =
+ .title = Захаваць
+pdfjs-save-button-label = Захаваць
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Сцягнуць
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Сцягнуць
+pdfjs-bookmark-button =
+ .title = Дзейная старонка (паглядзець URL-адрас з дзейнай старонкі)
+pdfjs-bookmark-button-label = Цяперашняя старонка
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Адкрыць у праграме
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Адкрыць у праграме
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Прылады
+pdfjs-tools-button-label = Прылады
+pdfjs-first-page-button =
+ .title = Перайсці на першую старонку
+pdfjs-first-page-button-label = Перайсці на першую старонку
+pdfjs-last-page-button =
+ .title = Перайсці на апошнюю старонку
+pdfjs-last-page-button-label = Перайсці на апошнюю старонку
+pdfjs-page-rotate-cw-button =
+ .title = Павярнуць па сонцу
+pdfjs-page-rotate-cw-button-label = Павярнуць па сонцу
+pdfjs-page-rotate-ccw-button =
+ .title = Павярнуць супраць сонца
+pdfjs-page-rotate-ccw-button-label = Павярнуць супраць сонца
+pdfjs-cursor-text-select-tool-button =
+ .title = Уключыць прыладу выбару тэксту
+pdfjs-cursor-text-select-tool-button-label = Прылада выбару тэксту
+pdfjs-cursor-hand-tool-button =
+ .title = Уключыць ручную прыладу
+pdfjs-cursor-hand-tool-button-label = Ручная прылада
+pdfjs-scroll-page-button =
+ .title = Выкарыстоўваць пракрутку старонкi
+pdfjs-scroll-page-button-label = Пракрутка старонкi
+pdfjs-scroll-vertical-button =
+ .title = Ужываць вертыкальную пракрутку
+pdfjs-scroll-vertical-button-label = Вертыкальная пракрутка
+pdfjs-scroll-horizontal-button =
+ .title = Ужываць гарызантальную пракрутку
+pdfjs-scroll-horizontal-button-label = Гарызантальная пракрутка
+pdfjs-scroll-wrapped-button =
+ .title = Ужываць маштабавальную пракрутку
+pdfjs-scroll-wrapped-button-label = Маштабавальная пракрутка
+pdfjs-spread-none-button =
+ .title = Не выкарыстоўваць разгорнутыя старонкі
+pdfjs-spread-none-button-label = Без разгорнутых старонак
+pdfjs-spread-odd-button =
+ .title = Разгорнутыя старонкі пачынаючы з няцотных нумароў
+pdfjs-spread-odd-button-label = Няцотныя старонкі злева
+pdfjs-spread-even-button =
+ .title = Разгорнутыя старонкі пачынаючы з цотных нумароў
+pdfjs-spread-even-button-label = Цотныя старонкі злева
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Уласцівасці дакумента…
+pdfjs-document-properties-button-label = Уласцівасці дакумента…
+pdfjs-document-properties-file-name = Назва файла:
+pdfjs-document-properties-file-size = Памер файла:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байт)
+pdfjs-document-properties-title = Загаловак:
+pdfjs-document-properties-author = Аўтар:
+pdfjs-document-properties-subject = Тэма:
+pdfjs-document-properties-keywords = Ключавыя словы:
+pdfjs-document-properties-creation-date = Дата стварэння:
+pdfjs-document-properties-modification-date = Дата змянення:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Стваральнік:
+pdfjs-document-properties-producer = Вырабнік PDF:
+pdfjs-document-properties-version = Версія PDF:
+pdfjs-document-properties-page-count = Колькасць старонак:
+pdfjs-document-properties-page-size = Памер старонкі:
+pdfjs-document-properties-page-size-unit-inches = цаляў
+pdfjs-document-properties-page-size-unit-millimeters = мм
+pdfjs-document-properties-page-size-orientation-portrait = кніжная
+pdfjs-document-properties-page-size-orientation-landscape = альбомная
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Хуткі прагляд у Інтэрнэце:
+pdfjs-document-properties-linearized-yes = Так
+pdfjs-document-properties-linearized-no = Не
+pdfjs-document-properties-close-button = Закрыць
+
+## Print
+
+pdfjs-print-progress-message = Падрыхтоўка дакумента да друку…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Скасаваць
+pdfjs-printing-not-supported = Папярэджанне: друк не падтрымліваецца цалкам гэтым браўзерам.
+pdfjs-printing-not-ready = Увага: PDF не сцягнуты цалкам для друкавання.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Паказаць/схаваць бакавую панэль
+pdfjs-toggle-sidebar-notification-button =
+ .title = Паказаць/схаваць бакавую панэль (дакумент мае змест/укладанні/пласты)
+pdfjs-toggle-sidebar-button-label = Паказаць/схаваць бакавую панэль
+pdfjs-document-outline-button =
+ .title = Паказаць структуру дакумента (двайная пстрычка, каб разгарнуць /згарнуць усе элементы)
+pdfjs-document-outline-button-label = Структура дакумента
+pdfjs-attachments-button =
+ .title = Паказаць далучэнні
+pdfjs-attachments-button-label = Далучэнні
+pdfjs-layers-button =
+ .title = Паказаць пласты (націсніце двойчы, каб скінуць усе пласты да прадвызначанага стану)
+pdfjs-layers-button-label = Пласты
+pdfjs-thumbs-button =
+ .title = Паказ мініяцюр
+pdfjs-thumbs-button-label = Мініяцюры
+pdfjs-current-outline-item-button =
+ .title = Знайсці бягучы элемент структуры
+pdfjs-current-outline-item-button-label = Бягучы элемент структуры
+pdfjs-findbar-button =
+ .title = Пошук у дакуменце
+pdfjs-findbar-button-label = Знайсці
+pdfjs-additional-layers = Дадатковыя пласты
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Старонка { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Мініяцюра старонкі { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Шукаць
+ .placeholder = Шукаць у дакуменце…
+pdfjs-find-previous-button =
+ .title = Знайсці папярэдні выпадак выразу
+pdfjs-find-previous-button-label = Папярэдні
+pdfjs-find-next-button =
+ .title = Знайсці наступны выпадак выразу
+pdfjs-find-next-button-label = Наступны
+pdfjs-find-highlight-checkbox = Падфарбаваць усе
+pdfjs-find-match-case-checkbox-label = Адрозніваць вялікія/малыя літары
+pdfjs-find-match-diacritics-checkbox-label = З улікам дыякрытык
+pdfjs-find-entire-word-checkbox-label = Словы цалкам
+pdfjs-find-reached-top = Дасягнуты пачатак дакумента, працяг з канца
+pdfjs-find-reached-bottom = Дасягнуты канец дакумента, працяг з пачатку
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } з { $total } супадзенняў
+ [few] { $current } з { $total } супадзенняў
+ *[many] { $current } з { $total } супадзенняў
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Больш за { $limit } супадзенне
+ [few] Больш за { $limit } супадзенні
+ *[many] Больш за { $limit } супадзенняў
+ }
+pdfjs-find-not-found = Выраз не знойдзены
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Шырыня старонкі
+pdfjs-page-scale-fit = Уцісненне старонкі
+pdfjs-page-scale-auto = Аўтаматычнае павелічэнне
+pdfjs-page-scale-actual = Сапраўдны памер
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Старонка { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Здарылася памылка ў часе загрузкі PDF.
+pdfjs-invalid-file-error = Няспраўны або пашкоджаны файл PDF.
+pdfjs-missing-file-error = Адсутны файл PDF.
+pdfjs-unexpected-response-error = Нечаканы адказ сервера.
+pdfjs-rendering-error = Здарылася памылка падчас адлюстравання старонкі.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = Увядзіце пароль, каб адкрыць гэты файл PDF.
+pdfjs-password-invalid = Нядзейсны пароль. Паспрабуйце зноў.
+pdfjs-password-ok-button = Добра
+pdfjs-password-cancel-button = Скасаваць
+pdfjs-web-fonts-disabled = Шрыфты Сеціва забаронены: немагчыма ўжываць укладзеныя шрыфты PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Тэкст
+pdfjs-editor-free-text-button-label = Тэкст
+pdfjs-editor-ink-button =
+ .title = Маляваць
+pdfjs-editor-ink-button-label = Маляваць
+pdfjs-editor-stamp-button =
+ .title = Дадаць або змяніць выявы
+pdfjs-editor-stamp-button-label = Дадаць або змяніць выявы
+pdfjs-editor-highlight-button =
+ .title = Вылучэнне
+pdfjs-editor-highlight-button-label = Вылучэнне
+pdfjs-highlight-floating-button =
+ .title = Вылучэнне
+pdfjs-highlight-floating-button1 =
+ .title = Падфарбаваць
+ .aria-label = Падфарбаваць
+pdfjs-highlight-floating-button-label = Падфарбаваць
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Выдаліць малюнак
+pdfjs-editor-remove-freetext-button =
+ .title = Выдаліць тэкст
+pdfjs-editor-remove-stamp-button =
+ .title = Выдаліць выяву
+pdfjs-editor-remove-highlight-button =
+ .title = Выдаліць падфарбоўку
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Колер
+pdfjs-editor-free-text-size-input = Памер
+pdfjs-editor-ink-color-input = Колер
+pdfjs-editor-ink-thickness-input = Таўшчыня
+pdfjs-editor-ink-opacity-input = Непразрыстасць
+pdfjs-editor-stamp-add-image-button =
+ .title = Дадаць выяву
+pdfjs-editor-stamp-add-image-button-label = Дадаць выяву
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Таўшчыня
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Змяняць таўшчыню пры вылучэнні іншых элементаў, акрамя тэксту
+pdfjs-free-text =
+ .aria-label = Тэкставы рэдактар
+pdfjs-free-text-default-content = Пачніце набор тэксту…
+pdfjs-ink =
+ .aria-label = Графічны рэдактар
+pdfjs-ink-canvas =
+ .aria-label = Выява, створаная карыстальнікам
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Альтэрнатыўны тэкст
+pdfjs-editor-alt-text-edit-button-label = Змяніць альтэрнатыўны тэкст
+pdfjs-editor-alt-text-dialog-label = Выберыце варыянт
+pdfjs-editor-alt-text-dialog-description = Альтэрнатыўны тэкст дапамагае, калі людзі не бачаць выяву або калі яна не загружаецца.
+pdfjs-editor-alt-text-add-description-label = Дадаць апісанне
+pdfjs-editor-alt-text-add-description-description = Старайцеся скласці 1-2 сказы, якія апісваюць прадмет, абстаноўку або дзеянні.
+pdfjs-editor-alt-text-mark-decorative-label = Пазначыць як дэкаратыўны
+pdfjs-editor-alt-text-mark-decorative-description = Выкарыстоўваецца для дэкаратыўных выяваў, такіх як рамкі або вадзяныя знакі.
+pdfjs-editor-alt-text-cancel-button = Скасаваць
+pdfjs-editor-alt-text-save-button = Захаваць
+pdfjs-editor-alt-text-decorative-tooltip = Пазначаны як дэкаратыўны
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Напрыклад, «Малады чалавек садзіцца за стол есці»
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Верхні левы кут — змяніць памер
+pdfjs-editor-resizer-label-top-middle = Уверсе пасярэдзіне — змяніць памер
+pdfjs-editor-resizer-label-top-right = Верхні правы кут — змяніць памер
+pdfjs-editor-resizer-label-middle-right = Пасярэдзіне справа — змяніць памер
+pdfjs-editor-resizer-label-bottom-right = Правы ніжні кут — змяніць памер
+pdfjs-editor-resizer-label-bottom-middle = Пасярэдзіне ўнізе — змяніць памер
+pdfjs-editor-resizer-label-bottom-left = Левы ніжні кут — змяніць памер
+pdfjs-editor-resizer-label-middle-left = Пасярэдзіне злева — змяніць памер
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Колер падфарбоўкі
+pdfjs-editor-colorpicker-button =
+ .title = Змяніць колер
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Выбар колеру
+pdfjs-editor-colorpicker-yellow =
+ .title = Жоўты
+pdfjs-editor-colorpicker-green =
+ .title = Зялёны
+pdfjs-editor-colorpicker-blue =
+ .title = Блакітны
+pdfjs-editor-colorpicker-pink =
+ .title = Ружовы
+pdfjs-editor-colorpicker-red =
+ .title = Чырвоны
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Паказаць усе
+pdfjs-editor-highlight-show-all-button =
+ .title = Паказаць усе
diff --git a/web/locale/bg/viewer.ftl b/web/locale/bg/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..7522054c024d99ae7a26f08e0816c64bc779e533
--- /dev/null
+++ b/web/locale/bg/viewer.ftl
@@ -0,0 +1,384 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Предишна страница
+pdfjs-previous-button-label = Предишна
+pdfjs-next-button =
+ .title = Следваща страница
+pdfjs-next-button-label = Следваща
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Страница
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = от { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } от { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Намаляване
+pdfjs-zoom-out-button-label = Намаляване
+pdfjs-zoom-in-button =
+ .title = Увеличаване
+pdfjs-zoom-in-button-label = Увеличаване
+pdfjs-zoom-select =
+ .title = Мащабиране
+pdfjs-presentation-mode-button =
+ .title = Превключване към режим на представяне
+pdfjs-presentation-mode-button-label = Режим на представяне
+pdfjs-open-file-button =
+ .title = Отваряне на файл
+pdfjs-open-file-button-label = Отваряне
+pdfjs-print-button =
+ .title = Отпечатване
+pdfjs-print-button-label = Отпечатване
+pdfjs-save-button =
+ .title = Запазване
+pdfjs-save-button-label = Запазване
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Изтегляне
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Изтегляне
+pdfjs-bookmark-button =
+ .title = Текуща страница (преглед на адреса на страницата)
+pdfjs-bookmark-button-label = Текуща страница
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Отваряне в приложение
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Отваряне в приложение
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Инструменти
+pdfjs-tools-button-label = Инструменти
+pdfjs-first-page-button =
+ .title = Към първата страница
+pdfjs-first-page-button-label = Към първата страница
+pdfjs-last-page-button =
+ .title = Към последната страница
+pdfjs-last-page-button-label = Към последната страница
+pdfjs-page-rotate-cw-button =
+ .title = Завъртане по час. стрелка
+pdfjs-page-rotate-cw-button-label = Завъртане по часовниковата стрелка
+pdfjs-page-rotate-ccw-button =
+ .title = Завъртане обратно на час. стрелка
+pdfjs-page-rotate-ccw-button-label = Завъртане обратно на часовниковата стрелка
+pdfjs-cursor-text-select-tool-button =
+ .title = Включване на инструмента за избор на текст
+pdfjs-cursor-text-select-tool-button-label = Инструмент за избор на текст
+pdfjs-cursor-hand-tool-button =
+ .title = Включване на инструмента ръка
+pdfjs-cursor-hand-tool-button-label = Инструмент ръка
+pdfjs-scroll-page-button =
+ .title = Използване на плъзгане на страници
+pdfjs-scroll-page-button-label = Плъзгане на страници
+pdfjs-scroll-vertical-button =
+ .title = Използване на вертикално плъзгане
+pdfjs-scroll-vertical-button-label = Вертикално плъзгане
+pdfjs-scroll-horizontal-button =
+ .title = Използване на хоризонтално
+pdfjs-scroll-horizontal-button-label = Хоризонтално плъзгане
+pdfjs-scroll-wrapped-button =
+ .title = Използване на мащабируемо плъзгане
+pdfjs-scroll-wrapped-button-label = Мащабируемо плъзгане
+pdfjs-spread-none-button =
+ .title = Режимът на сдвояване е изключен
+pdfjs-spread-none-button-label = Без сдвояване
+pdfjs-spread-odd-button =
+ .title = Сдвояване, започвайки от нечетните страници
+pdfjs-spread-odd-button-label = Нечетните отляво
+pdfjs-spread-even-button =
+ .title = Сдвояване, започвайки от четните страници
+pdfjs-spread-even-button-label = Четните отляво
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Свойства на документа…
+pdfjs-document-properties-button-label = Свойства на документа…
+pdfjs-document-properties-file-name = Име на файл:
+pdfjs-document-properties-file-size = Големина на файл:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байта)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байта)
+pdfjs-document-properties-title = Заглавие:
+pdfjs-document-properties-author = Автор:
+pdfjs-document-properties-subject = Тема:
+pdfjs-document-properties-keywords = Ключови думи:
+pdfjs-document-properties-creation-date = Дата на създаване:
+pdfjs-document-properties-modification-date = Дата на промяна:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Създател:
+pdfjs-document-properties-producer = PDF произведен от:
+pdfjs-document-properties-version = Издание на PDF:
+pdfjs-document-properties-page-count = Брой страници:
+pdfjs-document-properties-page-size = Размер на страницата:
+pdfjs-document-properties-page-size-unit-inches = инч
+pdfjs-document-properties-page-size-unit-millimeters = мм
+pdfjs-document-properties-page-size-orientation-portrait = портрет
+pdfjs-document-properties-page-size-orientation-landscape = пейзаж
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Правни въпроси
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Бърз преглед:
+pdfjs-document-properties-linearized-yes = Да
+pdfjs-document-properties-linearized-no = Не
+pdfjs-document-properties-close-button = Затваряне
+
+## Print
+
+pdfjs-print-progress-message = Подготвяне на документа за отпечатване…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Отказ
+pdfjs-printing-not-supported = Внимание: Този четец няма пълна поддръжка на отпечатване.
+pdfjs-printing-not-ready = Внимание: Този PDF файл не е напълно зареден за печат.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Превключване на страничната лента
+pdfjs-toggle-sidebar-notification-button =
+ .title = Превключване на страничната лента (документът има структура/прикачени файлове/слоеве)
+pdfjs-toggle-sidebar-button-label = Превключване на страничната лента
+pdfjs-document-outline-button =
+ .title = Показване на структурата на документа (двукратно щракване за свиване/разгъване на всичко)
+pdfjs-document-outline-button-label = Структура на документа
+pdfjs-attachments-button =
+ .title = Показване на притурките
+pdfjs-attachments-button-label = Притурки
+pdfjs-layers-button =
+ .title = Показване на слоевете (двукратно щракване за възстановяване на всички слоеве към състоянието по подразбиране)
+pdfjs-layers-button-label = Слоеве
+pdfjs-thumbs-button =
+ .title = Показване на миниатюрите
+pdfjs-thumbs-button-label = Миниатюри
+pdfjs-current-outline-item-button =
+ .title = Намиране на текущия елемент от структурата
+pdfjs-current-outline-item-button-label = Текущ елемент от структурата
+pdfjs-findbar-button =
+ .title = Намиране в документа
+pdfjs-findbar-button-label = Търсене
+pdfjs-additional-layers = Допълнителни слоеве
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Страница { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Миниатюра на страница { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Търсене
+ .placeholder = Търсене в документа…
+pdfjs-find-previous-button =
+ .title = Намиране на предишно съвпадение на фразата
+pdfjs-find-previous-button-label = Предишна
+pdfjs-find-next-button =
+ .title = Намиране на следващо съвпадение на фразата
+pdfjs-find-next-button-label = Следваща
+pdfjs-find-highlight-checkbox = Открояване на всички
+pdfjs-find-match-case-checkbox-label = Съвпадение на регистъра
+pdfjs-find-match-diacritics-checkbox-label = Без производни букви
+pdfjs-find-entire-word-checkbox-label = Цели думи
+pdfjs-find-reached-top = Достигнато е началото на документа, продължаване от края
+pdfjs-find-reached-bottom = Достигнат е краят на документа, продължаване от началото
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } от { $total } съвпадение
+ *[other] { $current } от { $total } съвпадения
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Повече от { $limit } съвпадение
+ *[other] Повече от { $limit } съвпадения
+ }
+pdfjs-find-not-found = Фразата не е намерена
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Ширина на страницата
+pdfjs-page-scale-fit = Вместване в страницата
+pdfjs-page-scale-auto = Автоматично мащабиране
+pdfjs-page-scale-actual = Действителен размер
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Страница { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Получи се грешка при зареждане на PDF-а.
+pdfjs-invalid-file-error = Невалиден или повреден PDF файл.
+pdfjs-missing-file-error = Липсващ PDF файл.
+pdfjs-unexpected-response-error = Неочакван отговор от сървъра.
+pdfjs-rendering-error = Грешка при изчертаване на страницата.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Анотация { $type }]
+
+## Password
+
+pdfjs-password-label = Въведете парола за отваряне на този PDF файл.
+pdfjs-password-invalid = Невалидна парола. Моля, опитайте отново.
+pdfjs-password-ok-button = Добре
+pdfjs-password-cancel-button = Отказ
+pdfjs-web-fonts-disabled = Уеб-шрифтовете са забранени: разрешаване на използването на вградените PDF шрифтове.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Текст
+pdfjs-editor-free-text-button-label = Текст
+pdfjs-editor-ink-button =
+ .title = Рисуване
+pdfjs-editor-ink-button-label = Рисуване
+pdfjs-editor-stamp-button =
+ .title = Добавяне или променяне на изображения
+pdfjs-editor-stamp-button-label = Добавяне или променяне на изображения
+pdfjs-editor-remove-button =
+ .title = Премахване
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Премахване на рисунката
+pdfjs-editor-remove-freetext-button =
+ .title = Премахване на текста
+pdfjs-editor-remove-stamp-button =
+ .title = Пермахване на изображението
+pdfjs-editor-remove-highlight-button =
+ .title = Премахване на открояването
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Цвят
+pdfjs-editor-free-text-size-input = Размер
+pdfjs-editor-ink-color-input = Цвят
+pdfjs-editor-ink-thickness-input = Дебелина
+pdfjs-editor-ink-opacity-input = Прозрачност
+pdfjs-editor-stamp-add-image-button =
+ .title = Добавяне на изображение
+pdfjs-editor-stamp-add-image-button-label = Добавяне на изображение
+pdfjs-free-text =
+ .aria-label = Текстов редактор
+pdfjs-free-text-default-content = Започнете да пишете…
+pdfjs-ink =
+ .aria-label = Промяна на рисунка
+pdfjs-ink-canvas =
+ .aria-label = Изображение, създадено от потребител
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Алтернативен текст
+pdfjs-editor-alt-text-edit-button-label = Промяна на алтернативния текст
+pdfjs-editor-alt-text-dialog-label = Изберете от възможностите
+pdfjs-editor-alt-text-dialog-description = Алтернативният текст помага на потребителите, когато не могат да видят изображението или то не се зарежда.
+pdfjs-editor-alt-text-add-description-label = Добавяне на описание
+pdfjs-editor-alt-text-add-description-description = Стремете се към 1-2 изречения, описващи предмета, настройката или действията.
+pdfjs-editor-alt-text-mark-decorative-label = Отбелязване като декоративно
+pdfjs-editor-alt-text-mark-decorative-description = Използва се за орнаменти или декоративни изображения, като контури и водни знаци.
+pdfjs-editor-alt-text-cancel-button = Отказ
+pdfjs-editor-alt-text-save-button = Запазване
+pdfjs-editor-alt-text-decorative-tooltip = Отбелязване като декоративно
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Например, „Млад мъж седи на маса и се храни“
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Горен ляв ъгъл — преоразмеряване
+pdfjs-editor-resizer-label-top-middle = Горе в средата — преоразмеряване
+pdfjs-editor-resizer-label-top-right = Горен десен ъгъл — преоразмеряване
+pdfjs-editor-resizer-label-middle-right = Дясно в средата — преоразмеряване
+pdfjs-editor-resizer-label-bottom-right = Долен десен ъгъл — преоразмеряване
+pdfjs-editor-resizer-label-bottom-middle = Долу в средата — преоразмеряване
+pdfjs-editor-resizer-label-bottom-left = Долен ляв ъгъл — преоразмеряване
+pdfjs-editor-resizer-label-middle-left = Ляво в средата — преоразмеряване
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Цвят на открояване
+pdfjs-editor-colorpicker-button =
+ .title = Промяна на цвят
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Избор на цвят
+pdfjs-editor-colorpicker-yellow =
+ .title = Жълто
+pdfjs-editor-colorpicker-green =
+ .title = Зелено
+pdfjs-editor-colorpicker-blue =
+ .title = Синьо
+pdfjs-editor-colorpicker-pink =
+ .title = Розово
+pdfjs-editor-colorpicker-red =
+ .title = Червено
diff --git a/web/locale/bn/viewer.ftl b/web/locale/bn/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..1e20ecb835e0b6bd0edd4d87579735aa879d2197
--- /dev/null
+++ b/web/locale/bn/viewer.ftl
@@ -0,0 +1,247 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = পূর্ববর্তী পাতা
+pdfjs-previous-button-label = পূর্ববর্তী
+pdfjs-next-button =
+ .title = পরবর্তী পাতা
+pdfjs-next-button-label = পরবর্তী
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = পাতা
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } এর
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pagesCount } এর { $pageNumber })
+pdfjs-zoom-out-button =
+ .title = ছোট আকারে প্রদর্শন
+pdfjs-zoom-out-button-label = ছোট আকারে প্রদর্শন
+pdfjs-zoom-in-button =
+ .title = বড় আকারে প্রদর্শন
+pdfjs-zoom-in-button-label = বড় আকারে প্রদর্শন
+pdfjs-zoom-select =
+ .title = বড় আকারে প্রদর্শন
+pdfjs-presentation-mode-button =
+ .title = উপস্থাপনা মোডে স্যুইচ করুন
+pdfjs-presentation-mode-button-label = উপস্থাপনা মোড
+pdfjs-open-file-button =
+ .title = ফাইল খুলুন
+pdfjs-open-file-button-label = খুলুন
+pdfjs-print-button =
+ .title = মুদ্রণ
+pdfjs-print-button-label = মুদ্রণ
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = টুল
+pdfjs-tools-button-label = টুল
+pdfjs-first-page-button =
+ .title = প্রথম পাতায় যাও
+pdfjs-first-page-button-label = প্রথম পাতায় যাও
+pdfjs-last-page-button =
+ .title = শেষ পাতায় যাও
+pdfjs-last-page-button-label = শেষ পাতায় যাও
+pdfjs-page-rotate-cw-button =
+ .title = ঘড়ির কাঁটার দিকে ঘোরাও
+pdfjs-page-rotate-cw-button-label = ঘড়ির কাঁটার দিকে ঘোরাও
+pdfjs-page-rotate-ccw-button =
+ .title = ঘড়ির কাঁটার বিপরীতে ঘোরাও
+pdfjs-page-rotate-ccw-button-label = ঘড়ির কাঁটার বিপরীতে ঘোরাও
+pdfjs-cursor-text-select-tool-button =
+ .title = লেখা নির্বাচক টুল সক্রিয় করুন
+pdfjs-cursor-text-select-tool-button-label = লেখা নির্বাচক টুল
+pdfjs-cursor-hand-tool-button =
+ .title = হ্যান্ড টুল সক্রিয় করুন
+pdfjs-cursor-hand-tool-button-label = হ্যান্ড টুল
+pdfjs-scroll-vertical-button =
+ .title = উলম্ব স্ক্রলিং ব্যবহার করুন
+pdfjs-scroll-vertical-button-label = উলম্ব স্ক্রলিং
+pdfjs-scroll-horizontal-button =
+ .title = অনুভূমিক স্ক্রলিং ব্যবহার করুন
+pdfjs-scroll-horizontal-button-label = অনুভূমিক স্ক্রলিং
+pdfjs-scroll-wrapped-button =
+ .title = Wrapped স্ক্রোলিং ব্যবহার করুন
+pdfjs-scroll-wrapped-button-label = Wrapped স্ক্রোলিং
+pdfjs-spread-none-button =
+ .title = পেজ স্প্রেডগুলোতে যোগদান করবেন না
+pdfjs-spread-none-button-label = Spreads নেই
+pdfjs-spread-odd-button-label = বিজোড় Spreads
+pdfjs-spread-even-button-label = জোড় Spreads
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = নথি বৈশিষ্ট্য…
+pdfjs-document-properties-button-label = নথি বৈশিষ্ট্য…
+pdfjs-document-properties-file-name = ফাইলের নাম:
+pdfjs-document-properties-file-size = ফাইলের আকার:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } কেবি ({ $size_b } বাইট)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } এমবি ({ $size_b } বাইট)
+pdfjs-document-properties-title = শিরোনাম:
+pdfjs-document-properties-author = লেখক:
+pdfjs-document-properties-subject = বিষয়:
+pdfjs-document-properties-keywords = কীওয়ার্ড:
+pdfjs-document-properties-creation-date = তৈরির তারিখ:
+pdfjs-document-properties-modification-date = পরিবর্তনের তারিখ:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = প্রস্তুতকারক:
+pdfjs-document-properties-producer = পিডিএফ প্রস্তুতকারক:
+pdfjs-document-properties-version = পিডিএফ সংষ্করণ:
+pdfjs-document-properties-page-count = মোট পাতা:
+pdfjs-document-properties-page-size = পাতার সাইজ:
+pdfjs-document-properties-page-size-unit-inches = এর মধ্যে
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = উলম্ব
+pdfjs-document-properties-page-size-orientation-landscape = অনুভূমিক
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = লেটার
+pdfjs-document-properties-page-size-name-legal = লীগাল
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = হ্যাঁ
+pdfjs-document-properties-linearized-no = না
+pdfjs-document-properties-close-button = বন্ধ
+
+## Print
+
+pdfjs-print-progress-message = মুদ্রণের জন্য নথি প্রস্তুত করা হচ্ছে…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = বাতিল
+pdfjs-printing-not-supported = সতর্কতা: এই ব্রাউজারে মুদ্রণ সম্পূর্ণভাবে সমর্থিত নয়।
+pdfjs-printing-not-ready = সতর্কীকরণ: পিডিএফটি মুদ্রণের জন্য সম্পূর্ণ লোড হয়নি।
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = সাইডবার টগল করুন
+pdfjs-toggle-sidebar-button-label = সাইডবার টগল করুন
+pdfjs-document-outline-button =
+ .title = নথির আউটলাইন দেখাও (সব আইটেম প্রসারিত/সঙ্কুচিত করতে ডবল ক্লিক করুন)
+pdfjs-document-outline-button-label = নথির রূপরেখা
+pdfjs-attachments-button =
+ .title = সংযুক্তি দেখাও
+pdfjs-attachments-button-label = সংযুক্তি
+pdfjs-thumbs-button =
+ .title = থাম্বনেইল সমূহ প্রদর্শন করুন
+pdfjs-thumbs-button-label = থাম্বনেইল সমূহ
+pdfjs-findbar-button =
+ .title = নথির মধ্যে খুঁজুন
+pdfjs-findbar-button-label = খুঁজুন
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = পাতা { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } পাতার থাম্বনেইল
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = খুঁজুন
+ .placeholder = নথির মধ্যে খুঁজুন…
+pdfjs-find-previous-button =
+ .title = বাক্যাংশের পূর্ববর্তী উপস্থিতি অনুসন্ধান
+pdfjs-find-previous-button-label = পূর্ববর্তী
+pdfjs-find-next-button =
+ .title = বাক্যাংশের পরবর্তী উপস্থিতি অনুসন্ধান
+pdfjs-find-next-button-label = পরবর্তী
+pdfjs-find-highlight-checkbox = সব হাইলাইট করুন
+pdfjs-find-match-case-checkbox-label = অক্ষরের ছাঁদ মেলানো
+pdfjs-find-entire-word-checkbox-label = সম্পূর্ণ শব্দ
+pdfjs-find-reached-top = পাতার শুরুতে পৌছে গেছে, নীচ থেকে আরম্ভ করা হয়েছে
+pdfjs-find-reached-bottom = পাতার শেষে পৌছে গেছে, উপর থেকে আরম্ভ করা হয়েছে
+pdfjs-find-not-found = বাক্যাংশ পাওয়া যায়নি
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = পাতার প্রস্থ
+pdfjs-page-scale-fit = পাতা ফিট করুন
+pdfjs-page-scale-auto = স্বয়ংক্রিয় জুম
+pdfjs-page-scale-actual = প্রকৃত আকার
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = পিডিএফ লোড করার সময় ত্রুটি দেখা দিয়েছে।
+pdfjs-invalid-file-error = অকার্যকর অথবা ক্ষতিগ্রস্ত পিডিএফ ফাইল।
+pdfjs-missing-file-error = নিখোঁজ PDF ফাইল।
+pdfjs-unexpected-response-error = অপ্রত্যাশীত সার্ভার প্রতিক্রিয়া।
+pdfjs-rendering-error = পাতা উপস্থাপনার সময় ত্রুটি দেখা দিয়েছে।
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } টীকা]
+
+## Password
+
+pdfjs-password-label = পিডিএফ ফাইলটি ওপেন করতে পাসওয়ার্ড দিন।
+pdfjs-password-invalid = ভুল পাসওয়ার্ড। অনুগ্রহ করে আবার চেষ্টা করুন।
+pdfjs-password-ok-button = ঠিক আছে
+pdfjs-password-cancel-button = বাতিল
+pdfjs-web-fonts-disabled = ওয়েব ফন্ট নিষ্ক্রিয়: সংযুক্ত পিডিএফ ফন্ট ব্যবহার করা যাচ্ছে না।
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/bo/viewer.ftl b/web/locale/bo/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..824eab4ffbaa319d94aa9a1876efd8359ac0ac7f
--- /dev/null
+++ b/web/locale/bo/viewer.ftl
@@ -0,0 +1,247 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = དྲ་ངོས་སྔོན་མ
+pdfjs-previous-button-label = སྔོན་མ
+pdfjs-next-button =
+ .title = དྲ་ངོས་རྗེས་མ
+pdfjs-next-button-label = རྗེས་མ
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = ཤོག་ངོས
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = of { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoom Out
+pdfjs-zoom-out-button-label = Zoom Out
+pdfjs-zoom-in-button =
+ .title = Zoom In
+pdfjs-zoom-in-button-label = Zoom In
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Switch to Presentation Mode
+pdfjs-presentation-mode-button-label = Presentation Mode
+pdfjs-open-file-button =
+ .title = Open File
+pdfjs-open-file-button-label = Open
+pdfjs-print-button =
+ .title = Print
+pdfjs-print-button-label = Print
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Tools
+pdfjs-tools-button-label = Tools
+pdfjs-first-page-button =
+ .title = Go to First Page
+pdfjs-first-page-button-label = Go to First Page
+pdfjs-last-page-button =
+ .title = Go to Last Page
+pdfjs-last-page-button-label = Go to Last Page
+pdfjs-page-rotate-cw-button =
+ .title = Rotate Clockwise
+pdfjs-page-rotate-cw-button-label = Rotate Clockwise
+pdfjs-page-rotate-ccw-button =
+ .title = Rotate Counterclockwise
+pdfjs-page-rotate-ccw-button-label = Rotate Counterclockwise
+pdfjs-cursor-text-select-tool-button =
+ .title = Enable Text Selection Tool
+pdfjs-cursor-text-select-tool-button-label = Text Selection Tool
+pdfjs-cursor-hand-tool-button =
+ .title = Enable Hand Tool
+pdfjs-cursor-hand-tool-button-label = Hand Tool
+pdfjs-scroll-vertical-button =
+ .title = Use Vertical Scrolling
+pdfjs-scroll-vertical-button-label = Vertical Scrolling
+pdfjs-scroll-horizontal-button =
+ .title = Use Horizontal Scrolling
+pdfjs-scroll-horizontal-button-label = Horizontal Scrolling
+pdfjs-scroll-wrapped-button =
+ .title = Use Wrapped Scrolling
+pdfjs-scroll-wrapped-button-label = Wrapped Scrolling
+pdfjs-spread-none-button =
+ .title = Do not join page spreads
+pdfjs-spread-none-button-label = No Spreads
+pdfjs-spread-odd-button =
+ .title = Join page spreads starting with odd-numbered pages
+pdfjs-spread-odd-button-label = Odd Spreads
+pdfjs-spread-even-button =
+ .title = Join page spreads starting with even-numbered pages
+pdfjs-spread-even-button-label = Even Spreads
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Document Properties…
+pdfjs-document-properties-button-label = Document Properties…
+pdfjs-document-properties-file-name = File name:
+pdfjs-document-properties-file-size = File size:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Title:
+pdfjs-document-properties-author = Author:
+pdfjs-document-properties-subject = Subject:
+pdfjs-document-properties-keywords = Keywords:
+pdfjs-document-properties-creation-date = Creation Date:
+pdfjs-document-properties-modification-date = Modification Date:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creator:
+pdfjs-document-properties-producer = PDF Producer:
+pdfjs-document-properties-version = PDF Version:
+pdfjs-document-properties-page-count = Page Count:
+pdfjs-document-properties-page-size = Page Size:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portrait
+pdfjs-document-properties-page-size-orientation-landscape = landscape
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = Yes
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Close
+
+## Print
+
+pdfjs-print-progress-message = Preparing document for printing…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancel
+pdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser.
+pdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Toggle Sidebar
+pdfjs-toggle-sidebar-button-label = Toggle Sidebar
+pdfjs-document-outline-button =
+ .title = Show Document Outline (double-click to expand/collapse all items)
+pdfjs-document-outline-button-label = Document Outline
+pdfjs-attachments-button =
+ .title = Show Attachments
+pdfjs-attachments-button-label = Attachments
+pdfjs-thumbs-button =
+ .title = Show Thumbnails
+pdfjs-thumbs-button-label = Thumbnails
+pdfjs-findbar-button =
+ .title = Find in Document
+pdfjs-findbar-button-label = Find
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Page { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Thumbnail of Page { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Find
+ .placeholder = Find in document…
+pdfjs-find-previous-button =
+ .title = Find the previous occurrence of the phrase
+pdfjs-find-previous-button-label = Previous
+pdfjs-find-next-button =
+ .title = Find the next occurrence of the phrase
+pdfjs-find-next-button-label = Next
+pdfjs-find-highlight-checkbox = Highlight all
+pdfjs-find-match-case-checkbox-label = Match case
+pdfjs-find-entire-word-checkbox-label = Whole words
+pdfjs-find-reached-top = Reached top of document, continued from bottom
+pdfjs-find-reached-bottom = Reached end of document, continued from top
+pdfjs-find-not-found = Phrase not found
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Page Width
+pdfjs-page-scale-fit = Page Fit
+pdfjs-page-scale-auto = Automatic Zoom
+pdfjs-page-scale-actual = Actual Size
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = An error occurred while loading the PDF.
+pdfjs-invalid-file-error = Invalid or corrupted PDF file.
+pdfjs-missing-file-error = Missing PDF file.
+pdfjs-unexpected-response-error = Unexpected server response.
+pdfjs-rendering-error = An error occurred while rendering the page.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = Enter the password to open this PDF file.
+pdfjs-password-invalid = Invalid password. Please try again.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Cancel
+pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/br/viewer.ftl b/web/locale/br/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..9049f68f1247b36d2ba0de4c870a5640ed2bb545
--- /dev/null
+++ b/web/locale/br/viewer.ftl
@@ -0,0 +1,313 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pajenn a-raok
+pdfjs-previous-button-label = A-raok
+pdfjs-next-button =
+ .title = Pajenn war-lerc'h
+pdfjs-next-button-label = War-lerc'h
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pajenn
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = eus { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } war { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoum bihanaat
+pdfjs-zoom-out-button-label = Zoum bihanaat
+pdfjs-zoom-in-button =
+ .title = Zoum brasaat
+pdfjs-zoom-in-button-label = Zoum brasaat
+pdfjs-zoom-select =
+ .title = Zoum
+pdfjs-presentation-mode-button =
+ .title = Trec'haoliñ etrezek ar mod kinnigadenn
+pdfjs-presentation-mode-button-label = Mod kinnigadenn
+pdfjs-open-file-button =
+ .title = Digeriñ ur restr
+pdfjs-open-file-button-label = Digeriñ ur restr
+pdfjs-print-button =
+ .title = Moullañ
+pdfjs-print-button-label = Moullañ
+pdfjs-save-button =
+ .title = Enrollañ
+pdfjs-save-button-label = Enrollañ
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Pellgargañ
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Pellgargañ
+pdfjs-bookmark-button-label = Pajenn a-vremañ
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Digeriñ en arload
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Digeriñ en arload
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Ostilhoù
+pdfjs-tools-button-label = Ostilhoù
+pdfjs-first-page-button =
+ .title = Mont d'ar bajenn gentañ
+pdfjs-first-page-button-label = Mont d'ar bajenn gentañ
+pdfjs-last-page-button =
+ .title = Mont d'ar bajenn diwezhañ
+pdfjs-last-page-button-label = Mont d'ar bajenn diwezhañ
+pdfjs-page-rotate-cw-button =
+ .title = C'hwelañ gant roud ar bizied
+pdfjs-page-rotate-cw-button-label = C'hwelañ gant roud ar bizied
+pdfjs-page-rotate-ccw-button =
+ .title = C'hwelañ gant roud gin ar bizied
+pdfjs-page-rotate-ccw-button-label = C'hwelañ gant roud gin ar bizied
+pdfjs-cursor-text-select-tool-button =
+ .title = Gweredekaat an ostilh diuzañ testenn
+pdfjs-cursor-text-select-tool-button-label = Ostilh diuzañ testenn
+pdfjs-cursor-hand-tool-button =
+ .title = Gweredekaat an ostilh dorn
+pdfjs-cursor-hand-tool-button-label = Ostilh dorn
+pdfjs-scroll-vertical-button =
+ .title = Arverañ an dibunañ a-blom
+pdfjs-scroll-vertical-button-label = Dibunañ a-serzh
+pdfjs-scroll-horizontal-button =
+ .title = Arverañ an dibunañ a-blaen
+pdfjs-scroll-horizontal-button-label = Dibunañ a-blaen
+pdfjs-scroll-wrapped-button =
+ .title = Arverañ an dibunañ paket
+pdfjs-scroll-wrapped-button-label = Dibunañ paket
+pdfjs-spread-none-button =
+ .title = Chom hep stagañ ar skignadurioù
+pdfjs-spread-none-button-label = Skignadenn ebet
+pdfjs-spread-odd-button =
+ .title = Lakaat ar pajennadoù en ur gregiñ gant ar pajennoù ampar
+pdfjs-spread-odd-button-label = Pajennoù ampar
+pdfjs-spread-even-button =
+ .title = Lakaat ar pajennadoù en ur gregiñ gant ar pajennoù par
+pdfjs-spread-even-button-label = Pajennoù par
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Perzhioù an teul…
+pdfjs-document-properties-button-label = Perzhioù an teul…
+pdfjs-document-properties-file-name = Anv restr:
+pdfjs-document-properties-file-size = Ment ar restr:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } Ke ({ $size_b } eizhbit)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } Me ({ $size_b } eizhbit)
+pdfjs-document-properties-title = Titl:
+pdfjs-document-properties-author = Aozer:
+pdfjs-document-properties-subject = Danvez:
+pdfjs-document-properties-keywords = Gerioù-alc'hwez:
+pdfjs-document-properties-creation-date = Deiziad krouiñ:
+pdfjs-document-properties-modification-date = Deiziad kemmañ:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Krouer:
+pdfjs-document-properties-producer = Kenderc'her PDF:
+pdfjs-document-properties-version = Handelv PDF:
+pdfjs-document-properties-page-count = Niver a bajennoù:
+pdfjs-document-properties-page-size = Ment ar bajenn:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = poltred
+pdfjs-document-properties-page-size-orientation-landscape = gweledva
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Lizher
+pdfjs-document-properties-page-size-name-legal = Lezennel
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Gwel Web Herrek:
+pdfjs-document-properties-linearized-yes = Ya
+pdfjs-document-properties-linearized-no = Ket
+pdfjs-document-properties-close-button = Serriñ
+
+## Print
+
+pdfjs-print-progress-message = O prientiñ an teul evit moullañ...
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Nullañ
+pdfjs-printing-not-supported = Kemenn: N'eo ket skoret penn-da-benn ar moullañ gant ar merdeer-mañ.
+pdfjs-printing-not-ready = Kemenn: N'hall ket bezañ moullet ar restr PDF rak n'eo ket karget penn-da-benn.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Diskouez/kuzhat ar varrenn gostez
+pdfjs-toggle-sidebar-notification-button =
+ .title = Trec'haoliñ ar varrenn-gostez (ur steuñv pe stagadennoù a zo en teul)
+pdfjs-toggle-sidebar-button-label = Diskouez/kuzhat ar varrenn gostez
+pdfjs-document-outline-button =
+ .title = Diskouez steuñv an teul (daouglikit evit brasaat/bihanaat an holl elfennoù)
+pdfjs-document-outline-button-label = Sinedoù an teuliad
+pdfjs-attachments-button =
+ .title = Diskouez ar c'henstagadurioù
+pdfjs-attachments-button-label = Kenstagadurioù
+pdfjs-layers-button =
+ .title = Diskouez ar gwiskadoù (daou-glikañ evit adderaouekaat an holl gwiskadoù d'o stad dre ziouer)
+pdfjs-layers-button-label = Gwiskadoù
+pdfjs-thumbs-button =
+ .title = Diskouez ar melvennoù
+pdfjs-thumbs-button-label = Melvennoù
+pdfjs-findbar-button =
+ .title = Klask e-barzh an teuliad
+pdfjs-findbar-button-label = Klask
+pdfjs-additional-layers = Gwiskadoù ouzhpenn
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pajenn { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Melvenn ar bajenn { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Klask
+ .placeholder = Klask e-barzh an teuliad
+pdfjs-find-previous-button =
+ .title = Kavout an tamm frazenn kent o klotañ ganti
+pdfjs-find-previous-button-label = Kent
+pdfjs-find-next-button =
+ .title = Kavout an tamm frazenn war-lerc'h o klotañ ganti
+pdfjs-find-next-button-label = War-lerc'h
+pdfjs-find-highlight-checkbox = Usskediñ pep tra
+pdfjs-find-match-case-checkbox-label = Teurel evezh ouzh ar pennlizherennoù
+pdfjs-find-entire-word-checkbox-label = Gerioù a-bezh
+pdfjs-find-reached-top = Tizhet eo bet derou ar bajenn, kenderc'hel diouzh an diaz
+pdfjs-find-reached-bottom = Tizhet eo bet dibenn ar bajenn, kenderc'hel diouzh ar c'hrec'h
+pdfjs-find-not-found = N'haller ket kavout ar frazenn
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Led ar bajenn
+pdfjs-page-scale-fit = Pajenn a-bezh
+pdfjs-page-scale-auto = Zoum emgefreek
+pdfjs-page-scale-actual = Ment wir
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Pajenn { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Degouezhet ez eus bet ur fazi e-pad kargañ ar PDF.
+pdfjs-invalid-file-error = Restr PDF didalvoudek pe kontronet.
+pdfjs-missing-file-error = Restr PDF o vankout.
+pdfjs-unexpected-response-error = Respont dic'hortoz a-berzh an dafariad
+pdfjs-rendering-error = Degouezhet ez eus bet ur fazi e-pad skrammañ ar bajennad.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Notennañ]
+
+## Password
+
+pdfjs-password-label = Enankit ar ger-tremen evit digeriñ ar restr PDF-mañ.
+pdfjs-password-invalid = Ger-tremen didalvoudek. Klaskit en-dro mar plij.
+pdfjs-password-ok-button = Mat eo
+pdfjs-password-cancel-button = Nullañ
+pdfjs-web-fonts-disabled = Diweredekaet eo an nodrezhoù web: n'haller ket arverañ an nodrezhoù PDF enframmet.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Testenn
+pdfjs-editor-free-text-button-label = Testenn
+pdfjs-editor-ink-button =
+ .title = Tresañ
+pdfjs-editor-ink-button-label = Tresañ
+pdfjs-editor-stamp-button =
+ .title = Ouzhpennañ pe aozañ skeudennoù
+pdfjs-editor-stamp-button-label = Ouzhpennañ pe aozañ skeudennoù
+
+## Remove button for the various kind of editor.
+
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Liv
+pdfjs-editor-free-text-size-input = Ment
+pdfjs-editor-ink-color-input = Liv
+pdfjs-editor-ink-thickness-input = Tevder
+pdfjs-editor-ink-opacity-input = Boullder
+pdfjs-editor-stamp-add-image-button =
+ .title = Ouzhpennañ ur skeudenn
+pdfjs-editor-stamp-add-image-button-label = Ouzhpennañ ur skeudenn
+pdfjs-free-text =
+ .aria-label = Aozer testennoù
+pdfjs-ink =
+ .aria-label = Aozer tresoù
+pdfjs-ink-canvas =
+ .aria-label = Skeudenn bet krouet gant an implijer·ez
+
+## Alt-text dialog
+
+pdfjs-editor-alt-text-add-description-label = Ouzhpennañ un deskrivadur
+pdfjs-editor-alt-text-cancel-button = Nullañ
+pdfjs-editor-alt-text-save-button = Enrollañ
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+
+## Color picker
+
diff --git a/web/locale/brx/viewer.ftl b/web/locale/brx/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..53ff72c5601db958a07d4552b4b798d7ab524492
--- /dev/null
+++ b/web/locale/brx/viewer.ftl
@@ -0,0 +1,218 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = आगोलनि बिलाइ
+pdfjs-previous-button-label = आगोलनि
+pdfjs-next-button =
+ .title = उननि बिलाइ
+pdfjs-next-button-label = उननि
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = बिलाइ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } नि
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pagesCount } नि { $pageNumber })
+pdfjs-zoom-out-button =
+ .title = फिसायै जुम खालाम
+pdfjs-zoom-out-button-label = फिसायै जुम खालाम
+pdfjs-zoom-in-button =
+ .title = गेदेरै जुम खालाम
+pdfjs-zoom-in-button-label = गेदेरै जुम खालाम
+pdfjs-zoom-select =
+ .title = जुम खालाम
+pdfjs-presentation-mode-button =
+ .title = दिन्थिफुंनाय म'डआव थां
+pdfjs-presentation-mode-button-label = दिन्थिफुंनाय म'ड
+pdfjs-open-file-button =
+ .title = फाइलखौ खेव
+pdfjs-open-file-button-label = खेव
+pdfjs-print-button =
+ .title = साफाय
+pdfjs-print-button-label = साफाय
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = टुल
+pdfjs-tools-button-label = टुल
+pdfjs-first-page-button =
+ .title = गिबि बिलाइआव थां
+pdfjs-first-page-button-label = गिबि बिलाइआव थां
+pdfjs-last-page-button =
+ .title = जोबथा बिलाइआव थां
+pdfjs-last-page-button-label = जोबथा बिलाइआव थां
+pdfjs-page-rotate-cw-button =
+ .title = घरि गिदिंनाय फार्से फिदिं
+pdfjs-page-rotate-cw-button-label = घरि गिदिंनाय फार्से फिदिं
+pdfjs-page-rotate-ccw-button =
+ .title = घरि गिदिंनाय उल्था फार्से फिदिं
+pdfjs-page-rotate-ccw-button-label = घरि गिदिंनाय उल्था फार्से फिदिं
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = फोरमान बिलाइनि आखुथाय...
+pdfjs-document-properties-button-label = फोरमान बिलाइनि आखुथाय...
+pdfjs-document-properties-file-name = फाइलनि मुं:
+pdfjs-document-properties-file-size = फाइलनि महर:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } बाइट)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } बाइट)
+pdfjs-document-properties-title = बिमुं:
+pdfjs-document-properties-author = लिरगिरि:
+pdfjs-document-properties-subject = आयदा:
+pdfjs-document-properties-keywords = गाहाय सोदोब:
+pdfjs-document-properties-creation-date = सोरजिनाय अक्ट':
+pdfjs-document-properties-modification-date = सुद्रायनाय अक्ट':
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = सोरजिग्रा:
+pdfjs-document-properties-producer = PDF दिहुनग्रा:
+pdfjs-document-properties-version = PDF बिसान:
+pdfjs-document-properties-page-count = बिलाइनि हिसाब:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = प'र्ट्रेट
+pdfjs-document-properties-page-size-orientation-landscape = लेण्डस्केप
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = लायजाम
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+pdfjs-document-properties-linearized-yes = नंगौ
+pdfjs-document-properties-linearized-no = नङा
+pdfjs-document-properties-close-button = बन्द खालाम
+
+## Print
+
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = नेवसि
+pdfjs-printing-not-supported = सांग्रांथि: साफायनाया बे ब्राउजारजों आबुङै हेफाजाब होजाया।
+pdfjs-printing-not-ready = सांग्रांथि: PDF खौ साफायनायनि थाखाय फुरायै ल'ड खालामाखै।
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = टग्गल साइडबार
+pdfjs-toggle-sidebar-button-label = टग्गल साइडबार
+pdfjs-document-outline-button-label = फोरमान बिलाइ सिमा हांखो
+pdfjs-attachments-button =
+ .title = नांजाब होनायखौ दिन्थि
+pdfjs-attachments-button-label = नांजाब होनाय
+pdfjs-thumbs-button =
+ .title = थामनेइलखौ दिन्थि
+pdfjs-thumbs-button-label = थामनेइल
+pdfjs-findbar-button =
+ .title = फोरमान बिलाइआव नागिरना दिहुन
+pdfjs-findbar-button-label = नायगिरना दिहुन
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = बिलाइ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = बिलाइ { $page } नि थामनेइल
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = नायगिरना दिहुन
+ .placeholder = फोरमान बिलाइआव नागिरना दिहुन...
+pdfjs-find-previous-button =
+ .title = बाथ्रा खोन्दोबनि सिगांनि नुजाथिनायखौ नागिर
+pdfjs-find-previous-button-label = आगोलनि
+pdfjs-find-next-button =
+ .title = बाथ्रा खोन्दोबनि उननि नुजाथिनायखौ नागिर
+pdfjs-find-next-button-label = उननि
+pdfjs-find-highlight-checkbox = गासैखौबो हाइलाइट खालाम
+pdfjs-find-match-case-checkbox-label = गोरोबनाय केस
+pdfjs-find-reached-top = थालो निफ्राय जागायनानै फोरमान बिलाइनि बिजौआव सौहैबाय
+pdfjs-find-reached-bottom = बिजौ निफ्राय जागायनानै फोरमान बिलाइनि बिजौआव सौहैबाय
+pdfjs-find-not-found = बाथ्रा खोन्दोब मोनाखै
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = बिलाइनि गुवार
+pdfjs-page-scale-fit = बिलाइ गोरोबनाय
+pdfjs-page-scale-auto = गावनोगाव जुम
+pdfjs-page-scale-actual = थार महर
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF ल'ड खालामनाय समाव मोनसे गोरोन्थि जाबाय।
+pdfjs-invalid-file-error = बाहायजायै एबा गाज्रि जानाय PDF फाइल
+pdfjs-missing-file-error = गोमानाय PDF फाइल
+pdfjs-unexpected-response-error = मिजिंथियै सार्भार फिननाय।
+pdfjs-rendering-error = बिलाइखौ राव सोलायनाय समाव मोनसे गोरोन्थि जादों।
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } सोदोब बेखेवनाय]
+
+## Password
+
+pdfjs-password-label = बे PDF फाइलखौ खेवनो पासवार्ड हाबहो।
+pdfjs-password-invalid = बाहायजायै पासवार्ड। अननानै फिन नाजा।
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = नेवसि
+pdfjs-web-fonts-disabled = वेब फन्टखौ लोरबां खालामबाय: अरजाबहोनाय PDF फन्टखौ बाहायनो हायाखै।
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/bs/viewer.ftl b/web/locale/bs/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..39440424fc2963c509721efbb9cab69ed11762ec
--- /dev/null
+++ b/web/locale/bs/viewer.ftl
@@ -0,0 +1,223 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Prethodna strana
+pdfjs-previous-button-label = Prethodna
+pdfjs-next-button =
+ .title = Sljedeća strna
+pdfjs-next-button-label = Sljedeća
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Strana
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = od { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } od { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Umanji
+pdfjs-zoom-out-button-label = Umanji
+pdfjs-zoom-in-button =
+ .title = Uvećaj
+pdfjs-zoom-in-button-label = Uvećaj
+pdfjs-zoom-select =
+ .title = Uvećanje
+pdfjs-presentation-mode-button =
+ .title = Prebaci se u prezentacijski režim
+pdfjs-presentation-mode-button-label = Prezentacijski režim
+pdfjs-open-file-button =
+ .title = Otvori fajl
+pdfjs-open-file-button-label = Otvori
+pdfjs-print-button =
+ .title = Štampaj
+pdfjs-print-button-label = Štampaj
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Alati
+pdfjs-tools-button-label = Alati
+pdfjs-first-page-button =
+ .title = Idi na prvu stranu
+pdfjs-first-page-button-label = Idi na prvu stranu
+pdfjs-last-page-button =
+ .title = Idi na zadnju stranu
+pdfjs-last-page-button-label = Idi na zadnju stranu
+pdfjs-page-rotate-cw-button =
+ .title = Rotiraj u smjeru kazaljke na satu
+pdfjs-page-rotate-cw-button-label = Rotiraj u smjeru kazaljke na satu
+pdfjs-page-rotate-ccw-button =
+ .title = Rotiraj suprotno smjeru kazaljke na satu
+pdfjs-page-rotate-ccw-button-label = Rotiraj suprotno smjeru kazaljke na satu
+pdfjs-cursor-text-select-tool-button =
+ .title = Omogući alat za označavanje teksta
+pdfjs-cursor-text-select-tool-button-label = Alat za označavanje teksta
+pdfjs-cursor-hand-tool-button =
+ .title = Omogući ručni alat
+pdfjs-cursor-hand-tool-button-label = Ručni alat
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Svojstva dokumenta...
+pdfjs-document-properties-button-label = Svojstva dokumenta...
+pdfjs-document-properties-file-name = Naziv fajla:
+pdfjs-document-properties-file-size = Veličina fajla:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajta)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajta)
+pdfjs-document-properties-title = Naslov:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Predmet:
+pdfjs-document-properties-keywords = Ključne riječi:
+pdfjs-document-properties-creation-date = Datum kreiranja:
+pdfjs-document-properties-modification-date = Datum promjene:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Kreator:
+pdfjs-document-properties-producer = PDF stvaratelj:
+pdfjs-document-properties-version = PDF verzija:
+pdfjs-document-properties-page-count = Broj stranica:
+pdfjs-document-properties-page-size = Veličina stranice:
+pdfjs-document-properties-page-size-unit-inches = u
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = uspravno
+pdfjs-document-properties-page-size-orientation-landscape = vodoravno
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Pismo
+pdfjs-document-properties-page-size-name-legal = Pravni
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+pdfjs-document-properties-close-button = Zatvori
+
+## Print
+
+pdfjs-print-progress-message = Pripremam dokument za štampu…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Otkaži
+pdfjs-printing-not-supported = Upozorenje: Štampanje nije u potpunosti podržano u ovom browseru.
+pdfjs-printing-not-ready = Upozorenje: PDF nije u potpunosti učitan za štampanje.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Uključi/isključi bočnu traku
+pdfjs-toggle-sidebar-button-label = Uključi/isključi bočnu traku
+pdfjs-document-outline-button =
+ .title = Prikaži outline dokumenta (dvoklik za skupljanje/širenje svih stavki)
+pdfjs-document-outline-button-label = Konture dokumenta
+pdfjs-attachments-button =
+ .title = Prikaži priloge
+pdfjs-attachments-button-label = Prilozi
+pdfjs-thumbs-button =
+ .title = Prikaži thumbnailove
+pdfjs-thumbs-button-label = Thumbnailovi
+pdfjs-findbar-button =
+ .title = Pronađi u dokumentu
+pdfjs-findbar-button-label = Pronađi
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Strana { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Thumbnail strane { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Pronađi
+ .placeholder = Pronađi u dokumentu…
+pdfjs-find-previous-button =
+ .title = Pronađi prethodno pojavljivanje fraze
+pdfjs-find-previous-button-label = Prethodno
+pdfjs-find-next-button =
+ .title = Pronađi sljedeće pojavljivanje fraze
+pdfjs-find-next-button-label = Sljedeće
+pdfjs-find-highlight-checkbox = Označi sve
+pdfjs-find-match-case-checkbox-label = Osjetljivost na karaktere
+pdfjs-find-reached-top = Dostigao sam vrh dokumenta, nastavljam sa dna
+pdfjs-find-reached-bottom = Dostigao sam kraj dokumenta, nastavljam sa vrha
+pdfjs-find-not-found = Fraza nije pronađena
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Širina strane
+pdfjs-page-scale-fit = Uklopi stranu
+pdfjs-page-scale-auto = Automatsko uvećanje
+pdfjs-page-scale-actual = Stvarna veličina
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Došlo je do greške prilikom učitavanja PDF-a.
+pdfjs-invalid-file-error = Neispravan ili oštećen PDF fajl.
+pdfjs-missing-file-error = Nedostaje PDF fajl.
+pdfjs-unexpected-response-error = Neočekivani odgovor servera.
+pdfjs-rendering-error = Došlo je do greške prilikom renderiranja strane.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } pribilješka]
+
+## Password
+
+pdfjs-password-label = Upišite lozinku da biste otvorili ovaj PDF fajl.
+pdfjs-password-invalid = Pogrešna lozinka. Pokušajte ponovo.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Otkaži
+pdfjs-web-fonts-disabled = Web fontovi su onemogućeni: nemoguće koristiti ubačene PDF fontove.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ca/viewer.ftl b/web/locale/ca/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..575dc5fb31783459ab5994563b40d61c443b2b0f
--- /dev/null
+++ b/web/locale/ca/viewer.ftl
@@ -0,0 +1,299 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pàgina anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Pàgina següent
+pdfjs-next-button-label = Següent
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pàgina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Redueix
+pdfjs-zoom-out-button-label = Redueix
+pdfjs-zoom-in-button =
+ .title = Amplia
+pdfjs-zoom-in-button-label = Amplia
+pdfjs-zoom-select =
+ .title = Escala
+pdfjs-presentation-mode-button =
+ .title = Canvia al mode de presentació
+pdfjs-presentation-mode-button-label = Mode de presentació
+pdfjs-open-file-button =
+ .title = Obre el fitxer
+pdfjs-open-file-button-label = Obre
+pdfjs-print-button =
+ .title = Imprimeix
+pdfjs-print-button-label = Imprimeix
+pdfjs-save-button =
+ .title = Desa
+pdfjs-save-button-label = Desa
+pdfjs-bookmark-button =
+ .title = Pàgina actual (mostra l'URL de la pàgina actual)
+pdfjs-bookmark-button-label = Pàgina actual
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Obre en una aplicació
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Obre en una aplicació
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Eines
+pdfjs-tools-button-label = Eines
+pdfjs-first-page-button =
+ .title = Vés a la primera pàgina
+pdfjs-first-page-button-label = Vés a la primera pàgina
+pdfjs-last-page-button =
+ .title = Vés a l'última pàgina
+pdfjs-last-page-button-label = Vés a l'última pàgina
+pdfjs-page-rotate-cw-button =
+ .title = Gira cap a la dreta
+pdfjs-page-rotate-cw-button-label = Gira cap a la dreta
+pdfjs-page-rotate-ccw-button =
+ .title = Gira cap a l'esquerra
+pdfjs-page-rotate-ccw-button-label = Gira cap a l'esquerra
+pdfjs-cursor-text-select-tool-button =
+ .title = Habilita l'eina de selecció de text
+pdfjs-cursor-text-select-tool-button-label = Eina de selecció de text
+pdfjs-cursor-hand-tool-button =
+ .title = Habilita l'eina de mà
+pdfjs-cursor-hand-tool-button-label = Eina de mà
+pdfjs-scroll-page-button =
+ .title = Usa el desplaçament de pàgina
+pdfjs-scroll-page-button-label = Desplaçament de pàgina
+pdfjs-scroll-vertical-button =
+ .title = Utilitza el desplaçament vertical
+pdfjs-scroll-vertical-button-label = Desplaçament vertical
+pdfjs-scroll-horizontal-button =
+ .title = Utilitza el desplaçament horitzontal
+pdfjs-scroll-horizontal-button-label = Desplaçament horitzontal
+pdfjs-scroll-wrapped-button =
+ .title = Activa el desplaçament continu
+pdfjs-scroll-wrapped-button-label = Desplaçament continu
+pdfjs-spread-none-button =
+ .title = No agrupis les pàgines de dues en dues
+pdfjs-spread-none-button-label = Una sola pàgina
+pdfjs-spread-odd-button =
+ .title = Mostra dues pàgines començant per les pàgines de numeració senar
+pdfjs-spread-odd-button-label = Doble pàgina (senar)
+pdfjs-spread-even-button =
+ .title = Mostra dues pàgines començant per les pàgines de numeració parell
+pdfjs-spread-even-button-label = Doble pàgina (parell)
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propietats del document…
+pdfjs-document-properties-button-label = Propietats del document…
+pdfjs-document-properties-file-name = Nom del fitxer:
+pdfjs-document-properties-file-size = Mida del fitxer:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Títol:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Assumpte:
+pdfjs-document-properties-keywords = Paraules clau:
+pdfjs-document-properties-creation-date = Data de creació:
+pdfjs-document-properties-modification-date = Data de modificació:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creador:
+pdfjs-document-properties-producer = Generador de PDF:
+pdfjs-document-properties-version = Versió de PDF:
+pdfjs-document-properties-page-count = Nombre de pàgines:
+pdfjs-document-properties-page-size = Mida de la pàgina:
+pdfjs-document-properties-page-size-unit-inches = polzades
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertical
+pdfjs-document-properties-page-size-orientation-landscape = apaïsat
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Carta
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista web ràpida:
+pdfjs-document-properties-linearized-yes = Sí
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Tanca
+
+## Print
+
+pdfjs-print-progress-message = S'està preparant la impressió del document…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancel·la
+pdfjs-printing-not-supported = Avís: la impressió no és plenament funcional en aquest navegador.
+pdfjs-printing-not-ready = Atenció: el PDF no s'ha acabat de carregar per imprimir-lo.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Mostra/amaga la barra lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Mostra/amaga la barra lateral (el document conté un esquema, adjuncions o capes)
+pdfjs-toggle-sidebar-button-label = Mostra/amaga la barra lateral
+pdfjs-document-outline-button =
+ .title = Mostra l'esquema del document (doble clic per ampliar/reduir tots els elements)
+pdfjs-document-outline-button-label = Esquema del document
+pdfjs-attachments-button =
+ .title = Mostra les adjuncions
+pdfjs-attachments-button-label = Adjuncions
+pdfjs-layers-button =
+ .title = Mostra les capes (doble clic per restablir totes les capes al seu estat per defecte)
+pdfjs-layers-button-label = Capes
+pdfjs-thumbs-button =
+ .title = Mostra les miniatures
+pdfjs-thumbs-button-label = Miniatures
+pdfjs-current-outline-item-button =
+ .title = Cerca l'element d'esquema actual
+pdfjs-current-outline-item-button-label = Element d'esquema actual
+pdfjs-findbar-button =
+ .title = Cerca al document
+pdfjs-findbar-button-label = Cerca
+pdfjs-additional-layers = Capes addicionals
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pàgina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura de la pàgina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Cerca
+ .placeholder = Cerca al document…
+pdfjs-find-previous-button =
+ .title = Cerca l'anterior coincidència de l'expressió
+pdfjs-find-previous-button-label = Anterior
+pdfjs-find-next-button =
+ .title = Cerca la següent coincidència de l'expressió
+pdfjs-find-next-button-label = Següent
+pdfjs-find-highlight-checkbox = Ressalta-ho tot
+pdfjs-find-match-case-checkbox-label = Distingeix entre majúscules i minúscules
+pdfjs-find-match-diacritics-checkbox-label = Respecta els diacrítics
+pdfjs-find-entire-word-checkbox-label = Paraules senceres
+pdfjs-find-reached-top = S'ha arribat al principi del document, es continua pel final
+pdfjs-find-reached-bottom = S'ha arribat al final del document, es continua pel principi
+pdfjs-find-not-found = No s'ha trobat l'expressió
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Amplada de la pàgina
+pdfjs-page-scale-fit = Ajusta la pàgina
+pdfjs-page-scale-auto = Zoom automàtic
+pdfjs-page-scale-actual = Mida real
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Pàgina { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = S'ha produït un error en carregar el PDF.
+pdfjs-invalid-file-error = El fitxer PDF no és vàlid o està malmès.
+pdfjs-missing-file-error = Falta el fitxer PDF.
+pdfjs-unexpected-response-error = Resposta inesperada del servidor.
+pdfjs-rendering-error = S'ha produït un error mentre es renderitzava la pàgina.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotació { $type }]
+
+## Password
+
+pdfjs-password-label = Introduïu la contrasenya per obrir aquest fitxer PDF.
+pdfjs-password-invalid = La contrasenya no és vàlida. Torneu-ho a provar.
+pdfjs-password-ok-button = D'acord
+pdfjs-password-cancel-button = Cancel·la
+pdfjs-web-fonts-disabled = Els tipus de lletra web estan desactivats: no es poden utilitzar els tipus de lletra incrustats al PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Text
+pdfjs-editor-free-text-button-label = Text
+pdfjs-editor-ink-button =
+ .title = Dibuixa
+pdfjs-editor-ink-button-label = Dibuixa
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Color
+pdfjs-editor-free-text-size-input = Mida
+pdfjs-editor-ink-color-input = Color
+pdfjs-editor-ink-thickness-input = Gruix
+pdfjs-editor-ink-opacity-input = Opacitat
+pdfjs-free-text =
+ .aria-label = Editor de text
+pdfjs-free-text-default-content = Escriviu…
+pdfjs-ink =
+ .aria-label = Editor de dibuix
+pdfjs-ink-canvas =
+ .aria-label = Imatge creada per l'usuari
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/cak/viewer.ftl b/web/locale/cak/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..f40c1e92a4e420611b535f30fd6834f699abb9f5
--- /dev/null
+++ b/web/locale/cak/viewer.ftl
@@ -0,0 +1,291 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Jun kan ruxaq
+pdfjs-previous-button-label = Jun kan
+pdfjs-next-button =
+ .title = Jun chik ruxaq
+pdfjs-next-button-label = Jun chik
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Ruxaq
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = richin { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } richin { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Tich'utinirisäx
+pdfjs-zoom-out-button-label = Tich'utinirisäx
+pdfjs-zoom-in-button =
+ .title = Tinimirisäx
+pdfjs-zoom-in-button-label = Tinimirisäx
+pdfjs-zoom-select =
+ .title = Sum
+pdfjs-presentation-mode-button =
+ .title = Tijal ri rub'anikil niwachin
+pdfjs-presentation-mode-button-label = Pa rub'eyal niwachin
+pdfjs-open-file-button =
+ .title = Tijaq Yakb'äl
+pdfjs-open-file-button-label = Tijaq
+pdfjs-print-button =
+ .title = Titz'ajb'äx
+pdfjs-print-button-label = Titz'ajb'äx
+pdfjs-save-button =
+ .title = Tiyak
+pdfjs-save-button-label = Tiyak
+pdfjs-bookmark-button-label = Ruxaq k'o wakami
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Samajib'äl
+pdfjs-tools-button-label = Samajib'äl
+pdfjs-first-page-button =
+ .title = Tib'e pa nab'ey ruxaq
+pdfjs-first-page-button-label = Tib'e pa nab'ey ruxaq
+pdfjs-last-page-button =
+ .title = Tib'e pa ruk'isib'äl ruxaq
+pdfjs-last-page-button-label = Tib'e pa ruk'isib'äl ruxaq
+pdfjs-page-rotate-cw-button =
+ .title = Tisutïx pan ajkiq'a'
+pdfjs-page-rotate-cw-button-label = Tisutïx pan ajkiq'a'
+pdfjs-page-rotate-ccw-button =
+ .title = Tisutïx pan ajxokon
+pdfjs-page-rotate-ccw-button-label = Tisutïx pan ajxokon
+pdfjs-cursor-text-select-tool-button =
+ .title = Titzij ri rusamajib'al Rucha'ik Rucholajem Tzij
+pdfjs-cursor-text-select-tool-button-label = Rusamajib'al Rucha'ik Rucholajem Tzij
+pdfjs-cursor-hand-tool-button =
+ .title = Titzij ri q'ab'aj samajib'äl
+pdfjs-cursor-hand-tool-button-label = Q'ab'aj Samajib'äl
+pdfjs-scroll-page-button =
+ .title = Tokisäx Ruxaq Q'axanem
+pdfjs-scroll-page-button-label = Ruxaq Q'axanem
+pdfjs-scroll-vertical-button =
+ .title = Tokisäx Pa'äl Q'axanem
+pdfjs-scroll-vertical-button-label = Pa'äl Q'axanem
+pdfjs-scroll-horizontal-button =
+ .title = Tokisäx Kotz'öl Q'axanem
+pdfjs-scroll-horizontal-button-label = Kotz'öl Q'axanem
+pdfjs-scroll-wrapped-button =
+ .title = Tokisäx Tzub'aj Q'axanem
+pdfjs-scroll-wrapped-button-label = Tzub'aj Q'axanem
+pdfjs-spread-none-button =
+ .title = Man ketun taq ruxaq pa rub'eyal wuj
+pdfjs-spread-none-button-label = Majun Rub'eyal
+pdfjs-spread-odd-button =
+ .title = Ke'atunu' ri taq ruxaq rik'in natikirisaj rik'in jun man k'ulaj ta rajilab'al
+pdfjs-spread-odd-button-label = Man K'ulaj Ta Rub'eyal
+pdfjs-spread-even-button =
+ .title = Ke'atunu' ri taq ruxaq rik'in natikirisaj rik'in jun k'ulaj rajilab'al
+pdfjs-spread-even-button-label = K'ulaj Rub'eyal
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Taq richinil wuj…
+pdfjs-document-properties-button-label = Taq richinil wuj…
+pdfjs-document-properties-file-name = Rub'i' yakb'äl:
+pdfjs-document-properties-file-size = Runimilem yakb'äl:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = B'i'aj:
+pdfjs-document-properties-author = B'anel:
+pdfjs-document-properties-subject = Taqikil:
+pdfjs-document-properties-keywords = Kixe'el taq tzij:
+pdfjs-document-properties-creation-date = Ruq'ijul xtz'uk:
+pdfjs-document-properties-modification-date = Ruq'ijul xjalwachïx:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Q'inonel:
+pdfjs-document-properties-producer = PDF b'anöy:
+pdfjs-document-properties-version = PDF ruwäch:
+pdfjs-document-properties-page-count = Jarupe' ruxaq:
+pdfjs-document-properties-page-size = Runimilem ri Ruxaq:
+pdfjs-document-properties-page-size-unit-inches = pa
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = rupalem
+pdfjs-document-properties-page-size-orientation-landscape = rukotz'olem
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Loman wuj
+pdfjs-document-properties-page-size-name-legal = Taqanel tzijol
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Anin Rutz'etik Ajk'amaya'l:
+pdfjs-document-properties-linearized-yes = Ja'
+pdfjs-document-properties-linearized-no = Mani
+pdfjs-document-properties-close-button = Titz'apïx
+
+## Print
+
+pdfjs-print-progress-message = Ruchojmirisaxik wuj richin nitz'ajb'äx…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Tiq'at
+pdfjs-printing-not-supported = Rutzijol k'ayewal: Ri rutz'ajb'axik man koch'el ta ronojel pa re okik'amaya'l re'.
+pdfjs-printing-not-ready = Rutzijol k'ayewal: Ri PDF man xusamajij ta ronojel richin nitz'ajb'äx.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Tijal ri ajxikin kajtz'ik
+pdfjs-toggle-sidebar-notification-button =
+ .title = Tik'ex ri ajxikin yuqkajtz'ik (ri wuj eruk'wan taq ruchi'/taqo/kuchuj)
+pdfjs-toggle-sidebar-button-label = Tijal ri ajxikin kajtz'ik
+pdfjs-document-outline-button =
+ .title = Tik'ut pe ruch'akulal wuj (kamul-pitz'oj richin nirik'/nich'utinirisäx ronojel ruch'akulal)
+pdfjs-document-outline-button-label = Ruch'akulal wuj
+pdfjs-attachments-button =
+ .title = Kek'ut pe ri taq taqoj
+pdfjs-attachments-button-label = Taq taqoj
+pdfjs-layers-button =
+ .title = Kek'ut taq Kuchuj (ka'i'-pitz' richin yetzolïx ronojel ri taq kuchuj e k'o wi)
+pdfjs-layers-button-label = Taq kuchuj
+pdfjs-thumbs-button =
+ .title = Kek'ut pe taq ch'utiq
+pdfjs-thumbs-button-label = Koköj
+pdfjs-current-outline-item-button =
+ .title = Kekanöx Taq Ch'akulal Kik'wan Chib'äl
+pdfjs-current-outline-item-button-label = Taq Ch'akulal Kik'wan Chib'äl
+pdfjs-findbar-button =
+ .title = Tikanöx chupam ri wuj
+pdfjs-findbar-button-label = Tikanöx
+pdfjs-additional-layers = Tz'aqat ta Kuchuj
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Ruxaq { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Ruch'utinirisaxik ruxaq { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Tikanöx
+ .placeholder = Tikanöx pa wuj…
+pdfjs-find-previous-button =
+ .title = Tib'an b'enam pa ri jun kan q'aptzij xilitäj
+pdfjs-find-previous-button-label = Jun kan
+pdfjs-find-next-button =
+ .title = Tib'e pa ri jun chik pajtzij xilitäj
+pdfjs-find-next-button-label = Jun chik
+pdfjs-find-highlight-checkbox = Tiya' retal ronojel
+pdfjs-find-match-case-checkbox-label = Tuk'äm ri' kik'in taq nimatz'ib' chuqa' taq ch'utitz'ib'
+pdfjs-find-match-diacritics-checkbox-label = Tiya' Kikojol Tz'aqat taq Tz'ib'
+pdfjs-find-entire-word-checkbox-label = Tz'aqät taq tzij
+pdfjs-find-reached-top = Xb'eq'i' ri rutikirib'al wuj, xtikanöx k'a pa ruk'isib'äl
+pdfjs-find-reached-bottom = Xb'eq'i' ri ruk'isib'äl wuj, xtikanöx pa rutikirib'al
+pdfjs-find-not-found = Man xilitäj ta ri pajtzij
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Ruwa ruxaq
+pdfjs-page-scale-fit = Tinuk' ruxaq
+pdfjs-page-scale-auto = Yonil chi nimilem
+pdfjs-page-scale-actual = Runimilem Wakami
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Ruxaq { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Xk'ulwachitäj jun sach'oj toq xnuk'ux ri PDF .
+pdfjs-invalid-file-error = Man oke ta o yujtajinäq ri PDF yakb'äl.
+pdfjs-missing-file-error = Man xilitäj ta ri PDF yakb'äl.
+pdfjs-unexpected-response-error = Man oyob'en ta tz'olin rutzij ruk'u'x samaj.
+pdfjs-rendering-error = Xk'ulwachitäj jun sachoj toq ninuk'wachij ri ruxaq.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Tz'ib'anïk]
+
+## Password
+
+pdfjs-password-label = Tatz'ib'aj ri ewan tzij richin najäq re yakb'äl re' pa PDF.
+pdfjs-password-invalid = Man okel ta ri ewan tzij: Tatojtob'ej chik.
+pdfjs-password-ok-button = Ütz
+pdfjs-password-cancel-button = Tiq'at
+pdfjs-web-fonts-disabled = E chupül ri taq ajk'amaya'l tz'ib': man tikirel ta nokisäx ri taq tz'ib' PDF pa ch'ikenïk
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Rucholajem tz'ib'
+pdfjs-editor-free-text-button-label = Rucholajem tz'ib'
+pdfjs-editor-ink-button =
+ .title = Tiwachib'ëx
+pdfjs-editor-ink-button-label = Tiwachib'ëx
+# Editor Parameters
+pdfjs-editor-free-text-color-input = B'onil
+pdfjs-editor-free-text-size-input = Nimilem
+pdfjs-editor-ink-color-input = B'onil
+pdfjs-editor-ink-thickness-input = Rupimil
+pdfjs-editor-ink-opacity-input = Q'equmal
+pdfjs-free-text =
+ .aria-label = Nuk'unel tz'ib'atzij
+pdfjs-free-text-default-content = Titikitisäx rutz'ib'axik…
+pdfjs-ink =
+ .aria-label = Nuk'unel wachib'äl
+pdfjs-ink-canvas =
+ .aria-label = Wachib'äl nuk'un ruma okisaxel
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ckb/viewer.ftl b/web/locale/ckb/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..ae87335b8ba1a163dbe1cbb79c8da595a2c04089
--- /dev/null
+++ b/web/locale/ckb/viewer.ftl
@@ -0,0 +1,242 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = پەڕەی پێشوو
+pdfjs-previous-button-label = پێشوو
+pdfjs-next-button =
+ .title = پەڕەی دوواتر
+pdfjs-next-button-label = دوواتر
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = پەرە
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = لە { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } لە { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = ڕۆچوونی
+pdfjs-zoom-out-button-label = ڕۆچوونی
+pdfjs-zoom-in-button =
+ .title = هێنانەپێش
+pdfjs-zoom-in-button-label = هێنانەپێش
+pdfjs-zoom-select =
+ .title = زووم
+pdfjs-presentation-mode-button =
+ .title = گۆڕین بۆ دۆخی پێشکەشکردن
+pdfjs-presentation-mode-button-label = دۆخی پێشکەشکردن
+pdfjs-open-file-button =
+ .title = پەڕگە بکەرەوە
+pdfjs-open-file-button-label = کردنەوە
+pdfjs-print-button =
+ .title = چاپکردن
+pdfjs-print-button-label = چاپکردن
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ئامرازەکان
+pdfjs-tools-button-label = ئامرازەکان
+pdfjs-first-page-button =
+ .title = برۆ بۆ یەکەم پەڕە
+pdfjs-first-page-button-label = بڕۆ بۆ یەکەم پەڕە
+pdfjs-last-page-button =
+ .title = بڕۆ بۆ کۆتا پەڕە
+pdfjs-last-page-button-label = بڕۆ بۆ کۆتا پەڕە
+pdfjs-page-rotate-cw-button =
+ .title = ئاڕاستەی میلی کاتژمێر
+pdfjs-page-rotate-cw-button-label = ئاڕاستەی میلی کاتژمێر
+pdfjs-page-rotate-ccw-button =
+ .title = پێچەوانەی میلی کاتژمێر
+pdfjs-page-rotate-ccw-button-label = پێچەوانەی میلی کاتژمێر
+pdfjs-cursor-text-select-tool-button =
+ .title = توڵامرازی نیشانکەری دەق چالاک بکە
+pdfjs-cursor-text-select-tool-button-label = توڵامرازی نیشانکەری دەق
+pdfjs-cursor-hand-tool-button =
+ .title = توڵامرازی دەستی چالاک بکە
+pdfjs-cursor-hand-tool-button-label = توڵامرازی دەستی
+pdfjs-scroll-vertical-button =
+ .title = ناردنی ئەستوونی بەکاربێنە
+pdfjs-scroll-vertical-button-label = ناردنی ئەستوونی
+pdfjs-scroll-horizontal-button =
+ .title = ناردنی ئاسۆیی بەکاربێنە
+pdfjs-scroll-horizontal-button-label = ناردنی ئاسۆیی
+pdfjs-scroll-wrapped-button =
+ .title = ناردنی لوولکراو بەکاربێنە
+pdfjs-scroll-wrapped-button-label = ناردنی لوولکراو
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = تایبەتمەندییەکانی بەڵگەنامە...
+pdfjs-document-properties-button-label = تایبەتمەندییەکانی بەڵگەنامە...
+pdfjs-document-properties-file-name = ناوی پەڕگە:
+pdfjs-document-properties-file-size = قەبارەی پەڕگە:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } کب ({ $size_b } بایت)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } مب ({ $size_b } بایت)
+pdfjs-document-properties-title = سەردێڕ:
+pdfjs-document-properties-author = نووسەر
+pdfjs-document-properties-subject = بابەت:
+pdfjs-document-properties-keywords = کلیلەوشە:
+pdfjs-document-properties-creation-date = بەرواری درووستکردن:
+pdfjs-document-properties-modification-date = بەرواری دەستکاریکردن:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = درووستکەر:
+pdfjs-document-properties-producer = بەرهەمهێنەری PDF:
+pdfjs-document-properties-version = وەشانی PDF:
+pdfjs-document-properties-page-count = ژمارەی پەرەکان:
+pdfjs-document-properties-page-size = قەبارەی پەڕە:
+pdfjs-document-properties-page-size-unit-inches = ئینچ
+pdfjs-document-properties-page-size-unit-millimeters = ملم
+pdfjs-document-properties-page-size-orientation-portrait = پۆرترەیت(درێژ)
+pdfjs-document-properties-page-size-orientation-landscape = پانیی
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = نامە
+pdfjs-document-properties-page-size-name-legal = یاسایی
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = پیشاندانی وێبی خێرا:
+pdfjs-document-properties-linearized-yes = بەڵێ
+pdfjs-document-properties-linearized-no = نەخێر
+pdfjs-document-properties-close-button = داخستن
+
+## Print
+
+pdfjs-print-progress-message = بەڵگەنامە ئامادەدەکرێت بۆ چاپکردن...
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = پاشگەزبوونەوە
+pdfjs-printing-not-supported = ئاگاداربە: چاپکردن بە تەواوی پشتگیر ناکرێت لەم وێبگەڕە.
+pdfjs-printing-not-ready = ئاگاداربە: PDF بە تەواوی بارنەبووە بۆ چاپکردن.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = لاتەنیشت پیشاندان/شاردنەوە
+pdfjs-toggle-sidebar-button-label = لاتەنیشت پیشاندان/شاردنەوە
+pdfjs-document-outline-button-label = سنووری چوارچێوە
+pdfjs-attachments-button =
+ .title = پاشکۆکان پیشان بدە
+pdfjs-attachments-button-label = پاشکۆکان
+pdfjs-layers-button-label = چینەکان
+pdfjs-thumbs-button =
+ .title = وێنۆچکە پیشان بدە
+pdfjs-thumbs-button-label = وێنۆچکە
+pdfjs-findbar-button =
+ .title = لە بەڵگەنامە بگەرێ
+pdfjs-findbar-button-label = دۆزینەوە
+pdfjs-additional-layers = چینی زیاتر
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = پەڕەی { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = وێنۆچکەی پەڕەی { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = دۆزینەوە
+ .placeholder = لە بەڵگەنامە بگەرێ...
+pdfjs-find-previous-button =
+ .title = هەبوونی پێشوو بدۆزرەوە لە ڕستەکەدا
+pdfjs-find-previous-button-label = پێشوو
+pdfjs-find-next-button =
+ .title = هەبوونی داهاتوو بدۆزەرەوە لە ڕستەکەدا
+pdfjs-find-next-button-label = دوواتر
+pdfjs-find-highlight-checkbox = هەمووی نیشانە بکە
+pdfjs-find-match-case-checkbox-label = دۆخی لەیەکچوون
+pdfjs-find-entire-word-checkbox-label = هەموو وشەکان
+pdfjs-find-reached-top = گەشتیتە سەرەوەی بەڵگەنامە، لە خوارەوە دەستت پێکرد
+pdfjs-find-reached-bottom = گەشتیتە کۆتایی بەڵگەنامە. لەسەرەوە دەستت پێکرد
+pdfjs-find-not-found = نووسین نەدۆزرایەوە
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = پانی پەڕە
+pdfjs-page-scale-fit = پڕبوونی پەڕە
+pdfjs-page-scale-auto = زوومی خۆکار
+pdfjs-page-scale-actual = قەبارەی ڕاستی
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = هەڵەیەک ڕوویدا لە کاتی بارکردنی PDF.
+pdfjs-invalid-file-error = پەڕگەی pdf تێکچووە یان نەگونجاوە.
+pdfjs-missing-file-error = پەڕگەی pdf بوونی نیە.
+pdfjs-unexpected-response-error = وەڵامی ڕاژەخوازی نەخوازراو.
+pdfjs-rendering-error = هەڵەیەک ڕوویدا لە کاتی پوختەکردنی (ڕێندەر) پەڕە.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } سەرنج]
+
+## Password
+
+pdfjs-password-label = وشەی تێپەڕ بنووسە بۆ کردنەوەی پەڕگەی pdf.
+pdfjs-password-invalid = وشەی تێپەڕ هەڵەیە. تکایە دووبارە هەوڵ بدەرەوە.
+pdfjs-password-ok-button = باشە
+pdfjs-password-cancel-button = پاشگەزبوونەوە
+pdfjs-web-fonts-disabled = جۆرەپیتی وێب ناچالاکە: نەتوانی جۆرەپیتی تێخراوی ناو pdfـەکە بەکاربێت.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/cs/viewer.ftl b/web/locale/cs/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..b05761b0a30decf501f2e6708460954ac82067dc
--- /dev/null
+++ b/web/locale/cs/viewer.ftl
@@ -0,0 +1,406 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Přejde na předchozí stránku
+pdfjs-previous-button-label = Předchozí
+pdfjs-next-button =
+ .title = Přejde na následující stránku
+pdfjs-next-button-label = Další
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Stránka
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = z { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zmenší velikost
+pdfjs-zoom-out-button-label = Zmenšit
+pdfjs-zoom-in-button =
+ .title = Zvětší velikost
+pdfjs-zoom-in-button-label = Zvětšit
+pdfjs-zoom-select =
+ .title = Nastaví velikost
+pdfjs-presentation-mode-button =
+ .title = Přepne do režimu prezentace
+pdfjs-presentation-mode-button-label = Režim prezentace
+pdfjs-open-file-button =
+ .title = Otevře soubor
+pdfjs-open-file-button-label = Otevřít
+pdfjs-print-button =
+ .title = Vytiskne dokument
+pdfjs-print-button-label = Vytisknout
+pdfjs-save-button =
+ .title = Uložit
+pdfjs-save-button-label = Uložit
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Stáhnout
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Stáhnout
+pdfjs-bookmark-button =
+ .title = Aktuální stránka (zobrazit URL od aktuální stránky)
+pdfjs-bookmark-button-label = Aktuální stránka
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Otevřít v aplikaci
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Otevřít v aplikaci
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Nástroje
+pdfjs-tools-button-label = Nástroje
+pdfjs-first-page-button =
+ .title = Přejde na první stránku
+pdfjs-first-page-button-label = Přejít na první stránku
+pdfjs-last-page-button =
+ .title = Přejde na poslední stránku
+pdfjs-last-page-button-label = Přejít na poslední stránku
+pdfjs-page-rotate-cw-button =
+ .title = Otočí po směru hodin
+pdfjs-page-rotate-cw-button-label = Otočit po směru hodin
+pdfjs-page-rotate-ccw-button =
+ .title = Otočí proti směru hodin
+pdfjs-page-rotate-ccw-button-label = Otočit proti směru hodin
+pdfjs-cursor-text-select-tool-button =
+ .title = Povolí výběr textu
+pdfjs-cursor-text-select-tool-button-label = Výběr textu
+pdfjs-cursor-hand-tool-button =
+ .title = Povolí nástroj ručička
+pdfjs-cursor-hand-tool-button-label = Nástroj ručička
+pdfjs-scroll-page-button =
+ .title = Posouvat po stránkách
+pdfjs-scroll-page-button-label = Posouvání po stránkách
+pdfjs-scroll-vertical-button =
+ .title = Použít svislé posouvání
+pdfjs-scroll-vertical-button-label = Svislé posouvání
+pdfjs-scroll-horizontal-button =
+ .title = Použít vodorovné posouvání
+pdfjs-scroll-horizontal-button-label = Vodorovné posouvání
+pdfjs-scroll-wrapped-button =
+ .title = Použít postupné posouvání
+pdfjs-scroll-wrapped-button-label = Postupné posouvání
+pdfjs-spread-none-button =
+ .title = Nesdružovat stránky
+pdfjs-spread-none-button-label = Žádné sdružení
+pdfjs-spread-odd-button =
+ .title = Sdruží stránky s umístěním lichých vlevo
+pdfjs-spread-odd-button-label = Sdružení stránek (liché vlevo)
+pdfjs-spread-even-button =
+ .title = Sdruží stránky s umístěním sudých vlevo
+pdfjs-spread-even-button-label = Sdružení stránek (sudé vlevo)
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Vlastnosti dokumentu…
+pdfjs-document-properties-button-label = Vlastnosti dokumentu…
+pdfjs-document-properties-file-name = Název souboru:
+pdfjs-document-properties-file-size = Velikost souboru:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtů)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtů)
+pdfjs-document-properties-title = Název stránky:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Předmět:
+pdfjs-document-properties-keywords = Klíčová slova:
+pdfjs-document-properties-creation-date = Datum vytvoření:
+pdfjs-document-properties-modification-date = Datum úpravy:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Vytvořil:
+pdfjs-document-properties-producer = Tvůrce PDF:
+pdfjs-document-properties-version = Verze PDF:
+pdfjs-document-properties-page-count = Počet stránek:
+pdfjs-document-properties-page-size = Velikost stránky:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = na výšku
+pdfjs-document-properties-page-size-orientation-landscape = na šířku
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Dopis
+pdfjs-document-properties-page-size-name-legal = Právní dokument
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Rychlé zobrazování z webu:
+pdfjs-document-properties-linearized-yes = Ano
+pdfjs-document-properties-linearized-no = Ne
+pdfjs-document-properties-close-button = Zavřít
+
+## Print
+
+pdfjs-print-progress-message = Příprava dokumentu pro tisk…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress } %
+pdfjs-print-progress-close-button = Zrušit
+pdfjs-printing-not-supported = Upozornění: Tisk není v tomto prohlížeči plně podporován.
+pdfjs-printing-not-ready = Upozornění: Dokument PDF není kompletně načten.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Postranní lišta
+pdfjs-toggle-sidebar-notification-button =
+ .title = Přepnout postranní lištu (dokument obsahuje osnovu/přílohy/vrstvy)
+pdfjs-toggle-sidebar-button-label = Postranní lišta
+pdfjs-document-outline-button =
+ .title = Zobrazí osnovu dokumentu (poklepání přepne zobrazení všech položek)
+pdfjs-document-outline-button-label = Osnova dokumentu
+pdfjs-attachments-button =
+ .title = Zobrazí přílohy
+pdfjs-attachments-button-label = Přílohy
+pdfjs-layers-button =
+ .title = Zobrazit vrstvy (poklepáním obnovíte všechny vrstvy do výchozího stavu)
+pdfjs-layers-button-label = Vrstvy
+pdfjs-thumbs-button =
+ .title = Zobrazí náhledy
+pdfjs-thumbs-button-label = Náhledy
+pdfjs-current-outline-item-button =
+ .title = Najít aktuální položku v osnově
+pdfjs-current-outline-item-button-label = Aktuální položka v osnově
+pdfjs-findbar-button =
+ .title = Najde v dokumentu
+pdfjs-findbar-button-label = Najít
+pdfjs-additional-layers = Další vrstvy
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Strana { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Náhled strany { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Najít
+ .placeholder = Najít v dokumentu…
+pdfjs-find-previous-button =
+ .title = Najde předchozí výskyt hledaného textu
+pdfjs-find-previous-button-label = Předchozí
+pdfjs-find-next-button =
+ .title = Najde další výskyt hledaného textu
+pdfjs-find-next-button-label = Další
+pdfjs-find-highlight-checkbox = Zvýraznit
+pdfjs-find-match-case-checkbox-label = Rozlišovat velikost
+pdfjs-find-match-diacritics-checkbox-label = Rozlišovat diakritiku
+pdfjs-find-entire-word-checkbox-label = Celá slova
+pdfjs-find-reached-top = Dosažen začátek dokumentu, pokračuje se od konce
+pdfjs-find-reached-bottom = Dosažen konec dokumentu, pokračuje se od začátku
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current }. z { $total } výskytu
+ [few] { $current }. z { $total } výskytů
+ [many] { $current }. z { $total } výskytů
+ *[other] { $current }. z { $total } výskytů
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Více než { $limit } výskyt
+ [few] Více než { $limit } výskyty
+ [many] Více než { $limit } výskytů
+ *[other] Více než { $limit } výskytů
+ }
+pdfjs-find-not-found = Hledaný text nenalezen
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Podle šířky
+pdfjs-page-scale-fit = Podle výšky
+pdfjs-page-scale-auto = Automatická velikost
+pdfjs-page-scale-actual = Skutečná velikost
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale } %
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Strana { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Při nahrávání PDF nastala chyba.
+pdfjs-invalid-file-error = Neplatný nebo chybný soubor PDF.
+pdfjs-missing-file-error = Chybí soubor PDF.
+pdfjs-unexpected-response-error = Neočekávaná odpověď serveru.
+pdfjs-rendering-error = Při vykreslování stránky nastala chyba.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotace typu { $type }]
+
+## Password
+
+pdfjs-password-label = Pro otevření PDF souboru vložte heslo.
+pdfjs-password-invalid = Neplatné heslo. Zkuste to znovu.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Zrušit
+pdfjs-web-fonts-disabled = Webová písma jsou zakázána, proto není možné použít vložená písma PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Text
+pdfjs-editor-free-text-button-label = Text
+pdfjs-editor-ink-button =
+ .title = Kreslení
+pdfjs-editor-ink-button-label = Kreslení
+pdfjs-editor-stamp-button =
+ .title = Přidání či úprava obrázků
+pdfjs-editor-stamp-button-label = Přidání či úprava obrázků
+pdfjs-editor-highlight-button =
+ .title = Zvýraznění
+pdfjs-editor-highlight-button-label = Zvýraznění
+pdfjs-highlight-floating-button =
+ .title = Zvýraznit
+pdfjs-highlight-floating-button1 =
+ .title = Zvýraznit
+ .aria-label = Zvýraznit
+pdfjs-highlight-floating-button-label = Zvýraznit
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Odebrat kresbu
+pdfjs-editor-remove-freetext-button =
+ .title = Odebrat text
+pdfjs-editor-remove-stamp-button =
+ .title = Odebrat obrázek
+pdfjs-editor-remove-highlight-button =
+ .title = Odebrat zvýraznění
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Barva
+pdfjs-editor-free-text-size-input = Velikost
+pdfjs-editor-ink-color-input = Barva
+pdfjs-editor-ink-thickness-input = Tloušťka
+pdfjs-editor-ink-opacity-input = Průhlednost
+pdfjs-editor-stamp-add-image-button =
+ .title = Přidat obrázek
+pdfjs-editor-stamp-add-image-button-label = Přidat obrázek
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Tloušťka
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Změna tloušťky při zvýrazňování jiných položek než textu
+pdfjs-free-text =
+ .aria-label = Textový editor
+pdfjs-free-text-default-content = Začněte psát…
+pdfjs-ink =
+ .aria-label = Editor kreslení
+pdfjs-ink-canvas =
+ .aria-label = Uživatelem vytvořený obrázek
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Náhradní popis
+pdfjs-editor-alt-text-edit-button-label = Upravit náhradní popis
+pdfjs-editor-alt-text-dialog-label = Vyberte možnost
+pdfjs-editor-alt-text-dialog-description = Náhradní popis pomáhá, když lidé obrázek nevidí nebo když se nenačítá.
+pdfjs-editor-alt-text-add-description-label = Přidat popis
+pdfjs-editor-alt-text-add-description-description = Snažte se o 1-2 věty, které popisují předmět, prostředí nebo činnosti.
+pdfjs-editor-alt-text-mark-decorative-label = Označit jako dekorativní
+pdfjs-editor-alt-text-mark-decorative-description = Používá se pro okrasné obrázky, jako jsou rámečky nebo vodoznaky.
+pdfjs-editor-alt-text-cancel-button = Zrušit
+pdfjs-editor-alt-text-save-button = Uložit
+pdfjs-editor-alt-text-decorative-tooltip = Označen jako dekorativní
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Například: “Mladý muž si sedá ke stolu, aby se najedl.”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Levý horní roh — změna velikosti
+pdfjs-editor-resizer-label-top-middle = Horní střed — změna velikosti
+pdfjs-editor-resizer-label-top-right = Pravý horní roh — změna velikosti
+pdfjs-editor-resizer-label-middle-right = Vpravo uprostřed — změna velikosti
+pdfjs-editor-resizer-label-bottom-right = Pravý dolní roh — změna velikosti
+pdfjs-editor-resizer-label-bottom-middle = Střed dole — změna velikosti
+pdfjs-editor-resizer-label-bottom-left = Levý dolní roh — změna velikosti
+pdfjs-editor-resizer-label-middle-left = Vlevo uprostřed — změna velikosti
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Barva zvýraznění
+pdfjs-editor-colorpicker-button =
+ .title = Změna barvy
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Výběr barev
+pdfjs-editor-colorpicker-yellow =
+ .title = Žlutá
+pdfjs-editor-colorpicker-green =
+ .title = Zelená
+pdfjs-editor-colorpicker-blue =
+ .title = Modrá
+pdfjs-editor-colorpicker-pink =
+ .title = Růžová
+pdfjs-editor-colorpicker-red =
+ .title = Červená
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Zobrazit vše
+pdfjs-editor-highlight-show-all-button =
+ .title = Zobrazit vše
diff --git a/web/locale/cy/viewer.ftl b/web/locale/cy/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..061237abacdc66cdcc5621ac6fd5f214927e8bdc
--- /dev/null
+++ b/web/locale/cy/viewer.ftl
@@ -0,0 +1,410 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Tudalen Flaenorol
+pdfjs-previous-button-label = Blaenorol
+pdfjs-next-button =
+ .title = Tudalen Nesaf
+pdfjs-next-button-label = Nesaf
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Tudalen
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = o { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } o { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Lleihau
+pdfjs-zoom-out-button-label = Lleihau
+pdfjs-zoom-in-button =
+ .title = Cynyddu
+pdfjs-zoom-in-button-label = Cynyddu
+pdfjs-zoom-select =
+ .title = Chwyddo
+pdfjs-presentation-mode-button =
+ .title = Newid i'r Modd Cyflwyno
+pdfjs-presentation-mode-button-label = Modd Cyflwyno
+pdfjs-open-file-button =
+ .title = Agor Ffeil
+pdfjs-open-file-button-label = Agor
+pdfjs-print-button =
+ .title = Argraffu
+pdfjs-print-button-label = Argraffu
+pdfjs-save-button =
+ .title = Cadw
+pdfjs-save-button-label = Cadw
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Llwytho i lawr
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Llwytho i lawr
+pdfjs-bookmark-button =
+ .title = Tudalen Gyfredol (Gweld URL o'r Dudalen Gyfredol)
+pdfjs-bookmark-button-label = Tudalen Gyfredol
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Agor yn yr ap
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Agor yn yr ap
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Offer
+pdfjs-tools-button-label = Offer
+pdfjs-first-page-button =
+ .title = Mynd i'r Dudalen Gyntaf
+pdfjs-first-page-button-label = Mynd i'r Dudalen Gyntaf
+pdfjs-last-page-button =
+ .title = Mynd i'r Dudalen Olaf
+pdfjs-last-page-button-label = Mynd i'r Dudalen Olaf
+pdfjs-page-rotate-cw-button =
+ .title = Cylchdroi Clocwedd
+pdfjs-page-rotate-cw-button-label = Cylchdroi Clocwedd
+pdfjs-page-rotate-ccw-button =
+ .title = Cylchdroi Gwrthglocwedd
+pdfjs-page-rotate-ccw-button-label = Cylchdroi Gwrthglocwedd
+pdfjs-cursor-text-select-tool-button =
+ .title = Galluogi Dewis Offeryn Testun
+pdfjs-cursor-text-select-tool-button-label = Offeryn Dewis Testun
+pdfjs-cursor-hand-tool-button =
+ .title = Galluogi Offeryn Llaw
+pdfjs-cursor-hand-tool-button-label = Offeryn Llaw
+pdfjs-scroll-page-button =
+ .title = Defnyddio Sgrolio Tudalen
+pdfjs-scroll-page-button-label = Sgrolio Tudalen
+pdfjs-scroll-vertical-button =
+ .title = Defnyddio Sgrolio Fertigol
+pdfjs-scroll-vertical-button-label = Sgrolio Fertigol
+pdfjs-scroll-horizontal-button =
+ .title = Defnyddio Sgrolio Llorweddol
+pdfjs-scroll-horizontal-button-label = Sgrolio Llorweddol
+pdfjs-scroll-wrapped-button =
+ .title = Defnyddio Sgrolio Amlapio
+pdfjs-scroll-wrapped-button-label = Sgrolio Amlapio
+pdfjs-spread-none-button =
+ .title = Peidio uno trawsdaleniadau
+pdfjs-spread-none-button-label = Dim Trawsdaleniadau
+pdfjs-spread-odd-button =
+ .title = Uno trawsdaleniadau gan gychwyn gyda thudalennau odrif
+pdfjs-spread-odd-button-label = Trawsdaleniadau Odrif
+pdfjs-spread-even-button =
+ .title = Uno trawsdaleniadau gan gychwyn gyda thudalennau eilrif
+pdfjs-spread-even-button-label = Trawsdaleniadau Eilrif
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Priodweddau Dogfen…
+pdfjs-document-properties-button-label = Priodweddau Dogfen…
+pdfjs-document-properties-file-name = Enw ffeil:
+pdfjs-document-properties-file-size = Maint ffeil:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } beit)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } beit)
+pdfjs-document-properties-title = Teitl:
+pdfjs-document-properties-author = Awdur:
+pdfjs-document-properties-subject = Pwnc:
+pdfjs-document-properties-keywords = Allweddair:
+pdfjs-document-properties-creation-date = Dyddiad Creu:
+pdfjs-document-properties-modification-date = Dyddiad Addasu:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Crewr:
+pdfjs-document-properties-producer = Cynhyrchydd PDF:
+pdfjs-document-properties-version = Fersiwn PDF:
+pdfjs-document-properties-page-count = Cyfrif Tudalen:
+pdfjs-document-properties-page-size = Maint Tudalen:
+pdfjs-document-properties-page-size-unit-inches = o fewn
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portread
+pdfjs-document-properties-page-size-orientation-landscape = tirlun
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Llythyr
+pdfjs-document-properties-page-size-name-legal = Cyfreithiol
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Golwg Gwe Cyflym:
+pdfjs-document-properties-linearized-yes = Iawn
+pdfjs-document-properties-linearized-no = Na
+pdfjs-document-properties-close-button = Cau
+
+## Print
+
+pdfjs-print-progress-message = Paratoi dogfen ar gyfer ei hargraffu…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Diddymu
+pdfjs-printing-not-supported = Rhybudd: Nid yw argraffu yn cael ei gynnal yn llawn gan y porwr.
+pdfjs-printing-not-ready = Rhybudd: Nid yw'r PDF wedi ei lwytho'n llawn ar gyfer argraffu.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Toglo'r Bar Ochr
+pdfjs-toggle-sidebar-notification-button =
+ .title = Toglo'r Bar Ochr (mae'r ddogfen yn cynnwys amlinelliadau/atodiadau/haenau)
+pdfjs-toggle-sidebar-button-label = Toglo'r Bar Ochr
+pdfjs-document-outline-button =
+ .title = Dangos Amlinell Dogfen (clic dwbl i ymestyn/cau pob eitem)
+pdfjs-document-outline-button-label = Amlinelliad Dogfen
+pdfjs-attachments-button =
+ .title = Dangos Atodiadau
+pdfjs-attachments-button-label = Atodiadau
+pdfjs-layers-button =
+ .title = Dangos Haenau (cliciwch ddwywaith i ailosod yr holl haenau i'r cyflwr rhagosodedig)
+pdfjs-layers-button-label = Haenau
+pdfjs-thumbs-button =
+ .title = Dangos Lluniau Bach
+pdfjs-thumbs-button-label = Lluniau Bach
+pdfjs-current-outline-item-button =
+ .title = Canfod yr Eitem Amlinellol Gyfredol
+pdfjs-current-outline-item-button-label = Yr Eitem Amlinellol Gyfredol
+pdfjs-findbar-button =
+ .title = Canfod yn y Ddogfen
+pdfjs-findbar-button-label = Canfod
+pdfjs-additional-layers = Haenau Ychwanegol
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Tudalen { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Llun Bach Tudalen { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Canfod
+ .placeholder = Canfod yn y ddogfen…
+pdfjs-find-previous-button =
+ .title = Canfod enghraifft flaenorol o'r ymadrodd
+pdfjs-find-previous-button-label = Blaenorol
+pdfjs-find-next-button =
+ .title = Canfod enghraifft nesaf yr ymadrodd
+pdfjs-find-next-button-label = Nesaf
+pdfjs-find-highlight-checkbox = Amlygu Popeth
+pdfjs-find-match-case-checkbox-label = Cydweddu Maint
+pdfjs-find-match-diacritics-checkbox-label = Diacritigau Cyfatebol
+pdfjs-find-entire-word-checkbox-label = Geiriau Cyfan
+pdfjs-find-reached-top = Wedi cyrraedd brig y dudalen, parhau o'r gwaelod
+pdfjs-find-reached-bottom = Wedi cyrraedd diwedd y dudalen, parhau o'r brig
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [zero] { $current } o { $total } cydweddiadau
+ [one] { $current } o { $total } cydweddiad
+ [two] { $current } o { $total } gydweddiad
+ [few] { $current } o { $total } cydweddiad
+ [many] { $current } o { $total } chydweddiad
+ *[other] { $current } o { $total } cydweddiad
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [zero] Mwy nag { $limit } cydweddiadau
+ [one] Mwy nag { $limit } cydweddiad
+ [two] Mwy nag { $limit } gydweddiad
+ [few] Mwy nag { $limit } cydweddiad
+ [many] Mwy nag { $limit } chydweddiad
+ *[other] Mwy nag { $limit } cydweddiad
+ }
+pdfjs-find-not-found = Heb ganfod ymadrodd
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Lled Tudalen
+pdfjs-page-scale-fit = Ffit Tudalen
+pdfjs-page-scale-auto = Chwyddo Awtomatig
+pdfjs-page-scale-actual = Maint Gwirioneddol
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Tudalen { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Digwyddodd gwall wrth lwytho'r PDF.
+pdfjs-invalid-file-error = Ffeil PDF annilys neu llwgr.
+pdfjs-missing-file-error = Ffeil PDF coll.
+pdfjs-unexpected-response-error = Ymateb annisgwyl gan y gweinydd.
+pdfjs-rendering-error = Digwyddodd gwall wrth adeiladu'r dudalen.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anodiad { $type } ]
+
+## Password
+
+pdfjs-password-label = Rhowch gyfrinair i agor y PDF.
+pdfjs-password-invalid = Cyfrinair annilys. Ceisiwch eto.
+pdfjs-password-ok-button = Iawn
+pdfjs-password-cancel-button = Diddymu
+pdfjs-web-fonts-disabled = Ffontiau gwe wedi eu hanalluogi: methu defnyddio ffontiau PDF mewnblanedig.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Testun
+pdfjs-editor-free-text-button-label = Testun
+pdfjs-editor-ink-button =
+ .title = Lluniadu
+pdfjs-editor-ink-button-label = Lluniadu
+pdfjs-editor-stamp-button =
+ .title = Ychwanegu neu olygu delweddau
+pdfjs-editor-stamp-button-label = Ychwanegu neu olygu delweddau
+pdfjs-editor-highlight-button =
+ .title = Amlygu
+pdfjs-editor-highlight-button-label = Amlygu
+pdfjs-highlight-floating-button =
+ .title = Amlygu
+pdfjs-highlight-floating-button1 =
+ .title = Amlygu
+ .aria-label = Amlygu
+pdfjs-highlight-floating-button-label = Amlygu
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Dileu lluniad
+pdfjs-editor-remove-freetext-button =
+ .title = Dileu testun
+pdfjs-editor-remove-stamp-button =
+ .title = Dileu delwedd
+pdfjs-editor-remove-highlight-button =
+ .title = Tynnu amlygiad
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Lliw
+pdfjs-editor-free-text-size-input = Maint
+pdfjs-editor-ink-color-input = Lliw
+pdfjs-editor-ink-thickness-input = Trwch
+pdfjs-editor-ink-opacity-input = Didreiddedd
+pdfjs-editor-stamp-add-image-button =
+ .title = Ychwanegu delwedd
+pdfjs-editor-stamp-add-image-button-label = Ychwanegu delwedd
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Trwch
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Newid trwch wrth amlygu eitemau heblaw testun
+pdfjs-free-text =
+ .aria-label = Golygydd Testun
+pdfjs-free-text-default-content = Cychwyn teipio…
+pdfjs-ink =
+ .aria-label = Golygydd Lluniadu
+pdfjs-ink-canvas =
+ .aria-label = Delwedd wedi'i chreu gan ddefnyddwyr
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Testun amgen (alt)
+pdfjs-editor-alt-text-edit-button-label = Golygu testun amgen
+pdfjs-editor-alt-text-dialog-label = Dewisiadau
+pdfjs-editor-alt-text-dialog-description = Mae testun amgen (testun alt) yn helpu pan na all pobl weld y ddelwedd neu pan nad yw'n llwytho.
+pdfjs-editor-alt-text-add-description-label = Ychwanegu disgrifiad
+pdfjs-editor-alt-text-add-description-description = Anelwch at 1-2 frawddeg sy'n disgrifio'r pwnc, y cefndir neu'r gweithredoedd.
+pdfjs-editor-alt-text-mark-decorative-label = Marcio fel addurniadol
+pdfjs-editor-alt-text-mark-decorative-description = Mae'n cael ei ddefnyddio ar gyfer delweddau addurniadol, fel borderi neu farciau dŵr.
+pdfjs-editor-alt-text-cancel-button = Diddymu
+pdfjs-editor-alt-text-save-button = Cadw
+pdfjs-editor-alt-text-decorative-tooltip = Marcio fel addurniadol
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Er enghraifft, “Mae dyn ifanc yn eistedd wrth fwrdd i fwyta pryd bwyd”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Y gornel chwith uchaf — newid maint
+pdfjs-editor-resizer-label-top-middle = Canol uchaf - newid maint
+pdfjs-editor-resizer-label-top-right = Y gornel dde uchaf - newid maint
+pdfjs-editor-resizer-label-middle-right = De canol - newid maint
+pdfjs-editor-resizer-label-bottom-right = Y gornel dde isaf — newid maint
+pdfjs-editor-resizer-label-bottom-middle = Canol gwaelod — newid maint
+pdfjs-editor-resizer-label-bottom-left = Y gornel chwith isaf — newid maint
+pdfjs-editor-resizer-label-middle-left = Chwith canol — newid maint
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Lliw amlygu
+pdfjs-editor-colorpicker-button =
+ .title = Newid lliw
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Dewisiadau lliw
+pdfjs-editor-colorpicker-yellow =
+ .title = Melyn
+pdfjs-editor-colorpicker-green =
+ .title = Gwyrdd
+pdfjs-editor-colorpicker-blue =
+ .title = Glas
+pdfjs-editor-colorpicker-pink =
+ .title = Pinc
+pdfjs-editor-colorpicker-red =
+ .title = Coch
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Dangos y cyfan
+pdfjs-editor-highlight-show-all-button =
+ .title = Dangos y cyfan
diff --git a/web/locale/da/viewer.ftl b/web/locale/da/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..968b22ffc69810b5fda17ac10b84c552e2def2ec
--- /dev/null
+++ b/web/locale/da/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Forrige side
+pdfjs-previous-button-label = Forrige
+pdfjs-next-button =
+ .title = Næste side
+pdfjs-next-button-label = Næste
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Side
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = af { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } af { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoom ud
+pdfjs-zoom-out-button-label = Zoom ud
+pdfjs-zoom-in-button =
+ .title = Zoom ind
+pdfjs-zoom-in-button-label = Zoom ind
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Skift til fuldskærmsvisning
+pdfjs-presentation-mode-button-label = Fuldskærmsvisning
+pdfjs-open-file-button =
+ .title = Åbn fil
+pdfjs-open-file-button-label = Åbn
+pdfjs-print-button =
+ .title = Udskriv
+pdfjs-print-button-label = Udskriv
+pdfjs-save-button =
+ .title = Gem
+pdfjs-save-button-label = Gem
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Hent
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Hent
+pdfjs-bookmark-button =
+ .title = Aktuel side (vis URL fra den aktuelle side)
+pdfjs-bookmark-button-label = Aktuel side
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Åbn i app
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Åbn i app
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Funktioner
+pdfjs-tools-button-label = Funktioner
+pdfjs-first-page-button =
+ .title = Gå til første side
+pdfjs-first-page-button-label = Gå til første side
+pdfjs-last-page-button =
+ .title = Gå til sidste side
+pdfjs-last-page-button-label = Gå til sidste side
+pdfjs-page-rotate-cw-button =
+ .title = Roter med uret
+pdfjs-page-rotate-cw-button-label = Roter med uret
+pdfjs-page-rotate-ccw-button =
+ .title = Roter mod uret
+pdfjs-page-rotate-ccw-button-label = Roter mod uret
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktiver markeringsværktøj
+pdfjs-cursor-text-select-tool-button-label = Markeringsværktøj
+pdfjs-cursor-hand-tool-button =
+ .title = Aktiver håndværktøj
+pdfjs-cursor-hand-tool-button-label = Håndværktøj
+pdfjs-scroll-page-button =
+ .title = Brug sidescrolling
+pdfjs-scroll-page-button-label = Sidescrolling
+pdfjs-scroll-vertical-button =
+ .title = Brug vertikal scrolling
+pdfjs-scroll-vertical-button-label = Vertikal scrolling
+pdfjs-scroll-horizontal-button =
+ .title = Brug horisontal scrolling
+pdfjs-scroll-horizontal-button-label = Horisontal scrolling
+pdfjs-scroll-wrapped-button =
+ .title = Brug ombrudt scrolling
+pdfjs-scroll-wrapped-button-label = Ombrudt scrolling
+pdfjs-spread-none-button =
+ .title = Vis enkeltsider
+pdfjs-spread-none-button-label = Enkeltsider
+pdfjs-spread-odd-button =
+ .title = Vis opslag med ulige sidenumre til venstre
+pdfjs-spread-odd-button-label = Opslag med forside
+pdfjs-spread-even-button =
+ .title = Vis opslag med lige sidenumre til venstre
+pdfjs-spread-even-button-label = Opslag uden forside
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumentegenskaber…
+pdfjs-document-properties-button-label = Dokumentegenskaber…
+pdfjs-document-properties-file-name = Filnavn:
+pdfjs-document-properties-file-size = Filstørrelse:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Titel:
+pdfjs-document-properties-author = Forfatter:
+pdfjs-document-properties-subject = Emne:
+pdfjs-document-properties-keywords = Nøgleord:
+pdfjs-document-properties-creation-date = Oprettet:
+pdfjs-document-properties-modification-date = Redigeret:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Program:
+pdfjs-document-properties-producer = PDF-producent:
+pdfjs-document-properties-version = PDF-version:
+pdfjs-document-properties-page-count = Antal sider:
+pdfjs-document-properties-page-size = Sidestørrelse:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = stående
+pdfjs-document-properties-page-size-orientation-landscape = liggende
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Hurtig web-visning:
+pdfjs-document-properties-linearized-yes = Ja
+pdfjs-document-properties-linearized-no = Nej
+pdfjs-document-properties-close-button = Luk
+
+## Print
+
+pdfjs-print-progress-message = Forbereder dokument til udskrivning…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Annuller
+pdfjs-printing-not-supported = Advarsel: Udskrivning er ikke fuldt understøttet af browseren.
+pdfjs-printing-not-ready = Advarsel: PDF-filen er ikke fuldt indlæst til udskrivning.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Slå sidepanel til eller fra
+pdfjs-toggle-sidebar-notification-button =
+ .title = Slå sidepanel til eller fra (dokumentet indeholder disposition/vedhæftede filer/lag)
+pdfjs-toggle-sidebar-button-label = Slå sidepanel til eller fra
+pdfjs-document-outline-button =
+ .title = Vis dokumentets disposition (dobbeltklik for at vise/skjule alle elementer)
+pdfjs-document-outline-button-label = Dokument-disposition
+pdfjs-attachments-button =
+ .title = Vis vedhæftede filer
+pdfjs-attachments-button-label = Vedhæftede filer
+pdfjs-layers-button =
+ .title = Vis lag (dobbeltklik for at nulstille alle lag til standard-tilstanden)
+pdfjs-layers-button-label = Lag
+pdfjs-thumbs-button =
+ .title = Vis miniaturer
+pdfjs-thumbs-button-label = Miniaturer
+pdfjs-current-outline-item-button =
+ .title = Find det aktuelle dispositions-element
+pdfjs-current-outline-item-button-label = Aktuelt dispositions-element
+pdfjs-findbar-button =
+ .title = Find i dokument
+pdfjs-findbar-button-label = Find
+pdfjs-additional-layers = Yderligere lag
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Side { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniature af side { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Find
+ .placeholder = Find i dokument…
+pdfjs-find-previous-button =
+ .title = Find den forrige forekomst
+pdfjs-find-previous-button-label = Forrige
+pdfjs-find-next-button =
+ .title = Find den næste forekomst
+pdfjs-find-next-button-label = Næste
+pdfjs-find-highlight-checkbox = Fremhæv alle
+pdfjs-find-match-case-checkbox-label = Forskel på store og små bogstaver
+pdfjs-find-match-diacritics-checkbox-label = Diakritiske tegn
+pdfjs-find-entire-word-checkbox-label = Hele ord
+pdfjs-find-reached-top = Toppen af siden blev nået, fortsatte fra bunden
+pdfjs-find-reached-bottom = Bunden af siden blev nået, fortsatte fra toppen
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } af { $total } forekomst
+ *[other] { $current } af { $total } forekomster
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Mere end { $limit } forekomst
+ *[other] Mere end { $limit } forekomster
+ }
+pdfjs-find-not-found = Der blev ikke fundet noget
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Sidebredde
+pdfjs-page-scale-fit = Tilpas til side
+pdfjs-page-scale-auto = Automatisk zoom
+pdfjs-page-scale-actual = Faktisk størrelse
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Side { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Der opstod en fejl ved indlæsning af PDF-filen.
+pdfjs-invalid-file-error = PDF-filen er ugyldig eller ødelagt.
+pdfjs-missing-file-error = Manglende PDF-fil.
+pdfjs-unexpected-response-error = Uventet svar fra serveren.
+pdfjs-rendering-error = Der opstod en fejl ved generering af siden.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type }kommentar]
+
+## Password
+
+pdfjs-password-label = Angiv adgangskode til at åbne denne PDF-fil.
+pdfjs-password-invalid = Ugyldig adgangskode. Prøv igen.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Fortryd
+pdfjs-web-fonts-disabled = Webskrifttyper er deaktiverede. De indlejrede skrifttyper i PDF-filen kan ikke anvendes.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+pdfjs-editor-ink-button =
+ .title = Tegn
+pdfjs-editor-ink-button-label = Tegn
+pdfjs-editor-stamp-button =
+ .title = Tilføj eller rediger billeder
+pdfjs-editor-stamp-button-label = Tilføj eller rediger billeder
+pdfjs-editor-highlight-button =
+ .title = Fremhæv
+pdfjs-editor-highlight-button-label = Fremhæv
+pdfjs-highlight-floating-button =
+ .title = Fremhæv
+pdfjs-highlight-floating-button1 =
+ .title = Fremhæv
+ .aria-label = Fremhæv
+pdfjs-highlight-floating-button-label = Fremhæv
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Fjern tegning
+pdfjs-editor-remove-freetext-button =
+ .title = Fjern tekst
+pdfjs-editor-remove-stamp-button =
+ .title = Fjern billede
+pdfjs-editor-remove-highlight-button =
+ .title = Fjern fremhævning
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Farve
+pdfjs-editor-free-text-size-input = Størrelse
+pdfjs-editor-ink-color-input = Farve
+pdfjs-editor-ink-thickness-input = Tykkelse
+pdfjs-editor-ink-opacity-input = Uigennemsigtighed
+pdfjs-editor-stamp-add-image-button =
+ .title = Tilføj billede
+pdfjs-editor-stamp-add-image-button-label = Tilføj billede
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Tykkelse
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Ændr tykkelse, når andre elementer end tekst fremhæves
+pdfjs-free-text =
+ .aria-label = Teksteditor
+pdfjs-free-text-default-content = Begynd at skrive…
+pdfjs-ink =
+ .aria-label = Tegnings-editor
+pdfjs-ink-canvas =
+ .aria-label = Brugeroprettet billede
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternativ tekst
+pdfjs-editor-alt-text-edit-button-label = Rediger alternativ tekst
+pdfjs-editor-alt-text-dialog-label = Vælg en indstilling
+pdfjs-editor-alt-text-dialog-description = Alternativ tekst hjælper folk, som ikke kan se billedet eller når det ikke indlæses.
+pdfjs-editor-alt-text-add-description-label = Tilføj en beskrivelse
+pdfjs-editor-alt-text-add-description-description = Sigt efter en eller to sætninger, der beskriver emnet, omgivelserne eller handlinger.
+pdfjs-editor-alt-text-mark-decorative-label = Marker som dekorativ
+pdfjs-editor-alt-text-mark-decorative-description = Dette bruges for dekorative billeder som rammer eller vandmærker.
+pdfjs-editor-alt-text-cancel-button = Annuller
+pdfjs-editor-alt-text-save-button = Gem
+pdfjs-editor-alt-text-decorative-tooltip = Markeret som dekorativ
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = For eksempel: "En ung mand sætter sig ved et bord for at spise et måltid mad"
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Øverste venstre hjørne — tilpas størrelse
+pdfjs-editor-resizer-label-top-middle = Øverste i midten — tilpas størrelse
+pdfjs-editor-resizer-label-top-right = Øverste højre hjørne — tilpas størrelse
+pdfjs-editor-resizer-label-middle-right = Midten til højre — tilpas størrelse
+pdfjs-editor-resizer-label-bottom-right = Nederste højre hjørne - tilpas størrelse
+pdfjs-editor-resizer-label-bottom-middle = Nederst i midten - tilpas størrelse
+pdfjs-editor-resizer-label-bottom-left = Nederste venstre hjørne - tilpas størrelse
+pdfjs-editor-resizer-label-middle-left = Midten til venstre — tilpas størrelse
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Fremhævningsfarve
+pdfjs-editor-colorpicker-button =
+ .title = Skift farve
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Farvevalg
+pdfjs-editor-colorpicker-yellow =
+ .title = Gul
+pdfjs-editor-colorpicker-green =
+ .title = Grøn
+pdfjs-editor-colorpicker-blue =
+ .title = Blå
+pdfjs-editor-colorpicker-pink =
+ .title = Lyserød
+pdfjs-editor-colorpicker-red =
+ .title = Rød
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Vis alle
+pdfjs-editor-highlight-show-all-button =
+ .title = Vis alle
diff --git a/web/locale/de/viewer.ftl b/web/locale/de/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..da073aa17bf6b6a99a431dbf5a0a707ec845945c
--- /dev/null
+++ b/web/locale/de/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Eine Seite zurück
+pdfjs-previous-button-label = Zurück
+pdfjs-next-button =
+ .title = Eine Seite vor
+pdfjs-next-button-label = Vor
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Seite
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = von { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } von { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Verkleinern
+pdfjs-zoom-out-button-label = Verkleinern
+pdfjs-zoom-in-button =
+ .title = Vergrößern
+pdfjs-zoom-in-button-label = Vergrößern
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = In Präsentationsmodus wechseln
+pdfjs-presentation-mode-button-label = Präsentationsmodus
+pdfjs-open-file-button =
+ .title = Datei öffnen
+pdfjs-open-file-button-label = Öffnen
+pdfjs-print-button =
+ .title = Drucken
+pdfjs-print-button-label = Drucken
+pdfjs-save-button =
+ .title = Speichern
+pdfjs-save-button-label = Speichern
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Herunterladen
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Herunterladen
+pdfjs-bookmark-button =
+ .title = Aktuelle Seite (URL von aktueller Seite anzeigen)
+pdfjs-bookmark-button-label = Aktuelle Seite
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Mit App öffnen
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Mit App öffnen
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Werkzeuge
+pdfjs-tools-button-label = Werkzeuge
+pdfjs-first-page-button =
+ .title = Erste Seite anzeigen
+pdfjs-first-page-button-label = Erste Seite anzeigen
+pdfjs-last-page-button =
+ .title = Letzte Seite anzeigen
+pdfjs-last-page-button-label = Letzte Seite anzeigen
+pdfjs-page-rotate-cw-button =
+ .title = Im Uhrzeigersinn drehen
+pdfjs-page-rotate-cw-button-label = Im Uhrzeigersinn drehen
+pdfjs-page-rotate-ccw-button =
+ .title = Gegen Uhrzeigersinn drehen
+pdfjs-page-rotate-ccw-button-label = Gegen Uhrzeigersinn drehen
+pdfjs-cursor-text-select-tool-button =
+ .title = Textauswahl-Werkzeug aktivieren
+pdfjs-cursor-text-select-tool-button-label = Textauswahl-Werkzeug
+pdfjs-cursor-hand-tool-button =
+ .title = Hand-Werkzeug aktivieren
+pdfjs-cursor-hand-tool-button-label = Hand-Werkzeug
+pdfjs-scroll-page-button =
+ .title = Seiten einzeln anordnen
+pdfjs-scroll-page-button-label = Einzelseitenanordnung
+pdfjs-scroll-vertical-button =
+ .title = Seiten übereinander anordnen
+pdfjs-scroll-vertical-button-label = Vertikale Seitenanordnung
+pdfjs-scroll-horizontal-button =
+ .title = Seiten nebeneinander anordnen
+pdfjs-scroll-horizontal-button-label = Horizontale Seitenanordnung
+pdfjs-scroll-wrapped-button =
+ .title = Seiten neben- und übereinander anordnen, abhängig vom Platz
+pdfjs-scroll-wrapped-button-label = Kombinierte Seitenanordnung
+pdfjs-spread-none-button =
+ .title = Seiten nicht nebeneinander anzeigen
+pdfjs-spread-none-button-label = Einzelne Seiten
+pdfjs-spread-odd-button =
+ .title = Jeweils eine ungerade und eine gerade Seite nebeneinander anzeigen
+pdfjs-spread-odd-button-label = Ungerade + gerade Seite
+pdfjs-spread-even-button =
+ .title = Jeweils eine gerade und eine ungerade Seite nebeneinander anzeigen
+pdfjs-spread-even-button-label = Gerade + ungerade Seite
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumenteigenschaften
+pdfjs-document-properties-button-label = Dokumenteigenschaften…
+pdfjs-document-properties-file-name = Dateiname:
+pdfjs-document-properties-file-size = Dateigröße:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } Bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } Bytes)
+pdfjs-document-properties-title = Titel:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Thema:
+pdfjs-document-properties-keywords = Stichwörter:
+pdfjs-document-properties-creation-date = Erstelldatum:
+pdfjs-document-properties-modification-date = Bearbeitungsdatum:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date } { $time }
+pdfjs-document-properties-creator = Anwendung:
+pdfjs-document-properties-producer = PDF erstellt mit:
+pdfjs-document-properties-version = PDF-Version:
+pdfjs-document-properties-page-count = Seitenzahl:
+pdfjs-document-properties-page-size = Seitengröße:
+pdfjs-document-properties-page-size-unit-inches = Zoll
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = Hochformat
+pdfjs-document-properties-page-size-orientation-landscape = Querformat
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Schnelle Webanzeige:
+pdfjs-document-properties-linearized-yes = Ja
+pdfjs-document-properties-linearized-no = Nein
+pdfjs-document-properties-close-button = Schließen
+
+## Print
+
+pdfjs-print-progress-message = Dokument wird für Drucken vorbereitet…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress } %
+pdfjs-print-progress-close-button = Abbrechen
+pdfjs-printing-not-supported = Warnung: Die Drucken-Funktion wird durch diesen Browser nicht vollständig unterstützt.
+pdfjs-printing-not-ready = Warnung: Die PDF-Datei ist nicht vollständig geladen, dies ist für das Drucken aber empfohlen.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Sidebar umschalten
+pdfjs-toggle-sidebar-notification-button =
+ .title = Sidebar umschalten (Dokument enthält Dokumentstruktur/Anhänge/Ebenen)
+pdfjs-toggle-sidebar-button-label = Sidebar umschalten
+pdfjs-document-outline-button =
+ .title = Dokumentstruktur anzeigen (Doppelklicken, um alle Einträge aus- bzw. einzuklappen)
+pdfjs-document-outline-button-label = Dokumentstruktur
+pdfjs-attachments-button =
+ .title = Anhänge anzeigen
+pdfjs-attachments-button-label = Anhänge
+pdfjs-layers-button =
+ .title = Ebenen anzeigen (Doppelklicken, um alle Ebenen auf den Standardzustand zurückzusetzen)
+pdfjs-layers-button-label = Ebenen
+pdfjs-thumbs-button =
+ .title = Miniaturansichten anzeigen
+pdfjs-thumbs-button-label = Miniaturansichten
+pdfjs-current-outline-item-button =
+ .title = Aktuelles Struktur-Element finden
+pdfjs-current-outline-item-button-label = Aktuelles Struktur-Element
+pdfjs-findbar-button =
+ .title = Dokument durchsuchen
+pdfjs-findbar-button-label = Suchen
+pdfjs-additional-layers = Zusätzliche Ebenen
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Seite { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniaturansicht von Seite { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Suchen
+ .placeholder = Dokument durchsuchen…
+pdfjs-find-previous-button =
+ .title = Vorheriges Vorkommen des Suchbegriffs finden
+pdfjs-find-previous-button-label = Zurück
+pdfjs-find-next-button =
+ .title = Nächstes Vorkommen des Suchbegriffs finden
+pdfjs-find-next-button-label = Weiter
+pdfjs-find-highlight-checkbox = Alle hervorheben
+pdfjs-find-match-case-checkbox-label = Groß-/Kleinschreibung beachten
+pdfjs-find-match-diacritics-checkbox-label = Akzente
+pdfjs-find-entire-word-checkbox-label = Ganze Wörter
+pdfjs-find-reached-top = Anfang des Dokuments erreicht, fahre am Ende fort
+pdfjs-find-reached-bottom = Ende des Dokuments erreicht, fahre am Anfang fort
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } von { $total } Übereinstimmung
+ *[other] { $current } von { $total } Übereinstimmungen
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Mehr als { $limit } Übereinstimmung
+ *[other] Mehr als { $limit } Übereinstimmungen
+ }
+pdfjs-find-not-found = Suchbegriff nicht gefunden
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Seitenbreite
+pdfjs-page-scale-fit = Seitengröße
+pdfjs-page-scale-auto = Automatischer Zoom
+pdfjs-page-scale-actual = Originalgröße
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale } %
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Seite { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Beim Laden der PDF-Datei trat ein Fehler auf.
+pdfjs-invalid-file-error = Ungültige oder beschädigte PDF-Datei
+pdfjs-missing-file-error = Fehlende PDF-Datei
+pdfjs-unexpected-response-error = Unerwartete Antwort des Servers
+pdfjs-rendering-error = Beim Darstellen der Seite trat ein Fehler auf.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anlage: { $type }]
+
+## Password
+
+pdfjs-password-label = Geben Sie zum Öffnen der PDF-Datei deren Passwort ein.
+pdfjs-password-invalid = Falsches Passwort. Bitte versuchen Sie es erneut.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Abbrechen
+pdfjs-web-fonts-disabled = Web-Schriftarten sind deaktiviert: Eingebettete PDF-Schriftarten konnten nicht geladen werden.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Text
+pdfjs-editor-free-text-button-label = Text
+pdfjs-editor-ink-button =
+ .title = Zeichnen
+pdfjs-editor-ink-button-label = Zeichnen
+pdfjs-editor-stamp-button =
+ .title = Grafiken hinzufügen oder bearbeiten
+pdfjs-editor-stamp-button-label = Grafiken hinzufügen oder bearbeiten
+pdfjs-editor-highlight-button =
+ .title = Hervorheben
+pdfjs-editor-highlight-button-label = Hervorheben
+pdfjs-highlight-floating-button =
+ .title = Hervorheben
+pdfjs-highlight-floating-button1 =
+ .title = Hervorheben
+ .aria-label = Hervorheben
+pdfjs-highlight-floating-button-label = Hervorheben
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Zeichnung entfernen
+pdfjs-editor-remove-freetext-button =
+ .title = Text entfernen
+pdfjs-editor-remove-stamp-button =
+ .title = Grafik entfernen
+pdfjs-editor-remove-highlight-button =
+ .title = Hervorhebung entfernen
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Farbe
+pdfjs-editor-free-text-size-input = Größe
+pdfjs-editor-ink-color-input = Farbe
+pdfjs-editor-ink-thickness-input = Linienstärke
+pdfjs-editor-ink-opacity-input = Deckkraft
+pdfjs-editor-stamp-add-image-button =
+ .title = Grafik hinzufügen
+pdfjs-editor-stamp-add-image-button-label = Grafik hinzufügen
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Linienstärke
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Linienstärke beim Hervorheben anderer Elemente als Text ändern
+pdfjs-free-text =
+ .aria-label = Texteditor
+pdfjs-free-text-default-content = Schreiben beginnen…
+pdfjs-ink =
+ .aria-label = Zeichnungseditor
+pdfjs-ink-canvas =
+ .aria-label = Vom Benutzer erstelltes Bild
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternativ-Text
+pdfjs-editor-alt-text-edit-button-label = Alternativ-Text bearbeiten
+pdfjs-editor-alt-text-dialog-label = Option wählen
+pdfjs-editor-alt-text-dialog-description = Alt-Text (Alternativtext) hilft, wenn Personen die Grafik nicht sehen können oder wenn sie nicht geladen wird.
+pdfjs-editor-alt-text-add-description-label = Beschreibung hinzufügen
+pdfjs-editor-alt-text-add-description-description = Ziel sind 1-2 Sätze, die das Thema, das Szenario oder Aktionen beschreiben.
+pdfjs-editor-alt-text-mark-decorative-label = Als dekorativ markieren
+pdfjs-editor-alt-text-mark-decorative-description = Dies wird für Ziergrafiken wie Ränder oder Wasserzeichen verwendet.
+pdfjs-editor-alt-text-cancel-button = Abbrechen
+pdfjs-editor-alt-text-save-button = Speichern
+pdfjs-editor-alt-text-decorative-tooltip = Als dekorativ markiert
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Zum Beispiel: "Ein junger Mann setzt sich an einen Tisch, um zu essen."
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Linke obere Ecke - Größe ändern
+pdfjs-editor-resizer-label-top-middle = Oben mittig - Größe ändern
+pdfjs-editor-resizer-label-top-right = Rechts oben - Größe ändern
+pdfjs-editor-resizer-label-middle-right = Mitte rechts - Größe ändern
+pdfjs-editor-resizer-label-bottom-right = Rechte untere Ecke - Größe ändern
+pdfjs-editor-resizer-label-bottom-middle = Unten mittig - Größe ändern
+pdfjs-editor-resizer-label-bottom-left = Linke untere Ecke - Größe ändern
+pdfjs-editor-resizer-label-middle-left = Mitte links - Größe ändern
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Hervorhebungsfarbe
+pdfjs-editor-colorpicker-button =
+ .title = Farbe ändern
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Farbauswahl
+pdfjs-editor-colorpicker-yellow =
+ .title = Gelb
+pdfjs-editor-colorpicker-green =
+ .title = Grün
+pdfjs-editor-colorpicker-blue =
+ .title = Blau
+pdfjs-editor-colorpicker-pink =
+ .title = Pink
+pdfjs-editor-colorpicker-red =
+ .title = Rot
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Alle anzeigen
+pdfjs-editor-highlight-show-all-button =
+ .title = Alle anzeigen
diff --git a/web/locale/dsb/viewer.ftl b/web/locale/dsb/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..e86f8153e608d381f1f4cf52c73ccf04a5f6a11e
--- /dev/null
+++ b/web/locale/dsb/viewer.ftl
@@ -0,0 +1,406 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pjerwjejšny bok
+pdfjs-previous-button-label = Slědk
+pdfjs-next-button =
+ .title = Pśiducy bok
+pdfjs-next-button-label = Dalej
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Bok
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = z { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Pómjeńšyś
+pdfjs-zoom-out-button-label = Pómjeńšyś
+pdfjs-zoom-in-button =
+ .title = Pówětšyś
+pdfjs-zoom-in-button-label = Pówětšyś
+pdfjs-zoom-select =
+ .title = Skalěrowanje
+pdfjs-presentation-mode-button =
+ .title = Do prezentaciskego modusa pśejś
+pdfjs-presentation-mode-button-label = Prezentaciski modus
+pdfjs-open-file-button =
+ .title = Dataju wócyniś
+pdfjs-open-file-button-label = Wócyniś
+pdfjs-print-button =
+ .title = Śišćaś
+pdfjs-print-button-label = Śišćaś
+pdfjs-save-button =
+ .title = Składowaś
+pdfjs-save-button-label = Składowaś
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Ześěgnuś
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Ześěgnuś
+pdfjs-bookmark-button =
+ .title = Aktualny bok (URL z aktualnego boka pokazaś)
+pdfjs-bookmark-button-label = Aktualny bok
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = W nałoženju wócyniś
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = W nałoženju wócyniś
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Rědy
+pdfjs-tools-button-label = Rědy
+pdfjs-first-page-button =
+ .title = K prědnemu bokoju
+pdfjs-first-page-button-label = K prědnemu bokoju
+pdfjs-last-page-button =
+ .title = K slědnemu bokoju
+pdfjs-last-page-button-label = K slědnemu bokoju
+pdfjs-page-rotate-cw-button =
+ .title = Wobwjertnuś ako špěra źo
+pdfjs-page-rotate-cw-button-label = Wobwjertnuś ako špěra źo
+pdfjs-page-rotate-ccw-button =
+ .title = Wobwjertnuś nawopaki ako špěra źo
+pdfjs-page-rotate-ccw-button-label = Wobwjertnuś nawopaki ako špěra źo
+pdfjs-cursor-text-select-tool-button =
+ .title = Rěd za wuběranje teksta zmóžniś
+pdfjs-cursor-text-select-tool-button-label = Rěd za wuběranje teksta
+pdfjs-cursor-hand-tool-button =
+ .title = Rucny rěd zmóžniś
+pdfjs-cursor-hand-tool-button-label = Rucny rěd
+pdfjs-scroll-page-button =
+ .title = Kulanje boka wužywaś
+pdfjs-scroll-page-button-label = Kulanje boka
+pdfjs-scroll-vertical-button =
+ .title = Wertikalne suwanje wužywaś
+pdfjs-scroll-vertical-button-label = Wertikalne suwanje
+pdfjs-scroll-horizontal-button =
+ .title = Horicontalne suwanje wužywaś
+pdfjs-scroll-horizontal-button-label = Horicontalne suwanje
+pdfjs-scroll-wrapped-button =
+ .title = Pózlažke suwanje wužywaś
+pdfjs-scroll-wrapped-button-label = Pózlažke suwanje
+pdfjs-spread-none-button =
+ .title = Boki njezwězaś
+pdfjs-spread-none-button-label = Žeden dwójny bok
+pdfjs-spread-odd-button =
+ .title = Boki zachopinajucy z njerownymi bokami zwězaś
+pdfjs-spread-odd-button-label = Njerowne boki
+pdfjs-spread-even-button =
+ .title = Boki zachopinajucy z rownymi bokami zwězaś
+pdfjs-spread-even-button-label = Rowne boki
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumentowe kakosći…
+pdfjs-document-properties-button-label = Dokumentowe kakosći…
+pdfjs-document-properties-file-name = Mě dataje:
+pdfjs-document-properties-file-size = Wjelikosć dataje:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtow)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtow)
+pdfjs-document-properties-title = Titel:
+pdfjs-document-properties-author = Awtor:
+pdfjs-document-properties-subject = Tema:
+pdfjs-document-properties-keywords = Klucowe słowa:
+pdfjs-document-properties-creation-date = Datum napóranja:
+pdfjs-document-properties-modification-date = Datum změny:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Awtor:
+pdfjs-document-properties-producer = PDF-gótowaŕ:
+pdfjs-document-properties-version = PDF-wersija:
+pdfjs-document-properties-page-count = Licba bokow:
+pdfjs-document-properties-page-size = Wjelikosć boka:
+pdfjs-document-properties-page-size-unit-inches = col
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = wusoki format
+pdfjs-document-properties-page-size-orientation-landscape = prěcny format
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = Jo
+pdfjs-document-properties-linearized-no = Ně
+pdfjs-document-properties-close-button = Zacyniś
+
+## Print
+
+pdfjs-print-progress-message = Dokument pśigótujo se za śišćanje…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Pśetergnuś
+pdfjs-printing-not-supported = Warnowanje: Śišćanje njepódpěra se połnje pśez toś ten wobglědowak.
+pdfjs-printing-not-ready = Warnowanje: PDF njejo se za śišćanje dopołnje zacytał.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Bócnicu pokazaś/schowaś
+pdfjs-toggle-sidebar-notification-button =
+ .title = Bocnicu pśešaltowaś (dokument rozrědowanje/pśipiski/warstwy wopśimujo)
+pdfjs-toggle-sidebar-button-label = Bócnicu pokazaś/schowaś
+pdfjs-document-outline-button =
+ .title = Dokumentowe naraźenje pokazaś (dwójne kliknjenje, aby se wšykne zapiski pokazali/schowali)
+pdfjs-document-outline-button-label = Dokumentowa struktura
+pdfjs-attachments-button =
+ .title = Pśidanki pokazaś
+pdfjs-attachments-button-label = Pśidanki
+pdfjs-layers-button =
+ .title = Warstwy pokazaś (klikniśo dwójcy, aby wšykne warstwy na standardny staw slědk stajił)
+pdfjs-layers-button-label = Warstwy
+pdfjs-thumbs-button =
+ .title = Miniatury pokazaś
+pdfjs-thumbs-button-label = Miniatury
+pdfjs-current-outline-item-button =
+ .title = Aktualny rozrědowański zapisk pytaś
+pdfjs-current-outline-item-button-label = Aktualny rozrědowański zapisk
+pdfjs-findbar-button =
+ .title = W dokumenśe pytaś
+pdfjs-findbar-button-label = Pytaś
+pdfjs-additional-layers = Dalšne warstwy
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Bok { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura boka { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Pytaś
+ .placeholder = W dokumenśe pytaś…
+pdfjs-find-previous-button =
+ .title = Pjerwjejšne wustupowanje pytańskego wuraza pytaś
+pdfjs-find-previous-button-label = Slědk
+pdfjs-find-next-button =
+ .title = Pśidujuce wustupowanje pytańskego wuraza pytaś
+pdfjs-find-next-button-label = Dalej
+pdfjs-find-highlight-checkbox = Wšykne wuzwignuś
+pdfjs-find-match-case-checkbox-label = Na wjelikopisanje źiwaś
+pdfjs-find-match-diacritics-checkbox-label = Diakritiske znamuška wužywaś
+pdfjs-find-entire-word-checkbox-label = Cełe słowa
+pdfjs-find-reached-top = Zachopjeńk dokumenta dostany, pókšacujo se z kóńcom
+pdfjs-find-reached-bottom = Kóńc dokumenta dostany, pókšacujo se ze zachopjeńkom
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } z { $total } wótpowědnika
+ [two] { $current } z { $total } wótpowědnikowu
+ [few] { $current } z { $total } wótpowědnikow
+ *[other] { $current } z { $total } wótpowědnikow
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Wušej { $limit } wótpowědnik
+ [two] Wušej { $limit } wótpowědnika
+ [few] Wušej { $limit } wótpowědniki
+ *[other] Wušej { $limit } wótpowědniki
+ }
+pdfjs-find-not-found = Pytański wuraz njejo se namakał
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Šyrokosć boka
+pdfjs-page-scale-fit = Wjelikosć boka
+pdfjs-page-scale-auto = Awtomatiske skalěrowanje
+pdfjs-page-scale-actual = Aktualna wjelikosć
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Bok { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Pśi zacytowanju PDF jo zmólka nastała.
+pdfjs-invalid-file-error = Njepłaśiwa abo wobškóźona PDF-dataja.
+pdfjs-missing-file-error = Felujuca PDF-dataja.
+pdfjs-unexpected-response-error = Njewócakane serwerowe wótegrono.
+pdfjs-rendering-error = Pśi zwobraznjanju boka jo zmólka nastała.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Typ pśipiskow: { $type }]
+
+## Password
+
+pdfjs-password-label = Zapódajśo gronidło, aby PDF-dataju wócynił.
+pdfjs-password-invalid = Njepłaśiwe gronidło. Pšosym wopytajśo hyšći raz.
+pdfjs-password-ok-button = W pórěźe
+pdfjs-password-cancel-button = Pśetergnuś
+pdfjs-web-fonts-disabled = Webpisma su znjemóžnjone: njejo móžno, zasajźone PDF-pisma wužywaś.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+pdfjs-editor-ink-button =
+ .title = Kresliś
+pdfjs-editor-ink-button-label = Kresliś
+pdfjs-editor-stamp-button =
+ .title = Wobraze pśidaś abo wobźěłaś
+pdfjs-editor-stamp-button-label = Wobraze pśidaś abo wobźěłaś
+pdfjs-editor-highlight-button =
+ .title = Wuzwignuś
+pdfjs-editor-highlight-button-label = Wuzwignuś
+pdfjs-highlight-floating-button =
+ .title = Wuzwignjenje
+pdfjs-highlight-floating-button1 =
+ .title = Wuzwignuś
+ .aria-label = Wuzwignuś
+pdfjs-highlight-floating-button-label = Wuzwignuś
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Kreslanku wótwónoźeś
+pdfjs-editor-remove-freetext-button =
+ .title = Tekst wótwónoźeś
+pdfjs-editor-remove-stamp-button =
+ .title = Wobraz wótwónoźeś
+pdfjs-editor-remove-highlight-button =
+ .title = Wuzwignjenje wótpóraś
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Barwa
+pdfjs-editor-free-text-size-input = Wjelikosć
+pdfjs-editor-ink-color-input = Barwa
+pdfjs-editor-ink-thickness-input = Tłustosć
+pdfjs-editor-ink-opacity-input = Opacita
+pdfjs-editor-stamp-add-image-button =
+ .title = Wobraz pśidaś
+pdfjs-editor-stamp-add-image-button-label = Wobraz pśidaś
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Tłustosć
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Tłustosć změniś, gaž se zapiski wuzwiguju, kótarež tekst njejsu
+pdfjs-free-text =
+ .aria-label = Tekstowy editor
+pdfjs-free-text-default-content = Zachopśo pisaś…
+pdfjs-ink =
+ .aria-label = Kresleński editor
+pdfjs-ink-canvas =
+ .aria-label = Wobraz napórany wót wužywarja
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternatiwny tekst
+pdfjs-editor-alt-text-edit-button-label = Alternatiwny tekst wobźěłaś
+pdfjs-editor-alt-text-dialog-label = Nastajenje wubraś
+pdfjs-editor-alt-text-dialog-description = Alternatiwny tekst pomaga, gaž luźe njamógu wobraz wiźeś abo gaž se wobraz njezacytajo.
+pdfjs-editor-alt-text-add-description-label = Wopisanje pśidaś
+pdfjs-editor-alt-text-add-description-description = Pišćo 1 sadu abo 2 saźe, kótarejž temu, nastajenje abo akcije wopisujotej.
+pdfjs-editor-alt-text-mark-decorative-label = Ako dekoratiwny markěrowaś
+pdfjs-editor-alt-text-mark-decorative-description = To se za pyšnjece wobraze wužywa, na pśikład ramiki abo wódowe znamjenja.
+pdfjs-editor-alt-text-cancel-button = Pśetergnuś
+pdfjs-editor-alt-text-save-button = Składowaś
+pdfjs-editor-alt-text-decorative-tooltip = Ako dekoratiwny markěrowany
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Na pśikład, „Młody muski za blidom sejźi, aby jěź jědł“
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Górjejce nalěwo – wjelikosć změniś
+pdfjs-editor-resizer-label-top-middle = Górjejce wesrjejź – wjelikosć změniś
+pdfjs-editor-resizer-label-top-right = Górjejce napšawo – wjelikosć změniś
+pdfjs-editor-resizer-label-middle-right = Wesrjejź napšawo – wjelikosć změniś
+pdfjs-editor-resizer-label-bottom-right = Dołojce napšawo – wjelikosć změniś
+pdfjs-editor-resizer-label-bottom-middle = Dołojce wesrjejź – wjelikosć změniś
+pdfjs-editor-resizer-label-bottom-left = Dołojce nalěwo – wjelikosć změniś
+pdfjs-editor-resizer-label-middle-left = Wesrjejź nalěwo – wjelikosć změniś
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Barwa wuzwignjenja
+pdfjs-editor-colorpicker-button =
+ .title = Barwu změniś
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Wuběrk barwow
+pdfjs-editor-colorpicker-yellow =
+ .title = Žołty
+pdfjs-editor-colorpicker-green =
+ .title = Zeleny
+pdfjs-editor-colorpicker-blue =
+ .title = Módry
+pdfjs-editor-colorpicker-pink =
+ .title = Pink
+pdfjs-editor-colorpicker-red =
+ .title = Cerwjeny
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Wšykne pokazaś
+pdfjs-editor-highlight-show-all-button =
+ .title = Wšykne pokazaś
diff --git a/web/locale/el/viewer.ftl b/web/locale/el/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..96d9dc36e73774fada0ab166b640ab44b7c7384c
--- /dev/null
+++ b/web/locale/el/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Προηγούμενη σελίδα
+pdfjs-previous-button-label = Προηγούμενη
+pdfjs-next-button =
+ .title = Επόμενη σελίδα
+pdfjs-next-button-label = Επόμενη
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Σελίδα
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = από { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } από { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Σμίκρυνση
+pdfjs-zoom-out-button-label = Σμίκρυνση
+pdfjs-zoom-in-button =
+ .title = Μεγέθυνση
+pdfjs-zoom-in-button-label = Μεγέθυνση
+pdfjs-zoom-select =
+ .title = Ζουμ
+pdfjs-presentation-mode-button =
+ .title = Εναλλαγή σε λειτουργία παρουσίασης
+pdfjs-presentation-mode-button-label = Λειτουργία παρουσίασης
+pdfjs-open-file-button =
+ .title = Άνοιγμα αρχείου
+pdfjs-open-file-button-label = Άνοιγμα
+pdfjs-print-button =
+ .title = Εκτύπωση
+pdfjs-print-button-label = Εκτύπωση
+pdfjs-save-button =
+ .title = Αποθήκευση
+pdfjs-save-button-label = Αποθήκευση
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Λήψη
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Λήψη
+pdfjs-bookmark-button =
+ .title = Τρέχουσα σελίδα (Προβολή URL από τρέχουσα σελίδα)
+pdfjs-bookmark-button-label = Τρέχουσα σελίδα
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Άνοιγμα σε εφαρμογή
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Άνοιγμα σε εφαρμογή
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Εργαλεία
+pdfjs-tools-button-label = Εργαλεία
+pdfjs-first-page-button =
+ .title = Μετάβαση στην πρώτη σελίδα
+pdfjs-first-page-button-label = Μετάβαση στην πρώτη σελίδα
+pdfjs-last-page-button =
+ .title = Μετάβαση στην τελευταία σελίδα
+pdfjs-last-page-button-label = Μετάβαση στην τελευταία σελίδα
+pdfjs-page-rotate-cw-button =
+ .title = Δεξιόστροφη περιστροφή
+pdfjs-page-rotate-cw-button-label = Δεξιόστροφη περιστροφή
+pdfjs-page-rotate-ccw-button =
+ .title = Αριστερόστροφη περιστροφή
+pdfjs-page-rotate-ccw-button-label = Αριστερόστροφη περιστροφή
+pdfjs-cursor-text-select-tool-button =
+ .title = Ενεργοποίηση εργαλείου επιλογής κειμένου
+pdfjs-cursor-text-select-tool-button-label = Εργαλείο επιλογής κειμένου
+pdfjs-cursor-hand-tool-button =
+ .title = Ενεργοποίηση εργαλείου χεριού
+pdfjs-cursor-hand-tool-button-label = Εργαλείο χεριού
+pdfjs-scroll-page-button =
+ .title = Χρήση κύλισης σελίδας
+pdfjs-scroll-page-button-label = Κύλιση σελίδας
+pdfjs-scroll-vertical-button =
+ .title = Χρήση κάθετης κύλισης
+pdfjs-scroll-vertical-button-label = Κάθετη κύλιση
+pdfjs-scroll-horizontal-button =
+ .title = Χρήση οριζόντιας κύλισης
+pdfjs-scroll-horizontal-button-label = Οριζόντια κύλιση
+pdfjs-scroll-wrapped-button =
+ .title = Χρήση κυκλικής κύλισης
+pdfjs-scroll-wrapped-button-label = Κυκλική κύλιση
+pdfjs-spread-none-button =
+ .title = Να μη γίνει σύνδεση επεκτάσεων σελίδων
+pdfjs-spread-none-button-label = Χωρίς επεκτάσεις
+pdfjs-spread-odd-button =
+ .title = Σύνδεση επεκτάσεων σελίδων ξεκινώντας από τις μονές σελίδες
+pdfjs-spread-odd-button-label = Μονές επεκτάσεις
+pdfjs-spread-even-button =
+ .title = Σύνδεση επεκτάσεων σελίδων ξεκινώντας από τις ζυγές σελίδες
+pdfjs-spread-even-button-label = Ζυγές επεκτάσεις
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Ιδιότητες εγγράφου…
+pdfjs-document-properties-button-label = Ιδιότητες εγγράφου…
+pdfjs-document-properties-file-name = Όνομα αρχείου:
+pdfjs-document-properties-file-size = Μέγεθος αρχείου:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Τίτλος:
+pdfjs-document-properties-author = Συγγραφέας:
+pdfjs-document-properties-subject = Θέμα:
+pdfjs-document-properties-keywords = Λέξεις-κλειδιά:
+pdfjs-document-properties-creation-date = Ημερομηνία δημιουργίας:
+pdfjs-document-properties-modification-date = Ημερομηνία τροποποίησης:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Δημιουργός:
+pdfjs-document-properties-producer = Παραγωγός PDF:
+pdfjs-document-properties-version = Έκδοση PDF:
+pdfjs-document-properties-page-count = Αριθμός σελίδων:
+pdfjs-document-properties-page-size = Μέγεθος σελίδας:
+pdfjs-document-properties-page-size-unit-inches = ίντσες
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = κατακόρυφα
+pdfjs-document-properties-page-size-orientation-landscape = οριζόντια
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Επιστολή
+pdfjs-document-properties-page-size-name-legal = Τύπου Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Ταχεία προβολή ιστού:
+pdfjs-document-properties-linearized-yes = Ναι
+pdfjs-document-properties-linearized-no = Όχι
+pdfjs-document-properties-close-button = Κλείσιμο
+
+## Print
+
+pdfjs-print-progress-message = Προετοιμασία του εγγράφου για εκτύπωση…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Ακύρωση
+pdfjs-printing-not-supported = Προειδοποίηση: Η εκτύπωση δεν υποστηρίζεται πλήρως από το πρόγραμμα περιήγησης.
+pdfjs-printing-not-ready = Προειδοποίηση: Το PDF δεν φορτώθηκε πλήρως για εκτύπωση.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = (Απ)ενεργοποίηση πλαϊνής γραμμής
+pdfjs-toggle-sidebar-notification-button =
+ .title = (Απ)ενεργοποίηση πλαϊνής γραμμής (το έγγραφο περιέχει περίγραμμα/συνημμένα/επίπεδα)
+pdfjs-toggle-sidebar-button-label = (Απ)ενεργοποίηση πλαϊνής γραμμής
+pdfjs-document-outline-button =
+ .title = Εμφάνιση διάρθρωσης εγγράφου (διπλό κλικ για ανάπτυξη/σύμπτυξη όλων των στοιχείων)
+pdfjs-document-outline-button-label = Διάρθρωση εγγράφου
+pdfjs-attachments-button =
+ .title = Εμφάνιση συνημμένων
+pdfjs-attachments-button-label = Συνημμένα
+pdfjs-layers-button =
+ .title = Εμφάνιση επιπέδων (διπλό κλικ για επαναφορά όλων των επιπέδων στην προεπιλεγμένη κατάσταση)
+pdfjs-layers-button-label = Επίπεδα
+pdfjs-thumbs-button =
+ .title = Εμφάνιση μικρογραφιών
+pdfjs-thumbs-button-label = Μικρογραφίες
+pdfjs-current-outline-item-button =
+ .title = Εύρεση τρέχοντος στοιχείου διάρθρωσης
+pdfjs-current-outline-item-button-label = Τρέχον στοιχείο διάρθρωσης
+pdfjs-findbar-button =
+ .title = Εύρεση στο έγγραφο
+pdfjs-findbar-button-label = Εύρεση
+pdfjs-additional-layers = Επιπρόσθετα επίπεδα
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Σελίδα { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Μικρογραφία σελίδας { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Εύρεση
+ .placeholder = Εύρεση στο έγγραφο…
+pdfjs-find-previous-button =
+ .title = Εύρεση της προηγούμενης εμφάνισης της φράσης
+pdfjs-find-previous-button-label = Προηγούμενο
+pdfjs-find-next-button =
+ .title = Εύρεση της επόμενης εμφάνισης της φράσης
+pdfjs-find-next-button-label = Επόμενο
+pdfjs-find-highlight-checkbox = Επισήμανση όλων
+pdfjs-find-match-case-checkbox-label = Συμφωνία πεζών/κεφαλαίων
+pdfjs-find-match-diacritics-checkbox-label = Αντιστοίχιση διακριτικών
+pdfjs-find-entire-word-checkbox-label = Ολόκληρες λέξεις
+pdfjs-find-reached-top = Φτάσατε στην αρχή του εγγράφου, συνέχεια από το τέλος
+pdfjs-find-reached-bottom = Φτάσατε στο τέλος του εγγράφου, συνέχεια από την αρχή
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } από { $total } αντιστοιχία
+ *[other] { $current } από { $total } αντιστοιχίες
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Περισσότερες από { $limit } αντιστοιχία
+ *[other] Περισσότερες από { $limit } αντιστοιχίες
+ }
+pdfjs-find-not-found = Η φράση δεν βρέθηκε
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Πλάτος σελίδας
+pdfjs-page-scale-fit = Μέγεθος σελίδας
+pdfjs-page-scale-auto = Αυτόματο ζουμ
+pdfjs-page-scale-actual = Πραγματικό μέγεθος
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Σελίδα { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Προέκυψε σφάλμα κατά τη φόρτωση του PDF.
+pdfjs-invalid-file-error = Μη έγκυρο ή κατεστραμμένο αρχείο PDF.
+pdfjs-missing-file-error = Λείπει αρχείο PDF.
+pdfjs-unexpected-response-error = Μη αναμενόμενη απόκριση από το διακομιστή.
+pdfjs-rendering-error = Προέκυψε σφάλμα κατά την εμφάνιση της σελίδας.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Σχόλιο «{ $type }»]
+
+## Password
+
+pdfjs-password-label = Εισαγάγετε τον κωδικό πρόσβασης για να ανοίξετε αυτό το αρχείο PDF.
+pdfjs-password-invalid = Μη έγκυρος κωδικός πρόσβασης. Παρακαλώ δοκιμάστε ξανά.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Ακύρωση
+pdfjs-web-fonts-disabled = Οι γραμματοσειρές ιστού είναι ανενεργές: δεν είναι δυνατή η χρήση των ενσωματωμένων γραμματοσειρών PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Κείμενο
+pdfjs-editor-free-text-button-label = Κείμενο
+pdfjs-editor-ink-button =
+ .title = Σχέδιο
+pdfjs-editor-ink-button-label = Σχέδιο
+pdfjs-editor-stamp-button =
+ .title = Προσθήκη ή επεξεργασία εικόνων
+pdfjs-editor-stamp-button-label = Προσθήκη ή επεξεργασία εικόνων
+pdfjs-editor-highlight-button =
+ .title = Επισήμανση
+pdfjs-editor-highlight-button-label = Επισήμανση
+pdfjs-highlight-floating-button =
+ .title = Επισήμανση
+pdfjs-highlight-floating-button1 =
+ .title = Επισήμανση
+ .aria-label = Επισήμανση
+pdfjs-highlight-floating-button-label = Επισήμανση
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Αφαίρεση σχεδίου
+pdfjs-editor-remove-freetext-button =
+ .title = Αφαίρεση κειμένου
+pdfjs-editor-remove-stamp-button =
+ .title = Αφαίρεση εικόνας
+pdfjs-editor-remove-highlight-button =
+ .title = Αφαίρεση επισήμανσης
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Χρώμα
+pdfjs-editor-free-text-size-input = Μέγεθος
+pdfjs-editor-ink-color-input = Χρώμα
+pdfjs-editor-ink-thickness-input = Πάχος
+pdfjs-editor-ink-opacity-input = Αδιαφάνεια
+pdfjs-editor-stamp-add-image-button =
+ .title = Προσθήκη εικόνας
+pdfjs-editor-stamp-add-image-button-label = Προσθήκη εικόνας
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Πάχος
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Αλλαγή πάχους κατά την επισήμανση στοιχείων εκτός κειμένου
+pdfjs-free-text =
+ .aria-label = Επεξεργασία κειμένου
+pdfjs-free-text-default-content = Ξεκινήστε να πληκτρολογείτε…
+pdfjs-ink =
+ .aria-label = Επεξεργασία σχεδίων
+pdfjs-ink-canvas =
+ .aria-label = Εικόνα από τον χρήστη
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Εναλλακτικό κείμενο
+pdfjs-editor-alt-text-edit-button-label = Επεξεργασία εναλλακτικού κειμένου
+pdfjs-editor-alt-text-dialog-label = Διαλέξτε μια επιλογή
+pdfjs-editor-alt-text-dialog-description = Το εναλλακτικό κείμενο είναι χρήσιμο όταν οι άνθρωποι δεν μπορούν να δουν την εικόνα ή όταν αυτή δεν φορτώνεται.
+pdfjs-editor-alt-text-add-description-label = Προσθήκη περιγραφής
+pdfjs-editor-alt-text-add-description-description = Στοχεύστε σε μία ή δύο προτάσεις που περιγράφουν το θέμα, τη ρύθμιση ή τις ενέργειες.
+pdfjs-editor-alt-text-mark-decorative-label = Επισήμανση ως διακοσμητικό
+pdfjs-editor-alt-text-mark-decorative-description = Χρησιμοποιείται για διακοσμητικές εικόνες, όπως περιγράμματα ή υδατογραφήματα.
+pdfjs-editor-alt-text-cancel-button = Ακύρωση
+pdfjs-editor-alt-text-save-button = Αποθήκευση
+pdfjs-editor-alt-text-decorative-tooltip = Επισημασμένο ως διακοσμητικό
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Για παράδειγμα, «Ένας νεαρός άνδρας κάθεται σε ένα τραπέζι για να φάει ένα γεύμα»
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Επάνω αριστερή γωνία — αλλαγή μεγέθους
+pdfjs-editor-resizer-label-top-middle = Μέσο επάνω πλευράς — αλλαγή μεγέθους
+pdfjs-editor-resizer-label-top-right = Επάνω δεξιά γωνία — αλλαγή μεγέθους
+pdfjs-editor-resizer-label-middle-right = Μέσο δεξιάς πλευράς — αλλαγή μεγέθους
+pdfjs-editor-resizer-label-bottom-right = Κάτω δεξιά γωνία — αλλαγή μεγέθους
+pdfjs-editor-resizer-label-bottom-middle = Μέσο κάτω πλευράς — αλλαγή μεγέθους
+pdfjs-editor-resizer-label-bottom-left = Κάτω αριστερή γωνία — αλλαγή μεγέθους
+pdfjs-editor-resizer-label-middle-left = Μέσο αριστερής πλευράς — αλλαγή μεγέθους
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Χρώμα επισήμανσης
+pdfjs-editor-colorpicker-button =
+ .title = Αλλαγή χρώματος
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Επιλογές χρωμάτων
+pdfjs-editor-colorpicker-yellow =
+ .title = Κίτρινο
+pdfjs-editor-colorpicker-green =
+ .title = Πράσινο
+pdfjs-editor-colorpicker-blue =
+ .title = Μπλε
+pdfjs-editor-colorpicker-pink =
+ .title = Ροζ
+pdfjs-editor-colorpicker-red =
+ .title = Κόκκινο
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Εμφάνιση όλων
+pdfjs-editor-highlight-show-all-button =
+ .title = Εμφάνιση όλων
diff --git a/web/locale/en-CA/viewer.ftl b/web/locale/en-CA/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..f87104e78dc8dba291805d64a037c3f308b5cab8
--- /dev/null
+++ b/web/locale/en-CA/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Previous Page
+pdfjs-previous-button-label = Previous
+pdfjs-next-button =
+ .title = Next Page
+pdfjs-next-button-label = Next
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Page
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = of { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoom Out
+pdfjs-zoom-out-button-label = Zoom Out
+pdfjs-zoom-in-button =
+ .title = Zoom In
+pdfjs-zoom-in-button-label = Zoom In
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Switch to Presentation Mode
+pdfjs-presentation-mode-button-label = Presentation Mode
+pdfjs-open-file-button =
+ .title = Open File
+pdfjs-open-file-button-label = Open
+pdfjs-print-button =
+ .title = Print
+pdfjs-print-button-label = Print
+pdfjs-save-button =
+ .title = Save
+pdfjs-save-button-label = Save
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Download
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Download
+pdfjs-bookmark-button =
+ .title = Current Page (View URL from Current Page)
+pdfjs-bookmark-button-label = Current Page
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Open in app
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Open in app
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Tools
+pdfjs-tools-button-label = Tools
+pdfjs-first-page-button =
+ .title = Go to First Page
+pdfjs-first-page-button-label = Go to First Page
+pdfjs-last-page-button =
+ .title = Go to Last Page
+pdfjs-last-page-button-label = Go to Last Page
+pdfjs-page-rotate-cw-button =
+ .title = Rotate Clockwise
+pdfjs-page-rotate-cw-button-label = Rotate Clockwise
+pdfjs-page-rotate-ccw-button =
+ .title = Rotate Counterclockwise
+pdfjs-page-rotate-ccw-button-label = Rotate Counterclockwise
+pdfjs-cursor-text-select-tool-button =
+ .title = Enable Text Selection Tool
+pdfjs-cursor-text-select-tool-button-label = Text Selection Tool
+pdfjs-cursor-hand-tool-button =
+ .title = Enable Hand Tool
+pdfjs-cursor-hand-tool-button-label = Hand Tool
+pdfjs-scroll-page-button =
+ .title = Use Page Scrolling
+pdfjs-scroll-page-button-label = Page Scrolling
+pdfjs-scroll-vertical-button =
+ .title = Use Vertical Scrolling
+pdfjs-scroll-vertical-button-label = Vertical Scrolling
+pdfjs-scroll-horizontal-button =
+ .title = Use Horizontal Scrolling
+pdfjs-scroll-horizontal-button-label = Horizontal Scrolling
+pdfjs-scroll-wrapped-button =
+ .title = Use Wrapped Scrolling
+pdfjs-scroll-wrapped-button-label = Wrapped Scrolling
+pdfjs-spread-none-button =
+ .title = Do not join page spreads
+pdfjs-spread-none-button-label = No Spreads
+pdfjs-spread-odd-button =
+ .title = Join page spreads starting with odd-numbered pages
+pdfjs-spread-odd-button-label = Odd Spreads
+pdfjs-spread-even-button =
+ .title = Join page spreads starting with even-numbered pages
+pdfjs-spread-even-button-label = Even Spreads
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Document Properties…
+pdfjs-document-properties-button-label = Document Properties…
+pdfjs-document-properties-file-name = File name:
+pdfjs-document-properties-file-size = File size:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Title:
+pdfjs-document-properties-author = Author:
+pdfjs-document-properties-subject = Subject:
+pdfjs-document-properties-keywords = Keywords:
+pdfjs-document-properties-creation-date = Creation Date:
+pdfjs-document-properties-modification-date = Modification Date:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creator:
+pdfjs-document-properties-producer = PDF Producer:
+pdfjs-document-properties-version = PDF Version:
+pdfjs-document-properties-page-count = Page Count:
+pdfjs-document-properties-page-size = Page Size:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portrait
+pdfjs-document-properties-page-size-orientation-landscape = landscape
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = Yes
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Close
+
+## Print
+
+pdfjs-print-progress-message = Preparing document for printing…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancel
+pdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser.
+pdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Toggle Sidebar
+pdfjs-toggle-sidebar-notification-button =
+ .title = Toggle Sidebar (document contains outline/attachments/layers)
+pdfjs-toggle-sidebar-button-label = Toggle Sidebar
+pdfjs-document-outline-button =
+ .title = Show Document Outline (double-click to expand/collapse all items)
+pdfjs-document-outline-button-label = Document Outline
+pdfjs-attachments-button =
+ .title = Show Attachments
+pdfjs-attachments-button-label = Attachments
+pdfjs-layers-button =
+ .title = Show Layers (double-click to reset all layers to the default state)
+pdfjs-layers-button-label = Layers
+pdfjs-thumbs-button =
+ .title = Show Thumbnails
+pdfjs-thumbs-button-label = Thumbnails
+pdfjs-current-outline-item-button =
+ .title = Find Current Outline Item
+pdfjs-current-outline-item-button-label = Current Outline Item
+pdfjs-findbar-button =
+ .title = Find in Document
+pdfjs-findbar-button-label = Find
+pdfjs-additional-layers = Additional Layers
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Page { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Thumbnail of Page { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Find
+ .placeholder = Find in document…
+pdfjs-find-previous-button =
+ .title = Find the previous occurrence of the phrase
+pdfjs-find-previous-button-label = Previous
+pdfjs-find-next-button =
+ .title = Find the next occurrence of the phrase
+pdfjs-find-next-button-label = Next
+pdfjs-find-highlight-checkbox = Highlight All
+pdfjs-find-match-case-checkbox-label = Match Case
+pdfjs-find-match-diacritics-checkbox-label = Match Diacritics
+pdfjs-find-entire-word-checkbox-label = Whole Words
+pdfjs-find-reached-top = Reached top of document, continued from bottom
+pdfjs-find-reached-bottom = Reached end of document, continued from top
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } of { $total } match
+ *[other] { $current } of { $total } matches
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] More than { $limit } match
+ *[other] More than { $limit } matches
+ }
+pdfjs-find-not-found = Phrase not found
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Page Width
+pdfjs-page-scale-fit = Page Fit
+pdfjs-page-scale-auto = Automatic Zoom
+pdfjs-page-scale-actual = Actual Size
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Page { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = An error occurred while loading the PDF.
+pdfjs-invalid-file-error = Invalid or corrupted PDF file.
+pdfjs-missing-file-error = Missing PDF file.
+pdfjs-unexpected-response-error = Unexpected server response.
+pdfjs-rendering-error = An error occurred while rendering the page.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = Enter the password to open this PDF file.
+pdfjs-password-invalid = Invalid password. Please try again.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Cancel
+pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Text
+pdfjs-editor-free-text-button-label = Text
+pdfjs-editor-ink-button =
+ .title = Draw
+pdfjs-editor-ink-button-label = Draw
+pdfjs-editor-stamp-button =
+ .title = Add or edit images
+pdfjs-editor-stamp-button-label = Add or edit images
+pdfjs-editor-highlight-button =
+ .title = Highlight
+pdfjs-editor-highlight-button-label = Highlight
+pdfjs-highlight-floating-button =
+ .title = Highlight
+pdfjs-highlight-floating-button1 =
+ .title = Highlight
+ .aria-label = Highlight
+pdfjs-highlight-floating-button-label = Highlight
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Remove drawing
+pdfjs-editor-remove-freetext-button =
+ .title = Remove text
+pdfjs-editor-remove-stamp-button =
+ .title = Remove image
+pdfjs-editor-remove-highlight-button =
+ .title = Remove highlight
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Colour
+pdfjs-editor-free-text-size-input = Size
+pdfjs-editor-ink-color-input = Colour
+pdfjs-editor-ink-thickness-input = Thickness
+pdfjs-editor-ink-opacity-input = Opacity
+pdfjs-editor-stamp-add-image-button =
+ .title = Add image
+pdfjs-editor-stamp-add-image-button-label = Add image
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Thickness
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Change thickness when highlighting items other than text
+pdfjs-free-text =
+ .aria-label = Text Editor
+pdfjs-free-text-default-content = Start typing…
+pdfjs-ink =
+ .aria-label = Draw Editor
+pdfjs-ink-canvas =
+ .aria-label = User-created image
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alt text
+pdfjs-editor-alt-text-edit-button-label = Edit alt text
+pdfjs-editor-alt-text-dialog-label = Choose an option
+pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can’t see the image or when it doesn’t load.
+pdfjs-editor-alt-text-add-description-label = Add a description
+pdfjs-editor-alt-text-add-description-description = Aim for 1-2 sentences that describe the subject, setting, or actions.
+pdfjs-editor-alt-text-mark-decorative-label = Mark as decorative
+pdfjs-editor-alt-text-mark-decorative-description = This is used for ornamental images, like borders or watermarks.
+pdfjs-editor-alt-text-cancel-button = Cancel
+pdfjs-editor-alt-text-save-button = Save
+pdfjs-editor-alt-text-decorative-tooltip = Marked as decorative
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = For example, “A young man sits down at a table to eat a meal”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Top left corner — resize
+pdfjs-editor-resizer-label-top-middle = Top middle — resize
+pdfjs-editor-resizer-label-top-right = Top right corner — resize
+pdfjs-editor-resizer-label-middle-right = Middle right — resize
+pdfjs-editor-resizer-label-bottom-right = Bottom right corner — resize
+pdfjs-editor-resizer-label-bottom-middle = Bottom middle — resize
+pdfjs-editor-resizer-label-bottom-left = Bottom left corner — resize
+pdfjs-editor-resizer-label-middle-left = Middle left — resize
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Highlight colour
+pdfjs-editor-colorpicker-button =
+ .title = Change colour
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Colour choices
+pdfjs-editor-colorpicker-yellow =
+ .title = Yellow
+pdfjs-editor-colorpicker-green =
+ .title = Green
+pdfjs-editor-colorpicker-blue =
+ .title = Blue
+pdfjs-editor-colorpicker-pink =
+ .title = Pink
+pdfjs-editor-colorpicker-red =
+ .title = Red
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Show all
+pdfjs-editor-highlight-show-all-button =
+ .title = Show all
diff --git a/web/locale/en-GB/viewer.ftl b/web/locale/en-GB/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..3b141aee16f3a5d4b5e57a1704972d433bbf2bee
--- /dev/null
+++ b/web/locale/en-GB/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Previous Page
+pdfjs-previous-button-label = Previous
+pdfjs-next-button =
+ .title = Next Page
+pdfjs-next-button-label = Next
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Page
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = of { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoom Out
+pdfjs-zoom-out-button-label = Zoom Out
+pdfjs-zoom-in-button =
+ .title = Zoom In
+pdfjs-zoom-in-button-label = Zoom In
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Switch to Presentation Mode
+pdfjs-presentation-mode-button-label = Presentation Mode
+pdfjs-open-file-button =
+ .title = Open File
+pdfjs-open-file-button-label = Open
+pdfjs-print-button =
+ .title = Print
+pdfjs-print-button-label = Print
+pdfjs-save-button =
+ .title = Save
+pdfjs-save-button-label = Save
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Download
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Download
+pdfjs-bookmark-button =
+ .title = Current Page (View URL from Current Page)
+pdfjs-bookmark-button-label = Current Page
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Open in app
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Open in app
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Tools
+pdfjs-tools-button-label = Tools
+pdfjs-first-page-button =
+ .title = Go to First Page
+pdfjs-first-page-button-label = Go to First Page
+pdfjs-last-page-button =
+ .title = Go to Last Page
+pdfjs-last-page-button-label = Go to Last Page
+pdfjs-page-rotate-cw-button =
+ .title = Rotate Clockwise
+pdfjs-page-rotate-cw-button-label = Rotate Clockwise
+pdfjs-page-rotate-ccw-button =
+ .title = Rotate Anti-Clockwise
+pdfjs-page-rotate-ccw-button-label = Rotate Anti-Clockwise
+pdfjs-cursor-text-select-tool-button =
+ .title = Enable Text Selection Tool
+pdfjs-cursor-text-select-tool-button-label = Text Selection Tool
+pdfjs-cursor-hand-tool-button =
+ .title = Enable Hand Tool
+pdfjs-cursor-hand-tool-button-label = Hand Tool
+pdfjs-scroll-page-button =
+ .title = Use Page Scrolling
+pdfjs-scroll-page-button-label = Page Scrolling
+pdfjs-scroll-vertical-button =
+ .title = Use Vertical Scrolling
+pdfjs-scroll-vertical-button-label = Vertical Scrolling
+pdfjs-scroll-horizontal-button =
+ .title = Use Horizontal Scrolling
+pdfjs-scroll-horizontal-button-label = Horizontal Scrolling
+pdfjs-scroll-wrapped-button =
+ .title = Use Wrapped Scrolling
+pdfjs-scroll-wrapped-button-label = Wrapped Scrolling
+pdfjs-spread-none-button =
+ .title = Do not join page spreads
+pdfjs-spread-none-button-label = No Spreads
+pdfjs-spread-odd-button =
+ .title = Join page spreads starting with odd-numbered pages
+pdfjs-spread-odd-button-label = Odd Spreads
+pdfjs-spread-even-button =
+ .title = Join page spreads starting with even-numbered pages
+pdfjs-spread-even-button-label = Even Spreads
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Document Properties…
+pdfjs-document-properties-button-label = Document Properties…
+pdfjs-document-properties-file-name = File name:
+pdfjs-document-properties-file-size = File size:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Title:
+pdfjs-document-properties-author = Author:
+pdfjs-document-properties-subject = Subject:
+pdfjs-document-properties-keywords = Keywords:
+pdfjs-document-properties-creation-date = Creation Date:
+pdfjs-document-properties-modification-date = Modification Date:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creator:
+pdfjs-document-properties-producer = PDF Producer:
+pdfjs-document-properties-version = PDF Version:
+pdfjs-document-properties-page-count = Page Count:
+pdfjs-document-properties-page-size = Page Size:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portrait
+pdfjs-document-properties-page-size-orientation-landscape = landscape
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = Yes
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Close
+
+## Print
+
+pdfjs-print-progress-message = Preparing document for printing…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancel
+pdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser.
+pdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Toggle Sidebar
+pdfjs-toggle-sidebar-notification-button =
+ .title = Toggle Sidebar (document contains outline/attachments/layers)
+pdfjs-toggle-sidebar-button-label = Toggle Sidebar
+pdfjs-document-outline-button =
+ .title = Show Document Outline (double-click to expand/collapse all items)
+pdfjs-document-outline-button-label = Document Outline
+pdfjs-attachments-button =
+ .title = Show Attachments
+pdfjs-attachments-button-label = Attachments
+pdfjs-layers-button =
+ .title = Show Layers (double-click to reset all layers to the default state)
+pdfjs-layers-button-label = Layers
+pdfjs-thumbs-button =
+ .title = Show Thumbnails
+pdfjs-thumbs-button-label = Thumbnails
+pdfjs-current-outline-item-button =
+ .title = Find Current Outline Item
+pdfjs-current-outline-item-button-label = Current Outline Item
+pdfjs-findbar-button =
+ .title = Find in Document
+pdfjs-findbar-button-label = Find
+pdfjs-additional-layers = Additional Layers
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Page { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Thumbnail of Page { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Find
+ .placeholder = Find in document…
+pdfjs-find-previous-button =
+ .title = Find the previous occurrence of the phrase
+pdfjs-find-previous-button-label = Previous
+pdfjs-find-next-button =
+ .title = Find the next occurrence of the phrase
+pdfjs-find-next-button-label = Next
+pdfjs-find-highlight-checkbox = Highlight All
+pdfjs-find-match-case-checkbox-label = Match Case
+pdfjs-find-match-diacritics-checkbox-label = Match Diacritics
+pdfjs-find-entire-word-checkbox-label = Whole Words
+pdfjs-find-reached-top = Reached top of document, continued from bottom
+pdfjs-find-reached-bottom = Reached end of document, continued from top
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } of { $total } match
+ *[other] { $current } of { $total } matches
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] More than { $limit } match
+ *[other] More than { $limit } matches
+ }
+pdfjs-find-not-found = Phrase not found
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Page Width
+pdfjs-page-scale-fit = Page Fit
+pdfjs-page-scale-auto = Automatic Zoom
+pdfjs-page-scale-actual = Actual Size
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Page { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = An error occurred while loading the PDF.
+pdfjs-invalid-file-error = Invalid or corrupted PDF file.
+pdfjs-missing-file-error = Missing PDF file.
+pdfjs-unexpected-response-error = Unexpected server response.
+pdfjs-rendering-error = An error occurred while rendering the page.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = Enter the password to open this PDF file.
+pdfjs-password-invalid = Invalid password. Please try again.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Cancel
+pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Text
+pdfjs-editor-free-text-button-label = Text
+pdfjs-editor-ink-button =
+ .title = Draw
+pdfjs-editor-ink-button-label = Draw
+pdfjs-editor-stamp-button =
+ .title = Add or edit images
+pdfjs-editor-stamp-button-label = Add or edit images
+pdfjs-editor-highlight-button =
+ .title = Highlight
+pdfjs-editor-highlight-button-label = Highlight
+pdfjs-highlight-floating-button =
+ .title = Highlight
+pdfjs-highlight-floating-button1 =
+ .title = Highlight
+ .aria-label = Highlight
+pdfjs-highlight-floating-button-label = Highlight
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Remove drawing
+pdfjs-editor-remove-freetext-button =
+ .title = Remove text
+pdfjs-editor-remove-stamp-button =
+ .title = Remove image
+pdfjs-editor-remove-highlight-button =
+ .title = Remove highlight
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Colour
+pdfjs-editor-free-text-size-input = Size
+pdfjs-editor-ink-color-input = Colour
+pdfjs-editor-ink-thickness-input = Thickness
+pdfjs-editor-ink-opacity-input = Opacity
+pdfjs-editor-stamp-add-image-button =
+ .title = Add image
+pdfjs-editor-stamp-add-image-button-label = Add image
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Thickness
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Change thickness when highlighting items other than text
+pdfjs-free-text =
+ .aria-label = Text Editor
+pdfjs-free-text-default-content = Start typing…
+pdfjs-ink =
+ .aria-label = Draw Editor
+pdfjs-ink-canvas =
+ .aria-label = User-created image
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alt text
+pdfjs-editor-alt-text-edit-button-label = Edit alt text
+pdfjs-editor-alt-text-dialog-label = Choose an option
+pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can’t see the image or when it doesn’t load.
+pdfjs-editor-alt-text-add-description-label = Add a description
+pdfjs-editor-alt-text-add-description-description = Aim for 1-2 sentences that describe the subject, setting, or actions.
+pdfjs-editor-alt-text-mark-decorative-label = Mark as decorative
+pdfjs-editor-alt-text-mark-decorative-description = This is used for ornamental images, like borders or watermarks.
+pdfjs-editor-alt-text-cancel-button = Cancel
+pdfjs-editor-alt-text-save-button = Save
+pdfjs-editor-alt-text-decorative-tooltip = Marked as decorative
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = For example, “A young man sits down at a table to eat a meal”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Top left corner — resize
+pdfjs-editor-resizer-label-top-middle = Top middle — resize
+pdfjs-editor-resizer-label-top-right = Top right corner — resize
+pdfjs-editor-resizer-label-middle-right = Middle right — resize
+pdfjs-editor-resizer-label-bottom-right = Bottom right corner — resize
+pdfjs-editor-resizer-label-bottom-middle = Bottom middle — resize
+pdfjs-editor-resizer-label-bottom-left = Bottom left corner — resize
+pdfjs-editor-resizer-label-middle-left = Middle left — resize
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Highlight colour
+pdfjs-editor-colorpicker-button =
+ .title = Change colour
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Colour choices
+pdfjs-editor-colorpicker-yellow =
+ .title = Yellow
+pdfjs-editor-colorpicker-green =
+ .title = Green
+pdfjs-editor-colorpicker-blue =
+ .title = Blue
+pdfjs-editor-colorpicker-pink =
+ .title = Pink
+pdfjs-editor-colorpicker-red =
+ .title = Red
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Show all
+pdfjs-editor-highlight-show-all-button =
+ .title = Show all
diff --git a/web/locale/en-US/viewer.ftl b/web/locale/en-US/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..8aea43959e0bd5a97914def0b6782844a87824ad
--- /dev/null
+++ b/web/locale/en-US/viewer.ftl
@@ -0,0 +1,418 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Previous Page
+pdfjs-previous-button-label = Previous
+pdfjs-next-button =
+ .title = Next Page
+pdfjs-next-button-label = Next
+
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Page
+
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = of { $pagesCount }
+
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })
+
+pdfjs-zoom-out-button =
+ .title = Zoom Out
+pdfjs-zoom-out-button-label = Zoom Out
+pdfjs-zoom-in-button =
+ .title = Zoom In
+pdfjs-zoom-in-button-label = Zoom In
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Switch to Presentation Mode
+pdfjs-presentation-mode-button-label = Presentation Mode
+pdfjs-open-file-button =
+ .title = Open File
+pdfjs-open-file-button-label = Open
+pdfjs-print-button =
+ .title = Print
+pdfjs-print-button-label = Print
+pdfjs-save-button =
+ .title = Save
+pdfjs-save-button-label = Save
+
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Download
+
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Download
+
+pdfjs-bookmark-button =
+ .title = Current Page (View URL from Current Page)
+pdfjs-bookmark-button-label = Current Page
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Tools
+
+pdfjs-tools-button-label = Tools
+pdfjs-first-page-button =
+ .title = Go to First Page
+pdfjs-first-page-button-label = Go to First Page
+pdfjs-last-page-button =
+ .title = Go to Last Page
+pdfjs-last-page-button-label = Go to Last Page
+pdfjs-page-rotate-cw-button =
+ .title = Rotate Clockwise
+pdfjs-page-rotate-cw-button-label = Rotate Clockwise
+pdfjs-page-rotate-ccw-button =
+ .title = Rotate Counterclockwise
+pdfjs-page-rotate-ccw-button-label = Rotate Counterclockwise
+pdfjs-cursor-text-select-tool-button =
+ .title = Enable Text Selection Tool
+pdfjs-cursor-text-select-tool-button-label = Text Selection Tool
+pdfjs-cursor-hand-tool-button =
+ .title = Enable Hand Tool
+pdfjs-cursor-hand-tool-button-label = Hand Tool
+pdfjs-scroll-page-button =
+ .title = Use Page Scrolling
+pdfjs-scroll-page-button-label = Page Scrolling
+pdfjs-scroll-vertical-button =
+ .title = Use Vertical Scrolling
+pdfjs-scroll-vertical-button-label = Vertical Scrolling
+pdfjs-scroll-horizontal-button =
+ .title = Use Horizontal Scrolling
+pdfjs-scroll-horizontal-button-label = Horizontal Scrolling
+pdfjs-scroll-wrapped-button =
+ .title = Use Wrapped Scrolling
+pdfjs-scroll-wrapped-button-label = Wrapped Scrolling
+pdfjs-spread-none-button =
+ .title = Do not join page spreads
+pdfjs-spread-none-button-label = No Spreads
+pdfjs-spread-odd-button =
+ .title = Join page spreads starting with odd-numbered pages
+pdfjs-spread-odd-button-label = Odd Spreads
+pdfjs-spread-even-button =
+ .title = Join page spreads starting with even-numbered pages
+pdfjs-spread-even-button-label = Even Spreads
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Document Properties…
+pdfjs-document-properties-button-label = Document Properties…
+pdfjs-document-properties-file-name = File name:
+pdfjs-document-properties-file-size = File size:
+
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+
+pdfjs-document-properties-title = Title:
+pdfjs-document-properties-author = Author:
+pdfjs-document-properties-subject = Subject:
+pdfjs-document-properties-keywords = Keywords:
+pdfjs-document-properties-creation-date = Creation Date:
+pdfjs-document-properties-modification-date = Modification Date:
+
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+
+pdfjs-document-properties-creator = Creator:
+pdfjs-document-properties-producer = PDF Producer:
+pdfjs-document-properties-version = PDF Version:
+pdfjs-document-properties-page-count = Page Count:
+pdfjs-document-properties-page-size = Page Size:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portrait
+pdfjs-document-properties-page-size-orientation-landscape = landscape
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = Yes
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Close
+
+## Print
+
+pdfjs-print-progress-message = Preparing document for printing…
+
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+
+pdfjs-print-progress-close-button = Cancel
+pdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser.
+pdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Toggle Sidebar
+pdfjs-toggle-sidebar-notification-button =
+ .title = Toggle Sidebar (document contains outline/attachments/layers)
+pdfjs-toggle-sidebar-button-label = Toggle Sidebar
+pdfjs-document-outline-button =
+ .title = Show Document Outline (double-click to expand/collapse all items)
+pdfjs-document-outline-button-label = Document Outline
+pdfjs-attachments-button =
+ .title = Show Attachments
+pdfjs-attachments-button-label = Attachments
+pdfjs-layers-button =
+ .title = Show Layers (double-click to reset all layers to the default state)
+pdfjs-layers-button-label = Layers
+pdfjs-thumbs-button =
+ .title = Show Thumbnails
+pdfjs-thumbs-button-label = Thumbnails
+pdfjs-current-outline-item-button =
+ .title = Find Current Outline Item
+pdfjs-current-outline-item-button-label = Current Outline Item
+pdfjs-findbar-button =
+ .title = Find in Document
+pdfjs-findbar-button-label = Find
+pdfjs-additional-layers = Additional Layers
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Page { $page }
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Thumbnail of Page { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Find
+ .placeholder = Find in document…
+pdfjs-find-previous-button =
+ .title = Find the previous occurrence of the phrase
+pdfjs-find-previous-button-label = Previous
+pdfjs-find-next-button =
+ .title = Find the next occurrence of the phrase
+pdfjs-find-next-button-label = Next
+pdfjs-find-highlight-checkbox = Highlight All
+pdfjs-find-match-case-checkbox-label = Match Case
+pdfjs-find-match-diacritics-checkbox-label = Match Diacritics
+pdfjs-find-entire-word-checkbox-label = Whole Words
+pdfjs-find-reached-top = Reached top of document, continued from bottom
+pdfjs-find-reached-bottom = Reached end of document, continued from top
+
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } of { $total } match
+ *[other] { $current } of { $total } matches
+ }
+
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] More than { $limit } match
+ *[other] More than { $limit } matches
+ }
+
+pdfjs-find-not-found = Phrase not found
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Page Width
+pdfjs-page-scale-fit = Page Fit
+pdfjs-page-scale-auto = Automatic Zoom
+pdfjs-page-scale-actual = Actual Size
+
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Page { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = An error occurred while loading the PDF.
+pdfjs-invalid-file-error = Invalid or corrupted PDF file.
+pdfjs-missing-file-error = Missing PDF file.
+pdfjs-unexpected-response-error = Unexpected server response.
+pdfjs-rendering-error = An error occurred while rendering the page.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = Enter the password to open this PDF file.
+pdfjs-password-invalid = Invalid password. Please try again.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Cancel
+pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Text
+pdfjs-editor-free-text-button-label = Text
+pdfjs-editor-ink-button =
+ .title = Draw
+pdfjs-editor-ink-button-label = Draw
+pdfjs-editor-stamp-button =
+ .title = Add or edit images
+pdfjs-editor-stamp-button-label = Add or edit images
+pdfjs-editor-highlight-button =
+ .title = Highlight
+pdfjs-editor-highlight-button-label = Highlight
+pdfjs-highlight-floating-button1 =
+ .title = Highlight
+ .aria-label = Highlight
+pdfjs-highlight-floating-button-label = Highlight
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Remove drawing
+pdfjs-editor-remove-freetext-button =
+ .title = Remove text
+pdfjs-editor-remove-stamp-button =
+ .title = Remove image
+pdfjs-editor-remove-highlight-button =
+ .title = Remove highlight
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Color
+pdfjs-editor-free-text-size-input = Size
+pdfjs-editor-ink-color-input = Color
+pdfjs-editor-ink-thickness-input = Thickness
+pdfjs-editor-ink-opacity-input = Opacity
+pdfjs-editor-stamp-add-image-button =
+ .title = Add image
+pdfjs-editor-stamp-add-image-button-label = Add image
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Thickness
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Change thickness when highlighting items other than text
+
+pdfjs-free-text =
+ .aria-label = Text Editor
+pdfjs-free-text-default-content = Start typing…
+pdfjs-ink =
+ .aria-label = Draw Editor
+pdfjs-ink-canvas =
+ .aria-label = User-created image
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alt text
+
+pdfjs-editor-alt-text-edit-button-label = Edit alt text
+pdfjs-editor-alt-text-dialog-label = Choose an option
+pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can’t see the image or when it doesn’t load.
+pdfjs-editor-alt-text-add-description-label = Add a description
+pdfjs-editor-alt-text-add-description-description = Aim for 1-2 sentences that describe the subject, setting, or actions.
+pdfjs-editor-alt-text-mark-decorative-label = Mark as decorative
+pdfjs-editor-alt-text-mark-decorative-description = This is used for ornamental images, like borders or watermarks.
+pdfjs-editor-alt-text-cancel-button = Cancel
+pdfjs-editor-alt-text-save-button = Save
+pdfjs-editor-alt-text-decorative-tooltip = Marked as decorative
+
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = For example, “A young man sits down at a table to eat a meal”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Top left corner — resize
+pdfjs-editor-resizer-label-top-middle = Top middle — resize
+pdfjs-editor-resizer-label-top-right = Top right corner — resize
+pdfjs-editor-resizer-label-middle-right = Middle right — resize
+pdfjs-editor-resizer-label-bottom-right = Bottom right corner — resize
+pdfjs-editor-resizer-label-bottom-middle = Bottom middle — resize
+pdfjs-editor-resizer-label-bottom-left = Bottom left corner — resize
+pdfjs-editor-resizer-label-middle-left = Middle left — resize
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Highlight color
+
+pdfjs-editor-colorpicker-button =
+ .title = Change color
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Color choices
+pdfjs-editor-colorpicker-yellow =
+ .title = Yellow
+pdfjs-editor-colorpicker-green =
+ .title = Green
+pdfjs-editor-colorpicker-blue =
+ .title = Blue
+pdfjs-editor-colorpicker-pink =
+ .title = Pink
+pdfjs-editor-colorpicker-red =
+ .title = Red
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Show all
+pdfjs-editor-highlight-show-all-button =
+ .title = Show all
diff --git a/web/locale/eo/viewer.ftl b/web/locale/eo/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..23c2b24fa2658ab7f0fc2d59a41d44b30654d85b
--- /dev/null
+++ b/web/locale/eo/viewer.ftl
@@ -0,0 +1,396 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Antaŭa paĝo
+pdfjs-previous-button-label = Malantaŭen
+pdfjs-next-button =
+ .title = Venonta paĝo
+pdfjs-next-button-label = Antaŭen
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Paĝo
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = el { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } el { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Malpligrandigi
+pdfjs-zoom-out-button-label = Malpligrandigi
+pdfjs-zoom-in-button =
+ .title = Pligrandigi
+pdfjs-zoom-in-button-label = Pligrandigi
+pdfjs-zoom-select =
+ .title = Pligrandigilo
+pdfjs-presentation-mode-button =
+ .title = Iri al prezenta reĝimo
+pdfjs-presentation-mode-button-label = Prezenta reĝimo
+pdfjs-open-file-button =
+ .title = Malfermi dosieron
+pdfjs-open-file-button-label = Malfermi
+pdfjs-print-button =
+ .title = Presi
+pdfjs-print-button-label = Presi
+pdfjs-save-button =
+ .title = Konservi
+pdfjs-save-button-label = Konservi
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Elŝuti
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Elŝuti
+pdfjs-bookmark-button =
+ .title = Nuna paĝo (Montri adreson de la nuna paĝo)
+pdfjs-bookmark-button-label = Nuna paĝo
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Iloj
+pdfjs-tools-button-label = Iloj
+pdfjs-first-page-button =
+ .title = Iri al la unua paĝo
+pdfjs-first-page-button-label = Iri al la unua paĝo
+pdfjs-last-page-button =
+ .title = Iri al la lasta paĝo
+pdfjs-last-page-button-label = Iri al la lasta paĝo
+pdfjs-page-rotate-cw-button =
+ .title = Rotaciigi dekstrume
+pdfjs-page-rotate-cw-button-label = Rotaciigi dekstrume
+pdfjs-page-rotate-ccw-button =
+ .title = Rotaciigi maldekstrume
+pdfjs-page-rotate-ccw-button-label = Rotaciigi maldekstrume
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktivigi tekstan elektilon
+pdfjs-cursor-text-select-tool-button-label = Teksta elektilo
+pdfjs-cursor-hand-tool-button =
+ .title = Aktivigi ilon de mano
+pdfjs-cursor-hand-tool-button-label = Ilo de mano
+pdfjs-scroll-page-button =
+ .title = Uzi rulumon de paĝo
+pdfjs-scroll-page-button-label = Rulumo de paĝo
+pdfjs-scroll-vertical-button =
+ .title = Uzi vertikalan rulumon
+pdfjs-scroll-vertical-button-label = Vertikala rulumo
+pdfjs-scroll-horizontal-button =
+ .title = Uzi horizontalan rulumon
+pdfjs-scroll-horizontal-button-label = Horizontala rulumo
+pdfjs-scroll-wrapped-button =
+ .title = Uzi ambaŭdirektan rulumon
+pdfjs-scroll-wrapped-button-label = Ambaŭdirekta rulumo
+pdfjs-spread-none-button =
+ .title = Ne montri paĝojn po du
+pdfjs-spread-none-button-label = Unupaĝa vido
+pdfjs-spread-odd-button =
+ .title = Kunigi paĝojn komencante per nepara paĝo
+pdfjs-spread-odd-button-label = Po du paĝoj, neparaj maldekstre
+pdfjs-spread-even-button =
+ .title = Kunigi paĝojn komencante per para paĝo
+pdfjs-spread-even-button-label = Po du paĝoj, paraj maldekstre
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Atributoj de dokumento…
+pdfjs-document-properties-button-label = Atributoj de dokumento…
+pdfjs-document-properties-file-name = Nomo de dosiero:
+pdfjs-document-properties-file-size = Grando de dosiero:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KO ({ $size_b } oktetoj)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MO ({ $size_b } oktetoj)
+pdfjs-document-properties-title = Titolo:
+pdfjs-document-properties-author = Aŭtoro:
+pdfjs-document-properties-subject = Temo:
+pdfjs-document-properties-keywords = Ŝlosilvorto:
+pdfjs-document-properties-creation-date = Dato de kreado:
+pdfjs-document-properties-modification-date = Dato de modifo:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Kreinto:
+pdfjs-document-properties-producer = Produktinto de PDF:
+pdfjs-document-properties-version = Versio de PDF:
+pdfjs-document-properties-page-count = Nombro de paĝoj:
+pdfjs-document-properties-page-size = Grando de paĝo:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertikala
+pdfjs-document-properties-page-size-orientation-landscape = horizontala
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letera
+pdfjs-document-properties-page-size-name-legal = Jura
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Rapida tekstaĵa vido:
+pdfjs-document-properties-linearized-yes = Jes
+pdfjs-document-properties-linearized-no = Ne
+pdfjs-document-properties-close-button = Fermi
+
+## Print
+
+pdfjs-print-progress-message = Preparo de dokumento por presi ĝin …
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Nuligi
+pdfjs-printing-not-supported = Averto: tiu ĉi retumilo ne plene subtenas presadon.
+pdfjs-printing-not-ready = Averto: la PDF dosiero ne estas plene ŝargita por presado.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Montri/kaŝi flankan strion
+pdfjs-toggle-sidebar-notification-button =
+ .title = Montri/kaŝi flankan strion (la dokumento enhavas konturon/kunsendaĵojn/tavolojn)
+pdfjs-toggle-sidebar-button-label = Montri/kaŝi flankan strion
+pdfjs-document-outline-button =
+ .title = Montri la konturon de dokumento (alklaku duoble por faldi/malfaldi ĉiujn elementojn)
+pdfjs-document-outline-button-label = Konturo de dokumento
+pdfjs-attachments-button =
+ .title = Montri kunsendaĵojn
+pdfjs-attachments-button-label = Kunsendaĵojn
+pdfjs-layers-button =
+ .title = Montri tavolojn (duoble alklaku por remeti ĉiujn tavolojn en la norman staton)
+pdfjs-layers-button-label = Tavoloj
+pdfjs-thumbs-button =
+ .title = Montri miniaturojn
+pdfjs-thumbs-button-label = Miniaturoj
+pdfjs-current-outline-item-button =
+ .title = Trovi nunan konturan elementon
+pdfjs-current-outline-item-button-label = Nuna kontura elemento
+pdfjs-findbar-button =
+ .title = Serĉi en dokumento
+pdfjs-findbar-button-label = Serĉi
+pdfjs-additional-layers = Aldonaj tavoloj
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Paĝo { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniaturo de paĝo { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Serĉi
+ .placeholder = Serĉi en dokumento…
+pdfjs-find-previous-button =
+ .title = Serĉi la antaŭan aperon de la frazo
+pdfjs-find-previous-button-label = Malantaŭen
+pdfjs-find-next-button =
+ .title = Serĉi la venontan aperon de la frazo
+pdfjs-find-next-button-label = Antaŭen
+pdfjs-find-highlight-checkbox = Elstarigi ĉiujn
+pdfjs-find-match-case-checkbox-label = Distingi inter majuskloj kaj minuskloj
+pdfjs-find-match-diacritics-checkbox-label = Respekti supersignojn
+pdfjs-find-entire-word-checkbox-label = Tutaj vortoj
+pdfjs-find-reached-top = Komenco de la dokumento atingita, daŭrigado ekde la fino
+pdfjs-find-reached-bottom = Fino de la dokumento atingita, daŭrigado ekde la komenco
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } el { $total } kongruo
+ *[other] { $current } el { $total } kongruoj
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Pli ol { $limit } kongruo
+ *[other] Pli ol { $limit } kongruoj
+ }
+pdfjs-find-not-found = Frazo ne trovita
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Larĝo de paĝo
+pdfjs-page-scale-fit = Adapti paĝon
+pdfjs-page-scale-auto = Aŭtomata skalo
+pdfjs-page-scale-actual = Reala grando
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Paĝo { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Okazis eraro dum la ŝargado de la PDF dosiero.
+pdfjs-invalid-file-error = Nevalida aŭ difektita PDF dosiero.
+pdfjs-missing-file-error = Mankas dosiero PDF.
+pdfjs-unexpected-response-error = Neatendita respondo de servilo.
+pdfjs-rendering-error = Okazis eraro dum la montro de la paĝo.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Prinoto: { $type }]
+
+## Password
+
+pdfjs-password-label = Tajpu pasvorton por malfermi tiun ĉi dosieron PDF.
+pdfjs-password-invalid = Nevalida pasvorto. Bonvolu provi denove.
+pdfjs-password-ok-button = Akcepti
+pdfjs-password-cancel-button = Nuligi
+pdfjs-web-fonts-disabled = Neaktivaj teksaĵaj tiparoj: ne elbas uzi enmetitajn tiparojn de PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Teksto
+pdfjs-editor-free-text-button-label = Teksto
+pdfjs-editor-ink-button =
+ .title = Desegni
+pdfjs-editor-ink-button-label = Desegni
+pdfjs-editor-stamp-button =
+ .title = Aldoni aŭ modifi bildojn
+pdfjs-editor-stamp-button-label = Aldoni aŭ modifi bildojn
+pdfjs-editor-highlight-button =
+ .title = Elstarigi
+pdfjs-editor-highlight-button-label = Elstarigi
+pdfjs-highlight-floating-button =
+ .title = Elstarigi
+pdfjs-highlight-floating-button1 =
+ .title = Elstarigi
+ .aria-label = Elstarigi
+pdfjs-highlight-floating-button-label = Elstarigi
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Forigi desegnon
+pdfjs-editor-remove-freetext-button =
+ .title = Forigi tekston
+pdfjs-editor-remove-stamp-button =
+ .title = Forigi bildon
+pdfjs-editor-remove-highlight-button =
+ .title = Forigi elstaraĵon
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Koloro
+pdfjs-editor-free-text-size-input = Grando
+pdfjs-editor-ink-color-input = Koloro
+pdfjs-editor-ink-thickness-input = Dikeco
+pdfjs-editor-ink-opacity-input = Maldiafaneco
+pdfjs-editor-stamp-add-image-button =
+ .title = Aldoni bildon
+pdfjs-editor-stamp-add-image-button-label = Aldoni bildon
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Dikeco
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Ŝanĝi dikecon dum elstarigo de netekstaj elementoj
+pdfjs-free-text =
+ .aria-label = Tekstan redaktilon
+pdfjs-free-text-default-content = Ektajpi…
+pdfjs-ink =
+ .aria-label = Desegnan redaktilon
+pdfjs-ink-canvas =
+ .aria-label = Bildo kreita de uzanto
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternativa teksto
+pdfjs-editor-alt-text-edit-button-label = Redakti alternativan tekston
+pdfjs-editor-alt-text-dialog-label = Elektu eblon
+pdfjs-editor-alt-text-dialog-description = Alternativa teksto helpas personojn, en la okazoj kiam ili ne povas vidi aŭ ŝargi la bildon.
+pdfjs-editor-alt-text-add-description-label = Aldoni priskribon
+pdfjs-editor-alt-text-add-description-description = La celo estas unu aŭ du frazoj, kiuj priskribas la temon, etoson aŭ agojn.
+pdfjs-editor-alt-text-mark-decorative-label = Marki kiel ornaman
+pdfjs-editor-alt-text-mark-decorative-description = Tio ĉi estas uzita por ornamaj bildoj, kiel randoj aŭ fonaj bildoj.
+pdfjs-editor-alt-text-cancel-button = Nuligi
+pdfjs-editor-alt-text-save-button = Konservi
+pdfjs-editor-alt-text-decorative-tooltip = Markita kiel ornama
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Ekzemple: “Juna persono sidiĝas ĉetable por ekmanĝi”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Supra maldekstra angulo — ŝangi grandon
+pdfjs-editor-resizer-label-top-middle = Supra mezo — ŝanĝi grandon
+pdfjs-editor-resizer-label-top-right = Supran dekstran angulon — ŝanĝi grandon
+pdfjs-editor-resizer-label-middle-right = Dekstra mezo — ŝanĝi grandon
+pdfjs-editor-resizer-label-bottom-right = Malsupra deksta angulo — ŝanĝi grandon
+pdfjs-editor-resizer-label-bottom-middle = Malsupra mezo — ŝanĝi grandon
+pdfjs-editor-resizer-label-bottom-left = Malsupra maldekstra angulo — ŝanĝi grandon
+pdfjs-editor-resizer-label-middle-left = Maldekstra mezo — ŝanĝi grandon
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Elstarigi koloron
+pdfjs-editor-colorpicker-button =
+ .title = Ŝanĝi koloron
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Elekto de koloroj
+pdfjs-editor-colorpicker-yellow =
+ .title = Flava
+pdfjs-editor-colorpicker-green =
+ .title = Verda
+pdfjs-editor-colorpicker-blue =
+ .title = Blua
+pdfjs-editor-colorpicker-pink =
+ .title = Roza
+pdfjs-editor-colorpicker-red =
+ .title = Ruĝa
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Montri ĉiujn
+pdfjs-editor-highlight-show-all-button =
+ .title = Montri ĉiujn
diff --git a/web/locale/es-AR/viewer.ftl b/web/locale/es-AR/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..40610b24eb3c509bef4e2b9ecdb0398c7ddd1190
--- /dev/null
+++ b/web/locale/es-AR/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Página anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Página siguiente
+pdfjs-next-button-label = Siguiente
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Página
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ( { $pageNumber } de { $pagesCount } )
+pdfjs-zoom-out-button =
+ .title = Alejar
+pdfjs-zoom-out-button-label = Alejar
+pdfjs-zoom-in-button =
+ .title = Acercar
+pdfjs-zoom-in-button-label = Acercar
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Cambiar a modo presentación
+pdfjs-presentation-mode-button-label = Modo presentación
+pdfjs-open-file-button =
+ .title = Abrir archivo
+pdfjs-open-file-button-label = Abrir
+pdfjs-print-button =
+ .title = Imprimir
+pdfjs-print-button-label = Imprimir
+pdfjs-save-button =
+ .title = Guardar
+pdfjs-save-button-label = Guardar
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Descargar
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Descargar
+pdfjs-bookmark-button =
+ .title = Página actual (Ver URL de la página actual)
+pdfjs-bookmark-button-label = Página actual
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Abrir en la aplicación
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Abrir en la aplicación
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Herramientas
+pdfjs-tools-button-label = Herramientas
+pdfjs-first-page-button =
+ .title = Ir a primera página
+pdfjs-first-page-button-label = Ir a primera página
+pdfjs-last-page-button =
+ .title = Ir a última página
+pdfjs-last-page-button-label = Ir a última página
+pdfjs-page-rotate-cw-button =
+ .title = Rotar horario
+pdfjs-page-rotate-cw-button-label = Rotar horario
+pdfjs-page-rotate-ccw-button =
+ .title = Rotar antihorario
+pdfjs-page-rotate-ccw-button-label = Rotar antihorario
+pdfjs-cursor-text-select-tool-button =
+ .title = Habilitar herramienta de selección de texto
+pdfjs-cursor-text-select-tool-button-label = Herramienta de selección de texto
+pdfjs-cursor-hand-tool-button =
+ .title = Habilitar herramienta mano
+pdfjs-cursor-hand-tool-button-label = Herramienta mano
+pdfjs-scroll-page-button =
+ .title = Usar desplazamiento de página
+pdfjs-scroll-page-button-label = Desplazamiento de página
+pdfjs-scroll-vertical-button =
+ .title = Usar desplazamiento vertical
+pdfjs-scroll-vertical-button-label = Desplazamiento vertical
+pdfjs-scroll-horizontal-button =
+ .title = Usar desplazamiento vertical
+pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Usar desplazamiento encapsulado
+pdfjs-scroll-wrapped-button-label = Desplazamiento encapsulado
+pdfjs-spread-none-button =
+ .title = No unir páginas dobles
+pdfjs-spread-none-button-label = Sin dobles
+pdfjs-spread-odd-button =
+ .title = Unir páginas dobles comenzando con las impares
+pdfjs-spread-odd-button-label = Dobles impares
+pdfjs-spread-even-button =
+ .title = Unir páginas dobles comenzando con las pares
+pdfjs-spread-even-button-label = Dobles pares
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propiedades del documento…
+pdfjs-document-properties-button-label = Propiedades del documento…
+pdfjs-document-properties-file-name = Nombre de archivo:
+pdfjs-document-properties-file-size = Tamaño de archovo:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Título:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Asunto:
+pdfjs-document-properties-keywords = Palabras clave:
+pdfjs-document-properties-creation-date = Fecha de creación:
+pdfjs-document-properties-modification-date = Fecha de modificación:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creador:
+pdfjs-document-properties-producer = PDF Productor:
+pdfjs-document-properties-version = Versión de PDF:
+pdfjs-document-properties-page-count = Cantidad de páginas:
+pdfjs-document-properties-page-size = Tamaño de página:
+pdfjs-document-properties-page-size-unit-inches = en
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = normal
+pdfjs-document-properties-page-size-orientation-landscape = apaisado
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Carta
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista rápida de la Web:
+pdfjs-document-properties-linearized-yes = Sí
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Cerrar
+
+## Print
+
+pdfjs-print-progress-message = Preparando documento para imprimir…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancelar
+pdfjs-printing-not-supported = Advertencia: La impresión no está totalmente soportada por este navegador.
+pdfjs-printing-not-ready = Advertencia: El PDF no está completamente cargado para impresión.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Alternar barra lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Alternar barra lateral (el documento contiene esquemas/adjuntos/capas)
+pdfjs-toggle-sidebar-button-label = Alternar barra lateral
+pdfjs-document-outline-button =
+ .title = Mostrar esquema del documento (doble clic para expandir/colapsar todos los ítems)
+pdfjs-document-outline-button-label = Esquema del documento
+pdfjs-attachments-button =
+ .title = Mostrar adjuntos
+pdfjs-attachments-button-label = Adjuntos
+pdfjs-layers-button =
+ .title = Mostrar capas (doble clic para restablecer todas las capas al estado predeterminado)
+pdfjs-layers-button-label = Capas
+pdfjs-thumbs-button =
+ .title = Mostrar miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-current-outline-item-button =
+ .title = Buscar elemento de esquema actual
+pdfjs-current-outline-item-button-label = Elemento de esquema actual
+pdfjs-findbar-button =
+ .title = Buscar en documento
+pdfjs-findbar-button-label = Buscar
+pdfjs-additional-layers = Capas adicionales
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Página { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura de página { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Buscar
+ .placeholder = Buscar en documento…
+pdfjs-find-previous-button =
+ .title = Buscar la aparición anterior de la frase
+pdfjs-find-previous-button-label = Anterior
+pdfjs-find-next-button =
+ .title = Buscar la siguiente aparición de la frase
+pdfjs-find-next-button-label = Siguiente
+pdfjs-find-highlight-checkbox = Resaltar todo
+pdfjs-find-match-case-checkbox-label = Coincidir mayúsculas
+pdfjs-find-match-diacritics-checkbox-label = Coincidir diacríticos
+pdfjs-find-entire-word-checkbox-label = Palabras completas
+pdfjs-find-reached-top = Inicio de documento alcanzado, continuando desde abajo
+pdfjs-find-reached-bottom = Fin de documento alcanzando, continuando desde arriba
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } de { $total } coincidencia
+ *[other] { $current } de { $total } coincidencias
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Más de { $limit } coincidencia
+ *[other] Más de { $limit } coincidencias
+ }
+pdfjs-find-not-found = Frase no encontrada
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Ancho de página
+pdfjs-page-scale-fit = Ajustar página
+pdfjs-page-scale-auto = Zoom automático
+pdfjs-page-scale-actual = Tamaño real
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Página { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ocurrió un error al cargar el PDF.
+pdfjs-invalid-file-error = Archivo PDF no válido o cocrrupto.
+pdfjs-missing-file-error = Archivo PDF faltante.
+pdfjs-unexpected-response-error = Respuesta del servidor inesperada.
+pdfjs-rendering-error = Ocurrió un error al dibujar la página.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Anotación]
+
+## Password
+
+pdfjs-password-label = Ingrese la contraseña para abrir este archivo PDF
+pdfjs-password-invalid = Contraseña inválida. Intente nuevamente.
+pdfjs-password-ok-button = Aceptar
+pdfjs-password-cancel-button = Cancelar
+pdfjs-web-fonts-disabled = Tipografía web deshabilitada: no se pueden usar tipos incrustados en PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texto
+pdfjs-editor-free-text-button-label = Texto
+pdfjs-editor-ink-button =
+ .title = Dibujar
+pdfjs-editor-ink-button-label = Dibujar
+pdfjs-editor-stamp-button =
+ .title = Agregar o editar imágenes
+pdfjs-editor-stamp-button-label = Agregar o editar imágenes
+pdfjs-editor-highlight-button =
+ .title = Resaltar
+pdfjs-editor-highlight-button-label = Resaltar
+pdfjs-highlight-floating-button =
+ .title = Resaltar
+pdfjs-highlight-floating-button1 =
+ .title = Resaltar
+ .aria-label = Resaltar
+pdfjs-highlight-floating-button-label = Resaltar
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Eliminar dibujo
+pdfjs-editor-remove-freetext-button =
+ .title = Eliminar texto
+pdfjs-editor-remove-stamp-button =
+ .title = Eliminar imagen
+pdfjs-editor-remove-highlight-button =
+ .title = Eliminar resaltado
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Color
+pdfjs-editor-free-text-size-input = Tamaño
+pdfjs-editor-ink-color-input = Color
+pdfjs-editor-ink-thickness-input = Espesor
+pdfjs-editor-ink-opacity-input = Opacidad
+pdfjs-editor-stamp-add-image-button =
+ .title = Agregar una imagen
+pdfjs-editor-stamp-add-image-button-label = Agregar una imagen
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Grosor
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Cambiar el grosor al resaltar elementos que no sean texto
+pdfjs-free-text =
+ .aria-label = Editor de texto
+pdfjs-free-text-default-content = Empezar a tipear…
+pdfjs-ink =
+ .aria-label = Editor de dibujos
+pdfjs-ink-canvas =
+ .aria-label = Imagen creada por el usuario
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Texto alternativo
+pdfjs-editor-alt-text-edit-button-label = Editar el texto alternativo
+pdfjs-editor-alt-text-dialog-label = Eligir una opción
+pdfjs-editor-alt-text-dialog-description = El texto alternativo (texto alternativo) ayuda cuando las personas no pueden ver la imagen o cuando no se carga.
+pdfjs-editor-alt-text-add-description-label = Agregar una descripción
+pdfjs-editor-alt-text-add-description-description = Intente escribir 1 o 2 oraciones que describan el tema, el entorno o las acciones.
+pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativo
+pdfjs-editor-alt-text-mark-decorative-description = Esto se usa para imágenes ornamentales, como bordes o marcas de agua.
+pdfjs-editor-alt-text-cancel-button = Cancelar
+pdfjs-editor-alt-text-save-button = Guardar
+pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativo
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Esquina superior izquierda — cambiar el tamaño
+pdfjs-editor-resizer-label-top-middle = Arriba en el medio — cambiar el tamaño
+pdfjs-editor-resizer-label-top-right = Esquina superior derecha — cambiar el tamaño
+pdfjs-editor-resizer-label-middle-right = Al centro a la derecha — cambiar el tamaño
+pdfjs-editor-resizer-label-bottom-right = Esquina inferior derecha — cambiar el tamaño
+pdfjs-editor-resizer-label-bottom-middle = Abajo en el medio — cambiar el tamaño
+pdfjs-editor-resizer-label-bottom-left = Esquina inferior izquierda — cambiar el tamaño
+pdfjs-editor-resizer-label-middle-left = Al centro a la izquierda — cambiar el tamaño
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Color de resaltado
+pdfjs-editor-colorpicker-button =
+ .title = Cambiar el color
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Opciones de color
+pdfjs-editor-colorpicker-yellow =
+ .title = Amarillo
+pdfjs-editor-colorpicker-green =
+ .title = Verde
+pdfjs-editor-colorpicker-blue =
+ .title = Azul
+pdfjs-editor-colorpicker-pink =
+ .title = Rosado
+pdfjs-editor-colorpicker-red =
+ .title = Rojo
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Mostrar todo
+pdfjs-editor-highlight-show-all-button =
+ .title = Mostrar todo
diff --git a/web/locale/es-CL/viewer.ftl b/web/locale/es-CL/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..c4507a3fab41589a55c19247a6be707c7dce7dcc
--- /dev/null
+++ b/web/locale/es-CL/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Página anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Página siguiente
+pdfjs-next-button-label = Siguiente
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Página
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Alejar
+pdfjs-zoom-out-button-label = Alejar
+pdfjs-zoom-in-button =
+ .title = Acercar
+pdfjs-zoom-in-button-label = Acercar
+pdfjs-zoom-select =
+ .title = Ampliación
+pdfjs-presentation-mode-button =
+ .title = Cambiar al modo de presentación
+pdfjs-presentation-mode-button-label = Modo de presentación
+pdfjs-open-file-button =
+ .title = Abrir archivo
+pdfjs-open-file-button-label = Abrir
+pdfjs-print-button =
+ .title = Imprimir
+pdfjs-print-button-label = Imprimir
+pdfjs-save-button =
+ .title = Guardar
+pdfjs-save-button-label = Guardar
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Descargar
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Descargar
+pdfjs-bookmark-button =
+ .title = Página actual (Ver URL de la página actual)
+pdfjs-bookmark-button-label = Página actual
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Abrir en una aplicación
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Abrir en una aplicación
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Herramientas
+pdfjs-tools-button-label = Herramientas
+pdfjs-first-page-button =
+ .title = Ir a la primera página
+pdfjs-first-page-button-label = Ir a la primera página
+pdfjs-last-page-button =
+ .title = Ir a la última página
+pdfjs-last-page-button-label = Ir a la última página
+pdfjs-page-rotate-cw-button =
+ .title = Girar a la derecha
+pdfjs-page-rotate-cw-button-label = Girar a la derecha
+pdfjs-page-rotate-ccw-button =
+ .title = Girar a la izquierda
+pdfjs-page-rotate-ccw-button-label = Girar a la izquierda
+pdfjs-cursor-text-select-tool-button =
+ .title = Activar la herramienta de selección de texto
+pdfjs-cursor-text-select-tool-button-label = Herramienta de selección de texto
+pdfjs-cursor-hand-tool-button =
+ .title = Activar la herramienta de mano
+pdfjs-cursor-hand-tool-button-label = Herramienta de mano
+pdfjs-scroll-page-button =
+ .title = Usar desplazamiento de página
+pdfjs-scroll-page-button-label = Desplazamiento de página
+pdfjs-scroll-vertical-button =
+ .title = Usar desplazamiento vertical
+pdfjs-scroll-vertical-button-label = Desplazamiento vertical
+pdfjs-scroll-horizontal-button =
+ .title = Usar desplazamiento horizontal
+pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Usar desplazamiento en bloque
+pdfjs-scroll-wrapped-button-label = Desplazamiento en bloque
+pdfjs-spread-none-button =
+ .title = No juntar páginas a modo de libro
+pdfjs-spread-none-button-label = Vista de una página
+pdfjs-spread-odd-button =
+ .title = Junta las páginas partiendo con una de número impar
+pdfjs-spread-odd-button-label = Vista de libro impar
+pdfjs-spread-even-button =
+ .title = Junta las páginas partiendo con una de número par
+pdfjs-spread-even-button-label = Vista de libro par
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propiedades del documento…
+pdfjs-document-properties-button-label = Propiedades del documento…
+pdfjs-document-properties-file-name = Nombre de archivo:
+pdfjs-document-properties-file-size = Tamaño del archivo:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Título:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Asunto:
+pdfjs-document-properties-keywords = Palabras clave:
+pdfjs-document-properties-creation-date = Fecha de creación:
+pdfjs-document-properties-modification-date = Fecha de modificación:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creador:
+pdfjs-document-properties-producer = Productor del PDF:
+pdfjs-document-properties-version = Versión de PDF:
+pdfjs-document-properties-page-count = Cantidad de páginas:
+pdfjs-document-properties-page-size = Tamaño de la página:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertical
+pdfjs-document-properties-page-size-orientation-landscape = horizontal
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Carta
+pdfjs-document-properties-page-size-name-legal = Oficio
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista rápida en Web:
+pdfjs-document-properties-linearized-yes = Sí
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Cerrar
+
+## Print
+
+pdfjs-print-progress-message = Preparando documento para impresión…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancelar
+pdfjs-printing-not-supported = Advertencia: Imprimir no está soportado completamente por este navegador.
+pdfjs-printing-not-ready = Advertencia: El PDF no está completamente cargado para ser impreso.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Barra lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Cambiar barra lateral (índice de contenidos del documento/adjuntos/capas)
+pdfjs-toggle-sidebar-button-label = Mostrar u ocultar la barra lateral
+pdfjs-document-outline-button =
+ .title = Mostrar esquema del documento (doble clic para expandir/contraer todos los elementos)
+pdfjs-document-outline-button-label = Esquema del documento
+pdfjs-attachments-button =
+ .title = Mostrar adjuntos
+pdfjs-attachments-button-label = Adjuntos
+pdfjs-layers-button =
+ .title = Mostrar capas (doble clic para restablecer todas las capas al estado predeterminado)
+pdfjs-layers-button-label = Capas
+pdfjs-thumbs-button =
+ .title = Mostrar miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-current-outline-item-button =
+ .title = Buscar elemento de esquema actual
+pdfjs-current-outline-item-button-label = Elemento de esquema actual
+pdfjs-findbar-button =
+ .title = Buscar en el documento
+pdfjs-findbar-button-label = Buscar
+pdfjs-additional-layers = Capas adicionales
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Página { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura de la página { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Encontrar
+ .placeholder = Encontrar en el documento…
+pdfjs-find-previous-button =
+ .title = Buscar la aparición anterior de la frase
+pdfjs-find-previous-button-label = Previo
+pdfjs-find-next-button =
+ .title = Buscar la siguiente aparición de la frase
+pdfjs-find-next-button-label = Siguiente
+pdfjs-find-highlight-checkbox = Destacar todos
+pdfjs-find-match-case-checkbox-label = Coincidir mayús./minús.
+pdfjs-find-match-diacritics-checkbox-label = Coincidir diacríticos
+pdfjs-find-entire-word-checkbox-label = Palabras completas
+pdfjs-find-reached-top = Se alcanzó el inicio del documento, continuando desde el final
+pdfjs-find-reached-bottom = Se alcanzó el final del documento, continuando desde el inicio
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] Coincidencia { $current } de { $total }
+ *[other] Coincidencia { $current } de { $total }
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Más de { $limit } coincidencia
+ *[other] Más de { $limit } coincidencias
+ }
+pdfjs-find-not-found = Frase no encontrada
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Ancho de página
+pdfjs-page-scale-fit = Ajuste de página
+pdfjs-page-scale-auto = Aumento automático
+pdfjs-page-scale-actual = Tamaño actual
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Página { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ocurrió un error al cargar el PDF.
+pdfjs-invalid-file-error = Archivo PDF inválido o corrupto.
+pdfjs-missing-file-error = Falta el archivo PDF.
+pdfjs-unexpected-response-error = Respuesta del servidor inesperada.
+pdfjs-rendering-error = Ocurrió un error al renderizar la página.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Anotación]
+
+## Password
+
+pdfjs-password-label = Ingrese la contraseña para abrir este archivo PDF.
+pdfjs-password-invalid = Contraseña inválida. Por favor, vuelve a intentarlo.
+pdfjs-password-ok-button = Aceptar
+pdfjs-password-cancel-button = Cancelar
+pdfjs-web-fonts-disabled = Las tipografías web están desactivadas: imposible usar las fuentes PDF embebidas.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texto
+pdfjs-editor-free-text-button-label = Texto
+pdfjs-editor-ink-button =
+ .title = Dibujar
+pdfjs-editor-ink-button-label = Dibujar
+pdfjs-editor-stamp-button =
+ .title = Añadir o editar imágenes
+pdfjs-editor-stamp-button-label = Añadir o editar imágenes
+pdfjs-editor-highlight-button =
+ .title = Destacar
+pdfjs-editor-highlight-button-label = Destacar
+pdfjs-highlight-floating-button =
+ .title = Destacar
+pdfjs-highlight-floating-button1 =
+ .title = Destacar
+ .aria-label = Destacar
+pdfjs-highlight-floating-button-label = Destacar
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Eliminar dibujo
+pdfjs-editor-remove-freetext-button =
+ .title = Eliminar texto
+pdfjs-editor-remove-stamp-button =
+ .title = Eliminar imagen
+pdfjs-editor-remove-highlight-button =
+ .title = Quitar resaltado
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Color
+pdfjs-editor-free-text-size-input = Tamaño
+pdfjs-editor-ink-color-input = Color
+pdfjs-editor-ink-thickness-input = Grosor
+pdfjs-editor-ink-opacity-input = Opacidad
+pdfjs-editor-stamp-add-image-button =
+ .title = Añadir imagen
+pdfjs-editor-stamp-add-image-button-label = Añadir imagen
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Grosor
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Cambia el grosor al resaltar elementos que no sean texto
+pdfjs-free-text =
+ .aria-label = Editor de texto
+pdfjs-free-text-default-content = Empieza a escribir…
+pdfjs-ink =
+ .aria-label = Editor de dibujos
+pdfjs-ink-canvas =
+ .aria-label = Imagen creada por el usuario
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Texto alternativo
+pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo
+pdfjs-editor-alt-text-dialog-label = Elige una opción
+pdfjs-editor-alt-text-dialog-description = El texto alternativo (alt text) ayuda cuando las personas no pueden ver la imagen o cuando no se carga.
+pdfjs-editor-alt-text-add-description-label = Añade una descripción
+pdfjs-editor-alt-text-add-description-description = Intenta escribir 1 o 2 oraciones que describan el tema, el ambiente o las acciones.
+pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativa
+pdfjs-editor-alt-text-mark-decorative-description = Se utiliza para imágenes ornamentales, como bordes o marcas de agua.
+pdfjs-editor-alt-text-cancel-button = Cancelar
+pdfjs-editor-alt-text-save-button = Guardar
+pdfjs-editor-alt-text-decorative-tooltip = Marcada como decorativa
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Esquina superior izquierda — cambiar el tamaño
+pdfjs-editor-resizer-label-top-middle = Borde superior en el medio — cambiar el tamaño
+pdfjs-editor-resizer-label-top-right = Esquina superior derecha — cambiar el tamaño
+pdfjs-editor-resizer-label-middle-right = Borde derecho en el medio — cambiar el tamaño
+pdfjs-editor-resizer-label-bottom-right = Esquina inferior derecha — cambiar el tamaño
+pdfjs-editor-resizer-label-bottom-middle = Borde inferior en el medio — cambiar el tamaño
+pdfjs-editor-resizer-label-bottom-left = Esquina inferior izquierda — cambiar el tamaño
+pdfjs-editor-resizer-label-middle-left = Borde izquierdo en el medio — cambiar el tamaño
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Color de resaltado
+pdfjs-editor-colorpicker-button =
+ .title = Cambiar color
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Opciones de color
+pdfjs-editor-colorpicker-yellow =
+ .title = Amarillo
+pdfjs-editor-colorpicker-green =
+ .title = Verde
+pdfjs-editor-colorpicker-blue =
+ .title = Azul
+pdfjs-editor-colorpicker-pink =
+ .title = Rosa
+pdfjs-editor-colorpicker-red =
+ .title = Rojo
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Mostrar todo
+pdfjs-editor-highlight-show-all-button =
+ .title = Mostrar todo
diff --git a/web/locale/es-ES/viewer.ftl b/web/locale/es-ES/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..e3f87b47b365d494102733c707b052d117c57976
--- /dev/null
+++ b/web/locale/es-ES/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Página anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Página siguiente
+pdfjs-next-button-label = Siguiente
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Página
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Reducir
+pdfjs-zoom-out-button-label = Reducir
+pdfjs-zoom-in-button =
+ .title = Aumentar
+pdfjs-zoom-in-button-label = Aumentar
+pdfjs-zoom-select =
+ .title = Tamaño
+pdfjs-presentation-mode-button =
+ .title = Cambiar al modo presentación
+pdfjs-presentation-mode-button-label = Modo presentación
+pdfjs-open-file-button =
+ .title = Abrir archivo
+pdfjs-open-file-button-label = Abrir
+pdfjs-print-button =
+ .title = Imprimir
+pdfjs-print-button-label = Imprimir
+pdfjs-save-button =
+ .title = Guardar
+pdfjs-save-button-label = Guardar
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Descargar
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Descargar
+pdfjs-bookmark-button =
+ .title = Página actual (Ver URL de la página actual)
+pdfjs-bookmark-button-label = Página actual
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Abrir en aplicación
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Abrir en aplicación
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Herramientas
+pdfjs-tools-button-label = Herramientas
+pdfjs-first-page-button =
+ .title = Ir a la primera página
+pdfjs-first-page-button-label = Ir a la primera página
+pdfjs-last-page-button =
+ .title = Ir a la última página
+pdfjs-last-page-button-label = Ir a la última página
+pdfjs-page-rotate-cw-button =
+ .title = Rotar en sentido horario
+pdfjs-page-rotate-cw-button-label = Rotar en sentido horario
+pdfjs-page-rotate-ccw-button =
+ .title = Rotar en sentido antihorario
+pdfjs-page-rotate-ccw-button-label = Rotar en sentido antihorario
+pdfjs-cursor-text-select-tool-button =
+ .title = Activar herramienta de selección de texto
+pdfjs-cursor-text-select-tool-button-label = Herramienta de selección de texto
+pdfjs-cursor-hand-tool-button =
+ .title = Activar herramienta de mano
+pdfjs-cursor-hand-tool-button-label = Herramienta de mano
+pdfjs-scroll-page-button =
+ .title = Usar desplazamiento de página
+pdfjs-scroll-page-button-label = Desplazamiento de página
+pdfjs-scroll-vertical-button =
+ .title = Usar desplazamiento vertical
+pdfjs-scroll-vertical-button-label = Desplazamiento vertical
+pdfjs-scroll-horizontal-button =
+ .title = Usar desplazamiento horizontal
+pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Usar desplazamiento en bloque
+pdfjs-scroll-wrapped-button-label = Desplazamiento en bloque
+pdfjs-spread-none-button =
+ .title = No juntar páginas en vista de libro
+pdfjs-spread-none-button-label = Vista de libro
+pdfjs-spread-odd-button =
+ .title = Juntar las páginas partiendo de una con número impar
+pdfjs-spread-odd-button-label = Vista de libro impar
+pdfjs-spread-even-button =
+ .title = Juntar las páginas partiendo de una con número par
+pdfjs-spread-even-button-label = Vista de libro par
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propiedades del documento…
+pdfjs-document-properties-button-label = Propiedades del documento…
+pdfjs-document-properties-file-name = Nombre de archivo:
+pdfjs-document-properties-file-size = Tamaño de archivo:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Título:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Asunto:
+pdfjs-document-properties-keywords = Palabras clave:
+pdfjs-document-properties-creation-date = Fecha de creación:
+pdfjs-document-properties-modification-date = Fecha de modificación:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creador:
+pdfjs-document-properties-producer = Productor PDF:
+pdfjs-document-properties-version = Versión PDF:
+pdfjs-document-properties-page-count = Número de páginas:
+pdfjs-document-properties-page-size = Tamaño de la página:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertical
+pdfjs-document-properties-page-size-orientation-landscape = horizontal
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Carta
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista rápida de la web:
+pdfjs-document-properties-linearized-yes = Sí
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Cerrar
+
+## Print
+
+pdfjs-print-progress-message = Preparando documento para impresión…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancelar
+pdfjs-printing-not-supported = Advertencia: Imprimir no está totalmente soportado por este navegador.
+pdfjs-printing-not-ready = Advertencia: Este PDF no se ha cargado completamente para poder imprimirse.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Cambiar barra lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Alternar barra lateral (el documento contiene esquemas/adjuntos/capas)
+pdfjs-toggle-sidebar-button-label = Cambiar barra lateral
+pdfjs-document-outline-button =
+ .title = Mostrar resumen del documento (doble clic para expandir/contraer todos los elementos)
+pdfjs-document-outline-button-label = Resumen de documento
+pdfjs-attachments-button =
+ .title = Mostrar adjuntos
+pdfjs-attachments-button-label = Adjuntos
+pdfjs-layers-button =
+ .title = Mostrar capas (doble clic para restablecer todas las capas al estado predeterminado)
+pdfjs-layers-button-label = Capas
+pdfjs-thumbs-button =
+ .title = Mostrar miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-current-outline-item-button =
+ .title = Encontrar elemento de esquema actual
+pdfjs-current-outline-item-button-label = Elemento de esquema actual
+pdfjs-findbar-button =
+ .title = Buscar en el documento
+pdfjs-findbar-button-label = Buscar
+pdfjs-additional-layers = Capas adicionales
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Página { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura de la página { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Buscar
+ .placeholder = Buscar en el documento…
+pdfjs-find-previous-button =
+ .title = Encontrar la anterior aparición de la frase
+pdfjs-find-previous-button-label = Anterior
+pdfjs-find-next-button =
+ .title = Encontrar la siguiente aparición de esta frase
+pdfjs-find-next-button-label = Siguiente
+pdfjs-find-highlight-checkbox = Resaltar todos
+pdfjs-find-match-case-checkbox-label = Coincidencia de mayús./minús.
+pdfjs-find-match-diacritics-checkbox-label = Coincidir diacríticos
+pdfjs-find-entire-word-checkbox-label = Palabras completas
+pdfjs-find-reached-top = Se alcanzó el inicio del documento, se continúa desde el final
+pdfjs-find-reached-bottom = Se alcanzó el final del documento, se continúa desde el inicio
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } de { $total } coincidencia
+ *[other] { $current } de { $total } coincidencias
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Más de { $limit } coincidencia
+ *[other] Más de { $limit } coincidencias
+ }
+pdfjs-find-not-found = Frase no encontrada
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Anchura de la página
+pdfjs-page-scale-fit = Ajuste de la página
+pdfjs-page-scale-auto = Tamaño automático
+pdfjs-page-scale-actual = Tamaño real
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Página { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ocurrió un error al cargar el PDF.
+pdfjs-invalid-file-error = Fichero PDF no válido o corrupto.
+pdfjs-missing-file-error = No hay fichero PDF.
+pdfjs-unexpected-response-error = Respuesta inesperada del servidor.
+pdfjs-rendering-error = Ocurrió un error al renderizar la página.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotación { $type }]
+
+## Password
+
+pdfjs-password-label = Introduzca la contraseña para abrir este archivo PDF.
+pdfjs-password-invalid = Contraseña no válida. Vuelva a intentarlo.
+pdfjs-password-ok-button = Aceptar
+pdfjs-password-cancel-button = Cancelar
+pdfjs-web-fonts-disabled = Las tipografías web están desactivadas: es imposible usar las tipografías PDF embebidas.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texto
+pdfjs-editor-free-text-button-label = Texto
+pdfjs-editor-ink-button =
+ .title = Dibujar
+pdfjs-editor-ink-button-label = Dibujar
+pdfjs-editor-stamp-button =
+ .title = Añadir o editar imágenes
+pdfjs-editor-stamp-button-label = Añadir o editar imágenes
+pdfjs-editor-highlight-button =
+ .title = Resaltar
+pdfjs-editor-highlight-button-label = Resaltar
+pdfjs-highlight-floating-button =
+ .title = Resaltar
+pdfjs-highlight-floating-button1 =
+ .title = Resaltar
+ .aria-label = Resaltar
+pdfjs-highlight-floating-button-label = Resaltar
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Eliminar dibujo
+pdfjs-editor-remove-freetext-button =
+ .title = Eliminar texto
+pdfjs-editor-remove-stamp-button =
+ .title = Eliminar imagen
+pdfjs-editor-remove-highlight-button =
+ .title = Quitar resaltado
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Color
+pdfjs-editor-free-text-size-input = Tamaño
+pdfjs-editor-ink-color-input = Color
+pdfjs-editor-ink-thickness-input = Grosor
+pdfjs-editor-ink-opacity-input = Opacidad
+pdfjs-editor-stamp-add-image-button =
+ .title = Añadir imagen
+pdfjs-editor-stamp-add-image-button-label = Añadir imagen
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Grosor
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Cambiar el grosor al resaltar elementos que no sean texto
+pdfjs-free-text =
+ .aria-label = Editor de texto
+pdfjs-free-text-default-content = Empezar a escribir…
+pdfjs-ink =
+ .aria-label = Editor de dibujos
+pdfjs-ink-canvas =
+ .aria-label = Imagen creada por el usuario
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Texto alternativo
+pdfjs-editor-alt-text-edit-button-label = Editar el texto alternativo
+pdfjs-editor-alt-text-dialog-label = Eligir una opción
+pdfjs-editor-alt-text-dialog-description = El texto alternativo (texto alternativo) ayuda cuando las personas no pueden ver la imagen o cuando no se carga.
+pdfjs-editor-alt-text-add-description-label = Añadir una descripción
+pdfjs-editor-alt-text-add-description-description = Intente escribir 1 o 2 frases que describan el tema, el entorno o las acciones.
+pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativa
+pdfjs-editor-alt-text-mark-decorative-description = Se utiliza para imágenes ornamentales, como bordes o marcas de agua.
+pdfjs-editor-alt-text-cancel-button = Cancelar
+pdfjs-editor-alt-text-save-button = Guardar
+pdfjs-editor-alt-text-decorative-tooltip = Marcada como decorativa
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Por ejemplo: “Un joven se sienta a la mesa a comer”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Esquina superior izquierda — redimensionar
+pdfjs-editor-resizer-label-top-middle = Borde superior en el medio — redimensionar
+pdfjs-editor-resizer-label-top-right = Esquina superior derecha — redimensionar
+pdfjs-editor-resizer-label-middle-right = Borde derecho en el medio — redimensionar
+pdfjs-editor-resizer-label-bottom-right = Esquina inferior derecha — redimensionar
+pdfjs-editor-resizer-label-bottom-middle = Borde inferior en el medio — redimensionar
+pdfjs-editor-resizer-label-bottom-left = Esquina inferior izquierda — redimensionar
+pdfjs-editor-resizer-label-middle-left = Borde izquierdo en el medio — redimensionar
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Color de resaltado
+pdfjs-editor-colorpicker-button =
+ .title = Cambiar color
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Opciones de color
+pdfjs-editor-colorpicker-yellow =
+ .title = Amarillo
+pdfjs-editor-colorpicker-green =
+ .title = Verde
+pdfjs-editor-colorpicker-blue =
+ .title = Azul
+pdfjs-editor-colorpicker-pink =
+ .title = Rosa
+pdfjs-editor-colorpicker-red =
+ .title = Rojo
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Mostrar todo
+pdfjs-editor-highlight-show-all-button =
+ .title = Mostrar todo
diff --git a/web/locale/es-MX/viewer.ftl b/web/locale/es-MX/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..0069c6eb53c869fa8d76225ee53d65a90fa66194
--- /dev/null
+++ b/web/locale/es-MX/viewer.ftl
@@ -0,0 +1,299 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Página anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Página siguiente
+pdfjs-next-button-label = Siguiente
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Página
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Reducir
+pdfjs-zoom-out-button-label = Reducir
+pdfjs-zoom-in-button =
+ .title = Aumentar
+pdfjs-zoom-in-button-label = Aumentar
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Cambiar al modo presentación
+pdfjs-presentation-mode-button-label = Modo presentación
+pdfjs-open-file-button =
+ .title = Abrir archivo
+pdfjs-open-file-button-label = Abrir
+pdfjs-print-button =
+ .title = Imprimir
+pdfjs-print-button-label = Imprimir
+pdfjs-save-button =
+ .title = Guardar
+pdfjs-save-button-label = Guardar
+pdfjs-bookmark-button =
+ .title = Página actual (Ver URL de la página actual)
+pdfjs-bookmark-button-label = Página actual
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Abrir en la aplicación
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Abrir en la aplicación
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Herramientas
+pdfjs-tools-button-label = Herramientas
+pdfjs-first-page-button =
+ .title = Ir a la primera página
+pdfjs-first-page-button-label = Ir a la primera página
+pdfjs-last-page-button =
+ .title = Ir a la última página
+pdfjs-last-page-button-label = Ir a la última página
+pdfjs-page-rotate-cw-button =
+ .title = Girar a la derecha
+pdfjs-page-rotate-cw-button-label = Girar a la derecha
+pdfjs-page-rotate-ccw-button =
+ .title = Girar a la izquierda
+pdfjs-page-rotate-ccw-button-label = Girar a la izquierda
+pdfjs-cursor-text-select-tool-button =
+ .title = Activar la herramienta de selección de texto
+pdfjs-cursor-text-select-tool-button-label = Herramienta de selección de texto
+pdfjs-cursor-hand-tool-button =
+ .title = Activar la herramienta de mano
+pdfjs-cursor-hand-tool-button-label = Herramienta de mano
+pdfjs-scroll-page-button =
+ .title = Usar desplazamiento de página
+pdfjs-scroll-page-button-label = Desplazamiento de página
+pdfjs-scroll-vertical-button =
+ .title = Usar desplazamiento vertical
+pdfjs-scroll-vertical-button-label = Desplazamiento vertical
+pdfjs-scroll-horizontal-button =
+ .title = Usar desplazamiento horizontal
+pdfjs-scroll-horizontal-button-label = Desplazamiento horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Usar desplazamiento encapsulado
+pdfjs-scroll-wrapped-button-label = Desplazamiento encapsulado
+pdfjs-spread-none-button =
+ .title = No unir páginas separadas
+pdfjs-spread-none-button-label = Vista de una página
+pdfjs-spread-odd-button =
+ .title = Unir las páginas partiendo con una de número impar
+pdfjs-spread-odd-button-label = Vista de libro impar
+pdfjs-spread-even-button =
+ .title = Juntar las páginas partiendo con una de número par
+pdfjs-spread-even-button-label = Vista de libro par
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propiedades del documento…
+pdfjs-document-properties-button-label = Propiedades del documento…
+pdfjs-document-properties-file-name = Nombre del archivo:
+pdfjs-document-properties-file-size = Tamaño del archivo:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Título:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Asunto:
+pdfjs-document-properties-keywords = Palabras claves:
+pdfjs-document-properties-creation-date = Fecha de creación:
+pdfjs-document-properties-modification-date = Fecha de modificación:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creador:
+pdfjs-document-properties-producer = Productor PDF:
+pdfjs-document-properties-version = Versión PDF:
+pdfjs-document-properties-page-count = Número de páginas:
+pdfjs-document-properties-page-size = Tamaño de la página:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertical
+pdfjs-document-properties-page-size-orientation-landscape = horizontal
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Carta
+pdfjs-document-properties-page-size-name-legal = Oficio
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista rápida de la web:
+pdfjs-document-properties-linearized-yes = Sí
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Cerrar
+
+## Print
+
+pdfjs-print-progress-message = Preparando documento para impresión…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancelar
+pdfjs-printing-not-supported = Advertencia: La impresión no esta completamente soportada por este navegador.
+pdfjs-printing-not-ready = Advertencia: El PDF no cargo completamente para impresión.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Cambiar barra lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Alternar barra lateral (el documento contiene esquemas/adjuntos/capas)
+pdfjs-toggle-sidebar-button-label = Cambiar barra lateral
+pdfjs-document-outline-button =
+ .title = Mostrar esquema del documento (doble clic para expandir/contraer todos los elementos)
+pdfjs-document-outline-button-label = Esquema del documento
+pdfjs-attachments-button =
+ .title = Mostrar adjuntos
+pdfjs-attachments-button-label = Adjuntos
+pdfjs-layers-button =
+ .title = Mostrar capas (doble clic para restablecer todas las capas al estado predeterminado)
+pdfjs-layers-button-label = Capas
+pdfjs-thumbs-button =
+ .title = Mostrar miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-current-outline-item-button =
+ .title = Buscar elemento de esquema actual
+pdfjs-current-outline-item-button-label = Elemento de esquema actual
+pdfjs-findbar-button =
+ .title = Buscar en el documento
+pdfjs-findbar-button-label = Buscar
+pdfjs-additional-layers = Capas adicionales
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Página { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura de la página { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Buscar
+ .placeholder = Buscar en el documento…
+pdfjs-find-previous-button =
+ .title = Ir a la anterior frase encontrada
+pdfjs-find-previous-button-label = Anterior
+pdfjs-find-next-button =
+ .title = Ir a la siguiente frase encontrada
+pdfjs-find-next-button-label = Siguiente
+pdfjs-find-highlight-checkbox = Resaltar todo
+pdfjs-find-match-case-checkbox-label = Coincidir con mayúsculas y minúsculas
+pdfjs-find-match-diacritics-checkbox-label = Coincidir diacríticos
+pdfjs-find-entire-word-checkbox-label = Palabras completas
+pdfjs-find-reached-top = Se alcanzó el inicio del documento, se buscará al final
+pdfjs-find-reached-bottom = Se alcanzó el final del documento, se buscará al inicio
+pdfjs-find-not-found = No se encontró la frase
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Ancho de página
+pdfjs-page-scale-fit = Ajustar página
+pdfjs-page-scale-auto = Zoom automático
+pdfjs-page-scale-actual = Tamaño real
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Página { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Un error ocurrió al cargar el PDF.
+pdfjs-invalid-file-error = Archivo PDF invalido o dañado.
+pdfjs-missing-file-error = Archivo PDF no encontrado.
+pdfjs-unexpected-response-error = Respuesta inesperada del servidor.
+pdfjs-rendering-error = Un error ocurrió al renderizar la página.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } anotación]
+
+## Password
+
+pdfjs-password-label = Ingresa la contraseña para abrir este archivo PDF.
+pdfjs-password-invalid = Contraseña inválida. Por favor intenta de nuevo.
+pdfjs-password-ok-button = Aceptar
+pdfjs-password-cancel-button = Cancelar
+pdfjs-web-fonts-disabled = Las fuentes web están desactivadas: es imposible usar las fuentes PDF embebidas.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texto
+pdfjs-editor-free-text-button-label = Texto
+pdfjs-editor-ink-button =
+ .title = Dibujar
+pdfjs-editor-ink-button-label = Dibujar
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Color
+pdfjs-editor-free-text-size-input = Tamaño
+pdfjs-editor-ink-color-input = Color
+pdfjs-editor-ink-thickness-input = Grossor
+pdfjs-editor-ink-opacity-input = Opacidad
+pdfjs-free-text =
+ .aria-label = Editor de texto
+pdfjs-free-text-default-content = Empieza a escribir…
+pdfjs-ink =
+ .aria-label = Editor de dibujo
+pdfjs-ink-canvas =
+ .aria-label = Imagen creada por el usuario
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/et/viewer.ftl b/web/locale/et/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..b28c6d5041226744693eda79faae80a26787cf70
--- /dev/null
+++ b/web/locale/et/viewer.ftl
@@ -0,0 +1,268 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Eelmine lehekülg
+pdfjs-previous-button-label = Eelmine
+pdfjs-next-button =
+ .title = Järgmine lehekülg
+pdfjs-next-button-label = Järgmine
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Leht
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = / { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber }/{ $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Vähenda
+pdfjs-zoom-out-button-label = Vähenda
+pdfjs-zoom-in-button =
+ .title = Suurenda
+pdfjs-zoom-in-button-label = Suurenda
+pdfjs-zoom-select =
+ .title = Suurendamine
+pdfjs-presentation-mode-button =
+ .title = Lülitu esitlusrežiimi
+pdfjs-presentation-mode-button-label = Esitlusrežiim
+pdfjs-open-file-button =
+ .title = Ava fail
+pdfjs-open-file-button-label = Ava
+pdfjs-print-button =
+ .title = Prindi
+pdfjs-print-button-label = Prindi
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Tööriistad
+pdfjs-tools-button-label = Tööriistad
+pdfjs-first-page-button =
+ .title = Mine esimesele leheküljele
+pdfjs-first-page-button-label = Mine esimesele leheküljele
+pdfjs-last-page-button =
+ .title = Mine viimasele leheküljele
+pdfjs-last-page-button-label = Mine viimasele leheküljele
+pdfjs-page-rotate-cw-button =
+ .title = Pööra päripäeva
+pdfjs-page-rotate-cw-button-label = Pööra päripäeva
+pdfjs-page-rotate-ccw-button =
+ .title = Pööra vastupäeva
+pdfjs-page-rotate-ccw-button-label = Pööra vastupäeva
+pdfjs-cursor-text-select-tool-button =
+ .title = Luba teksti valimise tööriist
+pdfjs-cursor-text-select-tool-button-label = Teksti valimise tööriist
+pdfjs-cursor-hand-tool-button =
+ .title = Luba sirvimistööriist
+pdfjs-cursor-hand-tool-button-label = Sirvimistööriist
+pdfjs-scroll-page-button =
+ .title = Kasutatakse lehe kaupa kerimist
+pdfjs-scroll-page-button-label = Lehe kaupa kerimine
+pdfjs-scroll-vertical-button =
+ .title = Kasuta vertikaalset kerimist
+pdfjs-scroll-vertical-button-label = Vertikaalne kerimine
+pdfjs-scroll-horizontal-button =
+ .title = Kasuta horisontaalset kerimist
+pdfjs-scroll-horizontal-button-label = Horisontaalne kerimine
+pdfjs-scroll-wrapped-button =
+ .title = Kasuta rohkem mahutavat kerimist
+pdfjs-scroll-wrapped-button-label = Rohkem mahutav kerimine
+pdfjs-spread-none-button =
+ .title = Ära kõrvuta lehekülgi
+pdfjs-spread-none-button-label = Lehtede kõrvutamine puudub
+pdfjs-spread-odd-button =
+ .title = Kõrvuta leheküljed, alustades paaritute numbritega lehekülgedega
+pdfjs-spread-odd-button-label = Kõrvutamine paaritute numbritega alustades
+pdfjs-spread-even-button =
+ .title = Kõrvuta leheküljed, alustades paarisnumbritega lehekülgedega
+pdfjs-spread-even-button-label = Kõrvutamine paarisnumbritega alustades
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumendi omadused…
+pdfjs-document-properties-button-label = Dokumendi omadused…
+pdfjs-document-properties-file-name = Faili nimi:
+pdfjs-document-properties-file-size = Faili suurus:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KiB ({ $size_b } baiti)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MiB ({ $size_b } baiti)
+pdfjs-document-properties-title = Pealkiri:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Teema:
+pdfjs-document-properties-keywords = Märksõnad:
+pdfjs-document-properties-creation-date = Loodud:
+pdfjs-document-properties-modification-date = Muudetud:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date } { $time }
+pdfjs-document-properties-creator = Looja:
+pdfjs-document-properties-producer = Generaator:
+pdfjs-document-properties-version = Generaatori versioon:
+pdfjs-document-properties-page-count = Lehekülgi:
+pdfjs-document-properties-page-size = Lehe suurus:
+pdfjs-document-properties-page-size-unit-inches = tolli
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertikaalpaigutus
+pdfjs-document-properties-page-size-orientation-landscape = rõhtpaigutus
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = "Fast Web View" tugi:
+pdfjs-document-properties-linearized-yes = Jah
+pdfjs-document-properties-linearized-no = Ei
+pdfjs-document-properties-close-button = Sulge
+
+## Print
+
+pdfjs-print-progress-message = Dokumendi ettevalmistamine printimiseks…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Loobu
+pdfjs-printing-not-supported = Hoiatus: printimine pole selle brauseri poolt täielikult toetatud.
+pdfjs-printing-not-ready = Hoiatus: PDF pole printimiseks täielikult laaditud.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Näita külgriba
+pdfjs-toggle-sidebar-notification-button =
+ .title = Näita külgriba (dokument sisaldab sisukorda/manuseid/kihte)
+pdfjs-toggle-sidebar-button-label = Näita külgriba
+pdfjs-document-outline-button =
+ .title = Näita sisukorda (kõigi punktide laiendamiseks/ahendamiseks topeltklõpsa)
+pdfjs-document-outline-button-label = Näita sisukorda
+pdfjs-attachments-button =
+ .title = Näita manuseid
+pdfjs-attachments-button-label = Manused
+pdfjs-layers-button =
+ .title = Näita kihte (kõikide kihtide vaikeolekusse lähtestamiseks topeltklõpsa)
+pdfjs-layers-button-label = Kihid
+pdfjs-thumbs-button =
+ .title = Näita pisipilte
+pdfjs-thumbs-button-label = Pisipildid
+pdfjs-current-outline-item-button =
+ .title = Otsi üles praegune kontuuriüksus
+pdfjs-current-outline-item-button-label = Praegune kontuuriüksus
+pdfjs-findbar-button =
+ .title = Otsi dokumendist
+pdfjs-findbar-button-label = Otsi
+pdfjs-additional-layers = Täiendavad kihid
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page }. lehekülg
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page }. lehekülje pisipilt
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Otsi
+ .placeholder = Otsi dokumendist…
+pdfjs-find-previous-button =
+ .title = Otsi fraasi eelmine esinemiskoht
+pdfjs-find-previous-button-label = Eelmine
+pdfjs-find-next-button =
+ .title = Otsi fraasi järgmine esinemiskoht
+pdfjs-find-next-button-label = Järgmine
+pdfjs-find-highlight-checkbox = Too kõik esile
+pdfjs-find-match-case-checkbox-label = Tõstutundlik
+pdfjs-find-match-diacritics-checkbox-label = Otsitakse diakriitiliselt
+pdfjs-find-entire-word-checkbox-label = Täissõnad
+pdfjs-find-reached-top = Jõuti dokumendi algusesse, jätkati lõpust
+pdfjs-find-reached-bottom = Jõuti dokumendi lõppu, jätkati algusest
+pdfjs-find-not-found = Fraasi ei leitud
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Mahuta laiusele
+pdfjs-page-scale-fit = Mahuta leheküljele
+pdfjs-page-scale-auto = Automaatne suurendamine
+pdfjs-page-scale-actual = Tegelik suurus
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Lehekülg { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDFi laadimisel esines viga.
+pdfjs-invalid-file-error = Vigane või rikutud PDF-fail.
+pdfjs-missing-file-error = PDF-fail puudub.
+pdfjs-unexpected-response-error = Ootamatu vastus serverilt.
+pdfjs-rendering-error = Lehe renderdamisel esines viga.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date } { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = PDF-faili avamiseks sisesta parool.
+pdfjs-password-invalid = Vigane parool. Palun proovi uuesti.
+pdfjs-password-ok-button = Sobib
+pdfjs-password-cancel-button = Loobu
+pdfjs-web-fonts-disabled = Veebifondid on keelatud: PDFiga kaasatud fonte pole võimalik kasutada.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/eu/viewer.ftl b/web/locale/eu/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..1ed37397d08ed40642167c19f2f9479d6946a2c9
--- /dev/null
+++ b/web/locale/eu/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Aurreko orria
+pdfjs-previous-button-label = Aurrekoa
+pdfjs-next-button =
+ .title = Hurrengo orria
+pdfjs-next-button-label = Hurrengoa
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Orria
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = / { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = { $pagesCount }/{ $pageNumber }
+pdfjs-zoom-out-button =
+ .title = Urrundu zooma
+pdfjs-zoom-out-button-label = Urrundu zooma
+pdfjs-zoom-in-button =
+ .title = Gerturatu zooma
+pdfjs-zoom-in-button-label = Gerturatu zooma
+pdfjs-zoom-select =
+ .title = Zooma
+pdfjs-presentation-mode-button =
+ .title = Aldatu aurkezpen modura
+pdfjs-presentation-mode-button-label = Arkezpen modua
+pdfjs-open-file-button =
+ .title = Ireki fitxategia
+pdfjs-open-file-button-label = Ireki
+pdfjs-print-button =
+ .title = Inprimatu
+pdfjs-print-button-label = Inprimatu
+pdfjs-save-button =
+ .title = Gorde
+pdfjs-save-button-label = Gorde
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Deskargatu
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Deskargatu
+pdfjs-bookmark-button =
+ .title = Uneko orria (ikusi uneko orriaren URLa)
+pdfjs-bookmark-button-label = Uneko orria
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Ireki aplikazioan
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Ireki aplikazioan
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Tresnak
+pdfjs-tools-button-label = Tresnak
+pdfjs-first-page-button =
+ .title = Joan lehen orrira
+pdfjs-first-page-button-label = Joan lehen orrira
+pdfjs-last-page-button =
+ .title = Joan azken orrira
+pdfjs-last-page-button-label = Joan azken orrira
+pdfjs-page-rotate-cw-button =
+ .title = Biratu erlojuaren norantzan
+pdfjs-page-rotate-cw-button-label = Biratu erlojuaren norantzan
+pdfjs-page-rotate-ccw-button =
+ .title = Biratu erlojuaren aurkako norantzan
+pdfjs-page-rotate-ccw-button-label = Biratu erlojuaren aurkako norantzan
+pdfjs-cursor-text-select-tool-button =
+ .title = Gaitu testuaren hautapen tresna
+pdfjs-cursor-text-select-tool-button-label = Testuaren hautapen tresna
+pdfjs-cursor-hand-tool-button =
+ .title = Gaitu eskuaren tresna
+pdfjs-cursor-hand-tool-button-label = Eskuaren tresna
+pdfjs-scroll-page-button =
+ .title = Erabili orriaren korritzea
+pdfjs-scroll-page-button-label = Orriaren korritzea
+pdfjs-scroll-vertical-button =
+ .title = Erabili korritze bertikala
+pdfjs-scroll-vertical-button-label = Korritze bertikala
+pdfjs-scroll-horizontal-button =
+ .title = Erabili korritze horizontala
+pdfjs-scroll-horizontal-button-label = Korritze horizontala
+pdfjs-scroll-wrapped-button =
+ .title = Erabili korritze egokitua
+pdfjs-scroll-wrapped-button-label = Korritze egokitua
+pdfjs-spread-none-button =
+ .title = Ez elkartu barreiatutako orriak
+pdfjs-spread-none-button-label = Barreiatzerik ez
+pdfjs-spread-odd-button =
+ .title = Elkartu barreiatutako orriak bakoiti zenbakidunekin hasita
+pdfjs-spread-odd-button-label = Barreiatze bakoitia
+pdfjs-spread-even-button =
+ .title = Elkartu barreiatutako orriak bikoiti zenbakidunekin hasita
+pdfjs-spread-even-button-label = Barreiatze bikoitia
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumentuaren propietateak…
+pdfjs-document-properties-button-label = Dokumentuaren propietateak…
+pdfjs-document-properties-file-name = Fitxategi-izena:
+pdfjs-document-properties-file-size = Fitxategiaren tamaina:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } byte)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte)
+pdfjs-document-properties-title = Izenburua:
+pdfjs-document-properties-author = Egilea:
+pdfjs-document-properties-subject = Gaia:
+pdfjs-document-properties-keywords = Gako-hitzak:
+pdfjs-document-properties-creation-date = Sortze-data:
+pdfjs-document-properties-modification-date = Aldatze-data:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Sortzailea:
+pdfjs-document-properties-producer = PDFaren ekoizlea:
+pdfjs-document-properties-version = PDF bertsioa:
+pdfjs-document-properties-page-count = Orrialde kopurua:
+pdfjs-document-properties-page-size = Orriaren tamaina:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = bertikala
+pdfjs-document-properties-page-size-orientation-landscape = horizontala
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Gutuna
+pdfjs-document-properties-page-size-name-legal = Legala
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Webeko ikuspegi bizkorra:
+pdfjs-document-properties-linearized-yes = Bai
+pdfjs-document-properties-linearized-no = Ez
+pdfjs-document-properties-close-button = Itxi
+
+## Print
+
+pdfjs-print-progress-message = Dokumentua inprimatzeko prestatzen…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = %{ $progress }
+pdfjs-print-progress-close-button = Utzi
+pdfjs-printing-not-supported = Abisua: inprimatzeko euskarria ez da erabatekoa nabigatzaile honetan.
+pdfjs-printing-not-ready = Abisua: PDFa ez dago erabat kargatuta inprimatzeko.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Txandakatu alboko barra
+pdfjs-toggle-sidebar-notification-button =
+ .title = Txandakatu alboko barra (dokumentuak eskema/eranskinak/geruzak ditu)
+pdfjs-toggle-sidebar-button-label = Txandakatu alboko barra
+pdfjs-document-outline-button =
+ .title = Erakutsi dokumentuaren eskema (klik bikoitza elementu guztiak zabaltzeko/tolesteko)
+pdfjs-document-outline-button-label = Dokumentuaren eskema
+pdfjs-attachments-button =
+ .title = Erakutsi eranskinak
+pdfjs-attachments-button-label = Eranskinak
+pdfjs-layers-button =
+ .title = Erakutsi geruzak (klik bikoitza geruza guztiak egoera lehenetsira berrezartzeko)
+pdfjs-layers-button-label = Geruzak
+pdfjs-thumbs-button =
+ .title = Erakutsi koadro txikiak
+pdfjs-thumbs-button-label = Koadro txikiak
+pdfjs-current-outline-item-button =
+ .title = Bilatu uneko eskemaren elementua
+pdfjs-current-outline-item-button-label = Uneko eskemaren elementua
+pdfjs-findbar-button =
+ .title = Bilatu dokumentuan
+pdfjs-findbar-button-label = Bilatu
+pdfjs-additional-layers = Geruza gehigarriak
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page }. orria
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page }. orriaren koadro txikia
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Bilatu
+ .placeholder = Bilatu dokumentuan…
+pdfjs-find-previous-button =
+ .title = Bilatu esaldiaren aurreko parekatzea
+pdfjs-find-previous-button-label = Aurrekoa
+pdfjs-find-next-button =
+ .title = Bilatu esaldiaren hurrengo parekatzea
+pdfjs-find-next-button-label = Hurrengoa
+pdfjs-find-highlight-checkbox = Nabarmendu guztia
+pdfjs-find-match-case-checkbox-label = Bat etorri maiuskulekin/minuskulekin
+pdfjs-find-match-diacritics-checkbox-label = Bereizi diakritikoak
+pdfjs-find-entire-word-checkbox-label = Hitz osoak
+pdfjs-find-reached-top = Dokumentuaren hasierara heldu da, bukaeratik jarraitzen
+pdfjs-find-reached-bottom = Dokumentuaren bukaerara heldu da, hasieratik jarraitzen
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $total }/{ $current }. bat-etortzea
+ *[other] { $total }/{ $current }. bat-etortzea
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Bat datorren { $limit } baino gehiago
+ *[other] Bat datozen { $limit } baino gehiago
+ }
+pdfjs-find-not-found = Esaldia ez da aurkitu
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Orriaren zabalera
+pdfjs-page-scale-fit = Doitu orrira
+pdfjs-page-scale-auto = Zoom automatikoa
+pdfjs-page-scale-actual = Benetako tamaina
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = %{ $scale }
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = { $page }. orria
+
+## Loading indicator messages
+
+pdfjs-loading-error = Errorea gertatu da PDFa kargatzean.
+pdfjs-invalid-file-error = PDF fitxategi baliogabe edo hondatua.
+pdfjs-missing-file-error = PDF fitxategia falta da.
+pdfjs-unexpected-response-error = Espero gabeko zerbitzariaren erantzuna.
+pdfjs-rendering-error = Errorea gertatu da orria errendatzean.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } ohartarazpena]
+
+## Password
+
+pdfjs-password-label = Idatzi PDF fitxategi hau irekitzeko pasahitza.
+pdfjs-password-invalid = Pasahitz baliogabea. Saiatu berriro mesedez.
+pdfjs-password-ok-button = Ados
+pdfjs-password-cancel-button = Utzi
+pdfjs-web-fonts-disabled = Webeko letra-tipoak desgaituta daude: ezin dira kapsulatutako PDF letra-tipoak erabili.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Testua
+pdfjs-editor-free-text-button-label = Testua
+pdfjs-editor-ink-button =
+ .title = Marrazkia
+pdfjs-editor-ink-button-label = Marrazkia
+pdfjs-editor-stamp-button =
+ .title = Gehitu edo editatu irudiak
+pdfjs-editor-stamp-button-label = Gehitu edo editatu irudiak
+pdfjs-editor-highlight-button =
+ .title = Nabarmendu
+pdfjs-editor-highlight-button-label = Nabarmendu
+pdfjs-highlight-floating-button =
+ .title = Nabarmendu
+pdfjs-highlight-floating-button1 =
+ .title = Nabarmendu
+ .aria-label = Nabarmendu
+pdfjs-highlight-floating-button-label = Nabarmendu
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Kendu marrazkia
+pdfjs-editor-remove-freetext-button =
+ .title = Kendu testua
+pdfjs-editor-remove-stamp-button =
+ .title = Kendu irudia
+pdfjs-editor-remove-highlight-button =
+ .title = Kendu nabarmentzea
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Kolorea
+pdfjs-editor-free-text-size-input = Tamaina
+pdfjs-editor-ink-color-input = Kolorea
+pdfjs-editor-ink-thickness-input = Loditasuna
+pdfjs-editor-ink-opacity-input = Opakutasuna
+pdfjs-editor-stamp-add-image-button =
+ .title = Gehitu irudia
+pdfjs-editor-stamp-add-image-button-label = Gehitu irudia
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Loditasuna
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Aldatu loditasuna testua ez beste elementuak nabarmentzean
+pdfjs-free-text =
+ .aria-label = Testu-editorea
+pdfjs-free-text-default-content = Hasi idazten…
+pdfjs-ink =
+ .aria-label = Marrazki-editorea
+pdfjs-ink-canvas =
+ .aria-label = Erabiltzaileak sortutako irudia
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Testu alternatiboa
+pdfjs-editor-alt-text-edit-button-label = Editatu testu alternatiboa
+pdfjs-editor-alt-text-dialog-label = Aukeratu aukera
+pdfjs-editor-alt-text-dialog-description = Testu alternatiboak laguntzen du jendeak ezin duenean irudia ikusi edo ez denean kargatzen.
+pdfjs-editor-alt-text-add-description-label = Gehitu azalpena
+pdfjs-editor-alt-text-add-description-description = Saiatu idazten gaia, ezarpena edo ekintzak deskribatzen dituen esaldi 1 edo 2.
+pdfjs-editor-alt-text-mark-decorative-label = Markatu apaingarri gisa
+pdfjs-editor-alt-text-mark-decorative-description = Irudiak apaingarrientzat erabiltzen da, adibidez ertz edo ur-marketarako.
+pdfjs-editor-alt-text-cancel-button = Utzi
+pdfjs-editor-alt-text-save-button = Gorde
+pdfjs-editor-alt-text-decorative-tooltip = Apaingarri gisa markatuta
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Adibidez, "gizon gaztea mahaian eserita dago bazkaltzeko"
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Goiko ezkerreko izkina — aldatu tamaina
+pdfjs-editor-resizer-label-top-middle = Goian erdian — aldatu tamaina
+pdfjs-editor-resizer-label-top-right = Goiko eskuineko izkina — aldatu tamaina
+pdfjs-editor-resizer-label-middle-right = Erdian eskuinean — aldatu tamaina
+pdfjs-editor-resizer-label-bottom-right = Beheko eskuineko izkina — aldatu tamaina
+pdfjs-editor-resizer-label-bottom-middle = Behean erdian — aldatu tamaina
+pdfjs-editor-resizer-label-bottom-left = Beheko ezkerreko izkina — aldatu tamaina
+pdfjs-editor-resizer-label-middle-left = Erdian ezkerrean — aldatu tamaina
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Nabarmentze kolorea
+pdfjs-editor-colorpicker-button =
+ .title = Aldatu kolorea
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Kolore-aukerak
+pdfjs-editor-colorpicker-yellow =
+ .title = Horia
+pdfjs-editor-colorpicker-green =
+ .title = Berdea
+pdfjs-editor-colorpicker-blue =
+ .title = Urdina
+pdfjs-editor-colorpicker-pink =
+ .title = Arrosa
+pdfjs-editor-colorpicker-red =
+ .title = Gorria
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Erakutsi denak
+pdfjs-editor-highlight-show-all-button =
+ .title = Erakutsi denak
diff --git a/web/locale/fa/viewer.ftl b/web/locale/fa/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..f367e3c6479b9deb0c947e04f49042c1cd9d19ee
--- /dev/null
+++ b/web/locale/fa/viewer.ftl
@@ -0,0 +1,246 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = صفحهٔ قبلی
+pdfjs-previous-button-label = قبلی
+pdfjs-next-button =
+ .title = صفحهٔ بعدی
+pdfjs-next-button-label = بعدی
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = صفحه
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = از { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber }از { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = کوچکنمایی
+pdfjs-zoom-out-button-label = کوچکنمایی
+pdfjs-zoom-in-button =
+ .title = بزرگنمایی
+pdfjs-zoom-in-button-label = بزرگنمایی
+pdfjs-zoom-select =
+ .title = زوم
+pdfjs-presentation-mode-button =
+ .title = تغییر به حالت ارائه
+pdfjs-presentation-mode-button-label = حالت ارائه
+pdfjs-open-file-button =
+ .title = باز کردن پرونده
+pdfjs-open-file-button-label = باز کردن
+pdfjs-print-button =
+ .title = چاپ
+pdfjs-print-button-label = چاپ
+pdfjs-save-button-label = ذخیره
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ابزارها
+pdfjs-tools-button-label = ابزارها
+pdfjs-first-page-button =
+ .title = برو به اولین صفحه
+pdfjs-first-page-button-label = برو به اولین صفحه
+pdfjs-last-page-button =
+ .title = برو به آخرین صفحه
+pdfjs-last-page-button-label = برو به آخرین صفحه
+pdfjs-page-rotate-cw-button =
+ .title = چرخش ساعتگرد
+pdfjs-page-rotate-cw-button-label = چرخش ساعتگرد
+pdfjs-page-rotate-ccw-button =
+ .title = چرخش پاد ساعتگرد
+pdfjs-page-rotate-ccw-button-label = چرخش پاد ساعتگرد
+pdfjs-cursor-text-select-tool-button =
+ .title = فعال کردن ابزارِ انتخابِ متن
+pdfjs-cursor-text-select-tool-button-label = ابزارِ انتخابِ متن
+pdfjs-cursor-hand-tool-button =
+ .title = فعال کردن ابزارِ دست
+pdfjs-cursor-hand-tool-button-label = ابزار دست
+pdfjs-scroll-vertical-button =
+ .title = استفاده از پیمایش عمودی
+pdfjs-scroll-vertical-button-label = پیمایش عمودی
+pdfjs-scroll-horizontal-button =
+ .title = استفاده از پیمایش افقی
+pdfjs-scroll-horizontal-button-label = پیمایش افقی
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = خصوصیات سند...
+pdfjs-document-properties-button-label = خصوصیات سند...
+pdfjs-document-properties-file-name = نام فایل:
+pdfjs-document-properties-file-size = حجم پرونده:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } کیلوبایت ({ $size_b } بایت)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } مگابایت ({ $size_b } بایت)
+pdfjs-document-properties-title = عنوان:
+pdfjs-document-properties-author = نویسنده:
+pdfjs-document-properties-subject = موضوع:
+pdfjs-document-properties-keywords = کلیدواژهها:
+pdfjs-document-properties-creation-date = تاریخ ایجاد:
+pdfjs-document-properties-modification-date = تاریخ ویرایش:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }، { $time }
+pdfjs-document-properties-creator = ایجاد کننده:
+pdfjs-document-properties-producer = ایجاد کننده PDF:
+pdfjs-document-properties-version = نسخه PDF:
+pdfjs-document-properties-page-count = تعداد صفحات:
+pdfjs-document-properties-page-size = اندازه صفحه:
+pdfjs-document-properties-page-size-unit-inches = اینچ
+pdfjs-document-properties-page-size-unit-millimeters = میلیمتر
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = نامه
+pdfjs-document-properties-page-size-name-legal = حقوقی
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+pdfjs-document-properties-linearized-yes = بله
+pdfjs-document-properties-linearized-no = خیر
+pdfjs-document-properties-close-button = بستن
+
+## Print
+
+pdfjs-print-progress-message = آماده سازی مدارک برای چاپ کردن…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = لغو
+pdfjs-printing-not-supported = هشدار: قابلیت چاپ بهطور کامل در این مرورگر پشتیبانی نمیشود.
+pdfjs-printing-not-ready = اخطار: پرونده PDF بطور کامل بارگیری نشده و امکان چاپ وجود ندارد.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = باز و بسته کردن نوار کناری
+pdfjs-toggle-sidebar-button-label = تغییرحالت نوارکناری
+pdfjs-document-outline-button =
+ .title = نمایش رئوس مطالب مدارک(برای بازشدن/جمع شدن همه موارد دوبار کلیک کنید)
+pdfjs-document-outline-button-label = طرح نوشتار
+pdfjs-attachments-button =
+ .title = نمایش پیوستها
+pdfjs-attachments-button-label = پیوستها
+pdfjs-layers-button-label = لایهها
+pdfjs-thumbs-button =
+ .title = نمایش تصاویر بندانگشتی
+pdfjs-thumbs-button-label = تصاویر بندانگشتی
+pdfjs-findbar-button =
+ .title = جستجو در سند
+pdfjs-findbar-button-label = پیدا کردن
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = صفحه { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = تصویر بند انگشتی صفحه { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = پیدا کردن
+ .placeholder = پیدا کردن در سند…
+pdfjs-find-previous-button =
+ .title = پیدا کردن رخداد قبلی عبارت
+pdfjs-find-previous-button-label = قبلی
+pdfjs-find-next-button =
+ .title = پیدا کردن رخداد بعدی عبارت
+pdfjs-find-next-button-label = بعدی
+pdfjs-find-highlight-checkbox = برجسته و هایلایت کردن همه موارد
+pdfjs-find-match-case-checkbox-label = تطبیق کوچکی و بزرگی حروف
+pdfjs-find-entire-word-checkbox-label = تمام کلمهها
+pdfjs-find-reached-top = به بالای صفحه رسیدیم، از پایین ادامه میدهیم
+pdfjs-find-reached-bottom = به آخر صفحه رسیدیم، از بالا ادامه میدهیم
+pdfjs-find-not-found = عبارت پیدا نشد
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = عرض صفحه
+pdfjs-page-scale-fit = اندازه کردن صفحه
+pdfjs-page-scale-auto = بزرگنمایی خودکار
+pdfjs-page-scale-actual = اندازه واقعی
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = صفحهٔ { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = هنگام بارگیری پرونده PDF خطایی رخ داد.
+pdfjs-invalid-file-error = پرونده PDF نامعتبر یامعیوب میباشد.
+pdfjs-missing-file-error = پرونده PDF یافت نشد.
+pdfjs-unexpected-response-error = پاسخ پیش بینی نشده سرور
+pdfjs-rendering-error = هنگام بارگیری صفحه خطایی رخ داد.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = جهت باز کردن پرونده PDF گذرواژه را وارد نمائید.
+pdfjs-password-invalid = گذرواژه نامعتبر. لطفا مجددا تلاش کنید.
+pdfjs-password-ok-button = تأیید
+pdfjs-password-cancel-button = لغو
+pdfjs-web-fonts-disabled = فونت های تحت وب غیر فعال شده اند: امکان استفاده از نمایش دهنده داخلی PDF وجود ندارد.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = متن
+pdfjs-editor-free-text-button-label = متن
+pdfjs-editor-ink-button =
+ .title = کشیدن
+pdfjs-editor-ink-button-label = کشیدن
+# Editor Parameters
+pdfjs-editor-free-text-color-input = رنگ
+pdfjs-editor-free-text-size-input = اندازه
+pdfjs-editor-ink-color-input = رنگ
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ff/viewer.ftl b/web/locale/ff/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..d1419f54340a11535131b14e377f88ee772e9357
--- /dev/null
+++ b/web/locale/ff/viewer.ftl
@@ -0,0 +1,247 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Hello Ɓennungo
+pdfjs-previous-button-label = Ɓennuɗo
+pdfjs-next-button =
+ .title = Hello faango
+pdfjs-next-button-label = Yeeso
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Hello
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = e nder { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Lonngo Woɗɗa
+pdfjs-zoom-out-button-label = Lonngo Woɗɗa
+pdfjs-zoom-in-button =
+ .title = Lonngo Ara
+pdfjs-zoom-in-button-label = Lonngo Ara
+pdfjs-zoom-select =
+ .title = Lonngo
+pdfjs-presentation-mode-button =
+ .title = Faytu to Presentation Mode
+pdfjs-presentation-mode-button-label = Presentation Mode
+pdfjs-open-file-button =
+ .title = Uddit Fiilde
+pdfjs-open-file-button-label = Uddit
+pdfjs-print-button =
+ .title = Winndito
+pdfjs-print-button-label = Winndito
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Kuutorɗe
+pdfjs-tools-button-label = Kuutorɗe
+pdfjs-first-page-button =
+ .title = Yah to hello adanngo
+pdfjs-first-page-button-label = Yah to hello adanngo
+pdfjs-last-page-button =
+ .title = Yah to hello wattindiingo
+pdfjs-last-page-button-label = Yah to hello wattindiingo
+pdfjs-page-rotate-cw-button =
+ .title = Yiiltu Faya Ñaamo
+pdfjs-page-rotate-cw-button-label = Yiiltu Faya Ñaamo
+pdfjs-page-rotate-ccw-button =
+ .title = Yiiltu Faya Nano
+pdfjs-page-rotate-ccw-button-label = Yiiltu Faya Nano
+pdfjs-cursor-text-select-tool-button =
+ .title = Gollin kaɓirgel cuɓirgel binndi
+pdfjs-cursor-text-select-tool-button-label = Kaɓirgel cuɓirgel binndi
+pdfjs-cursor-hand-tool-button =
+ .title = Hurmin kuutorgal junngo
+pdfjs-cursor-hand-tool-button-label = Kaɓirgel junngo
+pdfjs-scroll-vertical-button =
+ .title = Huutoro gorwitol daringol
+pdfjs-scroll-vertical-button-label = Gorwitol daringol
+pdfjs-scroll-horizontal-button =
+ .title = Huutoro gorwitol lelingol
+pdfjs-scroll-horizontal-button-label = Gorwitol daringol
+pdfjs-scroll-wrapped-button =
+ .title = Huutoro gorwitol coomingol
+pdfjs-scroll-wrapped-button-label = Gorwitol coomingol
+pdfjs-spread-none-button =
+ .title = Hoto tawtu kelle kelle
+pdfjs-spread-none-button-label = Alaa Spreads
+pdfjs-spread-odd-button =
+ .title = Tawtu kelle puɗɗortooɗe kelle teelɗe
+pdfjs-spread-odd-button-label = Kelle teelɗe
+pdfjs-spread-even-button =
+ .title = Tawtu ɗereeji kelle puɗɗoriiɗi kelle teeltuɗe
+pdfjs-spread-even-button-label = Kelle teeltuɗe
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Keeroraaɗi Winndannde…
+pdfjs-document-properties-button-label = Keeroraaɗi Winndannde…
+pdfjs-document-properties-file-name = Innde fiilde:
+pdfjs-document-properties-file-size = Ɓetol fiilde:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bite)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bite)
+pdfjs-document-properties-title = Tiitoonde:
+pdfjs-document-properties-author = Binnduɗo:
+pdfjs-document-properties-subject = Toɓɓere:
+pdfjs-document-properties-keywords = Kelmekele jiytirɗe:
+pdfjs-document-properties-creation-date = Ñalnde Sosaa:
+pdfjs-document-properties-modification-date = Ñalnde Waylaa:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Cosɗo:
+pdfjs-document-properties-producer = Paggiiɗo PDF:
+pdfjs-document-properties-version = Yamre PDF:
+pdfjs-document-properties-page-count = Limoore Kelle:
+pdfjs-document-properties-page-size = Ɓeto Hello:
+pdfjs-document-properties-page-size-unit-inches = nder
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = dariingo
+pdfjs-document-properties-page-size-orientation-landscape = wertiingo
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Ɓataake
+pdfjs-document-properties-page-size-name-legal = Laawol
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Ɗisngo geese yaawngo:
+pdfjs-document-properties-linearized-yes = Eey
+pdfjs-document-properties-linearized-no = Alaa
+pdfjs-document-properties-close-button = Uddu
+
+## Print
+
+pdfjs-print-progress-message = Nana heboo winnditaade fiilannde…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Haaytu
+pdfjs-printing-not-supported = Reentino: Winnditagol tammbitaaka no feewi e ndee wanngorde.
+pdfjs-printing-not-ready = Reentino: PDF oo loowaaki haa timmi ngam winnditagol.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Toggilo Palal Sawndo
+pdfjs-toggle-sidebar-button-label = Toggilo Palal Sawndo
+pdfjs-document-outline-button =
+ .title = Hollu Ƴiyal Fiilannde (dobdobo ngam wertude/taggude teme fof)
+pdfjs-document-outline-button-label = Toɓɓe Fiilannde
+pdfjs-attachments-button =
+ .title = Hollu Ɗisanɗe
+pdfjs-attachments-button-label = Ɗisanɗe
+pdfjs-thumbs-button =
+ .title = Hollu Dooɓe
+pdfjs-thumbs-button-label = Dooɓe
+pdfjs-findbar-button =
+ .title = Yiylo e fiilannde
+pdfjs-findbar-button-label = Yiytu
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Hello { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Dooɓre Hello { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Yiytu
+ .placeholder = Yiylo nder dokimaa
+pdfjs-find-previous-button =
+ .title = Yiylo cilol ɓennugol konngol ngol
+pdfjs-find-previous-button-label = Ɓennuɗo
+pdfjs-find-next-button =
+ .title = Yiylo cilol garowol konngol ngol
+pdfjs-find-next-button-label = Yeeso
+pdfjs-find-highlight-checkbox = Jalbin fof
+pdfjs-find-match-case-checkbox-label = Jaaɓnu darnde
+pdfjs-find-entire-word-checkbox-label = Kelme timmuɗe tan
+pdfjs-find-reached-top = Heɓii fuɗɗorde fiilannde, jokku faya les
+pdfjs-find-reached-bottom = Heɓii hoore fiilannde, jokku faya les
+pdfjs-find-not-found = Konngi njiyataa
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Njaajeendi Hello
+pdfjs-page-scale-fit = Keƴeendi Hello
+pdfjs-page-scale-auto = Loongorde Jaajol
+pdfjs-page-scale-actual = Ɓetol Jaati
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Juumre waɗii tuma nde loowata PDF oo.
+pdfjs-invalid-file-error = Fiilde PDF moƴƴaani walla jiibii.
+pdfjs-missing-file-error = Fiilde PDF ena ŋakki.
+pdfjs-unexpected-response-error = Jaabtol sarworde tijjinooka.
+pdfjs-rendering-error = Juumre waɗii tuma nde yoŋkittoo hello.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Siiftannde]
+
+## Password
+
+pdfjs-password-label = Naatu finnde ngam uddite ndee fiilde PDF.
+pdfjs-password-invalid = Finnde moƴƴaani. Tiiɗno eto kadi.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Haaytu
+pdfjs-web-fonts-disabled = Ponte geese ko daaƴaaɗe: horiima huutoraade ponte PDF coomtoraaɗe.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/fi/viewer.ftl b/web/locale/fi/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..51667837cea662f29b9ef2c9285735e3043e4bd8
--- /dev/null
+++ b/web/locale/fi/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Edellinen sivu
+pdfjs-previous-button-label = Edellinen
+pdfjs-next-button =
+ .title = Seuraava sivu
+pdfjs-next-button-label = Seuraava
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Sivu
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = / { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Loitonna
+pdfjs-zoom-out-button-label = Loitonna
+pdfjs-zoom-in-button =
+ .title = Lähennä
+pdfjs-zoom-in-button-label = Lähennä
+pdfjs-zoom-select =
+ .title = Suurennus
+pdfjs-presentation-mode-button =
+ .title = Siirry esitystilaan
+pdfjs-presentation-mode-button-label = Esitystila
+pdfjs-open-file-button =
+ .title = Avaa tiedosto
+pdfjs-open-file-button-label = Avaa
+pdfjs-print-button =
+ .title = Tulosta
+pdfjs-print-button-label = Tulosta
+pdfjs-save-button =
+ .title = Tallenna
+pdfjs-save-button-label = Tallenna
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Lataa
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Lataa
+pdfjs-bookmark-button =
+ .title = Nykyinen sivu (Näytä URL-osoite nykyiseltä sivulta)
+pdfjs-bookmark-button-label = Nykyinen sivu
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Avaa sovelluksessa
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Avaa sovelluksessa
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Tools
+pdfjs-tools-button-label = Tools
+pdfjs-first-page-button =
+ .title = Siirry ensimmäiselle sivulle
+pdfjs-first-page-button-label = Siirry ensimmäiselle sivulle
+pdfjs-last-page-button =
+ .title = Siirry viimeiselle sivulle
+pdfjs-last-page-button-label = Siirry viimeiselle sivulle
+pdfjs-page-rotate-cw-button =
+ .title = Kierrä oikealle
+pdfjs-page-rotate-cw-button-label = Kierrä oikealle
+pdfjs-page-rotate-ccw-button =
+ .title = Kierrä vasemmalle
+pdfjs-page-rotate-ccw-button-label = Kierrä vasemmalle
+pdfjs-cursor-text-select-tool-button =
+ .title = Käytä tekstinvalintatyökalua
+pdfjs-cursor-text-select-tool-button-label = Tekstinvalintatyökalu
+pdfjs-cursor-hand-tool-button =
+ .title = Käytä käsityökalua
+pdfjs-cursor-hand-tool-button-label = Käsityökalu
+pdfjs-scroll-page-button =
+ .title = Käytä sivun vieritystä
+pdfjs-scroll-page-button-label = Sivun vieritys
+pdfjs-scroll-vertical-button =
+ .title = Käytä pystysuuntaista vieritystä
+pdfjs-scroll-vertical-button-label = Pystysuuntainen vieritys
+pdfjs-scroll-horizontal-button =
+ .title = Käytä vaakasuuntaista vieritystä
+pdfjs-scroll-horizontal-button-label = Vaakasuuntainen vieritys
+pdfjs-scroll-wrapped-button =
+ .title = Käytä rivittyvää vieritystä
+pdfjs-scroll-wrapped-button-label = Rivittyvä vieritys
+pdfjs-spread-none-button =
+ .title = Älä yhdistä sivuja aukeamiksi
+pdfjs-spread-none-button-label = Ei aukeamia
+pdfjs-spread-odd-button =
+ .title = Yhdistä sivut aukeamiksi alkaen parittomalta sivulta
+pdfjs-spread-odd-button-label = Parittomalta alkavat aukeamat
+pdfjs-spread-even-button =
+ .title = Yhdistä sivut aukeamiksi alkaen parilliselta sivulta
+pdfjs-spread-even-button-label = Parilliselta alkavat aukeamat
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumentin ominaisuudet…
+pdfjs-document-properties-button-label = Dokumentin ominaisuudet…
+pdfjs-document-properties-file-name = Tiedoston nimi:
+pdfjs-document-properties-file-size = Tiedoston koko:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } kt ({ $size_b } tavua)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } Mt ({ $size_b } tavua)
+pdfjs-document-properties-title = Otsikko:
+pdfjs-document-properties-author = Tekijä:
+pdfjs-document-properties-subject = Aihe:
+pdfjs-document-properties-keywords = Avainsanat:
+pdfjs-document-properties-creation-date = Luomispäivämäärä:
+pdfjs-document-properties-modification-date = Muokkauspäivämäärä:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Luoja:
+pdfjs-document-properties-producer = PDF-tuottaja:
+pdfjs-document-properties-version = PDF-versio:
+pdfjs-document-properties-page-count = Sivujen määrä:
+pdfjs-document-properties-page-size = Sivun koko:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = pysty
+pdfjs-document-properties-page-size-orientation-landscape = vaaka
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Nopea web-katselu:
+pdfjs-document-properties-linearized-yes = Kyllä
+pdfjs-document-properties-linearized-no = Ei
+pdfjs-document-properties-close-button = Sulje
+
+## Print
+
+pdfjs-print-progress-message = Valmistellaan dokumenttia tulostamista varten…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress } %
+pdfjs-print-progress-close-button = Peruuta
+pdfjs-printing-not-supported = Varoitus: Selain ei tue kaikkia tulostustapoja.
+pdfjs-printing-not-ready = Varoitus: PDF-tiedosto ei ole vielä latautunut kokonaan, eikä sitä voi vielä tulostaa.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Näytä/piilota sivupaneeli
+pdfjs-toggle-sidebar-notification-button =
+ .title = Näytä/piilota sivupaneeli (dokumentissa on sisällys/liitteitä/tasoja)
+pdfjs-toggle-sidebar-button-label = Näytä/piilota sivupaneeli
+pdfjs-document-outline-button =
+ .title = Näytä dokumentin sisällys (laajenna tai kutista kohdat kaksoisnapsauttamalla)
+pdfjs-document-outline-button-label = Dokumentin sisällys
+pdfjs-attachments-button =
+ .title = Näytä liitteet
+pdfjs-attachments-button-label = Liitteet
+pdfjs-layers-button =
+ .title = Näytä tasot (kaksoisnapsauta palauttaaksesi kaikki tasot oletustilaan)
+pdfjs-layers-button-label = Tasot
+pdfjs-thumbs-button =
+ .title = Näytä pienoiskuvat
+pdfjs-thumbs-button-label = Pienoiskuvat
+pdfjs-current-outline-item-button =
+ .title = Etsi nykyinen sisällyksen kohta
+pdfjs-current-outline-item-button-label = Nykyinen sisällyksen kohta
+pdfjs-findbar-button =
+ .title = Etsi dokumentista
+pdfjs-findbar-button-label = Etsi
+pdfjs-additional-layers = Lisätasot
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Sivu { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Pienoiskuva sivusta { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Etsi
+ .placeholder = Etsi dokumentista…
+pdfjs-find-previous-button =
+ .title = Etsi hakusanan edellinen osuma
+pdfjs-find-previous-button-label = Edellinen
+pdfjs-find-next-button =
+ .title = Etsi hakusanan seuraava osuma
+pdfjs-find-next-button-label = Seuraava
+pdfjs-find-highlight-checkbox = Korosta kaikki
+pdfjs-find-match-case-checkbox-label = Huomioi kirjainkoko
+pdfjs-find-match-diacritics-checkbox-label = Erota tarkkeet
+pdfjs-find-entire-word-checkbox-label = Kokonaiset sanat
+pdfjs-find-reached-top = Päästiin dokumentin alkuun, jatketaan lopusta
+pdfjs-find-reached-bottom = Päästiin dokumentin loppuun, jatketaan alusta
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } / { $total } osuma
+ *[other] { $current } / { $total } osumaa
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Yli { $limit } osuma
+ *[other] Yli { $limit } osumaa
+ }
+pdfjs-find-not-found = Hakusanaa ei löytynyt
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Sivun leveys
+pdfjs-page-scale-fit = Koko sivu
+pdfjs-page-scale-auto = Automaattinen suurennus
+pdfjs-page-scale-actual = Todellinen koko
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale } %
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Sivu { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Tapahtui virhe ladattaessa PDF-tiedostoa.
+pdfjs-invalid-file-error = Virheellinen tai vioittunut PDF-tiedosto.
+pdfjs-missing-file-error = Puuttuva PDF-tiedosto.
+pdfjs-unexpected-response-error = Odottamaton vastaus palvelimelta.
+pdfjs-rendering-error = Tapahtui virhe piirrettäessä sivua.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type }-merkintä]
+
+## Password
+
+pdfjs-password-label = Kirjoita PDF-tiedoston salasana.
+pdfjs-password-invalid = Virheellinen salasana. Yritä uudestaan.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Peruuta
+pdfjs-web-fonts-disabled = Verkkosivujen omat kirjasinlajit on estetty: ei voida käyttää upotettuja PDF-kirjasinlajeja.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Teksti
+pdfjs-editor-free-text-button-label = Teksti
+pdfjs-editor-ink-button =
+ .title = Piirros
+pdfjs-editor-ink-button-label = Piirros
+pdfjs-editor-stamp-button =
+ .title = Lisää tai muokkaa kuvia
+pdfjs-editor-stamp-button-label = Lisää tai muokkaa kuvia
+pdfjs-editor-highlight-button =
+ .title = Korostus
+pdfjs-editor-highlight-button-label = Korostus
+pdfjs-highlight-floating-button =
+ .title = Korostus
+pdfjs-highlight-floating-button1 =
+ .title = Korostus
+ .aria-label = Korostus
+pdfjs-highlight-floating-button-label = Korostus
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Poista piirros
+pdfjs-editor-remove-freetext-button =
+ .title = Poista teksti
+pdfjs-editor-remove-stamp-button =
+ .title = Poista kuva
+pdfjs-editor-remove-highlight-button =
+ .title = Poista korostus
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Väri
+pdfjs-editor-free-text-size-input = Koko
+pdfjs-editor-ink-color-input = Väri
+pdfjs-editor-ink-thickness-input = Paksuus
+pdfjs-editor-ink-opacity-input = Peittävyys
+pdfjs-editor-stamp-add-image-button =
+ .title = Lisää kuva
+pdfjs-editor-stamp-add-image-button-label = Lisää kuva
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Paksuus
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Muuta paksuutta korostaessasi muita kohteita kuin tekstiä
+pdfjs-free-text =
+ .aria-label = Tekstimuokkain
+pdfjs-free-text-default-content = Aloita kirjoittaminen…
+pdfjs-ink =
+ .aria-label = Piirrustusmuokkain
+pdfjs-ink-canvas =
+ .aria-label = Käyttäjän luoma kuva
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Vaihtoehtoinen teksti
+pdfjs-editor-alt-text-edit-button-label = Muokkaa vaihtoehtoista tekstiä
+pdfjs-editor-alt-text-dialog-label = Valitse vaihtoehto
+pdfjs-editor-alt-text-dialog-description = Vaihtoehtoinen teksti ("alt-teksti") auttaa ihmisiä, jotka eivät näe kuvaa tai kun kuva ei lataudu.
+pdfjs-editor-alt-text-add-description-label = Lisää kuvaus
+pdfjs-editor-alt-text-add-description-description = Pyri 1-2 lauseeseen, jotka kuvaavat aihetta, ympäristöä tai toimintaa.
+pdfjs-editor-alt-text-mark-decorative-label = Merkitse koristeelliseksi
+pdfjs-editor-alt-text-mark-decorative-description = Tätä käytetään koristekuville, kuten reunuksille tai vesileimoille.
+pdfjs-editor-alt-text-cancel-button = Peruuta
+pdfjs-editor-alt-text-save-button = Tallenna
+pdfjs-editor-alt-text-decorative-tooltip = Merkitty koristeelliseksi
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Esimerkiksi "Nuori mies istuu pöytään syömään aterian"
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Vasen yläkulma - muuta kokoa
+pdfjs-editor-resizer-label-top-middle = Ylhäällä keskellä - muuta kokoa
+pdfjs-editor-resizer-label-top-right = Oikea yläkulma - muuta kokoa
+pdfjs-editor-resizer-label-middle-right = Keskellä oikealla - muuta kokoa
+pdfjs-editor-resizer-label-bottom-right = Oikea alakulma - muuta kokoa
+pdfjs-editor-resizer-label-bottom-middle = Alhaalla keskellä - muuta kokoa
+pdfjs-editor-resizer-label-bottom-left = Vasen alakulma - muuta kokoa
+pdfjs-editor-resizer-label-middle-left = Keskellä vasemmalla - muuta kokoa
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Korostusväri
+pdfjs-editor-colorpicker-button =
+ .title = Vaihda väri
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Värivalinnat
+pdfjs-editor-colorpicker-yellow =
+ .title = Keltainen
+pdfjs-editor-colorpicker-green =
+ .title = Vihreä
+pdfjs-editor-colorpicker-blue =
+ .title = Sininen
+pdfjs-editor-colorpicker-pink =
+ .title = Pinkki
+pdfjs-editor-colorpicker-red =
+ .title = Punainen
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Näytä kaikki
+pdfjs-editor-highlight-show-all-button =
+ .title = Näytä kaikki
diff --git a/web/locale/fr/viewer.ftl b/web/locale/fr/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..54c06c22c4af3c6bf7b6befbcaa926acfb52d9d5
--- /dev/null
+++ b/web/locale/fr/viewer.ftl
@@ -0,0 +1,398 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Page précédente
+pdfjs-previous-button-label = Précédent
+pdfjs-next-button =
+ .title = Page suivante
+pdfjs-next-button-label = Suivant
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Page
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = sur { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } sur { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoom arrière
+pdfjs-zoom-out-button-label = Zoom arrière
+pdfjs-zoom-in-button =
+ .title = Zoom avant
+pdfjs-zoom-in-button-label = Zoom avant
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Basculer en mode présentation
+pdfjs-presentation-mode-button-label = Mode présentation
+pdfjs-open-file-button =
+ .title = Ouvrir le fichier
+pdfjs-open-file-button-label = Ouvrir le fichier
+pdfjs-print-button =
+ .title = Imprimer
+pdfjs-print-button-label = Imprimer
+pdfjs-save-button =
+ .title = Enregistrer
+pdfjs-save-button-label = Enregistrer
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Télécharger
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Télécharger
+pdfjs-bookmark-button =
+ .title = Page courante (montrer l’adresse de la page courante)
+pdfjs-bookmark-button-label = Page courante
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Ouvrir dans une application
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Ouvrir dans une application
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Outils
+pdfjs-tools-button-label = Outils
+pdfjs-first-page-button =
+ .title = Aller à la première page
+pdfjs-first-page-button-label = Aller à la première page
+pdfjs-last-page-button =
+ .title = Aller à la dernière page
+pdfjs-last-page-button-label = Aller à la dernière page
+pdfjs-page-rotate-cw-button =
+ .title = Rotation horaire
+pdfjs-page-rotate-cw-button-label = Rotation horaire
+pdfjs-page-rotate-ccw-button =
+ .title = Rotation antihoraire
+pdfjs-page-rotate-ccw-button-label = Rotation antihoraire
+pdfjs-cursor-text-select-tool-button =
+ .title = Activer l’outil de sélection de texte
+pdfjs-cursor-text-select-tool-button-label = Outil de sélection de texte
+pdfjs-cursor-hand-tool-button =
+ .title = Activer l’outil main
+pdfjs-cursor-hand-tool-button-label = Outil main
+pdfjs-scroll-page-button =
+ .title = Utiliser le défilement par page
+pdfjs-scroll-page-button-label = Défilement par page
+pdfjs-scroll-vertical-button =
+ .title = Utiliser le défilement vertical
+pdfjs-scroll-vertical-button-label = Défilement vertical
+pdfjs-scroll-horizontal-button =
+ .title = Utiliser le défilement horizontal
+pdfjs-scroll-horizontal-button-label = Défilement horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Utiliser le défilement par bloc
+pdfjs-scroll-wrapped-button-label = Défilement par bloc
+pdfjs-spread-none-button =
+ .title = Ne pas afficher les pages deux à deux
+pdfjs-spread-none-button-label = Pas de double affichage
+pdfjs-spread-odd-button =
+ .title = Afficher les pages par deux, impaires à gauche
+pdfjs-spread-odd-button-label = Doubles pages, impaires à gauche
+pdfjs-spread-even-button =
+ .title = Afficher les pages par deux, paires à gauche
+pdfjs-spread-even-button-label = Doubles pages, paires à gauche
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propriétés du document…
+pdfjs-document-properties-button-label = Propriétés du document…
+pdfjs-document-properties-file-name = Nom du fichier :
+pdfjs-document-properties-file-size = Taille du fichier :
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } Ko ({ $size_b } octets)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } Mo ({ $size_b } octets)
+pdfjs-document-properties-title = Titre :
+pdfjs-document-properties-author = Auteur :
+pdfjs-document-properties-subject = Sujet :
+pdfjs-document-properties-keywords = Mots-clés :
+pdfjs-document-properties-creation-date = Date de création :
+pdfjs-document-properties-modification-date = Modifié le :
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date } à { $time }
+pdfjs-document-properties-creator = Créé par :
+pdfjs-document-properties-producer = Outil de conversion PDF :
+pdfjs-document-properties-version = Version PDF :
+pdfjs-document-properties-page-count = Nombre de pages :
+pdfjs-document-properties-page-size = Taille de la page :
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portrait
+pdfjs-document-properties-page-size-orientation-landscape = paysage
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = lettre
+pdfjs-document-properties-page-size-name-legal = document juridique
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Affichage rapide des pages web :
+pdfjs-document-properties-linearized-yes = Oui
+pdfjs-document-properties-linearized-no = Non
+pdfjs-document-properties-close-button = Fermer
+
+## Print
+
+pdfjs-print-progress-message = Préparation du document pour l’impression…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress } %
+pdfjs-print-progress-close-button = Annuler
+pdfjs-printing-not-supported = Attention : l’impression n’est pas totalement prise en charge par ce navigateur.
+pdfjs-printing-not-ready = Attention : le PDF n’est pas entièrement chargé pour pouvoir l’imprimer.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Afficher/Masquer le panneau latéral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Afficher/Masquer le panneau latéral (le document contient des signets/pièces jointes/calques)
+pdfjs-toggle-sidebar-button-label = Afficher/Masquer le panneau latéral
+pdfjs-document-outline-button =
+ .title = Afficher les signets du document (double-cliquer pour développer/réduire tous les éléments)
+pdfjs-document-outline-button-label = Signets du document
+pdfjs-attachments-button =
+ .title = Afficher les pièces jointes
+pdfjs-attachments-button-label = Pièces jointes
+pdfjs-layers-button =
+ .title = Afficher les calques (double-cliquer pour réinitialiser tous les calques à l’état par défaut)
+pdfjs-layers-button-label = Calques
+pdfjs-thumbs-button =
+ .title = Afficher les vignettes
+pdfjs-thumbs-button-label = Vignettes
+pdfjs-current-outline-item-button =
+ .title = Trouver l’élément de plan actuel
+pdfjs-current-outline-item-button-label = Élément de plan actuel
+pdfjs-findbar-button =
+ .title = Rechercher dans le document
+pdfjs-findbar-button-label = Rechercher
+pdfjs-additional-layers = Calques additionnels
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Page { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Vignette de la page { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Rechercher
+ .placeholder = Rechercher dans le document…
+pdfjs-find-previous-button =
+ .title = Trouver l’occurrence précédente de l’expression
+pdfjs-find-previous-button-label = Précédent
+pdfjs-find-next-button =
+ .title = Trouver la prochaine occurrence de l’expression
+pdfjs-find-next-button-label = Suivant
+pdfjs-find-highlight-checkbox = Tout surligner
+pdfjs-find-match-case-checkbox-label = Respecter la casse
+pdfjs-find-match-diacritics-checkbox-label = Respecter les accents et diacritiques
+pdfjs-find-entire-word-checkbox-label = Mots entiers
+pdfjs-find-reached-top = Haut de la page atteint, poursuite depuis la fin
+pdfjs-find-reached-bottom = Bas de la page atteint, poursuite au début
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count = Occurrence { $current } sur { $total }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Plus d’{ $limit } occurrence
+ *[other] Plus de { $limit } occurrences
+ }
+pdfjs-find-not-found = Expression non trouvée
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Pleine largeur
+pdfjs-page-scale-fit = Page entière
+pdfjs-page-scale-auto = Zoom automatique
+pdfjs-page-scale-actual = Taille réelle
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale } %
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Page { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Une erreur s’est produite lors du chargement du fichier PDF.
+pdfjs-invalid-file-error = Fichier PDF invalide ou corrompu.
+pdfjs-missing-file-error = Fichier PDF manquant.
+pdfjs-unexpected-response-error = Réponse inattendue du serveur.
+pdfjs-rendering-error = Une erreur s’est produite lors de l’affichage de la page.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date } à { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Annotation { $type }]
+
+## Password
+
+pdfjs-password-label = Veuillez saisir le mot de passe pour ouvrir ce fichier PDF.
+pdfjs-password-invalid = Mot de passe incorrect. Veuillez réessayer.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Annuler
+pdfjs-web-fonts-disabled = Les polices web sont désactivées : impossible d’utiliser les polices intégrées au PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texte
+pdfjs-editor-free-text-button-label = Texte
+pdfjs-editor-ink-button =
+ .title = Dessiner
+pdfjs-editor-ink-button-label = Dessiner
+pdfjs-editor-stamp-button =
+ .title = Ajouter ou modifier des images
+pdfjs-editor-stamp-button-label = Ajouter ou modifier des images
+pdfjs-editor-highlight-button =
+ .title = Surligner
+pdfjs-editor-highlight-button-label = Surligner
+pdfjs-highlight-floating-button =
+ .title = Surligner
+pdfjs-highlight-floating-button1 =
+ .title = Surligner
+ .aria-label = Surligner
+pdfjs-highlight-floating-button-label = Surligner
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Supprimer le dessin
+pdfjs-editor-remove-freetext-button =
+ .title = Supprimer le texte
+pdfjs-editor-remove-stamp-button =
+ .title = Supprimer l’image
+pdfjs-editor-remove-highlight-button =
+ .title = Supprimer le surlignage
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Couleur
+pdfjs-editor-free-text-size-input = Taille
+pdfjs-editor-ink-color-input = Couleur
+pdfjs-editor-ink-thickness-input = Épaisseur
+pdfjs-editor-ink-opacity-input = Opacité
+pdfjs-editor-stamp-add-image-button =
+ .title = Ajouter une image
+pdfjs-editor-stamp-add-image-button-label = Ajouter une image
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Épaisseur
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Modifier l’épaisseur pour le surlignage d’éléments non textuels
+pdfjs-free-text =
+ .aria-label = Éditeur de texte
+pdfjs-free-text-default-content = Commencer à écrire…
+pdfjs-ink =
+ .aria-label = Éditeur de dessin
+pdfjs-ink-canvas =
+ .aria-label = Image créée par l’utilisateur·trice
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Texte alternatif
+pdfjs-editor-alt-text-edit-button-label = Modifier le texte alternatif
+pdfjs-editor-alt-text-dialog-label = Sélectionnez une option
+pdfjs-editor-alt-text-dialog-description = Le texte alternatif est utile lorsque des personnes ne peuvent pas voir l’image ou que l’image ne se charge pas.
+pdfjs-editor-alt-text-add-description-label = Ajouter une description
+pdfjs-editor-alt-text-add-description-description = Il est conseillé de rédiger une ou deux phrases décrivant le sujet, le cadre ou les actions.
+pdfjs-editor-alt-text-mark-decorative-label = Marquer comme décorative
+pdfjs-editor-alt-text-mark-decorative-description = Cette option est utilisée pour les images décoratives, comme les bordures ou les filigranes.
+pdfjs-editor-alt-text-cancel-button = Annuler
+pdfjs-editor-alt-text-save-button = Enregistrer
+pdfjs-editor-alt-text-decorative-tooltip = Marquée comme décorative
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Par exemple, « Un jeune homme est assis à une table pour prendre un repas »
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Coin supérieur gauche — redimensionner
+pdfjs-editor-resizer-label-top-middle = Milieu haut — redimensionner
+pdfjs-editor-resizer-label-top-right = Coin supérieur droit — redimensionner
+pdfjs-editor-resizer-label-middle-right = Milieu droit — redimensionner
+pdfjs-editor-resizer-label-bottom-right = Coin inférieur droit — redimensionner
+pdfjs-editor-resizer-label-bottom-middle = Centre bas — redimensionner
+pdfjs-editor-resizer-label-bottom-left = Coin inférieur gauche — redimensionner
+pdfjs-editor-resizer-label-middle-left = Milieu gauche — redimensionner
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Couleur de surlignage
+pdfjs-editor-colorpicker-button =
+ .title = Changer de couleur
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Choix de couleurs
+pdfjs-editor-colorpicker-yellow =
+ .title = Jaune
+pdfjs-editor-colorpicker-green =
+ .title = Vert
+pdfjs-editor-colorpicker-blue =
+ .title = Bleu
+pdfjs-editor-colorpicker-pink =
+ .title = Rose
+pdfjs-editor-colorpicker-red =
+ .title = Rouge
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Tout afficher
+pdfjs-editor-highlight-show-all-button =
+ .title = Tout afficher
diff --git a/web/locale/fur/viewer.ftl b/web/locale/fur/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..8e8c8a0179bdcb72385b68a82490c908b5de78d5
--- /dev/null
+++ b/web/locale/fur/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pagjine precedente
+pdfjs-previous-button-label = Indaûr
+pdfjs-next-button =
+ .title = Prossime pagjine
+pdfjs-next-button-label = Indevant
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pagjine
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = di { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } di { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Impiçulìs
+pdfjs-zoom-out-button-label = Impiçulìs
+pdfjs-zoom-in-button =
+ .title = Ingrandìs
+pdfjs-zoom-in-button-label = Ingrandìs
+pdfjs-zoom-select =
+ .title = Ingrandiment
+pdfjs-presentation-mode-button =
+ .title = Passe ae modalitât presentazion
+pdfjs-presentation-mode-button-label = Modalitât presentazion
+pdfjs-open-file-button =
+ .title = Vierç un file
+pdfjs-open-file-button-label = Vierç
+pdfjs-print-button =
+ .title = Stampe
+pdfjs-print-button-label = Stampe
+pdfjs-save-button =
+ .title = Salve
+pdfjs-save-button-label = Salve
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Discjame
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Discjame
+pdfjs-bookmark-button =
+ .title = Pagjine corinte (mostre URL de pagjine atuâl)
+pdfjs-bookmark-button-label = Pagjine corinte
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Vierç te aplicazion
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Vierç te aplicazion
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Struments
+pdfjs-tools-button-label = Struments
+pdfjs-first-page-button =
+ .title = Va ae prime pagjine
+pdfjs-first-page-button-label = Va ae prime pagjine
+pdfjs-last-page-button =
+ .title = Va ae ultime pagjine
+pdfjs-last-page-button-label = Va ae ultime pagjine
+pdfjs-page-rotate-cw-button =
+ .title = Zire in sens orari
+pdfjs-page-rotate-cw-button-label = Zire in sens orari
+pdfjs-page-rotate-ccw-button =
+ .title = Zire in sens antiorari
+pdfjs-page-rotate-ccw-button-label = Zire in sens antiorari
+pdfjs-cursor-text-select-tool-button =
+ .title = Ative il strument di selezion dal test
+pdfjs-cursor-text-select-tool-button-label = Strument di selezion dal test
+pdfjs-cursor-hand-tool-button =
+ .title = Ative il strument manute
+pdfjs-cursor-hand-tool-button-label = Strument manute
+pdfjs-scroll-page-button =
+ .title = Dopre il scoriment des pagjinis
+pdfjs-scroll-page-button-label = Scoriment pagjinis
+pdfjs-scroll-vertical-button =
+ .title = Dopre scoriment verticâl
+pdfjs-scroll-vertical-button-label = Scoriment verticâl
+pdfjs-scroll-horizontal-button =
+ .title = Dopre scoriment orizontâl
+pdfjs-scroll-horizontal-button-label = Scoriment orizontâl
+pdfjs-scroll-wrapped-button =
+ .title = Dopre scoriment par blocs
+pdfjs-scroll-wrapped-button-label = Scoriment par blocs
+pdfjs-spread-none-button =
+ .title = No sta meti dongje pagjinis in cubie
+pdfjs-spread-none-button-label = No cubiis di pagjinis
+pdfjs-spread-odd-button =
+ .title = Met dongje cubiis di pagjinis scomençant des pagjinis dispar
+pdfjs-spread-odd-button-label = Cubiis di pagjinis, dispar a çampe
+pdfjs-spread-even-button =
+ .title = Met dongje cubiis di pagjinis scomençant des pagjinis pâr
+pdfjs-spread-even-button-label = Cubiis di pagjinis, pâr a çampe
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Proprietâts dal document…
+pdfjs-document-properties-button-label = Proprietâts dal document…
+pdfjs-document-properties-file-name = Non dal file:
+pdfjs-document-properties-file-size = Dimension dal file:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Titul:
+pdfjs-document-properties-author = Autôr:
+pdfjs-document-properties-subject = Ogjet:
+pdfjs-document-properties-keywords = Peraulis clâf:
+pdfjs-document-properties-creation-date = Date di creazion:
+pdfjs-document-properties-modification-date = Date di modifiche:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creatôr
+pdfjs-document-properties-producer = Gjeneradôr PDF:
+pdfjs-document-properties-version = Version PDF:
+pdfjs-document-properties-page-count = Numar di pagjinis:
+pdfjs-document-properties-page-size = Dimension de pagjine:
+pdfjs-document-properties-page-size-unit-inches = oncis
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = verticâl
+pdfjs-document-properties-page-size-orientation-landscape = orizontâl
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letare
+pdfjs-document-properties-page-size-name-legal = Legâl
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Visualizazion web svelte:
+pdfjs-document-properties-linearized-yes = Sì
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Siere
+
+## Print
+
+pdfjs-print-progress-message = Daûr a prontâ il document pe stampe…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Anule
+pdfjs-printing-not-supported = Atenzion: la stampe no je supuartade ad implen di chest navigadôr.
+pdfjs-printing-not-ready = Atenzion: il PDF nol è stât cjamât dal dut pe stampe.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Ative/Disative sbare laterâl
+pdfjs-toggle-sidebar-notification-button =
+ .title = Ative/Disative sbare laterâl (il document al conten struture/zontis/strâts)
+pdfjs-toggle-sidebar-button-label = Ative/Disative sbare laterâl
+pdfjs-document-outline-button =
+ .title = Mostre la struture dal document (dopli clic par slargjâ/strenzi ducj i elements)
+pdfjs-document-outline-button-label = Struture dal document
+pdfjs-attachments-button =
+ .title = Mostre lis zontis
+pdfjs-attachments-button-label = Zontis
+pdfjs-layers-button =
+ .title = Mostre i strâts (dopli clic par ristabilî ducj i strâts al stât predefinît)
+pdfjs-layers-button-label = Strâts
+pdfjs-thumbs-button =
+ .title = Mostre miniaturis
+pdfjs-thumbs-button-label = Miniaturis
+pdfjs-current-outline-item-button =
+ .title = Cjate l'element de struture atuâl
+pdfjs-current-outline-item-button-label = Element de struture atuâl
+pdfjs-findbar-button =
+ .title = Cjate tal document
+pdfjs-findbar-button-label = Cjate
+pdfjs-additional-layers = Strâts adizionâi
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pagjine { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniature de pagjine { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Cjate
+ .placeholder = Cjate tal document…
+pdfjs-find-previous-button =
+ .title = Cjate il câs precedent dal test
+pdfjs-find-previous-button-label = Precedent
+pdfjs-find-next-button =
+ .title = Cjate il câs sucessîf dal test
+pdfjs-find-next-button-label = Sucessîf
+pdfjs-find-highlight-checkbox = Evidenzie dut
+pdfjs-find-match-case-checkbox-label = Fâs distinzion tra maiusculis e minusculis
+pdfjs-find-match-diacritics-checkbox-label = Corispondence diacritiche
+pdfjs-find-entire-word-checkbox-label = Peraulis interiis
+pdfjs-find-reached-top = Si è rivâts al inizi dal document e si à continuât de fin
+pdfjs-find-reached-bottom = Si è rivât ae fin dal document e si à continuât dal inizi
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } di { $total } corispondence
+ *[other] { $current } di { $total } corispondencis
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Plui di { $limit } corispondence
+ *[other] Plui di { $limit } corispondencis
+ }
+pdfjs-find-not-found = Test no cjatât
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Largjece de pagjine
+pdfjs-page-scale-fit = Pagjine interie
+pdfjs-page-scale-auto = Ingrandiment automatic
+pdfjs-page-scale-actual = Dimension reâl
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Pagjine { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Al è vignût fûr un erôr intant che si cjariave il PDF.
+pdfjs-invalid-file-error = File PDF no valit o ruvinât.
+pdfjs-missing-file-error = Al mancje il file PDF.
+pdfjs-unexpected-response-error = Rispueste dal servidôr inspietade.
+pdfjs-rendering-error = Al è vignût fûr un erôr tal realizâ la visualizazion de pagjine.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotazion { $type }]
+
+## Password
+
+pdfjs-password-label = Inserìs la password par vierzi chest file PDF.
+pdfjs-password-invalid = Password no valide. Par plasê torne prove.
+pdfjs-password-ok-button = Va ben
+pdfjs-password-cancel-button = Anule
+pdfjs-web-fonts-disabled = I caratars dal Web a son disativâts: Impussibil doprâ i caratars PDF incorporâts.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Test
+pdfjs-editor-free-text-button-label = Test
+pdfjs-editor-ink-button =
+ .title = Dissen
+pdfjs-editor-ink-button-label = Dissen
+pdfjs-editor-stamp-button =
+ .title = Zonte o modifiche imagjins
+pdfjs-editor-stamp-button-label = Zonte o modifiche imagjins
+pdfjs-editor-highlight-button =
+ .title = Evidenzie
+pdfjs-editor-highlight-button-label = Evidenzie
+pdfjs-highlight-floating-button =
+ .title = Evidenzie
+pdfjs-highlight-floating-button1 =
+ .title = Evidenzie
+ .aria-label = Evidenzie
+pdfjs-highlight-floating-button-label = Evidenzie
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Gjave dissen
+pdfjs-editor-remove-freetext-button =
+ .title = Gjave test
+pdfjs-editor-remove-stamp-button =
+ .title = Gjave imagjin
+pdfjs-editor-remove-highlight-button =
+ .title = Gjave evidenziazion
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Colôr
+pdfjs-editor-free-text-size-input = Dimension
+pdfjs-editor-ink-color-input = Colôr
+pdfjs-editor-ink-thickness-input = Spessôr
+pdfjs-editor-ink-opacity-input = Opacitât
+pdfjs-editor-stamp-add-image-button =
+ .title = Zonte imagjin
+pdfjs-editor-stamp-add-image-button-label = Zonte imagjin
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Spessôr
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Modifiche il spessôr de selezion pai elements che no son testuâi
+pdfjs-free-text =
+ .aria-label = Editôr di test
+pdfjs-free-text-default-content = Scomence a scrivi…
+pdfjs-ink =
+ .aria-label = Editôr dissens
+pdfjs-ink-canvas =
+ .aria-label = Imagjin creade dal utent
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Test alternatîf
+pdfjs-editor-alt-text-edit-button-label = Modifiche test alternatîf
+pdfjs-editor-alt-text-dialog-label = Sielç une opzion
+pdfjs-editor-alt-text-dialog-description = Il test alternatîf (“alt text”) al jude cuant che lis personis no puedin viodi la imagjin o cuant che la imagjine no ven cjariade.
+pdfjs-editor-alt-text-add-description-label = Zonte une descrizion
+pdfjs-editor-alt-text-add-description-description = Ponte a une o dôs frasis che a descrivin l’argoment, la ambientazion o lis azions.
+pdfjs-editor-alt-text-mark-decorative-label = Segne come decorative
+pdfjs-editor-alt-text-mark-decorative-description = Chest al ven doprât pes imagjins ornamentâls, come i ôrs o lis filigranis.
+pdfjs-editor-alt-text-cancel-button = Anule
+pdfjs-editor-alt-text-save-button = Salve
+pdfjs-editor-alt-text-decorative-tooltip = Segnade come decorative
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Par esempli, “Un zovin si sente a taule par mangjâ”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Cjanton in alt a çampe — ridimensione
+pdfjs-editor-resizer-label-top-middle = Bande superiôr tal mieç — ridimensione
+pdfjs-editor-resizer-label-top-right = Cjanton in alt a diestre — ridimensione
+pdfjs-editor-resizer-label-middle-right = Bande diestre tal mieç — ridimensione
+pdfjs-editor-resizer-label-bottom-right = Cjanton in bas a diestre — ridimensione
+pdfjs-editor-resizer-label-bottom-middle = Bande inferiôr tal mieç — ridimensione
+pdfjs-editor-resizer-label-bottom-left = Cjanton in bas a çampe — ridimensione
+pdfjs-editor-resizer-label-middle-left = Bande di çampe tal mieç — ridimensione
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Colôr par evidenziâ
+pdfjs-editor-colorpicker-button =
+ .title = Cambie colôr
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Sieltis di colôr
+pdfjs-editor-colorpicker-yellow =
+ .title = Zâl
+pdfjs-editor-colorpicker-green =
+ .title = Vert
+pdfjs-editor-colorpicker-blue =
+ .title = Blu
+pdfjs-editor-colorpicker-pink =
+ .title = Rose
+pdfjs-editor-colorpicker-red =
+ .title = Ros
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Mostre dut
+pdfjs-editor-highlight-show-all-button =
+ .title = Mostre dut
diff --git a/web/locale/fy-NL/viewer.ftl b/web/locale/fy-NL/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..a67f9b9d3159922bf8c3fc950001a77519192786
--- /dev/null
+++ b/web/locale/fy-NL/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Foarige side
+pdfjs-previous-button-label = Foarige
+pdfjs-next-button =
+ .title = Folgjende side
+pdfjs-next-button-label = Folgjende
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Side
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = fan { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } fan { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Utzoome
+pdfjs-zoom-out-button-label = Utzoome
+pdfjs-zoom-in-button =
+ .title = Ynzoome
+pdfjs-zoom-in-button-label = Ynzoome
+pdfjs-zoom-select =
+ .title = Zoome
+pdfjs-presentation-mode-button =
+ .title = Wikselje nei presintaasjemodus
+pdfjs-presentation-mode-button-label = Presintaasjemodus
+pdfjs-open-file-button =
+ .title = Bestân iepenje
+pdfjs-open-file-button-label = Iepenje
+pdfjs-print-button =
+ .title = Ofdrukke
+pdfjs-print-button-label = Ofdrukke
+pdfjs-save-button =
+ .title = Bewarje
+pdfjs-save-button-label = Bewarje
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Downloade
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Downloade
+pdfjs-bookmark-button =
+ .title = Aktuele side (URL fan aktuele side besjen)
+pdfjs-bookmark-button-label = Aktuele side
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Iepenje yn app
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Iepenje yn app
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Ark
+pdfjs-tools-button-label = Ark
+pdfjs-first-page-button =
+ .title = Gean nei earste side
+pdfjs-first-page-button-label = Gean nei earste side
+pdfjs-last-page-button =
+ .title = Gean nei lêste side
+pdfjs-last-page-button-label = Gean nei lêste side
+pdfjs-page-rotate-cw-button =
+ .title = Rjochtsom draaie
+pdfjs-page-rotate-cw-button-label = Rjochtsom draaie
+pdfjs-page-rotate-ccw-button =
+ .title = Linksom draaie
+pdfjs-page-rotate-ccw-button-label = Linksom draaie
+pdfjs-cursor-text-select-tool-button =
+ .title = Tekstseleksjehelpmiddel ynskeakelje
+pdfjs-cursor-text-select-tool-button-label = Tekstseleksjehelpmiddel
+pdfjs-cursor-hand-tool-button =
+ .title = Hânhelpmiddel ynskeakelje
+pdfjs-cursor-hand-tool-button-label = Hânhelpmiddel
+pdfjs-scroll-page-button =
+ .title = Sideskowen brûke
+pdfjs-scroll-page-button-label = Sideskowen
+pdfjs-scroll-vertical-button =
+ .title = Fertikaal skowe brûke
+pdfjs-scroll-vertical-button-label = Fertikaal skowe
+pdfjs-scroll-horizontal-button =
+ .title = Horizontaal skowe brûke
+pdfjs-scroll-horizontal-button-label = Horizontaal skowe
+pdfjs-scroll-wrapped-button =
+ .title = Skowe mei oersjoch brûke
+pdfjs-scroll-wrapped-button-label = Skowe mei oersjoch
+pdfjs-spread-none-button =
+ .title = Sidesprieding net gearfetsje
+pdfjs-spread-none-button-label = Gjin sprieding
+pdfjs-spread-odd-button =
+ .title = Sidesprieding gearfetsje te starten mei ûneven nûmers
+pdfjs-spread-odd-button-label = Uneven sprieding
+pdfjs-spread-even-button =
+ .title = Sidesprieding gearfetsje te starten mei even nûmers
+pdfjs-spread-even-button-label = Even sprieding
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokuminteigenskippen…
+pdfjs-document-properties-button-label = Dokuminteigenskippen…
+pdfjs-document-properties-file-name = Bestânsnamme:
+pdfjs-document-properties-file-size = Bestânsgrutte:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Titel:
+pdfjs-document-properties-author = Auteur:
+pdfjs-document-properties-subject = Underwerp:
+pdfjs-document-properties-keywords = Kaaiwurden:
+pdfjs-document-properties-creation-date = Oanmaakdatum:
+pdfjs-document-properties-modification-date = Bewurkingsdatum:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Makker:
+pdfjs-document-properties-producer = PDF-makker:
+pdfjs-document-properties-version = PDF-ferzje:
+pdfjs-document-properties-page-count = Siden:
+pdfjs-document-properties-page-size = Sideformaat:
+pdfjs-document-properties-page-size-unit-inches = yn
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = steand
+pdfjs-document-properties-page-size-orientation-landscape = lizzend
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Juridysk
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Flugge webwerjefte:
+pdfjs-document-properties-linearized-yes = Ja
+pdfjs-document-properties-linearized-no = Nee
+pdfjs-document-properties-close-button = Slute
+
+## Print
+
+pdfjs-print-progress-message = Dokumint tariede oar ôfdrukken…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Annulearje
+pdfjs-printing-not-supported = Warning: Printen is net folslein stipe troch dizze browser.
+pdfjs-printing-not-ready = Warning: PDF is net folslein laden om ôf te drukken.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Sidebalke yn-/útskeakelje
+pdfjs-toggle-sidebar-notification-button =
+ .title = Sidebalke yn-/útskeakelje (dokumint befettet oersjoch/bylagen/lagen)
+pdfjs-toggle-sidebar-button-label = Sidebalke yn-/útskeakelje
+pdfjs-document-outline-button =
+ .title = Dokumintoersjoch toane (dûbelklik om alle items út/yn te klappen)
+pdfjs-document-outline-button-label = Dokumintoersjoch
+pdfjs-attachments-button =
+ .title = Bylagen toane
+pdfjs-attachments-button-label = Bylagen
+pdfjs-layers-button =
+ .title = Lagen toane (dûbelklik om alle lagen nei de standertsteat werom te setten)
+pdfjs-layers-button-label = Lagen
+pdfjs-thumbs-button =
+ .title = Foarbylden toane
+pdfjs-thumbs-button-label = Foarbylden
+pdfjs-current-outline-item-button =
+ .title = Aktueel item yn ynhâldsopjefte sykje
+pdfjs-current-outline-item-button-label = Aktueel item yn ynhâldsopjefte
+pdfjs-findbar-button =
+ .title = Sykje yn dokumint
+pdfjs-findbar-button-label = Sykje
+pdfjs-additional-layers = Oanfoljende lagen
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Side { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Foarbyld fan side { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Sykje
+ .placeholder = Sykje yn dokumint…
+pdfjs-find-previous-button =
+ .title = It foarige foarkommen fan de tekst sykje
+pdfjs-find-previous-button-label = Foarige
+pdfjs-find-next-button =
+ .title = It folgjende foarkommen fan de tekst sykje
+pdfjs-find-next-button-label = Folgjende
+pdfjs-find-highlight-checkbox = Alles markearje
+pdfjs-find-match-case-checkbox-label = Haadlettergefoelich
+pdfjs-find-match-diacritics-checkbox-label = Diakrityske tekens brûke
+pdfjs-find-entire-word-checkbox-label = Hiele wurden
+pdfjs-find-reached-top = Boppekant fan dokumint berikt, trochgien fan ûnder ôf
+pdfjs-find-reached-bottom = Ein fan dokumint berikt, trochgien fan boppe ôf
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } fan { $total } oerienkomst
+ *[other] { $current } fan { $total } oerienkomsten
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Mear as { $limit } oerienkomst
+ *[other] Mear as { $limit } oerienkomsten
+ }
+pdfjs-find-not-found = Tekst net fûn
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Sidebreedte
+pdfjs-page-scale-fit = Hiele side
+pdfjs-page-scale-auto = Automatysk zoome
+pdfjs-page-scale-actual = Werklike grutte
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Side { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Der is in flater bard by it laden fan de PDF.
+pdfjs-invalid-file-error = Ynfalide of korruptearre PDF-bestân.
+pdfjs-missing-file-error = PDF-bestân ûntbrekt.
+pdfjs-unexpected-response-error = Unferwacht serverantwurd.
+pdfjs-rendering-error = Der is in flater bard by it renderjen fan de side.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type }-annotaasje]
+
+## Password
+
+pdfjs-password-label = Jou it wachtwurd om dit PDF-bestân te iepenjen.
+pdfjs-password-invalid = Ferkeard wachtwurd. Probearje opnij.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Annulearje
+pdfjs-web-fonts-disabled = Weblettertypen binne útskeakele: gebrûk fan ynsluten PDF-lettertypen is net mooglik.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+pdfjs-editor-ink-button =
+ .title = Tekenje
+pdfjs-editor-ink-button-label = Tekenje
+pdfjs-editor-stamp-button =
+ .title = Ofbyldingen tafoegje of bewurkje
+pdfjs-editor-stamp-button-label = Ofbyldingen tafoegje of bewurkje
+pdfjs-editor-highlight-button =
+ .title = Markearje
+pdfjs-editor-highlight-button-label = Markearje
+pdfjs-highlight-floating-button =
+ .title = Markearje
+pdfjs-highlight-floating-button1 =
+ .title = Markearje
+ .aria-label = Markearje
+pdfjs-highlight-floating-button-label = Markearje
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Tekening fuortsmite
+pdfjs-editor-remove-freetext-button =
+ .title = Tekst fuortsmite
+pdfjs-editor-remove-stamp-button =
+ .title = Ofbylding fuortsmite
+pdfjs-editor-remove-highlight-button =
+ .title = Markearring fuortsmite
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Kleur
+pdfjs-editor-free-text-size-input = Grutte
+pdfjs-editor-ink-color-input = Kleur
+pdfjs-editor-ink-thickness-input = Tsjokte
+pdfjs-editor-ink-opacity-input = Transparânsje
+pdfjs-editor-stamp-add-image-button =
+ .title = Ofbylding tafoegje
+pdfjs-editor-stamp-add-image-button-label = Ofbylding tafoegje
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Tsjokte
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Tsjokte wizigje by aksintuearring fan oare items as tekst
+pdfjs-free-text =
+ .aria-label = Tekstbewurker
+pdfjs-free-text-default-content = Begjin mei typen…
+pdfjs-ink =
+ .aria-label = Tekeningbewurker
+pdfjs-ink-canvas =
+ .aria-label = Troch brûker makke ôfbylding
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternative tekst
+pdfjs-editor-alt-text-edit-button-label = Alternative tekst bewurkje
+pdfjs-editor-alt-text-dialog-label = Kies in opsje
+pdfjs-editor-alt-text-dialog-description = Alternative tekst helpt wannear’t minsken de ôfbylding net sjen kinne of wannear’t dizze net laden wurdt.
+pdfjs-editor-alt-text-add-description-label = Foegje in beskriuwing ta
+pdfjs-editor-alt-text-add-description-description = Stribje nei 1-2 sinnen dy’t it ûnderwerp, de omjouwing of de aksjes beskriuwe.
+pdfjs-editor-alt-text-mark-decorative-label = As dekoratyf markearje
+pdfjs-editor-alt-text-mark-decorative-description = Dit wurdt brûkt foar sierlike ôfbyldingen, lykas rânen of wettermerken.
+pdfjs-editor-alt-text-cancel-button = Annulearje
+pdfjs-editor-alt-text-save-button = Bewarje
+pdfjs-editor-alt-text-decorative-tooltip = As dekoratyf markearre
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Bygelyks, ‘In jonge man sit oan in tafel om te iten’
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Linkerboppehoek – formaat wizigje
+pdfjs-editor-resizer-label-top-middle = Midden boppe – formaat wizigje
+pdfjs-editor-resizer-label-top-right = Rjochterboppehoek – formaat wizigje
+pdfjs-editor-resizer-label-middle-right = Midden rjochts – formaat wizigje
+pdfjs-editor-resizer-label-bottom-right = Rjochterûnderhoek – formaat wizigje
+pdfjs-editor-resizer-label-bottom-middle = Midden ûnder – formaat wizigje
+pdfjs-editor-resizer-label-bottom-left = Linkerûnderhoek – formaat wizigje
+pdfjs-editor-resizer-label-middle-left = Links midden – formaat wizigje
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Markearringskleur
+pdfjs-editor-colorpicker-button =
+ .title = Kleur wizigje
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Kleurkarren
+pdfjs-editor-colorpicker-yellow =
+ .title = Giel
+pdfjs-editor-colorpicker-green =
+ .title = Grien
+pdfjs-editor-colorpicker-blue =
+ .title = Blau
+pdfjs-editor-colorpicker-pink =
+ .title = Roze
+pdfjs-editor-colorpicker-red =
+ .title = Read
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Alles toane
+pdfjs-editor-highlight-show-all-button =
+ .title = Alles toane
diff --git a/web/locale/ga-IE/viewer.ftl b/web/locale/ga-IE/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..cb5930890c0783e509ef4b161d63388078d47211
--- /dev/null
+++ b/web/locale/ga-IE/viewer.ftl
@@ -0,0 +1,213 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = An Leathanach Roimhe Seo
+pdfjs-previous-button-label = Roimhe Seo
+pdfjs-next-button =
+ .title = An Chéad Leathanach Eile
+pdfjs-next-button-label = Ar Aghaidh
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Leathanach
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = as { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } as { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Súmáil Amach
+pdfjs-zoom-out-button-label = Súmáil Amach
+pdfjs-zoom-in-button =
+ .title = Súmáil Isteach
+pdfjs-zoom-in-button-label = Súmáil Isteach
+pdfjs-zoom-select =
+ .title = Súmáil
+pdfjs-presentation-mode-button =
+ .title = Úsáid an Mód Láithreoireachta
+pdfjs-presentation-mode-button-label = Mód Láithreoireachta
+pdfjs-open-file-button =
+ .title = Oscail Comhad
+pdfjs-open-file-button-label = Oscail
+pdfjs-print-button =
+ .title = Priontáil
+pdfjs-print-button-label = Priontáil
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Uirlisí
+pdfjs-tools-button-label = Uirlisí
+pdfjs-first-page-button =
+ .title = Go dtí an chéad leathanach
+pdfjs-first-page-button-label = Go dtí an chéad leathanach
+pdfjs-last-page-button =
+ .title = Go dtí an leathanach deiridh
+pdfjs-last-page-button-label = Go dtí an leathanach deiridh
+pdfjs-page-rotate-cw-button =
+ .title = Rothlaigh ar deiseal
+pdfjs-page-rotate-cw-button-label = Rothlaigh ar deiseal
+pdfjs-page-rotate-ccw-button =
+ .title = Rothlaigh ar tuathal
+pdfjs-page-rotate-ccw-button-label = Rothlaigh ar tuathal
+pdfjs-cursor-text-select-tool-button =
+ .title = Cumasaigh an Uirlis Roghnaithe Téacs
+pdfjs-cursor-text-select-tool-button-label = Uirlis Roghnaithe Téacs
+pdfjs-cursor-hand-tool-button =
+ .title = Cumasaigh an Uirlis Láimhe
+pdfjs-cursor-hand-tool-button-label = Uirlis Láimhe
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Airíonna na Cáipéise…
+pdfjs-document-properties-button-label = Airíonna na Cáipéise…
+pdfjs-document-properties-file-name = Ainm an chomhaid:
+pdfjs-document-properties-file-size = Méid an chomhaid:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } beart)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } beart)
+pdfjs-document-properties-title = Teideal:
+pdfjs-document-properties-author = Údar:
+pdfjs-document-properties-subject = Ábhar:
+pdfjs-document-properties-keywords = Eochairfhocail:
+pdfjs-document-properties-creation-date = Dáta Cruthaithe:
+pdfjs-document-properties-modification-date = Dáta Athraithe:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Cruthaitheoir:
+pdfjs-document-properties-producer = Cruthaitheoir an PDF:
+pdfjs-document-properties-version = Leagan PDF:
+pdfjs-document-properties-page-count = Líon Leathanach:
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+
+##
+
+pdfjs-document-properties-close-button = Dún
+
+## Print
+
+pdfjs-print-progress-message = Cáipéis á hullmhú le priontáil…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cealaigh
+pdfjs-printing-not-supported = Rabhadh: Ní thacaíonn an brabhsálaí le priontáil go hiomlán.
+pdfjs-printing-not-ready = Rabhadh: Ní féidir an PDF a phriontáil go dtí go mbeidh an cháipéis iomlán lódáilte.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Scoránaigh an Barra Taoibh
+pdfjs-toggle-sidebar-button-label = Scoránaigh an Barra Taoibh
+pdfjs-document-outline-button =
+ .title = Taispeáin Imlíne na Cáipéise (déchliceáil chun chuile rud a leathnú nó a laghdú)
+pdfjs-document-outline-button-label = Creatlach na Cáipéise
+pdfjs-attachments-button =
+ .title = Taispeáin Iatáin
+pdfjs-attachments-button-label = Iatáin
+pdfjs-thumbs-button =
+ .title = Taispeáin Mionsamhlacha
+pdfjs-thumbs-button-label = Mionsamhlacha
+pdfjs-findbar-button =
+ .title = Aimsigh sa Cháipéis
+pdfjs-findbar-button-label = Aimsigh
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Leathanach { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Mionsamhail Leathanaigh { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Aimsigh
+ .placeholder = Aimsigh sa cháipéis…
+pdfjs-find-previous-button =
+ .title = Aimsigh an sampla roimhe seo den nath seo
+pdfjs-find-previous-button-label = Roimhe seo
+pdfjs-find-next-button =
+ .title = Aimsigh an chéad sampla eile den nath sin
+pdfjs-find-next-button-label = Ar aghaidh
+pdfjs-find-highlight-checkbox = Aibhsigh uile
+pdfjs-find-match-case-checkbox-label = Cásíogair
+pdfjs-find-entire-word-checkbox-label = Focail iomlána
+pdfjs-find-reached-top = Ag barr na cáipéise, ag leanúint ón mbun
+pdfjs-find-reached-bottom = Ag bun na cáipéise, ag leanúint ón mbarr
+pdfjs-find-not-found = Frása gan aimsiú
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Leithead Leathanaigh
+pdfjs-page-scale-fit = Laghdaigh go dtí an Leathanach
+pdfjs-page-scale-auto = Súmáil Uathoibríoch
+pdfjs-page-scale-actual = Fíormhéid
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Tharla earráid agus an cháipéis PDF á lódáil.
+pdfjs-invalid-file-error = Comhad neamhbhailí nó truaillithe PDF.
+pdfjs-missing-file-error = Comhad PDF ar iarraidh.
+pdfjs-unexpected-response-error = Freagra ón bhfreastalaí nach rabhthas ag súil leis.
+pdfjs-rendering-error = Tharla earráid agus an leathanach á leagan amach.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anótáil { $type }]
+
+## Password
+
+pdfjs-password-label = Cuir an focal faire isteach chun an comhad PDF seo a oscailt.
+pdfjs-password-invalid = Focal faire mícheart. Déan iarracht eile.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Cealaigh
+pdfjs-web-fonts-disabled = Tá clófhoirne Gréasáin díchumasaithe: ní féidir clófhoirne leabaithe PDF a úsáid.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/gd/viewer.ftl b/web/locale/gd/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..cc67391a16f55d5c1d67b4157539c2a71d6bd5c5
--- /dev/null
+++ b/web/locale/gd/viewer.ftl
@@ -0,0 +1,299 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = An duilleag roimhe
+pdfjs-previous-button-label = Air ais
+pdfjs-next-button =
+ .title = An ath-dhuilleag
+pdfjs-next-button-label = Air adhart
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Duilleag
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = à { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } à { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Sùm a-mach
+pdfjs-zoom-out-button-label = Sùm a-mach
+pdfjs-zoom-in-button =
+ .title = Sùm a-steach
+pdfjs-zoom-in-button-label = Sùm a-steach
+pdfjs-zoom-select =
+ .title = Sùm
+pdfjs-presentation-mode-button =
+ .title = Gearr leum dhan mhodh taisbeanaidh
+pdfjs-presentation-mode-button-label = Am modh taisbeanaidh
+pdfjs-open-file-button =
+ .title = Fosgail faidhle
+pdfjs-open-file-button-label = Fosgail
+pdfjs-print-button =
+ .title = Clò-bhuail
+pdfjs-print-button-label = Clò-bhuail
+pdfjs-save-button =
+ .title = Sàbhail
+pdfjs-save-button-label = Sàbhail
+pdfjs-bookmark-button =
+ .title = An duilleag làithreach (Seall an URL on duilleag làithreach)
+pdfjs-bookmark-button-label = An duilleag làithreach
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Fosgail san aplacaid
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Fosgail san aplacaid
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Innealan
+pdfjs-tools-button-label = Innealan
+pdfjs-first-page-button =
+ .title = Rach gun chiad duilleag
+pdfjs-first-page-button-label = Rach gun chiad duilleag
+pdfjs-last-page-button =
+ .title = Rach gun duilleag mu dheireadh
+pdfjs-last-page-button-label = Rach gun duilleag mu dheireadh
+pdfjs-page-rotate-cw-button =
+ .title = Cuairtich gu deiseil
+pdfjs-page-rotate-cw-button-label = Cuairtich gu deiseil
+pdfjs-page-rotate-ccw-button =
+ .title = Cuairtich gu tuathail
+pdfjs-page-rotate-ccw-button-label = Cuairtich gu tuathail
+pdfjs-cursor-text-select-tool-button =
+ .title = Cuir an comas inneal taghadh an teacsa
+pdfjs-cursor-text-select-tool-button-label = Inneal taghadh an teacsa
+pdfjs-cursor-hand-tool-button =
+ .title = Cuir inneal na làimhe an comas
+pdfjs-cursor-hand-tool-button-label = Inneal na làimhe
+pdfjs-scroll-page-button =
+ .title = Cleachd sgroladh duilleige
+pdfjs-scroll-page-button-label = Sgroladh duilleige
+pdfjs-scroll-vertical-button =
+ .title = Cleachd sgroladh inghearach
+pdfjs-scroll-vertical-button-label = Sgroladh inghearach
+pdfjs-scroll-horizontal-button =
+ .title = Cleachd sgroladh còmhnard
+pdfjs-scroll-horizontal-button-label = Sgroladh còmhnard
+pdfjs-scroll-wrapped-button =
+ .title = Cleachd sgroladh paisgte
+pdfjs-scroll-wrapped-button-label = Sgroladh paisgte
+pdfjs-spread-none-button =
+ .title = Na cuir còmhla sgoileadh dhuilleagan
+pdfjs-spread-none-button-label = Gun sgaoileadh dhuilleagan
+pdfjs-spread-odd-button =
+ .title = Cuir còmhla duilleagan sgaoilte a thòisicheas le duilleagan aig a bheil àireamh chorr
+pdfjs-spread-odd-button-label = Sgaoileadh dhuilleagan corra
+pdfjs-spread-even-button =
+ .title = Cuir còmhla duilleagan sgaoilte a thòisicheas le duilleagan aig a bheil àireamh chothrom
+pdfjs-spread-even-button-label = Sgaoileadh dhuilleagan cothrom
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Roghainnean na sgrìobhainne…
+pdfjs-document-properties-button-label = Roghainnean na sgrìobhainne…
+pdfjs-document-properties-file-name = Ainm an fhaidhle:
+pdfjs-document-properties-file-size = Meud an fhaidhle:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Tiotal:
+pdfjs-document-properties-author = Ùghdar:
+pdfjs-document-properties-subject = Cuspair:
+pdfjs-document-properties-keywords = Faclan-luirg:
+pdfjs-document-properties-creation-date = Latha a chruthachaidh:
+pdfjs-document-properties-modification-date = Latha atharrachaidh:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Cruthadair:
+pdfjs-document-properties-producer = Saothraiche a' PDF:
+pdfjs-document-properties-version = Tionndadh a' PDF:
+pdfjs-document-properties-page-count = Àireamh de dhuilleagan:
+pdfjs-document-properties-page-size = Meud na duilleige:
+pdfjs-document-properties-page-size-unit-inches = ann an
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portraid
+pdfjs-document-properties-page-size-orientation-landscape = dreach-tìre
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Litir
+pdfjs-document-properties-page-size-name-legal = Laghail
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Grad shealladh-lìn:
+pdfjs-document-properties-linearized-yes = Tha
+pdfjs-document-properties-linearized-no = Chan eil
+pdfjs-document-properties-close-button = Dùin
+
+## Print
+
+pdfjs-print-progress-message = Ag ullachadh na sgrìobhainn airson clò-bhualadh…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Sguir dheth
+pdfjs-printing-not-supported = Rabhadh: Chan eil am brabhsair seo a' cur làn-taic ri clò-bhualadh.
+pdfjs-printing-not-ready = Rabhadh: Cha deach am PDF a luchdadh gu tur airson clò-bhualadh.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Toglaich am bàr-taoibh
+pdfjs-toggle-sidebar-notification-button =
+ .title = Toglaich am bàr-taoibh (tha oir-loidhne/ceanglachain/breathan aig an sgrìobhainn)
+pdfjs-toggle-sidebar-button-label = Toglaich am bàr-taoibh
+pdfjs-document-outline-button =
+ .title = Seall oir-loidhne na sgrìobhainn (dèan briogadh dùbailte airson a h-uile nì a leudachadh/a cho-theannadh)
+pdfjs-document-outline-button-label = Oir-loidhne na sgrìobhainne
+pdfjs-attachments-button =
+ .title = Seall na ceanglachain
+pdfjs-attachments-button-label = Ceanglachain
+pdfjs-layers-button =
+ .title = Seall na breathan (dèan briogadh dùbailte airson a h-uile breath ath-shuidheachadh dhan staid bhunaiteach)
+pdfjs-layers-button-label = Breathan
+pdfjs-thumbs-button =
+ .title = Seall na dealbhagan
+pdfjs-thumbs-button-label = Dealbhagan
+pdfjs-current-outline-item-button =
+ .title = Lorg nì làithreach na h-oir-loidhne
+pdfjs-current-outline-item-button-label = Nì làithreach na h-oir-loidhne
+pdfjs-findbar-button =
+ .title = Lorg san sgrìobhainn
+pdfjs-findbar-button-label = Lorg
+pdfjs-additional-layers = Barrachd breathan
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Duilleag a { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Dealbhag duilleag a { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Lorg
+ .placeholder = Lorg san sgrìobhainn...
+pdfjs-find-previous-button =
+ .title = Lorg làthair roimhe na h-abairt seo
+pdfjs-find-previous-button-label = Air ais
+pdfjs-find-next-button =
+ .title = Lorg ath-làthair na h-abairt seo
+pdfjs-find-next-button-label = Air adhart
+pdfjs-find-highlight-checkbox = Soillsich a h-uile
+pdfjs-find-match-case-checkbox-label = Aire do litrichean mòra is beaga
+pdfjs-find-match-diacritics-checkbox-label = Aire do stràcan
+pdfjs-find-entire-word-checkbox-label = Faclan-slàna
+pdfjs-find-reached-top = Ràinig sinn barr na duilleige, a' leantainn air adhart o bhonn na duilleige
+pdfjs-find-reached-bottom = Ràinig sinn bonn na duilleige, a' leantainn air adhart o bharr na duilleige
+pdfjs-find-not-found = Cha deach an abairt a lorg
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Leud na duilleige
+pdfjs-page-scale-fit = Freagair ri meud na duilleige
+pdfjs-page-scale-auto = Sùm fèin-obrachail
+pdfjs-page-scale-actual = Am fìor-mheud
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Duilleag { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Thachair mearachd rè luchdadh a' PDF.
+pdfjs-invalid-file-error = Faidhle PDF a tha mì-dhligheach no coirbte.
+pdfjs-missing-file-error = Faidhle PDF a tha a dhìth.
+pdfjs-unexpected-response-error = Freagairt on fhrithealaiche ris nach robh dùil.
+pdfjs-rendering-error = Thachair mearachd rè reandaradh na duilleige.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Nòtachadh { $type }]
+
+## Password
+
+pdfjs-password-label = Cuir a-steach am facal-faire gus am faidhle PDF seo fhosgladh.
+pdfjs-password-invalid = Tha am facal-faire cearr. Nach fheuch thu ris a-rithist?
+pdfjs-password-ok-button = Ceart ma-thà
+pdfjs-password-cancel-button = Sguir dheth
+pdfjs-web-fonts-disabled = Tha cruthan-clò lìn à comas: Chan urrainn dhuinn cruthan-clò PDF leabaichte a chleachdadh.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Teacsa
+pdfjs-editor-free-text-button-label = Teacsa
+pdfjs-editor-ink-button =
+ .title = Tarraing
+pdfjs-editor-ink-button-label = Tarraing
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Dath
+pdfjs-editor-free-text-size-input = Meud
+pdfjs-editor-ink-color-input = Dath
+pdfjs-editor-ink-thickness-input = Tighead
+pdfjs-editor-ink-opacity-input = Trìd-dhoilleireachd
+pdfjs-free-text =
+ .aria-label = An deasaiche teacsa
+pdfjs-free-text-default-content = Tòisich air sgrìobhadh…
+pdfjs-ink =
+ .aria-label = An deasaiche tharraingean
+pdfjs-ink-canvas =
+ .aria-label = Dealbh a chruthaich cleachdaiche
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/gl/viewer.ftl b/web/locale/gl/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..a08fb1a3aa23e42f761d086a20d05de88e215217
--- /dev/null
+++ b/web/locale/gl/viewer.ftl
@@ -0,0 +1,364 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Páxina anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Seguinte páxina
+pdfjs-next-button-label = Seguinte
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Páxina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Reducir
+pdfjs-zoom-out-button-label = Reducir
+pdfjs-zoom-in-button =
+ .title = Ampliar
+pdfjs-zoom-in-button-label = Ampliar
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Cambiar ao modo presentación
+pdfjs-presentation-mode-button-label = Modo presentación
+pdfjs-open-file-button =
+ .title = Abrir ficheiro
+pdfjs-open-file-button-label = Abrir
+pdfjs-print-button =
+ .title = Imprimir
+pdfjs-print-button-label = Imprimir
+pdfjs-save-button =
+ .title = Gardar
+pdfjs-save-button-label = Gardar
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Descargar
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Descargar
+pdfjs-bookmark-button =
+ .title = Páxina actual (ver o URL da páxina actual)
+pdfjs-bookmark-button-label = Páxina actual
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Abrir cunha aplicación
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Abrir cunha aplicación
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Ferramentas
+pdfjs-tools-button-label = Ferramentas
+pdfjs-first-page-button =
+ .title = Ir á primeira páxina
+pdfjs-first-page-button-label = Ir á primeira páxina
+pdfjs-last-page-button =
+ .title = Ir á última páxina
+pdfjs-last-page-button-label = Ir á última páxina
+pdfjs-page-rotate-cw-button =
+ .title = Rotar no sentido das agullas do reloxo
+pdfjs-page-rotate-cw-button-label = Rotar no sentido das agullas do reloxo
+pdfjs-page-rotate-ccw-button =
+ .title = Rotar no sentido contrario ás agullas do reloxo
+pdfjs-page-rotate-ccw-button-label = Rotar no sentido contrario ás agullas do reloxo
+pdfjs-cursor-text-select-tool-button =
+ .title = Activar a ferramenta de selección de texto
+pdfjs-cursor-text-select-tool-button-label = Ferramenta de selección de texto
+pdfjs-cursor-hand-tool-button =
+ .title = Activar a ferramenta de man
+pdfjs-cursor-hand-tool-button-label = Ferramenta de man
+pdfjs-scroll-page-button =
+ .title = Usar o desprazamento da páxina
+pdfjs-scroll-page-button-label = Desprazamento da páxina
+pdfjs-scroll-vertical-button =
+ .title = Usar o desprazamento vertical
+pdfjs-scroll-vertical-button-label = Desprazamento vertical
+pdfjs-scroll-horizontal-button =
+ .title = Usar o desprazamento horizontal
+pdfjs-scroll-horizontal-button-label = Desprazamento horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Usar o desprazamento en bloque
+pdfjs-scroll-wrapped-button-label = Desprazamento por bloque
+pdfjs-spread-none-button =
+ .title = Non agrupar páxinas
+pdfjs-spread-none-button-label = Ningún agrupamento
+pdfjs-spread-odd-button =
+ .title = Crea grupo de páxinas que comezan con números de páxina impares
+pdfjs-spread-odd-button-label = Agrupamento impar
+pdfjs-spread-even-button =
+ .title = Crea grupo de páxinas que comezan con números de páxina pares
+pdfjs-spread-even-button-label = Agrupamento par
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propiedades do documento…
+pdfjs-document-properties-button-label = Propiedades do documento…
+pdfjs-document-properties-file-name = Nome do ficheiro:
+pdfjs-document-properties-file-size = Tamaño do ficheiro:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Título:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Asunto:
+pdfjs-document-properties-keywords = Palabras clave:
+pdfjs-document-properties-creation-date = Data de creación:
+pdfjs-document-properties-modification-date = Data de modificación:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creado por:
+pdfjs-document-properties-producer = Xenerador do PDF:
+pdfjs-document-properties-version = Versión de PDF:
+pdfjs-document-properties-page-count = Número de páxinas:
+pdfjs-document-properties-page-size = Tamaño da páxina:
+pdfjs-document-properties-page-size-unit-inches = pol
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertical
+pdfjs-document-properties-page-size-orientation-landscape = horizontal
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Carta
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Visualización rápida das páxinas web:
+pdfjs-document-properties-linearized-yes = Si
+pdfjs-document-properties-linearized-no = Non
+pdfjs-document-properties-close-button = Pechar
+
+## Print
+
+pdfjs-print-progress-message = Preparando o documento para imprimir…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancelar
+pdfjs-printing-not-supported = Aviso: A impresión non é compatíbel de todo con este navegador.
+pdfjs-printing-not-ready = Aviso: O PDF non se cargou completamente para imprimirse.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Amosar/agochar a barra lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Alternar barra lateral (o documento contén esquema/anexos/capas)
+pdfjs-toggle-sidebar-button-label = Amosar/agochar a barra lateral
+pdfjs-document-outline-button =
+ .title = Amosar a estrutura do documento (dobre clic para expandir/contraer todos os elementos)
+pdfjs-document-outline-button-label = Estrutura do documento
+pdfjs-attachments-button =
+ .title = Amosar anexos
+pdfjs-attachments-button-label = Anexos
+pdfjs-layers-button =
+ .title = Mostrar capas (prema dúas veces para restaurar todas as capas o estado predeterminado)
+pdfjs-layers-button-label = Capas
+pdfjs-thumbs-button =
+ .title = Amosar miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-current-outline-item-button =
+ .title = Atopar o elemento delimitado actualmente
+pdfjs-current-outline-item-button-label = Elemento delimitado actualmente
+pdfjs-findbar-button =
+ .title = Atopar no documento
+pdfjs-findbar-button-label = Atopar
+pdfjs-additional-layers = Capas adicionais
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Páxina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura da páxina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Atopar
+ .placeholder = Atopar no documento…
+pdfjs-find-previous-button =
+ .title = Atopar a anterior aparición da frase
+pdfjs-find-previous-button-label = Anterior
+pdfjs-find-next-button =
+ .title = Atopar a seguinte aparición da frase
+pdfjs-find-next-button-label = Seguinte
+pdfjs-find-highlight-checkbox = Realzar todo
+pdfjs-find-match-case-checkbox-label = Diferenciar maiúsculas de minúsculas
+pdfjs-find-match-diacritics-checkbox-label = Distinguir os diacríticos
+pdfjs-find-entire-word-checkbox-label = Palabras completas
+pdfjs-find-reached-top = Chegouse ao inicio do documento, continuar desde o final
+pdfjs-find-reached-bottom = Chegouse ao final do documento, continuar desde o inicio
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] Coincidencia { $current } de { $total }
+ *[other] Coincidencia { $current } de { $total }
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Máis de { $limit } coincidencia
+ *[other] Máis de { $limit } coincidencias
+ }
+pdfjs-find-not-found = Non se atopou a frase
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Largura da páxina
+pdfjs-page-scale-fit = Axuste de páxina
+pdfjs-page-scale-auto = Zoom automático
+pdfjs-page-scale-actual = Tamaño actual
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Páxina { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Produciuse un erro ao cargar o PDF.
+pdfjs-invalid-file-error = Ficheiro PDF danado ou non válido.
+pdfjs-missing-file-error = Falta o ficheiro PDF.
+pdfjs-unexpected-response-error = Resposta inesperada do servidor.
+pdfjs-rendering-error = Produciuse un erro ao representar a páxina.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotación { $type }]
+
+## Password
+
+pdfjs-password-label = Escriba o contrasinal para abrir este ficheiro PDF.
+pdfjs-password-invalid = Contrasinal incorrecto. Tente de novo.
+pdfjs-password-ok-button = Aceptar
+pdfjs-password-cancel-button = Cancelar
+pdfjs-web-fonts-disabled = Desactiváronse as fontes web: foi imposíbel usar as fontes incrustadas no PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texto
+pdfjs-editor-free-text-button-label = Texto
+pdfjs-editor-ink-button =
+ .title = Debuxo
+pdfjs-editor-ink-button-label = Debuxo
+pdfjs-editor-stamp-button =
+ .title = Engadir ou editar imaxes
+pdfjs-editor-stamp-button-label = Engadir ou editar imaxes
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-freetext-button =
+ .title = Eliminar o texto
+pdfjs-editor-remove-stamp-button =
+ .title = Eliminar a imaxe
+pdfjs-editor-remove-highlight-button =
+ .title = Eliminar o resaltado
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Cor
+pdfjs-editor-free-text-size-input = Tamaño
+pdfjs-editor-ink-color-input = Cor
+pdfjs-editor-ink-thickness-input = Grosor
+pdfjs-editor-ink-opacity-input = Opacidade
+pdfjs-editor-stamp-add-image-button =
+ .title = Engadir imaxe
+pdfjs-editor-stamp-add-image-button-label = Engadir imaxe
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Grosor
+pdfjs-free-text =
+ .aria-label = Editor de texto
+pdfjs-free-text-default-content = Comezar a teclear…
+pdfjs-ink =
+ .aria-label = Editor de debuxos
+pdfjs-ink-canvas =
+ .aria-label = Imaxe creada por unha usuaria
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Texto alternativo
+pdfjs-editor-alt-text-edit-button-label = Editar o texto alternativo
+pdfjs-editor-alt-text-dialog-label = Escoller unha opción
+pdfjs-editor-alt-text-add-description-label = Engadir unha descrición
+pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativo
+pdfjs-editor-alt-text-mark-decorative-description = Utilízase para imaxes ornamentais, como bordos ou marcas de auga.
+pdfjs-editor-alt-text-cancel-button = Cancelar
+pdfjs-editor-alt-text-save-button = Gardar
+pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativo
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Por exemplo, «Un mozo séntase á mesa para comer»
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Esquina superior esquerda: cambia o tamaño
+pdfjs-editor-resizer-label-top-middle = Medio superior: cambia o tamaño
+pdfjs-editor-resizer-label-top-right = Esquina superior dereita: cambia o tamaño
+pdfjs-editor-resizer-label-middle-right = Medio dereito: cambia o tamaño
+pdfjs-editor-resizer-label-bottom-right = Esquina inferior dereita: cambia o tamaño
+pdfjs-editor-resizer-label-bottom-middle = Abaixo medio: cambia o tamaño
+pdfjs-editor-resizer-label-bottom-left = Esquina inferior esquerda: cambia o tamaño
+pdfjs-editor-resizer-label-middle-left = Medio esquerdo: cambia o tamaño
+
+## Color picker
+
diff --git a/web/locale/gn/viewer.ftl b/web/locale/gn/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..29ec18ae3f66ded834dce56e7f8d455ca0e7787b
--- /dev/null
+++ b/web/locale/gn/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Kuatiarogue mboyvegua
+pdfjs-previous-button-label = Mboyvegua
+pdfjs-next-button =
+ .title = Kuatiarogue upeigua
+pdfjs-next-button-label = Upeigua
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Kuatiarogue
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } gui
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Momichĩ
+pdfjs-zoom-out-button-label = Momichĩ
+pdfjs-zoom-in-button =
+ .title = Mbotuicha
+pdfjs-zoom-in-button-label = Mbotuicha
+pdfjs-zoom-select =
+ .title = Tuichakue
+pdfjs-presentation-mode-button =
+ .title = Jehechauka reko moambue
+pdfjs-presentation-mode-button-label = Jehechauka reko
+pdfjs-open-file-button =
+ .title = Marandurendápe jeike
+pdfjs-open-file-button-label = Jeike
+pdfjs-print-button =
+ .title = Monguatia
+pdfjs-print-button-label = Monguatia
+pdfjs-save-button =
+ .title = Ñongatu
+pdfjs-save-button-label = Ñongatu
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Mboguejy
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Mboguejy
+pdfjs-bookmark-button =
+ .title = Kuatiarogue ag̃agua (Ehecha URL kuatiarogue ag̃agua)
+pdfjs-bookmark-button-label = Kuatiarogue Ag̃agua
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Embojuruja tembiporu’ípe
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Embojuruja tembiporu’ípe
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Tembiporu
+pdfjs-tools-button-label = Tembiporu
+pdfjs-first-page-button =
+ .title = Kuatiarogue ñepyrũme jeho
+pdfjs-first-page-button-label = Kuatiarogue ñepyrũme jeho
+pdfjs-last-page-button =
+ .title = Kuatiarogue pahápe jeho
+pdfjs-last-page-button-label = Kuatiarogue pahápe jeho
+pdfjs-page-rotate-cw-button =
+ .title = Aravóicha mbojere
+pdfjs-page-rotate-cw-button-label = Aravóicha mbojere
+pdfjs-page-rotate-ccw-button =
+ .title = Aravo rapykue gotyo mbojere
+pdfjs-page-rotate-ccw-button-label = Aravo rapykue gotyo mbojere
+pdfjs-cursor-text-select-tool-button =
+ .title = Emyandy moñe’ẽrã jeporavo rembiporu
+pdfjs-cursor-text-select-tool-button-label = Moñe’ẽrã jeporavo rembiporu
+pdfjs-cursor-hand-tool-button =
+ .title = Tembiporu po pegua myandy
+pdfjs-cursor-hand-tool-button-label = Tembiporu po pegua
+pdfjs-scroll-page-button =
+ .title = Eiporu kuatiarogue jeku’e
+pdfjs-scroll-page-button-label = Kuatiarogue jeku’e
+pdfjs-scroll-vertical-button =
+ .title = Eiporu jeku’e ykeguáva
+pdfjs-scroll-vertical-button-label = Jeku’e ykeguáva
+pdfjs-scroll-horizontal-button =
+ .title = Eiporu jeku’e yvate gotyo
+pdfjs-scroll-horizontal-button-label = Jeku’e yvate gotyo
+pdfjs-scroll-wrapped-button =
+ .title = Eiporu jeku’e mbohyrupyre
+pdfjs-scroll-wrapped-button-label = Jeku’e mbohyrupyre
+pdfjs-spread-none-button =
+ .title = Ani ejuaju spreads kuatiarogue ndive
+pdfjs-spread-none-button-label = Spreads ỹre
+pdfjs-spread-odd-button =
+ .title = Embojuaju kuatiarogue jepysokue eñepyrũvo kuatiarogue impar-vagui
+pdfjs-spread-odd-button-label = Spreads impar
+pdfjs-spread-even-button =
+ .title = Embojuaju kuatiarogue jepysokue eñepyrũvo kuatiarogue par-vagui
+pdfjs-spread-even-button-label = Ipukuve uvei
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Kuatia mba’etee…
+pdfjs-document-properties-button-label = Kuatia mba’etee…
+pdfjs-document-properties-file-name = Marandurenda réra:
+pdfjs-document-properties-file-size = Marandurenda tuichakue:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Teratee:
+pdfjs-document-properties-author = Apohára:
+pdfjs-document-properties-subject = Mba’egua:
+pdfjs-document-properties-keywords = Jehero:
+pdfjs-document-properties-creation-date = Teñoihague arange:
+pdfjs-document-properties-modification-date = Iñambue hague arange:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Apo’ypyha:
+pdfjs-document-properties-producer = PDF mbosako’iha:
+pdfjs-document-properties-version = PDF mbojuehegua:
+pdfjs-document-properties-page-count = Kuatiarogue papapy:
+pdfjs-document-properties-page-size = Kuatiarogue tuichakue:
+pdfjs-document-properties-page-size-unit-inches = Amo
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = Oĩháicha
+pdfjs-document-properties-page-size-orientation-landscape = apaisado
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Kuatiañe’ẽ
+pdfjs-document-properties-page-size-name-legal = Tee
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Ñanduti jahecha pya’e:
+pdfjs-document-properties-linearized-yes = Añete
+pdfjs-document-properties-linearized-no = Ahániri
+pdfjs-document-properties-close-button = Mboty
+
+## Print
+
+pdfjs-print-progress-message = Embosako’i kuatia emonguatia hag̃ua…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Heja
+pdfjs-printing-not-supported = Kyhyjerã: Ñembokuatia ndojokupytypái ko kundahára ndive.
+pdfjs-printing-not-ready = Kyhyjerã: Ko PDF nahenyhẽmbái oñembokuatia hag̃uáicha.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Tenda yke moambue
+pdfjs-toggle-sidebar-notification-button =
+ .title = Embojopyru tenda ykegua (kuatia oguereko kuaakaha/moirũha/ñuãha)
+pdfjs-toggle-sidebar-button-label = Tenda yke moambue
+pdfjs-document-outline-button =
+ .title = Ehechauka kuatia rape (eikutu mokõi jey embotuicha/emomichĩ hag̃ua opavavete mba’eporu)
+pdfjs-document-outline-button-label = Kuatia apopyre
+pdfjs-attachments-button =
+ .title = Moirũha jehechauka
+pdfjs-attachments-button-label = Moirũha
+pdfjs-layers-button =
+ .title = Ehechauka ñuãha (eikutu jo’a emomba’apo hag̃ua opaite ñuãha tekoypýpe)
+pdfjs-layers-button-label = Ñuãha
+pdfjs-thumbs-button =
+ .title = Mba’emirĩ jehechauka
+pdfjs-thumbs-button-label = Mba’emirĩ
+pdfjs-current-outline-item-button =
+ .title = Eheka mba’eporu ag̃aguaitéva
+pdfjs-current-outline-item-button-label = Mba’eporu ag̃aguaitéva
+pdfjs-findbar-button =
+ .title = Kuatiápe jeheka
+pdfjs-findbar-button-label = Juhu
+pdfjs-additional-layers = Ñuãha moirũguáva
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Kuatiarogue { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Kuatiarogue mba’emirĩ { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Juhu
+ .placeholder = Kuatiápe jejuhu…
+pdfjs-find-previous-button =
+ .title = Ejuhu ñe’ẽrysýi osẽ’ypy hague
+pdfjs-find-previous-button-label = Mboyvegua
+pdfjs-find-next-button =
+ .title = Eho ñe’ẽ juhupyre upeiguávape
+pdfjs-find-next-button-label = Upeigua
+pdfjs-find-highlight-checkbox = Embojekuaavepa
+pdfjs-find-match-case-checkbox-label = Ejesareko taiguasu/taimichĩre
+pdfjs-find-match-diacritics-checkbox-label = Diacrítico moñondive
+pdfjs-find-entire-word-checkbox-label = Ñe’ẽ oĩmbáva
+pdfjs-find-reached-top = Ojehupyty kuatia ñepyrũ, oku’ejeýta kuatia paha guive
+pdfjs-find-reached-bottom = Ojehupyty kuatia paha, oku’ejeýta kuatia ñepyrũ guive
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } ha { $total } ojueheguáva
+ *[other] { $current } ha { $total } ojueheguáva
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Hetave { $limit } ojueheguáva
+ *[other] Hetave { $limit } ojueheguáva
+ }
+pdfjs-find-not-found = Ñe’ẽrysýi ojejuhu’ỹva
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Kuatiarogue pekue
+pdfjs-page-scale-fit = Kuatiarogue ñemoĩporã
+pdfjs-page-scale-auto = Tuichakue ijeheguíva
+pdfjs-page-scale-actual = Tuichakue ag̃agua
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Kuatiarogue { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Oiko jejavy PDF oñemyeñyhẽnguévo.
+pdfjs-invalid-file-error = PDF marandurenda ndoikóiva térã ivaipyréva.
+pdfjs-missing-file-error = Ndaipóri PDF marandurenda
+pdfjs-unexpected-response-error = Mohendahavusu mbohovái eha’ãrõ’ỹva.
+pdfjs-rendering-error = Oiko jejavy ehechaukasévo kuatiarogue.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Jehaipy { $type }]
+
+## Password
+
+pdfjs-password-label = Emoinge ñe’ẽñemi eipe’a hag̃ua ko marandurenda PDF.
+pdfjs-password-invalid = Ñe’ẽñemi ndoikóiva. Eha’ã jey.
+pdfjs-password-ok-button = MONEĨ
+pdfjs-password-cancel-button = Heja
+pdfjs-web-fonts-disabled = Ñanduti taity oñemongéma: ndaikatumo’ãi eiporu PDF jehai’íva taity.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Moñe’ẽrã
+pdfjs-editor-free-text-button-label = Moñe’ẽrã
+pdfjs-editor-ink-button =
+ .title = Moha’ãnga
+pdfjs-editor-ink-button-label = Moha’ãnga
+pdfjs-editor-stamp-button =
+ .title = Embojuaju térã embosako’i ta’ãnga
+pdfjs-editor-stamp-button-label = Embojuaju térã embosako’i ta’ãnga
+pdfjs-editor-highlight-button =
+ .title = Mbosa’y
+pdfjs-editor-highlight-button-label = Mbosa’y
+pdfjs-highlight-floating-button =
+ .title = Mbosa’y
+pdfjs-highlight-floating-button1 =
+ .title = Mbosa’y
+ .aria-label = Mbosa’y
+pdfjs-highlight-floating-button-label = Mbosa’y
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Emboguete ta’ãnga
+pdfjs-editor-remove-freetext-button =
+ .title = Emboguete moñe’ẽrã
+pdfjs-editor-remove-stamp-button =
+ .title = Emboguete ta’ãnga
+pdfjs-editor-remove-highlight-button =
+ .title = Eipe’a jehechaveha
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Sa’y
+pdfjs-editor-free-text-size-input = Tuichakue
+pdfjs-editor-ink-color-input = Sa’y
+pdfjs-editor-ink-thickness-input = Anambusu
+pdfjs-editor-ink-opacity-input = Pytũngy
+pdfjs-editor-stamp-add-image-button =
+ .title = Embojuaju ta’ãnga
+pdfjs-editor-stamp-add-image-button-label = Embojuaju ta’ãnga
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Anambusu
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Emoambue anambusukue embosa’ývo mba’eporu ha’e’ỹva moñe’ẽrã
+pdfjs-free-text =
+ .aria-label = Moñe’ẽrã moheñoiha
+pdfjs-free-text-default-content = Ehai ñepyrũ…
+pdfjs-ink =
+ .aria-label = Ta’ãnga moheñoiha
+pdfjs-ink-canvas =
+ .aria-label = Ta’ãnga omoheñóiva poruhára
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Moñe’ẽrã mokõiháva
+pdfjs-editor-alt-text-edit-button-label = Embojuruja moñe’ẽrã mokõiháva
+pdfjs-editor-alt-text-dialog-label = Eiporavo poravorã
+pdfjs-editor-alt-text-dialog-description = Moñe’ẽrã ykepegua (moñe’ẽrã ykepegua) nepytyvõ nderehecháiramo ta’ãnga térã nahenyhẽiramo.
+pdfjs-editor-alt-text-add-description-label = Embojuaju ñemoha’ãnga
+pdfjs-editor-alt-text-add-description-description = Ehaimi 1 térã 2 ñe’ẽjuaju oñe’ẽva pe téma rehe, ijere térã mba’eapóre.
+pdfjs-editor-alt-text-mark-decorative-label = Emongurusu jeguakárõ
+pdfjs-editor-alt-text-mark-decorative-description = Ojeporu ta’ãnga jeguakarã, tembe’y térã ta’ãnga ruguarãramo.
+pdfjs-editor-alt-text-cancel-button = Heja
+pdfjs-editor-alt-text-save-button = Ñongatu
+pdfjs-editor-alt-text-decorative-tooltip = Jeguakárõ mongurusupyre
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Techapyrã: “Peteĩ mitãrusu oguapy mesápe okaru hag̃ua”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Yvate asu gotyo — emoambue tuichakue
+pdfjs-editor-resizer-label-top-middle = Yvate mbytépe — emoambue tuichakue
+pdfjs-editor-resizer-label-top-right = Yvate akatúape — emoambue tuichakue
+pdfjs-editor-resizer-label-middle-right = Mbyte akatúape — emoambue tuichakue
+pdfjs-editor-resizer-label-bottom-right = Yvy gotyo akatúape — emoambue tuichakue
+pdfjs-editor-resizer-label-bottom-middle = Yvy gotyo mbytépe — emoambue tuichakue
+pdfjs-editor-resizer-label-bottom-left = Iguýpe asu gotyo — emoambue tuichakue
+pdfjs-editor-resizer-label-middle-left = Mbyte asu gotyo — emoambue tuichakue
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Jehechaveha sa’y
+pdfjs-editor-colorpicker-button =
+ .title = Emoambue sa’y
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Sa’y poravopyrã
+pdfjs-editor-colorpicker-yellow =
+ .title = Sa’yju
+pdfjs-editor-colorpicker-green =
+ .title = Hovyũ
+pdfjs-editor-colorpicker-blue =
+ .title = Hovy
+pdfjs-editor-colorpicker-pink =
+ .title = Pytãngy
+pdfjs-editor-colorpicker-red =
+ .title = Pyha
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Techaukapa
+pdfjs-editor-highlight-show-all-button =
+ .title = Techaukapa
diff --git a/web/locale/gu-IN/viewer.ftl b/web/locale/gu-IN/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..5d8bb549f3125c9212c62ac180003457f489b470
--- /dev/null
+++ b/web/locale/gu-IN/viewer.ftl
@@ -0,0 +1,247 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = પહેલાનુ પાનું
+pdfjs-previous-button-label = પહેલાનુ
+pdfjs-next-button =
+ .title = આગળનુ પાનું
+pdfjs-next-button-label = આગળનું
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = પાનું
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = નો { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } નો { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = મોટુ કરો
+pdfjs-zoom-out-button-label = મોટુ કરો
+pdfjs-zoom-in-button =
+ .title = નાનું કરો
+pdfjs-zoom-in-button-label = નાનું કરો
+pdfjs-zoom-select =
+ .title = નાનું મોટુ કરો
+pdfjs-presentation-mode-button =
+ .title = રજૂઆત સ્થિતિમાં જાવ
+pdfjs-presentation-mode-button-label = રજૂઆત સ્થિતિ
+pdfjs-open-file-button =
+ .title = ફાઇલ ખોલો
+pdfjs-open-file-button-label = ખોલો
+pdfjs-print-button =
+ .title = છાપો
+pdfjs-print-button-label = છારો
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = સાધનો
+pdfjs-tools-button-label = સાધનો
+pdfjs-first-page-button =
+ .title = પહેલાં પાનામાં જાવ
+pdfjs-first-page-button-label = પ્રથમ પાનાં પર જાવ
+pdfjs-last-page-button =
+ .title = છેલ્લા પાનાં પર જાવ
+pdfjs-last-page-button-label = છેલ્લા પાનાં પર જાવ
+pdfjs-page-rotate-cw-button =
+ .title = ઘડિયાળનાં કાંટા તરફ ફેરવો
+pdfjs-page-rotate-cw-button-label = ઘડિયાળનાં કાંટા તરફ ફેરવો
+pdfjs-page-rotate-ccw-button =
+ .title = ઘડિયાળનાં કાંટાની ઉલટી દિશામાં ફેરવો
+pdfjs-page-rotate-ccw-button-label = ઘડિયાળનાં કાંટાની વિરુદ્દ ફેરવો
+pdfjs-cursor-text-select-tool-button =
+ .title = ટેક્સ્ટ પસંદગી ટૂલ સક્ષમ કરો
+pdfjs-cursor-text-select-tool-button-label = ટેક્સ્ટ પસંદગી ટૂલ
+pdfjs-cursor-hand-tool-button =
+ .title = હાથનાં સાધનને સક્રિય કરો
+pdfjs-cursor-hand-tool-button-label = હેન્ડ ટૂલ
+pdfjs-scroll-vertical-button =
+ .title = ઊભી સ્ક્રોલિંગનો ઉપયોગ કરો
+pdfjs-scroll-vertical-button-label = ઊભી સ્ક્રોલિંગ
+pdfjs-scroll-horizontal-button =
+ .title = આડી સ્ક્રોલિંગનો ઉપયોગ કરો
+pdfjs-scroll-horizontal-button-label = આડી સ્ક્રોલિંગ
+pdfjs-scroll-wrapped-button =
+ .title = આવરિત સ્ક્રોલિંગનો ઉપયોગ કરો
+pdfjs-scroll-wrapped-button-label = આવરિત સ્ક્રોલિંગ
+pdfjs-spread-none-button =
+ .title = પૃષ્ઠ સ્પ્રેડમાં જોડાવશો નહીં
+pdfjs-spread-none-button-label = કોઈ સ્પ્રેડ નથી
+pdfjs-spread-odd-button =
+ .title = એકી-ક્રમાંકિત પૃષ્ઠો સાથે પ્રારંભ થતાં પૃષ્ઠ સ્પ્રેડમાં જોડાઓ
+pdfjs-spread-odd-button-label = એકી સ્પ્રેડ્સ
+pdfjs-spread-even-button =
+ .title = નંબર-ક્રમાંકિત પૃષ્ઠોથી શરૂ થતાં પૃષ્ઠ સ્પ્રેડમાં જોડાઓ
+pdfjs-spread-even-button-label = સરખું ફેલાવવું
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = દસ્તાવેજ ગુણધર્મો…
+pdfjs-document-properties-button-label = દસ્તાવેજ ગુણધર્મો…
+pdfjs-document-properties-file-name = ફાઇલ નામ:
+pdfjs-document-properties-file-size = ફાઇલ માપ:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } બાઇટ)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } બાઇટ)
+pdfjs-document-properties-title = શીર્ષક:
+pdfjs-document-properties-author = લેખક:
+pdfjs-document-properties-subject = વિષય:
+pdfjs-document-properties-keywords = કિવર્ડ:
+pdfjs-document-properties-creation-date = નિર્માણ તારીખ:
+pdfjs-document-properties-modification-date = ફેરફાર તારીખ:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = નિર્માતા:
+pdfjs-document-properties-producer = PDF નિર્માતા:
+pdfjs-document-properties-version = PDF આવૃત્તિ:
+pdfjs-document-properties-page-count = પાનાં ગણતરી:
+pdfjs-document-properties-page-size = પૃષ્ઠનું કદ:
+pdfjs-document-properties-page-size-unit-inches = ઇંચ
+pdfjs-document-properties-page-size-unit-millimeters = મીમી
+pdfjs-document-properties-page-size-orientation-portrait = ઉભું
+pdfjs-document-properties-page-size-orientation-landscape = આડુ
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = પત્ર
+pdfjs-document-properties-page-size-name-legal = કાયદાકીય
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = ઝડપી વૅબ દૃશ્ય:
+pdfjs-document-properties-linearized-yes = હા
+pdfjs-document-properties-linearized-no = ના
+pdfjs-document-properties-close-button = બંધ કરો
+
+## Print
+
+pdfjs-print-progress-message = છાપકામ માટે દસ્તાવેજ તૈયાર કરી રહ્યા છે…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = રદ કરો
+pdfjs-printing-not-supported = ચેતવણી: છાપવાનું આ બ્રાઉઝર દ્દારા સંપૂર્ણપણે આધારભૂત નથી.
+pdfjs-printing-not-ready = Warning: PDF એ છાપવા માટે સંપૂર્ણપણે લાવેલ છે.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = ટૉગલ બાજુપટ્ટી
+pdfjs-toggle-sidebar-button-label = ટૉગલ બાજુપટ્ટી
+pdfjs-document-outline-button =
+ .title = દસ્તાવેજની રૂપરેખા બતાવો(બધી આઇટમ્સને વિસ્તૃત/સંકુચિત કરવા માટે ડબલ-ક્લિક કરો)
+pdfjs-document-outline-button-label = દસ્તાવેજ રૂપરેખા
+pdfjs-attachments-button =
+ .title = જોડાણોને બતાવો
+pdfjs-attachments-button-label = જોડાણો
+pdfjs-thumbs-button =
+ .title = થંબનેલ્સ બતાવો
+pdfjs-thumbs-button-label = થંબનેલ્સ
+pdfjs-findbar-button =
+ .title = દસ્તાવેજમાં શોધો
+pdfjs-findbar-button-label = શોધો
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = પાનું { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = પાનાં { $page } નું થંબનેલ્સ
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = શોધો
+ .placeholder = દસ્તાવેજમાં શોધો…
+pdfjs-find-previous-button =
+ .title = શબ્દસમૂહની પાછલી ઘટનાને શોધો
+pdfjs-find-previous-button-label = પહેલાંનુ
+pdfjs-find-next-button =
+ .title = શબ્દસમૂહની આગળની ઘટનાને શોધો
+pdfjs-find-next-button-label = આગળનું
+pdfjs-find-highlight-checkbox = બધુ પ્રકાશિત કરો
+pdfjs-find-match-case-checkbox-label = કેસ બંધબેસાડો
+pdfjs-find-entire-word-checkbox-label = સંપૂર્ણ શબ્દો
+pdfjs-find-reached-top = દસ્તાવેજનાં ટોચે પહોંચી ગયા, તળિયેથી ચાલુ કરેલ હતુ
+pdfjs-find-reached-bottom = દસ્તાવેજનાં અંતે પહોંચી ગયા, ઉપરથી ચાલુ કરેલ હતુ
+pdfjs-find-not-found = શબ્દસમૂહ મળ્યુ નથી
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = પાનાની પહોળાઇ
+pdfjs-page-scale-fit = પાનું બંધબેસતુ
+pdfjs-page-scale-auto = આપમેળે નાનુંમોટુ કરો
+pdfjs-page-scale-actual = ચોક્કસ માપ
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = ભૂલ ઉદ્ભવી જ્યારે PDF ને લાવી રહ્યા હોય.
+pdfjs-invalid-file-error = અયોગ્ય અથવા ભાંગેલ PDF ફાઇલ.
+pdfjs-missing-file-error = ગુમ થયેલ PDF ફાઇલ.
+pdfjs-unexpected-response-error = અનપેક્ષિત સર્વર પ્રતિસાદ.
+pdfjs-rendering-error = ભૂલ ઉદ્ભવી જ્યારે પાનાંનુ રેન્ડ કરી રહ્યા હોય.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = આ PDF ફાઇલને ખોલવા પાસવર્ડને દાખલ કરો.
+pdfjs-password-invalid = અયોગ્ય પાસવર્ડ. મહેરબાની કરીને ફરી પ્રયત્ન કરો.
+pdfjs-password-ok-button = બરાબર
+pdfjs-password-cancel-button = રદ કરો
+pdfjs-web-fonts-disabled = વેબ ફોન્ટ નિષ્ક્રિય થયેલ છે: ઍમ્બેડ થયેલ PDF ફોન્ટને વાપરવાનું અસમર્થ.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/he/viewer.ftl b/web/locale/he/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..624d2083fc4e7b93221c0d3f01db7845a5dc652d
--- /dev/null
+++ b/web/locale/he/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = דף קודם
+pdfjs-previous-button-label = קודם
+pdfjs-next-button =
+ .title = דף הבא
+pdfjs-next-button-label = הבא
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = דף
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = מתוך { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } מתוך { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = התרחקות
+pdfjs-zoom-out-button-label = התרחקות
+pdfjs-zoom-in-button =
+ .title = התקרבות
+pdfjs-zoom-in-button-label = התקרבות
+pdfjs-zoom-select =
+ .title = מרחק מתצוגה
+pdfjs-presentation-mode-button =
+ .title = מעבר למצב מצגת
+pdfjs-presentation-mode-button-label = מצב מצגת
+pdfjs-open-file-button =
+ .title = פתיחת קובץ
+pdfjs-open-file-button-label = פתיחה
+pdfjs-print-button =
+ .title = הדפסה
+pdfjs-print-button-label = הדפסה
+pdfjs-save-button =
+ .title = שמירה
+pdfjs-save-button-label = שמירה
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = הורדה
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = הורדה
+pdfjs-bookmark-button =
+ .title = עמוד נוכחי (הצגת כתובת האתר מהעמוד הנוכחי)
+pdfjs-bookmark-button-label = עמוד נוכחי
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = פתיחה ביישום
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = פתיחה ביישום
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = כלים
+pdfjs-tools-button-label = כלים
+pdfjs-first-page-button =
+ .title = מעבר לעמוד הראשון
+pdfjs-first-page-button-label = מעבר לעמוד הראשון
+pdfjs-last-page-button =
+ .title = מעבר לעמוד האחרון
+pdfjs-last-page-button-label = מעבר לעמוד האחרון
+pdfjs-page-rotate-cw-button =
+ .title = הטיה עם כיוון השעון
+pdfjs-page-rotate-cw-button-label = הטיה עם כיוון השעון
+pdfjs-page-rotate-ccw-button =
+ .title = הטיה כנגד כיוון השעון
+pdfjs-page-rotate-ccw-button-label = הטיה כנגד כיוון השעון
+pdfjs-cursor-text-select-tool-button =
+ .title = הפעלת כלי בחירת טקסט
+pdfjs-cursor-text-select-tool-button-label = כלי בחירת טקסט
+pdfjs-cursor-hand-tool-button =
+ .title = הפעלת כלי היד
+pdfjs-cursor-hand-tool-button-label = כלי יד
+pdfjs-scroll-page-button =
+ .title = שימוש בגלילת עמוד
+pdfjs-scroll-page-button-label = גלילת עמוד
+pdfjs-scroll-vertical-button =
+ .title = שימוש בגלילה אנכית
+pdfjs-scroll-vertical-button-label = גלילה אנכית
+pdfjs-scroll-horizontal-button =
+ .title = שימוש בגלילה אופקית
+pdfjs-scroll-horizontal-button-label = גלילה אופקית
+pdfjs-scroll-wrapped-button =
+ .title = שימוש בגלילה רציפה
+pdfjs-scroll-wrapped-button-label = גלילה רציפה
+pdfjs-spread-none-button =
+ .title = לא לצרף מפתחי עמודים
+pdfjs-spread-none-button-label = ללא מפתחים
+pdfjs-spread-odd-button =
+ .title = צירוף מפתחי עמודים שמתחילים בדפים עם מספרים אי־זוגיים
+pdfjs-spread-odd-button-label = מפתחים אי־זוגיים
+pdfjs-spread-even-button =
+ .title = צירוף מפתחי עמודים שמתחילים בדפים עם מספרים זוגיים
+pdfjs-spread-even-button-label = מפתחים זוגיים
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = מאפייני מסמך…
+pdfjs-document-properties-button-label = מאפייני מסמך…
+pdfjs-document-properties-file-name = שם קובץ:
+pdfjs-document-properties-file-size = גודל הקובץ:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } ק״ב ({ $size_b } בתים)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } מ״ב ({ $size_b } בתים)
+pdfjs-document-properties-title = כותרת:
+pdfjs-document-properties-author = מחבר:
+pdfjs-document-properties-subject = נושא:
+pdfjs-document-properties-keywords = מילות מפתח:
+pdfjs-document-properties-creation-date = תאריך יצירה:
+pdfjs-document-properties-modification-date = תאריך שינוי:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = יוצר:
+pdfjs-document-properties-producer = יצרן PDF:
+pdfjs-document-properties-version = גרסת PDF:
+pdfjs-document-properties-page-count = מספר דפים:
+pdfjs-document-properties-page-size = גודל העמוד:
+pdfjs-document-properties-page-size-unit-inches = אינ׳
+pdfjs-document-properties-page-size-unit-millimeters = מ״מ
+pdfjs-document-properties-page-size-orientation-portrait = לאורך
+pdfjs-document-properties-page-size-orientation-landscape = לרוחב
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = מכתב
+pdfjs-document-properties-page-size-name-legal = דף משפטי
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = תצוגת דף מהירה:
+pdfjs-document-properties-linearized-yes = כן
+pdfjs-document-properties-linearized-no = לא
+pdfjs-document-properties-close-button = סגירה
+
+## Print
+
+pdfjs-print-progress-message = מסמך בהכנה להדפסה…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = ביטול
+pdfjs-printing-not-supported = אזהרה: הדפסה אינה נתמכת במלואה בדפדפן זה.
+pdfjs-printing-not-ready = אזהרה: מסמך ה־PDF לא נטען לחלוטין עד מצב שמאפשר הדפסה.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = הצגה/הסתרה של סרגל הצד
+pdfjs-toggle-sidebar-notification-button =
+ .title = החלפת תצוגת סרגל צד (מסמך שמכיל תוכן עניינים/קבצים מצורפים/שכבות)
+pdfjs-toggle-sidebar-button-label = הצגה/הסתרה של סרגל הצד
+pdfjs-document-outline-button =
+ .title = הצגת תוכן העניינים של המסמך (לחיצה כפולה כדי להרחיב או לצמצם את כל הפריטים)
+pdfjs-document-outline-button-label = תוכן העניינים של המסמך
+pdfjs-attachments-button =
+ .title = הצגת צרופות
+pdfjs-attachments-button-label = צרופות
+pdfjs-layers-button =
+ .title = הצגת שכבות (יש ללחוץ לחיצה כפולה כדי לאפס את כל השכבות למצב ברירת המחדל)
+pdfjs-layers-button-label = שכבות
+pdfjs-thumbs-button =
+ .title = הצגת תצוגה מקדימה
+pdfjs-thumbs-button-label = תצוגה מקדימה
+pdfjs-current-outline-item-button =
+ .title = מציאת פריט תוכן העניינים הנוכחי
+pdfjs-current-outline-item-button-label = פריט תוכן העניינים הנוכחי
+pdfjs-findbar-button =
+ .title = חיפוש במסמך
+pdfjs-findbar-button-label = חיפוש
+pdfjs-additional-layers = שכבות נוספות
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = עמוד { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = תצוגה מקדימה של עמוד { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = חיפוש
+ .placeholder = חיפוש במסמך…
+pdfjs-find-previous-button =
+ .title = מציאת המופע הקודם של הביטוי
+pdfjs-find-previous-button-label = קודם
+pdfjs-find-next-button =
+ .title = מציאת המופע הבא של הביטוי
+pdfjs-find-next-button-label = הבא
+pdfjs-find-highlight-checkbox = הדגשת הכול
+pdfjs-find-match-case-checkbox-label = התאמת אותיות
+pdfjs-find-match-diacritics-checkbox-label = התאמה דיאקריטית
+pdfjs-find-entire-word-checkbox-label = מילים שלמות
+pdfjs-find-reached-top = הגיע לראש הדף, ממשיך מלמטה
+pdfjs-find-reached-bottom = הגיע לסוף הדף, ממשיך מלמעלה
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } מתוך { $total } תוצאות
+ *[other] { $current } מתוך { $total } תוצאות
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] יותר מתוצאה אחת
+ *[other] יותר מ־{ $limit } תוצאות
+ }
+pdfjs-find-not-found = הביטוי לא נמצא
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = רוחב העמוד
+pdfjs-page-scale-fit = התאמה לעמוד
+pdfjs-page-scale-auto = מרחק מתצוגה אוטומטי
+pdfjs-page-scale-actual = גודל אמיתי
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = עמוד { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = אירעה שגיאה בעת טעינת ה־PDF.
+pdfjs-invalid-file-error = קובץ PDF פגום או לא תקין.
+pdfjs-missing-file-error = קובץ PDF חסר.
+pdfjs-unexpected-response-error = תגובת שרת לא צפויה.
+pdfjs-rendering-error = אירעה שגיאה בעת עיבוד הדף.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [הערת { $type }]
+
+## Password
+
+pdfjs-password-label = נא להכניס את הססמה לפתיחת קובץ PDF זה.
+pdfjs-password-invalid = ססמה שגויה. נא לנסות שנית.
+pdfjs-password-ok-button = אישור
+pdfjs-password-cancel-button = ביטול
+pdfjs-web-fonts-disabled = גופני רשת מנוטרלים: לא ניתן להשתמש בגופני PDF מוטבעים.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = טקסט
+pdfjs-editor-free-text-button-label = טקסט
+pdfjs-editor-ink-button =
+ .title = ציור
+pdfjs-editor-ink-button-label = ציור
+pdfjs-editor-stamp-button =
+ .title = הוספה או עריכת תמונות
+pdfjs-editor-stamp-button-label = הוספה או עריכת תמונות
+pdfjs-editor-highlight-button =
+ .title = סימון
+pdfjs-editor-highlight-button-label = סימון
+pdfjs-highlight-floating-button =
+ .title = סימון
+pdfjs-highlight-floating-button1 =
+ .title = סימון
+ .aria-label = סימון
+pdfjs-highlight-floating-button-label = סימון
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = הסרת ציור
+pdfjs-editor-remove-freetext-button =
+ .title = הסרת טקסט
+pdfjs-editor-remove-stamp-button =
+ .title = הסרת תמונה
+pdfjs-editor-remove-highlight-button =
+ .title = הסרת הדגשה
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = צבע
+pdfjs-editor-free-text-size-input = גודל
+pdfjs-editor-ink-color-input = צבע
+pdfjs-editor-ink-thickness-input = עובי
+pdfjs-editor-ink-opacity-input = אטימות
+pdfjs-editor-stamp-add-image-button =
+ .title = הוספת תמונה
+pdfjs-editor-stamp-add-image-button-label = הוספת תמונה
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = עובי
+pdfjs-editor-free-highlight-thickness-title =
+ .title = שינוי עובי בעת הדגשת פריטים שאינם טקסט
+pdfjs-free-text =
+ .aria-label = עורך טקסט
+pdfjs-free-text-default-content = להתחיל להקליד…
+pdfjs-ink =
+ .aria-label = עורך ציור
+pdfjs-ink-canvas =
+ .aria-label = תמונה שנוצרה על־ידי משתמש
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = טקסט חלופי
+pdfjs-editor-alt-text-edit-button-label = עריכת טקסט חלופי
+pdfjs-editor-alt-text-dialog-label = בחירת אפשרות
+pdfjs-editor-alt-text-dialog-description = טקסט חלופי עוזר כשאנשים לא יכולים לראות את התמונה או כשהיא לא נטענת.
+pdfjs-editor-alt-text-add-description-label = הוספת תיאור
+pdfjs-editor-alt-text-add-description-description = כדאי לתאר במשפט אחד או שניים את הנושא, התפאורה או הפעולות.
+pdfjs-editor-alt-text-mark-decorative-label = סימון כדקורטיבי
+pdfjs-editor-alt-text-mark-decorative-description = זה משמש לתמונות נוי, כמו גבולות או סימני מים.
+pdfjs-editor-alt-text-cancel-button = ביטול
+pdfjs-editor-alt-text-save-button = שמירה
+pdfjs-editor-alt-text-decorative-tooltip = מסומן כדקורטיבי
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = לדוגמה, ״גבר צעיר מתיישב ליד שולחן לאכול ארוחה״
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = פינה שמאלית עליונה - שינוי גודל
+pdfjs-editor-resizer-label-top-middle = למעלה באמצע - שינוי גודל
+pdfjs-editor-resizer-label-top-right = פינה ימנית עליונה - שינוי גודל
+pdfjs-editor-resizer-label-middle-right = ימינה באמצע - שינוי גודל
+pdfjs-editor-resizer-label-bottom-right = פינה ימנית תחתונה - שינוי גודל
+pdfjs-editor-resizer-label-bottom-middle = למטה באמצע - שינוי גודל
+pdfjs-editor-resizer-label-bottom-left = פינה שמאלית תחתונה - שינוי גודל
+pdfjs-editor-resizer-label-middle-left = שמאלה באמצע - שינוי גודל
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = צבע הדגשה
+pdfjs-editor-colorpicker-button =
+ .title = שינוי צבע
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = בחירת צבע
+pdfjs-editor-colorpicker-yellow =
+ .title = צהוב
+pdfjs-editor-colorpicker-green =
+ .title = ירוק
+pdfjs-editor-colorpicker-blue =
+ .title = כחול
+pdfjs-editor-colorpicker-pink =
+ .title = ורוד
+pdfjs-editor-colorpicker-red =
+ .title = אדום
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = הצגת הכול
+pdfjs-editor-highlight-show-all-button =
+ .title = הצגת הכול
diff --git a/web/locale/hi-IN/viewer.ftl b/web/locale/hi-IN/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..1ead593066bb3b5b1b4b92b6d6560d9dcaeb86bf
--- /dev/null
+++ b/web/locale/hi-IN/viewer.ftl
@@ -0,0 +1,253 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = पिछला पृष्ठ
+pdfjs-previous-button-label = पिछला
+pdfjs-next-button =
+ .title = अगला पृष्ठ
+pdfjs-next-button-label = आगे
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = पृष्ठ:
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } का
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = छोटा करें
+pdfjs-zoom-out-button-label = छोटा करें
+pdfjs-zoom-in-button =
+ .title = बड़ा करें
+pdfjs-zoom-in-button-label = बड़ा करें
+pdfjs-zoom-select =
+ .title = बड़ा-छोटा करें
+pdfjs-presentation-mode-button =
+ .title = प्रस्तुति अवस्था में जाएँ
+pdfjs-presentation-mode-button-label = प्रस्तुति अवस्था
+pdfjs-open-file-button =
+ .title = फ़ाइल खोलें
+pdfjs-open-file-button-label = खोलें
+pdfjs-print-button =
+ .title = छापें
+pdfjs-print-button-label = छापें
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = ऐप में खोलें
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = ऐप में खोलें
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = औज़ार
+pdfjs-tools-button-label = औज़ार
+pdfjs-first-page-button =
+ .title = प्रथम पृष्ठ पर जाएँ
+pdfjs-first-page-button-label = प्रथम पृष्ठ पर जाएँ
+pdfjs-last-page-button =
+ .title = अंतिम पृष्ठ पर जाएँ
+pdfjs-last-page-button-label = अंतिम पृष्ठ पर जाएँ
+pdfjs-page-rotate-cw-button =
+ .title = घड़ी की दिशा में घुमाएँ
+pdfjs-page-rotate-cw-button-label = घड़ी की दिशा में घुमाएँ
+pdfjs-page-rotate-ccw-button =
+ .title = घड़ी की दिशा से उल्टा घुमाएँ
+pdfjs-page-rotate-ccw-button-label = घड़ी की दिशा से उल्टा घुमाएँ
+pdfjs-cursor-text-select-tool-button =
+ .title = पाठ चयन उपकरण सक्षम करें
+pdfjs-cursor-text-select-tool-button-label = पाठ चयन उपकरण
+pdfjs-cursor-hand-tool-button =
+ .title = हस्त उपकरण सक्षम करें
+pdfjs-cursor-hand-tool-button-label = हस्त उपकरण
+pdfjs-scroll-vertical-button =
+ .title = लंबवत स्क्रॉलिंग का उपयोग करें
+pdfjs-scroll-vertical-button-label = लंबवत स्क्रॉलिंग
+pdfjs-scroll-horizontal-button =
+ .title = क्षितिजिय स्क्रॉलिंग का उपयोग करें
+pdfjs-scroll-horizontal-button-label = क्षितिजिय स्क्रॉलिंग
+pdfjs-scroll-wrapped-button =
+ .title = व्राप्पेड स्क्रॉलिंग का उपयोग करें
+pdfjs-spread-none-button-label = कोई स्प्रेड उपलब्ध नहीं
+pdfjs-spread-odd-button =
+ .title = विषम-क्रमांकित पृष्ठों से प्रारंभ होने वाले पृष्ठ स्प्रेड में शामिल हों
+pdfjs-spread-odd-button-label = विषम फैलाव
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = दस्तावेज़ विशेषता...
+pdfjs-document-properties-button-label = दस्तावेज़ विशेषता...
+pdfjs-document-properties-file-name = फ़ाइल नाम:
+pdfjs-document-properties-file-size = फाइल आकारः
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = शीर्षक:
+pdfjs-document-properties-author = लेखकः
+pdfjs-document-properties-subject = विषय:
+pdfjs-document-properties-keywords = कुंजी-शब्द:
+pdfjs-document-properties-creation-date = निर्माण दिनांक:
+pdfjs-document-properties-modification-date = संशोधन दिनांक:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = निर्माता:
+pdfjs-document-properties-producer = PDF उत्पादक:
+pdfjs-document-properties-version = PDF संस्करण:
+pdfjs-document-properties-page-count = पृष्ठ गिनती:
+pdfjs-document-properties-page-size = पृष्ठ आकार:
+pdfjs-document-properties-page-size-unit-inches = इंच
+pdfjs-document-properties-page-size-unit-millimeters = मिमी
+pdfjs-document-properties-page-size-orientation-portrait = पोर्ट्रेट
+pdfjs-document-properties-page-size-orientation-landscape = लैंडस्केप
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = पत्र
+pdfjs-document-properties-page-size-name-legal = क़ानूनी
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = तीव्र वेब व्यू:
+pdfjs-document-properties-linearized-yes = हाँ
+pdfjs-document-properties-linearized-no = नहीं
+pdfjs-document-properties-close-button = बंद करें
+
+## Print
+
+pdfjs-print-progress-message = छपाई के लिए दस्तावेज़ को तैयार किया जा रहा है...
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = रद्द करें
+pdfjs-printing-not-supported = चेतावनी: इस ब्राउज़र पर छपाई पूरी तरह से समर्थित नहीं है.
+pdfjs-printing-not-ready = चेतावनी: PDF छपाई के लिए पूरी तरह से लोड नहीं है.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = स्लाइडर टॉगल करें
+pdfjs-toggle-sidebar-button-label = स्लाइडर टॉगल करें
+pdfjs-document-outline-button =
+ .title = दस्तावेज़ की रूपरेखा दिखाइए (सारी वस्तुओं को फलने अथवा समेटने के लिए दो बार क्लिक करें)
+pdfjs-document-outline-button-label = दस्तावेज़ आउटलाइन
+pdfjs-attachments-button =
+ .title = संलग्नक दिखायें
+pdfjs-attachments-button-label = संलग्नक
+pdfjs-thumbs-button =
+ .title = लघुछवियाँ दिखाएँ
+pdfjs-thumbs-button-label = लघु छवि
+pdfjs-findbar-button =
+ .title = दस्तावेज़ में ढूँढ़ें
+pdfjs-findbar-button-label = ढूँढें
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = पृष्ठ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = पृष्ठ { $page } की लघु-छवि
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = ढूँढें
+ .placeholder = दस्तावेज़ में खोजें...
+pdfjs-find-previous-button =
+ .title = वाक्यांश की पिछली उपस्थिति ढूँढ़ें
+pdfjs-find-previous-button-label = पिछला
+pdfjs-find-next-button =
+ .title = वाक्यांश की अगली उपस्थिति ढूँढ़ें
+pdfjs-find-next-button-label = अगला
+pdfjs-find-highlight-checkbox = सभी आलोकित करें
+pdfjs-find-match-case-checkbox-label = मिलान स्थिति
+pdfjs-find-entire-word-checkbox-label = संपूर्ण शब्द
+pdfjs-find-reached-top = पृष्ठ के ऊपर पहुंच गया, नीचे से जारी रखें
+pdfjs-find-reached-bottom = पृष्ठ के नीचे में जा पहुँचा, ऊपर से जारी
+pdfjs-find-not-found = वाक्यांश नहीं मिला
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = पृष्ठ चौड़ाई
+pdfjs-page-scale-fit = पृष्ठ फिट
+pdfjs-page-scale-auto = स्वचालित जूम
+pdfjs-page-scale-actual = वास्तविक आकार
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF लोड करते समय एक त्रुटि हुई.
+pdfjs-invalid-file-error = अमान्य या भ्रष्ट PDF फ़ाइल.
+pdfjs-missing-file-error = अनुपस्थित PDF फ़ाइल.
+pdfjs-unexpected-response-error = अप्रत्याशित सर्वर प्रतिक्रिया.
+pdfjs-rendering-error = पृष्ठ रेंडरिंग के दौरान त्रुटि आई.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = इस PDF फ़ाइल को खोलने के लिए कृपया कूटशब्द भरें.
+pdfjs-password-invalid = अवैध कूटशब्द, कृपया फिर कोशिश करें.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = रद्द करें
+pdfjs-web-fonts-disabled = वेब फॉन्ट्स निष्क्रिय हैं: अंतःस्थापित PDF फॉन्टस के उपयोग में असमर्थ.
+
+## Editing
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = रंग
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/hr/viewer.ftl b/web/locale/hr/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..23d88e76a10907a8eaddf9771fa93a0e5dc66156
--- /dev/null
+++ b/web/locale/hr/viewer.ftl
@@ -0,0 +1,279 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Prethodna stranica
+pdfjs-previous-button-label = Prethodna
+pdfjs-next-button =
+ .title = Sljedeća stranica
+pdfjs-next-button-label = Sljedeća
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Stranica
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = od { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } od { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Umanji
+pdfjs-zoom-out-button-label = Umanji
+pdfjs-zoom-in-button =
+ .title = Uvećaj
+pdfjs-zoom-in-button-label = Uvećaj
+pdfjs-zoom-select =
+ .title = Zumiranje
+pdfjs-presentation-mode-button =
+ .title = Prebaci u prezentacijski način rada
+pdfjs-presentation-mode-button-label = Prezentacijski način rada
+pdfjs-open-file-button =
+ .title = Otvori datoteku
+pdfjs-open-file-button-label = Otvori
+pdfjs-print-button =
+ .title = Ispiši
+pdfjs-print-button-label = Ispiši
+pdfjs-save-button =
+ .title = Spremi
+pdfjs-save-button-label = Spremi
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Alati
+pdfjs-tools-button-label = Alati
+pdfjs-first-page-button =
+ .title = Idi na prvu stranicu
+pdfjs-first-page-button-label = Idi na prvu stranicu
+pdfjs-last-page-button =
+ .title = Idi na posljednju stranicu
+pdfjs-last-page-button-label = Idi na posljednju stranicu
+pdfjs-page-rotate-cw-button =
+ .title = Rotiraj u smjeru kazaljke na satu
+pdfjs-page-rotate-cw-button-label = Rotiraj u smjeru kazaljke na satu
+pdfjs-page-rotate-ccw-button =
+ .title = Rotiraj obrnutno od smjera kazaljke na satu
+pdfjs-page-rotate-ccw-button-label = Rotiraj obrnutno od smjera kazaljke na satu
+pdfjs-cursor-text-select-tool-button =
+ .title = Omogući alat za označavanje teksta
+pdfjs-cursor-text-select-tool-button-label = Alat za označavanje teksta
+pdfjs-cursor-hand-tool-button =
+ .title = Omogući ručni alat
+pdfjs-cursor-hand-tool-button-label = Ručni alat
+pdfjs-scroll-vertical-button =
+ .title = Koristi okomito pomicanje
+pdfjs-scroll-vertical-button-label = Okomito pomicanje
+pdfjs-scroll-horizontal-button =
+ .title = Koristi vodoravno pomicanje
+pdfjs-scroll-horizontal-button-label = Vodoravno pomicanje
+pdfjs-scroll-wrapped-button =
+ .title = Koristi kontinuirani raspored stranica
+pdfjs-scroll-wrapped-button-label = Kontinuirani raspored stranica
+pdfjs-spread-none-button =
+ .title = Ne izrađuj duplerice
+pdfjs-spread-none-button-label = Pojedinačne stranice
+pdfjs-spread-odd-button =
+ .title = Izradi duplerice koje počinju s neparnim stranicama
+pdfjs-spread-odd-button-label = Neparne duplerice
+pdfjs-spread-even-button =
+ .title = Izradi duplerice koje počinju s parnim stranicama
+pdfjs-spread-even-button-label = Parne duplerice
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Svojstva dokumenta …
+pdfjs-document-properties-button-label = Svojstva dokumenta …
+pdfjs-document-properties-file-name = Naziv datoteke:
+pdfjs-document-properties-file-size = Veličina datoteke:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtova)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtova)
+pdfjs-document-properties-title = Naslov:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Predmet:
+pdfjs-document-properties-keywords = Ključne riječi:
+pdfjs-document-properties-creation-date = Datum stvaranja:
+pdfjs-document-properties-modification-date = Datum promjene:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Stvaratelj:
+pdfjs-document-properties-producer = PDF stvaratelj:
+pdfjs-document-properties-version = PDF verzija:
+pdfjs-document-properties-page-count = Broj stranica:
+pdfjs-document-properties-page-size = Dimenzije stranice:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = uspravno
+pdfjs-document-properties-page-size-orientation-landscape = položeno
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Brzi web pregled:
+pdfjs-document-properties-linearized-yes = Da
+pdfjs-document-properties-linearized-no = Ne
+pdfjs-document-properties-close-button = Zatvori
+
+## Print
+
+pdfjs-print-progress-message = Pripremanje dokumenta za ispis…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Odustani
+pdfjs-printing-not-supported = Upozorenje: Ovaj preglednik ne podržava u potpunosti ispisivanje.
+pdfjs-printing-not-ready = Upozorenje: PDF nije u potpunosti učitan za ispis.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Prikaži/sakrij bočnu traku
+pdfjs-toggle-sidebar-notification-button =
+ .title = Prikazivanje i sklanjanje bočne trake (dokument sadrži strukturu/privitke/slojeve)
+pdfjs-toggle-sidebar-button-label = Prikaži/sakrij bočnu traku
+pdfjs-document-outline-button =
+ .title = Prikaži strukturu dokumenta (dvostruki klik za rasklapanje/sklapanje svih stavki)
+pdfjs-document-outline-button-label = Struktura dokumenta
+pdfjs-attachments-button =
+ .title = Prikaži privitke
+pdfjs-attachments-button-label = Privitci
+pdfjs-layers-button =
+ .title = Prikaži slojeve (dvoklik za vraćanje svih slojeva u zadano stanje)
+pdfjs-layers-button-label = Slojevi
+pdfjs-thumbs-button =
+ .title = Prikaži minijature
+pdfjs-thumbs-button-label = Minijature
+pdfjs-current-outline-item-button =
+ .title = Pronađi trenutačni element strukture
+pdfjs-current-outline-item-button-label = Trenutačni element strukture
+pdfjs-findbar-button =
+ .title = Pronađi u dokumentu
+pdfjs-findbar-button-label = Pronađi
+pdfjs-additional-layers = Dodatni slojevi
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Stranica { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Minijatura stranice { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Pronađi
+ .placeholder = Pronađi u dokumentu …
+pdfjs-find-previous-button =
+ .title = Pronađi prethodno pojavljivanje ovog izraza
+pdfjs-find-previous-button-label = Prethodno
+pdfjs-find-next-button =
+ .title = Pronađi sljedeće pojavljivanje ovog izraza
+pdfjs-find-next-button-label = Sljedeće
+pdfjs-find-highlight-checkbox = Istankni sve
+pdfjs-find-match-case-checkbox-label = Razlikovanje velikih i malih slova
+pdfjs-find-entire-word-checkbox-label = Cijele riječi
+pdfjs-find-reached-top = Dosegnut početak dokumenta, nastavak s kraja
+pdfjs-find-reached-bottom = Dosegnut kraj dokumenta, nastavak s početka
+pdfjs-find-not-found = Izraz nije pronađen
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Prilagodi širini prozora
+pdfjs-page-scale-fit = Prilagodi veličini prozora
+pdfjs-page-scale-auto = Automatsko zumiranje
+pdfjs-page-scale-actual = Stvarna veličina
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale } %
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Stranica { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Došlo je do greške pri učitavanju PDF-a.
+pdfjs-invalid-file-error = Neispravna ili oštećena PDF datoteka.
+pdfjs-missing-file-error = Nedostaje PDF datoteka.
+pdfjs-unexpected-response-error = Neočekivani odgovor poslužitelja.
+pdfjs-rendering-error = Došlo je do greške prilikom iscrtavanja stranice.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Bilješka]
+
+## Password
+
+pdfjs-password-label = Za otvoranje ove PDF datoteku upiši lozinku.
+pdfjs-password-invalid = Neispravna lozinka. Pokušaj ponovo.
+pdfjs-password-ok-button = U redu
+pdfjs-password-cancel-button = Odustani
+pdfjs-web-fonts-disabled = Web fontovi su deaktivirani: nije moguće koristiti ugrađene PDF fontove.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Boja
+pdfjs-editor-free-text-size-input = Veličina
+pdfjs-editor-ink-color-input = Boja
+pdfjs-editor-ink-thickness-input = Debljina
+pdfjs-editor-ink-opacity-input = Neprozirnost
+pdfjs-free-text =
+ .aria-label = Uređivač teksta
+pdfjs-free-text-default-content = Počni tipkati …
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/hsb/viewer.ftl b/web/locale/hsb/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..46feaf1bdd4dbf4d95115d072dcfa4429ae01617
--- /dev/null
+++ b/web/locale/hsb/viewer.ftl
@@ -0,0 +1,406 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Předchadna strona
+pdfjs-previous-button-label = Wróćo
+pdfjs-next-button =
+ .title = Přichodna strona
+pdfjs-next-button-label = Dale
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Strona
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = z { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Pomjeńšić
+pdfjs-zoom-out-button-label = Pomjeńšić
+pdfjs-zoom-in-button =
+ .title = Powjetšić
+pdfjs-zoom-in-button-label = Powjetšić
+pdfjs-zoom-select =
+ .title = Skalowanje
+pdfjs-presentation-mode-button =
+ .title = Do prezentaciskeho modusa přeńć
+pdfjs-presentation-mode-button-label = Prezentaciski modus
+pdfjs-open-file-button =
+ .title = Dataju wočinić
+pdfjs-open-file-button-label = Wočinić
+pdfjs-print-button =
+ .title = Ćišćeć
+pdfjs-print-button-label = Ćišćeć
+pdfjs-save-button =
+ .title = Składować
+pdfjs-save-button-label = Składować
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Sćahnyć
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Sćahnyć
+pdfjs-bookmark-button =
+ .title = Aktualna strona (URL z aktualneje strony pokazać)
+pdfjs-bookmark-button-label = Aktualna strona
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = W nałoženju wočinić
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = W nałoženju wočinić
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Nastroje
+pdfjs-tools-button-label = Nastroje
+pdfjs-first-page-button =
+ .title = K prěnjej stronje
+pdfjs-first-page-button-label = K prěnjej stronje
+pdfjs-last-page-button =
+ .title = K poslednjej stronje
+pdfjs-last-page-button-label = K poslednjej stronje
+pdfjs-page-rotate-cw-button =
+ .title = K směrej časnika wjerćeć
+pdfjs-page-rotate-cw-button-label = K směrej časnika wjerćeć
+pdfjs-page-rotate-ccw-button =
+ .title = Přećiwo směrej časnika wjerćeć
+pdfjs-page-rotate-ccw-button-label = Přećiwo směrej časnika wjerćeć
+pdfjs-cursor-text-select-tool-button =
+ .title = Nastroj za wuběranje teksta zmóžnić
+pdfjs-cursor-text-select-tool-button-label = Nastroj za wuběranje teksta
+pdfjs-cursor-hand-tool-button =
+ .title = Ručny nastroj zmóžnić
+pdfjs-cursor-hand-tool-button-label = Ručny nastroj
+pdfjs-scroll-page-button =
+ .title = Kulenje strony wužiwać
+pdfjs-scroll-page-button-label = Kulenje strony
+pdfjs-scroll-vertical-button =
+ .title = Wertikalne suwanje wužiwać
+pdfjs-scroll-vertical-button-label = Wertikalne suwanje
+pdfjs-scroll-horizontal-button =
+ .title = Horicontalne suwanje wužiwać
+pdfjs-scroll-horizontal-button-label = Horicontalne suwanje
+pdfjs-scroll-wrapped-button =
+ .title = Postupne suwanje wužiwać
+pdfjs-scroll-wrapped-button-label = Postupne suwanje
+pdfjs-spread-none-button =
+ .title = Strony njezwjazać
+pdfjs-spread-none-button-label = Žana dwójna strona
+pdfjs-spread-odd-button =
+ .title = Strony započinajo z njerunymi stronami zwjazać
+pdfjs-spread-odd-button-label = Njerune strony
+pdfjs-spread-even-button =
+ .title = Strony započinajo z runymi stronami zwjazać
+pdfjs-spread-even-button-label = Rune strony
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumentowe kajkosće…
+pdfjs-document-properties-button-label = Dokumentowe kajkosće…
+pdfjs-document-properties-file-name = Mjeno dataje:
+pdfjs-document-properties-file-size = Wulkosć dataje:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtow)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtow)
+pdfjs-document-properties-title = Titul:
+pdfjs-document-properties-author = Awtor:
+pdfjs-document-properties-subject = Předmjet:
+pdfjs-document-properties-keywords = Klučowe słowa:
+pdfjs-document-properties-creation-date = Datum wutworjenja:
+pdfjs-document-properties-modification-date = Datum změny:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Awtor:
+pdfjs-document-properties-producer = PDF-zhotowjer:
+pdfjs-document-properties-version = PDF-wersija:
+pdfjs-document-properties-page-count = Ličba stronow:
+pdfjs-document-properties-page-size = Wulkosć strony:
+pdfjs-document-properties-page-size-unit-inches = cól
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = wysoki format
+pdfjs-document-properties-page-size-orientation-landscape = prěčny format
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = Haj
+pdfjs-document-properties-linearized-no = Ně
+pdfjs-document-properties-close-button = Začinić
+
+## Print
+
+pdfjs-print-progress-message = Dokument so za ćišćenje přihotuje…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Přetorhnyć
+pdfjs-printing-not-supported = Warnowanje: Ćišćenje so přez tutón wobhladowak połnje njepodpěruje.
+pdfjs-printing-not-ready = Warnowanje: PDF njeje so za ćišćenje dospołnje začitał.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Bóčnicu pokazać/schować
+pdfjs-toggle-sidebar-notification-button =
+ .title = Bóčnicu přepinać (dokument rozrjad/přiwěški/woršty wobsahuje)
+pdfjs-toggle-sidebar-button-label = Bóčnicu pokazać/schować
+pdfjs-document-outline-button =
+ .title = Dokumentowy naćisk pokazać (dwójne kliknjenje, zo bychu so wšě zapiski pokazali/schowali)
+pdfjs-document-outline-button-label = Dokumentowa struktura
+pdfjs-attachments-button =
+ .title = Přiwěški pokazać
+pdfjs-attachments-button-label = Přiwěški
+pdfjs-layers-button =
+ .title = Woršty pokazać (klikńće dwójce, zo byšće wšě woršty na standardny staw wróćo stajił)
+pdfjs-layers-button-label = Woršty
+pdfjs-thumbs-button =
+ .title = Miniatury pokazać
+pdfjs-thumbs-button-label = Miniatury
+pdfjs-current-outline-item-button =
+ .title = Aktualny rozrjadowy zapisk pytać
+pdfjs-current-outline-item-button-label = Aktualny rozrjadowy zapisk
+pdfjs-findbar-button =
+ .title = W dokumenće pytać
+pdfjs-findbar-button-label = Pytać
+pdfjs-additional-layers = Dalše woršty
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Strona { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura strony { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Pytać
+ .placeholder = W dokumenće pytać…
+pdfjs-find-previous-button =
+ .title = Předchadne wustupowanje pytanskeho wuraza pytać
+pdfjs-find-previous-button-label = Wróćo
+pdfjs-find-next-button =
+ .title = Přichodne wustupowanje pytanskeho wuraza pytać
+pdfjs-find-next-button-label = Dale
+pdfjs-find-highlight-checkbox = Wšě wuzběhnyć
+pdfjs-find-match-case-checkbox-label = Wulkopisanje wobkedźbować
+pdfjs-find-match-diacritics-checkbox-label = Diakritiske znamješka wužiwać
+pdfjs-find-entire-word-checkbox-label = Cyłe słowa
+pdfjs-find-reached-top = Spočatk dokumenta docpěty, pokročuje so z kóncom
+pdfjs-find-reached-bottom = Kónc dokument docpěty, pokročuje so ze spočatkom
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } z { $total } wotpowědnika
+ [two] { $current } z { $total } wotpowědnikow
+ [few] { $current } z { $total } wotpowědnikow
+ *[other] { $current } z { $total } wotpowědnikow
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Wyše { $limit } wotpowědnik
+ [two] Wyše { $limit } wotpowědnikaj
+ [few] Wyše { $limit } wotpowědniki
+ *[other] Wyše { $limit } wotpowědnikow
+ }
+pdfjs-find-not-found = Pytanski wuraz njeje so namakał
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Šěrokosć strony
+pdfjs-page-scale-fit = Wulkosć strony
+pdfjs-page-scale-auto = Awtomatiske skalowanje
+pdfjs-page-scale-actual = Aktualna wulkosć
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Strona { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Při začitowanju PDF je zmylk wustupił.
+pdfjs-invalid-file-error = Njepłaćiwa abo wobškodźena PDF-dataja.
+pdfjs-missing-file-error = Falowaca PDF-dataja.
+pdfjs-unexpected-response-error = Njewočakowana serwerowa wotmołwa.
+pdfjs-rendering-error = Při zwobraznjenju strony je zmylk wustupił.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Typ přispomnjenki: { $type }]
+
+## Password
+
+pdfjs-password-label = Zapodajće hesło, zo byšće PDF-dataju wočinił.
+pdfjs-password-invalid = Njepłaćiwe hesło. Prošu spytajće hišće raz.
+pdfjs-password-ok-button = W porjadku
+pdfjs-password-cancel-button = Přetorhnyć
+pdfjs-web-fonts-disabled = Webpisma su znjemóžnjene: njeje móžno, zasadźene PDF-pisma wužiwać.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+pdfjs-editor-ink-button =
+ .title = Rysować
+pdfjs-editor-ink-button-label = Rysować
+pdfjs-editor-stamp-button =
+ .title = Wobrazy přidać abo wobdźěłać
+pdfjs-editor-stamp-button-label = Wobrazy přidać abo wobdźěłać
+pdfjs-editor-highlight-button =
+ .title = Wuzběhnyć
+pdfjs-editor-highlight-button-label = Wuzběhnyć
+pdfjs-highlight-floating-button =
+ .title = Wuzběhnyć
+pdfjs-highlight-floating-button1 =
+ .title = Wuzběhnjenje
+ .aria-label = Wuzběhnjenje
+pdfjs-highlight-floating-button-label = Wuzběhnjenje
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Rysowanku wotstronić
+pdfjs-editor-remove-freetext-button =
+ .title = Tekst wotstronić
+pdfjs-editor-remove-stamp-button =
+ .title = Wobraz wotstronić
+pdfjs-editor-remove-highlight-button =
+ .title = Wuzběhnjenje wotstronić
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Barba
+pdfjs-editor-free-text-size-input = Wulkosć
+pdfjs-editor-ink-color-input = Barba
+pdfjs-editor-ink-thickness-input = Tołstosć
+pdfjs-editor-ink-opacity-input = Opacita
+pdfjs-editor-stamp-add-image-button =
+ .title = Wobraz přidać
+pdfjs-editor-stamp-add-image-button-label = Wobraz přidać
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Tołstosć
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Tołstosć změnić, hdyž so zapiski wuzběhuja, kotrež tekst njejsu
+pdfjs-free-text =
+ .aria-label = Tekstowy editor
+pdfjs-free-text-default-content = Započńće pisać…
+pdfjs-ink =
+ .aria-label = Rysowanski editor
+pdfjs-ink-canvas =
+ .aria-label = Wobraz wutworjeny wot wužiwarja
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternatiwny tekst
+pdfjs-editor-alt-text-edit-button-label = Alternatiwny tekst wobdźěłać
+pdfjs-editor-alt-text-dialog-label = Nastajenje wubrać
+pdfjs-editor-alt-text-dialog-description = Alternatiwny tekst pomha, hdyž ludźo njemóža wobraz widźeć abo hdyž so wobraz njezačita.
+pdfjs-editor-alt-text-add-description-label = Wopisanje přidać
+pdfjs-editor-alt-text-add-description-description = Pisajće 1 sadu abo 2 sadźe, kotrejž temu, nastajenje abo akcije wopisujetej.
+pdfjs-editor-alt-text-mark-decorative-label = Jako dekoratiwny markěrować
+pdfjs-editor-alt-text-mark-decorative-description = To so za pyšace wobrazy wužiwa, na přikład ramiki abo wodowe znamjenja.
+pdfjs-editor-alt-text-cancel-button = Přetorhnyć
+pdfjs-editor-alt-text-save-button = Składować
+pdfjs-editor-alt-text-decorative-tooltip = Jako dekoratiwny markěrowany
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Na přikład, „Młody muž za blidom sedźi, zo by jědź jědł“
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Horjeka nalěwo – wulkosć změnić
+pdfjs-editor-resizer-label-top-middle = Horjeka wosrjedź – wulkosć změnić
+pdfjs-editor-resizer-label-top-right = Horjeka naprawo – wulkosć změnić
+pdfjs-editor-resizer-label-middle-right = Wosrjedź naprawo – wulkosć změnić
+pdfjs-editor-resizer-label-bottom-right = Deleka naprawo – wulkosć změnić
+pdfjs-editor-resizer-label-bottom-middle = Deleka wosrjedź – wulkosć změnić
+pdfjs-editor-resizer-label-bottom-left = Deleka nalěwo – wulkosć změnić
+pdfjs-editor-resizer-label-middle-left = Wosrjedź nalěwo – wulkosć změnić
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Barba wuzběhnjenja
+pdfjs-editor-colorpicker-button =
+ .title = Barbu změnić
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Wuběr barbow
+pdfjs-editor-colorpicker-yellow =
+ .title = Žołty
+pdfjs-editor-colorpicker-green =
+ .title = Zeleny
+pdfjs-editor-colorpicker-blue =
+ .title = Módry
+pdfjs-editor-colorpicker-pink =
+ .title = Pink
+pdfjs-editor-colorpicker-red =
+ .title = Čerwjeny
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Wšě pokazać
+pdfjs-editor-highlight-show-all-button =
+ .title = Wšě pokazać
diff --git a/web/locale/hu/viewer.ftl b/web/locale/hu/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..0c33e51bef9378bca64620bd8ca26ce22ea8bd55
--- /dev/null
+++ b/web/locale/hu/viewer.ftl
@@ -0,0 +1,396 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Előző oldal
+pdfjs-previous-button-label = Előző
+pdfjs-next-button =
+ .title = Következő oldal
+pdfjs-next-button-label = Tovább
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Oldal
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = összesen: { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Kicsinyítés
+pdfjs-zoom-out-button-label = Kicsinyítés
+pdfjs-zoom-in-button =
+ .title = Nagyítás
+pdfjs-zoom-in-button-label = Nagyítás
+pdfjs-zoom-select =
+ .title = Nagyítás
+pdfjs-presentation-mode-button =
+ .title = Váltás bemutató módba
+pdfjs-presentation-mode-button-label = Bemutató mód
+pdfjs-open-file-button =
+ .title = Fájl megnyitása
+pdfjs-open-file-button-label = Megnyitás
+pdfjs-print-button =
+ .title = Nyomtatás
+pdfjs-print-button-label = Nyomtatás
+pdfjs-save-button =
+ .title = Mentés
+pdfjs-save-button-label = Mentés
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Letöltés
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Letöltés
+pdfjs-bookmark-button =
+ .title = Jelenlegi oldal (webcím megtekintése a jelenlegi oldalról)
+pdfjs-bookmark-button-label = Jelenlegi oldal
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Eszközök
+pdfjs-tools-button-label = Eszközök
+pdfjs-first-page-button =
+ .title = Ugrás az első oldalra
+pdfjs-first-page-button-label = Ugrás az első oldalra
+pdfjs-last-page-button =
+ .title = Ugrás az utolsó oldalra
+pdfjs-last-page-button-label = Ugrás az utolsó oldalra
+pdfjs-page-rotate-cw-button =
+ .title = Forgatás az óramutató járásával egyezően
+pdfjs-page-rotate-cw-button-label = Forgatás az óramutató járásával egyezően
+pdfjs-page-rotate-ccw-button =
+ .title = Forgatás az óramutató járásával ellentétesen
+pdfjs-page-rotate-ccw-button-label = Forgatás az óramutató járásával ellentétesen
+pdfjs-cursor-text-select-tool-button =
+ .title = Szövegkijelölő eszköz bekapcsolása
+pdfjs-cursor-text-select-tool-button-label = Szövegkijelölő eszköz
+pdfjs-cursor-hand-tool-button =
+ .title = Kéz eszköz bekapcsolása
+pdfjs-cursor-hand-tool-button-label = Kéz eszköz
+pdfjs-scroll-page-button =
+ .title = Oldalgörgetés használata
+pdfjs-scroll-page-button-label = Oldalgörgetés
+pdfjs-scroll-vertical-button =
+ .title = Függőleges görgetés használata
+pdfjs-scroll-vertical-button-label = Függőleges görgetés
+pdfjs-scroll-horizontal-button =
+ .title = Vízszintes görgetés használata
+pdfjs-scroll-horizontal-button-label = Vízszintes görgetés
+pdfjs-scroll-wrapped-button =
+ .title = Rácsos elrendezés használata
+pdfjs-scroll-wrapped-button-label = Rácsos elrendezés
+pdfjs-spread-none-button =
+ .title = Ne tapassza össze az oldalakat
+pdfjs-spread-none-button-label = Nincs összetapasztás
+pdfjs-spread-odd-button =
+ .title = Lapok összetapasztása, a páratlan számú oldalakkal kezdve
+pdfjs-spread-odd-button-label = Összetapasztás: páratlan
+pdfjs-spread-even-button =
+ .title = Lapok összetapasztása, a páros számú oldalakkal kezdve
+pdfjs-spread-even-button-label = Összetapasztás: páros
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumentum tulajdonságai…
+pdfjs-document-properties-button-label = Dokumentum tulajdonságai…
+pdfjs-document-properties-file-name = Fájlnév:
+pdfjs-document-properties-file-size = Fájlméret:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bájt)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bájt)
+pdfjs-document-properties-title = Cím:
+pdfjs-document-properties-author = Szerző:
+pdfjs-document-properties-subject = Tárgy:
+pdfjs-document-properties-keywords = Kulcsszavak:
+pdfjs-document-properties-creation-date = Létrehozás dátuma:
+pdfjs-document-properties-modification-date = Módosítás dátuma:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Létrehozta:
+pdfjs-document-properties-producer = PDF előállító:
+pdfjs-document-properties-version = PDF verzió:
+pdfjs-document-properties-page-count = Oldalszám:
+pdfjs-document-properties-page-size = Lapméret:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = álló
+pdfjs-document-properties-page-size-orientation-landscape = fekvő
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Jogi információk
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Gyors webes nézet:
+pdfjs-document-properties-linearized-yes = Igen
+pdfjs-document-properties-linearized-no = Nem
+pdfjs-document-properties-close-button = Bezárás
+
+## Print
+
+pdfjs-print-progress-message = Dokumentum előkészítése nyomtatáshoz…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Mégse
+pdfjs-printing-not-supported = Figyelmeztetés: Ez a böngésző nem teljesen támogatja a nyomtatást.
+pdfjs-printing-not-ready = Figyelmeztetés: A PDF nincs teljesen betöltve a nyomtatáshoz.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Oldalsáv be/ki
+pdfjs-toggle-sidebar-notification-button =
+ .title = Oldalsáv be/ki (a dokumentum vázlatot/mellékleteket/rétegeket tartalmaz)
+pdfjs-toggle-sidebar-button-label = Oldalsáv be/ki
+pdfjs-document-outline-button =
+ .title = Dokumentum megjelenítése online (dupla kattintás minden elem kinyitásához/összecsukásához)
+pdfjs-document-outline-button-label = Dokumentumvázlat
+pdfjs-attachments-button =
+ .title = Mellékletek megjelenítése
+pdfjs-attachments-button-label = Van melléklet
+pdfjs-layers-button =
+ .title = Rétegek megjelenítése (dupla kattintás az összes réteg alapértelmezett állapotra visszaállításához)
+pdfjs-layers-button-label = Rétegek
+pdfjs-thumbs-button =
+ .title = Bélyegképek megjelenítése
+pdfjs-thumbs-button-label = Bélyegképek
+pdfjs-current-outline-item-button =
+ .title = Jelenlegi vázlatelem megkeresése
+pdfjs-current-outline-item-button-label = Jelenlegi vázlatelem
+pdfjs-findbar-button =
+ .title = Keresés a dokumentumban
+pdfjs-findbar-button-label = Keresés
+pdfjs-additional-layers = További rétegek
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page }. oldal
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page }. oldal bélyegképe
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Keresés
+ .placeholder = Keresés a dokumentumban…
+pdfjs-find-previous-button =
+ .title = A kifejezés előző előfordulásának keresése
+pdfjs-find-previous-button-label = Előző
+pdfjs-find-next-button =
+ .title = A kifejezés következő előfordulásának keresése
+pdfjs-find-next-button-label = Tovább
+pdfjs-find-highlight-checkbox = Összes kiemelése
+pdfjs-find-match-case-checkbox-label = Kis- és nagybetűk megkülönböztetése
+pdfjs-find-match-diacritics-checkbox-label = Diakritikus jelek
+pdfjs-find-entire-word-checkbox-label = Teljes szavak
+pdfjs-find-reached-top = A dokumentum eleje elérve, folytatás a végétől
+pdfjs-find-reached-bottom = A dokumentum vége elérve, folytatás az elejétől
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } / { $total } találat
+ *[other] { $current } / { $total } találat
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Több mint { $limit } találat
+ *[other] Több mint { $limit } találat
+ }
+pdfjs-find-not-found = A kifejezés nem található
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Oldalszélesség
+pdfjs-page-scale-fit = Teljes oldal
+pdfjs-page-scale-auto = Automatikus nagyítás
+pdfjs-page-scale-actual = Valódi méret
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = { $page }. oldal
+
+## Loading indicator messages
+
+pdfjs-loading-error = Hiba történt a PDF betöltésekor.
+pdfjs-invalid-file-error = Érvénytelen vagy sérült PDF fájl.
+pdfjs-missing-file-error = Hiányzó PDF fájl.
+pdfjs-unexpected-response-error = Váratlan kiszolgálóválasz.
+pdfjs-rendering-error = Hiba történt az oldal feldolgozása közben.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } megjegyzés]
+
+## Password
+
+pdfjs-password-label = Adja meg a jelszót a PDF fájl megnyitásához.
+pdfjs-password-invalid = Helytelen jelszó. Próbálja újra.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Mégse
+pdfjs-web-fonts-disabled = Webes betűkészletek letiltva: nem használhatók a beágyazott PDF betűkészletek.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Szöveg
+pdfjs-editor-free-text-button-label = Szöveg
+pdfjs-editor-ink-button =
+ .title = Rajzolás
+pdfjs-editor-ink-button-label = Rajzolás
+pdfjs-editor-stamp-button =
+ .title = Képek hozzáadása vagy szerkesztése
+pdfjs-editor-stamp-button-label = Képek hozzáadása vagy szerkesztése
+pdfjs-editor-highlight-button =
+ .title = Kiemelés
+pdfjs-editor-highlight-button-label = Kiemelés
+pdfjs-highlight-floating-button =
+ .title = Kiemelés
+pdfjs-highlight-floating-button1 =
+ .title = Kiemelés
+ .aria-label = Kiemelés
+pdfjs-highlight-floating-button-label = Kiemelés
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Rajz eltávolítása
+pdfjs-editor-remove-freetext-button =
+ .title = Szöveg eltávolítása
+pdfjs-editor-remove-stamp-button =
+ .title = Kép eltávolítása
+pdfjs-editor-remove-highlight-button =
+ .title = Kiemelés eltávolítása
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Szín
+pdfjs-editor-free-text-size-input = Méret
+pdfjs-editor-ink-color-input = Szín
+pdfjs-editor-ink-thickness-input = Vastagság
+pdfjs-editor-ink-opacity-input = Átlátszatlanság
+pdfjs-editor-stamp-add-image-button =
+ .title = Kép hozzáadása
+pdfjs-editor-stamp-add-image-button-label = Kép hozzáadása
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Vastagság
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Vastagság módosítása, ha nem szöveges elemeket emel ki
+pdfjs-free-text =
+ .aria-label = Szövegszerkesztő
+pdfjs-free-text-default-content = Kezdjen el gépelni…
+pdfjs-ink =
+ .aria-label = Rajzszerkesztő
+pdfjs-ink-canvas =
+ .aria-label = Felhasználó által készített kép
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternatív szöveg
+pdfjs-editor-alt-text-edit-button-label = Alternatív szöveg szerkesztése
+pdfjs-editor-alt-text-dialog-label = Válasszon egy lehetőséget
+pdfjs-editor-alt-text-dialog-description = Az alternatív szöveg segít, ha az emberek nem látják a képet, vagy ha az nem töltődik be.
+pdfjs-editor-alt-text-add-description-label = Leírás hozzáadása
+pdfjs-editor-alt-text-add-description-description = Törekedjen 1-2 mondatra, amely jellemzi a témát, környezetet vagy cselekvést.
+pdfjs-editor-alt-text-mark-decorative-label = Megjelölés dekoratívként
+pdfjs-editor-alt-text-mark-decorative-description = Ez a díszítőképeknél használatos, mint a szegélyek vagy a vízjelek.
+pdfjs-editor-alt-text-cancel-button = Mégse
+pdfjs-editor-alt-text-save-button = Mentés
+pdfjs-editor-alt-text-decorative-tooltip = Megjelölve dekoratívként
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Például: „Egy fiatal férfi leül enni egy asztalhoz”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Bal felső sarok – átméretezés
+pdfjs-editor-resizer-label-top-middle = Felül középen – átméretezés
+pdfjs-editor-resizer-label-top-right = Jobb felső sarok – átméretezés
+pdfjs-editor-resizer-label-middle-right = Jobbra középen – átméretezés
+pdfjs-editor-resizer-label-bottom-right = Jobb alsó sarok – átméretezés
+pdfjs-editor-resizer-label-bottom-middle = Alul középen – átméretezés
+pdfjs-editor-resizer-label-bottom-left = Bal alsó sarok – átméretezés
+pdfjs-editor-resizer-label-middle-left = Balra középen – átméretezés
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Kiemelés színe
+pdfjs-editor-colorpicker-button =
+ .title = Szín módosítása
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Színválasztások
+pdfjs-editor-colorpicker-yellow =
+ .title = Sárga
+pdfjs-editor-colorpicker-green =
+ .title = Zöld
+pdfjs-editor-colorpicker-blue =
+ .title = Kék
+pdfjs-editor-colorpicker-pink =
+ .title = Rózsaszín
+pdfjs-editor-colorpicker-red =
+ .title = Vörös
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Összes megjelenítése
+pdfjs-editor-highlight-show-all-button =
+ .title = Összes megjelenítése
diff --git a/web/locale/hy-AM/viewer.ftl b/web/locale/hy-AM/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..5c9dd27b180a603048216ca0961e0a6bc116313e
--- /dev/null
+++ b/web/locale/hy-AM/viewer.ftl
@@ -0,0 +1,272 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Նախորդ էջը
+pdfjs-previous-button-label = Նախորդը
+pdfjs-next-button =
+ .title = Հաջորդ էջը
+pdfjs-next-button-label = Հաջորդը
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Էջ.
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = -ը՝ { $pagesCount }-ից
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber }-ը { $pagesCount })-ից
+pdfjs-zoom-out-button =
+ .title = Փոքրացնել
+pdfjs-zoom-out-button-label = Փոքրացնել
+pdfjs-zoom-in-button =
+ .title = Խոշորացնել
+pdfjs-zoom-in-button-label = Խոշորացնել
+pdfjs-zoom-select =
+ .title = Դիտափոխում
+pdfjs-presentation-mode-button =
+ .title = Անցնել Ներկայացման եղանակին
+pdfjs-presentation-mode-button-label = Ներկայացման եղանակ
+pdfjs-open-file-button =
+ .title = Բացել նիշք
+pdfjs-open-file-button-label = Բացել
+pdfjs-print-button =
+ .title = Տպել
+pdfjs-print-button-label = Տպել
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Ներբեռնել
+pdfjs-bookmark-button-label = Ընթացիկ էջ
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Գործիքներ
+pdfjs-tools-button-label = Գործիքներ
+pdfjs-first-page-button =
+ .title = Անցնել առաջին էջին
+pdfjs-first-page-button-label = Անցնել առաջին էջին
+pdfjs-last-page-button =
+ .title = Անցնել վերջին էջին
+pdfjs-last-page-button-label = Անցնել վերջին էջին
+pdfjs-page-rotate-cw-button =
+ .title = Պտտել ըստ ժամացույցի սլաքի
+pdfjs-page-rotate-cw-button-label = Պտտել ըստ ժամացույցի սլաքի
+pdfjs-page-rotate-ccw-button =
+ .title = Պտտել հակառակ ժամացույցի սլաքի
+pdfjs-page-rotate-ccw-button-label = Պտտել հակառակ ժամացույցի սլաքի
+pdfjs-cursor-text-select-tool-button =
+ .title = Միացնել գրույթ ընտրելու գործիքը
+pdfjs-cursor-text-select-tool-button-label = Գրույթը ընտրելու գործիք
+pdfjs-cursor-hand-tool-button =
+ .title = Միացնել Ձեռքի գործիքը
+pdfjs-cursor-hand-tool-button-label = Ձեռքի գործիք
+pdfjs-scroll-vertical-button =
+ .title = Օգտագործել ուղղահայաց ոլորում
+pdfjs-scroll-vertical-button-label = Ուղղահայաց ոլորում
+pdfjs-scroll-horizontal-button =
+ .title = Օգտագործել հորիզոնական ոլորում
+pdfjs-scroll-horizontal-button-label = Հորիզոնական ոլորում
+pdfjs-scroll-wrapped-button =
+ .title = Օգտագործել փաթաթված ոլորում
+pdfjs-scroll-wrapped-button-label = Փաթաթված ոլորում
+pdfjs-spread-none-button =
+ .title = Մի միացեք էջի վերածածկերին
+pdfjs-spread-none-button-label = Չկա վերածածկեր
+pdfjs-spread-odd-button =
+ .title = Միացեք էջի վերածածկերին սկսելով՝ կենտ համարակալված էջերով
+pdfjs-spread-odd-button-label = Կենտ վերածածկեր
+pdfjs-spread-even-button =
+ .title = Միացեք էջի վերածածկերին սկսելով՝ զույգ համարակալված էջերով
+pdfjs-spread-even-button-label = Զույգ վերածածկեր
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Փաստաթղթի հատկությունները…
+pdfjs-document-properties-button-label = Փաստաթղթի հատկությունները…
+pdfjs-document-properties-file-name = Նիշքի անունը.
+pdfjs-document-properties-file-size = Նիշք չափը.
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } ԿԲ ({ $size_b } բայթ)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } ՄԲ ({ $size_b } բայթ)
+pdfjs-document-properties-title = Վերնագիր.
+pdfjs-document-properties-author = Հեղինակ․
+pdfjs-document-properties-subject = Վերնագիր.
+pdfjs-document-properties-keywords = Հիմնաբառ.
+pdfjs-document-properties-creation-date = Ստեղծելու ամսաթիվը.
+pdfjs-document-properties-modification-date = Փոփոխելու ամսաթիվը.
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Ստեղծող.
+pdfjs-document-properties-producer = PDF-ի հեղինակը.
+pdfjs-document-properties-version = PDF-ի տարբերակը.
+pdfjs-document-properties-page-count = Էջերի քանակը.
+pdfjs-document-properties-page-size = Էջի չափը.
+pdfjs-document-properties-page-size-unit-inches = ում
+pdfjs-document-properties-page-size-unit-millimeters = մմ
+pdfjs-document-properties-page-size-orientation-portrait = ուղղաձիգ
+pdfjs-document-properties-page-size-orientation-landscape = հորիզոնական
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Նամակ
+pdfjs-document-properties-page-size-name-legal = Օրինական
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Արագ վեբ դիտում․
+pdfjs-document-properties-linearized-yes = Այո
+pdfjs-document-properties-linearized-no = Ոչ
+pdfjs-document-properties-close-button = Փակել
+
+## Print
+
+pdfjs-print-progress-message = Նախապատրաստում է փաստաթուղթը տպելուն...
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Չեղարկել
+pdfjs-printing-not-supported = Զգուշացում. Տպելը ամբողջությամբ չի աջակցվում դիտարկիչի կողմից։
+pdfjs-printing-not-ready = Զգուշացում. PDF-ը ամբողջությամբ չի բեռնավորվել տպելու համար:
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Բացել/Փակել Կողային վահանակը
+pdfjs-toggle-sidebar-button-label = Բացել/Փակել Կողային վահանակը
+pdfjs-document-outline-button =
+ .title = Ցուցադրել փաստաթղթի ուրվագիծը (կրկնակի սեղմեք՝ միավորները ընդարձակելու/կոծկելու համար)
+pdfjs-document-outline-button-label = Փաստաթղթի բովանդակությունը
+pdfjs-attachments-button =
+ .title = Ցուցադրել կցորդները
+pdfjs-attachments-button-label = Կցորդներ
+pdfjs-thumbs-button =
+ .title = Ցուցադրել Մանրապատկերը
+pdfjs-thumbs-button-label = Մանրապատկերը
+pdfjs-findbar-button =
+ .title = Գտնել փաստաթղթում
+pdfjs-findbar-button-label = Որոնում
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Էջը { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Էջի մանրապատկերը { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Որոնում
+ .placeholder = Գտնել փաստաթղթում...
+pdfjs-find-previous-button =
+ .title = Գտնել անրահայտության նախորդ հանդիպումը
+pdfjs-find-previous-button-label = Նախորդը
+pdfjs-find-next-button =
+ .title = Գտիր արտահայտության հաջորդ հանդիպումը
+pdfjs-find-next-button-label = Հաջորդը
+pdfjs-find-highlight-checkbox = Գունանշել բոլորը
+pdfjs-find-match-case-checkbox-label = Մեծ(փոքր)ատառ հաշվի առնել
+pdfjs-find-entire-word-checkbox-label = Ամբողջ բառերը
+pdfjs-find-reached-top = Հասել եք փաստաթղթի վերևին, կշարունակվի ներքևից
+pdfjs-find-reached-bottom = Հասել եք փաստաթղթի վերջին, կշարունակվի վերևից
+pdfjs-find-not-found = Արտահայտությունը չգտնվեց
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Էջի լայնքը
+pdfjs-page-scale-fit = Ձգել էջը
+pdfjs-page-scale-auto = Ինքնաշխատ
+pdfjs-page-scale-actual = Իրական չափը
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Սխալ՝ PDF ֆայլը բացելիս։
+pdfjs-invalid-file-error = Սխալ կամ վնասված PDF ֆայլ:
+pdfjs-missing-file-error = PDF ֆայլը բացակայում է:
+pdfjs-unexpected-response-error = Սպասարկիչի անսպասելի պատասխան:
+pdfjs-rendering-error = Սխալ՝ էջը ստեղծելիս:
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Ծանոթություն]
+
+## Password
+
+pdfjs-password-label = Մուտքագրեք PDF-ի գաղտնաբառը:
+pdfjs-password-invalid = Գաղտնաբառը սխալ է: Կրկին փորձեք:
+pdfjs-password-ok-button = Լավ
+pdfjs-password-cancel-button = Չեղարկել
+pdfjs-web-fonts-disabled = Վեբ-տառատեսակները անջատված են. հնարավոր չէ օգտագործել ներկառուցված PDF տառատեսակները:
+
+## Editing
+
+
+## Remove button for the various kind of editor.
+
+
+##
+
+pdfjs-free-text-default-content = Սկսել մուտքագրումը…
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+
+## Color picker
+
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Ցուցադրել բոլորը
+pdfjs-editor-highlight-show-all-button =
+ .title = Ցուցադրել բոլորը
diff --git a/web/locale/hye/viewer.ftl b/web/locale/hye/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..75cdc06431cbdc1fc7a475ea1fc05a55ea0fedb6
--- /dev/null
+++ b/web/locale/hye/viewer.ftl
@@ -0,0 +1,268 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Նախորդ էջ
+pdfjs-previous-button-label = Նախորդը
+pdfjs-next-button =
+ .title = Յաջորդ էջ
+pdfjs-next-button-label = Յաջորդը
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = էջ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount }-ից
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber }-ը { $pagesCount })-ից
+pdfjs-zoom-out-button =
+ .title = Փոքրացնել
+pdfjs-zoom-out-button-label = Փոքրացնել
+pdfjs-zoom-in-button =
+ .title = Խոշորացնել
+pdfjs-zoom-in-button-label = Խոշորացնել
+pdfjs-zoom-select =
+ .title = Խոշորացում
+pdfjs-presentation-mode-button =
+ .title = Անցնել ներկայացման եղանակին
+pdfjs-presentation-mode-button-label = Ներկայացման եղանակ
+pdfjs-open-file-button =
+ .title = Բացել նիշքը
+pdfjs-open-file-button-label = Բացել
+pdfjs-print-button =
+ .title = Տպել
+pdfjs-print-button-label = Տպել
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Գործիքներ
+pdfjs-tools-button-label = Գործիքներ
+pdfjs-first-page-button =
+ .title = Գնալ դէպի առաջին էջ
+pdfjs-first-page-button-label = Գնալ դէպի առաջին էջ
+pdfjs-last-page-button =
+ .title = Գնալ դէպի վերջին էջ
+pdfjs-last-page-button-label = Գնալ դէպի վերջին էջ
+pdfjs-page-rotate-cw-button =
+ .title = Պտտել ժամացոյցի սլաքի ուղղութեամբ
+pdfjs-page-rotate-cw-button-label = Պտտել ժամացոյցի սլաքի ուղղութեամբ
+pdfjs-page-rotate-ccw-button =
+ .title = Պտտել ժամացոյցի սլաքի հակառակ ուղղութեամբ
+pdfjs-page-rotate-ccw-button-label = Պտտել ժամացոյցի սլաքի հակառակ ուղղութեամբ
+pdfjs-cursor-text-select-tool-button =
+ .title = Միացնել գրոյթ ընտրելու գործիքը
+pdfjs-cursor-text-select-tool-button-label = Գրուածք ընտրելու գործիք
+pdfjs-cursor-hand-tool-button =
+ .title = Միացնել ձեռքի գործիքը
+pdfjs-cursor-hand-tool-button-label = Ձեռքի գործիք
+pdfjs-scroll-page-button =
+ .title = Աւգտագործել էջի ոլորում
+pdfjs-scroll-page-button-label = Էջի ոլորում
+pdfjs-scroll-vertical-button =
+ .title = Աւգտագործել ուղղահայեաց ոլորում
+pdfjs-scroll-vertical-button-label = Ուղղահայեաց ոլորում
+pdfjs-scroll-horizontal-button =
+ .title = Աւգտագործել հորիզոնական ոլորում
+pdfjs-scroll-horizontal-button-label = Հորիզոնական ոլորում
+pdfjs-scroll-wrapped-button =
+ .title = Աւգտագործել փաթաթուած ոլորում
+pdfjs-scroll-wrapped-button-label = Փաթաթուած ոլորում
+pdfjs-spread-none-button =
+ .title = Մի միացէք էջի կոնտեքստում
+pdfjs-spread-none-button-label = Չկայ կոնտեքստ
+pdfjs-spread-odd-button =
+ .title = Միացէք էջի կոնտեքստին սկսելով՝ կենտ համարակալուած էջերով
+pdfjs-spread-odd-button-label = Տարաւրինակ կոնտեքստ
+pdfjs-spread-even-button =
+ .title = Միացէք էջի կոնտեքստին սկսելով՝ զոյգ համարակալուած էջերով
+pdfjs-spread-even-button-label = Հաւասար վերածածկեր
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Փաստաթղթի հատկութիւնները…
+pdfjs-document-properties-button-label = Փաստաթղթի յատկութիւնները…
+pdfjs-document-properties-file-name = Նիշքի անունը․
+pdfjs-document-properties-file-size = Նիշք չափը.
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } ԿԲ ({ $size_b } բայթ)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } ՄԲ ({ $size_b } բայթ)
+pdfjs-document-properties-title = Վերնագիր
+pdfjs-document-properties-author = Հեղինակ․
+pdfjs-document-properties-subject = առարկայ
+pdfjs-document-properties-keywords = Հիմնաբառեր
+pdfjs-document-properties-creation-date = Ստեղծման ամսաթիւ
+pdfjs-document-properties-modification-date = Փոփոխութեան ամսաթիւ.
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Ստեղծող
+pdfjs-document-properties-producer = PDF-ի Արտադրողը.
+pdfjs-document-properties-version = PDF-ի տարբերակը.
+pdfjs-document-properties-page-count = Էջերի քանակը.
+pdfjs-document-properties-page-size = Էջի չափը.
+pdfjs-document-properties-page-size-unit-inches = ում
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = ուղղաձիգ
+pdfjs-document-properties-page-size-orientation-landscape = հորիզոնական
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Նամակ
+pdfjs-document-properties-page-size-name-legal = Աւրինական
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Արագ վեբ դիտում․
+pdfjs-document-properties-linearized-yes = Այո
+pdfjs-document-properties-linearized-no = Ոչ
+pdfjs-document-properties-close-button = Փակել
+
+## Print
+
+pdfjs-print-progress-message = Նախապատրաստում է փաստաթուղթը տպելուն…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Չեղարկել
+pdfjs-printing-not-supported = Զգուշացում. Տպելը ամբողջութեամբ չի աջակցուում զննարկիչի կողմից։
+pdfjs-printing-not-ready = Զգուշացում. PDF֊ը ամբողջութեամբ չի բեռնաւորուել տպելու համար։
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Փոխարկել կողային վահանակը
+pdfjs-toggle-sidebar-notification-button =
+ .title = Փոխանջատել կողմնասիւնը (փաստաթուղթը պարունակում է ուրուագիծ/կցորդներ/շերտեր)
+pdfjs-toggle-sidebar-button-label = Փոխարկել կողային վահանակը
+pdfjs-document-outline-button =
+ .title = Ցուցադրել փաստաթղթի ուրուագիծը (կրկնակի սեղմէք՝ միաւորները ընդարձակելու/կոծկելու համար)
+pdfjs-document-outline-button-label = Փաստաթղթի ուրուագիծ
+pdfjs-attachments-button =
+ .title = Ցուցադրել կցորդները
+pdfjs-attachments-button-label = Կցորդներ
+pdfjs-layers-button =
+ .title = Ցուցադրել շերտերը (կրկնահպել վերակայելու բոլոր շերտերը սկզբնադիր վիճակի)
+pdfjs-layers-button-label = Շերտեր
+pdfjs-thumbs-button =
+ .title = Ցուցադրել մանրապատկերը
+pdfjs-thumbs-button-label = Մանրապատկեր
+pdfjs-current-outline-item-button =
+ .title = Գտէք ընթացիկ գծագրման տարրը
+pdfjs-current-outline-item-button-label = Ընթացիկ գծագրման տարր
+pdfjs-findbar-button =
+ .title = Գտնել փաստաթղթում
+pdfjs-findbar-button-label = Որոնում
+pdfjs-additional-layers = Լրացուցիչ շերտեր
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Էջը { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Էջի մանրապատկերը { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Որոնում
+ .placeholder = Գտնել փաստաթղթում…
+pdfjs-find-previous-button =
+ .title = Գտնել արտայայտութեան նախորդ արտայայտութիւնը
+pdfjs-find-previous-button-label = Նախորդը
+pdfjs-find-next-button =
+ .title = Գտիր արտայայտութեան յաջորդ արտայայտութիւնը
+pdfjs-find-next-button-label = Հաջորդը
+pdfjs-find-highlight-checkbox = Գունանշել բոլորը
+pdfjs-find-match-case-checkbox-label = Հաշուի առնել հանգամանքը
+pdfjs-find-match-diacritics-checkbox-label = Հնչիւնատարբերիչ նշանների համապատասխանեցում
+pdfjs-find-entire-word-checkbox-label = Ամբողջ բառերը
+pdfjs-find-reached-top = Հասել եք փաստաթղթի վերեւին,շարունակել ներքեւից
+pdfjs-find-reached-bottom = Հասել էք փաստաթղթի վերջին, շարունակել վերեւից
+pdfjs-find-not-found = Արտայայտութիւնը չգտնուեց
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Էջի լայնութիւն
+pdfjs-page-scale-fit = Հարմարեցնել էջը
+pdfjs-page-scale-auto = Ինքնաշխատ խոշորացում
+pdfjs-page-scale-actual = Իրական չափը
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Էջ { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF նիշքը բացելիս սխալ է տեղի ունեցել։
+pdfjs-invalid-file-error = Սխալ կամ վնասուած PDF նիշք։
+pdfjs-missing-file-error = PDF նիշքը բացակաիւմ է։
+pdfjs-unexpected-response-error = Սպասարկիչի անսպասելի պատասխան։
+pdfjs-rendering-error = Սխալ է տեղի ունեցել էջի մեկնաբանման ժամանակ
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Ծանոթութիւն]
+
+## Password
+
+pdfjs-password-label = Մուտքագրէք գաղտնաբառը այս PDF նիշքը բացելու համար
+pdfjs-password-invalid = Գաղտնաբառը սխալ է: Կրկին փորձէք:
+pdfjs-password-ok-button = Լաւ
+pdfjs-password-cancel-button = Չեղարկել
+pdfjs-web-fonts-disabled = Վեբ-տառատեսակները անջատուած են. հնարաւոր չէ աւգտագործել ներկառուցուած PDF տառատեսակները։
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ia/viewer.ftl b/web/locale/ia/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..4cddfa28140345a6f26f2350ef937b9c0ba37cfc
--- /dev/null
+++ b/web/locale/ia/viewer.ftl
@@ -0,0 +1,394 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pagina previe
+pdfjs-previous-button-label = Previe
+pdfjs-next-button =
+ .title = Pagina sequente
+pdfjs-next-button-label = Sequente
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pagina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Distantiar
+pdfjs-zoom-out-button-label = Distantiar
+pdfjs-zoom-in-button =
+ .title = Approximar
+pdfjs-zoom-in-button-label = Approximar
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Excambiar a modo presentation
+pdfjs-presentation-mode-button-label = Modo presentation
+pdfjs-open-file-button =
+ .title = Aperir le file
+pdfjs-open-file-button-label = Aperir
+pdfjs-print-button =
+ .title = Imprimer
+pdfjs-print-button-label = Imprimer
+pdfjs-save-button =
+ .title = Salvar
+pdfjs-save-button-label = Salvar
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Discargar
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Discargar
+pdfjs-bookmark-button =
+ .title = Pagina actual (vide le URL del pagina actual)
+pdfjs-bookmark-button-label = Pagina actual
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Instrumentos
+pdfjs-tools-button-label = Instrumentos
+pdfjs-first-page-button =
+ .title = Ir al prime pagina
+pdfjs-first-page-button-label = Ir al prime pagina
+pdfjs-last-page-button =
+ .title = Ir al ultime pagina
+pdfjs-last-page-button-label = Ir al ultime pagina
+pdfjs-page-rotate-cw-button =
+ .title = Rotar in senso horari
+pdfjs-page-rotate-cw-button-label = Rotar in senso horari
+pdfjs-page-rotate-ccw-button =
+ .title = Rotar in senso antihorari
+pdfjs-page-rotate-ccw-button-label = Rotar in senso antihorari
+pdfjs-cursor-text-select-tool-button =
+ .title = Activar le instrumento de selection de texto
+pdfjs-cursor-text-select-tool-button-label = Instrumento de selection de texto
+pdfjs-cursor-hand-tool-button =
+ .title = Activar le instrumento mano
+pdfjs-cursor-hand-tool-button-label = Instrumento mano
+pdfjs-scroll-page-button =
+ .title = Usar rolamento de pagina
+pdfjs-scroll-page-button-label = Rolamento de pagina
+pdfjs-scroll-vertical-button =
+ .title = Usar rolamento vertical
+pdfjs-scroll-vertical-button-label = Rolamento vertical
+pdfjs-scroll-horizontal-button =
+ .title = Usar rolamento horizontal
+pdfjs-scroll-horizontal-button-label = Rolamento horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Usar rolamento incapsulate
+pdfjs-scroll-wrapped-button-label = Rolamento incapsulate
+pdfjs-spread-none-button =
+ .title = Non junger paginas dual
+pdfjs-spread-none-button-label = Sin paginas dual
+pdfjs-spread-odd-button =
+ .title = Junger paginas dual a partir de paginas con numeros impar
+pdfjs-spread-odd-button-label = Paginas dual impar
+pdfjs-spread-even-button =
+ .title = Junger paginas dual a partir de paginas con numeros par
+pdfjs-spread-even-button-label = Paginas dual par
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Proprietates del documento…
+pdfjs-document-properties-button-label = Proprietates del documento…
+pdfjs-document-properties-file-name = Nomine del file:
+pdfjs-document-properties-file-size = Dimension de file:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Titulo:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Subjecto:
+pdfjs-document-properties-keywords = Parolas clave:
+pdfjs-document-properties-creation-date = Data de creation:
+pdfjs-document-properties-modification-date = Data de modification:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creator:
+pdfjs-document-properties-producer = Productor PDF:
+pdfjs-document-properties-version = Version PDF:
+pdfjs-document-properties-page-count = Numero de paginas:
+pdfjs-document-properties-page-size = Dimension del pagina:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertical
+pdfjs-document-properties-page-size-orientation-landscape = horizontal
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Littera
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista web rapide:
+pdfjs-document-properties-linearized-yes = Si
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Clauder
+
+## Print
+
+pdfjs-print-progress-message = Preparation del documento pro le impression…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancellar
+pdfjs-printing-not-supported = Attention : le impression non es totalmente supportate per ce navigator.
+pdfjs-printing-not-ready = Attention: le file PDF non es integremente cargate pro lo poter imprimer.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Monstrar/celar le barra lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Monstrar/celar le barra lateral (le documento contine structura/attachamentos/stratos)
+pdfjs-toggle-sidebar-button-label = Monstrar/celar le barra lateral
+pdfjs-document-outline-button =
+ .title = Monstrar le schema del documento (clic duple pro expander/contraher tote le elementos)
+pdfjs-document-outline-button-label = Schema del documento
+pdfjs-attachments-button =
+ .title = Monstrar le annexos
+pdfjs-attachments-button-label = Annexos
+pdfjs-layers-button =
+ .title = Monstrar stratos (clicca duple pro remontar tote le stratos al stato predefinite)
+pdfjs-layers-button-label = Stratos
+pdfjs-thumbs-button =
+ .title = Monstrar le vignettes
+pdfjs-thumbs-button-label = Vignettes
+pdfjs-current-outline-item-button =
+ .title = Trovar le elemento de structura actual
+pdfjs-current-outline-item-button-label = Elemento de structura actual
+pdfjs-findbar-button =
+ .title = Cercar in le documento
+pdfjs-findbar-button-label = Cercar
+pdfjs-additional-layers = Altere stratos
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pagina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Vignette del pagina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Cercar
+ .placeholder = Cercar in le documento…
+pdfjs-find-previous-button =
+ .title = Trovar le previe occurrentia del phrase
+pdfjs-find-previous-button-label = Previe
+pdfjs-find-next-button =
+ .title = Trovar le successive occurrentia del phrase
+pdfjs-find-next-button-label = Sequente
+pdfjs-find-highlight-checkbox = Evidentiar toto
+pdfjs-find-match-case-checkbox-label = Distinguer majusculas/minusculas
+pdfjs-find-match-diacritics-checkbox-label = Differentiar diacriticos
+pdfjs-find-entire-word-checkbox-label = Parolas integre
+pdfjs-find-reached-top = Initio del documento attingite, continuation ab fin
+pdfjs-find-reached-bottom = Fin del documento attingite, continuation ab initio
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } de { $total } correspondentia
+ *[other] { $current } de { $total } correspondentias
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Plus de { $limit } correspondentia
+ *[other] Plus de { $limit } correspondentias
+ }
+pdfjs-find-not-found = Phrase non trovate
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Plen largor del pagina
+pdfjs-page-scale-fit = Pagina integre
+pdfjs-page-scale-auto = Zoom automatic
+pdfjs-page-scale-actual = Dimension real
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Pagina { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Un error occurreva durante que on cargava le file PDF.
+pdfjs-invalid-file-error = File PDF corrumpite o non valide.
+pdfjs-missing-file-error = File PDF mancante.
+pdfjs-unexpected-response-error = Responsa del servitor inexpectate.
+pdfjs-rendering-error = Un error occurreva durante que on processava le pagina.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = Insere le contrasigno pro aperir iste file PDF.
+pdfjs-password-invalid = Contrasigno invalide. Per favor retenta.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Cancellar
+pdfjs-web-fonts-disabled = Le typos de litteras web es disactivate: impossibile usar le typos de litteras PDF incorporate.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texto
+pdfjs-editor-free-text-button-label = Texto
+pdfjs-editor-ink-button =
+ .title = Designar
+pdfjs-editor-ink-button-label = Designar
+pdfjs-editor-stamp-button =
+ .title = Adder o rediger imagines
+pdfjs-editor-stamp-button-label = Adder o rediger imagines
+pdfjs-editor-highlight-button =
+ .title = Evidentia
+pdfjs-editor-highlight-button-label = Evidentia
+pdfjs-highlight-floating-button1 =
+ .title = Evidentiar
+ .aria-label = Evidentiar
+pdfjs-highlight-floating-button-label = Evidentiar
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Remover le designo
+pdfjs-editor-remove-freetext-button =
+ .title = Remover texto
+pdfjs-editor-remove-stamp-button =
+ .title = Remover imagine
+pdfjs-editor-remove-highlight-button =
+ .title = Remover evidentia
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Color
+pdfjs-editor-free-text-size-input = Dimension
+pdfjs-editor-ink-color-input = Color
+pdfjs-editor-ink-thickness-input = Spissor
+pdfjs-editor-ink-opacity-input = Opacitate
+pdfjs-editor-stamp-add-image-button =
+ .title = Adder imagine
+pdfjs-editor-stamp-add-image-button-label = Adder imagine
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Spissor
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Cambiar spissor evidentiante elementos differente de texto
+pdfjs-free-text =
+ .aria-label = Editor de texto
+pdfjs-free-text-default-content = Comenciar a scriber…
+pdfjs-ink =
+ .aria-label = Editor de designos
+pdfjs-ink-canvas =
+ .aria-label = Imagine create per le usator
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Texto alternative
+pdfjs-editor-alt-text-edit-button-label = Rediger texto alternative
+pdfjs-editor-alt-text-dialog-label = Elige un option
+pdfjs-editor-alt-text-dialog-description = Le texto alternative (alt text) adjuta quando le personas non pote vider le imagine o quando illo non carga.
+pdfjs-editor-alt-text-add-description-label = Adder un description
+pdfjs-editor-alt-text-add-description-description = Mira a 1-2 phrases que describe le subjecto, parametro, o actiones.
+pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorative
+pdfjs-editor-alt-text-mark-decorative-description = Isto es usate pro imagines ornamental, como bordaturas o filigranas.
+pdfjs-editor-alt-text-cancel-button = Cancellar
+pdfjs-editor-alt-text-save-button = Salvar
+pdfjs-editor-alt-text-decorative-tooltip = Marcate como decorative
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Per exemplo, “Un juvene sede a un tabula pro mangiar un repasto”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Angulo superior sinistre — redimensionar
+pdfjs-editor-resizer-label-top-middle = Medio superior — redimensionar
+pdfjs-editor-resizer-label-top-right = Angulo superior dextre — redimensionar
+pdfjs-editor-resizer-label-middle-right = Medio dextre — redimensionar
+pdfjs-editor-resizer-label-bottom-right = Angulo inferior dextre — redimensionar
+pdfjs-editor-resizer-label-bottom-middle = Medio inferior — redimensionar
+pdfjs-editor-resizer-label-bottom-left = Angulo inferior sinistre — redimensionar
+pdfjs-editor-resizer-label-middle-left = Medio sinistre — redimensionar
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Color pro evidentiar
+pdfjs-editor-colorpicker-button =
+ .title = Cambiar color
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Electiones del color
+pdfjs-editor-colorpicker-yellow =
+ .title = Jalne
+pdfjs-editor-colorpicker-green =
+ .title = Verde
+pdfjs-editor-colorpicker-blue =
+ .title = Blau
+pdfjs-editor-colorpicker-pink =
+ .title = Rosate
+pdfjs-editor-colorpicker-red =
+ .title = Rubie
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Monstrar toto
+pdfjs-editor-highlight-show-all-button =
+ .title = Monstrar toto
diff --git a/web/locale/id/viewer.ftl b/web/locale/id/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..fee8d18bfcaf940f58feebe8c0b3f2b560504301
--- /dev/null
+++ b/web/locale/id/viewer.ftl
@@ -0,0 +1,293 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Laman Sebelumnya
+pdfjs-previous-button-label = Sebelumnya
+pdfjs-next-button =
+ .title = Laman Selanjutnya
+pdfjs-next-button-label = Selanjutnya
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Halaman
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = dari { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } dari { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Perkecil
+pdfjs-zoom-out-button-label = Perkecil
+pdfjs-zoom-in-button =
+ .title = Perbesar
+pdfjs-zoom-in-button-label = Perbesar
+pdfjs-zoom-select =
+ .title = Perbesaran
+pdfjs-presentation-mode-button =
+ .title = Ganti ke Mode Presentasi
+pdfjs-presentation-mode-button-label = Mode Presentasi
+pdfjs-open-file-button =
+ .title = Buka Berkas
+pdfjs-open-file-button-label = Buka
+pdfjs-print-button =
+ .title = Cetak
+pdfjs-print-button-label = Cetak
+pdfjs-save-button =
+ .title = Simpan
+pdfjs-save-button-label = Simpan
+pdfjs-bookmark-button =
+ .title = Laman Saat Ini (Lihat URL dari Laman Sekarang)
+pdfjs-bookmark-button-label = Laman Saat Ini
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Alat
+pdfjs-tools-button-label = Alat
+pdfjs-first-page-button =
+ .title = Buka Halaman Pertama
+pdfjs-first-page-button-label = Buka Halaman Pertama
+pdfjs-last-page-button =
+ .title = Buka Halaman Terakhir
+pdfjs-last-page-button-label = Buka Halaman Terakhir
+pdfjs-page-rotate-cw-button =
+ .title = Putar Searah Jarum Jam
+pdfjs-page-rotate-cw-button-label = Putar Searah Jarum Jam
+pdfjs-page-rotate-ccw-button =
+ .title = Putar Berlawanan Arah Jarum Jam
+pdfjs-page-rotate-ccw-button-label = Putar Berlawanan Arah Jarum Jam
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktifkan Alat Seleksi Teks
+pdfjs-cursor-text-select-tool-button-label = Alat Seleksi Teks
+pdfjs-cursor-hand-tool-button =
+ .title = Aktifkan Alat Tangan
+pdfjs-cursor-hand-tool-button-label = Alat Tangan
+pdfjs-scroll-page-button =
+ .title = Gunakan Pengguliran Laman
+pdfjs-scroll-page-button-label = Pengguliran Laman
+pdfjs-scroll-vertical-button =
+ .title = Gunakan Penggeseran Vertikal
+pdfjs-scroll-vertical-button-label = Penggeseran Vertikal
+pdfjs-scroll-horizontal-button =
+ .title = Gunakan Penggeseran Horizontal
+pdfjs-scroll-horizontal-button-label = Penggeseran Horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Gunakan Penggeseran Terapit
+pdfjs-scroll-wrapped-button-label = Penggeseran Terapit
+pdfjs-spread-none-button =
+ .title = Jangan gabungkan lembar halaman
+pdfjs-spread-none-button-label = Tidak Ada Lembaran
+pdfjs-spread-odd-button =
+ .title = Gabungkan lembar lamanan mulai dengan halaman ganjil
+pdfjs-spread-odd-button-label = Lembaran Ganjil
+pdfjs-spread-even-button =
+ .title = Gabungkan lembar halaman dimulai dengan halaman genap
+pdfjs-spread-even-button-label = Lembaran Genap
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Properti Dokumen…
+pdfjs-document-properties-button-label = Properti Dokumen…
+pdfjs-document-properties-file-name = Nama berkas:
+pdfjs-document-properties-file-size = Ukuran berkas:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } byte)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte)
+pdfjs-document-properties-title = Judul:
+pdfjs-document-properties-author = Penyusun:
+pdfjs-document-properties-subject = Subjek:
+pdfjs-document-properties-keywords = Kata Kunci:
+pdfjs-document-properties-creation-date = Tanggal Dibuat:
+pdfjs-document-properties-modification-date = Tanggal Dimodifikasi:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Pembuat:
+pdfjs-document-properties-producer = Pemroduksi PDF:
+pdfjs-document-properties-version = Versi PDF:
+pdfjs-document-properties-page-count = Jumlah Halaman:
+pdfjs-document-properties-page-size = Ukuran Laman:
+pdfjs-document-properties-page-size-unit-inches = inci
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = tegak
+pdfjs-document-properties-page-size-orientation-landscape = mendatar
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Tampilan Web Kilat:
+pdfjs-document-properties-linearized-yes = Ya
+pdfjs-document-properties-linearized-no = Tidak
+pdfjs-document-properties-close-button = Tutup
+
+## Print
+
+pdfjs-print-progress-message = Menyiapkan dokumen untuk pencetakan…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Batalkan
+pdfjs-printing-not-supported = Peringatan: Pencetakan tidak didukung secara lengkap pada peramban ini.
+pdfjs-printing-not-ready = Peringatan: Berkas PDF masih belum dimuat secara lengkap untuk dapat dicetak.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Aktif/Nonaktifkan Bilah Samping
+pdfjs-toggle-sidebar-notification-button =
+ .title = Aktif/Nonaktifkan Bilah Samping (dokumen berisi kerangka/lampiran/lapisan)
+pdfjs-toggle-sidebar-button-label = Aktif/Nonaktifkan Bilah Samping
+pdfjs-document-outline-button =
+ .title = Tampilkan Kerangka Dokumen (klik ganda untuk membentangkan/menciutkan semua item)
+pdfjs-document-outline-button-label = Kerangka Dokumen
+pdfjs-attachments-button =
+ .title = Tampilkan Lampiran
+pdfjs-attachments-button-label = Lampiran
+pdfjs-layers-button =
+ .title = Tampilkan Lapisan (klik ganda untuk mengatur ulang semua lapisan ke keadaan baku)
+pdfjs-layers-button-label = Lapisan
+pdfjs-thumbs-button =
+ .title = Tampilkan Miniatur
+pdfjs-thumbs-button-label = Miniatur
+pdfjs-current-outline-item-button =
+ .title = Cari Butir Ikhtisar Saat Ini
+pdfjs-current-outline-item-button-label = Butir Ikhtisar Saat Ini
+pdfjs-findbar-button =
+ .title = Temukan di Dokumen
+pdfjs-findbar-button-label = Temukan
+pdfjs-additional-layers = Lapisan Tambahan
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Laman { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatur Laman { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Temukan
+ .placeholder = Temukan di dokumen…
+pdfjs-find-previous-button =
+ .title = Temukan kata sebelumnya
+pdfjs-find-previous-button-label = Sebelumnya
+pdfjs-find-next-button =
+ .title = Temukan lebih lanjut
+pdfjs-find-next-button-label = Selanjutnya
+pdfjs-find-highlight-checkbox = Sorot semuanya
+pdfjs-find-match-case-checkbox-label = Cocokkan BESAR/kecil
+pdfjs-find-match-diacritics-checkbox-label = Pencocokan Diakritik
+pdfjs-find-entire-word-checkbox-label = Seluruh teks
+pdfjs-find-reached-top = Sampai di awal dokumen, dilanjutkan dari bawah
+pdfjs-find-reached-bottom = Sampai di akhir dokumen, dilanjutkan dari atas
+pdfjs-find-not-found = Frasa tidak ditemukan
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Lebar Laman
+pdfjs-page-scale-fit = Muat Laman
+pdfjs-page-scale-auto = Perbesaran Otomatis
+pdfjs-page-scale-actual = Ukuran Asli
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Halaman { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Galat terjadi saat memuat PDF.
+pdfjs-invalid-file-error = Berkas PDF tidak valid atau rusak.
+pdfjs-missing-file-error = Berkas PDF tidak ada.
+pdfjs-unexpected-response-error = Balasan server yang tidak diharapkan.
+pdfjs-rendering-error = Galat terjadi saat merender laman.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotasi { $type }]
+
+## Password
+
+pdfjs-password-label = Masukkan sandi untuk membuka berkas PDF ini.
+pdfjs-password-invalid = Sandi tidak valid. Silakan coba lagi.
+pdfjs-password-ok-button = Oke
+pdfjs-password-cancel-button = Batal
+pdfjs-web-fonts-disabled = Font web dinonaktifkan: tidak dapat menggunakan font PDF yang tersemat.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Teks
+pdfjs-editor-free-text-button-label = Teks
+pdfjs-editor-ink-button =
+ .title = Gambar
+pdfjs-editor-ink-button-label = Gambar
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Warna
+pdfjs-editor-free-text-size-input = Ukuran
+pdfjs-editor-ink-color-input = Warna
+pdfjs-editor-ink-thickness-input = Ketebalan
+pdfjs-editor-ink-opacity-input = Opasitas
+pdfjs-free-text =
+ .aria-label = Editor Teks
+pdfjs-free-text-default-content = Mulai mengetik…
+pdfjs-ink =
+ .aria-label = Editor Gambar
+pdfjs-ink-canvas =
+ .aria-label = Gambar yang dibuat pengguna
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/is/viewer.ftl b/web/locale/is/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..d3afef3eae02b2c478ebc75235f81838ee456a99
--- /dev/null
+++ b/web/locale/is/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Fyrri síða
+pdfjs-previous-button-label = Fyrri
+pdfjs-next-button =
+ .title = Næsta síða
+pdfjs-next-button-label = Næsti
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Síða
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = af { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } af { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Minnka aðdrátt
+pdfjs-zoom-out-button-label = Minnka aðdrátt
+pdfjs-zoom-in-button =
+ .title = Auka aðdrátt
+pdfjs-zoom-in-button-label = Auka aðdrátt
+pdfjs-zoom-select =
+ .title = Aðdráttur
+pdfjs-presentation-mode-button =
+ .title = Skipta yfir á kynningarham
+pdfjs-presentation-mode-button-label = Kynningarhamur
+pdfjs-open-file-button =
+ .title = Opna skrá
+pdfjs-open-file-button-label = Opna
+pdfjs-print-button =
+ .title = Prenta
+pdfjs-print-button-label = Prenta
+pdfjs-save-button =
+ .title = Vista
+pdfjs-save-button-label = Vista
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Sækja
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Sækja
+pdfjs-bookmark-button =
+ .title = Núverandi síða (Skoða vefslóð frá núverandi síðu)
+pdfjs-bookmark-button-label = Núverandi síða
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Opna í smáforriti
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Opna í smáforriti
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Verkfæri
+pdfjs-tools-button-label = Verkfæri
+pdfjs-first-page-button =
+ .title = Fara á fyrstu síðu
+pdfjs-first-page-button-label = Fara á fyrstu síðu
+pdfjs-last-page-button =
+ .title = Fara á síðustu síðu
+pdfjs-last-page-button-label = Fara á síðustu síðu
+pdfjs-page-rotate-cw-button =
+ .title = Snúa réttsælis
+pdfjs-page-rotate-cw-button-label = Snúa réttsælis
+pdfjs-page-rotate-ccw-button =
+ .title = Snúa rangsælis
+pdfjs-page-rotate-ccw-button-label = Snúa rangsælis
+pdfjs-cursor-text-select-tool-button =
+ .title = Virkja textavalsáhald
+pdfjs-cursor-text-select-tool-button-label = Textavalsáhald
+pdfjs-cursor-hand-tool-button =
+ .title = Virkja handarverkfæri
+pdfjs-cursor-hand-tool-button-label = Handarverkfæri
+pdfjs-scroll-page-button =
+ .title = Nota síðuskrun
+pdfjs-scroll-page-button-label = Síðuskrun
+pdfjs-scroll-vertical-button =
+ .title = Nota lóðrétt skrun
+pdfjs-scroll-vertical-button-label = Lóðrétt skrun
+pdfjs-scroll-horizontal-button =
+ .title = Nota lárétt skrun
+pdfjs-scroll-horizontal-button-label = Lárétt skrun
+pdfjs-scroll-wrapped-button =
+ .title = Nota línuskipt síðuskrun
+pdfjs-scroll-wrapped-button-label = Línuskipt síðuskrun
+pdfjs-spread-none-button =
+ .title = Ekki taka þátt í dreifingu síðna
+pdfjs-spread-none-button-label = Engin dreifing
+pdfjs-spread-odd-button =
+ .title = Taka þátt í dreifingu síðna með oddatölum
+pdfjs-spread-odd-button-label = Oddatöludreifing
+pdfjs-spread-even-button =
+ .title = Taktu þátt í dreifingu síðna með jöfnuntölum
+pdfjs-spread-even-button-label = Jafnatöludreifing
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Eiginleikar skjals…
+pdfjs-document-properties-button-label = Eiginleikar skjals…
+pdfjs-document-properties-file-name = Skráarnafn:
+pdfjs-document-properties-file-size = Skrárstærð:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Titill:
+pdfjs-document-properties-author = Hönnuður:
+pdfjs-document-properties-subject = Efni:
+pdfjs-document-properties-keywords = Stikkorð:
+pdfjs-document-properties-creation-date = Búið til:
+pdfjs-document-properties-modification-date = Dags breytingar:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Höfundur:
+pdfjs-document-properties-producer = PDF framleiðandi:
+pdfjs-document-properties-version = PDF útgáfa:
+pdfjs-document-properties-page-count = Blaðsíðufjöldi:
+pdfjs-document-properties-page-size = Stærð síðu:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = skammsnið
+pdfjs-document-properties-page-size-orientation-landscape = langsnið
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fljótleg vefskoðun:
+pdfjs-document-properties-linearized-yes = Já
+pdfjs-document-properties-linearized-no = Nei
+pdfjs-document-properties-close-button = Loka
+
+## Print
+
+pdfjs-print-progress-message = Undirbý skjal fyrir prentun…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Hætta við
+pdfjs-printing-not-supported = Aðvörun: Prentun er ekki með fyllilegan stuðning á þessum vafra.
+pdfjs-printing-not-ready = Aðvörun: Ekki er búið að hlaða inn allri PDF skránni fyrir prentun.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Víxla hliðarspjaldi af/á
+pdfjs-toggle-sidebar-notification-button =
+ .title = Víxla hliðarslá (skjal inniheldur yfirlit/viðhengi/lög)
+pdfjs-toggle-sidebar-button-label = Víxla hliðarspjaldi af/á
+pdfjs-document-outline-button =
+ .title = Sýna yfirlit skjals (tvísmelltu til að opna/loka öllum hlutum)
+pdfjs-document-outline-button-label = Efnisskipan skjals
+pdfjs-attachments-button =
+ .title = Sýna viðhengi
+pdfjs-attachments-button-label = Viðhengi
+pdfjs-layers-button =
+ .title = Birta lög (tvísmelltu til að endurstilla öll lög í sjálfgefna stöðu)
+pdfjs-layers-button-label = Lög
+pdfjs-thumbs-button =
+ .title = Sýna smámyndir
+pdfjs-thumbs-button-label = Smámyndir
+pdfjs-current-outline-item-button =
+ .title = Finna núverandi atriði efnisskipunar
+pdfjs-current-outline-item-button-label = Núverandi atriði efnisskipunar
+pdfjs-findbar-button =
+ .title = Leita í skjali
+pdfjs-findbar-button-label = Leita
+pdfjs-additional-layers = Viðbótarlög
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Síða { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Smámynd af síðu { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Leita
+ .placeholder = Leita í skjali…
+pdfjs-find-previous-button =
+ .title = Leita að fyrra tilfelli þessara orða
+pdfjs-find-previous-button-label = Fyrri
+pdfjs-find-next-button =
+ .title = Leita að næsta tilfelli þessara orða
+pdfjs-find-next-button-label = Næsti
+pdfjs-find-highlight-checkbox = Lita allt
+pdfjs-find-match-case-checkbox-label = Passa við stafstöðu
+pdfjs-find-match-diacritics-checkbox-label = Passa við broddstafi
+pdfjs-find-entire-word-checkbox-label = Heil orð
+pdfjs-find-reached-top = Náði efst í skjal, held áfram neðst
+pdfjs-find-reached-bottom = Náði enda skjals, held áfram efst
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } af { $total } passar við
+ *[other] { $current } af { $total } passa við
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Fleiri en { $limit } passar við
+ *[other] Fleiri en { $limit } passa við
+ }
+pdfjs-find-not-found = Fann ekki orðið
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Síðubreidd
+pdfjs-page-scale-fit = Passa á síðu
+pdfjs-page-scale-auto = Sjálfvirkur aðdráttur
+pdfjs-page-scale-actual = Raunstærð
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Síða { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Villa kom upp við að hlaða inn PDF.
+pdfjs-invalid-file-error = Ógild eða skemmd PDF skrá.
+pdfjs-missing-file-error = Vantar PDF skrá.
+pdfjs-unexpected-response-error = Óvænt svar frá netþjóni.
+pdfjs-rendering-error = Upp kom villa við að birta síðuna.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Skýring]
+
+## Password
+
+pdfjs-password-label = Sláðu inn lykilorð til að opna þessa PDF skrá.
+pdfjs-password-invalid = Ógilt lykilorð. Reyndu aftur.
+pdfjs-password-ok-button = Í lagi
+pdfjs-password-cancel-button = Hætta við
+pdfjs-web-fonts-disabled = Vef leturgerðir eru óvirkar: get ekki notað innbyggðar PDF leturgerðir.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texti
+pdfjs-editor-free-text-button-label = Texti
+pdfjs-editor-ink-button =
+ .title = Teikna
+pdfjs-editor-ink-button-label = Teikna
+pdfjs-editor-stamp-button =
+ .title = Bæta við eða breyta myndum
+pdfjs-editor-stamp-button-label = Bæta við eða breyta myndum
+pdfjs-editor-highlight-button =
+ .title = Áherslulita
+pdfjs-editor-highlight-button-label = Áherslulita
+pdfjs-highlight-floating-button =
+ .title = Áherslulita
+pdfjs-highlight-floating-button1 =
+ .title = Áherslulita
+ .aria-label = Áherslulita
+pdfjs-highlight-floating-button-label = Áherslulita
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Fjarlægja teikningu
+pdfjs-editor-remove-freetext-button =
+ .title = Fjarlægja texta
+pdfjs-editor-remove-stamp-button =
+ .title = Fjarlægja mynd
+pdfjs-editor-remove-highlight-button =
+ .title = Fjarlægja áherslulit
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Litur
+pdfjs-editor-free-text-size-input = Stærð
+pdfjs-editor-ink-color-input = Litur
+pdfjs-editor-ink-thickness-input = Þykkt
+pdfjs-editor-ink-opacity-input = Ógegnsæi
+pdfjs-editor-stamp-add-image-button =
+ .title = Bæta við mynd
+pdfjs-editor-stamp-add-image-button-label = Bæta við mynd
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Þykkt
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Breyta þykkt við áherslulitun annarra atriða en texta
+pdfjs-free-text =
+ .aria-label = Textaritill
+pdfjs-free-text-default-content = Byrjaðu að skrifa…
+pdfjs-ink =
+ .aria-label = Teikniritill
+pdfjs-ink-canvas =
+ .aria-label = Mynd gerð af notanda
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alt-varatexti
+pdfjs-editor-alt-text-edit-button-label = Breyta alt-varatexta
+pdfjs-editor-alt-text-dialog-label = Veldu valkost
+pdfjs-editor-alt-text-dialog-description = Alt-varatexti (auka-myndatexti) hjálpar þegar fólk getur ekki séð myndina eða þegar hún hleðst ekki inn.
+pdfjs-editor-alt-text-add-description-label = Bættu við lýsingu
+pdfjs-editor-alt-text-add-description-description = Reyndu að takmarka þetta við 1-2 setningar sem lýsa efninu, umhverfi eða aðgerðum.
+pdfjs-editor-alt-text-mark-decorative-label = Merkja sem skraut
+pdfjs-editor-alt-text-mark-decorative-description = Þetta er notað fyrir skrautmyndir, eins og borða eða vatnsmerki.
+pdfjs-editor-alt-text-cancel-button = Hætta við
+pdfjs-editor-alt-text-save-button = Vista
+pdfjs-editor-alt-text-decorative-tooltip = Merkt sem skraut
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Til dæmis: „Ungur maður sest við borð til að snæða máltíð“
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Efst í vinstra horni - breyta stærð
+pdfjs-editor-resizer-label-top-middle = Efst á miðju - breyta stærð
+pdfjs-editor-resizer-label-top-right = Efst í hægra horni - breyta stærð
+pdfjs-editor-resizer-label-middle-right = Miðja til hægri - breyta stærð
+pdfjs-editor-resizer-label-bottom-right = Neðst í hægra horni - breyta stærð
+pdfjs-editor-resizer-label-bottom-middle = Neðst á miðju - breyta stærð
+pdfjs-editor-resizer-label-bottom-left = Neðst í vinstra horni - breyta stærð
+pdfjs-editor-resizer-label-middle-left = Miðja til vinstri - breyta stærð
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Áherslulitur
+pdfjs-editor-colorpicker-button =
+ .title = Skipta um lit
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Val lita
+pdfjs-editor-colorpicker-yellow =
+ .title = Gult
+pdfjs-editor-colorpicker-green =
+ .title = Grænt
+pdfjs-editor-colorpicker-blue =
+ .title = Blátt
+pdfjs-editor-colorpicker-pink =
+ .title = Bleikt
+pdfjs-editor-colorpicker-red =
+ .title = Rautt
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Birta allt
+pdfjs-editor-highlight-show-all-button =
+ .title = Birta allt
diff --git a/web/locale/it/viewer.ftl b/web/locale/it/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..fcdab36a974ddf55224d0f9236d12e5caf91771b
--- /dev/null
+++ b/web/locale/it/viewer.ftl
@@ -0,0 +1,399 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pagina precedente
+pdfjs-previous-button-label = Precedente
+pdfjs-next-button =
+ .title = Pagina successiva
+pdfjs-next-button-label = Successiva
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pagina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = di { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } di { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Riduci zoom
+pdfjs-zoom-out-button-label = Riduci zoom
+pdfjs-zoom-in-button =
+ .title = Aumenta zoom
+pdfjs-zoom-in-button-label = Aumenta zoom
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Passa alla modalità presentazione
+pdfjs-presentation-mode-button-label = Modalità presentazione
+pdfjs-open-file-button =
+ .title = Apri file
+pdfjs-open-file-button-label = Apri
+pdfjs-print-button =
+ .title = Stampa
+pdfjs-print-button-label = Stampa
+pdfjs-save-button =
+ .title = Salva
+pdfjs-save-button-label = Salva
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Scarica
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Scarica
+pdfjs-bookmark-button =
+ .title = Pagina corrente (mostra URL della pagina corrente)
+pdfjs-bookmark-button-label = Pagina corrente
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Strumenti
+pdfjs-tools-button-label = Strumenti
+pdfjs-first-page-button =
+ .title = Vai alla prima pagina
+pdfjs-first-page-button-label = Vai alla prima pagina
+pdfjs-last-page-button =
+ .title = Vai all’ultima pagina
+pdfjs-last-page-button-label = Vai all’ultima pagina
+pdfjs-page-rotate-cw-button =
+ .title = Ruota in senso orario
+pdfjs-page-rotate-cw-button-label = Ruota in senso orario
+pdfjs-page-rotate-ccw-button =
+ .title = Ruota in senso antiorario
+pdfjs-page-rotate-ccw-button-label = Ruota in senso antiorario
+pdfjs-cursor-text-select-tool-button =
+ .title = Attiva strumento di selezione testo
+pdfjs-cursor-text-select-tool-button-label = Strumento di selezione testo
+pdfjs-cursor-hand-tool-button =
+ .title = Attiva strumento mano
+pdfjs-cursor-hand-tool-button-label = Strumento mano
+pdfjs-scroll-page-button =
+ .title = Utilizza scorrimento pagine
+pdfjs-scroll-page-button-label = Scorrimento pagine
+pdfjs-scroll-vertical-button =
+ .title = Scorri le pagine in verticale
+pdfjs-scroll-vertical-button-label = Scorrimento verticale
+pdfjs-scroll-horizontal-button =
+ .title = Scorri le pagine in orizzontale
+pdfjs-scroll-horizontal-button-label = Scorrimento orizzontale
+pdfjs-scroll-wrapped-button =
+ .title = Scorri le pagine in verticale, disponendole da sinistra a destra e andando a capo automaticamente
+pdfjs-scroll-wrapped-button-label = Scorrimento con a capo automatico
+pdfjs-spread-none-button =
+ .title = Non raggruppare pagine
+pdfjs-spread-none-button-label = Nessun raggruppamento
+pdfjs-spread-odd-button =
+ .title = Crea gruppi di pagine che iniziano con numeri di pagina dispari
+pdfjs-spread-odd-button-label = Raggruppamento dispari
+pdfjs-spread-even-button =
+ .title = Crea gruppi di pagine che iniziano con numeri di pagina pari
+pdfjs-spread-even-button-label = Raggruppamento pari
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Proprietà del documento…
+pdfjs-document-properties-button-label = Proprietà del documento…
+pdfjs-document-properties-file-name = Nome file:
+pdfjs-document-properties-file-size = Dimensione file:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } byte)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte)
+pdfjs-document-properties-title = Titolo:
+pdfjs-document-properties-author = Autore:
+pdfjs-document-properties-subject = Oggetto:
+pdfjs-document-properties-keywords = Parole chiave:
+pdfjs-document-properties-creation-date = Data creazione:
+pdfjs-document-properties-modification-date = Data modifica:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Autore originale:
+pdfjs-document-properties-producer = Produttore PDF:
+pdfjs-document-properties-version = Versione PDF:
+pdfjs-document-properties-page-count = Conteggio pagine:
+pdfjs-document-properties-page-size = Dimensioni pagina:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = verticale
+pdfjs-document-properties-page-size-orientation-landscape = orizzontale
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Lettera
+pdfjs-document-properties-page-size-name-legal = Legale
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Visualizzazione web veloce:
+pdfjs-document-properties-linearized-yes = Sì
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Chiudi
+
+## Print
+
+pdfjs-print-progress-message = Preparazione documento per la stampa…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Annulla
+pdfjs-printing-not-supported = Attenzione: la stampa non è completamente supportata da questo browser.
+pdfjs-printing-not-ready = Attenzione: il PDF non è ancora stato caricato completamente per la stampa.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Attiva/disattiva barra laterale
+pdfjs-toggle-sidebar-notification-button =
+ .title = Attiva/disattiva barra laterale (il documento contiene struttura/allegati/livelli)
+pdfjs-toggle-sidebar-button-label = Attiva/disattiva barra laterale
+pdfjs-document-outline-button =
+ .title = Visualizza la struttura del documento (doppio clic per visualizzare/comprimere tutti gli elementi)
+pdfjs-document-outline-button-label = Struttura documento
+pdfjs-attachments-button =
+ .title = Visualizza allegati
+pdfjs-attachments-button-label = Allegati
+pdfjs-layers-button =
+ .title = Visualizza livelli (doppio clic per ripristinare tutti i livelli allo stato predefinito)
+pdfjs-layers-button-label = Livelli
+pdfjs-thumbs-button =
+ .title = Mostra le miniature
+pdfjs-thumbs-button-label = Miniature
+pdfjs-current-outline-item-button =
+ .title = Trova elemento struttura corrente
+pdfjs-current-outline-item-button-label = Elemento struttura corrente
+pdfjs-findbar-button =
+ .title = Trova nel documento
+pdfjs-findbar-button-label = Trova
+pdfjs-additional-layers = Livelli aggiuntivi
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pagina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura della pagina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Trova
+ .placeholder = Trova nel documento…
+pdfjs-find-previous-button =
+ .title = Trova l’occorrenza precedente del testo da cercare
+pdfjs-find-previous-button-label = Precedente
+pdfjs-find-next-button =
+ .title = Trova l’occorrenza successiva del testo da cercare
+pdfjs-find-next-button-label = Successivo
+pdfjs-find-highlight-checkbox = Evidenzia
+pdfjs-find-match-case-checkbox-label = Maiuscole/minuscole
+pdfjs-find-match-diacritics-checkbox-label = Segni diacritici
+pdfjs-find-entire-word-checkbox-label = Parole intere
+pdfjs-find-reached-top = Raggiunto l’inizio della pagina, continua dalla fine
+pdfjs-find-reached-bottom = Raggiunta la fine della pagina, continua dall’inizio
+
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } di { $total } corrispondenza
+ *[other] { $current } di { $total } corrispondenze
+ }
+
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Più di una { $limit } corrispondenza
+ *[other] Più di { $limit } corrispondenze
+ }
+
+pdfjs-find-not-found = Testo non trovato
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Larghezza pagina
+pdfjs-page-scale-fit = Adatta a una pagina
+pdfjs-page-scale-auto = Zoom automatico
+pdfjs-page-scale-actual = Dimensioni effettive
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Pagina { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Si è verificato un errore durante il caricamento del PDF.
+pdfjs-invalid-file-error = File PDF non valido o danneggiato.
+pdfjs-missing-file-error = File PDF non disponibile.
+pdfjs-unexpected-response-error = Risposta imprevista del server
+pdfjs-rendering-error = Si è verificato un errore durante il rendering della pagina.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Annotazione: { $type }]
+
+## Password
+
+pdfjs-password-label = Inserire la password per aprire questo file PDF.
+pdfjs-password-invalid = Password non corretta. Riprovare.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Annulla
+pdfjs-web-fonts-disabled = I web font risultano disattivati: impossibile utilizzare i caratteri incorporati nel PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Testo
+pdfjs-editor-free-text-button-label = Testo
+pdfjs-editor-ink-button =
+ .title = Disegno
+pdfjs-editor-ink-button-label = Disegno
+pdfjs-editor-stamp-button =
+ .title = Aggiungi o rimuovi immagine
+pdfjs-editor-stamp-button-label = Aggiungi o rimuovi immagine
+pdfjs-editor-highlight-button =
+ .title = Evidenzia
+pdfjs-editor-highlight-button-label = Evidenzia
+pdfjs-highlight-floating-button1 =
+ .title = Evidenzia
+ .aria-label = Evidenzia
+pdfjs-highlight-floating-button-label = Evidenzia
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Rimuovi disegno
+pdfjs-editor-remove-freetext-button =
+ .title = Rimuovi testo
+pdfjs-editor-remove-stamp-button =
+ .title = Rimuovi immagine
+pdfjs-editor-remove-highlight-button =
+ .title = Rimuovi evidenziazione
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Colore
+pdfjs-editor-free-text-size-input = Dimensione
+pdfjs-editor-ink-color-input = Colore
+pdfjs-editor-ink-thickness-input = Spessore
+pdfjs-editor-ink-opacity-input = Opacità
+pdfjs-editor-stamp-add-image-button =
+ .title = Aggiungi immagine
+pdfjs-editor-stamp-add-image-button-label = Aggiungi immagine
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Spessore
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Modifica lo spessore della selezione per elementi non testuali
+
+pdfjs-free-text =
+ .aria-label = Editor di testo
+pdfjs-free-text-default-content = Inizia a digitare…
+pdfjs-ink =
+ .aria-label = Editor disegni
+pdfjs-ink-canvas =
+ .aria-label = Immagine creata dall’utente
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Testo alternativo
+pdfjs-editor-alt-text-edit-button-label = Modifica testo alternativo
+pdfjs-editor-alt-text-dialog-label = Scegli un’opzione
+pdfjs-editor-alt-text-dialog-description = Il testo alternativo (“alt text”) aiuta quando le persone non possono vedere l’immagine o quando l’immagine non viene caricata.
+pdfjs-editor-alt-text-add-description-label = Aggiungi una descrizione
+pdfjs-editor-alt-text-add-description-description = Punta a una o due frasi che descrivono l’argomento, l’ambientazione o le azioni.
+pdfjs-editor-alt-text-mark-decorative-label = Contrassegna come decorativa
+pdfjs-editor-alt-text-mark-decorative-description = Viene utilizzato per immagini ornamentali, come bordi o filigrane.
+pdfjs-editor-alt-text-cancel-button = Annulla
+pdfjs-editor-alt-text-save-button = Salva
+pdfjs-editor-alt-text-decorative-tooltip = Contrassegnata come decorativa
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Ad esempio, “Un giovane si siede a tavola per mangiare”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Angolo in alto a sinistra — ridimensiona
+pdfjs-editor-resizer-label-top-middle = Lato superiore nel mezzo — ridimensiona
+pdfjs-editor-resizer-label-top-right = Angolo in alto a destra — ridimensiona
+pdfjs-editor-resizer-label-middle-right = Lato destro nel mezzo — ridimensiona
+pdfjs-editor-resizer-label-bottom-right = Angolo in basso a destra — ridimensiona
+pdfjs-editor-resizer-label-bottom-middle = Lato inferiore nel mezzo — ridimensiona
+pdfjs-editor-resizer-label-bottom-left = Angolo in basso a sinistra — ridimensiona
+pdfjs-editor-resizer-label-middle-left = Lato sinistro nel mezzo — ridimensiona
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Colore evidenziatore
+
+pdfjs-editor-colorpicker-button =
+ .title = Cambia colore
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Colori disponibili
+pdfjs-editor-colorpicker-yellow =
+ .title = Giallo
+pdfjs-editor-colorpicker-green =
+ .title = Verde
+pdfjs-editor-colorpicker-blue =
+ .title = Blu
+pdfjs-editor-colorpicker-pink =
+ .title = Rosa
+pdfjs-editor-colorpicker-red =
+ .title = Rosso
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Mostra tutto
+pdfjs-editor-highlight-show-all-button =
+ .title = Mostra tutto
diff --git a/web/locale/ja/viewer.ftl b/web/locale/ja/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..2246cfd49b08dd006d5941c99a441cec4504976b
--- /dev/null
+++ b/web/locale/ja/viewer.ftl
@@ -0,0 +1,396 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = 前のページへ戻ります
+pdfjs-previous-button-label = 前へ
+pdfjs-next-button =
+ .title = 次のページへ進みます
+pdfjs-next-button-label = 次へ
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = ページ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = / { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = 表示を縮小します
+pdfjs-zoom-out-button-label = 縮小
+pdfjs-zoom-in-button =
+ .title = 表示を拡大します
+pdfjs-zoom-in-button-label = 拡大
+pdfjs-zoom-select =
+ .title = 拡大/縮小
+pdfjs-presentation-mode-button =
+ .title = プレゼンテーションモードに切り替えます
+pdfjs-presentation-mode-button-label = プレゼンテーションモード
+pdfjs-open-file-button =
+ .title = ファイルを開きます
+pdfjs-open-file-button-label = 開く
+pdfjs-print-button =
+ .title = 印刷します
+pdfjs-print-button-label = 印刷
+pdfjs-save-button =
+ .title = 保存します
+pdfjs-save-button-label = 保存
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = ダウンロードします
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = ダウンロード
+pdfjs-bookmark-button =
+ .title = 現在のページの URL です (現在のページを表示する URL)
+pdfjs-bookmark-button-label = 現在のページ
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ツール
+pdfjs-tools-button-label = ツール
+pdfjs-first-page-button =
+ .title = 最初のページへ移動します
+pdfjs-first-page-button-label = 最初のページへ移動
+pdfjs-last-page-button =
+ .title = 最後のページへ移動します
+pdfjs-last-page-button-label = 最後のページへ移動
+pdfjs-page-rotate-cw-button =
+ .title = ページを右へ回転します
+pdfjs-page-rotate-cw-button-label = 右回転
+pdfjs-page-rotate-ccw-button =
+ .title = ページを左へ回転します
+pdfjs-page-rotate-ccw-button-label = 左回転
+pdfjs-cursor-text-select-tool-button =
+ .title = テキスト選択ツールを有効にします
+pdfjs-cursor-text-select-tool-button-label = テキスト選択ツール
+pdfjs-cursor-hand-tool-button =
+ .title = 手のひらツールを有効にします
+pdfjs-cursor-hand-tool-button-label = 手のひらツール
+pdfjs-scroll-page-button =
+ .title = ページ単位でスクロールします
+pdfjs-scroll-page-button-label = ページ単位でスクロール
+pdfjs-scroll-vertical-button =
+ .title = 縦スクロールにします
+pdfjs-scroll-vertical-button-label = 縦スクロール
+pdfjs-scroll-horizontal-button =
+ .title = 横スクロールにします
+pdfjs-scroll-horizontal-button-label = 横スクロール
+pdfjs-scroll-wrapped-button =
+ .title = 折り返しスクロールにします
+pdfjs-scroll-wrapped-button-label = 折り返しスクロール
+pdfjs-spread-none-button =
+ .title = 見開きにしません
+pdfjs-spread-none-button-label = 見開きにしない
+pdfjs-spread-odd-button =
+ .title = 奇数ページ開始で見開きにします
+pdfjs-spread-odd-button-label = 奇数ページ見開き
+pdfjs-spread-even-button =
+ .title = 偶数ページ開始で見開きにします
+pdfjs-spread-even-button-label = 偶数ページ見開き
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = 文書のプロパティ...
+pdfjs-document-properties-button-label = 文書のプロパティ...
+pdfjs-document-properties-file-name = ファイル名:
+pdfjs-document-properties-file-size = ファイルサイズ:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } バイト)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } バイト)
+pdfjs-document-properties-title = タイトル:
+pdfjs-document-properties-author = 作成者:
+pdfjs-document-properties-subject = 件名:
+pdfjs-document-properties-keywords = キーワード:
+pdfjs-document-properties-creation-date = 作成日:
+pdfjs-document-properties-modification-date = 更新日:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = アプリケーション:
+pdfjs-document-properties-producer = PDF 作成:
+pdfjs-document-properties-version = PDF のバージョン:
+pdfjs-document-properties-page-count = ページ数:
+pdfjs-document-properties-page-size = ページサイズ:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = 縦
+pdfjs-document-properties-page-size-orientation-landscape = 横
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = レター
+pdfjs-document-properties-page-size-name-legal = リーガル
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = ウェブ表示用に最適化:
+pdfjs-document-properties-linearized-yes = はい
+pdfjs-document-properties-linearized-no = いいえ
+pdfjs-document-properties-close-button = 閉じる
+
+## Print
+
+pdfjs-print-progress-message = 文書の印刷を準備しています...
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = キャンセル
+pdfjs-printing-not-supported = 警告: このブラウザーでは印刷が完全にサポートされていません。
+pdfjs-printing-not-ready = 警告: PDF を印刷するための読み込みが終了していません。
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = サイドバー表示を切り替えます
+pdfjs-toggle-sidebar-notification-button =
+ .title = サイドバー表示を切り替えます (文書に含まれるアウトライン / 添付 / レイヤー)
+pdfjs-toggle-sidebar-button-label = サイドバーの切り替え
+pdfjs-document-outline-button =
+ .title = 文書の目次を表示します (ダブルクリックで項目を開閉します)
+pdfjs-document-outline-button-label = 文書の目次
+pdfjs-attachments-button =
+ .title = 添付ファイルを表示します
+pdfjs-attachments-button-label = 添付ファイル
+pdfjs-layers-button =
+ .title = レイヤーを表示します (ダブルクリックですべてのレイヤーが初期状態に戻ります)
+pdfjs-layers-button-label = レイヤー
+pdfjs-thumbs-button =
+ .title = 縮小版を表示します
+pdfjs-thumbs-button-label = 縮小版
+pdfjs-current-outline-item-button =
+ .title = 現在のアウトライン項目を検索
+pdfjs-current-outline-item-button-label = 現在のアウトライン項目
+pdfjs-findbar-button =
+ .title = 文書内を検索します
+pdfjs-findbar-button-label = 検索
+pdfjs-additional-layers = 追加レイヤー
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page } ページ
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } ページの縮小版
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = 検索
+ .placeholder = 文書内を検索...
+pdfjs-find-previous-button =
+ .title = 現在より前の位置で指定文字列が現れる部分を検索します
+pdfjs-find-previous-button-label = 前へ
+pdfjs-find-next-button =
+ .title = 現在より後の位置で指定文字列が現れる部分を検索します
+pdfjs-find-next-button-label = 次へ
+pdfjs-find-highlight-checkbox = すべて強調表示
+pdfjs-find-match-case-checkbox-label = 大文字/小文字を区別
+pdfjs-find-match-diacritics-checkbox-label = 発音区別符号を区別
+pdfjs-find-entire-word-checkbox-label = 単語一致
+pdfjs-find-reached-top = 文書先頭に到達したので末尾から続けて検索します
+pdfjs-find-reached-bottom = 文書末尾に到達したので先頭から続けて検索します
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $total } 件中 { $current } 件目
+ *[other] { $total } 件中 { $current } 件目
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] { $limit } 件以上一致
+ *[other] { $limit } 件以上一致
+ }
+pdfjs-find-not-found = 見つかりませんでした
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = 幅に合わせる
+pdfjs-page-scale-fit = ページのサイズに合わせる
+pdfjs-page-scale-auto = 自動ズーム
+pdfjs-page-scale-actual = 実際のサイズ
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = { $page } ページ
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF の読み込み中にエラーが発生しました。
+pdfjs-invalid-file-error = 無効または破損した PDF ファイル。
+pdfjs-missing-file-error = PDF ファイルが見つかりません。
+pdfjs-unexpected-response-error = サーバーから予期せぬ応答がありました。
+pdfjs-rendering-error = ページのレンダリング中にエラーが発生しました。
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } 注釈]
+
+## Password
+
+pdfjs-password-label = この PDF ファイルを開くためのパスワードを入力してください。
+pdfjs-password-invalid = 無効なパスワードです。もう一度やり直してください。
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = キャンセル
+pdfjs-web-fonts-disabled = ウェブフォントが無効になっています: 埋め込まれた PDF のフォントを使用できません。
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = フリーテキスト注釈を追加します
+pdfjs-editor-free-text-button-label = フリーテキスト注釈
+pdfjs-editor-ink-button =
+ .title = インク注釈を追加します
+pdfjs-editor-ink-button-label = インク注釈
+pdfjs-editor-stamp-button =
+ .title = 画像を追加または編集します
+pdfjs-editor-stamp-button-label = 画像を追加または編集
+pdfjs-editor-highlight-button =
+ .title = 強調します
+pdfjs-editor-highlight-button-label = 強調
+pdfjs-highlight-floating-button =
+ .title = 強調
+pdfjs-highlight-floating-button1 =
+ .title = 強調
+ .aria-label = 強調します
+pdfjs-highlight-floating-button-label = 強調
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = インク注釈を削除します
+pdfjs-editor-remove-freetext-button =
+ .title = テキストを削除します
+pdfjs-editor-remove-stamp-button =
+ .title = 画像を削除します
+pdfjs-editor-remove-highlight-button =
+ .title = 強調を削除します
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = 色
+pdfjs-editor-free-text-size-input = サイズ
+pdfjs-editor-ink-color-input = 色
+pdfjs-editor-ink-thickness-input = 太さ
+pdfjs-editor-ink-opacity-input = 不透明度
+pdfjs-editor-stamp-add-image-button =
+ .title = 画像を追加します
+pdfjs-editor-stamp-add-image-button-label = 画像を追加
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = 太さ
+pdfjs-editor-free-highlight-thickness-title =
+ .title = テキスト以外のアイテムを強調する時の太さを変更します
+pdfjs-free-text =
+ .aria-label = フリーテキスト注釈エディター
+pdfjs-free-text-default-content = テキストを入力してください...
+pdfjs-ink =
+ .aria-label = インク注釈エディター
+pdfjs-ink-canvas =
+ .aria-label = ユーザー作成画像
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = 代替テキスト
+pdfjs-editor-alt-text-edit-button-label = 代替テキストを編集
+pdfjs-editor-alt-text-dialog-label = オプションの選択
+pdfjs-editor-alt-text-dialog-description = 代替テキストは画像が表示されない場合や読み込まれない場合にユーザーの助けになります。
+pdfjs-editor-alt-text-add-description-label = 説明を追加
+pdfjs-editor-alt-text-add-description-description = 対象や設定、動作を説明する短い文章を記入してください。
+pdfjs-editor-alt-text-mark-decorative-label = 装飾マークを付ける
+pdfjs-editor-alt-text-mark-decorative-description = これは区切り線やウォーターマークなどの装飾画像に使用されます。
+pdfjs-editor-alt-text-cancel-button = キャンセル
+pdfjs-editor-alt-text-save-button = 保存
+pdfjs-editor-alt-text-decorative-tooltip = 装飾マークが付いています
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = 例:「若い人がテーブルの席について食事をしています」
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = 左上隅 — サイズ変更
+pdfjs-editor-resizer-label-top-middle = 上中央 — サイズ変更
+pdfjs-editor-resizer-label-top-right = 右上隅 — サイズ変更
+pdfjs-editor-resizer-label-middle-right = 右中央 — サイズ変更
+pdfjs-editor-resizer-label-bottom-right = 右下隅 — サイズ変更
+pdfjs-editor-resizer-label-bottom-middle = 下中央 — サイズ変更
+pdfjs-editor-resizer-label-bottom-left = 左下隅 — サイズ変更
+pdfjs-editor-resizer-label-middle-left = 左中央 — サイズ変更
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = 強調色
+pdfjs-editor-colorpicker-button =
+ .title = 色を変更します
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = 色の選択
+pdfjs-editor-colorpicker-yellow =
+ .title = 黄色
+pdfjs-editor-colorpicker-green =
+ .title = 緑色
+pdfjs-editor-colorpicker-blue =
+ .title = 青色
+pdfjs-editor-colorpicker-pink =
+ .title = ピンク色
+pdfjs-editor-colorpicker-red =
+ .title = 赤色
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = すべて表示
+pdfjs-editor-highlight-show-all-button =
+ .title = 強調の表示を切り替えます
diff --git a/web/locale/ka/viewer.ftl b/web/locale/ka/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..f31898fad0587c60b4e0cb60576bed6191678c19
--- /dev/null
+++ b/web/locale/ka/viewer.ftl
@@ -0,0 +1,387 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = წინა გვერდი
+pdfjs-previous-button-label = წინა
+pdfjs-next-button =
+ .title = შემდეგი გვერდი
+pdfjs-next-button-label = შემდეგი
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = გვერდი
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount }-დან
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } { $pagesCount }-დან)
+pdfjs-zoom-out-button =
+ .title = ზომის შემცირება
+pdfjs-zoom-out-button-label = დაშორება
+pdfjs-zoom-in-button =
+ .title = ზომის გაზრდა
+pdfjs-zoom-in-button-label = მოახლოება
+pdfjs-zoom-select =
+ .title = ზომა
+pdfjs-presentation-mode-button =
+ .title = ჩვენების რეჟიმზე გადართვა
+pdfjs-presentation-mode-button-label = ჩვენების რეჟიმი
+pdfjs-open-file-button =
+ .title = ფაილის გახსნა
+pdfjs-open-file-button-label = გახსნა
+pdfjs-print-button =
+ .title = ამობეჭდვა
+pdfjs-print-button-label = ამობეჭდვა
+pdfjs-save-button =
+ .title = შენახვა
+pdfjs-save-button-label = შენახვა
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = ჩამოტვირთვა
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = ჩამოტვირთვა
+pdfjs-bookmark-button =
+ .title = მიმდინარე გვერდი (ბმული ამ გვერდისთვის)
+pdfjs-bookmark-button-label = მიმდინარე გვერდი
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = გახსნა პროგრამით
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = გახსნა პროგრამით
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ხელსაწყოები
+pdfjs-tools-button-label = ხელსაწყოები
+pdfjs-first-page-button =
+ .title = პირველ გვერდზე გადასვლა
+pdfjs-first-page-button-label = პირველ გვერდზე გადასვლა
+pdfjs-last-page-button =
+ .title = ბოლო გვერდზე გადასვლა
+pdfjs-last-page-button-label = ბოლო გვერდზე გადასვლა
+pdfjs-page-rotate-cw-button =
+ .title = საათის ისრის მიმართულებით შებრუნება
+pdfjs-page-rotate-cw-button-label = მარჯვნივ გადაბრუნება
+pdfjs-page-rotate-ccw-button =
+ .title = საათის ისრის საპირისპიროდ შებრუნება
+pdfjs-page-rotate-ccw-button-label = მარცხნივ გადაბრუნება
+pdfjs-cursor-text-select-tool-button =
+ .title = მოსანიშნი მაჩვენებლის გამოყენება
+pdfjs-cursor-text-select-tool-button-label = მოსანიშნი მაჩვენებელი
+pdfjs-cursor-hand-tool-button =
+ .title = გადასაადგილებელი მაჩვენებლის გამოყენება
+pdfjs-cursor-hand-tool-button-label = გადასაადგილებელი
+pdfjs-scroll-page-button =
+ .title = გვერდზე გადაადგილების გამოყენება
+pdfjs-scroll-page-button-label = გვერდშივე გადაადგილება
+pdfjs-scroll-vertical-button =
+ .title = გვერდების შვეულად ჩვენება
+pdfjs-scroll-vertical-button-label = შვეული გადაადგილება
+pdfjs-scroll-horizontal-button =
+ .title = გვერდების თარაზულად ჩვენება
+pdfjs-scroll-horizontal-button-label = განივი გადაადგილება
+pdfjs-scroll-wrapped-button =
+ .title = გვერდების ცხრილურად ჩვენება
+pdfjs-scroll-wrapped-button-label = ცხრილური გადაადგილება
+pdfjs-spread-none-button =
+ .title = ორ გვერდზე გაშლის გარეშე
+pdfjs-spread-none-button-label = ცალგვერდიანი ჩვენება
+pdfjs-spread-odd-button =
+ .title = ორ გვერდზე გაშლა კენტი გვერდიდან
+pdfjs-spread-odd-button-label = ორ გვერდზე კენტიდან
+pdfjs-spread-even-button =
+ .title = ორ გვერდზე გაშლა ლუწი გვერდიდან
+pdfjs-spread-even-button-label = ორ გვერდზე ლუწიდან
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = დოკუმენტის შესახებ…
+pdfjs-document-properties-button-label = დოკუმენტის შესახებ…
+pdfjs-document-properties-file-name = ფაილის სახელი:
+pdfjs-document-properties-file-size = ფაილის მოცულობა:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } კბ ({ $size_b } ბაიტი)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } მბ ({ $size_b } ბაიტი)
+pdfjs-document-properties-title = სათაური:
+pdfjs-document-properties-author = შემქმნელი:
+pdfjs-document-properties-subject = თემა:
+pdfjs-document-properties-keywords = საკვანძო სიტყვები:
+pdfjs-document-properties-creation-date = შექმნის დრო:
+pdfjs-document-properties-modification-date = ჩასწორების დრო:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = შემდგენელი:
+pdfjs-document-properties-producer = PDF-შემდგენელი:
+pdfjs-document-properties-version = PDF-ვერსია:
+pdfjs-document-properties-page-count = გვერდები:
+pdfjs-document-properties-page-size = გვერდის ზომა:
+pdfjs-document-properties-page-size-unit-inches = დუიმი
+pdfjs-document-properties-page-size-unit-millimeters = მმ
+pdfjs-document-properties-page-size-orientation-portrait = შვეულად
+pdfjs-document-properties-page-size-orientation-landscape = თარაზულად
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = მსუბუქი ვებჩვენება:
+pdfjs-document-properties-linearized-yes = დიახ
+pdfjs-document-properties-linearized-no = არა
+pdfjs-document-properties-close-button = დახურვა
+
+## Print
+
+pdfjs-print-progress-message = დოკუმენტი მზადდება ამოსაბეჭდად…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = გაუქმება
+pdfjs-printing-not-supported = გაფრთხილება: ამობეჭდვა ამ ბრაუზერში არაა სრულად მხარდაჭერილი.
+pdfjs-printing-not-ready = გაფრთხილება: PDF სრულად ჩატვირთული არაა, ამობეჭდვის დასაწყებად.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = გვერდითა ზოლის გამოჩენა/დამალვა
+pdfjs-toggle-sidebar-notification-button =
+ .title = გვერდითი ზოლის გამოჩენა (შეიცავს სარჩევს/დანართს/ფენებს)
+pdfjs-toggle-sidebar-button-label = გვერდითა ზოლის გამოჩენა/დამალვა
+pdfjs-document-outline-button =
+ .title = დოკუმენტის სარჩევის ჩვენება (ორმაგი წკაპით თითოეულის ჩამოშლა/აკეცვა)
+pdfjs-document-outline-button-label = დოკუმენტის სარჩევი
+pdfjs-attachments-button =
+ .title = დანართების ჩვენება
+pdfjs-attachments-button-label = დანართები
+pdfjs-layers-button =
+ .title = ფენების გამოჩენა (ორმაგი წკაპით ყველა ფენის ნაგულისხმევზე დაბრუნება)
+pdfjs-layers-button-label = ფენები
+pdfjs-thumbs-button =
+ .title = შეთვალიერება
+pdfjs-thumbs-button-label = ესკიზები
+pdfjs-current-outline-item-button =
+ .title = მიმდინარე გვერდის მონახვა სარჩევში
+pdfjs-current-outline-item-button-label = მიმდინარე გვერდი სარჩევში
+pdfjs-findbar-button =
+ .title = პოვნა დოკუმენტში
+pdfjs-findbar-button-label = ძიება
+pdfjs-additional-layers = დამატებითი ფენები
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = გვერდი { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = გვერდის შეთვალიერება { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = ძიება
+ .placeholder = პოვნა დოკუმენტში…
+pdfjs-find-previous-button =
+ .title = ფრაზის წინა კონტექსტის პოვნა
+pdfjs-find-previous-button-label = წინა
+pdfjs-find-next-button =
+ .title = ფრაზის შემდეგი კონტექსტის პოვნა
+pdfjs-find-next-button-label = შემდეგი
+pdfjs-find-highlight-checkbox = ყველაფრის მონიშვნა
+pdfjs-find-match-case-checkbox-label = მთავრულით
+pdfjs-find-match-diacritics-checkbox-label = ნიშნებით
+pdfjs-find-entire-word-checkbox-label = მთლიანი სიტყვები
+pdfjs-find-reached-top = მიღწეულია დოკუმენტის დასაწყისი, გრძელდება ბოლოდან
+pdfjs-find-reached-bottom = მიღწეულია დოკუმენტის ბოლო, გრძელდება დასაწყისიდან
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] თანხვედრა { $current }, სულ { $total }
+ *[other] თანხვედრა { $current }, სულ { $total }
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] არანაკლებ { $limit } თანხვედრა
+ *[other] არანაკლებ { $limit } თანხვედრა
+ }
+pdfjs-find-not-found = ფრაზა ვერ მოიძებნა
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = გვერდის სიგანეზე
+pdfjs-page-scale-fit = მთლიანი გვერდი
+pdfjs-page-scale-auto = ავტომატური
+pdfjs-page-scale-actual = საწყისი ზომა
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = გვერდი { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = შეცდომა, PDF-ფაილის ჩატვირთვისას.
+pdfjs-invalid-file-error = არამართებული ან დაზიანებული PDF-ფაილი.
+pdfjs-missing-file-error = ნაკლული PDF-ფაილი.
+pdfjs-unexpected-response-error = სერვერის მოულოდნელი პასუხი.
+pdfjs-rendering-error = შეცდომა, გვერდის ჩვენებისას.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } შენიშვნა]
+
+## Password
+
+pdfjs-password-label = შეიყვანეთ პაროლი PDF-ფაილის გასახსნელად.
+pdfjs-password-invalid = არასწორი პაროლი. გთხოვთ, სცადოთ ხელახლა.
+pdfjs-password-ok-button = კარგი
+pdfjs-password-cancel-button = გაუქმება
+pdfjs-web-fonts-disabled = ვებშრიფტები გამორთულია: ჩაშენებული PDF-შრიფტების გამოყენება ვერ ხერხდება.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = წარწერა
+pdfjs-editor-free-text-button-label = ტექსტი
+pdfjs-editor-ink-button =
+ .title = ხაზვა
+pdfjs-editor-ink-button-label = ხაზვა
+pdfjs-editor-stamp-button =
+ .title = სურათების დართვა ან ჩასწორება
+pdfjs-editor-stamp-button-label = სურათების დართვა ან ჩასწორება
+pdfjs-editor-remove-button =
+ .title = მოცილება
+pdfjs-editor-highlight-button =
+ .title = მონიშვნა
+pdfjs-editor-highlight-button-label = მონიშვნა
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = დახაზულის მოცილება
+pdfjs-editor-remove-freetext-button =
+ .title = წარწერის მოცილება
+pdfjs-editor-remove-stamp-button =
+ .title = სურათის მოცილება
+pdfjs-editor-remove-highlight-button =
+ .title = მონიშვნის მოცილება
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = ფერი
+pdfjs-editor-free-text-size-input = ზომა
+pdfjs-editor-ink-color-input = ფერი
+pdfjs-editor-ink-thickness-input = სისქე
+pdfjs-editor-ink-opacity-input = გაუმჭვირვალობა
+pdfjs-editor-stamp-add-image-button =
+ .title = სურათის დამატება
+pdfjs-editor-stamp-add-image-button-label = სურათის დამატება
+pdfjs-free-text =
+ .aria-label = ნაწერის ჩასწორება
+pdfjs-free-text-default-content = აკრიფეთ…
+pdfjs-ink =
+ .aria-label = დახაზულის შესწორება
+pdfjs-ink-canvas =
+ .aria-label = მომხმარებლის შექმნილი სურათი
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = თანდართული წარწერა
+pdfjs-editor-alt-text-edit-button-label = თანდართული წარწერის ჩასწორება
+pdfjs-editor-alt-text-dialog-label = არჩევა
+pdfjs-editor-alt-text-dialog-description = თანდართული (შემნაცვლებელი) წარწერა გამოსადეგია მათთვის, ვინც ვერ ხედავს სურათებს ან გამოისახება მაშინ, როცა სურათი ვერ ჩაიტვირთება.
+pdfjs-editor-alt-text-add-description-label = აღწერილობის მითითება
+pdfjs-editor-alt-text-add-description-description = განკუთვნილია 1-2 წინადადებით საგნის, მახასიათებლის ან მოქმედების აღსაწერად.
+pdfjs-editor-alt-text-mark-decorative-label = მოინიშნოს მორთულობად
+pdfjs-editor-alt-text-mark-decorative-description = განკუთვნილია შესამკობი სურათებისთვის, გარსშემოსავლები ჩარჩოებისა და ჭვირნიშნებისთვის.
+pdfjs-editor-alt-text-cancel-button = გაუქმება
+pdfjs-editor-alt-text-save-button = შენახვა
+pdfjs-editor-alt-text-decorative-tooltip = მოინიშნოს მორთულობად
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = მაგალითად, „ახალგაზრდა მამაკაცი მაგიდასთან ზის და სადილობს“
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = ზევით მარცხნივ — ზომაცვლა
+pdfjs-editor-resizer-label-top-middle = ზევით შუაში — ზომაცვლა
+pdfjs-editor-resizer-label-top-right = ზევით მარჯვნივ — ზომაცვლა
+pdfjs-editor-resizer-label-middle-right = შუაში მარჯვნივ — ზომაცვლა
+pdfjs-editor-resizer-label-bottom-right = ქვევით მარჯვნივ — ზომაცვლა
+pdfjs-editor-resizer-label-bottom-middle = ქვევით შუაში — ზომაცვლა
+pdfjs-editor-resizer-label-bottom-left = ზვევით მარცხნივ — ზომაცვლა
+pdfjs-editor-resizer-label-middle-left = შუაში მარცხნივ — ზომაცვლა
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = მოსანიშნი ფერი
+pdfjs-editor-colorpicker-button =
+ .title = ფერის შეცვლა
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = ფერის არჩევა
+pdfjs-editor-colorpicker-yellow =
+ .title = ყვითელი
+pdfjs-editor-colorpicker-green =
+ .title = მწვანე
+pdfjs-editor-colorpicker-blue =
+ .title = ლურჯი
+pdfjs-editor-colorpicker-pink =
+ .title = ვარდისფერი
+pdfjs-editor-colorpicker-red =
+ .title = წითელი
diff --git a/web/locale/kab/viewer.ftl b/web/locale/kab/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..5f16478e3eb8fb3608c36ef12cdf5ae0dcc7cecb
--- /dev/null
+++ b/web/locale/kab/viewer.ftl
@@ -0,0 +1,337 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Asebter azewwar
+pdfjs-previous-button-label = Azewwar
+pdfjs-next-button =
+ .title = Asebter d-iteddun
+pdfjs-next-button-label = Ddu ɣer zdat
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Asebter
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = ɣef { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } n { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Semẓi
+pdfjs-zoom-out-button-label = Semẓi
+pdfjs-zoom-in-button =
+ .title = Semɣeṛ
+pdfjs-zoom-in-button-label = Semɣeṛ
+pdfjs-zoom-select =
+ .title = Semɣeṛ/Semẓi
+pdfjs-presentation-mode-button =
+ .title = Uɣal ɣer Uskar Tihawt
+pdfjs-presentation-mode-button-label = Askar Tihawt
+pdfjs-open-file-button =
+ .title = Ldi Afaylu
+pdfjs-open-file-button-label = Ldi
+pdfjs-print-button =
+ .title = Siggez
+pdfjs-print-button-label = Siggez
+pdfjs-save-button =
+ .title = Sekles
+pdfjs-save-button-label = Sekles
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Sader
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Sader
+pdfjs-bookmark-button =
+ .title = Asebter amiran (Sken-d tansa URL seg usebter amiran)
+pdfjs-bookmark-button-label = Asebter amiran
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Ldi deg usnas
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Ldi deg usnas
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Ifecka
+pdfjs-tools-button-label = Ifecka
+pdfjs-first-page-button =
+ .title = Ddu ɣer usebter amezwaru
+pdfjs-first-page-button-label = Ddu ɣer usebter amezwaru
+pdfjs-last-page-button =
+ .title = Ddu ɣer usebter aneggaru
+pdfjs-last-page-button-label = Ddu ɣer usebter aneggaru
+pdfjs-page-rotate-cw-button =
+ .title = Tuzzya tusrigt
+pdfjs-page-rotate-cw-button-label = Tuzzya tusrigt
+pdfjs-page-rotate-ccw-button =
+ .title = Tuzzya amgal-usrig
+pdfjs-page-rotate-ccw-button-label = Tuzzya amgal-usrig
+pdfjs-cursor-text-select-tool-button =
+ .title = Rmed afecku n tefrant n uḍris
+pdfjs-cursor-text-select-tool-button-label = Afecku n tefrant n uḍris
+pdfjs-cursor-hand-tool-button =
+ .title = Rmed afecku afus
+pdfjs-cursor-hand-tool-button-label = Afecku afus
+pdfjs-scroll-page-button =
+ .title = Seqdec adrurem n usebter
+pdfjs-scroll-page-button-label = Adrurem n usebter
+pdfjs-scroll-vertical-button =
+ .title = Seqdec adrurem ubdid
+pdfjs-scroll-vertical-button-label = Adrurem ubdid
+pdfjs-scroll-horizontal-button =
+ .title = Seqdec adrurem aglawan
+pdfjs-scroll-horizontal-button-label = Adrurem aglawan
+pdfjs-scroll-wrapped-button =
+ .title = Seqdec adrurem yuẓen
+pdfjs-scroll-wrapped-button-label = Adrurem yuẓen
+pdfjs-spread-none-button =
+ .title = Ur sedday ara isiɣzaf n usebter
+pdfjs-spread-none-button-label = Ulac isiɣzaf
+pdfjs-spread-odd-button =
+ .title = Seddu isiɣzaf n usebter ibeddun s yisebtar irayuganen
+pdfjs-spread-odd-button-label = Isiɣzaf irayuganen
+pdfjs-spread-even-button =
+ .title = Seddu isiɣzaf n usebter ibeddun s yisebtar iyuganen
+pdfjs-spread-even-button-label = Isiɣzaf iyuganen
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Taɣaṛa n isemli…
+pdfjs-document-properties-button-label = Taɣaṛa n isemli…
+pdfjs-document-properties-file-name = Isem n ufaylu:
+pdfjs-document-properties-file-size = Teɣzi n ufaylu:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KAṬ ({ $size_b } ibiten)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MAṬ ({ $size_b } iṭamḍanen)
+pdfjs-document-properties-title = Azwel:
+pdfjs-document-properties-author = Ameskar:
+pdfjs-document-properties-subject = Amgay:
+pdfjs-document-properties-keywords = Awalen n tsaruţ
+pdfjs-document-properties-creation-date = Azemz n tmerna:
+pdfjs-document-properties-modification-date = Azemz n usnifel:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Yerna-t:
+pdfjs-document-properties-producer = Afecku n uselket PDF:
+pdfjs-document-properties-version = Lqem PDF:
+pdfjs-document-properties-page-count = Amḍan n yisebtar:
+pdfjs-document-properties-page-size = Tuγzi n usebter:
+pdfjs-document-properties-page-size-unit-inches = deg
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = s teɣzi
+pdfjs-document-properties-page-size-orientation-landscape = s tehri
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Asekkil
+pdfjs-document-properties-page-size-name-legal = Usḍif
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Taskant Web taruradt:
+pdfjs-document-properties-linearized-yes = Ih
+pdfjs-document-properties-linearized-no = Ala
+pdfjs-document-properties-close-button = Mdel
+
+## Print
+
+pdfjs-print-progress-message = Aheggi i usiggez n isemli…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Sefsex
+pdfjs-printing-not-supported = Ɣuṛ-k: Asiggez ur ittusefrak ara yakan imaṛṛa deg iminig-a.
+pdfjs-printing-not-ready = Ɣuṛ-k: Afaylu PDF ur d-yuli ara imeṛṛa akken ad ittusiggez.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Sken/Fer agalis adisan
+pdfjs-toggle-sidebar-notification-button =
+ .title = Ffer/Sekn agalis adisan (isemli yegber aɣawas/ticeqqufin yeddan/tissiwin)
+pdfjs-toggle-sidebar-button-label = Sken/Fer agalis adisan
+pdfjs-document-outline-button =
+ .title = Sken isemli (Senned snat tikal i wesemɣer/Afneẓ n iferdisen meṛṛa)
+pdfjs-document-outline-button-label = Isɣalen n isebtar
+pdfjs-attachments-button =
+ .title = Sken ticeqqufin yeddan
+pdfjs-attachments-button-label = Ticeqqufin yeddan
+pdfjs-layers-button =
+ .title = Skeen tissiwin (sit sin yiberdan i uwennez n meṛṛa tissiwin ɣer waddad amezwer)
+pdfjs-layers-button-label = Tissiwin
+pdfjs-thumbs-button =
+ .title = Sken tanfult.
+pdfjs-thumbs-button-label = Tinfulin
+pdfjs-current-outline-item-button =
+ .title = Af-d aferdis n uɣawas amiran
+pdfjs-current-outline-item-button-label = Aferdis n uɣawas amiran
+pdfjs-findbar-button =
+ .title = Nadi deg isemli
+pdfjs-findbar-button-label = Nadi
+pdfjs-additional-layers = Tissiwin-nniḍen
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Asebter { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Tanfult n usebter { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Nadi
+ .placeholder = Nadi deg isemli…
+pdfjs-find-previous-button =
+ .title = Aff-d tamseḍriwt n twinest n deffir
+pdfjs-find-previous-button-label = Azewwar
+pdfjs-find-next-button =
+ .title = Aff-d timseḍriwt n twinest d-iteddun
+pdfjs-find-next-button-label = Ddu ɣer zdat
+pdfjs-find-highlight-checkbox = Err izirig imaṛṛa
+pdfjs-find-match-case-checkbox-label = Qadeṛ amasal n isekkilen
+pdfjs-find-match-diacritics-checkbox-label = Qadeṛ ifeskilen
+pdfjs-find-entire-word-checkbox-label = Awalen iččuranen
+pdfjs-find-reached-top = Yabbeḍ s afella n usebter, tuɣalin s wadda
+pdfjs-find-reached-bottom = Tebḍeḍ s adda n usebter, tuɣalin s afella
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] Timeḍriwt { $current } ɣef { $total }
+ *[other] Timeḍriwin { $current } ɣef { $total }
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Ugar n { $limit } umṣada
+ *[other] Ugar n { $limit } yimṣadayen
+ }
+pdfjs-find-not-found = Ulac tawinest
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Tehri n usebter
+pdfjs-page-scale-fit = Asebter imaṛṛa
+pdfjs-page-scale-auto = Asemɣeṛ/Asemẓi awurman
+pdfjs-page-scale-actual = Teɣzi tilawt
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Asebter { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Teḍra-d tuccḍa deg alluy n PDF:
+pdfjs-invalid-file-error = Afaylu PDF arameɣtu neɣ yexṣeṛ.
+pdfjs-missing-file-error = Ulac afaylu PDF.
+pdfjs-unexpected-response-error = Aqeddac yerra-d yir tiririt ur nettwaṛǧi ara.
+pdfjs-rendering-error = Teḍra-d tuccḍa deg uskan n usebter.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Tabzimt { $type }]
+
+## Password
+
+pdfjs-password-label = Sekcem awal uffir akken ad ldiḍ afaylu-yagi PDF
+pdfjs-password-invalid = Awal uffir mačči d ameɣtu, Ɛreḍ tikelt-nniḍen.
+pdfjs-password-ok-button = IH
+pdfjs-password-cancel-button = Sefsex
+pdfjs-web-fonts-disabled = Tisefsiyin web ttwassensent; D awezɣi useqdec n tsefsiyin yettwarnan ɣer PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Aḍris
+pdfjs-editor-free-text-button-label = Aḍris
+pdfjs-editor-ink-button =
+ .title = Suneɣ
+pdfjs-editor-ink-button-label = Suneɣ
+pdfjs-editor-stamp-button =
+ .title = Rnu neɣ ẓreg tugniwin
+pdfjs-editor-stamp-button-label = Rnu neɣ ẓreg tugniwin
+pdfjs-editor-remove-button =
+ .title = Kkes
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Initen
+pdfjs-editor-free-text-size-input = Teɣzi
+pdfjs-editor-ink-color-input = Ini
+pdfjs-editor-ink-thickness-input = Tuzert
+pdfjs-editor-ink-opacity-input = Tebrek
+pdfjs-editor-stamp-add-image-button =
+ .title = Rnu tawlaft
+pdfjs-editor-stamp-add-image-button-label = Rnu tawlaft
+pdfjs-free-text =
+ .aria-label = Amaẓrag n uḍris
+pdfjs-free-text-default-content = Bdu tira...
+pdfjs-ink =
+ .aria-label = Amaẓrag n usuneɣ
+pdfjs-ink-canvas =
+ .aria-label = Tugna yettwarnan sɣur useqdac
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Aḍris amaskal
+pdfjs-editor-alt-text-edit-button-label = Ẓreg aḍris amaskal
+pdfjs-editor-alt-text-dialog-label = Fren taxtirt
+pdfjs-editor-alt-text-add-description-label = Rnu aglam
+pdfjs-editor-alt-text-mark-decorative-label = Creḍ d adlag
+pdfjs-editor-alt-text-cancel-button = Sefsex
+pdfjs-editor-alt-text-save-button = Sekles
+pdfjs-editor-alt-text-decorative-tooltip = Yettwacreḍ d adlag
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/kk/viewer.ftl b/web/locale/kk/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..57260a2da9ec5f812929adeb6e448cf72f8e9048
--- /dev/null
+++ b/web/locale/kk/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Алдыңғы парақ
+pdfjs-previous-button-label = Алдыңғысы
+pdfjs-next-button =
+ .title = Келесі парақ
+pdfjs-next-button-label = Келесі
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Парақ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } ішінен
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = (парақ { $pageNumber }, { $pagesCount } ішінен)
+pdfjs-zoom-out-button =
+ .title = Кішірейту
+pdfjs-zoom-out-button-label = Кішірейту
+pdfjs-zoom-in-button =
+ .title = Үлкейту
+pdfjs-zoom-in-button-label = Үлкейту
+pdfjs-zoom-select =
+ .title = Масштаб
+pdfjs-presentation-mode-button =
+ .title = Презентация режиміне ауысу
+pdfjs-presentation-mode-button-label = Презентация режимі
+pdfjs-open-file-button =
+ .title = Файлды ашу
+pdfjs-open-file-button-label = Ашу
+pdfjs-print-button =
+ .title = Баспаға шығару
+pdfjs-print-button-label = Баспаға шығару
+pdfjs-save-button =
+ .title = Сақтау
+pdfjs-save-button-label = Сақтау
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Жүктеп алу
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Жүктеп алу
+pdfjs-bookmark-button =
+ .title = Ағымдағы бет (Ағымдағы беттен URL адресін көру)
+pdfjs-bookmark-button-label = Ағымдағы бет
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Қолданбада ашу
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Қолданбада ашу
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Құралдар
+pdfjs-tools-button-label = Құралдар
+pdfjs-first-page-button =
+ .title = Алғашқы параққа өту
+pdfjs-first-page-button-label = Алғашқы параққа өту
+pdfjs-last-page-button =
+ .title = Соңғы параққа өту
+pdfjs-last-page-button-label = Соңғы параққа өту
+pdfjs-page-rotate-cw-button =
+ .title = Сағат тілі бағытымен айналдыру
+pdfjs-page-rotate-cw-button-label = Сағат тілі бағытымен бұру
+pdfjs-page-rotate-ccw-button =
+ .title = Сағат тілі бағытына қарсы бұру
+pdfjs-page-rotate-ccw-button-label = Сағат тілі бағытына қарсы бұру
+pdfjs-cursor-text-select-tool-button =
+ .title = Мәтінді таңдау құралын іске қосу
+pdfjs-cursor-text-select-tool-button-label = Мәтінді таңдау құралы
+pdfjs-cursor-hand-tool-button =
+ .title = Қол құралын іске қосу
+pdfjs-cursor-hand-tool-button-label = Қол құралы
+pdfjs-scroll-page-button =
+ .title = Беттерді айналдыруды пайдалану
+pdfjs-scroll-page-button-label = Беттерді айналдыру
+pdfjs-scroll-vertical-button =
+ .title = Вертикалды айналдыруды қолдану
+pdfjs-scroll-vertical-button-label = Вертикалды айналдыру
+pdfjs-scroll-horizontal-button =
+ .title = Горизонталды айналдыруды қолдану
+pdfjs-scroll-horizontal-button-label = Горизонталды айналдыру
+pdfjs-scroll-wrapped-button =
+ .title = Масштабталатын айналдыруды қолдану
+pdfjs-scroll-wrapped-button-label = Масштабталатын айналдыру
+pdfjs-spread-none-button =
+ .title = Жазық беттер режимін қолданбау
+pdfjs-spread-none-button-label = Жазық беттер режимсіз
+pdfjs-spread-odd-button =
+ .title = Жазық беттер тақ нөмірлі беттерден басталады
+pdfjs-spread-odd-button-label = Тақ нөмірлі беттер сол жақтан
+pdfjs-spread-even-button =
+ .title = Жазық беттер жұп нөмірлі беттерден басталады
+pdfjs-spread-even-button-label = Жұп нөмірлі беттер сол жақтан
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Құжат қасиеттері…
+pdfjs-document-properties-button-label = Құжат қасиеттері…
+pdfjs-document-properties-file-name = Файл аты:
+pdfjs-document-properties-file-size = Файл өлшемі:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байт)
+pdfjs-document-properties-title = Тақырыбы:
+pdfjs-document-properties-author = Авторы:
+pdfjs-document-properties-subject = Тақырыбы:
+pdfjs-document-properties-keywords = Кілт сөздер:
+pdfjs-document-properties-creation-date = Жасалған күні:
+pdfjs-document-properties-modification-date = Түзету күні:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Жасаған:
+pdfjs-document-properties-producer = PDF өндірген:
+pdfjs-document-properties-version = PDF нұсқасы:
+pdfjs-document-properties-page-count = Беттер саны:
+pdfjs-document-properties-page-size = Бет өлшемі:
+pdfjs-document-properties-page-size-unit-inches = дюйм
+pdfjs-document-properties-page-size-unit-millimeters = мм
+pdfjs-document-properties-page-size-orientation-portrait = тік
+pdfjs-document-properties-page-size-orientation-landscape = жатық
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Жылдам Web көрінісі:
+pdfjs-document-properties-linearized-yes = Иә
+pdfjs-document-properties-linearized-no = Жоқ
+pdfjs-document-properties-close-button = Жабу
+
+## Print
+
+pdfjs-print-progress-message = Құжатты баспаға шығару үшін дайындау…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Бас тарту
+pdfjs-printing-not-supported = Ескерту: Баспаға шығаруды бұл браузер толығымен қолдамайды.
+pdfjs-printing-not-ready = Ескерту: Баспаға шығару үшін, бұл PDF толығымен жүктеліп алынбады.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Бүйір панелін көрсету/жасыру
+pdfjs-toggle-sidebar-notification-button =
+ .title = Бүйір панелін көрсету/жасыру (құжатта құрылымы/салынымдар/қабаттар бар)
+pdfjs-toggle-sidebar-button-label = Бүйір панелін көрсету/жасыру
+pdfjs-document-outline-button =
+ .title = Құжат құрылымын көрсету (барлық нәрселерді жазық қылу/жинау үшін қос шерту керек)
+pdfjs-document-outline-button-label = Құжат құрамасы
+pdfjs-attachments-button =
+ .title = Салынымдарды көрсету
+pdfjs-attachments-button-label = Салынымдар
+pdfjs-layers-button =
+ .title = Қабаттарды көрсету (барлық қабаттарды бастапқы күйге келтіру үшін екі рет шертіңіз)
+pdfjs-layers-button-label = Қабаттар
+pdfjs-thumbs-button =
+ .title = Кіші көріністерді көрсету
+pdfjs-thumbs-button-label = Кіші көріністер
+pdfjs-current-outline-item-button =
+ .title = Құрылымның ағымдағы элементін табу
+pdfjs-current-outline-item-button-label = Құрылымның ағымдағы элементі
+pdfjs-findbar-button =
+ .title = Құжаттан табу
+pdfjs-findbar-button-label = Табу
+pdfjs-additional-layers = Қосымша қабаттар
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page } парағы
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } парағы үшін кіші көрінісі
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Табу
+ .placeholder = Құжаттан табу…
+pdfjs-find-previous-button =
+ .title = Осы сөздердің мәтіннен алдыңғы кездесуін табу
+pdfjs-find-previous-button-label = Алдыңғысы
+pdfjs-find-next-button =
+ .title = Осы сөздердің мәтіннен келесі кездесуін табу
+pdfjs-find-next-button-label = Келесі
+pdfjs-find-highlight-checkbox = Барлығын түспен ерекшелеу
+pdfjs-find-match-case-checkbox-label = Регистрді ескеру
+pdfjs-find-match-diacritics-checkbox-label = Диакритиканы ескеру
+pdfjs-find-entire-word-checkbox-label = Сөздер толығымен
+pdfjs-find-reached-top = Құжаттың басына жеттік, соңынан бастап жалғастырамыз
+pdfjs-find-reached-bottom = Құжаттың соңына жеттік, басынан бастап жалғастырамыз
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } сәйкестік, барлығы { $total }
+ *[other] { $current } сәйкестік, барлығы { $total }
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] { $limit } сәйкестіктен көп
+ *[other] { $limit } сәйкестіктен көп
+ }
+pdfjs-find-not-found = Сөз(дер) табылмады
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Парақ ені
+pdfjs-page-scale-fit = Парақты сыйдыру
+pdfjs-page-scale-auto = Автомасштабтау
+pdfjs-page-scale-actual = Нақты өлшемі
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Бет { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF жүктеу кезінде қате кетті.
+pdfjs-invalid-file-error = Зақымдалған немесе қате PDF файл.
+pdfjs-missing-file-error = PDF файлы жоқ.
+pdfjs-unexpected-response-error = Сервердің күтпеген жауабы.
+pdfjs-rendering-error = Парақты өңдеу кезінде қате кетті.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } аңдатпасы]
+
+## Password
+
+pdfjs-password-label = Бұл PDF файлын ашу үшін парольді енгізіңіз.
+pdfjs-password-invalid = Пароль дұрыс емес. Қайталап көріңіз.
+pdfjs-password-ok-button = ОК
+pdfjs-password-cancel-button = Бас тарту
+pdfjs-web-fonts-disabled = Веб қаріптері сөндірілген: құрамына енгізілген PDF қаріптерін қолдану мүмкін емес.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Мәтін
+pdfjs-editor-free-text-button-label = Мәтін
+pdfjs-editor-ink-button =
+ .title = Сурет салу
+pdfjs-editor-ink-button-label = Сурет салу
+pdfjs-editor-stamp-button =
+ .title = Суреттерді қосу немесе түзету
+pdfjs-editor-stamp-button-label = Суреттерді қосу немесе түзету
+pdfjs-editor-highlight-button =
+ .title = Ерекшелеу
+pdfjs-editor-highlight-button-label = Ерекшелеу
+pdfjs-highlight-floating-button =
+ .title = Ерекшелеу
+pdfjs-highlight-floating-button1 =
+ .title = Ерекшелеу
+ .aria-label = Ерекшелеу
+pdfjs-highlight-floating-button-label = Ерекшелеу
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Сызбаны өшіру
+pdfjs-editor-remove-freetext-button =
+ .title = Мәтінді өшіру
+pdfjs-editor-remove-stamp-button =
+ .title = Суретті өшіру
+pdfjs-editor-remove-highlight-button =
+ .title = Түспен ерекшелеуді өшіру
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Түс
+pdfjs-editor-free-text-size-input = Өлшемі
+pdfjs-editor-ink-color-input = Түс
+pdfjs-editor-ink-thickness-input = Қалыңдығы
+pdfjs-editor-ink-opacity-input = Мөлдірсіздігі
+pdfjs-editor-stamp-add-image-button =
+ .title = Суретті қосу
+pdfjs-editor-stamp-add-image-button-label = Суретті қосу
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Қалыңдығы
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Мәтіннен басқа элементтерді ерекшелеу кезінде қалыңдықты өзгерту
+pdfjs-free-text =
+ .aria-label = Мәтін түзеткіші
+pdfjs-free-text-default-content = Теруді бастау…
+pdfjs-ink =
+ .aria-label = Сурет түзеткіші
+pdfjs-ink-canvas =
+ .aria-label = Пайдаланушы жасаған сурет
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Балама мәтін
+pdfjs-editor-alt-text-edit-button-label = Балама мәтінді өңдеу
+pdfjs-editor-alt-text-dialog-label = Опцияны таңдау
+pdfjs-editor-alt-text-dialog-description = Балама мәтін адамдар суретті көре алмағанда немесе ол жүктелмегенде көмектеседі.
+pdfjs-editor-alt-text-add-description-label = Сипаттаманы қосу
+pdfjs-editor-alt-text-add-description-description = Тақырыпты, баптауды немесе әрекетті сипаттайтын 1-2 сөйлемді қолдануға тырысыңыз.
+pdfjs-editor-alt-text-mark-decorative-label = Декоративті деп белгілеу
+pdfjs-editor-alt-text-mark-decorative-description = Бұл жиектер немесе су белгілері сияқты оюлық суреттер үшін пайдаланылады.
+pdfjs-editor-alt-text-cancel-button = Бас тарту
+pdfjs-editor-alt-text-save-button = Сақтау
+pdfjs-editor-alt-text-decorative-tooltip = Декоративті деп белгіленген
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Мысалы, "Жас жігіт тамақ ішу үшін үстел басына отырады"
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Жоғарғы сол жақ бұрыш — өлшемін өзгерту
+pdfjs-editor-resizer-label-top-middle = Жоғарғы ортасы — өлшемін өзгерту
+pdfjs-editor-resizer-label-top-right = Жоғарғы оң жақ бұрыш — өлшемін өзгерту
+pdfjs-editor-resizer-label-middle-right = Ортаңғы оң жақ — өлшемін өзгерту
+pdfjs-editor-resizer-label-bottom-right = Төменгі оң жақ бұрыш — өлшемін өзгерту
+pdfjs-editor-resizer-label-bottom-middle = Төменгі ортасы — өлшемін өзгерту
+pdfjs-editor-resizer-label-bottom-left = Төменгі сол жақ бұрыш — өлшемін өзгерту
+pdfjs-editor-resizer-label-middle-left = Ортаңғы сол жақ — өлшемін өзгерту
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Ерекшелеу түсі
+pdfjs-editor-colorpicker-button =
+ .title = Түсті өзгерту
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Түс таңдаулары
+pdfjs-editor-colorpicker-yellow =
+ .title = Сары
+pdfjs-editor-colorpicker-green =
+ .title = Жасыл
+pdfjs-editor-colorpicker-blue =
+ .title = Көк
+pdfjs-editor-colorpicker-pink =
+ .title = Қызғылт
+pdfjs-editor-colorpicker-red =
+ .title = Қызыл
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Барлығын көрсету
+pdfjs-editor-highlight-show-all-button =
+ .title = Барлығын көрсету
diff --git a/web/locale/km/viewer.ftl b/web/locale/km/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..6efd105460f5f691a990896cb7e96fe1b0e69471
--- /dev/null
+++ b/web/locale/km/viewer.ftl
@@ -0,0 +1,223 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = ទំព័រមុន
+pdfjs-previous-button-label = មុន
+pdfjs-next-button =
+ .title = ទំព័របន្ទាប់
+pdfjs-next-button-label = បន្ទាប់
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = ទំព័រ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = នៃ { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } នៃ { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = បង្រួម
+pdfjs-zoom-out-button-label = បង្រួម
+pdfjs-zoom-in-button =
+ .title = ពង្រីក
+pdfjs-zoom-in-button-label = ពង្រីក
+pdfjs-zoom-select =
+ .title = ពង្រីក
+pdfjs-presentation-mode-button =
+ .title = ប្ដូរទៅរបៀបបទបង្ហាញ
+pdfjs-presentation-mode-button-label = របៀបបទបង្ហាញ
+pdfjs-open-file-button =
+ .title = បើកឯកសារ
+pdfjs-open-file-button-label = បើក
+pdfjs-print-button =
+ .title = បោះពុម្ព
+pdfjs-print-button-label = បោះពុម្ព
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ឧបករណ៍
+pdfjs-tools-button-label = ឧបករណ៍
+pdfjs-first-page-button =
+ .title = ទៅកាន់ទំព័រដំបូង
+pdfjs-first-page-button-label = ទៅកាន់ទំព័រដំបូង
+pdfjs-last-page-button =
+ .title = ទៅកាន់ទំព័រចុងក្រោយ
+pdfjs-last-page-button-label = ទៅកាន់ទំព័រចុងក្រោយ
+pdfjs-page-rotate-cw-button =
+ .title = បង្វិលស្របទ្រនិចនាឡិកា
+pdfjs-page-rotate-cw-button-label = បង្វិលស្របទ្រនិចនាឡិកា
+pdfjs-page-rotate-ccw-button =
+ .title = បង្វិលច្រាសទ្រនិចនាឡិកា
+pdfjs-page-rotate-ccw-button-label = បង្វិលច្រាសទ្រនិចនាឡិកា
+pdfjs-cursor-text-select-tool-button =
+ .title = បើកឧបករណ៍ជ្រើសអត្ថបទ
+pdfjs-cursor-text-select-tool-button-label = ឧបករណ៍ជ្រើសអត្ថបទ
+pdfjs-cursor-hand-tool-button =
+ .title = បើកឧបករណ៍ដៃ
+pdfjs-cursor-hand-tool-button-label = ឧបករណ៍ដៃ
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = លក្ខណសម្បត្តិឯកសារ…
+pdfjs-document-properties-button-label = លក្ខណសម្បត្តិឯកសារ…
+pdfjs-document-properties-file-name = ឈ្មោះឯកសារ៖
+pdfjs-document-properties-file-size = ទំហំឯកសារ៖
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } បៃ)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } បៃ)
+pdfjs-document-properties-title = ចំណងជើង៖
+pdfjs-document-properties-author = អ្នកនិពន្ធ៖
+pdfjs-document-properties-subject = ប្រធានបទ៖
+pdfjs-document-properties-keywords = ពាក្យគន្លឹះ៖
+pdfjs-document-properties-creation-date = កាលបរិច្ឆេទបង្កើត៖
+pdfjs-document-properties-modification-date = កាលបរិច្ឆេទកែប្រែ៖
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = អ្នកបង្កើត៖
+pdfjs-document-properties-producer = កម្មវិធីបង្កើត PDF ៖
+pdfjs-document-properties-version = កំណែ PDF ៖
+pdfjs-document-properties-page-count = ចំនួនទំព័រ៖
+pdfjs-document-properties-page-size-unit-inches = អ៊ីញ
+pdfjs-document-properties-page-size-unit-millimeters = មម
+pdfjs-document-properties-page-size-orientation-portrait = បញ្ឈរ
+pdfjs-document-properties-page-size-orientation-landscape = ផ្តេក
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = សំបុត្រ
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+pdfjs-document-properties-linearized-yes = បាទ/ចាស
+pdfjs-document-properties-linearized-no = ទេ
+pdfjs-document-properties-close-button = បិទ
+
+## Print
+
+pdfjs-print-progress-message = កំពុងរៀបចំឯកសារសម្រាប់បោះពុម្ព…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = បោះបង់
+pdfjs-printing-not-supported = ការព្រមាន ៖ ការបោះពុម្ពមិនត្រូវបានគាំទ្រពេញលេញដោយកម្មវិធីរុករកនេះទេ ។
+pdfjs-printing-not-ready = ព្រមាន៖ PDF មិនត្រូវបានផ្ទុកទាំងស្រុងដើម្បីបោះពុម្ពទេ។
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = បិទ/បើកគ្រាប់រំកិល
+pdfjs-toggle-sidebar-button-label = បិទ/បើកគ្រាប់រំកិល
+pdfjs-document-outline-button =
+ .title = បង្ហាញគ្រោងឯកសារ (ចុចទ្វេដងដើម្បីពង្រីក/បង្រួមធាតុទាំងអស់)
+pdfjs-document-outline-button-label = គ្រោងឯកសារ
+pdfjs-attachments-button =
+ .title = បង្ហាញឯកសារភ្ជាប់
+pdfjs-attachments-button-label = ឯកសារភ្ជាប់
+pdfjs-thumbs-button =
+ .title = បង្ហាញរូបភាពតូចៗ
+pdfjs-thumbs-button-label = រួបភាពតូចៗ
+pdfjs-findbar-button =
+ .title = រកនៅក្នុងឯកសារ
+pdfjs-findbar-button-label = រក
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = ទំព័រ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = រូបភាពតូចរបស់ទំព័រ { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = រក
+ .placeholder = រកនៅក្នុងឯកសារ...
+pdfjs-find-previous-button =
+ .title = រកពាក្យ ឬឃ្លាដែលបានជួបមុន
+pdfjs-find-previous-button-label = មុន
+pdfjs-find-next-button =
+ .title = រកពាក្យ ឬឃ្លាដែលបានជួបបន្ទាប់
+pdfjs-find-next-button-label = បន្ទាប់
+pdfjs-find-highlight-checkbox = បន្លិចទាំងអស់
+pdfjs-find-match-case-checkbox-label = ករណីដំណូច
+pdfjs-find-reached-top = បានបន្តពីខាងក្រោម ទៅដល់ខាងលើនៃឯកសារ
+pdfjs-find-reached-bottom = បានបន្តពីខាងលើ ទៅដល់ចុងនៃឯកសារ
+pdfjs-find-not-found = រកមិនឃើញពាក្យ ឬឃ្លា
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = ទទឹងទំព័រ
+pdfjs-page-scale-fit = សមទំព័រ
+pdfjs-page-scale-auto = ពង្រីកស្វ័យប្រវត្តិ
+pdfjs-page-scale-actual = ទំហំជាក់ស្ដែង
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = មានកំហុសបានកើតឡើងពេលកំពុងផ្ទុក PDF ។
+pdfjs-invalid-file-error = ឯកសារ PDF ខូច ឬមិនត្រឹមត្រូវ ។
+pdfjs-missing-file-error = បាត់ឯកសារ PDF
+pdfjs-unexpected-response-error = ការឆ្លើយតមម៉ាស៊ីនមេដែលមិនបានរំពឹង។
+pdfjs-rendering-error = មានកំហុសបានកើតឡើងពេលបង្ហាញទំព័រ ។
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } ចំណារពន្យល់]
+
+## Password
+
+pdfjs-password-label = បញ្ចូលពាក្យសម្ងាត់ដើម្បីបើកឯកសារ PDF នេះ។
+pdfjs-password-invalid = ពាក្យសម្ងាត់មិនត្រឹមត្រូវ។ សូមព្យាយាមម្ដងទៀត។
+pdfjs-password-ok-button = យល់ព្រម
+pdfjs-password-cancel-button = បោះបង់
+pdfjs-web-fonts-disabled = បានបិទពុម្ពអក្សរបណ្ដាញ ៖ មិនអាចប្រើពុម្ពអក្សរ PDF ដែលបានបង្កប់បានទេ ។
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/kn/viewer.ftl b/web/locale/kn/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..03322555e194156db29945cc45f63d70f8651b5e
--- /dev/null
+++ b/web/locale/kn/viewer.ftl
@@ -0,0 +1,213 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = ಹಿಂದಿನ ಪುಟ
+pdfjs-previous-button-label = ಹಿಂದಿನ
+pdfjs-next-button =
+ .title = ಮುಂದಿನ ಪುಟ
+pdfjs-next-button-label = ಮುಂದಿನ
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = ಪುಟ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } ರಲ್ಲಿ
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pagesCount } ರಲ್ಲಿ { $pageNumber })
+pdfjs-zoom-out-button =
+ .title = ಕಿರಿದಾಗಿಸು
+pdfjs-zoom-out-button-label = ಕಿರಿದಾಗಿಸಿ
+pdfjs-zoom-in-button =
+ .title = ಹಿರಿದಾಗಿಸು
+pdfjs-zoom-in-button-label = ಹಿರಿದಾಗಿಸಿ
+pdfjs-zoom-select =
+ .title = ಗಾತ್ರಬದಲಿಸು
+pdfjs-presentation-mode-button =
+ .title = ಪ್ರಸ್ತುತಿ (ಪ್ರಸೆಂಟೇಶನ್) ಕ್ರಮಕ್ಕೆ ಬದಲಾಯಿಸು
+pdfjs-presentation-mode-button-label = ಪ್ರಸ್ತುತಿ (ಪ್ರಸೆಂಟೇಶನ್) ಕ್ರಮ
+pdfjs-open-file-button =
+ .title = ಕಡತವನ್ನು ತೆರೆ
+pdfjs-open-file-button-label = ತೆರೆಯಿರಿ
+pdfjs-print-button =
+ .title = ಮುದ್ರಿಸು
+pdfjs-print-button-label = ಮುದ್ರಿಸಿ
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ಉಪಕರಣಗಳು
+pdfjs-tools-button-label = ಉಪಕರಣಗಳು
+pdfjs-first-page-button =
+ .title = ಮೊದಲ ಪುಟಕ್ಕೆ ತೆರಳು
+pdfjs-first-page-button-label = ಮೊದಲ ಪುಟಕ್ಕೆ ತೆರಳು
+pdfjs-last-page-button =
+ .title = ಕೊನೆಯ ಪುಟಕ್ಕೆ ತೆರಳು
+pdfjs-last-page-button-label = ಕೊನೆಯ ಪುಟಕ್ಕೆ ತೆರಳು
+pdfjs-page-rotate-cw-button =
+ .title = ಪ್ರದಕ್ಷಿಣೆಯಲ್ಲಿ ತಿರುಗಿಸು
+pdfjs-page-rotate-cw-button-label = ಪ್ರದಕ್ಷಿಣೆಯಲ್ಲಿ ತಿರುಗಿಸು
+pdfjs-page-rotate-ccw-button =
+ .title = ಅಪ್ರದಕ್ಷಿಣೆಯಲ್ಲಿ ತಿರುಗಿಸು
+pdfjs-page-rotate-ccw-button-label = ಅಪ್ರದಕ್ಷಿಣೆಯಲ್ಲಿ ತಿರುಗಿಸು
+pdfjs-cursor-text-select-tool-button =
+ .title = ಪಠ್ಯ ಆಯ್ಕೆ ಉಪಕರಣವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ
+pdfjs-cursor-text-select-tool-button-label = ಪಠ್ಯ ಆಯ್ಕೆಯ ಉಪಕರಣ
+pdfjs-cursor-hand-tool-button =
+ .title = ಕೈ ಉಪಕರಣವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ
+pdfjs-cursor-hand-tool-button-label = ಕೈ ಉಪಕರಣ
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = ಡಾಕ್ಯುಮೆಂಟ್ ಗುಣಗಳು...
+pdfjs-document-properties-button-label = ಡಾಕ್ಯುಮೆಂಟ್ ಗುಣಗಳು...
+pdfjs-document-properties-file-name = ಕಡತದ ಹೆಸರು:
+pdfjs-document-properties-file-size = ಕಡತದ ಗಾತ್ರ:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } ಬೈಟ್ಗಳು)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } ಬೈಟ್ಗಳು)
+pdfjs-document-properties-title = ಶೀರ್ಷಿಕೆ:
+pdfjs-document-properties-author = ಕರ್ತೃ:
+pdfjs-document-properties-subject = ವಿಷಯ:
+pdfjs-document-properties-keywords = ಮುಖ್ಯಪದಗಳು:
+pdfjs-document-properties-creation-date = ರಚಿಸಿದ ದಿನಾಂಕ:
+pdfjs-document-properties-modification-date = ಮಾರ್ಪಡಿಸಲಾದ ದಿನಾಂಕ:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = ರಚಿಸಿದವರು:
+pdfjs-document-properties-producer = PDF ಉತ್ಪಾದಕ:
+pdfjs-document-properties-version = PDF ಆವೃತ್ತಿ:
+pdfjs-document-properties-page-count = ಪುಟದ ಎಣಿಕೆ:
+pdfjs-document-properties-page-size-unit-inches = ಇದರಲ್ಲಿ
+pdfjs-document-properties-page-size-orientation-portrait = ಭಾವಚಿತ್ರ
+pdfjs-document-properties-page-size-orientation-landscape = ಪ್ರಕೃತಿ ಚಿತ್ರ
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+
+##
+
+pdfjs-document-properties-close-button = ಮುಚ್ಚು
+
+## Print
+
+pdfjs-print-progress-message = ಮುದ್ರಿಸುವುದಕ್ಕಾಗಿ ದಸ್ತಾವೇಜನ್ನು ಸಿದ್ಧಗೊಳಿಸಲಾಗುತ್ತಿದೆ…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = ರದ್ದು ಮಾಡು
+pdfjs-printing-not-supported = ಎಚ್ಚರಿಕೆ: ಈ ಜಾಲವೀಕ್ಷಕದಲ್ಲಿ ಮುದ್ರಣಕ್ಕೆ ಸಂಪೂರ್ಣ ಬೆಂಬಲವಿಲ್ಲ.
+pdfjs-printing-not-ready = ಎಚ್ಚರಿಕೆ: PDF ಕಡತವು ಮುದ್ರಿಸಲು ಸಂಪೂರ್ಣವಾಗಿ ಲೋಡ್ ಆಗಿಲ್ಲ.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = ಬದಿಪಟ್ಟಿಯನ್ನು ಹೊರಳಿಸು
+pdfjs-toggle-sidebar-button-label = ಬದಿಪಟ್ಟಿಯನ್ನು ಹೊರಳಿಸು
+pdfjs-document-outline-button-label = ದಸ್ತಾವೇಜಿನ ಹೊರರೇಖೆ
+pdfjs-attachments-button =
+ .title = ಲಗತ್ತುಗಳನ್ನು ತೋರಿಸು
+pdfjs-attachments-button-label = ಲಗತ್ತುಗಳು
+pdfjs-thumbs-button =
+ .title = ಚಿಕ್ಕಚಿತ್ರದಂತೆ ತೋರಿಸು
+pdfjs-thumbs-button-label = ಚಿಕ್ಕಚಿತ್ರಗಳು
+pdfjs-findbar-button =
+ .title = ದಸ್ತಾವೇಜಿನಲ್ಲಿ ಹುಡುಕು
+pdfjs-findbar-button-label = ಹುಡುಕು
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = ಪುಟ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = ಪುಟವನ್ನು ಚಿಕ್ಕಚಿತ್ರದಂತೆ ತೋರಿಸು { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = ಹುಡುಕು
+ .placeholder = ದಸ್ತಾವೇಜಿನಲ್ಲಿ ಹುಡುಕು…
+pdfjs-find-previous-button =
+ .title = ವಾಕ್ಯದ ಹಿಂದಿನ ಇರುವಿಕೆಯನ್ನು ಹುಡುಕು
+pdfjs-find-previous-button-label = ಹಿಂದಿನ
+pdfjs-find-next-button =
+ .title = ವಾಕ್ಯದ ಮುಂದಿನ ಇರುವಿಕೆಯನ್ನು ಹುಡುಕು
+pdfjs-find-next-button-label = ಮುಂದಿನ
+pdfjs-find-highlight-checkbox = ಎಲ್ಲವನ್ನು ಹೈಲೈಟ್ ಮಾಡು
+pdfjs-find-match-case-checkbox-label = ಕೇಸನ್ನು ಹೊಂದಿಸು
+pdfjs-find-reached-top = ದಸ್ತಾವೇಜಿನ ಮೇಲ್ಭಾಗವನ್ನು ತಲುಪಿದೆ, ಕೆಳಗಿನಿಂದ ಆರಂಭಿಸು
+pdfjs-find-reached-bottom = ದಸ್ತಾವೇಜಿನ ಕೊನೆಯನ್ನು ತಲುಪಿದೆ, ಮೇಲಿನಿಂದ ಆರಂಭಿಸು
+pdfjs-find-not-found = ವಾಕ್ಯವು ಕಂಡು ಬಂದಿಲ್ಲ
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = ಪುಟದ ಅಗಲ
+pdfjs-page-scale-fit = ಪುಟದ ಸರಿಹೊಂದಿಕೆ
+pdfjs-page-scale-auto = ಸ್ವಯಂಚಾಲಿತ ಗಾತ್ರಬದಲಾವಣೆ
+pdfjs-page-scale-actual = ನಿಜವಾದ ಗಾತ್ರ
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF ಅನ್ನು ಲೋಡ್ ಮಾಡುವಾಗ ಒಂದು ದೋಷ ಎದುರಾಗಿದೆ.
+pdfjs-invalid-file-error = ಅಮಾನ್ಯವಾದ ಅಥವ ಹಾಳಾದ PDF ಕಡತ.
+pdfjs-missing-file-error = PDF ಕಡತ ಇಲ್ಲ.
+pdfjs-unexpected-response-error = ಅನಿರೀಕ್ಷಿತವಾದ ಪೂರೈಕೆಗಣಕದ ಪ್ರತಿಕ್ರಿಯೆ.
+pdfjs-rendering-error = ಪುಟವನ್ನು ನಿರೂಪಿಸುವಾಗ ಒಂದು ದೋಷ ಎದುರಾಗಿದೆ.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } ಟಿಪ್ಪಣಿ]
+
+## Password
+
+pdfjs-password-label = PDF ಅನ್ನು ತೆರೆಯಲು ಗುಪ್ತಪದವನ್ನು ನಮೂದಿಸಿ.
+pdfjs-password-invalid = ಅಮಾನ್ಯವಾದ ಗುಪ್ತಪದ, ದಯವಿಟ್ಟು ಇನ್ನೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = ರದ್ದು ಮಾಡು
+pdfjs-web-fonts-disabled = ಜಾಲ ಅಕ್ಷರಶೈಲಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ: ಅಡಕಗೊಳಿಸಿದ PDF ಅಕ್ಷರಶೈಲಿಗಳನ್ನು ಬಳಸಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ko/viewer.ftl b/web/locale/ko/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..2afce14404bb01e01cb37358f69ef05909d0695f
--- /dev/null
+++ b/web/locale/ko/viewer.ftl
@@ -0,0 +1,394 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = 이전 페이지
+pdfjs-previous-button-label = 이전
+pdfjs-next-button =
+ .title = 다음 페이지
+pdfjs-next-button-label = 다음
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = 페이지
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = / { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = 축소
+pdfjs-zoom-out-button-label = 축소
+pdfjs-zoom-in-button =
+ .title = 확대
+pdfjs-zoom-in-button-label = 확대
+pdfjs-zoom-select =
+ .title = 확대/축소
+pdfjs-presentation-mode-button =
+ .title = 프레젠테이션 모드로 전환
+pdfjs-presentation-mode-button-label = 프레젠테이션 모드
+pdfjs-open-file-button =
+ .title = 파일 열기
+pdfjs-open-file-button-label = 열기
+pdfjs-print-button =
+ .title = 인쇄
+pdfjs-print-button-label = 인쇄
+pdfjs-save-button =
+ .title = 저장
+pdfjs-save-button-label = 저장
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = 다운로드
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = 다운로드
+pdfjs-bookmark-button =
+ .title = 현재 페이지 (현재 페이지에서 URL 보기)
+pdfjs-bookmark-button-label = 현재 페이지
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = 앱에서 열기
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = 앱에서 열기
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = 도구
+pdfjs-tools-button-label = 도구
+pdfjs-first-page-button =
+ .title = 첫 페이지로 이동
+pdfjs-first-page-button-label = 첫 페이지로 이동
+pdfjs-last-page-button =
+ .title = 마지막 페이지로 이동
+pdfjs-last-page-button-label = 마지막 페이지로 이동
+pdfjs-page-rotate-cw-button =
+ .title = 시계방향으로 회전
+pdfjs-page-rotate-cw-button-label = 시계방향으로 회전
+pdfjs-page-rotate-ccw-button =
+ .title = 시계 반대방향으로 회전
+pdfjs-page-rotate-ccw-button-label = 시계 반대방향으로 회전
+pdfjs-cursor-text-select-tool-button =
+ .title = 텍스트 선택 도구 활성화
+pdfjs-cursor-text-select-tool-button-label = 텍스트 선택 도구
+pdfjs-cursor-hand-tool-button =
+ .title = 손 도구 활성화
+pdfjs-cursor-hand-tool-button-label = 손 도구
+pdfjs-scroll-page-button =
+ .title = 페이지 스크롤 사용
+pdfjs-scroll-page-button-label = 페이지 스크롤
+pdfjs-scroll-vertical-button =
+ .title = 세로 스크롤 사용
+pdfjs-scroll-vertical-button-label = 세로 스크롤
+pdfjs-scroll-horizontal-button =
+ .title = 가로 스크롤 사용
+pdfjs-scroll-horizontal-button-label = 가로 스크롤
+pdfjs-scroll-wrapped-button =
+ .title = 래핑(자동 줄 바꿈) 스크롤 사용
+pdfjs-scroll-wrapped-button-label = 래핑 스크롤
+pdfjs-spread-none-button =
+ .title = 한 페이지 보기
+pdfjs-spread-none-button-label = 펼침 없음
+pdfjs-spread-odd-button =
+ .title = 홀수 페이지로 시작하는 두 페이지 보기
+pdfjs-spread-odd-button-label = 홀수 펼침
+pdfjs-spread-even-button =
+ .title = 짝수 페이지로 시작하는 두 페이지 보기
+pdfjs-spread-even-button-label = 짝수 펼침
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = 문서 속성…
+pdfjs-document-properties-button-label = 문서 속성…
+pdfjs-document-properties-file-name = 파일 이름:
+pdfjs-document-properties-file-size = 파일 크기:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b }바이트)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b }바이트)
+pdfjs-document-properties-title = 제목:
+pdfjs-document-properties-author = 작성자:
+pdfjs-document-properties-subject = 주제:
+pdfjs-document-properties-keywords = 키워드:
+pdfjs-document-properties-creation-date = 작성 날짜:
+pdfjs-document-properties-modification-date = 수정 날짜:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = 작성 프로그램:
+pdfjs-document-properties-producer = PDF 변환 소프트웨어:
+pdfjs-document-properties-version = PDF 버전:
+pdfjs-document-properties-page-count = 페이지 수:
+pdfjs-document-properties-page-size = 페이지 크기:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = 세로 방향
+pdfjs-document-properties-page-size-orientation-landscape = 가로 방향
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = 레터
+pdfjs-document-properties-page-size-name-legal = 리걸
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = 빠른 웹 보기:
+pdfjs-document-properties-linearized-yes = 예
+pdfjs-document-properties-linearized-no = 아니요
+pdfjs-document-properties-close-button = 닫기
+
+## Print
+
+pdfjs-print-progress-message = 인쇄 문서 준비 중…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = 취소
+pdfjs-printing-not-supported = 경고: 이 브라우저는 인쇄를 완전히 지원하지 않습니다.
+pdfjs-printing-not-ready = 경고: 이 PDF를 인쇄를 할 수 있을 정도로 읽어들이지 못했습니다.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = 사이드바 표시/숨기기
+pdfjs-toggle-sidebar-notification-button =
+ .title = 사이드바 표시/숨기기 (문서에 아웃라인/첨부파일/레이어 포함됨)
+pdfjs-toggle-sidebar-button-label = 사이드바 표시/숨기기
+pdfjs-document-outline-button =
+ .title = 문서 아웃라인 보기 (더블 클릭해서 모든 항목 펼치기/접기)
+pdfjs-document-outline-button-label = 문서 아웃라인
+pdfjs-attachments-button =
+ .title = 첨부파일 보기
+pdfjs-attachments-button-label = 첨부파일
+pdfjs-layers-button =
+ .title = 레이어 보기 (더블 클릭해서 모든 레이어를 기본 상태로 재설정)
+pdfjs-layers-button-label = 레이어
+pdfjs-thumbs-button =
+ .title = 미리보기
+pdfjs-thumbs-button-label = 미리보기
+pdfjs-current-outline-item-button =
+ .title = 현재 아웃라인 항목 찾기
+pdfjs-current-outline-item-button-label = 현재 아웃라인 항목
+pdfjs-findbar-button =
+ .title = 검색
+pdfjs-findbar-button-label = 검색
+pdfjs-additional-layers = 추가 레이어
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page } 페이지
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } 페이지 미리보기
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = 찾기
+ .placeholder = 문서에서 찾기…
+pdfjs-find-previous-button =
+ .title = 지정 문자열에 일치하는 1개 부분을 검색
+pdfjs-find-previous-button-label = 이전
+pdfjs-find-next-button =
+ .title = 지정 문자열에 일치하는 다음 부분을 검색
+pdfjs-find-next-button-label = 다음
+pdfjs-find-highlight-checkbox = 모두 강조 표시
+pdfjs-find-match-case-checkbox-label = 대/소문자 구분
+pdfjs-find-match-diacritics-checkbox-label = 분음 부호 일치
+pdfjs-find-entire-word-checkbox-label = 단어 단위로
+pdfjs-find-reached-top = 문서 처음까지 검색하고 끝으로 돌아와 검색했습니다.
+pdfjs-find-reached-bottom = 문서 끝까지 검색하고 앞으로 돌아와 검색했습니다.
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count = { $current } / { $total } 일치
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit = { $limit }개 이상 일치
+pdfjs-find-not-found = 검색 결과 없음
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = 페이지 너비에 맞추기
+pdfjs-page-scale-fit = 페이지에 맞추기
+pdfjs-page-scale-auto = 자동
+pdfjs-page-scale-actual = 실제 크기
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = { $page } 페이지
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF를 로드하는 동안 오류가 발생했습니다.
+pdfjs-invalid-file-error = 잘못되었거나 손상된 PDF 파일.
+pdfjs-missing-file-error = PDF 파일 없음.
+pdfjs-unexpected-response-error = 예기치 않은 서버 응답입니다.
+pdfjs-rendering-error = 페이지를 렌더링하는 동안 오류가 발생했습니다.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date } { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } 주석]
+
+## Password
+
+pdfjs-password-label = 이 PDF 파일을 열 수 있는 비밀번호를 입력하세요.
+pdfjs-password-invalid = 잘못된 비밀번호입니다. 다시 시도하세요.
+pdfjs-password-ok-button = 확인
+pdfjs-password-cancel-button = 취소
+pdfjs-web-fonts-disabled = 웹 폰트가 비활성화됨: 내장된 PDF 글꼴을 사용할 수 없습니다.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = 텍스트
+pdfjs-editor-free-text-button-label = 텍스트
+pdfjs-editor-ink-button =
+ .title = 그리기
+pdfjs-editor-ink-button-label = 그리기
+pdfjs-editor-stamp-button =
+ .title = 이미지 추가 또는 편집
+pdfjs-editor-stamp-button-label = 이미지 추가 또는 편집
+pdfjs-editor-highlight-button =
+ .title = 강조 표시
+pdfjs-editor-highlight-button-label = 강조 표시
+pdfjs-highlight-floating-button =
+ .title = 강조 표시
+pdfjs-highlight-floating-button1 =
+ .title = 강조 표시
+ .aria-label = 강조 표시
+pdfjs-highlight-floating-button-label = 강조 표시
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = 그리기 제거
+pdfjs-editor-remove-freetext-button =
+ .title = 텍스트 제거
+pdfjs-editor-remove-stamp-button =
+ .title = 이미지 제거
+pdfjs-editor-remove-highlight-button =
+ .title = 강조 표시 제거
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = 색상
+pdfjs-editor-free-text-size-input = 크기
+pdfjs-editor-ink-color-input = 색상
+pdfjs-editor-ink-thickness-input = 두께
+pdfjs-editor-ink-opacity-input = 불투명도
+pdfjs-editor-stamp-add-image-button =
+ .title = 이미지 추가
+pdfjs-editor-stamp-add-image-button-label = 이미지 추가
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = 두께
+pdfjs-editor-free-highlight-thickness-title =
+ .title = 텍스트 이외의 항목을 강조 표시할 때 두께 변경
+pdfjs-free-text =
+ .aria-label = 텍스트 편집기
+pdfjs-free-text-default-content = 입력하세요…
+pdfjs-ink =
+ .aria-label = 그리기 편집기
+pdfjs-ink-canvas =
+ .aria-label = 사용자 생성 이미지
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = 대체 텍스트
+pdfjs-editor-alt-text-edit-button-label = 대체 텍스트 편집
+pdfjs-editor-alt-text-dialog-label = 옵션을 선택하세요
+pdfjs-editor-alt-text-dialog-description = 대체 텍스트는 사람들이 이미지를 볼 수 없거나 이미지가 로드되지 않을 때 도움이 됩니다.
+pdfjs-editor-alt-text-add-description-label = 설명 추가
+pdfjs-editor-alt-text-add-description-description = 주제, 설정, 동작을 설명하는 1~2개의 문장을 목표로 하세요.
+pdfjs-editor-alt-text-mark-decorative-label = 장식용으로 표시
+pdfjs-editor-alt-text-mark-decorative-description = 테두리나 워터마크와 같은 장식적인 이미지에 사용됩니다.
+pdfjs-editor-alt-text-cancel-button = 취소
+pdfjs-editor-alt-text-save-button = 저장
+pdfjs-editor-alt-text-decorative-tooltip = 장식용으로 표시됨
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = 예를 들어, “한 청년이 식탁에 앉아 식사를 하고 있습니다.”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = 왼쪽 위 — 크기 조정
+pdfjs-editor-resizer-label-top-middle = 가운데 위 - 크기 조정
+pdfjs-editor-resizer-label-top-right = 오른쪽 위 — 크기 조정
+pdfjs-editor-resizer-label-middle-right = 오른쪽 가운데 — 크기 조정
+pdfjs-editor-resizer-label-bottom-right = 오른쪽 아래 - 크기 조정
+pdfjs-editor-resizer-label-bottom-middle = 가운데 아래 — 크기 조정
+pdfjs-editor-resizer-label-bottom-left = 왼쪽 아래 - 크기 조정
+pdfjs-editor-resizer-label-middle-left = 왼쪽 가운데 — 크기 조정
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = 색상
+pdfjs-editor-colorpicker-button =
+ .title = 색상 변경
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = 색상 선택
+pdfjs-editor-colorpicker-yellow =
+ .title = 노란색
+pdfjs-editor-colorpicker-green =
+ .title = 녹색
+pdfjs-editor-colorpicker-blue =
+ .title = 파란색
+pdfjs-editor-colorpicker-pink =
+ .title = 분홍색
+pdfjs-editor-colorpicker-red =
+ .title = 빨간색
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = 모두 보기
+pdfjs-editor-highlight-show-all-button =
+ .title = 모두 보기
diff --git a/web/locale/lij/viewer.ftl b/web/locale/lij/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..b2941f9f659b5e9ada14be71eb3fad7456f1fd47
--- /dev/null
+++ b/web/locale/lij/viewer.ftl
@@ -0,0 +1,247 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pagina primma
+pdfjs-previous-button-label = Precedente
+pdfjs-next-button =
+ .title = Pagina dòppo
+pdfjs-next-button-label = Pròscima
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pagina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Diminoisci zoom
+pdfjs-zoom-out-button-label = Diminoisci zoom
+pdfjs-zoom-in-button =
+ .title = Aomenta zoom
+pdfjs-zoom-in-button-label = Aomenta zoom
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Vanni into mòddo de prezentaçion
+pdfjs-presentation-mode-button-label = Mòddo de prezentaçion
+pdfjs-open-file-button =
+ .title = Arvi file
+pdfjs-open-file-button-label = Arvi
+pdfjs-print-button =
+ .title = Stanpa
+pdfjs-print-button-label = Stanpa
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Atressi
+pdfjs-tools-button-label = Atressi
+pdfjs-first-page-button =
+ .title = Vanni a-a primma pagina
+pdfjs-first-page-button-label = Vanni a-a primma pagina
+pdfjs-last-page-button =
+ .title = Vanni a l'urtima pagina
+pdfjs-last-page-button-label = Vanni a l'urtima pagina
+pdfjs-page-rotate-cw-button =
+ .title = Gia into verso oraio
+pdfjs-page-rotate-cw-button-label = Gia into verso oraio
+pdfjs-page-rotate-ccw-button =
+ .title = Gia into verso antioraio
+pdfjs-page-rotate-ccw-button-label = Gia into verso antioraio
+pdfjs-cursor-text-select-tool-button =
+ .title = Abilita strumento de seleçion do testo
+pdfjs-cursor-text-select-tool-button-label = Strumento de seleçion do testo
+pdfjs-cursor-hand-tool-button =
+ .title = Abilita strumento man
+pdfjs-cursor-hand-tool-button-label = Strumento man
+pdfjs-scroll-vertical-button =
+ .title = Deuvia rebelamento verticale
+pdfjs-scroll-vertical-button-label = Rebelamento verticale
+pdfjs-scroll-horizontal-button =
+ .title = Deuvia rebelamento orizontâ
+pdfjs-scroll-horizontal-button-label = Rebelamento orizontâ
+pdfjs-scroll-wrapped-button =
+ .title = Deuvia rebelamento incapsolou
+pdfjs-scroll-wrapped-button-label = Rebelamento incapsolou
+pdfjs-spread-none-button =
+ .title = No unite a-a difuxon de pagina
+pdfjs-spread-none-button-label = No difuxon
+pdfjs-spread-odd-button =
+ .title = Uniscite a-a difuxon de pagina co-o numero dèspa
+pdfjs-spread-odd-button-label = Difuxon dèspa
+pdfjs-spread-even-button =
+ .title = Uniscite a-a difuxon de pagina co-o numero pari
+pdfjs-spread-even-button-label = Difuxon pari
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propietæ do documento…
+pdfjs-document-properties-button-label = Propietæ do documento…
+pdfjs-document-properties-file-name = Nomme schedaio:
+pdfjs-document-properties-file-size = Dimenscion schedaio:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } byte)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte)
+pdfjs-document-properties-title = Titolo:
+pdfjs-document-properties-author = Aoto:
+pdfjs-document-properties-subject = Ogetto:
+pdfjs-document-properties-keywords = Paròlle ciave:
+pdfjs-document-properties-creation-date = Dæta creaçion:
+pdfjs-document-properties-modification-date = Dæta cangiamento:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Aotô originale:
+pdfjs-document-properties-producer = Produtô PDF:
+pdfjs-document-properties-version = Verscion PDF:
+pdfjs-document-properties-page-count = Contezzo pagine:
+pdfjs-document-properties-page-size = Dimenscion da pagina:
+pdfjs-document-properties-page-size-unit-inches = dii gròsci
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = drito
+pdfjs-document-properties-page-size-orientation-landscape = desteizo
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letia
+pdfjs-document-properties-page-size-name-legal = Lezze
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista veloce do Web:
+pdfjs-document-properties-linearized-yes = Sci
+pdfjs-document-properties-linearized-no = No
+pdfjs-document-properties-close-button = Særa
+
+## Print
+
+pdfjs-print-progress-message = Praparo o documento pe-a stanpa…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Anulla
+pdfjs-printing-not-supported = Atençion: a stanpa a no l'é conpletamente soportâ da sto navegatô.
+pdfjs-printing-not-ready = Atençion: o PDF o no l'é ancon caregou conpletamente pe-a stanpa.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Ativa/dizativa bara de scianco
+pdfjs-toggle-sidebar-button-label = Ativa/dizativa bara de scianco
+pdfjs-document-outline-button =
+ .title = Fanni vedde o contorno do documento (scicca doggio pe espande/ridue tutti i elementi)
+pdfjs-document-outline-button-label = Contorno do documento
+pdfjs-attachments-button =
+ .title = Fanni vedde alegæ
+pdfjs-attachments-button-label = Alegæ
+pdfjs-thumbs-button =
+ .title = Mostra miniatue
+pdfjs-thumbs-button-label = Miniatue
+pdfjs-findbar-button =
+ .title = Treuva into documento
+pdfjs-findbar-button-label = Treuva
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pagina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatua da pagina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Treuva
+ .placeholder = Treuva into documento…
+pdfjs-find-previous-button =
+ .title = Treuva a ripetiçion precedente do testo da çercâ
+pdfjs-find-previous-button-label = Precedente
+pdfjs-find-next-button =
+ .title = Treuva a ripetiçion dòppo do testo da çercâ
+pdfjs-find-next-button-label = Segoente
+pdfjs-find-highlight-checkbox = Evidençia
+pdfjs-find-match-case-checkbox-label = Maioscole/minoscole
+pdfjs-find-entire-word-checkbox-label = Poula intrega
+pdfjs-find-reached-top = Razonto a fin da pagina, continoa da l'iniçio
+pdfjs-find-reached-bottom = Razonto l'iniçio da pagina, continoa da-a fin
+pdfjs-find-not-found = Testo no trovou
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Larghessa pagina
+pdfjs-page-scale-fit = Adatta a una pagina
+pdfjs-page-scale-auto = Zoom aotomatico
+pdfjs-page-scale-actual = Dimenscioin efetive
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = S'é verificou 'n'erô itno caregamento do PDF.
+pdfjs-invalid-file-error = O schedaio PDF o l'é no valido ò aroinou.
+pdfjs-missing-file-error = O schedaio PDF o no gh'é.
+pdfjs-unexpected-response-error = Risposta inprevista do-u server
+pdfjs-rendering-error = Gh'é stæto 'n'erô itno rendering da pagina.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotaçion: { $type }]
+
+## Password
+
+pdfjs-password-label = Dimme a paròlla segreta pe arvî sto schedaio PDF.
+pdfjs-password-invalid = Paròlla segreta sbalia. Preuva torna.
+pdfjs-password-ok-button = Va ben
+pdfjs-password-cancel-button = Anulla
+pdfjs-web-fonts-disabled = I font do web en dizativæ: inposcibile adeuviâ i carateri do PDF.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/lo/viewer.ftl b/web/locale/lo/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..fdad16adfd5bced56a104add4ec63d237b08cdca
--- /dev/null
+++ b/web/locale/lo/viewer.ftl
@@ -0,0 +1,299 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = ຫນ້າກ່ອນຫນ້າ
+pdfjs-previous-button-label = ກ່ອນຫນ້າ
+pdfjs-next-button =
+ .title = ຫນ້າຖັດໄປ
+pdfjs-next-button-label = ຖັດໄປ
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = ຫນ້າ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = ຈາກ { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } ຈາກ { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = ຂະຫຍາຍອອກ
+pdfjs-zoom-out-button-label = ຂະຫຍາຍອອກ
+pdfjs-zoom-in-button =
+ .title = ຂະຫຍາຍເຂົ້າ
+pdfjs-zoom-in-button-label = ຂະຫຍາຍເຂົ້າ
+pdfjs-zoom-select =
+ .title = ຂະຫຍາຍ
+pdfjs-presentation-mode-button =
+ .title = ສັບປ່ຽນເປັນໂຫມດການນຳສະເຫນີ
+pdfjs-presentation-mode-button-label = ໂຫມດການນຳສະເຫນີ
+pdfjs-open-file-button =
+ .title = ເປີດໄຟລ໌
+pdfjs-open-file-button-label = ເປີດ
+pdfjs-print-button =
+ .title = ພິມ
+pdfjs-print-button-label = ພິມ
+pdfjs-save-button =
+ .title = ບັນທຶກ
+pdfjs-save-button-label = ບັນທຶກ
+pdfjs-bookmark-button =
+ .title = ໜ້າປັດຈຸບັນ (ເບິ່ງ URL ຈາກໜ້າປັດຈຸບັນ)
+pdfjs-bookmark-button-label = ຫນ້າປັດຈຸບັນ
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = ເປີດໃນ App
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = ເປີດໃນ App
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ເຄື່ອງມື
+pdfjs-tools-button-label = ເຄື່ອງມື
+pdfjs-first-page-button =
+ .title = ໄປທີ່ຫນ້າທຳອິດ
+pdfjs-first-page-button-label = ໄປທີ່ຫນ້າທຳອິດ
+pdfjs-last-page-button =
+ .title = ໄປທີ່ຫນ້າສຸດທ້າຍ
+pdfjs-last-page-button-label = ໄປທີ່ຫນ້າສຸດທ້າຍ
+pdfjs-page-rotate-cw-button =
+ .title = ຫມູນຕາມເຂັມໂມງ
+pdfjs-page-rotate-cw-button-label = ຫມູນຕາມເຂັມໂມງ
+pdfjs-page-rotate-ccw-button =
+ .title = ຫມູນທວນເຂັມໂມງ
+pdfjs-page-rotate-ccw-button-label = ຫມູນທວນເຂັມໂມງ
+pdfjs-cursor-text-select-tool-button =
+ .title = ເປີດໃຊ້ເຄື່ອງມືການເລືອກຂໍ້ຄວາມ
+pdfjs-cursor-text-select-tool-button-label = ເຄື່ອງມືເລືອກຂໍ້ຄວາມ
+pdfjs-cursor-hand-tool-button =
+ .title = ເປີດໃຊ້ເຄື່ອງມືມື
+pdfjs-cursor-hand-tool-button-label = ເຄື່ອງມືມື
+pdfjs-scroll-page-button =
+ .title = ໃຊ້ການເລື່ອນໜ້າ
+pdfjs-scroll-page-button-label = ເລື່ອນໜ້າ
+pdfjs-scroll-vertical-button =
+ .title = ໃຊ້ການເລື່ອນແນວຕັ້ງ
+pdfjs-scroll-vertical-button-label = ເລື່ອນແນວຕັ້ງ
+pdfjs-scroll-horizontal-button =
+ .title = ໃຊ້ການເລື່ອນແນວນອນ
+pdfjs-scroll-horizontal-button-label = ເລື່ອນແນວນອນ
+pdfjs-scroll-wrapped-button =
+ .title = ໃຊ້ Wrapped Scrolling
+pdfjs-scroll-wrapped-button-label = Wrapped Scrolling
+pdfjs-spread-none-button =
+ .title = ບໍ່ຕ້ອງຮ່ວມການແຜ່ກະຈາຍຫນ້າ
+pdfjs-spread-none-button-label = ບໍ່ມີການແຜ່ກະຈາຍ
+pdfjs-spread-odd-button =
+ .title = ເຂົ້າຮ່ວມການແຜ່ກະຈາຍຫນ້າເລີ່ມຕົ້ນດ້ວຍຫນ້າເລກຄີກ
+pdfjs-spread-odd-button-label = ການແຜ່ກະຈາຍຄີກ
+pdfjs-spread-even-button =
+ .title = ເຂົ້າຮ່ວມການແຜ່ກະຈາຍຂອງຫນ້າເລີ່ມຕົ້ນດ້ວຍຫນ້າເລກຄູ່
+pdfjs-spread-even-button-label = ການແຜ່ກະຈາຍຄູ່
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = ຄຸນສົມບັດເອກະສານ...
+pdfjs-document-properties-button-label = ຄຸນສົມບັດເອກະສານ...
+pdfjs-document-properties-file-name = ຊື່ໄຟລ໌:
+pdfjs-document-properties-file-size = ຂະຫນາດໄຟລ໌:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } ໄບຕ໌)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } ໄບຕ໌)
+pdfjs-document-properties-title = ຫົວຂໍ້:
+pdfjs-document-properties-author = ຜູ້ຂຽນ:
+pdfjs-document-properties-subject = ຫົວຂໍ້:
+pdfjs-document-properties-keywords = ຄໍາທີ່ຕ້ອງການຄົ້ນຫາ:
+pdfjs-document-properties-creation-date = ວັນທີສ້າງ:
+pdfjs-document-properties-modification-date = ວັນທີແກ້ໄຂ:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = ຜູ້ສ້າງ:
+pdfjs-document-properties-producer = ຜູ້ຜະລິດ PDF:
+pdfjs-document-properties-version = ເວີຊັ່ນ PDF:
+pdfjs-document-properties-page-count = ຈຳນວນໜ້າ:
+pdfjs-document-properties-page-size = ຂະໜາດໜ້າ:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = ລວງຕັ້ງ
+pdfjs-document-properties-page-size-orientation-landscape = ລວງນອນ
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = ຈົດໝາຍ
+pdfjs-document-properties-page-size-name-legal = ຂໍ້ກົດຫມາຍ
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = ມຸມມອງເວັບທີ່ໄວ:
+pdfjs-document-properties-linearized-yes = ແມ່ນ
+pdfjs-document-properties-linearized-no = ບໍ່
+pdfjs-document-properties-close-button = ປິດ
+
+## Print
+
+pdfjs-print-progress-message = ກຳລັງກະກຽມເອກະສານສຳລັບການພິມ...
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = ຍົກເລີກ
+pdfjs-printing-not-supported = ຄຳເຕືອນ: ບຼາວເຊີນີ້ບໍ່ຮອງຮັບການພິມຢ່າງເຕັມທີ່.
+pdfjs-printing-not-ready = ຄໍາເຕືອນ: PDF ບໍ່ໄດ້ຖືກໂຫຼດຢ່າງເຕັມທີ່ສໍາລັບການພິມ.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = ເປີດ/ປິດແຖບຂ້າງ
+pdfjs-toggle-sidebar-notification-button =
+ .title = ສະຫຼັບແຖບດ້ານຂ້າງ (ເອກະສານປະກອບມີໂຄງຮ່າງ/ໄຟລ໌ແນບ/ຊັ້ນຂໍ້ມູນ)
+pdfjs-toggle-sidebar-button-label = ເປີດ/ປິດແຖບຂ້າງ
+pdfjs-document-outline-button =
+ .title = ສະແດງໂຄງຮ່າງເອກະສານ (ກົດສອງຄັ້ງເພື່ອຂະຫຍາຍ / ຫຍໍ້ລາຍການທັງຫມົດ)
+pdfjs-document-outline-button-label = ເຄົ້າຮ່າງເອກະສານ
+pdfjs-attachments-button =
+ .title = ສະແດງໄຟລ໌ແນບ
+pdfjs-attachments-button-label = ໄຟລ໌ແນບ
+pdfjs-layers-button =
+ .title = ສະແດງຊັ້ນຂໍ້ມູນ (ຄລິກສອງເທື່ອເພື່ອຣີເຊັດຊັ້ນຂໍ້ມູນທັງໝົດໃຫ້ເປັນສະຖານະເລີ່ມຕົ້ນ)
+pdfjs-layers-button-label = ຊັ້ນ
+pdfjs-thumbs-button =
+ .title = ສະແດງຮູບຫຍໍ້
+pdfjs-thumbs-button-label = ຮູບຕົວຢ່າງ
+pdfjs-current-outline-item-button =
+ .title = ຊອກຫາລາຍການໂຄງຮ່າງປະຈຸບັນ
+pdfjs-current-outline-item-button-label = ລາຍການໂຄງຮ່າງປະຈຸບັນ
+pdfjs-findbar-button =
+ .title = ຊອກຫາໃນເອກະສານ
+pdfjs-findbar-button-label = ຄົ້ນຫາ
+pdfjs-additional-layers = ຊັ້ນຂໍ້ມູນເພີ່ມເຕີມ
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = ໜ້າ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = ຮູບຕົວຢ່າງຂອງໜ້າ { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = ຄົ້ນຫາ
+ .placeholder = ຊອກຫາໃນເອກະສານ...
+pdfjs-find-previous-button =
+ .title = ຊອກຫາການປະກົດຕົວທີ່ຜ່ານມາຂອງປະໂຫຍກ
+pdfjs-find-previous-button-label = ກ່ອນຫນ້ານີ້
+pdfjs-find-next-button =
+ .title = ຊອກຫາຕຳແຫນ່ງຖັດໄປຂອງວະລີ
+pdfjs-find-next-button-label = ຕໍ່ໄປ
+pdfjs-find-highlight-checkbox = ໄຮໄລທ໌ທັງຫມົດ
+pdfjs-find-match-case-checkbox-label = ກໍລະນີທີ່ກົງກັນ
+pdfjs-find-match-diacritics-checkbox-label = ເຄື່ອງໝາຍກຳກັບການອອກສຽງກົງກັນ
+pdfjs-find-entire-word-checkbox-label = ກົງກັນທຸກຄຳ
+pdfjs-find-reached-top = ມາຮອດເທິງຂອງເອກະສານ, ສືບຕໍ່ຈາກລຸ່ມ
+pdfjs-find-reached-bottom = ຮອດຕອນທ້າຍຂອງເອກະສານ, ສືບຕໍ່ຈາກເທິງ
+pdfjs-find-not-found = ບໍ່ພົບວະລີທີ່ຕ້ອງການ
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = ຄວາມກວ້າງໜ້າ
+pdfjs-page-scale-fit = ໜ້າພໍດີ
+pdfjs-page-scale-auto = ຊູມອັດຕະໂນມັດ
+pdfjs-page-scale-actual = ຂະໜາດຕົວຈິງ
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = ໜ້າ { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = ມີຂໍ້ຜິດພາດເກີດຂື້ນຂະນະທີ່ກຳລັງໂຫລດ PDF.
+pdfjs-invalid-file-error = ໄຟລ໌ PDF ບໍ່ຖືກຕ້ອງຫລືເສຍຫາຍ.
+pdfjs-missing-file-error = ບໍ່ມີໄຟລ໌ PDF.
+pdfjs-unexpected-response-error = ການຕອບສະໜອງຂອງເຊີບເວີທີ່ບໍ່ຄາດຄິດ.
+pdfjs-rendering-error = ມີຂໍ້ຜິດພາດເກີດຂື້ນຂະນະທີ່ກຳລັງເຣັນເດີຫນ້າ.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } ຄຳບັນຍາຍ]
+
+## Password
+
+pdfjs-password-label = ໃສ່ລະຫັດຜ່ານເພື່ອເປີດໄຟລ໌ PDF ນີ້.
+pdfjs-password-invalid = ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ. ກະລຸນາລອງອີກຄັ້ງ.
+pdfjs-password-ok-button = ຕົກລົງ
+pdfjs-password-cancel-button = ຍົກເລີກ
+pdfjs-web-fonts-disabled = ຟອນເວັບຖືກປິດໃຊ້ງານ: ບໍ່ສາມາດໃຊ້ຟອນ PDF ທີ່ຝັງໄວ້ໄດ້.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = ຂໍ້ຄວາມ
+pdfjs-editor-free-text-button-label = ຂໍ້ຄວາມ
+pdfjs-editor-ink-button =
+ .title = ແຕ້ມ
+pdfjs-editor-ink-button-label = ແຕ້ມ
+# Editor Parameters
+pdfjs-editor-free-text-color-input = ສີ
+pdfjs-editor-free-text-size-input = ຂະຫນາດ
+pdfjs-editor-ink-color-input = ສີ
+pdfjs-editor-ink-thickness-input = ຄວາມຫນາ
+pdfjs-editor-ink-opacity-input = ຄວາມໂປ່ງໃສ
+pdfjs-free-text =
+ .aria-label = ຕົວແກ້ໄຂຂໍ້ຄວາມ
+pdfjs-free-text-default-content = ເລີ່ມພິມ...
+pdfjs-ink =
+ .aria-label = ຕົວແກ້ໄຂຮູບແຕ້ມ
+pdfjs-ink-canvas =
+ .aria-label = ຮູບພາບທີ່ຜູ້ໃຊ້ສ້າງ
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/locale.json b/web/locale/locale.json
new file mode 100644
index 0000000000000000000000000000000000000000..20122111f21c9c692440d1cb01c47ac6a71fd2b9
--- /dev/null
+++ b/web/locale/locale.json
@@ -0,0 +1 @@
+{"ach":"ach/viewer.ftl","af":"af/viewer.ftl","an":"an/viewer.ftl","ar":"ar/viewer.ftl","ast":"ast/viewer.ftl","az":"az/viewer.ftl","be":"be/viewer.ftl","bg":"bg/viewer.ftl","bn":"bn/viewer.ftl","bo":"bo/viewer.ftl","br":"br/viewer.ftl","brx":"brx/viewer.ftl","bs":"bs/viewer.ftl","ca":"ca/viewer.ftl","cak":"cak/viewer.ftl","ckb":"ckb/viewer.ftl","cs":"cs/viewer.ftl","cy":"cy/viewer.ftl","da":"da/viewer.ftl","de":"de/viewer.ftl","dsb":"dsb/viewer.ftl","el":"el/viewer.ftl","en-ca":"en-CA/viewer.ftl","en-gb":"en-GB/viewer.ftl","en-us":"en-US/viewer.ftl","eo":"eo/viewer.ftl","es-ar":"es-AR/viewer.ftl","es-cl":"es-CL/viewer.ftl","es-es":"es-ES/viewer.ftl","es-mx":"es-MX/viewer.ftl","et":"et/viewer.ftl","eu":"eu/viewer.ftl","fa":"fa/viewer.ftl","ff":"ff/viewer.ftl","fi":"fi/viewer.ftl","fr":"fr/viewer.ftl","fur":"fur/viewer.ftl","fy-nl":"fy-NL/viewer.ftl","ga-ie":"ga-IE/viewer.ftl","gd":"gd/viewer.ftl","gl":"gl/viewer.ftl","gn":"gn/viewer.ftl","gu-in":"gu-IN/viewer.ftl","he":"he/viewer.ftl","hi-in":"hi-IN/viewer.ftl","hr":"hr/viewer.ftl","hsb":"hsb/viewer.ftl","hu":"hu/viewer.ftl","hy-am":"hy-AM/viewer.ftl","hye":"hye/viewer.ftl","ia":"ia/viewer.ftl","id":"id/viewer.ftl","is":"is/viewer.ftl","it":"it/viewer.ftl","ja":"ja/viewer.ftl","ka":"ka/viewer.ftl","kab":"kab/viewer.ftl","kk":"kk/viewer.ftl","km":"km/viewer.ftl","kn":"kn/viewer.ftl","ko":"ko/viewer.ftl","lij":"lij/viewer.ftl","lo":"lo/viewer.ftl","lt":"lt/viewer.ftl","ltg":"ltg/viewer.ftl","lv":"lv/viewer.ftl","meh":"meh/viewer.ftl","mk":"mk/viewer.ftl","mr":"mr/viewer.ftl","ms":"ms/viewer.ftl","my":"my/viewer.ftl","nb-no":"nb-NO/viewer.ftl","ne-np":"ne-NP/viewer.ftl","nl":"nl/viewer.ftl","nn-no":"nn-NO/viewer.ftl","oc":"oc/viewer.ftl","pa-in":"pa-IN/viewer.ftl","pl":"pl/viewer.ftl","pt-br":"pt-BR/viewer.ftl","pt-pt":"pt-PT/viewer.ftl","rm":"rm/viewer.ftl","ro":"ro/viewer.ftl","ru":"ru/viewer.ftl","sat":"sat/viewer.ftl","sc":"sc/viewer.ftl","scn":"scn/viewer.ftl","sco":"sco/viewer.ftl","si":"si/viewer.ftl","sk":"sk/viewer.ftl","skr":"skr/viewer.ftl","sl":"sl/viewer.ftl","son":"son/viewer.ftl","sq":"sq/viewer.ftl","sr":"sr/viewer.ftl","sv-se":"sv-SE/viewer.ftl","szl":"szl/viewer.ftl","ta":"ta/viewer.ftl","te":"te/viewer.ftl","tg":"tg/viewer.ftl","th":"th/viewer.ftl","tl":"tl/viewer.ftl","tr":"tr/viewer.ftl","trs":"trs/viewer.ftl","uk":"uk/viewer.ftl","ur":"ur/viewer.ftl","uz":"uz/viewer.ftl","vi":"vi/viewer.ftl","wo":"wo/viewer.ftl","xh":"xh/viewer.ftl","zh-cn":"zh-CN/viewer.ftl","zh-tw":"zh-TW/viewer.ftl"}
\ No newline at end of file
diff --git a/web/locale/lt/viewer.ftl b/web/locale/lt/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..a8ee7a08f3745fe8a2d3c489b8bd763d7afb6e94
--- /dev/null
+++ b/web/locale/lt/viewer.ftl
@@ -0,0 +1,268 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Ankstesnis puslapis
+pdfjs-previous-button-label = Ankstesnis
+pdfjs-next-button =
+ .title = Kitas puslapis
+pdfjs-next-button-label = Kitas
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Puslapis
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = iš { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } iš { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Sumažinti
+pdfjs-zoom-out-button-label = Sumažinti
+pdfjs-zoom-in-button =
+ .title = Padidinti
+pdfjs-zoom-in-button-label = Padidinti
+pdfjs-zoom-select =
+ .title = Mastelis
+pdfjs-presentation-mode-button =
+ .title = Pereiti į pateikties veikseną
+pdfjs-presentation-mode-button-label = Pateikties veiksena
+pdfjs-open-file-button =
+ .title = Atverti failą
+pdfjs-open-file-button-label = Atverti
+pdfjs-print-button =
+ .title = Spausdinti
+pdfjs-print-button-label = Spausdinti
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Priemonės
+pdfjs-tools-button-label = Priemonės
+pdfjs-first-page-button =
+ .title = Eiti į pirmą puslapį
+pdfjs-first-page-button-label = Eiti į pirmą puslapį
+pdfjs-last-page-button =
+ .title = Eiti į paskutinį puslapį
+pdfjs-last-page-button-label = Eiti į paskutinį puslapį
+pdfjs-page-rotate-cw-button =
+ .title = Pasukti pagal laikrodžio rodyklę
+pdfjs-page-rotate-cw-button-label = Pasukti pagal laikrodžio rodyklę
+pdfjs-page-rotate-ccw-button =
+ .title = Pasukti prieš laikrodžio rodyklę
+pdfjs-page-rotate-ccw-button-label = Pasukti prieš laikrodžio rodyklę
+pdfjs-cursor-text-select-tool-button =
+ .title = Įjungti teksto žymėjimo įrankį
+pdfjs-cursor-text-select-tool-button-label = Teksto žymėjimo įrankis
+pdfjs-cursor-hand-tool-button =
+ .title = Įjungti vilkimo įrankį
+pdfjs-cursor-hand-tool-button-label = Vilkimo įrankis
+pdfjs-scroll-page-button =
+ .title = Naudoti puslapio slinkimą
+pdfjs-scroll-page-button-label = Puslapio slinkimas
+pdfjs-scroll-vertical-button =
+ .title = Naudoti vertikalų slinkimą
+pdfjs-scroll-vertical-button-label = Vertikalus slinkimas
+pdfjs-scroll-horizontal-button =
+ .title = Naudoti horizontalų slinkimą
+pdfjs-scroll-horizontal-button-label = Horizontalus slinkimas
+pdfjs-scroll-wrapped-button =
+ .title = Naudoti išklotą slinkimą
+pdfjs-scroll-wrapped-button-label = Išklotas slinkimas
+pdfjs-spread-none-button =
+ .title = Nejungti puslapių į dvilapius
+pdfjs-spread-none-button-label = Be dvilapių
+pdfjs-spread-odd-button =
+ .title = Sujungti į dvilapius pradedant nelyginiais puslapiais
+pdfjs-spread-odd-button-label = Nelyginiai dvilapiai
+pdfjs-spread-even-button =
+ .title = Sujungti į dvilapius pradedant lyginiais puslapiais
+pdfjs-spread-even-button-label = Lyginiai dvilapiai
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumento savybės…
+pdfjs-document-properties-button-label = Dokumento savybės…
+pdfjs-document-properties-file-name = Failo vardas:
+pdfjs-document-properties-file-size = Failo dydis:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } B)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } B)
+pdfjs-document-properties-title = Antraštė:
+pdfjs-document-properties-author = Autorius:
+pdfjs-document-properties-subject = Tema:
+pdfjs-document-properties-keywords = Reikšminiai žodžiai:
+pdfjs-document-properties-creation-date = Sukūrimo data:
+pdfjs-document-properties-modification-date = Modifikavimo data:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Kūrėjas:
+pdfjs-document-properties-producer = PDF generatorius:
+pdfjs-document-properties-version = PDF versija:
+pdfjs-document-properties-page-count = Puslapių skaičius:
+pdfjs-document-properties-page-size = Puslapio dydis:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = stačias
+pdfjs-document-properties-page-size-orientation-landscape = gulsčias
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Laiškas
+pdfjs-document-properties-page-size-name-legal = Dokumentas
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Spartus žiniatinklio rodinys:
+pdfjs-document-properties-linearized-yes = Taip
+pdfjs-document-properties-linearized-no = Ne
+pdfjs-document-properties-close-button = Užverti
+
+## Print
+
+pdfjs-print-progress-message = Dokumentas ruošiamas spausdinimui…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Atsisakyti
+pdfjs-printing-not-supported = Dėmesio! Spausdinimas šioje naršyklėje nėra pilnai realizuotas.
+pdfjs-printing-not-ready = Dėmesio! PDF failas dar nėra pilnai įkeltas spausdinimui.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Rodyti / slėpti šoninį polangį
+pdfjs-toggle-sidebar-notification-button =
+ .title = Parankinė (dokumentas turi struktūrą / priedų / sluoksnių)
+pdfjs-toggle-sidebar-button-label = Šoninis polangis
+pdfjs-document-outline-button =
+ .title = Rodyti dokumento struktūrą (spustelėkite dukart norėdami išplėsti/suskleisti visus elementus)
+pdfjs-document-outline-button-label = Dokumento struktūra
+pdfjs-attachments-button =
+ .title = Rodyti priedus
+pdfjs-attachments-button-label = Priedai
+pdfjs-layers-button =
+ .title = Rodyti sluoksnius (spustelėkite dukart, norėdami atstatyti visus sluoksnius į numatytąją būseną)
+pdfjs-layers-button-label = Sluoksniai
+pdfjs-thumbs-button =
+ .title = Rodyti puslapių miniatiūras
+pdfjs-thumbs-button-label = Miniatiūros
+pdfjs-current-outline-item-button =
+ .title = Rasti dabartinį struktūros elementą
+pdfjs-current-outline-item-button-label = Dabartinis struktūros elementas
+pdfjs-findbar-button =
+ .title = Ieškoti dokumente
+pdfjs-findbar-button-label = Rasti
+pdfjs-additional-layers = Papildomi sluoksniai
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page } puslapis
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } puslapio miniatiūra
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Rasti
+ .placeholder = Rasti dokumente…
+pdfjs-find-previous-button =
+ .title = Ieškoti ankstesnio frazės egzemplioriaus
+pdfjs-find-previous-button-label = Ankstesnis
+pdfjs-find-next-button =
+ .title = Ieškoti tolesnio frazės egzemplioriaus
+pdfjs-find-next-button-label = Tolesnis
+pdfjs-find-highlight-checkbox = Viską paryškinti
+pdfjs-find-match-case-checkbox-label = Skirti didžiąsias ir mažąsias raides
+pdfjs-find-match-diacritics-checkbox-label = Skirti diakritinius ženklus
+pdfjs-find-entire-word-checkbox-label = Ištisi žodžiai
+pdfjs-find-reached-top = Pasiekus dokumento pradžią, paieška pratęsta nuo pabaigos
+pdfjs-find-reached-bottom = Pasiekus dokumento pabaigą, paieška pratęsta nuo pradžios
+pdfjs-find-not-found = Ieškoma frazė nerasta
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Priderinti prie lapo pločio
+pdfjs-page-scale-fit = Pritaikyti prie lapo dydžio
+pdfjs-page-scale-auto = Automatinis mastelis
+pdfjs-page-scale-actual = Tikras dydis
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = { $page } puslapis
+
+## Loading indicator messages
+
+pdfjs-loading-error = Įkeliant PDF failą įvyko klaida.
+pdfjs-invalid-file-error = Tai nėra PDF failas arba jis yra sugadintas.
+pdfjs-missing-file-error = PDF failas nerastas.
+pdfjs-unexpected-response-error = Netikėtas serverio atsakas.
+pdfjs-rendering-error = Atvaizduojant puslapį įvyko klaida.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [„{ $type }“ tipo anotacija]
+
+## Password
+
+pdfjs-password-label = Įveskite slaptažodį šiam PDF failui atverti.
+pdfjs-password-invalid = Slaptažodis neteisingas. Bandykite dar kartą.
+pdfjs-password-ok-button = Gerai
+pdfjs-password-cancel-button = Atsisakyti
+pdfjs-web-fonts-disabled = Saityno šriftai išjungti – PDF faile esančių šriftų naudoti negalima.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ltg/viewer.ftl b/web/locale/ltg/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..d262165450683970b7ed90eadf2f885002458cf9
--- /dev/null
+++ b/web/locale/ltg/viewer.ftl
@@ -0,0 +1,246 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Īprīkšejā lopa
+pdfjs-previous-button-label = Īprīkšejā
+pdfjs-next-button =
+ .title = Nuokomuo lopa
+pdfjs-next-button-label = Nuokomuo
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Lopa
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = nu { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } nu { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Attuolynuot
+pdfjs-zoom-out-button-label = Attuolynuot
+pdfjs-zoom-in-button =
+ .title = Pītuvynuot
+pdfjs-zoom-in-button-label = Pītuvynuot
+pdfjs-zoom-select =
+ .title = Palelynuojums
+pdfjs-presentation-mode-button =
+ .title = Puorslēgtīs iz Prezentacejis režymu
+pdfjs-presentation-mode-button-label = Prezentacejis režyms
+pdfjs-open-file-button =
+ .title = Attaiseit failu
+pdfjs-open-file-button-label = Attaiseit
+pdfjs-print-button =
+ .title = Drukuošona
+pdfjs-print-button-label = Drukōt
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Reiki
+pdfjs-tools-button-label = Reiki
+pdfjs-first-page-button =
+ .title = Īt iz pyrmū lopu
+pdfjs-first-page-button-label = Īt iz pyrmū lopu
+pdfjs-last-page-button =
+ .title = Īt iz piedejū lopu
+pdfjs-last-page-button-label = Īt iz piedejū lopu
+pdfjs-page-rotate-cw-button =
+ .title = Pagrīzt pa pulksteni
+pdfjs-page-rotate-cw-button-label = Pagrīzt pa pulksteni
+pdfjs-page-rotate-ccw-button =
+ .title = Pagrīzt pret pulksteni
+pdfjs-page-rotate-ccw-button-label = Pagrīzt pret pulksteni
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktivizēt teksta izvieles reiku
+pdfjs-cursor-text-select-tool-button-label = Teksta izvieles reiks
+pdfjs-cursor-hand-tool-button =
+ .title = Aktivēt rūkys reiku
+pdfjs-cursor-hand-tool-button-label = Rūkys reiks
+pdfjs-scroll-vertical-button =
+ .title = Izmontōt vertikalū ritinōšonu
+pdfjs-scroll-vertical-button-label = Vertikalō ritinōšona
+pdfjs-scroll-horizontal-button =
+ .title = Izmontōt horizontalū ritinōšonu
+pdfjs-scroll-horizontal-button-label = Horizontalō ritinōšona
+pdfjs-scroll-wrapped-button =
+ .title = Izmontōt mārūgojamū ritinōšonu
+pdfjs-scroll-wrapped-button-label = Mārūgojamō ritinōšona
+pdfjs-spread-none-button =
+ .title = Naizmontōt lopu atvāruma režimu
+pdfjs-spread-none-button-label = Bez atvārumim
+pdfjs-spread-odd-button =
+ .title = Izmontōt lopu atvārumus sōkut nu napōra numeru lopom
+pdfjs-spread-odd-button-label = Napōra lopys pa kreisi
+pdfjs-spread-even-button =
+ .title = Izmontōt lopu atvārumus sōkut nu pōra numeru lopom
+pdfjs-spread-even-button-label = Pōra lopys pa kreisi
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumenta īstatiejumi…
+pdfjs-document-properties-button-label = Dokumenta īstatiejumi…
+pdfjs-document-properties-file-name = Faila nūsaukums:
+pdfjs-document-properties-file-size = Faila izmārs:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } biti)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } biti)
+pdfjs-document-properties-title = Nūsaukums:
+pdfjs-document-properties-author = Autors:
+pdfjs-document-properties-subject = Tema:
+pdfjs-document-properties-keywords = Atslāgi vuordi:
+pdfjs-document-properties-creation-date = Izveides datums:
+pdfjs-document-properties-modification-date = lobuošonys datums:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Radeituojs:
+pdfjs-document-properties-producer = PDF producents:
+pdfjs-document-properties-version = PDF verseja:
+pdfjs-document-properties-page-count = Lopu skaits:
+pdfjs-document-properties-page-size = Lopas izmārs:
+pdfjs-document-properties-page-size-unit-inches = collas
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portreta orientaceja
+pdfjs-document-properties-page-size-orientation-landscape = ainovys orientaceja
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = Jā
+pdfjs-document-properties-linearized-no = Nā
+pdfjs-document-properties-close-button = Aiztaiseit
+
+## Print
+
+pdfjs-print-progress-message = Preparing document for printing…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Atceļt
+pdfjs-printing-not-supported = Uzmaneibu: Drukuošona nu itei puorlūka dorbojās tikai daleji.
+pdfjs-printing-not-ready = Uzmaneibu: PDF nav pilneibā īluodeits drukuošonai.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Puorslēgt suonu jūslu
+pdfjs-toggle-sidebar-button-label = Puorslēgt suonu jūslu
+pdfjs-document-outline-button =
+ .title = Show Document Outline (double-click to expand/collapse all items)
+pdfjs-document-outline-button-label = Dokumenta saturs
+pdfjs-attachments-button =
+ .title = Show Attachments
+pdfjs-attachments-button-label = Attachments
+pdfjs-thumbs-button =
+ .title = Paruodeit seiktālus
+pdfjs-thumbs-button-label = Seiktāli
+pdfjs-findbar-button =
+ .title = Mekleit dokumentā
+pdfjs-findbar-button-label = Mekleit
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Lopa { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Lopys { $page } seiktāls
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Mekleit
+ .placeholder = Mekleit dokumentā…
+pdfjs-find-previous-button =
+ .title = Atrast īprīkšejū
+pdfjs-find-previous-button-label = Īprīkšejā
+pdfjs-find-next-button =
+ .title = Atrast nuokamū
+pdfjs-find-next-button-label = Nuokomuo
+pdfjs-find-highlight-checkbox = Īkruosuot vysys
+pdfjs-find-match-case-checkbox-label = Lelū, mozū burtu jiuteigs
+pdfjs-find-reached-top = Sasnīgts dokumenta suokums, turpynojom nu beigom
+pdfjs-find-reached-bottom = Sasnīgtys dokumenta beigys, turpynojom nu suokuma
+pdfjs-find-not-found = Frāze nav atrosta
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Lopys plotumā
+pdfjs-page-scale-fit = Ītylpynūt lopu
+pdfjs-page-scale-auto = Automatiskais izmārs
+pdfjs-page-scale-actual = Patīsais izmārs
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Īluodejūt PDF nūtyka klaida.
+pdfjs-invalid-file-error = Nadereigs voi būjuots PDF fails.
+pdfjs-missing-file-error = PDF fails nav atrosts.
+pdfjs-unexpected-response-error = Unexpected server response.
+pdfjs-rendering-error = Attālojūt lopu rodās klaida
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = Īvodit paroli, kab attaiseitu PDF failu.
+pdfjs-password-invalid = Napareiza parole, raugit vēļreiz.
+pdfjs-password-ok-button = Labi
+pdfjs-password-cancel-button = Atceļt
+pdfjs-web-fonts-disabled = Šķārsteikla fonti nav aktivizāti: Navar īgult PDF fontus.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/lv/viewer.ftl b/web/locale/lv/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..067dc105e659c3fa6e21da04935378e586346f2b
--- /dev/null
+++ b/web/locale/lv/viewer.ftl
@@ -0,0 +1,247 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Iepriekšējā lapa
+pdfjs-previous-button-label = Iepriekšējā
+pdfjs-next-button =
+ .title = Nākamā lapa
+pdfjs-next-button-label = Nākamā
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Lapa
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = no { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } no { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Attālināt
+pdfjs-zoom-out-button-label = Attālināt
+pdfjs-zoom-in-button =
+ .title = Pietuvināt
+pdfjs-zoom-in-button-label = Pietuvināt
+pdfjs-zoom-select =
+ .title = Palielinājums
+pdfjs-presentation-mode-button =
+ .title = Pārslēgties uz Prezentācijas režīmu
+pdfjs-presentation-mode-button-label = Prezentācijas režīms
+pdfjs-open-file-button =
+ .title = Atvērt failu
+pdfjs-open-file-button-label = Atvērt
+pdfjs-print-button =
+ .title = Drukāšana
+pdfjs-print-button-label = Drukāt
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Rīki
+pdfjs-tools-button-label = Rīki
+pdfjs-first-page-button =
+ .title = Iet uz pirmo lapu
+pdfjs-first-page-button-label = Iet uz pirmo lapu
+pdfjs-last-page-button =
+ .title = Iet uz pēdējo lapu
+pdfjs-last-page-button-label = Iet uz pēdējo lapu
+pdfjs-page-rotate-cw-button =
+ .title = Pagriezt pa pulksteni
+pdfjs-page-rotate-cw-button-label = Pagriezt pa pulksteni
+pdfjs-page-rotate-ccw-button =
+ .title = Pagriezt pret pulksteni
+pdfjs-page-rotate-ccw-button-label = Pagriezt pret pulksteni
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktivizēt teksta izvēles rīku
+pdfjs-cursor-text-select-tool-button-label = Teksta izvēles rīks
+pdfjs-cursor-hand-tool-button =
+ .title = Aktivēt rokas rīku
+pdfjs-cursor-hand-tool-button-label = Rokas rīks
+pdfjs-scroll-vertical-button =
+ .title = Izmantot vertikālo ritināšanu
+pdfjs-scroll-vertical-button-label = Vertikālā ritināšana
+pdfjs-scroll-horizontal-button =
+ .title = Izmantot horizontālo ritināšanu
+pdfjs-scroll-horizontal-button-label = Horizontālā ritināšana
+pdfjs-scroll-wrapped-button =
+ .title = Izmantot apkļauto ritināšanu
+pdfjs-scroll-wrapped-button-label = Apkļautā ritināšana
+pdfjs-spread-none-button =
+ .title = Nepievienoties lapu izpletumiem
+pdfjs-spread-none-button-label = Neizmantot izpletumus
+pdfjs-spread-odd-button =
+ .title = Izmantot lapu izpletumus sākot ar nepāra numuru lapām
+pdfjs-spread-odd-button-label = Nepāra izpletumi
+pdfjs-spread-even-button =
+ .title = Izmantot lapu izpletumus sākot ar pāra numuru lapām
+pdfjs-spread-even-button-label = Pāra izpletumi
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumenta iestatījumi…
+pdfjs-document-properties-button-label = Dokumenta iestatījumi…
+pdfjs-document-properties-file-name = Faila nosaukums:
+pdfjs-document-properties-file-size = Faila izmērs:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } biti)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } biti)
+pdfjs-document-properties-title = Nosaukums:
+pdfjs-document-properties-author = Autors:
+pdfjs-document-properties-subject = Tēma:
+pdfjs-document-properties-keywords = Atslēgas vārdi:
+pdfjs-document-properties-creation-date = Izveides datums:
+pdfjs-document-properties-modification-date = LAbošanas datums:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Radītājs:
+pdfjs-document-properties-producer = PDF producents:
+pdfjs-document-properties-version = PDF versija:
+pdfjs-document-properties-page-count = Lapu skaits:
+pdfjs-document-properties-page-size = Papīra izmērs:
+pdfjs-document-properties-page-size-unit-inches = collas
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portretorientācija
+pdfjs-document-properties-page-size-orientation-landscape = ainavorientācija
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Vēstule
+pdfjs-document-properties-page-size-name-legal = Juridiskie teksti
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Ātrā tīmekļa skats:
+pdfjs-document-properties-linearized-yes = Jā
+pdfjs-document-properties-linearized-no = Nē
+pdfjs-document-properties-close-button = Aizvērt
+
+## Print
+
+pdfjs-print-progress-message = Gatavo dokumentu drukāšanai...
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Atcelt
+pdfjs-printing-not-supported = Uzmanību: Drukāšana no šī pārlūka darbojas tikai daļēji.
+pdfjs-printing-not-ready = Uzmanību: PDF nav pilnībā ielādēts drukāšanai.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Pārslēgt sānu joslu
+pdfjs-toggle-sidebar-button-label = Pārslēgt sānu joslu
+pdfjs-document-outline-button =
+ .title = Rādīt dokumenta struktūru (veiciet dubultklikšķi lai izvērstu/sakļautu visus vienumus)
+pdfjs-document-outline-button-label = Dokumenta saturs
+pdfjs-attachments-button =
+ .title = Rādīt pielikumus
+pdfjs-attachments-button-label = Pielikumi
+pdfjs-thumbs-button =
+ .title = Parādīt sīktēlus
+pdfjs-thumbs-button-label = Sīktēli
+pdfjs-findbar-button =
+ .title = Meklēt dokumentā
+pdfjs-findbar-button-label = Meklēt
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Lapa { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Lapas { $page } sīktēls
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Meklēt
+ .placeholder = Meklēt dokumentā…
+pdfjs-find-previous-button =
+ .title = Atrast iepriekšējo
+pdfjs-find-previous-button-label = Iepriekšējā
+pdfjs-find-next-button =
+ .title = Atrast nākamo
+pdfjs-find-next-button-label = Nākamā
+pdfjs-find-highlight-checkbox = Iekrāsot visas
+pdfjs-find-match-case-checkbox-label = Lielo, mazo burtu jutīgs
+pdfjs-find-entire-word-checkbox-label = Veselus vārdus
+pdfjs-find-reached-top = Sasniegts dokumenta sākums, turpinām no beigām
+pdfjs-find-reached-bottom = Sasniegtas dokumenta beigas, turpinām no sākuma
+pdfjs-find-not-found = Frāze nav atrasta
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Lapas platumā
+pdfjs-page-scale-fit = Ietilpinot lapu
+pdfjs-page-scale-auto = Automātiskais izmērs
+pdfjs-page-scale-actual = Patiesais izmērs
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ielādējot PDF notika kļūda.
+pdfjs-invalid-file-error = Nederīgs vai bojāts PDF fails.
+pdfjs-missing-file-error = PDF fails nav atrasts.
+pdfjs-unexpected-response-error = Negaidīa servera atbilde.
+pdfjs-rendering-error = Attēlojot lapu radās kļūda
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } anotācija]
+
+## Password
+
+pdfjs-password-label = Ievadiet paroli, lai atvērtu PDF failu.
+pdfjs-password-invalid = Nepareiza parole, mēģiniet vēlreiz.
+pdfjs-password-ok-button = Labi
+pdfjs-password-cancel-button = Atcelt
+pdfjs-web-fonts-disabled = Tīmekļa fonti nav aktivizēti: Nevar iegult PDF fontus.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/meh/viewer.ftl b/web/locale/meh/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..d8bddc9d543f3a2b39d8ab81e0e1af9d7085f8ba
--- /dev/null
+++ b/web/locale/meh/viewer.ftl
@@ -0,0 +1,87 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Página yata
+pdfjs-zoom-select =
+ .title = Nasa´a ka´nu/Nasa´a luli
+pdfjs-open-file-button-label = Síne
+
+## Secondary toolbar and context menu
+
+
+## Document properties dialog
+
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+pdfjs-document-properties-linearized-yes = Kuvi
+pdfjs-document-properties-close-button = Nakasɨ
+
+## Print
+
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Nkuvi-ka
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-findbar-button-label = Nánuku
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+
+## Find panel button title and messages
+
+
+## Predefined zoom values
+
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+
+## Password
+
+pdfjs-password-cancel-button = Nkuvi-ka
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/mk/viewer.ftl b/web/locale/mk/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..47d24b24091fc81ff86d3a52d9cb717277ea92d4
--- /dev/null
+++ b/web/locale/mk/viewer.ftl
@@ -0,0 +1,215 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Претходна страница
+pdfjs-previous-button-label = Претходна
+pdfjs-next-button =
+ .title = Следна страница
+pdfjs-next-button-label = Следна
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Страница
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = од { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } од { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Намалување
+pdfjs-zoom-out-button-label = Намали
+pdfjs-zoom-in-button =
+ .title = Зголемување
+pdfjs-zoom-in-button-label = Зголеми
+pdfjs-zoom-select =
+ .title = Променување на големина
+pdfjs-presentation-mode-button =
+ .title = Премини во презентациски режим
+pdfjs-presentation-mode-button-label = Презентациски режим
+pdfjs-open-file-button =
+ .title = Отворање датотека
+pdfjs-open-file-button-label = Отвори
+pdfjs-print-button =
+ .title = Печатење
+pdfjs-print-button-label = Печати
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Алатки
+pdfjs-tools-button-label = Алатки
+pdfjs-first-page-button =
+ .title = Оди до првата страница
+pdfjs-first-page-button-label = Оди до првата страница
+pdfjs-last-page-button =
+ .title = Оди до последната страница
+pdfjs-last-page-button-label = Оди до последната страница
+pdfjs-page-rotate-cw-button =
+ .title = Ротирај по стрелките на часовникот
+pdfjs-page-rotate-cw-button-label = Ротирај по стрелките на часовникот
+pdfjs-page-rotate-ccw-button =
+ .title = Ротирај спротивно од стрелките на часовникот
+pdfjs-page-rotate-ccw-button-label = Ротирај спротивно од стрелките на часовникот
+pdfjs-cursor-text-select-tool-button =
+ .title = Овозможи алатка за избор на текст
+pdfjs-cursor-text-select-tool-button-label = Алатка за избор на текст
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Својства на документот…
+pdfjs-document-properties-button-label = Својства на документот…
+pdfjs-document-properties-file-name = Име на датотека:
+pdfjs-document-properties-file-size = Големина на датотеката:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } бајти)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } бајти)
+pdfjs-document-properties-title = Наслов:
+pdfjs-document-properties-author = Автор:
+pdfjs-document-properties-subject = Тема:
+pdfjs-document-properties-keywords = Клучни зборови:
+pdfjs-document-properties-creation-date = Датум на создавање:
+pdfjs-document-properties-modification-date = Датум на промена:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Креатор:
+pdfjs-document-properties-version = Верзија на PDF:
+pdfjs-document-properties-page-count = Број на страници:
+pdfjs-document-properties-page-size = Големина на страница:
+pdfjs-document-properties-page-size-unit-inches = инч
+pdfjs-document-properties-page-size-unit-millimeters = мм
+pdfjs-document-properties-page-size-orientation-portrait = портрет
+pdfjs-document-properties-page-size-orientation-landscape = пејзаж
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Писмо
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+pdfjs-document-properties-linearized-yes = Да
+pdfjs-document-properties-linearized-no = Не
+pdfjs-document-properties-close-button = Затвори
+
+## Print
+
+pdfjs-print-progress-message = Документ се подготвува за печатење…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Откажи
+pdfjs-printing-not-supported = Предупредување: Печатењето не е целосно поддржано во овој прелистувач.
+pdfjs-printing-not-ready = Предупредување: PDF документот не е целосно вчитан за печатење.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Вклучи странична лента
+pdfjs-toggle-sidebar-button-label = Вклучи странична лента
+pdfjs-document-outline-button-label = Содржина на документот
+pdfjs-attachments-button =
+ .title = Прикажи додатоци
+pdfjs-thumbs-button =
+ .title = Прикажување на икони
+pdfjs-thumbs-button-label = Икони
+pdfjs-findbar-button =
+ .title = Најди во документот
+pdfjs-findbar-button-label = Најди
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Страница { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Икона од страница { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Пронајди
+ .placeholder = Пронајди во документот…
+pdfjs-find-previous-button =
+ .title = Најди ја предходната појава на фразата
+pdfjs-find-previous-button-label = Претходно
+pdfjs-find-next-button =
+ .title = Најди ја следната појава на фразата
+pdfjs-find-next-button-label = Следно
+pdfjs-find-highlight-checkbox = Означи сѐ
+pdfjs-find-match-case-checkbox-label = Токму така
+pdfjs-find-entire-word-checkbox-label = Цели зборови
+pdfjs-find-reached-top = Барањето стигна до почетокот на документот и почнува од крајот
+pdfjs-find-reached-bottom = Барањето стигна до крајот на документот и почнува од почеток
+pdfjs-find-not-found = Фразата не е пронајдена
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Ширина на страница
+pdfjs-page-scale-fit = Цела страница
+pdfjs-page-scale-auto = Автоматска големина
+pdfjs-page-scale-actual = Вистинска големина
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Настана грешка при вчитувањето на PDF-от.
+pdfjs-invalid-file-error = Невалидна или корумпирана PDF датотека.
+pdfjs-missing-file-error = Недостасува PDF документ.
+pdfjs-unexpected-response-error = Неочекуван одговор од серверот.
+pdfjs-rendering-error = Настана грешка при прикажувањето на страницата.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+
+## Password
+
+pdfjs-password-label = Внесете ја лозинката за да ја отворите оваа датотека.
+pdfjs-password-invalid = Невалидна лозинка. Обидете се повторно.
+pdfjs-password-ok-button = Во ред
+pdfjs-password-cancel-button = Откажи
+pdfjs-web-fonts-disabled = Интернет фонтовите се оневозможени: не може да се користат вградените PDF фонтови.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/mr/viewer.ftl b/web/locale/mr/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..49948b1938a14e18eb6be727ba672105942449bb
--- /dev/null
+++ b/web/locale/mr/viewer.ftl
@@ -0,0 +1,239 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = मागील पृष्ठ
+pdfjs-previous-button-label = मागील
+pdfjs-next-button =
+ .title = पुढील पृष्ठ
+pdfjs-next-button-label = पुढील
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = पृष्ठ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount }पैकी
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pagesCount } पैकी { $pageNumber })
+pdfjs-zoom-out-button =
+ .title = छोटे करा
+pdfjs-zoom-out-button-label = छोटे करा
+pdfjs-zoom-in-button =
+ .title = मोठे करा
+pdfjs-zoom-in-button-label = मोठे करा
+pdfjs-zoom-select =
+ .title = लहान किंवा मोठे करा
+pdfjs-presentation-mode-button =
+ .title = प्रस्तुतिकरण मोडचा वापर करा
+pdfjs-presentation-mode-button-label = प्रस्तुतिकरण मोड
+pdfjs-open-file-button =
+ .title = फाइल उघडा
+pdfjs-open-file-button-label = उघडा
+pdfjs-print-button =
+ .title = छपाई करा
+pdfjs-print-button-label = छपाई करा
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = साधने
+pdfjs-tools-button-label = साधने
+pdfjs-first-page-button =
+ .title = पहिल्या पृष्ठावर जा
+pdfjs-first-page-button-label = पहिल्या पृष्ठावर जा
+pdfjs-last-page-button =
+ .title = शेवटच्या पृष्ठावर जा
+pdfjs-last-page-button-label = शेवटच्या पृष्ठावर जा
+pdfjs-page-rotate-cw-button =
+ .title = घड्याळाच्या काट्याच्या दिशेने फिरवा
+pdfjs-page-rotate-cw-button-label = घड्याळाच्या काट्याच्या दिशेने फिरवा
+pdfjs-page-rotate-ccw-button =
+ .title = घड्याळाच्या काट्याच्या उलट दिशेने फिरवा
+pdfjs-page-rotate-ccw-button-label = घड्याळाच्या काट्याच्या उलट दिशेने फिरवा
+pdfjs-cursor-text-select-tool-button =
+ .title = मजकूर निवड साधन कार्यान्वयीत करा
+pdfjs-cursor-text-select-tool-button-label = मजकूर निवड साधन
+pdfjs-cursor-hand-tool-button =
+ .title = हात साधन कार्यान्वित करा
+pdfjs-cursor-hand-tool-button-label = हस्त साधन
+pdfjs-scroll-vertical-button =
+ .title = अनुलंब स्क्रोलिंग वापरा
+pdfjs-scroll-vertical-button-label = अनुलंब स्क्रोलिंग
+pdfjs-scroll-horizontal-button =
+ .title = क्षैतिज स्क्रोलिंग वापरा
+pdfjs-scroll-horizontal-button-label = क्षैतिज स्क्रोलिंग
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = दस्तऐवज गुणधर्म…
+pdfjs-document-properties-button-label = दस्तऐवज गुणधर्म…
+pdfjs-document-properties-file-name = फाइलचे नाव:
+pdfjs-document-properties-file-size = फाइल आकार:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } बाइट्स)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } बाइट्स)
+pdfjs-document-properties-title = शिर्षक:
+pdfjs-document-properties-author = लेखक:
+pdfjs-document-properties-subject = विषय:
+pdfjs-document-properties-keywords = मुख्यशब्द:
+pdfjs-document-properties-creation-date = निर्माण दिनांक:
+pdfjs-document-properties-modification-date = दुरूस्ती दिनांक:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = निर्माता:
+pdfjs-document-properties-producer = PDF निर्माता:
+pdfjs-document-properties-version = PDF आवृत्ती:
+pdfjs-document-properties-page-count = पृष्ठ संख्या:
+pdfjs-document-properties-page-size = पृष्ठ आकार:
+pdfjs-document-properties-page-size-unit-inches = इंच
+pdfjs-document-properties-page-size-unit-millimeters = मीमी
+pdfjs-document-properties-page-size-orientation-portrait = उभी मांडणी
+pdfjs-document-properties-page-size-orientation-landscape = आडवे
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = जलद वेब दृष्य:
+pdfjs-document-properties-linearized-yes = हो
+pdfjs-document-properties-linearized-no = नाही
+pdfjs-document-properties-close-button = बंद करा
+
+## Print
+
+pdfjs-print-progress-message = छपाई करीता पृष्ठ तयार करीत आहे…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = रद्द करा
+pdfjs-printing-not-supported = सावधानता: या ब्राउझरतर्फे छपाइ पूर्णपणे समर्थीत नाही.
+pdfjs-printing-not-ready = सावधानता: छपाईकरिता PDF पूर्णतया लोड झाले नाही.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = बाजूचीपट्टी टॉगल करा
+pdfjs-toggle-sidebar-button-label = बाजूचीपट्टी टॉगल करा
+pdfjs-document-outline-button =
+ .title = दस्तऐवज बाह्यरेखा दर्शवा (विस्तृत करण्यासाठी दोनवेळा क्लिक करा /सर्व घटक दाखवा)
+pdfjs-document-outline-button-label = दस्तऐवज रूपरेषा
+pdfjs-attachments-button =
+ .title = जोडपत्र दाखवा
+pdfjs-attachments-button-label = जोडपत्र
+pdfjs-thumbs-button =
+ .title = थंबनेल्स् दाखवा
+pdfjs-thumbs-button-label = थंबनेल्स्
+pdfjs-findbar-button =
+ .title = दस्तऐवजात शोधा
+pdfjs-findbar-button-label = शोधा
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = पृष्ठ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = पृष्ठाचे थंबनेल { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = शोधा
+ .placeholder = दस्तऐवजात शोधा…
+pdfjs-find-previous-button =
+ .title = वाकप्रयोगची मागील घटना शोधा
+pdfjs-find-previous-button-label = मागील
+pdfjs-find-next-button =
+ .title = वाकप्रयोगची पुढील घटना शोधा
+pdfjs-find-next-button-label = पुढील
+pdfjs-find-highlight-checkbox = सर्व ठळक करा
+pdfjs-find-match-case-checkbox-label = आकार जुळवा
+pdfjs-find-entire-word-checkbox-label = संपूर्ण शब्द
+pdfjs-find-reached-top = दस्तऐवजाच्या शीर्षकास पोहचले, तळपासून पुढे
+pdfjs-find-reached-bottom = दस्तऐवजाच्या तळाला पोहचले, शीर्षकापासून पुढे
+pdfjs-find-not-found = वाकप्रयोग आढळले नाही
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = पृष्ठाची रूंदी
+pdfjs-page-scale-fit = पृष्ठ बसवा
+pdfjs-page-scale-auto = स्वयं लाहन किंवा मोठे करणे
+pdfjs-page-scale-actual = प्रत्यक्ष आकार
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF लोड करतेवेळी त्रुटी आढळली.
+pdfjs-invalid-file-error = अवैध किंवा दोषीत PDF फाइल.
+pdfjs-missing-file-error = न आढळणारी PDF फाइल.
+pdfjs-unexpected-response-error = अनपेक्षित सर्व्हर प्रतिसाद.
+pdfjs-rendering-error = पृष्ठ दाखवतेवेळी त्रुटी आढळली.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } टिपण्णी]
+
+## Password
+
+pdfjs-password-label = ही PDF फाइल उघडण्याकरिता पासवर्ड द्या.
+pdfjs-password-invalid = अवैध पासवर्ड. कृपया पुन्हा प्रयत्न करा.
+pdfjs-password-ok-button = ठीक आहे
+pdfjs-password-cancel-button = रद्द करा
+pdfjs-web-fonts-disabled = वेब टंक असमर्थीत आहेत: एम्बेडेड PDF टंक वापर अशक्य.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ms/viewer.ftl b/web/locale/ms/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..11b866514144a08da53a07849bdf2089031b46d0
--- /dev/null
+++ b/web/locale/ms/viewer.ftl
@@ -0,0 +1,247 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Halaman Dahulu
+pdfjs-previous-button-label = Dahulu
+pdfjs-next-button =
+ .title = Halaman Berikut
+pdfjs-next-button-label = Berikut
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Halaman
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = daripada { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } daripada { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zum Keluar
+pdfjs-zoom-out-button-label = Zum Keluar
+pdfjs-zoom-in-button =
+ .title = Zum Masuk
+pdfjs-zoom-in-button-label = Zum Masuk
+pdfjs-zoom-select =
+ .title = Zum
+pdfjs-presentation-mode-button =
+ .title = Tukar ke Mod Persembahan
+pdfjs-presentation-mode-button-label = Mod Persembahan
+pdfjs-open-file-button =
+ .title = Buka Fail
+pdfjs-open-file-button-label = Buka
+pdfjs-print-button =
+ .title = Cetak
+pdfjs-print-button-label = Cetak
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Alatan
+pdfjs-tools-button-label = Alatan
+pdfjs-first-page-button =
+ .title = Pergi ke Halaman Pertama
+pdfjs-first-page-button-label = Pergi ke Halaman Pertama
+pdfjs-last-page-button =
+ .title = Pergi ke Halaman Terakhir
+pdfjs-last-page-button-label = Pergi ke Halaman Terakhir
+pdfjs-page-rotate-cw-button =
+ .title = Berputar ikut arah Jam
+pdfjs-page-rotate-cw-button-label = Berputar ikut arah Jam
+pdfjs-page-rotate-ccw-button =
+ .title = Pusing berlawan arah jam
+pdfjs-page-rotate-ccw-button-label = Pusing berlawan arah jam
+pdfjs-cursor-text-select-tool-button =
+ .title = Dayakan Alatan Pilihan Teks
+pdfjs-cursor-text-select-tool-button-label = Alatan Pilihan Teks
+pdfjs-cursor-hand-tool-button =
+ .title = Dayakan Alatan Tangan
+pdfjs-cursor-hand-tool-button-label = Alatan Tangan
+pdfjs-scroll-vertical-button =
+ .title = Guna Skrol Menegak
+pdfjs-scroll-vertical-button-label = Skrol Menegak
+pdfjs-scroll-horizontal-button =
+ .title = Guna Skrol Mengufuk
+pdfjs-scroll-horizontal-button-label = Skrol Mengufuk
+pdfjs-scroll-wrapped-button =
+ .title = Guna Skrol Berbalut
+pdfjs-scroll-wrapped-button-label = Skrol Berbalut
+pdfjs-spread-none-button =
+ .title = Jangan hubungkan hamparan halaman
+pdfjs-spread-none-button-label = Tanpa Hamparan
+pdfjs-spread-odd-button =
+ .title = Hubungkan hamparan halaman dengan halaman nombor ganjil
+pdfjs-spread-odd-button-label = Hamparan Ganjil
+pdfjs-spread-even-button =
+ .title = Hubungkan hamparan halaman dengan halaman nombor genap
+pdfjs-spread-even-button-label = Hamparan Seimbang
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Sifat Dokumen…
+pdfjs-document-properties-button-label = Sifat Dokumen…
+pdfjs-document-properties-file-name = Nama fail:
+pdfjs-document-properties-file-size = Saiz fail:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bait)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bait)
+pdfjs-document-properties-title = Tajuk:
+pdfjs-document-properties-author = Pengarang:
+pdfjs-document-properties-subject = Subjek:
+pdfjs-document-properties-keywords = Kata kunci:
+pdfjs-document-properties-creation-date = Masa Dicipta:
+pdfjs-document-properties-modification-date = Tarikh Ubahsuai:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Pencipta:
+pdfjs-document-properties-producer = Pengeluar PDF:
+pdfjs-document-properties-version = Versi PDF:
+pdfjs-document-properties-page-count = Kiraan Laman:
+pdfjs-document-properties-page-size = Saiz Halaman:
+pdfjs-document-properties-page-size-unit-inches = dalam
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = potret
+pdfjs-document-properties-page-size-orientation-landscape = landskap
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Paparan Web Pantas:
+pdfjs-document-properties-linearized-yes = Ya
+pdfjs-document-properties-linearized-no = Tidak
+pdfjs-document-properties-close-button = Tutup
+
+## Print
+
+pdfjs-print-progress-message = Menyediakan dokumen untuk dicetak…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Batal
+pdfjs-printing-not-supported = Amaran: Cetakan ini tidak sepenuhnya disokong oleh pelayar ini.
+pdfjs-printing-not-ready = Amaran: PDF tidak sepenuhnya dimuatkan untuk dicetak.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Togol Bar Sisi
+pdfjs-toggle-sidebar-button-label = Togol Bar Sisi
+pdfjs-document-outline-button =
+ .title = Papar Rangka Dokumen (klik-dua-kali untuk kembangkan/kolaps semua item)
+pdfjs-document-outline-button-label = Rangka Dokumen
+pdfjs-attachments-button =
+ .title = Papar Lampiran
+pdfjs-attachments-button-label = Lampiran
+pdfjs-thumbs-button =
+ .title = Papar Thumbnails
+pdfjs-thumbs-button-label = Imej kecil
+pdfjs-findbar-button =
+ .title = Cari didalam Dokumen
+pdfjs-findbar-button-label = Cari
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Halaman { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Halaman Imej kecil { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Cari
+ .placeholder = Cari dalam dokumen…
+pdfjs-find-previous-button =
+ .title = Cari teks frasa berkenaan yang terdahulu
+pdfjs-find-previous-button-label = Dahulu
+pdfjs-find-next-button =
+ .title = Cari teks frasa berkenaan yang berikut
+pdfjs-find-next-button-label = Berikut
+pdfjs-find-highlight-checkbox = Serlahkan semua
+pdfjs-find-match-case-checkbox-label = Huruf sepadan
+pdfjs-find-entire-word-checkbox-label = Seluruh perkataan
+pdfjs-find-reached-top = Mencapai teratas daripada dokumen, sambungan daripada bawah
+pdfjs-find-reached-bottom = Mencapai terakhir daripada dokumen, sambungan daripada atas
+pdfjs-find-not-found = Frasa tidak ditemui
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Lebar Halaman
+pdfjs-page-scale-fit = Muat Halaman
+pdfjs-page-scale-auto = Zoom Automatik
+pdfjs-page-scale-actual = Saiz Sebenar
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Masalah berlaku semasa menuatkan sebuah PDF.
+pdfjs-invalid-file-error = Tidak sah atau fail PDF rosak.
+pdfjs-missing-file-error = Fail PDF Hilang.
+pdfjs-unexpected-response-error = Respon pelayan yang tidak dijangka.
+pdfjs-rendering-error = Ralat berlaku ketika memberikan halaman.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Anotasi]
+
+## Password
+
+pdfjs-password-label = Masukan kata kunci untuk membuka fail PDF ini.
+pdfjs-password-invalid = Kata laluan salah. Cuba lagi.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Batal
+pdfjs-web-fonts-disabled = Fon web dinyahdayakan: tidak dapat menggunakan fon terbenam PDF.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/my/viewer.ftl b/web/locale/my/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..d3b973d87f18aecd9cbc12c783359566dc273171
--- /dev/null
+++ b/web/locale/my/viewer.ftl
@@ -0,0 +1,206 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = အရင် စာမျက်နှာ
+pdfjs-previous-button-label = အရင်နေရာ
+pdfjs-next-button =
+ .title = ရှေ့ စာမျက်နှာ
+pdfjs-next-button-label = နောက်တခု
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = စာမျက်နှာ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } ၏
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pagesCount } ၏ { $pageNumber })
+pdfjs-zoom-out-button =
+ .title = ချုံ့ပါ
+pdfjs-zoom-out-button-label = ချုံ့ပါ
+pdfjs-zoom-in-button =
+ .title = ချဲ့ပါ
+pdfjs-zoom-in-button-label = ချဲ့ပါ
+pdfjs-zoom-select =
+ .title = ချုံ့/ချဲ့ပါ
+pdfjs-presentation-mode-button =
+ .title = ဆွေးနွေးတင်ပြစနစ်သို့ ကူးပြောင်းပါ
+pdfjs-presentation-mode-button-label = ဆွေးနွေးတင်ပြစနစ်
+pdfjs-open-file-button =
+ .title = ဖိုင်အားဖွင့်ပါ။
+pdfjs-open-file-button-label = ဖွင့်ပါ
+pdfjs-print-button =
+ .title = ပုံနှိုပ်ပါ
+pdfjs-print-button-label = ပုံနှိုပ်ပါ
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ကိရိယာများ
+pdfjs-tools-button-label = ကိရိယာများ
+pdfjs-first-page-button =
+ .title = ပထမ စာမျက်နှာသို့
+pdfjs-first-page-button-label = ပထမ စာမျက်နှာသို့
+pdfjs-last-page-button =
+ .title = နောက်ဆုံး စာမျက်နှာသို့
+pdfjs-last-page-button-label = နောက်ဆုံး စာမျက်နှာသို့
+pdfjs-page-rotate-cw-button =
+ .title = နာရီလက်တံ အတိုင်း
+pdfjs-page-rotate-cw-button-label = နာရီလက်တံ အတိုင်း
+pdfjs-page-rotate-ccw-button =
+ .title = နာရီလက်တံ ပြောင်းပြန်
+pdfjs-page-rotate-ccw-button-label = နာရီလက်တံ ပြောင်းပြန်
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = မှတ်တမ်းမှတ်ရာ ဂုဏ်သတ္တိများ
+pdfjs-document-properties-button-label = မှတ်တမ်းမှတ်ရာ ဂုဏ်သတ္တိများ
+pdfjs-document-properties-file-name = ဖိုင် :
+pdfjs-document-properties-file-size = ဖိုင်ဆိုဒ် :
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } ကီလိုဘိုတ် ({ $size_b }ဘိုတ်)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = ခေါင်းစဉ် -
+pdfjs-document-properties-author = ရေးသားသူ:
+pdfjs-document-properties-subject = အကြောင်းအရာ:
+pdfjs-document-properties-keywords = သော့ချက် စာလုံး:
+pdfjs-document-properties-creation-date = ထုတ်လုပ်ရက်စွဲ:
+pdfjs-document-properties-modification-date = ပြင်ဆင်ရက်စွဲ:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = ဖန်တီးသူ:
+pdfjs-document-properties-producer = PDF ထုတ်လုပ်သူ:
+pdfjs-document-properties-version = PDF ဗားရှင်း:
+pdfjs-document-properties-page-count = စာမျက်နှာအရေအတွက်:
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+
+##
+
+pdfjs-document-properties-close-button = ပိတ်
+
+## Print
+
+pdfjs-print-progress-message = Preparing document for printing…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = ပယ်ဖျက်ပါ
+pdfjs-printing-not-supported = သတိပေးချက်၊ပရင့်ထုတ်ခြင်းကိုဤဘယောက်ဆာသည် ပြည့်ဝစွာထောက်ပံ့မထားပါ ။
+pdfjs-printing-not-ready = သတိပေးချက်: ယခု PDF ဖိုင်သည် ပုံနှိပ်ရန် မပြည့်စုံပါ
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = ဘေးတန်းဖွင့်ပိတ်
+pdfjs-toggle-sidebar-button-label = ဖွင့်ပိတ် ဆလိုက်ဒါ
+pdfjs-document-outline-button =
+ .title = စာတမ်းအကျဉ်းချုပ်ကို ပြပါ (စာရင်းအားလုံးကို ချုံ့/ချဲ့ရန် ကလစ်နှစ်ချက်နှိပ်ပါ)
+pdfjs-document-outline-button-label = စာတမ်းအကျဉ်းချုပ်
+pdfjs-attachments-button =
+ .title = တွဲချက်များ ပြပါ
+pdfjs-attachments-button-label = တွဲထားချက်များ
+pdfjs-thumbs-button =
+ .title = ပုံရိပ်ငယ်များကို ပြပါ
+pdfjs-thumbs-button-label = ပုံရိပ်ငယ်များ
+pdfjs-findbar-button =
+ .title = Find in Document
+pdfjs-findbar-button-label = ရှာဖွေပါ
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = စာမျက်နှာ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = စာမျက်နှာရဲ့ ပုံရိပ်ငယ် { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = ရှာဖွေပါ
+ .placeholder = စာတမ်းထဲတွင် ရှာဖွေရန်…
+pdfjs-find-previous-button =
+ .title = စကားစုရဲ့ အရင် ဖြစ်ပွားမှုကို ရှာဖွေပါ
+pdfjs-find-previous-button-label = နောက်သို့
+pdfjs-find-next-button =
+ .title = စကားစုရဲ့ နောက်ထပ် ဖြစ်ပွားမှုကို ရှာဖွေပါ
+pdfjs-find-next-button-label = ရှေ့သို့
+pdfjs-find-highlight-checkbox = အားလုံးကို မျဉ်းသားပါ
+pdfjs-find-match-case-checkbox-label = စာလုံး တိုက်ဆိုင်ပါ
+pdfjs-find-reached-top = စာမျက်နှာထိပ် ရောက်နေပြီ၊ အဆုံးကနေ ပြန်စပါ
+pdfjs-find-reached-bottom = စာမျက်နှာအဆုံး ရောက်နေပြီ၊ ထိပ်ကနေ ပြန်စပါ
+pdfjs-find-not-found = စကားစု မတွေ့ရဘူး
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = စာမျက်နှာ အကျယ်
+pdfjs-page-scale-fit = စာမျက်နှာ ကွက်တိ
+pdfjs-page-scale-auto = အလိုအလျောက် ချုံ့ချဲ့
+pdfjs-page-scale-actual = အမှန်တကယ်ရှိတဲ့ အရွယ်
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF ဖိုင် ကိုဆွဲတင်နေချိန်မှာ အမှားတစ်ခုတွေ့ရပါတယ်။
+pdfjs-invalid-file-error = မရသော သို့ ပျက်နေသော PDF ဖိုင်
+pdfjs-missing-file-error = PDF ပျောက်ဆုံး
+pdfjs-unexpected-response-error = မမျှော်လင့်ထားသော ဆာဗာမှ ပြန်ကြားချက်
+pdfjs-rendering-error = စာမျက်နှာကို ပုံဖော်နေချိန်မှာ အမှားတစ်ခုတွေ့ရပါတယ်။
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } အဓိပ္ပာယ်ဖွင့်ဆိုချက်]
+
+## Password
+
+pdfjs-password-label = ယခု PDF ကို ဖွင့်ရန် စကားဝှက်ကို ရိုက်ပါ။
+pdfjs-password-invalid = စာဝှက် မှားသည်။ ထပ်ကြိုးစားကြည့်ပါ။
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = ပယ်ဖျက်ပါ
+pdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/nb-NO/viewer.ftl b/web/locale/nb-NO/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..6519adb3887b9316f5d943c7988afe3dcdb9826b
--- /dev/null
+++ b/web/locale/nb-NO/viewer.ftl
@@ -0,0 +1,396 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Forrige side
+pdfjs-previous-button-label = Forrige
+pdfjs-next-button =
+ .title = Neste side
+pdfjs-next-button-label = Neste
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Side
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = av { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } av { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoom ut
+pdfjs-zoom-out-button-label = Zoom ut
+pdfjs-zoom-in-button =
+ .title = Zoom inn
+pdfjs-zoom-in-button-label = Zoom inn
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Bytt til presentasjonsmodus
+pdfjs-presentation-mode-button-label = Presentasjonsmodus
+pdfjs-open-file-button =
+ .title = Åpne fil
+pdfjs-open-file-button-label = Åpne
+pdfjs-print-button =
+ .title = Skriv ut
+pdfjs-print-button-label = Skriv ut
+pdfjs-save-button =
+ .title = Lagre
+pdfjs-save-button-label = Lagre
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Last ned
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Last ned
+pdfjs-bookmark-button =
+ .title = Gjeldende side (se URL fra gjeldende side)
+pdfjs-bookmark-button-label = Gjeldende side
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Verktøy
+pdfjs-tools-button-label = Verktøy
+pdfjs-first-page-button =
+ .title = Gå til første side
+pdfjs-first-page-button-label = Gå til første side
+pdfjs-last-page-button =
+ .title = Gå til siste side
+pdfjs-last-page-button-label = Gå til siste side
+pdfjs-page-rotate-cw-button =
+ .title = Roter med klokken
+pdfjs-page-rotate-cw-button-label = Roter med klokken
+pdfjs-page-rotate-ccw-button =
+ .title = Roter mot klokken
+pdfjs-page-rotate-ccw-button-label = Roter mot klokken
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktiver tekstmarkeringsverktøy
+pdfjs-cursor-text-select-tool-button-label = Tekstmarkeringsverktøy
+pdfjs-cursor-hand-tool-button =
+ .title = Aktiver handverktøy
+pdfjs-cursor-hand-tool-button-label = Handverktøy
+pdfjs-scroll-page-button =
+ .title = Bruk siderulling
+pdfjs-scroll-page-button-label = Siderulling
+pdfjs-scroll-vertical-button =
+ .title = Bruk vertikal rulling
+pdfjs-scroll-vertical-button-label = Vertikal rulling
+pdfjs-scroll-horizontal-button =
+ .title = Bruk horisontal rulling
+pdfjs-scroll-horizontal-button-label = Horisontal rulling
+pdfjs-scroll-wrapped-button =
+ .title = Bruk flersiderulling
+pdfjs-scroll-wrapped-button-label = Flersiderulling
+pdfjs-spread-none-button =
+ .title = Vis enkeltsider
+pdfjs-spread-none-button-label = Enkeltsider
+pdfjs-spread-odd-button =
+ .title = Vis oppslag med ulike sidenumre til venstre
+pdfjs-spread-odd-button-label = Oppslag med forside
+pdfjs-spread-even-button =
+ .title = Vis oppslag med like sidenumre til venstre
+pdfjs-spread-even-button-label = Oppslag uten forside
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumentegenskaper …
+pdfjs-document-properties-button-label = Dokumentegenskaper …
+pdfjs-document-properties-file-name = Filnavn:
+pdfjs-document-properties-file-size = Filstørrelse:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Dokumentegenskaper …
+pdfjs-document-properties-author = Forfatter:
+pdfjs-document-properties-subject = Emne:
+pdfjs-document-properties-keywords = Nøkkelord:
+pdfjs-document-properties-creation-date = Opprettet dato:
+pdfjs-document-properties-modification-date = Endret dato:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Opprettet av:
+pdfjs-document-properties-producer = PDF-verktøy:
+pdfjs-document-properties-version = PDF-versjon:
+pdfjs-document-properties-page-count = Sideantall:
+pdfjs-document-properties-page-size = Sidestørrelse:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = stående
+pdfjs-document-properties-page-size-orientation-landscape = liggende
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Hurtig nettvisning:
+pdfjs-document-properties-linearized-yes = Ja
+pdfjs-document-properties-linearized-no = Nei
+pdfjs-document-properties-close-button = Lukk
+
+## Print
+
+pdfjs-print-progress-message = Forbereder dokument for utskrift …
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Avbryt
+pdfjs-printing-not-supported = Advarsel: Utskrift er ikke fullstendig støttet av denne nettleseren.
+pdfjs-printing-not-ready = Advarsel: PDF er ikke fullstendig innlastet for utskrift.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Slå av/på sidestolpe
+pdfjs-toggle-sidebar-notification-button =
+ .title = Vis/gjem sidestolpe (dokumentet inneholder oversikt/vedlegg/lag)
+pdfjs-toggle-sidebar-button-label = Slå av/på sidestolpe
+pdfjs-document-outline-button =
+ .title = Vis dokumentdisposisjonen (dobbeltklikk for å utvide/skjule alle elementer)
+pdfjs-document-outline-button-label = Dokumentdisposisjon
+pdfjs-attachments-button =
+ .title = Vis vedlegg
+pdfjs-attachments-button-label = Vedlegg
+pdfjs-layers-button =
+ .title = Vis lag (dobbeltklikk for å tilbakestille alle lag til standardtilstand)
+pdfjs-layers-button-label = Lag
+pdfjs-thumbs-button =
+ .title = Vis miniatyrbilde
+pdfjs-thumbs-button-label = Miniatyrbilde
+pdfjs-current-outline-item-button =
+ .title = Finn gjeldende disposisjonselement
+pdfjs-current-outline-item-button-label = Gjeldende disposisjonselement
+pdfjs-findbar-button =
+ .title = Finn i dokumentet
+pdfjs-findbar-button-label = Finn
+pdfjs-additional-layers = Ytterligere lag
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Side { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatyrbilde av side { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Søk
+ .placeholder = Søk i dokument…
+pdfjs-find-previous-button =
+ .title = Finn forrige forekomst av frasen
+pdfjs-find-previous-button-label = Forrige
+pdfjs-find-next-button =
+ .title = Finn neste forekomst av frasen
+pdfjs-find-next-button-label = Neste
+pdfjs-find-highlight-checkbox = Uthev alle
+pdfjs-find-match-case-checkbox-label = Skill store/små bokstaver
+pdfjs-find-match-diacritics-checkbox-label = Samsvar diakritiske tegn
+pdfjs-find-entire-word-checkbox-label = Hele ord
+pdfjs-find-reached-top = Nådde toppen av dokumentet, fortsetter fra bunnen
+pdfjs-find-reached-bottom = Nådde bunnen av dokumentet, fortsetter fra toppen
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } av { $total } treff
+ *[other] { $current } av { $total } treff
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Mer enn { $limit } treff
+ *[other] Mer enn { $limit } treff
+ }
+pdfjs-find-not-found = Fant ikke teksten
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Sidebredde
+pdfjs-page-scale-fit = Tilpass til siden
+pdfjs-page-scale-auto = Automatisk zoom
+pdfjs-page-scale-actual = Virkelig størrelse
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale } %
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Side { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = En feil oppstod ved lasting av PDF.
+pdfjs-invalid-file-error = Ugyldig eller skadet PDF-fil.
+pdfjs-missing-file-error = Manglende PDF-fil.
+pdfjs-unexpected-response-error = Uventet serverrespons.
+pdfjs-rendering-error = En feil oppstod ved opptegning av siden.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } annotasjon]
+
+## Password
+
+pdfjs-password-label = Skriv inn passordet for å åpne denne PDF-filen.
+pdfjs-password-invalid = Ugyldig passord. Prøv igjen.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Avbryt
+pdfjs-web-fonts-disabled = Web-fonter er avslått: Kan ikke bruke innbundne PDF-fonter.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+pdfjs-editor-ink-button =
+ .title = Tegn
+pdfjs-editor-ink-button-label = Tegn
+pdfjs-editor-stamp-button =
+ .title = Legg til eller rediger bilder
+pdfjs-editor-stamp-button-label = Legg til eller rediger bilder
+pdfjs-editor-highlight-button =
+ .title = Markere
+pdfjs-editor-highlight-button-label = Markere
+pdfjs-highlight-floating-button =
+ .title = Markere
+pdfjs-highlight-floating-button1 =
+ .title = Markere
+ .aria-label = Markere
+pdfjs-highlight-floating-button-label = Markere
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Fjern tegningen
+pdfjs-editor-remove-freetext-button =
+ .title = Fjern tekst
+pdfjs-editor-remove-stamp-button =
+ .title = Fjern bildet
+pdfjs-editor-remove-highlight-button =
+ .title = Fjern utheving
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Farge
+pdfjs-editor-free-text-size-input = Størrelse
+pdfjs-editor-ink-color-input = Farge
+pdfjs-editor-ink-thickness-input = Tykkelse
+pdfjs-editor-ink-opacity-input = Ugjennomsiktighet
+pdfjs-editor-stamp-add-image-button =
+ .title = Legg til bilde
+pdfjs-editor-stamp-add-image-button-label = Legg til bilde
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Tykkelse
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Endre tykkelse når du markerer andre elementer enn tekst
+pdfjs-free-text =
+ .aria-label = Tekstredigering
+pdfjs-free-text-default-content = Begynn å skrive…
+pdfjs-ink =
+ .aria-label = Tegneredigering
+pdfjs-ink-canvas =
+ .aria-label = Brukerskapt bilde
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alt-tekst
+pdfjs-editor-alt-text-edit-button-label = Rediger alt-tekst tekst
+pdfjs-editor-alt-text-dialog-label = Velg et alternativ
+pdfjs-editor-alt-text-dialog-description = Alt-tekst (alternativ tekst) hjelper når folk ikke kan se bildet eller når det ikke lastes inn.
+pdfjs-editor-alt-text-add-description-label = Legg til en beskrivelse
+pdfjs-editor-alt-text-add-description-description = Gå etter 1-2 setninger som beskriver emnet, settingen eller handlingene.
+pdfjs-editor-alt-text-mark-decorative-label = Merk som dekorativt
+pdfjs-editor-alt-text-mark-decorative-description = Dette brukes til dekorative bilder, som kantlinjer eller vannmerker.
+pdfjs-editor-alt-text-cancel-button = Avbryt
+pdfjs-editor-alt-text-save-button = Lagre
+pdfjs-editor-alt-text-decorative-tooltip = Merket som dekorativ
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = For eksempel, «En ung mann setter seg ved et bord for å spise et måltid»
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Øverste venstre hjørne – endre størrelse
+pdfjs-editor-resizer-label-top-middle = Øverst i midten — endre størrelse
+pdfjs-editor-resizer-label-top-right = Øverste høyre hjørne – endre størrelse
+pdfjs-editor-resizer-label-middle-right = Midt til høyre – endre størrelse
+pdfjs-editor-resizer-label-bottom-right = Nederste høyre hjørne – endre størrelse
+pdfjs-editor-resizer-label-bottom-middle = Nederst i midten — endre størrelse
+pdfjs-editor-resizer-label-bottom-left = Nederste venstre hjørne – endre størrelse
+pdfjs-editor-resizer-label-middle-left = Midt til venstre — endre størrelse
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Uthevingsfarge
+pdfjs-editor-colorpicker-button =
+ .title = Endre farge
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Fargevalg
+pdfjs-editor-colorpicker-yellow =
+ .title = Gul
+pdfjs-editor-colorpicker-green =
+ .title = Grønn
+pdfjs-editor-colorpicker-blue =
+ .title = Blå
+pdfjs-editor-colorpicker-pink =
+ .title = Rosa
+pdfjs-editor-colorpicker-red =
+ .title = Rød
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Vis alle
+pdfjs-editor-highlight-show-all-button =
+ .title = Vis alle
diff --git a/web/locale/ne-NP/viewer.ftl b/web/locale/ne-NP/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..65193b6e8cd3a13f7647c6d67fa7ec4b7ecd007e
--- /dev/null
+++ b/web/locale/ne-NP/viewer.ftl
@@ -0,0 +1,234 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = अघिल्लो पृष्ठ
+pdfjs-previous-button-label = अघिल्लो
+pdfjs-next-button =
+ .title = पछिल्लो पृष्ठ
+pdfjs-next-button-label = पछिल्लो
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = पृष्ठ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } मध्ये
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pagesCount } को { $pageNumber })
+pdfjs-zoom-out-button =
+ .title = जुम घटाउनुहोस्
+pdfjs-zoom-out-button-label = जुम घटाउनुहोस्
+pdfjs-zoom-in-button =
+ .title = जुम बढाउनुहोस्
+pdfjs-zoom-in-button-label = जुम बढाउनुहोस्
+pdfjs-zoom-select =
+ .title = जुम गर्नुहोस्
+pdfjs-presentation-mode-button =
+ .title = प्रस्तुति मोडमा जानुहोस्
+pdfjs-presentation-mode-button-label = प्रस्तुति मोड
+pdfjs-open-file-button =
+ .title = फाइल खोल्नुहोस्
+pdfjs-open-file-button-label = खोल्नुहोस्
+pdfjs-print-button =
+ .title = मुद्रण गर्नुहोस्
+pdfjs-print-button-label = मुद्रण गर्नुहोस्
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = औजारहरू
+pdfjs-tools-button-label = औजारहरू
+pdfjs-first-page-button =
+ .title = पहिलो पृष्ठमा जानुहोस्
+pdfjs-first-page-button-label = पहिलो पृष्ठमा जानुहोस्
+pdfjs-last-page-button =
+ .title = पछिल्लो पृष्ठमा जानुहोस्
+pdfjs-last-page-button-label = पछिल्लो पृष्ठमा जानुहोस्
+pdfjs-page-rotate-cw-button =
+ .title = घडीको दिशामा घुमाउनुहोस्
+pdfjs-page-rotate-cw-button-label = घडीको दिशामा घुमाउनुहोस्
+pdfjs-page-rotate-ccw-button =
+ .title = घडीको विपरित दिशामा घुमाउनुहोस्
+pdfjs-page-rotate-ccw-button-label = घडीको विपरित दिशामा घुमाउनुहोस्
+pdfjs-cursor-text-select-tool-button =
+ .title = पाठ चयन उपकरण सक्षम गर्नुहोस्
+pdfjs-cursor-text-select-tool-button-label = पाठ चयन उपकरण
+pdfjs-cursor-hand-tool-button =
+ .title = हाते उपकरण सक्षम गर्नुहोस्
+pdfjs-cursor-hand-tool-button-label = हाते उपकरण
+pdfjs-scroll-vertical-button =
+ .title = ठाडो स्क्रोलिङ्ग प्रयोग गर्नुहोस्
+pdfjs-scroll-vertical-button-label = ठाडो स्क्र्रोलिङ्ग
+pdfjs-scroll-horizontal-button =
+ .title = तेर्सो स्क्रोलिङ्ग प्रयोग गर्नुहोस्
+pdfjs-scroll-horizontal-button-label = तेर्सो स्क्रोलिङ्ग
+pdfjs-scroll-wrapped-button =
+ .title = लिपि स्क्रोलिङ्ग प्रयोग गर्नुहोस्
+pdfjs-scroll-wrapped-button-label = लिपि स्क्रोलिङ्ग
+pdfjs-spread-none-button =
+ .title = पृष्ठ स्प्रेडमा सामेल हुनुहुन्न
+pdfjs-spread-none-button-label = स्प्रेड छैन
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = कागजात विशेषताहरू...
+pdfjs-document-properties-button-label = कागजात विशेषताहरू...
+pdfjs-document-properties-file-name = फाइल नाम:
+pdfjs-document-properties-file-size = फाइल आकार:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = शीर्षक:
+pdfjs-document-properties-author = लेखक:
+pdfjs-document-properties-subject = विषयः
+pdfjs-document-properties-keywords = शब्दकुञ्जीः
+pdfjs-document-properties-creation-date = सिर्जना गरिएको मिति:
+pdfjs-document-properties-modification-date = परिमार्जित मिति:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = सर्जक:
+pdfjs-document-properties-producer = PDF निर्माता:
+pdfjs-document-properties-version = PDF संस्करण
+pdfjs-document-properties-page-count = पृष्ठ गणना:
+pdfjs-document-properties-page-size = पृष्ठ आकार:
+pdfjs-document-properties-page-size-unit-inches = इन्च
+pdfjs-document-properties-page-size-unit-millimeters = मि.मि.
+pdfjs-document-properties-page-size-orientation-portrait = पोट्रेट
+pdfjs-document-properties-page-size-orientation-landscape = परिदृश्य
+pdfjs-document-properties-page-size-name-letter = अक्षर
+pdfjs-document-properties-page-size-name-legal = कानूनी
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+
+##
+
+pdfjs-document-properties-linearized-yes = हो
+pdfjs-document-properties-linearized-no = होइन
+pdfjs-document-properties-close-button = बन्द गर्नुहोस्
+
+## Print
+
+pdfjs-print-progress-message = मुद्रणका लागि कागजात तयारी गरिदै…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = रद्द गर्नुहोस्
+pdfjs-printing-not-supported = चेतावनी: यो ब्राउजरमा मुद्रण पूर्णतया समर्थित छैन।
+pdfjs-printing-not-ready = चेतावनी: PDF मुद्रणका लागि पूर्णतया लोड भएको छैन।
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = टगल साइडबार
+pdfjs-toggle-sidebar-button-label = टगल साइडबार
+pdfjs-document-outline-button =
+ .title = कागजातको रूपरेखा देखाउनुहोस् (सबै वस्तुहरू विस्तार/पतन गर्न डबल-क्लिक गर्नुहोस्)
+pdfjs-document-outline-button-label = दस्तावेजको रूपरेखा
+pdfjs-attachments-button =
+ .title = संलग्नहरू देखाउनुहोस्
+pdfjs-attachments-button-label = संलग्नकहरू
+pdfjs-thumbs-button =
+ .title = थम्बनेलहरू देखाउनुहोस्
+pdfjs-thumbs-button-label = थम्बनेलहरू
+pdfjs-findbar-button =
+ .title = कागजातमा फेला पार्नुहोस्
+pdfjs-findbar-button-label = फेला पार्नुहोस्
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = पृष्ठ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } पृष्ठको थम्बनेल
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = फेला पार्नुहोस्
+ .placeholder = कागजातमा फेला पार्नुहोस्…
+pdfjs-find-previous-button =
+ .title = यस वाक्यांशको अघिल्लो घटना फेला पार्नुहोस्
+pdfjs-find-previous-button-label = अघिल्लो
+pdfjs-find-next-button =
+ .title = यस वाक्यांशको पछिल्लो घटना फेला पार्नुहोस्
+pdfjs-find-next-button-label = अर्को
+pdfjs-find-highlight-checkbox = सबै हाइलाइट गर्ने
+pdfjs-find-match-case-checkbox-label = केस जोडा मिलाउनुहोस्
+pdfjs-find-entire-word-checkbox-label = पुरा शब्दहरु
+pdfjs-find-reached-top = पृष्ठको शिर्षमा पुगीयो, तलबाट जारी गरिएको थियो
+pdfjs-find-reached-bottom = पृष्ठको अन्त्यमा पुगीयो, शिर्षबाट जारी गरिएको थियो
+pdfjs-find-not-found = वाक्यांश फेला परेन
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = पृष्ठ चौडाइ
+pdfjs-page-scale-fit = पृष्ठ ठिक्क मिल्ने
+pdfjs-page-scale-auto = स्वचालित जुम
+pdfjs-page-scale-actual = वास्तविक आकार
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = यो PDF लोड गर्दा एउटा त्रुटि देखापर्यो।
+pdfjs-invalid-file-error = अवैध वा दुषित PDF फाइल।
+pdfjs-missing-file-error = हराईरहेको PDF फाइल।
+pdfjs-unexpected-response-error = अप्रत्याशित सर्भर प्रतिक्रिया।
+pdfjs-rendering-error = पृष्ठ प्रतिपादन गर्दा एउटा त्रुटि देखापर्यो।
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = यस PDF फाइललाई खोल्न गोप्यशब्द प्रविष्ट गर्नुहोस्।
+pdfjs-password-invalid = अवैध गोप्यशब्द। पुनः प्रयास गर्नुहोस्।
+pdfjs-password-ok-button = ठिक छ
+pdfjs-password-cancel-button = रद्द गर्नुहोस्
+pdfjs-web-fonts-disabled = वेब फन्ट असक्षम छन्: एम्बेडेड PDF फन्ट प्रयोग गर्न असमर्थ।
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/nl/viewer.ftl b/web/locale/nl/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..a1dd47d3c165bfa2605c5e267f028d227669068d
--- /dev/null
+++ b/web/locale/nl/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Vorige pagina
+pdfjs-previous-button-label = Vorige
+pdfjs-next-button =
+ .title = Volgende pagina
+pdfjs-next-button-label = Volgende
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pagina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = van { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } van { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Uitzoomen
+pdfjs-zoom-out-button-label = Uitzoomen
+pdfjs-zoom-in-button =
+ .title = Inzoomen
+pdfjs-zoom-in-button-label = Inzoomen
+pdfjs-zoom-select =
+ .title = Zoomen
+pdfjs-presentation-mode-button =
+ .title = Wisselen naar presentatiemodus
+pdfjs-presentation-mode-button-label = Presentatiemodus
+pdfjs-open-file-button =
+ .title = Bestand openen
+pdfjs-open-file-button-label = Openen
+pdfjs-print-button =
+ .title = Afdrukken
+pdfjs-print-button-label = Afdrukken
+pdfjs-save-button =
+ .title = Opslaan
+pdfjs-save-button-label = Opslaan
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Downloaden
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Downloaden
+pdfjs-bookmark-button =
+ .title = Huidige pagina (URL van huidige pagina bekijken)
+pdfjs-bookmark-button-label = Huidige pagina
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Openen in app
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Openen in app
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Hulpmiddelen
+pdfjs-tools-button-label = Hulpmiddelen
+pdfjs-first-page-button =
+ .title = Naar eerste pagina gaan
+pdfjs-first-page-button-label = Naar eerste pagina gaan
+pdfjs-last-page-button =
+ .title = Naar laatste pagina gaan
+pdfjs-last-page-button-label = Naar laatste pagina gaan
+pdfjs-page-rotate-cw-button =
+ .title = Rechtsom draaien
+pdfjs-page-rotate-cw-button-label = Rechtsom draaien
+pdfjs-page-rotate-ccw-button =
+ .title = Linksom draaien
+pdfjs-page-rotate-ccw-button-label = Linksom draaien
+pdfjs-cursor-text-select-tool-button =
+ .title = Tekstselectiehulpmiddel inschakelen
+pdfjs-cursor-text-select-tool-button-label = Tekstselectiehulpmiddel
+pdfjs-cursor-hand-tool-button =
+ .title = Handhulpmiddel inschakelen
+pdfjs-cursor-hand-tool-button-label = Handhulpmiddel
+pdfjs-scroll-page-button =
+ .title = Paginascrollen gebruiken
+pdfjs-scroll-page-button-label = Paginascrollen
+pdfjs-scroll-vertical-button =
+ .title = Verticaal scrollen gebruiken
+pdfjs-scroll-vertical-button-label = Verticaal scrollen
+pdfjs-scroll-horizontal-button =
+ .title = Horizontaal scrollen gebruiken
+pdfjs-scroll-horizontal-button-label = Horizontaal scrollen
+pdfjs-scroll-wrapped-button =
+ .title = Scrollen met terugloop gebruiken
+pdfjs-scroll-wrapped-button-label = Scrollen met terugloop
+pdfjs-spread-none-button =
+ .title = Dubbele pagina’s niet samenvoegen
+pdfjs-spread-none-button-label = Geen dubbele pagina’s
+pdfjs-spread-odd-button =
+ .title = Dubbele pagina’s samenvoegen vanaf oneven pagina’s
+pdfjs-spread-odd-button-label = Oneven dubbele pagina’s
+pdfjs-spread-even-button =
+ .title = Dubbele pagina’s samenvoegen vanaf even pagina’s
+pdfjs-spread-even-button-label = Even dubbele pagina’s
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Documenteigenschappen…
+pdfjs-document-properties-button-label = Documenteigenschappen…
+pdfjs-document-properties-file-name = Bestandsnaam:
+pdfjs-document-properties-file-size = Bestandsgrootte:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Titel:
+pdfjs-document-properties-author = Auteur:
+pdfjs-document-properties-subject = Onderwerp:
+pdfjs-document-properties-keywords = Sleutelwoorden:
+pdfjs-document-properties-creation-date = Aanmaakdatum:
+pdfjs-document-properties-modification-date = Wijzigingsdatum:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Maker:
+pdfjs-document-properties-producer = PDF-producent:
+pdfjs-document-properties-version = PDF-versie:
+pdfjs-document-properties-page-count = Aantal pagina’s:
+pdfjs-document-properties-page-size = Paginagrootte:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = staand
+pdfjs-document-properties-page-size-orientation-landscape = liggend
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Snelle webweergave:
+pdfjs-document-properties-linearized-yes = Ja
+pdfjs-document-properties-linearized-no = Nee
+pdfjs-document-properties-close-button = Sluiten
+
+## Print
+
+pdfjs-print-progress-message = Document voorbereiden voor afdrukken…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Annuleren
+pdfjs-printing-not-supported = Waarschuwing: afdrukken wordt niet volledig ondersteund door deze browser.
+pdfjs-printing-not-ready = Waarschuwing: de PDF is niet volledig geladen voor afdrukken.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Zijbalk in-/uitschakelen
+pdfjs-toggle-sidebar-notification-button =
+ .title = Zijbalk in-/uitschakelen (document bevat overzicht/bijlagen/lagen)
+pdfjs-toggle-sidebar-button-label = Zijbalk in-/uitschakelen
+pdfjs-document-outline-button =
+ .title = Documentoverzicht tonen (dubbelklik om alle items uit/samen te vouwen)
+pdfjs-document-outline-button-label = Documentoverzicht
+pdfjs-attachments-button =
+ .title = Bijlagen tonen
+pdfjs-attachments-button-label = Bijlagen
+pdfjs-layers-button =
+ .title = Lagen tonen (dubbelklik om alle lagen naar de standaardstatus terug te zetten)
+pdfjs-layers-button-label = Lagen
+pdfjs-thumbs-button =
+ .title = Miniaturen tonen
+pdfjs-thumbs-button-label = Miniaturen
+pdfjs-current-outline-item-button =
+ .title = Huidig item in inhoudsopgave zoeken
+pdfjs-current-outline-item-button-label = Huidig item in inhoudsopgave
+pdfjs-findbar-button =
+ .title = Zoeken in document
+pdfjs-findbar-button-label = Zoeken
+pdfjs-additional-layers = Aanvullende lagen
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pagina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatuur van pagina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Zoeken
+ .placeholder = Zoeken in document…
+pdfjs-find-previous-button =
+ .title = De vorige overeenkomst van de tekst zoeken
+pdfjs-find-previous-button-label = Vorige
+pdfjs-find-next-button =
+ .title = De volgende overeenkomst van de tekst zoeken
+pdfjs-find-next-button-label = Volgende
+pdfjs-find-highlight-checkbox = Alles markeren
+pdfjs-find-match-case-checkbox-label = Hoofdlettergevoelig
+pdfjs-find-match-diacritics-checkbox-label = Diakritische tekens gebruiken
+pdfjs-find-entire-word-checkbox-label = Hele woorden
+pdfjs-find-reached-top = Bovenkant van document bereikt, doorgegaan vanaf onderkant
+pdfjs-find-reached-bottom = Onderkant van document bereikt, doorgegaan vanaf bovenkant
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } van { $total } overeenkomst
+ *[other] { $current } van { $total } overeenkomsten
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Meer dan { $limit } overeenkomst
+ *[other] Meer dan { $limit } overeenkomsten
+ }
+pdfjs-find-not-found = Tekst niet gevonden
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Paginabreedte
+pdfjs-page-scale-fit = Hele pagina
+pdfjs-page-scale-auto = Automatisch zoomen
+pdfjs-page-scale-actual = Werkelijke grootte
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Pagina { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Er is een fout opgetreden bij het laden van de PDF.
+pdfjs-invalid-file-error = Ongeldig of beschadigd PDF-bestand.
+pdfjs-missing-file-error = PDF-bestand ontbreekt.
+pdfjs-unexpected-response-error = Onverwacht serverantwoord.
+pdfjs-rendering-error = Er is een fout opgetreden bij het weergeven van de pagina.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type }-aantekening]
+
+## Password
+
+pdfjs-password-label = Voer het wachtwoord in om dit PDF-bestand te openen.
+pdfjs-password-invalid = Ongeldig wachtwoord. Probeer het opnieuw.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Annuleren
+pdfjs-web-fonts-disabled = Weblettertypen zijn uitgeschakeld: gebruik van ingebedde PDF-lettertypen is niet mogelijk.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+pdfjs-editor-ink-button =
+ .title = Tekenen
+pdfjs-editor-ink-button-label = Tekenen
+pdfjs-editor-stamp-button =
+ .title = Afbeeldingen toevoegen of bewerken
+pdfjs-editor-stamp-button-label = Afbeeldingen toevoegen of bewerken
+pdfjs-editor-highlight-button =
+ .title = Markeren
+pdfjs-editor-highlight-button-label = Markeren
+pdfjs-highlight-floating-button =
+ .title = Markeren
+pdfjs-highlight-floating-button1 =
+ .title = Markeren
+ .aria-label = Markeren
+pdfjs-highlight-floating-button-label = Markeren
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Tekening verwijderen
+pdfjs-editor-remove-freetext-button =
+ .title = Tekst verwijderen
+pdfjs-editor-remove-stamp-button =
+ .title = Afbeelding verwijderen
+pdfjs-editor-remove-highlight-button =
+ .title = Markering verwijderen
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Kleur
+pdfjs-editor-free-text-size-input = Grootte
+pdfjs-editor-ink-color-input = Kleur
+pdfjs-editor-ink-thickness-input = Dikte
+pdfjs-editor-ink-opacity-input = Opaciteit
+pdfjs-editor-stamp-add-image-button =
+ .title = Afbeelding toevoegen
+pdfjs-editor-stamp-add-image-button-label = Afbeelding toevoegen
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Dikte
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Dikte wijzigen bij accentuering van andere items dan tekst
+pdfjs-free-text =
+ .aria-label = Tekstbewerker
+pdfjs-free-text-default-content = Begin met typen…
+pdfjs-ink =
+ .aria-label = Tekeningbewerker
+pdfjs-ink-canvas =
+ .aria-label = Door gebruiker gemaakte afbeelding
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternatieve tekst
+pdfjs-editor-alt-text-edit-button-label = Alternatieve tekst bewerken
+pdfjs-editor-alt-text-dialog-label = Kies een optie
+pdfjs-editor-alt-text-dialog-description = Alternatieve tekst helpt wanneer mensen de afbeelding niet kunnen zien of wanneer deze niet wordt geladen.
+pdfjs-editor-alt-text-add-description-label = Voeg een beschrijving toe
+pdfjs-editor-alt-text-add-description-description = Streef naar 1-2 zinnen die het onderwerp, de omgeving of de acties beschrijven.
+pdfjs-editor-alt-text-mark-decorative-label = Als decoratief markeren
+pdfjs-editor-alt-text-mark-decorative-description = Dit wordt gebruikt voor sierafbeeldingen, zoals randen of watermerken.
+pdfjs-editor-alt-text-cancel-button = Annuleren
+pdfjs-editor-alt-text-save-button = Opslaan
+pdfjs-editor-alt-text-decorative-tooltip = Als decoratief gemarkeerd
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Bijvoorbeeld: ‘Een jonge man gaat aan een tafel zitten om te eten’
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Linkerbovenhoek – formaat wijzigen
+pdfjs-editor-resizer-label-top-middle = Midden boven – formaat wijzigen
+pdfjs-editor-resizer-label-top-right = Rechterbovenhoek – formaat wijzigen
+pdfjs-editor-resizer-label-middle-right = Midden rechts – formaat wijzigen
+pdfjs-editor-resizer-label-bottom-right = Rechterbenedenhoek – formaat wijzigen
+pdfjs-editor-resizer-label-bottom-middle = Midden onder – formaat wijzigen
+pdfjs-editor-resizer-label-bottom-left = Linkerbenedenhoek – formaat wijzigen
+pdfjs-editor-resizer-label-middle-left = Links midden – formaat wijzigen
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Markeringskleur
+pdfjs-editor-colorpicker-button =
+ .title = Kleur wijzigen
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Kleurkeuzes
+pdfjs-editor-colorpicker-yellow =
+ .title = Geel
+pdfjs-editor-colorpicker-green =
+ .title = Groen
+pdfjs-editor-colorpicker-blue =
+ .title = Blauw
+pdfjs-editor-colorpicker-pink =
+ .title = Roze
+pdfjs-editor-colorpicker-red =
+ .title = Rood
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Alles tonen
+pdfjs-editor-highlight-show-all-button =
+ .title = Alles tonen
diff --git a/web/locale/nn-NO/viewer.ftl b/web/locale/nn-NO/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..476e4c15bf1f037caa63abf08fb64c3fd73911de
--- /dev/null
+++ b/web/locale/nn-NO/viewer.ftl
@@ -0,0 +1,360 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Føregåande side
+pdfjs-previous-button-label = Føregåande
+pdfjs-next-button =
+ .title = Neste side
+pdfjs-next-button-label = Neste
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Side
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = av { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } av { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoom ut
+pdfjs-zoom-out-button-label = Zoom ut
+pdfjs-zoom-in-button =
+ .title = Zoom inn
+pdfjs-zoom-in-button-label = Zoom inn
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Byt til presentasjonsmodus
+pdfjs-presentation-mode-button-label = Presentasjonsmodus
+pdfjs-open-file-button =
+ .title = Opne fil
+pdfjs-open-file-button-label = Opne
+pdfjs-print-button =
+ .title = Skriv ut
+pdfjs-print-button-label = Skriv ut
+pdfjs-save-button =
+ .title = Lagre
+pdfjs-save-button-label = Lagre
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Last ned
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Last ned
+pdfjs-bookmark-button =
+ .title = Gjeldande side (sjå URL frå gjeldande side)
+pdfjs-bookmark-button-label = Gjeldande side
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Opne i app
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Opne i app
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Verktøy
+pdfjs-tools-button-label = Verktøy
+pdfjs-first-page-button =
+ .title = Gå til første side
+pdfjs-first-page-button-label = Gå til første side
+pdfjs-last-page-button =
+ .title = Gå til siste side
+pdfjs-last-page-button-label = Gå til siste side
+pdfjs-page-rotate-cw-button =
+ .title = Roter med klokka
+pdfjs-page-rotate-cw-button-label = Roter med klokka
+pdfjs-page-rotate-ccw-button =
+ .title = Roter mot klokka
+pdfjs-page-rotate-ccw-button-label = Roter mot klokka
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktiver tekstmarkeringsverktøy
+pdfjs-cursor-text-select-tool-button-label = Tekstmarkeringsverktøy
+pdfjs-cursor-hand-tool-button =
+ .title = Aktiver handverktøy
+pdfjs-cursor-hand-tool-button-label = Handverktøy
+pdfjs-scroll-page-button =
+ .title = Bruk siderulling
+pdfjs-scroll-page-button-label = Siderulling
+pdfjs-scroll-vertical-button =
+ .title = Bruk vertikal rulling
+pdfjs-scroll-vertical-button-label = Vertikal rulling
+pdfjs-scroll-horizontal-button =
+ .title = Bruk horisontal rulling
+pdfjs-scroll-horizontal-button-label = Horisontal rulling
+pdfjs-scroll-wrapped-button =
+ .title = Bruk fleirsiderulling
+pdfjs-scroll-wrapped-button-label = Fleirsiderulling
+pdfjs-spread-none-button =
+ .title = Vis enkeltsider
+pdfjs-spread-none-button-label = Enkeltside
+pdfjs-spread-odd-button =
+ .title = Vis oppslag med ulike sidenummer til venstre
+pdfjs-spread-odd-button-label = Oppslag med framside
+pdfjs-spread-even-button =
+ .title = Vis oppslag med like sidenummmer til venstre
+pdfjs-spread-even-button-label = Oppslag utan framside
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumenteigenskapar…
+pdfjs-document-properties-button-label = Dokumenteigenskapar…
+pdfjs-document-properties-file-name = Filnamn:
+pdfjs-document-properties-file-size = Filstorleik:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Tittel:
+pdfjs-document-properties-author = Forfattar:
+pdfjs-document-properties-subject = Emne:
+pdfjs-document-properties-keywords = Stikkord:
+pdfjs-document-properties-creation-date = Dato oppretta:
+pdfjs-document-properties-modification-date = Dato endra:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Oppretta av:
+pdfjs-document-properties-producer = PDF-verktøy:
+pdfjs-document-properties-version = PDF-versjon:
+pdfjs-document-properties-page-count = Sidetal:
+pdfjs-document-properties-page-size = Sidestørrelse:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = ståande
+pdfjs-document-properties-page-size-orientation-landscape = liggande
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Brev
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Rask nettvising:
+pdfjs-document-properties-linearized-yes = Ja
+pdfjs-document-properties-linearized-no = Nei
+pdfjs-document-properties-close-button = Lat att
+
+## Print
+
+pdfjs-print-progress-message = Førebur dokumentet for utskrift…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Avbryt
+pdfjs-printing-not-supported = Åtvaring: Utskrift er ikkje fullstendig støtta av denne nettlesaren.
+pdfjs-printing-not-ready = Åtvaring: PDF ikkje fullstendig innlasta for utskrift.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Slå av/på sidestolpe
+pdfjs-toggle-sidebar-notification-button =
+ .title = Vis/gøym sidestolpe (dokumentet inneheld oversikt/vedlegg/lag)
+pdfjs-toggle-sidebar-button-label = Slå av/på sidestolpe
+pdfjs-document-outline-button =
+ .title = Vis dokumentdisposisjonen (dobbelklikk for å utvide/gøyme alle elementa)
+pdfjs-document-outline-button-label = Dokumentdisposisjon
+pdfjs-attachments-button =
+ .title = Vis vedlegg
+pdfjs-attachments-button-label = Vedlegg
+pdfjs-layers-button =
+ .title = Vis lag (dobbeltklikk for å tilbakestille alle lag til standardtilstand)
+pdfjs-layers-button-label = Lag
+pdfjs-thumbs-button =
+ .title = Vis miniatyrbilde
+pdfjs-thumbs-button-label = Miniatyrbilde
+pdfjs-current-outline-item-button =
+ .title = Finn gjeldande disposisjonselement
+pdfjs-current-outline-item-button-label = Gjeldande disposisjonselement
+pdfjs-findbar-button =
+ .title = Finn i dokumentet
+pdfjs-findbar-button-label = Finn
+pdfjs-additional-layers = Ytterlegare lag
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Side { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatyrbilde av side { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Søk
+ .placeholder = Søk i dokument…
+pdfjs-find-previous-button =
+ .title = Finn førre førekomst av frasen
+pdfjs-find-previous-button-label = Førre
+pdfjs-find-next-button =
+ .title = Finn neste førekomst av frasen
+pdfjs-find-next-button-label = Neste
+pdfjs-find-highlight-checkbox = Uthev alle
+pdfjs-find-match-case-checkbox-label = Skil store/små bokstavar
+pdfjs-find-match-diacritics-checkbox-label = Samsvar diakritiske teikn
+pdfjs-find-entire-word-checkbox-label = Heile ord
+pdfjs-find-reached-top = Nådde toppen av dokumentet, fortset frå botnen
+pdfjs-find-reached-bottom = Nådde botnen av dokumentet, fortset frå toppen
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } av { $total } treff
+ *[other] { $current } av { $total } treff
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Meir enn { $limit } treff
+ *[other] Meir enn { $limit } treff
+ }
+pdfjs-find-not-found = Fann ikkje teksten
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Sidebreidde
+pdfjs-page-scale-fit = Tilpass til sida
+pdfjs-page-scale-auto = Automatisk skalering
+pdfjs-page-scale-actual = Verkeleg storleik
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Side { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ein feil oppstod ved lasting av PDF.
+pdfjs-invalid-file-error = Ugyldig eller korrupt PDF-fil.
+pdfjs-missing-file-error = Manglande PDF-fil.
+pdfjs-unexpected-response-error = Uventa tenarrespons.
+pdfjs-rendering-error = Ein feil oppstod under vising av sida.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date } { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } annotasjon]
+
+## Password
+
+pdfjs-password-label = Skriv inn passordet for å opne denne PDF-fila.
+pdfjs-password-invalid = Ugyldig passord. Prøv på nytt.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Avbryt
+pdfjs-web-fonts-disabled = Web-skrifter er slått av: Kan ikkje bruke innbundne PDF-skrifter.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+pdfjs-editor-ink-button =
+ .title = Teikne
+pdfjs-editor-ink-button-label = Teikne
+pdfjs-editor-stamp-button =
+ .title = Legg til eller rediger bilde
+pdfjs-editor-stamp-button-label = Legg til eller rediger bilde
+
+## Remove button for the various kind of editor.
+
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Farge
+pdfjs-editor-free-text-size-input = Storleik
+pdfjs-editor-ink-color-input = Farge
+pdfjs-editor-ink-thickness-input = Tjukkleik
+pdfjs-editor-ink-opacity-input = Ugjennomskinleg
+pdfjs-editor-stamp-add-image-button =
+ .title = Legg til bilde
+pdfjs-editor-stamp-add-image-button-label = Legg til bilde
+pdfjs-free-text =
+ .aria-label = Tekstredigering
+pdfjs-free-text-default-content = Byrje å skrive…
+pdfjs-ink =
+ .aria-label = Teikneredigering
+pdfjs-ink-canvas =
+ .aria-label = Brukarskapt bilde
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alt-tekst
+pdfjs-editor-alt-text-edit-button-label = Rediger alt-tekst tekst
+pdfjs-editor-alt-text-dialog-label = Vel eit alternativ
+pdfjs-editor-alt-text-dialog-description = Alt-tekst (alternativ tekst) hjelper når folk ikkje kan sjå bildet eller når det ikkje vert lasta inn.
+pdfjs-editor-alt-text-add-description-label = Legg til ei skildring
+pdfjs-editor-alt-text-add-description-description = Gå etter 1-2 setninger som skildrar emnet, settinga eller handlingane.
+pdfjs-editor-alt-text-mark-decorative-label = Merk som dekorativt
+pdfjs-editor-alt-text-mark-decorative-description = Dette vert brukt til dekorative bilde, som kantlinjer eller vassmerke.
+pdfjs-editor-alt-text-cancel-button = Avbryt
+pdfjs-editor-alt-text-save-button = Lagre
+pdfjs-editor-alt-text-decorative-tooltip = Merkt som dekorativ
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Til dømes, «Ein ung mann set seg ved eit bord for å ete eit måltid»
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+
+## Color picker
+
+pdfjs-editor-colorpicker-yellow =
+ .title = Gul
+pdfjs-editor-colorpicker-green =
+ .title = Grøn
+pdfjs-editor-colorpicker-blue =
+ .title = Blå
+pdfjs-editor-colorpicker-pink =
+ .title = Rosa
+pdfjs-editor-colorpicker-red =
+ .title = Raud
diff --git a/web/locale/oc/viewer.ftl b/web/locale/oc/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..68889798b0e3f2889e97d468961d0b726253adc7
--- /dev/null
+++ b/web/locale/oc/viewer.ftl
@@ -0,0 +1,354 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pagina precedenta
+pdfjs-previous-button-label = Precedent
+pdfjs-next-button =
+ .title = Pagina seguenta
+pdfjs-next-button-label = Seguent
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pagina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = sus { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoom arrièr
+pdfjs-zoom-out-button-label = Zoom arrièr
+pdfjs-zoom-in-button =
+ .title = Zoom avant
+pdfjs-zoom-in-button-label = Zoom avant
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Bascular en mòde presentacion
+pdfjs-presentation-mode-button-label = Mòde Presentacion
+pdfjs-open-file-button =
+ .title = Dobrir lo fichièr
+pdfjs-open-file-button-label = Dobrir
+pdfjs-print-button =
+ .title = Imprimir
+pdfjs-print-button-label = Imprimir
+pdfjs-save-button =
+ .title = Enregistrar
+pdfjs-save-button-label = Enregistrar
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Telecargar
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Telecargar
+pdfjs-bookmark-button =
+ .title = Pagina actuala (mostrar l’adreça de la pagina actuala)
+pdfjs-bookmark-button-label = Pagina actuala
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Dobrir amb l’aplicacion
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Dobrir amb l’aplicacion
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Aisinas
+pdfjs-tools-button-label = Aisinas
+pdfjs-first-page-button =
+ .title = Anar a la primièra pagina
+pdfjs-first-page-button-label = Anar a la primièra pagina
+pdfjs-last-page-button =
+ .title = Anar a la darrièra pagina
+pdfjs-last-page-button-label = Anar a la darrièra pagina
+pdfjs-page-rotate-cw-button =
+ .title = Rotacion orària
+pdfjs-page-rotate-cw-button-label = Rotacion orària
+pdfjs-page-rotate-ccw-button =
+ .title = Rotacion antiorària
+pdfjs-page-rotate-ccw-button-label = Rotacion antiorària
+pdfjs-cursor-text-select-tool-button =
+ .title = Activar l'aisina de seleccion de tèxte
+pdfjs-cursor-text-select-tool-button-label = Aisina de seleccion de tèxte
+pdfjs-cursor-hand-tool-button =
+ .title = Activar l’aisina man
+pdfjs-cursor-hand-tool-button-label = Aisina man
+pdfjs-scroll-page-button =
+ .title = Activar lo defilament per pagina
+pdfjs-scroll-page-button-label = Defilament per pagina
+pdfjs-scroll-vertical-button =
+ .title = Utilizar lo defilament vertical
+pdfjs-scroll-vertical-button-label = Defilament vertical
+pdfjs-scroll-horizontal-button =
+ .title = Utilizar lo defilament orizontal
+pdfjs-scroll-horizontal-button-label = Defilament orizontal
+pdfjs-scroll-wrapped-button =
+ .title = Activar lo defilament continú
+pdfjs-scroll-wrapped-button-label = Defilament continú
+pdfjs-spread-none-button =
+ .title = Agropar pas las paginas doas a doas
+pdfjs-spread-none-button-label = Una sola pagina
+pdfjs-spread-odd-button =
+ .title = Mostrar doas paginas en començant per las paginas imparas a esquèrra
+pdfjs-spread-odd-button-label = Dobla pagina, impara a drecha
+pdfjs-spread-even-button =
+ .title = Mostrar doas paginas en començant per las paginas paras a esquèrra
+pdfjs-spread-even-button-label = Dobla pagina, para a drecha
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Proprietats del document…
+pdfjs-document-properties-button-label = Proprietats del document…
+pdfjs-document-properties-file-name = Nom del fichièr :
+pdfjs-document-properties-file-size = Talha del fichièr :
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } Ko ({ $size_b } octets)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } Mo ({ $size_b } octets)
+pdfjs-document-properties-title = Títol :
+pdfjs-document-properties-author = Autor :
+pdfjs-document-properties-subject = Subjècte :
+pdfjs-document-properties-keywords = Mots claus :
+pdfjs-document-properties-creation-date = Data de creacion :
+pdfjs-document-properties-modification-date = Data de modificacion :
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, a { $time }
+pdfjs-document-properties-creator = Creator :
+pdfjs-document-properties-producer = Aisina de conversion PDF :
+pdfjs-document-properties-version = Version PDF :
+pdfjs-document-properties-page-count = Nombre de paginas :
+pdfjs-document-properties-page-size = Talha de la pagina :
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = retrach
+pdfjs-document-properties-page-size-orientation-landscape = païsatge
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letra
+pdfjs-document-properties-page-size-name-legal = Document juridic
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista web rapida :
+pdfjs-document-properties-linearized-yes = Òc
+pdfjs-document-properties-linearized-no = Non
+pdfjs-document-properties-close-button = Tampar
+
+## Print
+
+pdfjs-print-progress-message = Preparacion del document per l’impression…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Anullar
+pdfjs-printing-not-supported = Atencion : l'impression es pas complètament gerida per aqueste navegador.
+pdfjs-printing-not-ready = Atencion : lo PDF es pas entièrament cargat per lo poder imprimir.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Afichar/amagar lo panèl lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Afichar/amagar lo panèl lateral (lo document conten esquèmas/pèças juntas/calques)
+pdfjs-toggle-sidebar-button-label = Afichar/amagar lo panèl lateral
+pdfjs-document-outline-button =
+ .title = Mostrar los esquèmas del document (dobleclicar per espandre/reduire totes los elements)
+pdfjs-document-outline-button-label = Marcapaginas del document
+pdfjs-attachments-button =
+ .title = Visualizar las pèças juntas
+pdfjs-attachments-button-label = Pèças juntas
+pdfjs-layers-button =
+ .title = Afichar los calques (doble-clicar per reïnicializar totes los calques a l’estat per defaut)
+pdfjs-layers-button-label = Calques
+pdfjs-thumbs-button =
+ .title = Afichar las vinhetas
+pdfjs-thumbs-button-label = Vinhetas
+pdfjs-current-outline-item-button =
+ .title = Trobar l’element de plan actual
+pdfjs-current-outline-item-button-label = Element de plan actual
+pdfjs-findbar-button =
+ .title = Cercar dins lo document
+pdfjs-findbar-button-label = Recercar
+pdfjs-additional-layers = Calques suplementaris
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pagina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Vinheta de la pagina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Recercar
+ .placeholder = Cercar dins lo document…
+pdfjs-find-previous-button =
+ .title = Tròba l'ocurréncia precedenta de la frasa
+pdfjs-find-previous-button-label = Precedent
+pdfjs-find-next-button =
+ .title = Tròba l'ocurréncia venenta de la frasa
+pdfjs-find-next-button-label = Seguent
+pdfjs-find-highlight-checkbox = Suslinhar tot
+pdfjs-find-match-case-checkbox-label = Respectar la cassa
+pdfjs-find-match-diacritics-checkbox-label = Respectar los diacritics
+pdfjs-find-entire-word-checkbox-label = Mots entièrs
+pdfjs-find-reached-top = Naut de la pagina atenh, perseguida del bas
+pdfjs-find-reached-bottom = Bas de la pagina atench, perseguida al començament
+pdfjs-find-not-found = Frasa pas trobada
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Largor plena
+pdfjs-page-scale-fit = Pagina entièra
+pdfjs-page-scale-auto = Zoom automatic
+pdfjs-page-scale-actual = Talha vertadièra
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Pagina { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Una error s'es producha pendent lo cargament del fichièr PDF.
+pdfjs-invalid-file-error = Fichièr PDF invalid o corromput.
+pdfjs-missing-file-error = Fichièr PDF mancant.
+pdfjs-unexpected-response-error = Responsa de servidor imprevista.
+pdfjs-rendering-error = Una error s'es producha pendent l'afichatge de la pagina.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date } a { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotacion { $type }]
+
+## Password
+
+pdfjs-password-label = Picatz lo senhal per dobrir aqueste fichièr PDF.
+pdfjs-password-invalid = Senhal incorrècte. Tornatz ensajar.
+pdfjs-password-ok-button = D'acòrdi
+pdfjs-password-cancel-button = Anullar
+pdfjs-web-fonts-disabled = Las polissas web son desactivadas : impossible d'utilizar las polissas integradas al PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tèxte
+pdfjs-editor-free-text-button-label = Tèxte
+pdfjs-editor-ink-button =
+ .title = Dessenhar
+pdfjs-editor-ink-button-label = Dessenhar
+pdfjs-editor-stamp-button =
+ .title = Apondre o modificar d’imatges
+pdfjs-editor-stamp-button-label = Apondre o modificar d’imatges
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-freetext-button =
+ .title = Suprimir lo tèxte
+pdfjs-editor-remove-stamp-button =
+ .title = Suprimir l’imatge
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Color
+pdfjs-editor-free-text-size-input = Talha
+pdfjs-editor-ink-color-input = Color
+pdfjs-editor-ink-thickness-input = Espessor
+pdfjs-editor-ink-opacity-input = Opacitat
+pdfjs-editor-stamp-add-image-button =
+ .title = Apondre imatge
+pdfjs-editor-stamp-add-image-button-label = Apondre imatge
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Espessor
+pdfjs-free-text =
+ .aria-label = Editor de tèxte
+pdfjs-free-text-default-content = Començatz d’escriure…
+pdfjs-ink =
+ .aria-label = Editor de dessenh
+pdfjs-ink-canvas =
+ .aria-label = Imatge creat per l’utilizaire
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Tèxt alternatiu
+pdfjs-editor-alt-text-edit-button-label = Modificar lo tèxt alternatiu
+pdfjs-editor-alt-text-dialog-label = Causir una opcion
+pdfjs-editor-alt-text-add-description-label = Apondre una descripcion
+pdfjs-editor-alt-text-cancel-button = Anullar
+pdfjs-editor-alt-text-save-button = Enregistrar
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Color de suslinhatge
+pdfjs-editor-colorpicker-button =
+ .title = Cambiar de color
+pdfjs-editor-colorpicker-yellow =
+ .title = Jaune
+pdfjs-editor-colorpicker-green =
+ .title = Verd
+pdfjs-editor-colorpicker-blue =
+ .title = Blau
+pdfjs-editor-colorpicker-pink =
+ .title = Ròse
+pdfjs-editor-colorpicker-red =
+ .title = Roge
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = O afichar tot
+pdfjs-editor-highlight-show-all-button =
+ .title = O afichar tot
diff --git a/web/locale/pa-IN/viewer.ftl b/web/locale/pa-IN/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..eef67d354f204748e7c01d33cd33f5e6379eda87
--- /dev/null
+++ b/web/locale/pa-IN/viewer.ftl
@@ -0,0 +1,396 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = ਪਿਛਲਾ ਸਫ਼ਾ
+pdfjs-previous-button-label = ਪਿੱਛੇ
+pdfjs-next-button =
+ .title = ਅਗਲਾ ਸਫ਼ਾ
+pdfjs-next-button-label = ਅੱਗੇ
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = ਸਫ਼ਾ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } ਵਿੱਚੋਂ
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = { $pagesCount }) ਵਿੱਚੋਂ ({ $pageNumber }
+pdfjs-zoom-out-button =
+ .title = ਜ਼ੂਮ ਆਉਟ
+pdfjs-zoom-out-button-label = ਜ਼ੂਮ ਆਉਟ
+pdfjs-zoom-in-button =
+ .title = ਜ਼ੂਮ ਇਨ
+pdfjs-zoom-in-button-label = ਜ਼ੂਮ ਇਨ
+pdfjs-zoom-select =
+ .title = ਜ਼ੂਨ
+pdfjs-presentation-mode-button =
+ .title = ਪਰਿਜੈਂਟੇਸ਼ਨ ਮੋਡ ਵਿੱਚ ਜਾਓ
+pdfjs-presentation-mode-button-label = ਪਰਿਜੈਂਟੇਸ਼ਨ ਮੋਡ
+pdfjs-open-file-button =
+ .title = ਫਾਈਲ ਨੂੰ ਖੋਲ੍ਹੋ
+pdfjs-open-file-button-label = ਖੋਲ੍ਹੋ
+pdfjs-print-button =
+ .title = ਪਰਿੰਟ
+pdfjs-print-button-label = ਪਰਿੰਟ
+pdfjs-save-button =
+ .title = ਸੰਭਾਲੋ
+pdfjs-save-button-label = ਸੰਭਾਲੋ
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = ਡਾਊਨਲੋਡ
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = ਡਾਊਨਲੋਡ
+pdfjs-bookmark-button =
+ .title = ਮੌਜੂਦਾ ਸਫ਼਼ਾ (ਮੌਜੂਦਾ ਸਫ਼ੇ ਤੋਂ URL ਵੇਖੋ)
+pdfjs-bookmark-button-label = ਮੌਜੂਦਾ ਸਫ਼਼ਾ
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ਟੂਲ
+pdfjs-tools-button-label = ਟੂਲ
+pdfjs-first-page-button =
+ .title = ਪਹਿਲੇ ਸਫ਼ੇ ਉੱਤੇ ਜਾਓ
+pdfjs-first-page-button-label = ਪਹਿਲੇ ਸਫ਼ੇ ਉੱਤੇ ਜਾਓ
+pdfjs-last-page-button =
+ .title = ਆਖਰੀ ਸਫ਼ੇ ਉੱਤੇ ਜਾਓ
+pdfjs-last-page-button-label = ਆਖਰੀ ਸਫ਼ੇ ਉੱਤੇ ਜਾਓ
+pdfjs-page-rotate-cw-button =
+ .title = ਸੱਜੇ ਦਾਅ ਘੁੰਮਾਓ
+pdfjs-page-rotate-cw-button-label = ਸੱਜੇ ਦਾਅ ਘੁੰਮਾਓ
+pdfjs-page-rotate-ccw-button =
+ .title = ਖੱਬੇ ਦਾਅ ਘੁੰਮਾਓ
+pdfjs-page-rotate-ccw-button-label = ਖੱਬੇ ਦਾਅ ਘੁੰਮਾਓ
+pdfjs-cursor-text-select-tool-button =
+ .title = ਲਿਖਤ ਚੋਣ ਟੂਲ ਸਮਰੱਥ ਕਰੋ
+pdfjs-cursor-text-select-tool-button-label = ਲਿਖਤ ਚੋਣ ਟੂਲ
+pdfjs-cursor-hand-tool-button =
+ .title = ਹੱਥ ਟੂਲ ਸਮਰੱਥ ਕਰੋ
+pdfjs-cursor-hand-tool-button-label = ਹੱਥ ਟੂਲ
+pdfjs-scroll-page-button =
+ .title = ਸਫ਼ਾ ਖਿਸਕਾਉਣ ਨੂੰ ਵਰਤੋਂ
+pdfjs-scroll-page-button-label = ਸਫ਼ਾ ਖਿਸਕਾਉਣਾ
+pdfjs-scroll-vertical-button =
+ .title = ਖੜ੍ਹਵੇਂ ਸਕਰਾਉਣ ਨੂੰ ਵਰਤੋਂ
+pdfjs-scroll-vertical-button-label = ਖੜ੍ਹਵਾਂ ਸਰਕਾਉਣਾ
+pdfjs-scroll-horizontal-button =
+ .title = ਲੇਟਵੇਂ ਸਰਕਾਉਣ ਨੂੰ ਵਰਤੋਂ
+pdfjs-scroll-horizontal-button-label = ਲੇਟਵਾਂ ਸਰਕਾਉਣਾ
+pdfjs-scroll-wrapped-button =
+ .title = ਸਮੇਟੇ ਸਰਕਾਉਣ ਨੂੰ ਵਰਤੋਂ
+pdfjs-scroll-wrapped-button-label = ਸਮੇਟਿਆ ਸਰਕਾਉਣਾ
+pdfjs-spread-none-button =
+ .title = ਸਫ਼ਾ ਫੈਲਾਅ ਵਿੱਚ ਸ਼ਾਮਲ ਨਾ ਹੋਵੋ
+pdfjs-spread-none-button-label = ਕੋਈ ਫੈਲਾਅ ਨਹੀਂ
+pdfjs-spread-odd-button =
+ .title = ਟਾਂਕ ਅੰਕ ਵਾਲੇ ਸਫ਼ਿਆਂ ਨਾਲ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲੇ ਸਫਿਆਂ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ
+pdfjs-spread-odd-button-label = ਟਾਂਕ ਫੈਲਾਅ
+pdfjs-spread-even-button =
+ .title = ਜਿਸਤ ਅੰਕ ਵਾਲੇ ਸਫ਼ਿਆਂ ਨਾਲ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲੇ ਸਫਿਆਂ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ
+pdfjs-spread-even-button-label = ਜਿਸਤ ਫੈਲਾਅ
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = …ਦਸਤਾਵੇਜ਼ ਦੀ ਵਿਸ਼ੇਸ਼ਤਾ
+pdfjs-document-properties-button-label = …ਦਸਤਾਵੇਜ਼ ਦੀ ਵਿਸ਼ੇਸ਼ਤਾ
+pdfjs-document-properties-file-name = ਫਾਈਲ ਦਾ ਨਾਂ:
+pdfjs-document-properties-file-size = ਫਾਈਲ ਦਾ ਆਕਾਰ:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } ਬਾਈਟ)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } ਬਾਈਟ)
+pdfjs-document-properties-title = ਟਾਈਟਲ:
+pdfjs-document-properties-author = ਲੇਖਕ:
+pdfjs-document-properties-subject = ਵਿਸ਼ਾ:
+pdfjs-document-properties-keywords = ਸ਼ਬਦ:
+pdfjs-document-properties-creation-date = ਬਣਾਉਣ ਦੀ ਮਿਤੀ:
+pdfjs-document-properties-modification-date = ਸੋਧ ਦੀ ਮਿਤੀ:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = ਨਿਰਮਾਤਾ:
+pdfjs-document-properties-producer = PDF ਪ੍ਰੋਡਿਊਸਰ:
+pdfjs-document-properties-version = PDF ਵਰਜਨ:
+pdfjs-document-properties-page-count = ਸਫ਼ੇ ਦੀ ਗਿਣਤੀ:
+pdfjs-document-properties-page-size = ਸਫ਼ਾ ਆਕਾਰ:
+pdfjs-document-properties-page-size-unit-inches = ਇੰਚ
+pdfjs-document-properties-page-size-unit-millimeters = ਮਿਮੀ
+pdfjs-document-properties-page-size-orientation-portrait = ਪੋਰਟਰੇਟ
+pdfjs-document-properties-page-size-orientation-landscape = ਲੈਂਡਸਕੇਪ
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = ਲੈਟਰ
+pdfjs-document-properties-page-size-name-legal = ਕਨੂੰਨੀ
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = ਤੇਜ਼ ਵੈੱਬ ਝਲਕ:
+pdfjs-document-properties-linearized-yes = ਹਾਂ
+pdfjs-document-properties-linearized-no = ਨਹੀਂ
+pdfjs-document-properties-close-button = ਬੰਦ ਕਰੋ
+
+## Print
+
+pdfjs-print-progress-message = …ਪਰਿੰਟ ਕਰਨ ਲਈ ਦਸਤਾਵੇਜ਼ ਨੂੰ ਤਿਆਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = ਰੱਦ ਕਰੋ
+pdfjs-printing-not-supported = ਸਾਵਧਾਨ: ਇਹ ਬਰਾਊਜ਼ਰ ਪਰਿੰਟ ਕਰਨ ਲਈ ਪੂਰੀ ਤਰ੍ਹਾਂ ਸਹਾਇਕ ਨਹੀਂ ਹੈ।
+pdfjs-printing-not-ready = ਸਾਵਧਾਨ: PDF ਨੂੰ ਪਰਿੰਟ ਕਰਨ ਲਈ ਪੂਰੀ ਤਰ੍ਹਾਂ ਲੋਡ ਨਹੀਂ ਹੈ।
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = ਬਾਹੀ ਬਦਲੋ
+pdfjs-toggle-sidebar-notification-button =
+ .title = ਬਾਹੀ ਨੂੰ ਬਦਲੋ (ਦਸਤਾਵੇਜ਼ ਖਾਕਾ/ਅਟੈਚਮੈਂਟ/ਪਰਤਾਂ ਰੱਖਦਾ ਹੈ)
+pdfjs-toggle-sidebar-button-label = ਬਾਹੀ ਬਦਲੋ
+pdfjs-document-outline-button =
+ .title = ਦਸਤਾਵੇਜ਼ ਖਾਕਾ ਦਿਖਾਓ (ਸਾਰੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਫੈਲਾਉਣ/ਸਮੇਟਣ ਲਈ ਦੋ ਵਾਰ ਕਲਿੱਕ ਕਰੋ)
+pdfjs-document-outline-button-label = ਦਸਤਾਵੇਜ਼ ਖਾਕਾ
+pdfjs-attachments-button =
+ .title = ਅਟੈਚਮੈਂਟ ਵੇਖਾਓ
+pdfjs-attachments-button-label = ਅਟੈਚਮੈਂਟਾਂ
+pdfjs-layers-button =
+ .title = ਪਰਤਾਂ ਵੇਖਾਓ (ਸਾਰੀਆਂ ਪਰਤਾਂ ਨੂੰ ਮੂਲ ਹਾਲਤ ਉੱਤੇ ਮੁੜ-ਸੈੱਟ ਕਰਨ ਲਈ ਦੋ ਵਾਰ ਕਲਿੱਕ ਕਰੋ)
+pdfjs-layers-button-label = ਪਰਤਾਂ
+pdfjs-thumbs-button =
+ .title = ਥੰਮਨੇਲ ਨੂੰ ਵੇਖਾਓ
+pdfjs-thumbs-button-label = ਥੰਮਨੇਲ
+pdfjs-current-outline-item-button =
+ .title = ਮੌੌਜੂਦਾ ਖਾਕਾ ਚੀਜ਼ ਲੱਭੋ
+pdfjs-current-outline-item-button-label = ਮੌਜੂਦਾ ਖਾਕਾ ਚੀਜ਼
+pdfjs-findbar-button =
+ .title = ਦਸਤਾਵੇਜ਼ ਵਿੱਚ ਲੱਭੋ
+pdfjs-findbar-button-label = ਲੱਭੋ
+pdfjs-additional-layers = ਵਾਧੂ ਪਰਤਾਂ
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = ਸਫ਼ਾ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } ਸਫ਼ੇ ਦਾ ਥੰਮਨੇਲ
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = ਲੱਭੋ
+ .placeholder = …ਦਸਤਾਵੇਜ਼ 'ਚ ਲੱਭੋ
+pdfjs-find-previous-button =
+ .title = ਵਾਕ ਦੀ ਪਿਛਲੀ ਮੌਜੂਦਗੀ ਲੱਭੋ
+pdfjs-find-previous-button-label = ਪਿੱਛੇ
+pdfjs-find-next-button =
+ .title = ਵਾਕ ਦੀ ਅਗਲੀ ਮੌਜੂਦਗੀ ਲੱਭੋ
+pdfjs-find-next-button-label = ਅੱਗੇ
+pdfjs-find-highlight-checkbox = ਸਭ ਉਭਾਰੋ
+pdfjs-find-match-case-checkbox-label = ਅੱਖਰ ਆਕਾਰ ਨੂੰ ਮਿਲਾਉ
+pdfjs-find-match-diacritics-checkbox-label = ਭੇਦਸੂਚਕ ਮੇਲ
+pdfjs-find-entire-word-checkbox-label = ਪੂਰੇ ਸ਼ਬਦ
+pdfjs-find-reached-top = ਦਸਤਾਵੇਜ਼ ਦੇ ਉੱਤੇ ਆ ਗਏ ਹਾਂ, ਥੱਲੇ ਤੋਂ ਜਾਰੀ ਰੱਖਿਆ ਹੈ
+pdfjs-find-reached-bottom = ਦਸਤਾਵੇਜ਼ ਦੇ ਅੰਤ ਉੱਤੇ ਆ ਗਏ ਹਾਂ, ਉੱਤੇ ਤੋਂ ਜਾਰੀ ਰੱਖਿਆ ਹੈ
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $total } ਵਿੱਚੋਂ { $current } ਮੇਲ
+ *[other] { $total } ਵਿੱਚੋਂ { $current } ਮੇਲ
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] { $limit } ਤੋਂ ਵੱਧ ਮੇਲ
+ *[other] { $limit } ਤੋਂ ਵੱਧ ਮੇਲ
+ }
+pdfjs-find-not-found = ਵਾਕ ਨਹੀਂ ਲੱਭਿਆ
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = ਸਫ਼ੇ ਦੀ ਚੌੜਾਈ
+pdfjs-page-scale-fit = ਸਫ਼ਾ ਫਿੱਟ
+pdfjs-page-scale-auto = ਆਟੋਮੈਟਿਕ ਜ਼ੂਮ ਕਰੋ
+pdfjs-page-scale-actual = ਆਟੋਮੈਟਿਕ ਆਕਾਰ
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = ਸਫ਼ਾ { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF ਲੋਡ ਕਰਨ ਦੇ ਦੌਰਾਨ ਗਲਤੀ ਆਈ ਹੈ।
+pdfjs-invalid-file-error = ਗਲਤ ਜਾਂ ਨਿਕਾਰਾ PDF ਫਾਈਲ ਹੈ।
+pdfjs-missing-file-error = ਨਾ-ਮੌਜੂਦ PDF ਫਾਈਲ।
+pdfjs-unexpected-response-error = ਅਣਜਾਣ ਸਰਵਰ ਜਵਾਬ।
+pdfjs-rendering-error = ਸਫ਼ਾ ਰੈਡਰ ਕਰਨ ਦੇ ਦੌਰਾਨ ਗਲਤੀ ਆਈ ਹੈ।
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } ਵਿਆਖਿਆ]
+
+## Password
+
+pdfjs-password-label = ਇਹ PDF ਫਾਈਲ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਪਾਸਵਰਡ ਦਿਉ।
+pdfjs-password-invalid = ਗਲਤ ਪਾਸਵਰਡ। ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ।
+pdfjs-password-ok-button = ਠੀਕ ਹੈ
+pdfjs-password-cancel-button = ਰੱਦ ਕਰੋ
+pdfjs-web-fonts-disabled = ਵੈਬ ਫੋਂਟ ਬੰਦ ਹਨ: ਇੰਬੈਡ PDF ਫੋਂਟ ਨੂੰ ਵਰਤਣ ਲਈ ਅਸਮਰੱਥ ਹੈ।
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = ਲਿਖਤ
+pdfjs-editor-free-text-button-label = ਲਿਖਤ
+pdfjs-editor-ink-button =
+ .title = ਵਾਹੋ
+pdfjs-editor-ink-button-label = ਵਾਹੋ
+pdfjs-editor-stamp-button =
+ .title = ਚਿੱਤਰ ਜੋੜੋ ਜਾਂ ਸੋਧੋ
+pdfjs-editor-stamp-button-label = ਚਿੱਤਰ ਜੋੜੋ ਜਾਂ ਸੋਧੋ
+pdfjs-editor-highlight-button =
+ .title = ਹਾਈਲਾਈਟ
+pdfjs-editor-highlight-button-label = ਹਾਈਲਾਈਟ
+pdfjs-highlight-floating-button =
+ .title = ਹਾਈਲਾਈਟ
+pdfjs-highlight-floating-button1 =
+ .title = ਹਾਈਲਾਈਟ
+ .aria-label = ਹਾਈਲਾਈਟ
+pdfjs-highlight-floating-button-label = ਹਾਈਲਾਈਟ
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = ਡਰਾਇੰਗ ਨੂੰ ਹਟਾਓ
+pdfjs-editor-remove-freetext-button =
+ .title = ਲਿਖਤ ਨੂੰ ਹਟਾਓ
+pdfjs-editor-remove-stamp-button =
+ .title = ਚਿੱਤਰ ਨੂੰ ਹਟਾਓ
+pdfjs-editor-remove-highlight-button =
+ .title = ਹਾਈਲਾਈਟ ਨੂੰ ਹਟਾਓ
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = ਰੰਗ
+pdfjs-editor-free-text-size-input = ਆਕਾਰ
+pdfjs-editor-ink-color-input = ਰੰਗ
+pdfjs-editor-ink-thickness-input = ਮੋਟਾਈ
+pdfjs-editor-ink-opacity-input = ਧੁੰਦਲਾਪਨ
+pdfjs-editor-stamp-add-image-button =
+ .title = ਚਿੱਤਰ ਜੋੜੋ
+pdfjs-editor-stamp-add-image-button-label = ਚਿੱਤਰ ਜੋੜੋ
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = ਮੋਟਾਈ
+pdfjs-editor-free-highlight-thickness-title =
+ .title = ਚੀਜ਼ਾਂ ਨੂੰ ਹੋਰ ਲਿਖਤਾਂ ਤੋਂ ਉਘਾੜਨ ਸਮੇਂ ਮੋਟਾਈ ਨੂੰ ਬਦਲੋ
+pdfjs-free-text =
+ .aria-label = ਲਿਖਤ ਐਡੀਟਰ
+pdfjs-free-text-default-content = …ਲਿਖਣਾ ਸ਼ੁਰੂ ਕਰੋ
+pdfjs-ink =
+ .aria-label = ਵਹਾਉਣ ਐਡੀਟਰ
+pdfjs-ink-canvas =
+ .aria-label = ਵਰਤੋਂਕਾਰ ਵਲੋਂ ਬਣਾਇਆ ਚਿੱਤਰ
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = ਬਦਲਵੀਂ ਲਿਖਤ
+pdfjs-editor-alt-text-edit-button-label = ਬਦਲਵੀ ਲਿਖਤ ਨੂੰ ਸੋਧੋ
+pdfjs-editor-alt-text-dialog-label = ਚੋਣ ਕਰੋ
+pdfjs-editor-alt-text-dialog-description = ਚਿੱਤਰ ਨਾ ਦਿੱਸਣ ਜਾਂ ਲੋਡ ਨਾ ਹੋਣ ਦੀ ਹਾਲਤ ਵਿੱਚ Alt ਲਿਖਤ (ਬਦਲਵੀਂ ਲਿਖਤ) ਲੋਕਾਂ ਲਈ ਮਦਦਗਾਰ ਹੁੰਦੀ ਹੈ।
+pdfjs-editor-alt-text-add-description-label = ਵਰਣਨ ਜੋੜੋ
+pdfjs-editor-alt-text-add-description-description = 1-2 ਵਾਕ ਰੱਖੋ, ਜੋ ਕਿ ਵਿਸ਼ੇ, ਸੈਟਿੰਗ ਜਾਂ ਕਾਰਵਾਈਆਂ ਬਾਰੇ ਦਰਸਾਉਂਦੇ ਹੋਣ।
+pdfjs-editor-alt-text-mark-decorative-label = ਸਜਾਵਟ ਵਜੋਂ ਨਿਸ਼ਾਨ ਲਾਇਆ
+pdfjs-editor-alt-text-mark-decorative-description = ਇਸ ਨੂੰ ਸਜਾਵਟੀ ਚਿੱਤਰਾਂ ਲਈ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ ਜਿਵੇਂ ਕਿ ਹਾਸ਼ੀਆ ਜਾਂ ਵਾਟਰਮਾਰਕ ਆਦਿ।
+pdfjs-editor-alt-text-cancel-button = ਰੱਦ ਕਰੋ
+pdfjs-editor-alt-text-save-button = ਸੰਭਾਲੋ
+pdfjs-editor-alt-text-decorative-tooltip = ਸਜਾਵਟ ਵਜੋਂ ਨਿਸ਼ਾਨ ਲਾਓ
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = ਮਿਸਾਲ ਵਜੋਂ, “ਗੱਭਰੂ ਭੋਜਨ ਲੈ ਕੇ ਮੇਜ਼ ਉੱਤੇ ਬੈਠਾ ਹੈ”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = ਉੱਤੇ ਖੱਬਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ
+pdfjs-editor-resizer-label-top-middle = ਉੱਤੇ ਮੱਧ — ਮੁੜ-ਆਕਾਰ ਕਰੋ
+pdfjs-editor-resizer-label-top-right = ਉੱਤੇ ਸੱਜਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ
+pdfjs-editor-resizer-label-middle-right = ਮੱਧ ਸੱਜਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ
+pdfjs-editor-resizer-label-bottom-right = ਹੇਠਾਂ ਸੱਜਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ
+pdfjs-editor-resizer-label-bottom-middle = ਹੇਠਾਂ ਮੱਧ — ਮੁੜ-ਆਕਾਰ ਕਰੋ
+pdfjs-editor-resizer-label-bottom-left = ਹੇਠਾਂ ਖੱਬਾ ਕੋਨਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ
+pdfjs-editor-resizer-label-middle-left = ਮੱਧ ਖੱਬਾ — ਮੁੜ-ਆਕਾਰ ਕਰੋ
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = ਹਾਈਟਲਾਈਟ ਦਾ ਰੰਗ
+pdfjs-editor-colorpicker-button =
+ .title = ਰੰਗ ਨੂੰ ਬਦਲੋ
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = ਰੰਗ ਚੋਣਾਂ
+pdfjs-editor-colorpicker-yellow =
+ .title = ਪੀਲਾ
+pdfjs-editor-colorpicker-green =
+ .title = ਹਰਾ
+pdfjs-editor-colorpicker-blue =
+ .title = ਨੀਲਾ
+pdfjs-editor-colorpicker-pink =
+ .title = ਗੁਲਾਬੀ
+pdfjs-editor-colorpicker-red =
+ .title = ਲਾਲ
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = ਸਭ ਵੇਖੋ
+pdfjs-editor-highlight-show-all-button =
+ .title = ਸਭ ਵੇਖੋ
diff --git a/web/locale/pl/viewer.ftl b/web/locale/pl/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..b34d607444def8d82dd0645bcff99820f4e95d6b
--- /dev/null
+++ b/web/locale/pl/viewer.ftl
@@ -0,0 +1,404 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Poprzednia strona
+pdfjs-previous-button-label = Poprzednia
+pdfjs-next-button =
+ .title = Następna strona
+pdfjs-next-button-label = Następna
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Strona
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = z { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Pomniejsz
+pdfjs-zoom-out-button-label = Pomniejsz
+pdfjs-zoom-in-button =
+ .title = Powiększ
+pdfjs-zoom-in-button-label = Powiększ
+pdfjs-zoom-select =
+ .title = Skala
+pdfjs-presentation-mode-button =
+ .title = Przełącz na tryb prezentacji
+pdfjs-presentation-mode-button-label = Tryb prezentacji
+pdfjs-open-file-button =
+ .title = Otwórz plik
+pdfjs-open-file-button-label = Otwórz
+pdfjs-print-button =
+ .title = Drukuj
+pdfjs-print-button-label = Drukuj
+pdfjs-save-button =
+ .title = Zapisz
+pdfjs-save-button-label = Zapisz
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Pobierz
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Pobierz
+pdfjs-bookmark-button =
+ .title = Bieżąca strona (adres do otwarcia na bieżącej stronie)
+pdfjs-bookmark-button-label = Bieżąca strona
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Otwórz w aplikacji
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Otwórz w aplikacji
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Narzędzia
+pdfjs-tools-button-label = Narzędzia
+pdfjs-first-page-button =
+ .title = Przejdź do pierwszej strony
+pdfjs-first-page-button-label = Przejdź do pierwszej strony
+pdfjs-last-page-button =
+ .title = Przejdź do ostatniej strony
+pdfjs-last-page-button-label = Przejdź do ostatniej strony
+pdfjs-page-rotate-cw-button =
+ .title = Obróć zgodnie z ruchem wskazówek zegara
+pdfjs-page-rotate-cw-button-label = Obróć zgodnie z ruchem wskazówek zegara
+pdfjs-page-rotate-ccw-button =
+ .title = Obróć przeciwnie do ruchu wskazówek zegara
+pdfjs-page-rotate-ccw-button-label = Obróć przeciwnie do ruchu wskazówek zegara
+pdfjs-cursor-text-select-tool-button =
+ .title = Włącz narzędzie zaznaczania tekstu
+pdfjs-cursor-text-select-tool-button-label = Narzędzie zaznaczania tekstu
+pdfjs-cursor-hand-tool-button =
+ .title = Włącz narzędzie rączka
+pdfjs-cursor-hand-tool-button-label = Narzędzie rączka
+pdfjs-scroll-page-button =
+ .title = Przewijaj strony
+pdfjs-scroll-page-button-label = Przewijanie stron
+pdfjs-scroll-vertical-button =
+ .title = Przewijaj dokument w pionie
+pdfjs-scroll-vertical-button-label = Przewijanie pionowe
+pdfjs-scroll-horizontal-button =
+ .title = Przewijaj dokument w poziomie
+pdfjs-scroll-horizontal-button-label = Przewijanie poziome
+pdfjs-scroll-wrapped-button =
+ .title = Strony dokumentu wyświetlaj i przewijaj w kolumnach
+pdfjs-scroll-wrapped-button-label = Widok dwóch stron
+pdfjs-spread-none-button =
+ .title = Nie ustawiaj stron obok siebie
+pdfjs-spread-none-button-label = Brak kolumn
+pdfjs-spread-odd-button =
+ .title = Strony nieparzyste ustawiaj na lewo od parzystych
+pdfjs-spread-odd-button-label = Nieparzyste po lewej
+pdfjs-spread-even-button =
+ .title = Strony parzyste ustawiaj na lewo od nieparzystych
+pdfjs-spread-even-button-label = Parzyste po lewej
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Właściwości dokumentu…
+pdfjs-document-properties-button-label = Właściwości dokumentu…
+pdfjs-document-properties-file-name = Nazwa pliku:
+pdfjs-document-properties-file-size = Rozmiar pliku:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } B)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } B)
+pdfjs-document-properties-title = Tytuł:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Temat:
+pdfjs-document-properties-keywords = Słowa kluczowe:
+pdfjs-document-properties-creation-date = Data utworzenia:
+pdfjs-document-properties-modification-date = Data modyfikacji:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Utworzony przez:
+pdfjs-document-properties-producer = PDF wyprodukowany przez:
+pdfjs-document-properties-version = Wersja PDF:
+pdfjs-document-properties-page-count = Liczba stron:
+pdfjs-document-properties-page-size = Wymiary strony:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = pionowa
+pdfjs-document-properties-page-size-orientation-landscape = pozioma
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = US Letter
+pdfjs-document-properties-page-size-name-legal = US Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width }×{ $height } { $unit } (orientacja { $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width }×{ $height } { $unit } ({ $name }, orientacja { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Szybki podgląd w Internecie:
+pdfjs-document-properties-linearized-yes = tak
+pdfjs-document-properties-linearized-no = nie
+pdfjs-document-properties-close-button = Zamknij
+
+## Print
+
+pdfjs-print-progress-message = Przygotowywanie dokumentu do druku…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Anuluj
+pdfjs-printing-not-supported = Ostrzeżenie: drukowanie nie jest w pełni obsługiwane przez tę przeglądarkę.
+pdfjs-printing-not-ready = Ostrzeżenie: dokument PDF nie jest całkowicie wczytany, więc nie można go wydrukować.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Przełącz panel boczny
+pdfjs-toggle-sidebar-notification-button =
+ .title = Przełącz panel boczny (dokument zawiera konspekt/załączniki/warstwy)
+pdfjs-toggle-sidebar-button-label = Przełącz panel boczny
+pdfjs-document-outline-button =
+ .title = Konspekt dokumentu (podwójne kliknięcie rozwija lub zwija wszystkie pozycje)
+pdfjs-document-outline-button-label = Konspekt dokumentu
+pdfjs-attachments-button =
+ .title = Załączniki
+pdfjs-attachments-button-label = Załączniki
+pdfjs-layers-button =
+ .title = Warstwy (podwójne kliknięcie przywraca wszystkie warstwy do stanu domyślnego)
+pdfjs-layers-button-label = Warstwy
+pdfjs-thumbs-button =
+ .title = Miniatury
+pdfjs-thumbs-button-label = Miniatury
+pdfjs-current-outline-item-button =
+ .title = Znajdź bieżący element konspektu
+pdfjs-current-outline-item-button-label = Bieżący element konspektu
+pdfjs-findbar-button =
+ .title = Znajdź w dokumencie
+pdfjs-findbar-button-label = Znajdź
+pdfjs-additional-layers = Dodatkowe warstwy
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page }. strona
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura { $page }. strony
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Znajdź
+ .placeholder = Znajdź w dokumencie…
+pdfjs-find-previous-button =
+ .title = Znajdź poprzednie wystąpienie tekstu
+pdfjs-find-previous-button-label = Poprzednie
+pdfjs-find-next-button =
+ .title = Znajdź następne wystąpienie tekstu
+pdfjs-find-next-button-label = Następne
+pdfjs-find-highlight-checkbox = Wyróżnianie wszystkich
+pdfjs-find-match-case-checkbox-label = Rozróżnianie wielkości liter
+pdfjs-find-match-diacritics-checkbox-label = Rozróżnianie liter diakrytyzowanych
+pdfjs-find-entire-word-checkbox-label = Całe słowa
+pdfjs-find-reached-top = Początek dokumentu. Wyszukiwanie od końca.
+pdfjs-find-reached-bottom = Koniec dokumentu. Wyszukiwanie od początku.
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current }. z { $total } trafienia
+ [few] { $current }. z { $total } trafień
+ *[many] { $current }. z { $total } trafień
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Więcej niż { $limit } trafienie
+ [few] Więcej niż { $limit } trafienia
+ *[many] Więcej niż { $limit } trafień
+ }
+pdfjs-find-not-found = Nie znaleziono tekstu
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Szerokość strony
+pdfjs-page-scale-fit = Dopasowanie strony
+pdfjs-page-scale-auto = Skala automatyczna
+pdfjs-page-scale-actual = Rozmiar oryginalny
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = { $page }. strona
+
+## Loading indicator messages
+
+pdfjs-loading-error = Podczas wczytywania dokumentu PDF wystąpił błąd.
+pdfjs-invalid-file-error = Nieprawidłowy lub uszkodzony plik PDF.
+pdfjs-missing-file-error = Brak pliku PDF.
+pdfjs-unexpected-response-error = Nieoczekiwana odpowiedź serwera.
+pdfjs-rendering-error = Podczas renderowania strony wystąpił błąd.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Przypis: { $type }]
+
+## Password
+
+pdfjs-password-label = Wprowadź hasło, aby otworzyć ten dokument PDF.
+pdfjs-password-invalid = Nieprawidłowe hasło. Proszę spróbować ponownie.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Anuluj
+pdfjs-web-fonts-disabled = Czcionki sieciowe są wyłączone: nie można użyć osadzonych czcionek PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+pdfjs-editor-ink-button =
+ .title = Rysunek
+pdfjs-editor-ink-button-label = Rysunek
+pdfjs-editor-stamp-button =
+ .title = Dodaj lub edytuj obrazy
+pdfjs-editor-stamp-button-label = Dodaj lub edytuj obrazy
+pdfjs-editor-highlight-button =
+ .title = Wyróżnij
+pdfjs-editor-highlight-button-label = Wyróżnij
+pdfjs-highlight-floating-button =
+ .title = Wyróżnij
+pdfjs-highlight-floating-button1 =
+ .title = Wyróżnij
+ .aria-label = Wyróżnij
+pdfjs-highlight-floating-button-label = Wyróżnij
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Usuń rysunek
+pdfjs-editor-remove-freetext-button =
+ .title = Usuń tekst
+pdfjs-editor-remove-stamp-button =
+ .title = Usuń obraz
+pdfjs-editor-remove-highlight-button =
+ .title = Usuń wyróżnienie
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Kolor
+pdfjs-editor-free-text-size-input = Rozmiar
+pdfjs-editor-ink-color-input = Kolor
+pdfjs-editor-ink-thickness-input = Grubość
+pdfjs-editor-ink-opacity-input = Nieprzezroczystość
+pdfjs-editor-stamp-add-image-button =
+ .title = Dodaj obraz
+pdfjs-editor-stamp-add-image-button-label = Dodaj obraz
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Grubość
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Zmień grubość podczas wyróżniania elementów innych niż tekst
+pdfjs-free-text =
+ .aria-label = Edytor tekstu
+pdfjs-free-text-default-content = Zacznij pisać…
+pdfjs-ink =
+ .aria-label = Edytor rysunku
+pdfjs-ink-canvas =
+ .aria-label = Obraz utworzony przez użytkownika
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Tekst alternatywny
+pdfjs-editor-alt-text-edit-button-label = Edytuj tekst alternatywny
+pdfjs-editor-alt-text-dialog-label = Wybierz opcję
+pdfjs-editor-alt-text-dialog-description = Tekst alternatywny pomaga, kiedy ktoś nie może zobaczyć obrazu lub gdy się nie wczytuje.
+pdfjs-editor-alt-text-add-description-label = Dodaj opis
+pdfjs-editor-alt-text-add-description-description = Staraj się napisać 1-2 zdania opisujące temat, miejsce lub działania.
+pdfjs-editor-alt-text-mark-decorative-label = Oznacz jako dekoracyjne
+pdfjs-editor-alt-text-mark-decorative-description = Używane w przypadku obrazów ozdobnych, takich jak obramowania lub znaki wodne.
+pdfjs-editor-alt-text-cancel-button = Anuluj
+pdfjs-editor-alt-text-save-button = Zapisz
+pdfjs-editor-alt-text-decorative-tooltip = Oznaczone jako dekoracyjne
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Na przykład: „Młody człowiek siada przy stole, aby zjeść posiłek”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Lewy górny róg — zmień rozmiar
+pdfjs-editor-resizer-label-top-middle = Górny środkowy — zmień rozmiar
+pdfjs-editor-resizer-label-top-right = Prawy górny róg — zmień rozmiar
+pdfjs-editor-resizer-label-middle-right = Prawy środkowy — zmień rozmiar
+pdfjs-editor-resizer-label-bottom-right = Prawy dolny róg — zmień rozmiar
+pdfjs-editor-resizer-label-bottom-middle = Dolny środkowy — zmień rozmiar
+pdfjs-editor-resizer-label-bottom-left = Lewy dolny róg — zmień rozmiar
+pdfjs-editor-resizer-label-middle-left = Lewy środkowy — zmień rozmiar
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Kolor wyróżnienia
+pdfjs-editor-colorpicker-button =
+ .title = Zmień kolor
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Wybór kolorów
+pdfjs-editor-colorpicker-yellow =
+ .title = Żółty
+pdfjs-editor-colorpicker-green =
+ .title = Zielony
+pdfjs-editor-colorpicker-blue =
+ .title = Niebieski
+pdfjs-editor-colorpicker-pink =
+ .title = Różowy
+pdfjs-editor-colorpicker-red =
+ .title = Czerwony
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Pokaż wszystkie
+pdfjs-editor-highlight-show-all-button =
+ .title = Pokaż wszystkie
diff --git a/web/locale/pt-BR/viewer.ftl b/web/locale/pt-BR/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..153f0426aeb07b829b83c6d8069213c7b73e87eb
--- /dev/null
+++ b/web/locale/pt-BR/viewer.ftl
@@ -0,0 +1,396 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Página anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Próxima página
+pdfjs-next-button-label = Próxima
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Página
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Reduzir
+pdfjs-zoom-out-button-label = Reduzir
+pdfjs-zoom-in-button =
+ .title = Ampliar
+pdfjs-zoom-in-button-label = Ampliar
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Mudar para o modo de apresentação
+pdfjs-presentation-mode-button-label = Modo de apresentação
+pdfjs-open-file-button =
+ .title = Abrir arquivo
+pdfjs-open-file-button-label = Abrir
+pdfjs-print-button =
+ .title = Imprimir
+pdfjs-print-button-label = Imprimir
+pdfjs-save-button =
+ .title = Salvar
+pdfjs-save-button-label = Salvar
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Baixar
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Baixar
+pdfjs-bookmark-button =
+ .title = Página atual (ver URL da página atual)
+pdfjs-bookmark-button-label = Pagina atual
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Ferramentas
+pdfjs-tools-button-label = Ferramentas
+pdfjs-first-page-button =
+ .title = Ir para a primeira página
+pdfjs-first-page-button-label = Ir para a primeira página
+pdfjs-last-page-button =
+ .title = Ir para a última página
+pdfjs-last-page-button-label = Ir para a última página
+pdfjs-page-rotate-cw-button =
+ .title = Girar no sentido horário
+pdfjs-page-rotate-cw-button-label = Girar no sentido horário
+pdfjs-page-rotate-ccw-button =
+ .title = Girar no sentido anti-horário
+pdfjs-page-rotate-ccw-button-label = Girar no sentido anti-horário
+pdfjs-cursor-text-select-tool-button =
+ .title = Ativar a ferramenta de seleção de texto
+pdfjs-cursor-text-select-tool-button-label = Ferramenta de seleção de texto
+pdfjs-cursor-hand-tool-button =
+ .title = Ativar ferramenta de deslocamento
+pdfjs-cursor-hand-tool-button-label = Ferramenta de deslocamento
+pdfjs-scroll-page-button =
+ .title = Usar rolagem de página
+pdfjs-scroll-page-button-label = Rolagem de página
+pdfjs-scroll-vertical-button =
+ .title = Usar deslocamento vertical
+pdfjs-scroll-vertical-button-label = Deslocamento vertical
+pdfjs-scroll-horizontal-button =
+ .title = Usar deslocamento horizontal
+pdfjs-scroll-horizontal-button-label = Deslocamento horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Usar deslocamento contido
+pdfjs-scroll-wrapped-button-label = Deslocamento contido
+pdfjs-spread-none-button =
+ .title = Não reagrupar páginas
+pdfjs-spread-none-button-label = Não estender
+pdfjs-spread-odd-button =
+ .title = Agrupar páginas começando em páginas com números ímpares
+pdfjs-spread-odd-button-label = Estender ímpares
+pdfjs-spread-even-button =
+ .title = Agrupar páginas começando em páginas com números pares
+pdfjs-spread-even-button-label = Estender pares
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propriedades do documento…
+pdfjs-document-properties-button-label = Propriedades do documento…
+pdfjs-document-properties-file-name = Nome do arquivo:
+pdfjs-document-properties-file-size = Tamanho do arquivo:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Título:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Assunto:
+pdfjs-document-properties-keywords = Palavras-chave:
+pdfjs-document-properties-creation-date = Data da criação:
+pdfjs-document-properties-modification-date = Data da modificação:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Criação:
+pdfjs-document-properties-producer = Criador do PDF:
+pdfjs-document-properties-version = Versão do PDF:
+pdfjs-document-properties-page-count = Número de páginas:
+pdfjs-document-properties-page-size = Tamanho da página:
+pdfjs-document-properties-page-size-unit-inches = pol.
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = retrato
+pdfjs-document-properties-page-size-orientation-landscape = paisagem
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Carta
+pdfjs-document-properties-page-size-name-legal = Jurídico
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Exibição web rápida:
+pdfjs-document-properties-linearized-yes = Sim
+pdfjs-document-properties-linearized-no = Não
+pdfjs-document-properties-close-button = Fechar
+
+## Print
+
+pdfjs-print-progress-message = Preparando documento para impressão…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress } %
+pdfjs-print-progress-close-button = Cancelar
+pdfjs-printing-not-supported = Aviso: a impressão não é totalmente suportada neste navegador.
+pdfjs-printing-not-ready = Aviso: o PDF não está totalmente carregado para impressão.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Exibir/ocultar painel lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Exibir/ocultar painel (documento contém estrutura/anexos/camadas)
+pdfjs-toggle-sidebar-button-label = Exibir/ocultar painel
+pdfjs-document-outline-button =
+ .title = Mostrar estrutura do documento (duplo-clique expande/recolhe todos os itens)
+pdfjs-document-outline-button-label = Estrutura do documento
+pdfjs-attachments-button =
+ .title = Mostrar anexos
+pdfjs-attachments-button-label = Anexos
+pdfjs-layers-button =
+ .title = Mostrar camadas (duplo-clique redefine todas as camadas ao estado predefinido)
+pdfjs-layers-button-label = Camadas
+pdfjs-thumbs-button =
+ .title = Mostrar miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-current-outline-item-button =
+ .title = Encontrar item atual da estrutura
+pdfjs-current-outline-item-button-label = Item atual da estrutura
+pdfjs-findbar-button =
+ .title = Procurar no documento
+pdfjs-findbar-button-label = Procurar
+pdfjs-additional-layers = Camadas adicionais
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Página { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura da página { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Procurar
+ .placeholder = Procurar no documento…
+pdfjs-find-previous-button =
+ .title = Procurar a ocorrência anterior da frase
+pdfjs-find-previous-button-label = Anterior
+pdfjs-find-next-button =
+ .title = Procurar a próxima ocorrência da frase
+pdfjs-find-next-button-label = Próxima
+pdfjs-find-highlight-checkbox = Destacar tudo
+pdfjs-find-match-case-checkbox-label = Diferenciar maiúsculas/minúsculas
+pdfjs-find-match-diacritics-checkbox-label = Considerar acentuação
+pdfjs-find-entire-word-checkbox-label = Palavras completas
+pdfjs-find-reached-top = Início do documento alcançado, continuando do fim
+pdfjs-find-reached-bottom = Fim do documento alcançado, continuando do início
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } de { $total } ocorrência
+ *[other] { $current } de { $total } ocorrências
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Mais de { $limit } ocorrência
+ *[other] Mais de { $limit } ocorrências
+ }
+pdfjs-find-not-found = Não encontrado
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Largura da página
+pdfjs-page-scale-fit = Ajustar à janela
+pdfjs-page-scale-auto = Zoom automático
+pdfjs-page-scale-actual = Tamanho real
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Página { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ocorreu um erro ao carregar o PDF.
+pdfjs-invalid-file-error = Arquivo PDF corrompido ou inválido.
+pdfjs-missing-file-error = Arquivo PDF ausente.
+pdfjs-unexpected-response-error = Resposta inesperada do servidor.
+pdfjs-rendering-error = Ocorreu um erro ao renderizar a página.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotação { $type }]
+
+## Password
+
+pdfjs-password-label = Forneça a senha para abrir este arquivo PDF.
+pdfjs-password-invalid = Senha inválida. Tente novamente.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Cancelar
+pdfjs-web-fonts-disabled = As fontes web estão desativadas: não foi possível usar fontes incorporadas do PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texto
+pdfjs-editor-free-text-button-label = Texto
+pdfjs-editor-ink-button =
+ .title = Desenho
+pdfjs-editor-ink-button-label = Desenho
+pdfjs-editor-stamp-button =
+ .title = Adicionar ou editar imagens
+pdfjs-editor-stamp-button-label = Adicionar ou editar imagens
+pdfjs-editor-highlight-button =
+ .title = Destaque
+pdfjs-editor-highlight-button-label = Destaque
+pdfjs-highlight-floating-button =
+ .title = Destaque
+pdfjs-highlight-floating-button1 =
+ .title = Destaque
+ .aria-label = Destaque
+pdfjs-highlight-floating-button-label = Destaque
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Remover desenho
+pdfjs-editor-remove-freetext-button =
+ .title = Remover texto
+pdfjs-editor-remove-stamp-button =
+ .title = Remover imagem
+pdfjs-editor-remove-highlight-button =
+ .title = Remover destaque
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Cor
+pdfjs-editor-free-text-size-input = Tamanho
+pdfjs-editor-ink-color-input = Cor
+pdfjs-editor-ink-thickness-input = Espessura
+pdfjs-editor-ink-opacity-input = Opacidade
+pdfjs-editor-stamp-add-image-button =
+ .title = Adicionar imagem
+pdfjs-editor-stamp-add-image-button-label = Adicionar imagem
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Espessura
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Mudar espessura ao destacar itens que não são texto
+pdfjs-free-text =
+ .aria-label = Editor de texto
+pdfjs-free-text-default-content = Comece digitando…
+pdfjs-ink =
+ .aria-label = Editor de desenho
+pdfjs-ink-canvas =
+ .aria-label = Imagem criada pelo usuário
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Texto alternativo
+pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo
+pdfjs-editor-alt-text-dialog-label = Escolha uma opção
+pdfjs-editor-alt-text-dialog-description = O texto alternativo ajuda quando uma imagem não aparece ou não é carregada.
+pdfjs-editor-alt-text-add-description-label = Adicionar uma descrição
+pdfjs-editor-alt-text-add-description-description = Procure usar uma ou duas frases que descrevam o assunto, cenário ou ação.
+pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativa
+pdfjs-editor-alt-text-mark-decorative-description = Isto é usado em imagens ornamentais, como bordas ou marcas d'água.
+pdfjs-editor-alt-text-cancel-button = Cancelar
+pdfjs-editor-alt-text-save-button = Salvar
+pdfjs-editor-alt-text-decorative-tooltip = Marcado como decorativa
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Por exemplo, “Um jovem senta-se à mesa para comer uma refeição”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Canto superior esquerdo — redimensionar
+pdfjs-editor-resizer-label-top-middle = No centro do topo — redimensionar
+pdfjs-editor-resizer-label-top-right = Canto superior direito — redimensionar
+pdfjs-editor-resizer-label-middle-right = No meio à direita — redimensionar
+pdfjs-editor-resizer-label-bottom-right = Canto inferior direito — redimensionar
+pdfjs-editor-resizer-label-bottom-middle = No centro da base — redimensionar
+pdfjs-editor-resizer-label-bottom-left = Canto inferior esquerdo — redimensionar
+pdfjs-editor-resizer-label-middle-left = No meio à esquerda — redimensionar
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Cor de destaque
+pdfjs-editor-colorpicker-button =
+ .title = Mudar cor
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Opções de cores
+pdfjs-editor-colorpicker-yellow =
+ .title = Amarelo
+pdfjs-editor-colorpicker-green =
+ .title = Verde
+pdfjs-editor-colorpicker-blue =
+ .title = Azul
+pdfjs-editor-colorpicker-pink =
+ .title = Rosa
+pdfjs-editor-colorpicker-red =
+ .title = Vermelho
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Mostrar todos
+pdfjs-editor-highlight-show-all-button =
+ .title = Mostrar todos
diff --git a/web/locale/pt-PT/viewer.ftl b/web/locale/pt-PT/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..7fd8d37855ac7bf857d32aab253e5e6dda6fc7a0
--- /dev/null
+++ b/web/locale/pt-PT/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Página anterior
+pdfjs-previous-button-label = Anterior
+pdfjs-next-button =
+ .title = Página seguinte
+pdfjs-next-button-label = Seguinte
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Página
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Reduzir
+pdfjs-zoom-out-button-label = Reduzir
+pdfjs-zoom-in-button =
+ .title = Ampliar
+pdfjs-zoom-in-button-label = Ampliar
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Trocar para o modo de apresentação
+pdfjs-presentation-mode-button-label = Modo de apresentação
+pdfjs-open-file-button =
+ .title = Abrir ficheiro
+pdfjs-open-file-button-label = Abrir
+pdfjs-print-button =
+ .title = Imprimir
+pdfjs-print-button-label = Imprimir
+pdfjs-save-button =
+ .title = Guardar
+pdfjs-save-button-label = Guardar
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Transferir
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Transferir
+pdfjs-bookmark-button =
+ .title = Página atual (ver URL da página atual)
+pdfjs-bookmark-button-label = Pagina atual
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Abrir na aplicação
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Abrir na aplicação
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Ferramentas
+pdfjs-tools-button-label = Ferramentas
+pdfjs-first-page-button =
+ .title = Ir para a primeira página
+pdfjs-first-page-button-label = Ir para a primeira página
+pdfjs-last-page-button =
+ .title = Ir para a última página
+pdfjs-last-page-button-label = Ir para a última página
+pdfjs-page-rotate-cw-button =
+ .title = Rodar à direita
+pdfjs-page-rotate-cw-button-label = Rodar à direita
+pdfjs-page-rotate-ccw-button =
+ .title = Rodar à esquerda
+pdfjs-page-rotate-ccw-button-label = Rodar à esquerda
+pdfjs-cursor-text-select-tool-button =
+ .title = Ativar ferramenta de seleção de texto
+pdfjs-cursor-text-select-tool-button-label = Ferramenta de seleção de texto
+pdfjs-cursor-hand-tool-button =
+ .title = Ativar ferramenta de mão
+pdfjs-cursor-hand-tool-button-label = Ferramenta de mão
+pdfjs-scroll-page-button =
+ .title = Utilizar deslocamento da página
+pdfjs-scroll-page-button-label = Deslocamento da página
+pdfjs-scroll-vertical-button =
+ .title = Utilizar deslocação vertical
+pdfjs-scroll-vertical-button-label = Deslocação vertical
+pdfjs-scroll-horizontal-button =
+ .title = Utilizar deslocação horizontal
+pdfjs-scroll-horizontal-button-label = Deslocação horizontal
+pdfjs-scroll-wrapped-button =
+ .title = Utilizar deslocação encapsulada
+pdfjs-scroll-wrapped-button-label = Deslocação encapsulada
+pdfjs-spread-none-button =
+ .title = Não juntar páginas dispersas
+pdfjs-spread-none-button-label = Sem spreads
+pdfjs-spread-odd-button =
+ .title = Juntar páginas dispersas a partir de páginas com números ímpares
+pdfjs-spread-odd-button-label = Spreads ímpares
+pdfjs-spread-even-button =
+ .title = Juntar páginas dispersas a partir de páginas com números pares
+pdfjs-spread-even-button-label = Spreads pares
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propriedades do documento…
+pdfjs-document-properties-button-label = Propriedades do documento…
+pdfjs-document-properties-file-name = Nome do ficheiro:
+pdfjs-document-properties-file-size = Tamanho do ficheiro:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Título:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Assunto:
+pdfjs-document-properties-keywords = Palavras-chave:
+pdfjs-document-properties-creation-date = Data de criação:
+pdfjs-document-properties-modification-date = Data de modificação:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Criador:
+pdfjs-document-properties-producer = Produtor de PDF:
+pdfjs-document-properties-version = Versão do PDF:
+pdfjs-document-properties-page-count = N.º de páginas:
+pdfjs-document-properties-page-size = Tamanho da página:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = retrato
+pdfjs-document-properties-page-size-orientation-landscape = paisagem
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Carta
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista rápida web:
+pdfjs-document-properties-linearized-yes = Sim
+pdfjs-document-properties-linearized-no = Não
+pdfjs-document-properties-close-button = Fechar
+
+## Print
+
+pdfjs-print-progress-message = A preparar o documento para impressão…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cancelar
+pdfjs-printing-not-supported = Aviso: a impressão não é totalmente suportada por este navegador.
+pdfjs-printing-not-ready = Aviso: o PDF ainda não está totalmente carregado.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Alternar barra lateral
+pdfjs-toggle-sidebar-notification-button =
+ .title = Alternar barra lateral (o documento contém contornos/anexos/camadas)
+pdfjs-toggle-sidebar-button-label = Alternar barra lateral
+pdfjs-document-outline-button =
+ .title = Mostrar esquema do documento (duplo clique para expandir/colapsar todos os itens)
+pdfjs-document-outline-button-label = Esquema do documento
+pdfjs-attachments-button =
+ .title = Mostrar anexos
+pdfjs-attachments-button-label = Anexos
+pdfjs-layers-button =
+ .title = Mostrar camadas (clique duas vezes para repor todas as camadas para o estado predefinido)
+pdfjs-layers-button-label = Camadas
+pdfjs-thumbs-button =
+ .title = Mostrar miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-current-outline-item-button =
+ .title = Encontrar o item atualmente destacado
+pdfjs-current-outline-item-button-label = Item atualmente destacado
+pdfjs-findbar-button =
+ .title = Localizar em documento
+pdfjs-findbar-button-label = Localizar
+pdfjs-additional-layers = Camadas adicionais
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Página { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura da página { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Localizar
+ .placeholder = Localizar em documento…
+pdfjs-find-previous-button =
+ .title = Localizar ocorrência anterior da frase
+pdfjs-find-previous-button-label = Anterior
+pdfjs-find-next-button =
+ .title = Localizar ocorrência seguinte da frase
+pdfjs-find-next-button-label = Seguinte
+pdfjs-find-highlight-checkbox = Destacar tudo
+pdfjs-find-match-case-checkbox-label = Correspondência
+pdfjs-find-match-diacritics-checkbox-label = Corresponder diacríticos
+pdfjs-find-entire-word-checkbox-label = Palavras completas
+pdfjs-find-reached-top = Topo do documento atingido, a continuar a partir do fundo
+pdfjs-find-reached-bottom = Fim do documento atingido, a continuar a partir do topo
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } de { $total } correspondência
+ *[other] { $current } de { $total } correspondências
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Mais de { $limit } correspondência
+ *[other] Mais de { $limit } correspondências
+ }
+pdfjs-find-not-found = Frase não encontrada
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Ajustar à largura
+pdfjs-page-scale-fit = Ajustar à página
+pdfjs-page-scale-auto = Zoom automático
+pdfjs-page-scale-actual = Tamanho real
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Página { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ocorreu um erro ao carregar o PDF.
+pdfjs-invalid-file-error = Ficheiro PDF inválido ou danificado.
+pdfjs-missing-file-error = Ficheiro PDF inexistente.
+pdfjs-unexpected-response-error = Resposta inesperada do servidor.
+pdfjs-rendering-error = Ocorreu um erro ao processar a página.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotação { $type }]
+
+## Password
+
+pdfjs-password-label = Introduza a palavra-passe para abrir este ficheiro PDF.
+pdfjs-password-invalid = Palavra-passe inválida. Por favor, tente novamente.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Cancelar
+pdfjs-web-fonts-disabled = Os tipos de letra web estão desativados: não é possível utilizar os tipos de letra PDF embutidos.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Texto
+pdfjs-editor-free-text-button-label = Texto
+pdfjs-editor-ink-button =
+ .title = Desenhar
+pdfjs-editor-ink-button-label = Desenhar
+pdfjs-editor-stamp-button =
+ .title = Adicionar ou editar imagens
+pdfjs-editor-stamp-button-label = Adicionar ou editar imagens
+pdfjs-editor-highlight-button =
+ .title = Destaque
+pdfjs-editor-highlight-button-label = Destaque
+pdfjs-highlight-floating-button =
+ .title = Destaque
+pdfjs-highlight-floating-button1 =
+ .title = Realçar
+ .aria-label = Realçar
+pdfjs-highlight-floating-button-label = Realçar
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Remover desenho
+pdfjs-editor-remove-freetext-button =
+ .title = Remover texto
+pdfjs-editor-remove-stamp-button =
+ .title = Remover imagem
+pdfjs-editor-remove-highlight-button =
+ .title = Remover destaque
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Cor
+pdfjs-editor-free-text-size-input = Tamanho
+pdfjs-editor-ink-color-input = Cor
+pdfjs-editor-ink-thickness-input = Espessura
+pdfjs-editor-ink-opacity-input = Opacidade
+pdfjs-editor-stamp-add-image-button =
+ .title = Adicionar imagem
+pdfjs-editor-stamp-add-image-button-label = Adicionar imagem
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Espessura
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Alterar espessura quando destacar itens que não sejam texto
+pdfjs-free-text =
+ .aria-label = Editor de texto
+pdfjs-free-text-default-content = Começar a digitar…
+pdfjs-ink =
+ .aria-label = Editor de desenho
+pdfjs-ink-canvas =
+ .aria-label = Imagem criada pelo utilizador
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Texto alternativo
+pdfjs-editor-alt-text-edit-button-label = Editar texto alternativo
+pdfjs-editor-alt-text-dialog-label = Escolher uma opção
+pdfjs-editor-alt-text-dialog-description = O texto alternativo (texto alternativo) ajuda quando as pessoas não conseguem ver a imagem ou quando a mesma não é carregada.
+pdfjs-editor-alt-text-add-description-label = Adicionar uma descrição
+pdfjs-editor-alt-text-add-description-description = Aponte para 1-2 frases que descrevam o assunto, definição ou ações.
+pdfjs-editor-alt-text-mark-decorative-label = Marcar como decorativa
+pdfjs-editor-alt-text-mark-decorative-description = Isto é utilizado para imagens decorativas, tais como limites ou marcas d'água.
+pdfjs-editor-alt-text-cancel-button = Cancelar
+pdfjs-editor-alt-text-save-button = Guardar
+pdfjs-editor-alt-text-decorative-tooltip = Marcada como decorativa
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Por exemplo, “Um jovem senta-se à mesa para comer uma refeição”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Canto superior esquerdo — redimensionar
+pdfjs-editor-resizer-label-top-middle = Superior ao centro — redimensionar
+pdfjs-editor-resizer-label-top-right = Canto superior direito — redimensionar
+pdfjs-editor-resizer-label-middle-right = Centro à direita — redimensionar
+pdfjs-editor-resizer-label-bottom-right = Canto inferior direito — redimensionar
+pdfjs-editor-resizer-label-bottom-middle = Inferior ao centro — redimensionar
+pdfjs-editor-resizer-label-bottom-left = Canto inferior esquerdo — redimensionar
+pdfjs-editor-resizer-label-middle-left = Centro à esquerda — redimensionar
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Cor de destaque
+pdfjs-editor-colorpicker-button =
+ .title = Alterar cor
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Escolhas de cor
+pdfjs-editor-colorpicker-yellow =
+ .title = Amarelo
+pdfjs-editor-colorpicker-green =
+ .title = Verde
+pdfjs-editor-colorpicker-blue =
+ .title = Azul
+pdfjs-editor-colorpicker-pink =
+ .title = Rosa
+pdfjs-editor-colorpicker-red =
+ .title = Vermelho
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Mostrar tudo
+pdfjs-editor-highlight-show-all-button =
+ .title = Mostrar tudo
diff --git a/web/locale/rm/viewer.ftl b/web/locale/rm/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..e428e1334aa03261c80dd9ecb9449ad759b59004
--- /dev/null
+++ b/web/locale/rm/viewer.ftl
@@ -0,0 +1,396 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pagina precedenta
+pdfjs-previous-button-label = Enavos
+pdfjs-next-button =
+ .title = Proxima pagina
+pdfjs-next-button-label = Enavant
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pagina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = da { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } da { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Empitschnir
+pdfjs-zoom-out-button-label = Empitschnir
+pdfjs-zoom-in-button =
+ .title = Engrondir
+pdfjs-zoom-in-button-label = Engrondir
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Midar en il modus da preschentaziun
+pdfjs-presentation-mode-button-label = Modus da preschentaziun
+pdfjs-open-file-button =
+ .title = Avrir datoteca
+pdfjs-open-file-button-label = Avrir
+pdfjs-print-button =
+ .title = Stampar
+pdfjs-print-button-label = Stampar
+pdfjs-save-button =
+ .title = Memorisar
+pdfjs-save-button-label = Memorisar
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Telechargiar
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Telechargiar
+pdfjs-bookmark-button =
+ .title = Pagina actuala (mussar l'URL da la pagina actuala)
+pdfjs-bookmark-button-label = Pagina actuala
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Utensils
+pdfjs-tools-button-label = Utensils
+pdfjs-first-page-button =
+ .title = Siglir a l'emprima pagina
+pdfjs-first-page-button-label = Siglir a l'emprima pagina
+pdfjs-last-page-button =
+ .title = Siglir a la davosa pagina
+pdfjs-last-page-button-label = Siglir a la davosa pagina
+pdfjs-page-rotate-cw-button =
+ .title = Rotar en direcziun da l'ura
+pdfjs-page-rotate-cw-button-label = Rotar en direcziun da l'ura
+pdfjs-page-rotate-ccw-button =
+ .title = Rotar en direcziun cuntraria a l'ura
+pdfjs-page-rotate-ccw-button-label = Rotar en direcziun cuntraria a l'ura
+pdfjs-cursor-text-select-tool-button =
+ .title = Activar l'utensil per selecziunar text
+pdfjs-cursor-text-select-tool-button-label = Utensil per selecziunar text
+pdfjs-cursor-hand-tool-button =
+ .title = Activar l'utensil da maun
+pdfjs-cursor-hand-tool-button-label = Utensil da maun
+pdfjs-scroll-page-button =
+ .title = Utilisar la defilada per pagina
+pdfjs-scroll-page-button-label = Defilada per pagina
+pdfjs-scroll-vertical-button =
+ .title = Utilisar il defilar vertical
+pdfjs-scroll-vertical-button-label = Defilar vertical
+pdfjs-scroll-horizontal-button =
+ .title = Utilisar il defilar orizontal
+pdfjs-scroll-horizontal-button-label = Defilar orizontal
+pdfjs-scroll-wrapped-button =
+ .title = Utilisar il defilar en colonnas
+pdfjs-scroll-wrapped-button-label = Defilar en colonnas
+pdfjs-spread-none-button =
+ .title = Betg parallelisar las paginas
+pdfjs-spread-none-button-label = Betg parallel
+pdfjs-spread-odd-button =
+ .title = Parallelisar las paginas cun cumenzar cun paginas spèras
+pdfjs-spread-odd-button-label = Parallel spèr
+pdfjs-spread-even-button =
+ .title = Parallelisar las paginas cun cumenzar cun paginas pèras
+pdfjs-spread-even-button-label = Parallel pèr
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Caracteristicas dal document…
+pdfjs-document-properties-button-label = Caracteristicas dal document…
+pdfjs-document-properties-file-name = Num da la datoteca:
+pdfjs-document-properties-file-size = Grondezza da la datoteca:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Titel:
+pdfjs-document-properties-author = Autur:
+pdfjs-document-properties-subject = Tema:
+pdfjs-document-properties-keywords = Chavazzins:
+pdfjs-document-properties-creation-date = Data da creaziun:
+pdfjs-document-properties-modification-date = Data da modificaziun:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date } { $time }
+pdfjs-document-properties-creator = Creà da:
+pdfjs-document-properties-producer = Creà il PDF cun:
+pdfjs-document-properties-version = Versiun da PDF:
+pdfjs-document-properties-page-count = Dumber da paginas:
+pdfjs-document-properties-page-size = Grondezza da la pagina:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = vertical
+pdfjs-document-properties-page-size-orientation-landscape = orizontal
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Web View:
+pdfjs-document-properties-linearized-yes = Gea
+pdfjs-document-properties-linearized-no = Na
+pdfjs-document-properties-close-button = Serrar
+
+## Print
+
+pdfjs-print-progress-message = Preparar il document per stampar…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Interrumper
+pdfjs-printing-not-supported = Attenziun: Il stampar na funcziunescha anc betg dal tut en quest navigatur.
+pdfjs-printing-not-ready = Attenziun: Il PDF n'è betg chargià cumplettamain per stampar.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Activar/deactivar la trav laterala
+pdfjs-toggle-sidebar-notification-button =
+ .title = Activar/deactivar la trav laterala (il document cuntegna structura dal document/agiuntas/nivels)
+pdfjs-toggle-sidebar-button-label = Activar/deactivar la trav laterala
+pdfjs-document-outline-button =
+ .title = Mussar la structura dal document (cliccar duas giadas per extender/cumprimer tut ils elements)
+pdfjs-document-outline-button-label = Structura dal document
+pdfjs-attachments-button =
+ .title = Mussar agiuntas
+pdfjs-attachments-button-label = Agiuntas
+pdfjs-layers-button =
+ .title = Mussar ils nivels (cliccar dubel per restaurar il stadi da standard da tut ils nivels)
+pdfjs-layers-button-label = Nivels
+pdfjs-thumbs-button =
+ .title = Mussar las miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-current-outline-item-button =
+ .title = Tschertgar l'element da structura actual
+pdfjs-current-outline-item-button-label = Element da structura actual
+pdfjs-findbar-button =
+ .title = Tschertgar en il document
+pdfjs-findbar-button-label = Tschertgar
+pdfjs-additional-layers = Nivels supplementars
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pagina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura da la pagina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Tschertgar
+ .placeholder = Tschertgar en il document…
+pdfjs-find-previous-button =
+ .title = Tschertgar la posiziun precedenta da l'expressiun
+pdfjs-find-previous-button-label = Enavos
+pdfjs-find-next-button =
+ .title = Tschertgar la proxima posiziun da l'expressiun
+pdfjs-find-next-button-label = Enavant
+pdfjs-find-highlight-checkbox = Relevar tuts
+pdfjs-find-match-case-checkbox-label = Resguardar maiusclas/minusclas
+pdfjs-find-match-diacritics-checkbox-label = Resguardar ils segns diacritics
+pdfjs-find-entire-word-checkbox-label = Pleds entirs
+pdfjs-find-reached-top = Il cumenzament dal document è cuntanschì, la tschertga cuntinuescha a la fin dal document
+pdfjs-find-reached-bottom = La fin dal document è cuntanschì, la tschertga cuntinuescha al cumenzament dal document
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } dad { $total } correspundenza
+ *[other] { $current } da { $total } correspundenzas
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Dapli che { $limit } correspundenza
+ *[other] Dapli che { $limit } correspundenzas
+ }
+pdfjs-find-not-found = Impussibel da chattar l'expressiun
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Ladezza da la pagina
+pdfjs-page-scale-fit = Entira pagina
+pdfjs-page-scale-auto = Zoom automatic
+pdfjs-page-scale-actual = Grondezza actuala
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Pagina { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ina errur è cumparida cun chargiar il PDF.
+pdfjs-invalid-file-error = Datoteca PDF nunvalida u donnegiada.
+pdfjs-missing-file-error = Datoteca PDF manconta.
+pdfjs-unexpected-response-error = Resposta nunspetgada dal server.
+pdfjs-rendering-error = Ina errur è cumparida cun visualisar questa pagina.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Annotaziun da { $type }]
+
+## Password
+
+pdfjs-password-label = Endatescha il pled-clav per avrir questa datoteca da PDF.
+pdfjs-password-invalid = Pled-clav nunvalid. Emprova anc ina giada.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Interrumper
+pdfjs-web-fonts-disabled = Scrittiras dal web èn deactivadas: impussibel dad utilisar las scrittiras integradas en il PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Text
+pdfjs-editor-free-text-button-label = Text
+pdfjs-editor-ink-button =
+ .title = Dissegnar
+pdfjs-editor-ink-button-label = Dissegnar
+pdfjs-editor-stamp-button =
+ .title = Agiuntar u modifitgar maletgs
+pdfjs-editor-stamp-button-label = Agiuntar u modifitgar maletgs
+pdfjs-editor-highlight-button =
+ .title = Marcar
+pdfjs-editor-highlight-button-label = Marcar
+pdfjs-highlight-floating-button =
+ .title = Relevar
+pdfjs-highlight-floating-button1 =
+ .title = Marcar
+ .aria-label = Marcar
+pdfjs-highlight-floating-button-label = Marcar
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Allontanar il dissegn
+pdfjs-editor-remove-freetext-button =
+ .title = Allontanar il text
+pdfjs-editor-remove-stamp-button =
+ .title = Allontanar la grafica
+pdfjs-editor-remove-highlight-button =
+ .title = Allontanar l'emfasa
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Colur
+pdfjs-editor-free-text-size-input = Grondezza
+pdfjs-editor-ink-color-input = Colur
+pdfjs-editor-ink-thickness-input = Grossezza
+pdfjs-editor-ink-opacity-input = Opacitad
+pdfjs-editor-stamp-add-image-button =
+ .title = Agiuntar in maletg
+pdfjs-editor-stamp-add-image-button-label = Agiuntar in maletg
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Grossezza
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Midar la grossezza cun relevar elements betg textuals
+pdfjs-free-text =
+ .aria-label = Editur da text
+pdfjs-free-text-default-content = Cumenzar a tippar…
+pdfjs-ink =
+ .aria-label = Editur dissegn
+pdfjs-ink-canvas =
+ .aria-label = Maletg creà da l'utilisader
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Text alternativ
+pdfjs-editor-alt-text-edit-button-label = Modifitgar il text alternativ
+pdfjs-editor-alt-text-dialog-label = Tscherner ina opziun
+pdfjs-editor-alt-text-dialog-description = Il text alternativ (alt text) gida en cas che persunas na vesan betg il maletg u sch'i na reussescha betg d'al chargiar.
+pdfjs-editor-alt-text-add-description-label = Agiuntar ina descripziun
+pdfjs-editor-alt-text-add-description-description = Scriva idealmain 1-2 frasas che descrivan l'object, la situaziun u las acziuns.
+pdfjs-editor-alt-text-mark-decorative-label = Marcar sco decorativ
+pdfjs-editor-alt-text-mark-decorative-description = Quai vegn duvrà per maletgs ornamentals, sco urs u filigranas.
+pdfjs-editor-alt-text-cancel-button = Interrumper
+pdfjs-editor-alt-text-save-button = Memorisar
+pdfjs-editor-alt-text-decorative-tooltip = Marcà sco decorativ
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Per exempel: «In um giuven sesa a maisa per mangiar in past»
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Chantun sura a sanestra — redimensiunar
+pdfjs-editor-resizer-label-top-middle = Sura amez — redimensiunar
+pdfjs-editor-resizer-label-top-right = Chantun sura a dretga — redimensiunar
+pdfjs-editor-resizer-label-middle-right = Da vart dretga amez — redimensiunar
+pdfjs-editor-resizer-label-bottom-right = Chantun sut a dretga — redimensiunar
+pdfjs-editor-resizer-label-bottom-middle = Sutvart amez — redimensiunar
+pdfjs-editor-resizer-label-bottom-left = Chantun sut a sanestra — redimensiunar
+pdfjs-editor-resizer-label-middle-left = Vart sanestra amez — redimensiunar
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Colur per l'emfasa
+pdfjs-editor-colorpicker-button =
+ .title = Midar la colur
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Colurs disponiblas
+pdfjs-editor-colorpicker-yellow =
+ .title = Mellen
+pdfjs-editor-colorpicker-green =
+ .title = Verd
+pdfjs-editor-colorpicker-blue =
+ .title = Blau
+pdfjs-editor-colorpicker-pink =
+ .title = Rosa
+pdfjs-editor-colorpicker-red =
+ .title = Cotschen
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Mussar tut
+pdfjs-editor-highlight-show-all-button =
+ .title = Mussar tut
diff --git a/web/locale/ro/viewer.ftl b/web/locale/ro/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..7c6f0b6a33d7161da483a570dfdabf2d48872f99
--- /dev/null
+++ b/web/locale/ro/viewer.ftl
@@ -0,0 +1,251 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pagina precedentă
+pdfjs-previous-button-label = Înapoi
+pdfjs-next-button =
+ .title = Pagina următoare
+pdfjs-next-button-label = Înainte
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pagina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = din { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } din { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Micșorează
+pdfjs-zoom-out-button-label = Micșorează
+pdfjs-zoom-in-button =
+ .title = Mărește
+pdfjs-zoom-in-button-label = Mărește
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Comută la modul de prezentare
+pdfjs-presentation-mode-button-label = Mod de prezentare
+pdfjs-open-file-button =
+ .title = Deschide un fișier
+pdfjs-open-file-button-label = Deschide
+pdfjs-print-button =
+ .title = Tipărește
+pdfjs-print-button-label = Tipărește
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Instrumente
+pdfjs-tools-button-label = Instrumente
+pdfjs-first-page-button =
+ .title = Mergi la prima pagină
+pdfjs-first-page-button-label = Mergi la prima pagină
+pdfjs-last-page-button =
+ .title = Mergi la ultima pagină
+pdfjs-last-page-button-label = Mergi la ultima pagină
+pdfjs-page-rotate-cw-button =
+ .title = Rotește în sensul acelor de ceas
+pdfjs-page-rotate-cw-button-label = Rotește în sensul acelor de ceas
+pdfjs-page-rotate-ccw-button =
+ .title = Rotește în sens invers al acelor de ceas
+pdfjs-page-rotate-ccw-button-label = Rotește în sens invers al acelor de ceas
+pdfjs-cursor-text-select-tool-button =
+ .title = Activează instrumentul de selecție a textului
+pdfjs-cursor-text-select-tool-button-label = Instrumentul de selecție a textului
+pdfjs-cursor-hand-tool-button =
+ .title = Activează instrumentul mână
+pdfjs-cursor-hand-tool-button-label = Unealta mână
+pdfjs-scroll-vertical-button =
+ .title = Folosește derularea verticală
+pdfjs-scroll-vertical-button-label = Derulare verticală
+pdfjs-scroll-horizontal-button =
+ .title = Folosește derularea orizontală
+pdfjs-scroll-horizontal-button-label = Derulare orizontală
+pdfjs-scroll-wrapped-button =
+ .title = Folosește derularea încadrată
+pdfjs-scroll-wrapped-button-label = Derulare încadrată
+pdfjs-spread-none-button =
+ .title = Nu uni paginile broșate
+pdfjs-spread-none-button-label = Fără pagini broșate
+pdfjs-spread-odd-button =
+ .title = Unește paginile broșate începând cu cele impare
+pdfjs-spread-odd-button-label = Broșare pagini impare
+pdfjs-spread-even-button =
+ .title = Unește paginile broșate începând cu cele pare
+pdfjs-spread-even-button-label = Broșare pagini pare
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Proprietățile documentului…
+pdfjs-document-properties-button-label = Proprietățile documentului…
+pdfjs-document-properties-file-name = Numele fișierului:
+pdfjs-document-properties-file-size = Mărimea fișierului:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } byți)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byți)
+pdfjs-document-properties-title = Titlu:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Subiect:
+pdfjs-document-properties-keywords = Cuvinte cheie:
+pdfjs-document-properties-creation-date = Data creării:
+pdfjs-document-properties-modification-date = Data modificării:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Autor:
+pdfjs-document-properties-producer = Producător PDF:
+pdfjs-document-properties-version = Versiune PDF:
+pdfjs-document-properties-page-count = Număr de pagini:
+pdfjs-document-properties-page-size = Mărimea paginii:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = verticală
+pdfjs-document-properties-page-size-orientation-landscape = orizontală
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Literă
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vizualizare web rapidă:
+pdfjs-document-properties-linearized-yes = Da
+pdfjs-document-properties-linearized-no = Nu
+pdfjs-document-properties-close-button = Închide
+
+## Print
+
+pdfjs-print-progress-message = Se pregătește documentul pentru tipărire…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Renunță
+pdfjs-printing-not-supported = Avertisment: Tipărirea nu este suportată în totalitate de acest browser.
+pdfjs-printing-not-ready = Avertisment: PDF-ul nu este încărcat complet pentru tipărire.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Comută bara laterală
+pdfjs-toggle-sidebar-button-label = Comută bara laterală
+pdfjs-document-outline-button =
+ .title = Afișează schița documentului (dublu-clic pentru a extinde/restrânge toate elementele)
+pdfjs-document-outline-button-label = Schița documentului
+pdfjs-attachments-button =
+ .title = Afișează atașamentele
+pdfjs-attachments-button-label = Atașamente
+pdfjs-thumbs-button =
+ .title = Afișează miniaturi
+pdfjs-thumbs-button-label = Miniaturi
+pdfjs-findbar-button =
+ .title = Caută în document
+pdfjs-findbar-button-label = Caută
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pagina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura paginii { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Caută
+ .placeholder = Caută în document…
+pdfjs-find-previous-button =
+ .title = Mergi la apariția anterioară a textului
+pdfjs-find-previous-button-label = Înapoi
+pdfjs-find-next-button =
+ .title = Mergi la apariția următoare a textului
+pdfjs-find-next-button-label = Înainte
+pdfjs-find-highlight-checkbox = Evidențiază toate aparițiile
+pdfjs-find-match-case-checkbox-label = Ține cont de majuscule și minuscule
+pdfjs-find-entire-word-checkbox-label = Cuvinte întregi
+pdfjs-find-reached-top = Am ajuns la începutul documentului, continuă de la sfârșit
+pdfjs-find-reached-bottom = Am ajuns la sfârșitul documentului, continuă de la început
+pdfjs-find-not-found = Nu s-a găsit textul
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Lățime pagină
+pdfjs-page-scale-fit = Potrivire la pagină
+pdfjs-page-scale-auto = Zoom automat
+pdfjs-page-scale-actual = Mărime reală
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = A intervenit o eroare la încărcarea PDF-ului.
+pdfjs-invalid-file-error = Fișier PDF nevalid sau corupt.
+pdfjs-missing-file-error = Fișier PDF lipsă.
+pdfjs-unexpected-response-error = Răspuns neașteptat de la server.
+pdfjs-rendering-error = A intervenit o eroare la randarea paginii.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Adnotare { $type }]
+
+## Password
+
+pdfjs-password-label = Introdu parola pentru a deschide acest fișier PDF.
+pdfjs-password-invalid = Parolă nevalidă. Te rugăm să încerci din nou.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Renunță
+pdfjs-web-fonts-disabled = Fonturile web sunt dezactivate: nu se pot folosi fonturile PDF încorporate.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ru/viewer.ftl b/web/locale/ru/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..6e3713ce4c0b93fc9a0c810314746b2517841e48
--- /dev/null
+++ b/web/locale/ru/viewer.ftl
@@ -0,0 +1,404 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Предыдущая страница
+pdfjs-previous-button-label = Предыдущая
+pdfjs-next-button =
+ .title = Следующая страница
+pdfjs-next-button-label = Следующая
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Страница
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = из { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } из { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Уменьшить
+pdfjs-zoom-out-button-label = Уменьшить
+pdfjs-zoom-in-button =
+ .title = Увеличить
+pdfjs-zoom-in-button-label = Увеличить
+pdfjs-zoom-select =
+ .title = Масштаб
+pdfjs-presentation-mode-button =
+ .title = Перейти в режим презентации
+pdfjs-presentation-mode-button-label = Режим презентации
+pdfjs-open-file-button =
+ .title = Открыть файл
+pdfjs-open-file-button-label = Открыть
+pdfjs-print-button =
+ .title = Печать
+pdfjs-print-button-label = Печать
+pdfjs-save-button =
+ .title = Сохранить
+pdfjs-save-button-label = Сохранить
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Загрузить
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Загрузить
+pdfjs-bookmark-button =
+ .title = Текущая страница (просмотр URL-адреса с текущей страницы)
+pdfjs-bookmark-button-label = Текущая страница
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Открыть в приложении
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Открыть в программе
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Инструменты
+pdfjs-tools-button-label = Инструменты
+pdfjs-first-page-button =
+ .title = Перейти на первую страницу
+pdfjs-first-page-button-label = Перейти на первую страницу
+pdfjs-last-page-button =
+ .title = Перейти на последнюю страницу
+pdfjs-last-page-button-label = Перейти на последнюю страницу
+pdfjs-page-rotate-cw-button =
+ .title = Повернуть по часовой стрелке
+pdfjs-page-rotate-cw-button-label = Повернуть по часовой стрелке
+pdfjs-page-rotate-ccw-button =
+ .title = Повернуть против часовой стрелки
+pdfjs-page-rotate-ccw-button-label = Повернуть против часовой стрелки
+pdfjs-cursor-text-select-tool-button =
+ .title = Включить Инструмент «Выделение текста»
+pdfjs-cursor-text-select-tool-button-label = Инструмент «Выделение текста»
+pdfjs-cursor-hand-tool-button =
+ .title = Включить Инструмент «Рука»
+pdfjs-cursor-hand-tool-button-label = Инструмент «Рука»
+pdfjs-scroll-page-button =
+ .title = Использовать прокрутку страниц
+pdfjs-scroll-page-button-label = Прокрутка страниц
+pdfjs-scroll-vertical-button =
+ .title = Использовать вертикальную прокрутку
+pdfjs-scroll-vertical-button-label = Вертикальная прокрутка
+pdfjs-scroll-horizontal-button =
+ .title = Использовать горизонтальную прокрутку
+pdfjs-scroll-horizontal-button-label = Горизонтальная прокрутка
+pdfjs-scroll-wrapped-button =
+ .title = Использовать масштабируемую прокрутку
+pdfjs-scroll-wrapped-button-label = Масштабируемая прокрутка
+pdfjs-spread-none-button =
+ .title = Не использовать режим разворотов страниц
+pdfjs-spread-none-button-label = Без разворотов страниц
+pdfjs-spread-odd-button =
+ .title = Развороты начинаются с нечётных номеров страниц
+pdfjs-spread-odd-button-label = Нечётные страницы слева
+pdfjs-spread-even-button =
+ .title = Развороты начинаются с чётных номеров страниц
+pdfjs-spread-even-button-label = Чётные страницы слева
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Свойства документа…
+pdfjs-document-properties-button-label = Свойства документа…
+pdfjs-document-properties-file-name = Имя файла:
+pdfjs-document-properties-file-size = Размер файла:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байт)
+pdfjs-document-properties-title = Заголовок:
+pdfjs-document-properties-author = Автор:
+pdfjs-document-properties-subject = Тема:
+pdfjs-document-properties-keywords = Ключевые слова:
+pdfjs-document-properties-creation-date = Дата создания:
+pdfjs-document-properties-modification-date = Дата изменения:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Приложение:
+pdfjs-document-properties-producer = Производитель PDF:
+pdfjs-document-properties-version = Версия PDF:
+pdfjs-document-properties-page-count = Число страниц:
+pdfjs-document-properties-page-size = Размер страницы:
+pdfjs-document-properties-page-size-unit-inches = дюймов
+pdfjs-document-properties-page-size-unit-millimeters = мм
+pdfjs-document-properties-page-size-orientation-portrait = книжная
+pdfjs-document-properties-page-size-orientation-landscape = альбомная
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Быстрый просмотр в Web:
+pdfjs-document-properties-linearized-yes = Да
+pdfjs-document-properties-linearized-no = Нет
+pdfjs-document-properties-close-button = Закрыть
+
+## Print
+
+pdfjs-print-progress-message = Подготовка документа к печати…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Отмена
+pdfjs-printing-not-supported = Предупреждение: В этом браузере не полностью поддерживается печать.
+pdfjs-printing-not-ready = Предупреждение: PDF не полностью загружен для печати.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Показать/скрыть боковую панель
+pdfjs-toggle-sidebar-notification-button =
+ .title = Показать/скрыть боковую панель (документ имеет содержание/вложения/слои)
+pdfjs-toggle-sidebar-button-label = Показать/скрыть боковую панель
+pdfjs-document-outline-button =
+ .title = Показать содержание документа (двойной щелчок, чтобы развернуть/свернуть все элементы)
+pdfjs-document-outline-button-label = Содержание документа
+pdfjs-attachments-button =
+ .title = Показать вложения
+pdfjs-attachments-button-label = Вложения
+pdfjs-layers-button =
+ .title = Показать слои (дважды щёлкните, чтобы сбросить все слои к состоянию по умолчанию)
+pdfjs-layers-button-label = Слои
+pdfjs-thumbs-button =
+ .title = Показать миниатюры
+pdfjs-thumbs-button-label = Миниатюры
+pdfjs-current-outline-item-button =
+ .title = Найти текущий элемент структуры
+pdfjs-current-outline-item-button-label = Текущий элемент структуры
+pdfjs-findbar-button =
+ .title = Найти в документе
+pdfjs-findbar-button-label = Найти
+pdfjs-additional-layers = Дополнительные слои
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Страница { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Миниатюра страницы { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Найти
+ .placeholder = Найти в документе…
+pdfjs-find-previous-button =
+ .title = Найти предыдущее вхождение фразы в текст
+pdfjs-find-previous-button-label = Назад
+pdfjs-find-next-button =
+ .title = Найти следующее вхождение фразы в текст
+pdfjs-find-next-button-label = Далее
+pdfjs-find-highlight-checkbox = Подсветить все
+pdfjs-find-match-case-checkbox-label = С учётом регистра
+pdfjs-find-match-diacritics-checkbox-label = С учётом диакритических знаков
+pdfjs-find-entire-word-checkbox-label = Слова целиком
+pdfjs-find-reached-top = Достигнут верх документа, продолжено снизу
+pdfjs-find-reached-bottom = Достигнут конец документа, продолжено сверху
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } из { $total } совпадения
+ [few] { $current } из { $total } совпадений
+ *[many] { $current } из { $total } совпадений
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Более { $limit } совпадения
+ [few] Более { $limit } совпадений
+ *[many] Более { $limit } совпадений
+ }
+pdfjs-find-not-found = Фраза не найдена
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = По ширине страницы
+pdfjs-page-scale-fit = По размеру страницы
+pdfjs-page-scale-auto = Автоматически
+pdfjs-page-scale-actual = Реальный размер
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Страница { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = При загрузке PDF произошла ошибка.
+pdfjs-invalid-file-error = Некорректный или повреждённый PDF-файл.
+pdfjs-missing-file-error = PDF-файл отсутствует.
+pdfjs-unexpected-response-error = Неожиданный ответ сервера.
+pdfjs-rendering-error = При создании страницы произошла ошибка.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Аннотация { $type }]
+
+## Password
+
+pdfjs-password-label = Введите пароль, чтобы открыть этот PDF-файл.
+pdfjs-password-invalid = Неверный пароль. Пожалуйста, попробуйте снова.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Отмена
+pdfjs-web-fonts-disabled = Веб-шрифты отключены: не удалось задействовать встроенные PDF-шрифты.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Текст
+pdfjs-editor-free-text-button-label = Текст
+pdfjs-editor-ink-button =
+ .title = Рисовать
+pdfjs-editor-ink-button-label = Рисовать
+pdfjs-editor-stamp-button =
+ .title = Добавить или изменить изображения
+pdfjs-editor-stamp-button-label = Добавить или изменить изображения
+pdfjs-editor-highlight-button =
+ .title = Выделение
+pdfjs-editor-highlight-button-label = Выделение
+pdfjs-highlight-floating-button =
+ .title = Выделение
+pdfjs-highlight-floating-button1 =
+ .title = Выделение
+ .aria-label = Выделение
+pdfjs-highlight-floating-button-label = Выделение
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Удалить рисунок
+pdfjs-editor-remove-freetext-button =
+ .title = Удалить текст
+pdfjs-editor-remove-stamp-button =
+ .title = Удалить изображение
+pdfjs-editor-remove-highlight-button =
+ .title = Удалить выделение
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Цвет
+pdfjs-editor-free-text-size-input = Размер
+pdfjs-editor-ink-color-input = Цвет
+pdfjs-editor-ink-thickness-input = Толщина
+pdfjs-editor-ink-opacity-input = Прозрачность
+pdfjs-editor-stamp-add-image-button =
+ .title = Добавить изображение
+pdfjs-editor-stamp-add-image-button-label = Добавить изображение
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Толщина
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Изменить толщину при выделении элементов, кроме текста
+pdfjs-free-text =
+ .aria-label = Текстовый редактор
+pdfjs-free-text-default-content = Начните вводить…
+pdfjs-ink =
+ .aria-label = Редактор рисования
+pdfjs-ink-canvas =
+ .aria-label = Созданное пользователем изображение
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Альтернативный текст
+pdfjs-editor-alt-text-edit-button-label = Изменить альтернативный текст
+pdfjs-editor-alt-text-dialog-label = Выберите вариант
+pdfjs-editor-alt-text-dialog-description = Альтернативный текст помогает, когда люди не видят изображение или оно не загружается.
+pdfjs-editor-alt-text-add-description-label = Добавить описание
+pdfjs-editor-alt-text-add-description-description = Старайтесь составлять 1–2 предложения, описывающих предмет, обстановку или действия.
+pdfjs-editor-alt-text-mark-decorative-label = Отметить как декоративное
+pdfjs-editor-alt-text-mark-decorative-description = Используется для декоративных изображений, таких как рамки или водяные знаки.
+pdfjs-editor-alt-text-cancel-button = Отменить
+pdfjs-editor-alt-text-save-button = Сохранить
+pdfjs-editor-alt-text-decorative-tooltip = Помечен как декоративный
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Например: «Молодой человек садится за стол, чтобы поесть»
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Левый верхний угол — изменить размер
+pdfjs-editor-resizer-label-top-middle = Вверху посередине — изменить размер
+pdfjs-editor-resizer-label-top-right = Верхний правый угол — изменить размер
+pdfjs-editor-resizer-label-middle-right = В центре справа — изменить размер
+pdfjs-editor-resizer-label-bottom-right = Нижний правый угол — изменить размер
+pdfjs-editor-resizer-label-bottom-middle = Внизу посередине — изменить размер
+pdfjs-editor-resizer-label-bottom-left = Нижний левый угол — изменить размер
+pdfjs-editor-resizer-label-middle-left = В центре слева — изменить размер
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Цвет выделения
+pdfjs-editor-colorpicker-button =
+ .title = Изменить цвет
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Выбор цвета
+pdfjs-editor-colorpicker-yellow =
+ .title = Жёлтый
+pdfjs-editor-colorpicker-green =
+ .title = Зелёный
+pdfjs-editor-colorpicker-blue =
+ .title = Синий
+pdfjs-editor-colorpicker-pink =
+ .title = Розовый
+pdfjs-editor-colorpicker-red =
+ .title = Красный
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Показать все
+pdfjs-editor-highlight-show-all-button =
+ .title = Показать все
diff --git a/web/locale/sat/viewer.ftl b/web/locale/sat/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..90f12a314268fbd9309494245836ed7fc7669c3f
--- /dev/null
+++ b/web/locale/sat/viewer.ftl
@@ -0,0 +1,311 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = ᱢᱟᱲᱟᱝ ᱥᱟᱦᱴᱟ
+pdfjs-previous-button-label = ᱢᱟᱲᱟᱝᱟᱜ
+pdfjs-next-button =
+ .title = ᱤᱱᱟᱹ ᱛᱟᱭᱚᱢ ᱥᱟᱦᱴᱟ
+pdfjs-next-button-label = ᱤᱱᱟᱹ ᱛᱟᱭᱚᱢ
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = ᱥᱟᱦᱴᱟ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = ᱨᱮᱭᱟᱜ { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } ᱠᱷᱚᱱ { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = ᱦᱤᱲᱤᱧ ᱛᱮᱭᱟᱨ
+pdfjs-zoom-out-button-label = ᱦᱤᱲᱤᱧ ᱛᱮᱭᱟᱨ
+pdfjs-zoom-in-button =
+ .title = ᱢᱟᱨᱟᱝ ᱛᱮᱭᱟᱨ
+pdfjs-zoom-in-button-label = ᱢᱟᱨᱟᱝ ᱛᱮᱭᱟᱨ
+pdfjs-zoom-select =
+ .title = ᱡᱩᱢ
+pdfjs-presentation-mode-button =
+ .title = ᱩᱫᱩᱜ ᱥᱚᱫᱚᱨ ᱚᱵᱚᱥᱛᱟ ᱨᱮ ᱚᱛᱟᱭ ᱢᱮ
+pdfjs-presentation-mode-button-label = ᱩᱫᱩᱜ ᱥᱚᱫᱚᱨ ᱚᱵᱚᱥᱛᱟ ᱨᱮ
+pdfjs-open-file-button =
+ .title = ᱨᱮᱫ ᱡᱷᱤᱡᱽ ᱢᱮ
+pdfjs-open-file-button-label = ᱡᱷᱤᱡᱽ ᱢᱮ
+pdfjs-print-button =
+ .title = ᱪᱷᱟᱯᱟ
+pdfjs-print-button-label = ᱪᱷᱟᱯᱟ
+pdfjs-save-button =
+ .title = ᱥᱟᱺᱪᱟᱣ ᱢᱮ
+pdfjs-save-button-label = ᱥᱟᱺᱪᱟᱣ ᱢᱮ
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = ᱰᱟᱣᱩᱱᱞᱚᱰ
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = ᱰᱟᱣᱩᱱᱞᱚᱰ
+pdfjs-bookmark-button =
+ .title = ᱱᱤᱛᱚᱜᱟᱜ ᱥᱟᱦᱴᱟ (ᱱᱤᱛᱚᱜᱟᱜ ᱥᱟᱦᱴᱟ ᱠᱷᱚᱱ URL ᱫᱮᱠᱷᱟᱣ ᱢᱮ)
+pdfjs-bookmark-button-label = ᱱᱤᱛᱚᱜᱟᱜ ᱥᱟᱦᱴᱟ
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = ᱮᱯ ᱨᱮ ᱡᱷᱤᱡᱽ ᱢᱮ
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = ᱮᱯ ᱨᱮ ᱡᱷᱤᱡᱽ ᱢᱮ
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = ᱦᱟᱹᱛᱤᱭᱟᱹᱨ ᱠᱚ
+pdfjs-tools-button-label = ᱦᱟᱹᱛᱤᱭᱟᱹᱨ ᱠᱚ
+pdfjs-first-page-button =
+ .title = ᱯᱩᱭᱞᱩ ᱥᱟᱦᱴᱟ ᱥᱮᱫ ᱪᱟᱞᱟᱜ ᱢᱮ
+pdfjs-first-page-button-label = ᱯᱩᱭᱞᱩ ᱥᱟᱦᱴᱟ ᱥᱮᱫ ᱪᱟᱞᱟᱜ ᱢᱮ
+pdfjs-last-page-button =
+ .title = ᱢᱩᱪᱟᱹᱫ ᱥᱟᱦᱴᱟ ᱥᱮᱫ ᱪᱟᱞᱟᱜ ᱢᱮ
+pdfjs-last-page-button-label = ᱢᱩᱪᱟᱹᱫ ᱥᱟᱦᱴᱟ ᱥᱮᱫ ᱪᱟᱞᱟᱜ ᱢᱮ
+pdfjs-page-rotate-cw-button =
+ .title = ᱜᱷᱚᱰᱤ ᱦᱤᱥᱟᱹᱵ ᱛᱮ ᱟᱹᱪᱩᱨ
+pdfjs-page-rotate-cw-button-label = ᱜᱷᱚᱰᱤ ᱦᱤᱥᱟᱹᱵ ᱛᱮ ᱟᱹᱪᱩᱨ
+pdfjs-page-rotate-ccw-button =
+ .title = ᱜᱷᱚᱰᱤ ᱦᱤᱥᱟᱹᱵ ᱛᱮ ᱩᱞᱴᱟᱹ ᱟᱹᱪᱩᱨ
+pdfjs-page-rotate-ccw-button-label = ᱜᱷᱚᱰᱤ ᱦᱤᱥᱟᱹᱵ ᱛᱮ ᱩᱞᱴᱟᱹ ᱟᱹᱪᱩᱨ
+pdfjs-cursor-text-select-tool-button =
+ .title = ᱚᱞ ᱵᱟᱪᱷᱟᱣ ᱦᱟᱹᱛᱤᱭᱟᱨ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ
+pdfjs-cursor-text-select-tool-button-label = ᱚᱞ ᱵᱟᱪᱷᱟᱣ ᱦᱟᱹᱛᱤᱭᱟᱨ
+pdfjs-cursor-hand-tool-button =
+ .title = ᱛᱤ ᱦᱟᱹᱛᱤᱭᱟᱨ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ
+pdfjs-cursor-hand-tool-button-label = ᱛᱤ ᱦᱟᱹᱛᱤᱭᱟᱨ
+pdfjs-scroll-page-button =
+ .title = ᱥᱟᱦᱴᱟ ᱜᱩᱲᱟᱹᱣ ᱵᱮᱵᱷᱟᱨ ᱢᱮ
+pdfjs-scroll-page-button-label = ᱥᱟᱦᱴᱟ ᱜᱩᱲᱟᱹᱣ
+pdfjs-scroll-vertical-button =
+ .title = ᱥᱤᱫᱽ ᱜᱩᱲᱟᱹᱣ ᱵᱮᱵᱷᱟᱨ ᱢᱮ
+pdfjs-scroll-vertical-button-label = ᱥᱤᱫᱽ ᱜᱩᱲᱟᱹᱣ
+pdfjs-scroll-horizontal-button =
+ .title = ᱜᱤᱛᱤᱡ ᱛᱮ ᱜᱩᱲᱟᱹᱣ ᱵᱮᱵᱷᱟᱨ ᱢᱮ
+pdfjs-scroll-horizontal-button-label = ᱜᱤᱛᱤᱡ ᱛᱮ ᱜᱩᱲᱟᱹᱣ
+pdfjs-scroll-wrapped-button =
+ .title = ᱞᱤᱯᱴᱟᱹᱣ ᱜᱩᱰᱨᱟᱹᱣ ᱵᱮᱵᱷᱟᱨ ᱢᱮ
+pdfjs-scroll-wrapped-button-label = ᱞᱤᱯᱴᱟᱣ ᱜᱩᱰᱨᱟᱹᱣ
+pdfjs-spread-none-button =
+ .title = ᱟᱞᱚᱢ ᱡᱚᱲᱟᱣ ᱟ ᱥᱟᱦᱴᱟ ᱫᱚ ᱯᱟᱥᱱᱟᱣᱜᱼᱟ
+pdfjs-spread-none-button-label = ᱯᱟᱥᱱᱟᱣ ᱵᱟᱹᱱᱩᱜᱼᱟ
+pdfjs-spread-odd-button =
+ .title = ᱥᱟᱦᱴᱟ ᱯᱟᱥᱱᱟᱣ ᱡᱚᱲᱟᱣ ᱢᱮ ᱡᱟᱦᱟᱸ ᱫᱚ ᱚᱰᱼᱮᱞ ᱥᱟᱦᱴᱟᱠᱚ ᱥᱟᱞᱟᱜ ᱮᱛᱦᱚᱵᱚᱜ ᱠᱟᱱᱟ
+pdfjs-spread-odd-button-label = ᱚᱰ ᱯᱟᱥᱱᱟᱣ
+pdfjs-spread-even-button =
+ .title = ᱥᱟᱦᱴᱟ ᱯᱟᱥᱱᱟᱣ ᱡᱚᱲᱟᱣ ᱢᱮ ᱡᱟᱦᱟᱸ ᱫᱚ ᱤᱣᱮᱱᱼᱮᱞ ᱥᱟᱦᱴᱟᱠᱚ ᱥᱟᱞᱟᱜ ᱮᱛᱦᱚᱵᱚᱜ ᱠᱟᱱᱟ
+pdfjs-spread-even-button-label = ᱯᱟᱥᱱᱟᱣ ᱤᱣᱮᱱ
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = ᱫᱚᱞᱤᱞ ᱜᱩᱱᱠᱚ …
+pdfjs-document-properties-button-label = ᱫᱚᱞᱤᱞ ᱜᱩᱱᱠᱚ …
+pdfjs-document-properties-file-name = ᱨᱮᱫᱽ ᱧᱩᱛᱩᱢ :
+pdfjs-document-properties-file-size = ᱨᱮᱫᱽ ᱢᱟᱯ :
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } ᱵᱟᱭᱤᱴ ᱠᱚ)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } ᱵᱟᱭᱤᱴ ᱠᱚ)
+pdfjs-document-properties-title = ᱧᱩᱛᱩᱢ :
+pdfjs-document-properties-author = ᱚᱱᱚᱞᱤᱭᱟᱹ :
+pdfjs-document-properties-subject = ᱵᱤᱥᱚᱭ :
+pdfjs-document-properties-keywords = ᱠᱟᱹᱴᱷᱤ ᱥᱟᱵᱟᱫᱽ :
+pdfjs-document-properties-creation-date = ᱛᱮᱭᱟᱨ ᱢᱟᱸᱦᱤᱛ :
+pdfjs-document-properties-modification-date = ᱵᱚᱫᱚᱞ ᱦᱚᱪᱚ ᱢᱟᱹᱦᱤᱛ :
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = ᱵᱮᱱᱟᱣᱤᱡ :
+pdfjs-document-properties-producer = PDF ᱛᱮᱭᱟᱨ ᱚᱰᱚᱠᱤᱡ :
+pdfjs-document-properties-version = PDF ᱵᱷᱟᱹᱨᱥᱚᱱ :
+pdfjs-document-properties-page-count = ᱥᱟᱦᱴᱟ ᱞᱮᱠᱷᱟ :
+pdfjs-document-properties-page-size = ᱥᱟᱦᱴᱟ ᱢᱟᱯ :
+pdfjs-document-properties-page-size-unit-inches = ᱤᱧᱪ
+pdfjs-document-properties-page-size-unit-millimeters = ᱢᱤᱢᱤ
+pdfjs-document-properties-page-size-orientation-portrait = ᱯᱚᱴᱨᱮᱴ
+pdfjs-document-properties-page-size-orientation-landscape = ᱞᱮᱱᱰᱥᱠᱮᱯ
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = ᱪᱤᱴᱷᱤ
+pdfjs-document-properties-page-size-name-legal = ᱠᱟᱹᱱᱩᱱᱤ
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = ᱞᱚᱜᱚᱱ ᱣᱮᱵᱽ ᱧᱮᱞ :
+pdfjs-document-properties-linearized-yes = ᱦᱚᱭ
+pdfjs-document-properties-linearized-no = ᱵᱟᱝ
+pdfjs-document-properties-close-button = ᱵᱚᱸᱫᱚᱭ ᱢᱮ
+
+## Print
+
+pdfjs-print-progress-message = ᱪᱷᱟᱯᱟ ᱞᱟᱹᱜᱤᱫ ᱫᱚᱞᱤᱞ ᱛᱮᱭᱟᱨᱚᱜ ᱠᱟᱱᱟ …
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = ᱵᱟᱹᱰᱨᱟᱹ
+pdfjs-printing-not-supported = ᱦᱚᱥᱤᱭᱟᱨ : ᱪᱷᱟᱯᱟ ᱱᱚᱣᱟ ᱯᱟᱱᱛᱮᱭᱟᱜ ᱫᱟᱨᱟᱭ ᱛᱮ ᱯᱩᱨᱟᱹᱣ ᱵᱟᱭ ᱜᱚᱲᱚᱣᱟᱠᱟᱱᱟ ᱾
+pdfjs-printing-not-ready = ᱦᱩᱥᱤᱭᱟᱹᱨ : ᱪᱷᱟᱯᱟ ᱞᱟᱹᱜᱤᱫ PDF ᱯᱩᱨᱟᱹ ᱵᱟᱭ ᱞᱟᱫᱮ ᱟᱠᱟᱱᱟ ᱾
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = ᱫᱷᱟᱨᱮᱵᱟᱨ ᱥᱮᱫ ᱩᱪᱟᱹᱲᱚᱜ ᱢᱮ
+pdfjs-toggle-sidebar-notification-button =
+ .title = ᱫᱷᱟᱨᱮᱵᱟᱨ ᱥᱮᱫ ᱩᱪᱟᱹᱲᱚᱜ ᱢᱮ (ᱫᱚᱞᱤᱞ ᱨᱮ ᱟᱣᱴᱞᱟᱭᱤᱢ ᱢᱮᱱᱟᱜᱼᱟ/ᱞᱟᱪᱷᱟᱠᱚ/ᱯᱚᱨᱚᱛᱠᱚ)
+pdfjs-toggle-sidebar-button-label = ᱫᱷᱟᱨᱮᱵᱟᱨ ᱥᱮᱫ ᱩᱪᱟᱹᱲᱚᱜ ᱢᱮ
+pdfjs-document-outline-button =
+ .title = ᱫᱚᱞᱚᱞ ᱟᱣᱴᱞᱟᱭᱤᱱ ᱫᱮᱠᱷᱟᱣ ᱢᱮ (ᱡᱷᱚᱛᱚ ᱡᱤᱱᱤᱥᱠᱚ ᱵᱟᱨ ᱡᱮᱠᱷᱟ ᱚᱛᱟ ᱠᱮᱛᱮ ᱡᱷᱟᱹᱞ/ᱦᱩᱰᱤᱧ ᱪᱷᱚᱭ ᱢᱮ)
+pdfjs-document-outline-button-label = ᱫᱚᱞᱤᱞ ᱛᱮᱭᱟᱨ ᱛᱮᱫ
+pdfjs-attachments-button =
+ .title = ᱞᱟᱴᱷᱟ ᱥᱮᱞᱮᱫ ᱠᱚ ᱩᱫᱩᱜᱽ ᱢᱮ
+pdfjs-attachments-button-label = ᱞᱟᱴᱷᱟ ᱥᱮᱞᱮᱫ ᱠᱚ
+pdfjs-layers-button =
+ .title = ᱯᱚᱨᱚᱛ ᱫᱮᱠᱷᱟᱣ ᱢᱮ (ᱢᱩᱞ ᱡᱟᱭᱜᱟ ᱛᱮ ᱡᱷᱚᱛᱚ ᱯᱚᱨᱚᱛᱠᱚ ᱨᱤᱥᱮᱴ ᱞᱟᱹᱜᱤᱫ ᱵᱟᱨ ᱡᱮᱠᱷᱟ ᱚᱛᱚᱭ ᱢᱮ)
+pdfjs-layers-button-label = ᱯᱚᱨᱚᱛᱠᱚ
+pdfjs-thumbs-button =
+ .title = ᱪᱤᱛᱟᱹᱨ ᱟᱦᱞᱟ ᱠᱚ ᱩᱫᱩᱜᱽ ᱢᱮ
+pdfjs-thumbs-button-label = ᱪᱤᱛᱟᱹᱨ ᱟᱦᱞᱟ ᱠᱚ
+pdfjs-current-outline-item-button =
+ .title = ᱱᱤᱛᱚᱜᱟᱜ ᱟᱣᱴᱞᱟᱭᱤᱱ ᱡᱟᱱᱤᱥ ᱯᱟᱱᱛᱮ ᱢᱮ
+pdfjs-current-outline-item-button-label = ᱱᱤᱛᱚᱜᱟᱜ ᱟᱣᱴᱞᱟᱭᱤᱱ ᱡᱟᱱᱤᱥ
+pdfjs-findbar-button =
+ .title = ᱫᱚᱞᱤᱞ ᱨᱮ ᱯᱟᱱᱛᱮ
+pdfjs-findbar-button-label = ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ
+pdfjs-additional-layers = ᱵᱟᱹᱲᱛᱤ ᱯᱚᱨᱚᱛᱠᱚ
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page } ᱥᱟᱦᱴᱟ
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } ᱥᱟᱦᱴᱟ ᱨᱮᱭᱟᱜ ᱪᱤᱛᱟᱹᱨ ᱟᱦᱞᱟ
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ
+ .placeholder = ᱫᱚᱞᱤᱞ ᱨᱮ ᱯᱟᱱᱛᱮ ᱢᱮ …
+pdfjs-find-previous-button =
+ .title = ᱟᱭᱟᱛ ᱦᱤᱸᱥ ᱨᱮᱭᱟᱜ ᱯᱟᱹᱦᱤᱞ ᱥᱮᱫᱟᱜ ᱚᱰᱚᱠ ᱧᱟᱢ ᱢᱮ
+pdfjs-find-previous-button-label = ᱢᱟᱲᱟᱝᱟᱜ
+pdfjs-find-next-button =
+ .title = ᱟᱭᱟᱛ ᱦᱤᱸᱥ ᱨᱮᱭᱟᱜ ᱤᱱᱟᱹ ᱛᱟᱭᱚᱢ ᱚᱰᱚᱠ ᱧᱟᱢ ᱢᱮ
+pdfjs-find-next-button-label = ᱤᱱᱟᱹ ᱛᱟᱭᱚᱢ
+pdfjs-find-highlight-checkbox = ᱡᱷᱚᱛᱚ ᱩᱫᱩᱜ ᱨᱟᱠᱟᱵ
+pdfjs-find-match-case-checkbox-label = ᱡᱚᱲ ᱠᱟᱛᱷᱟ
+pdfjs-find-match-diacritics-checkbox-label = ᱵᱤᱥᱮᱥᱚᱠ ᱠᱚ ᱢᱮᱲᱟᱣ ᱢᱮ
+pdfjs-find-entire-word-checkbox-label = ᱡᱷᱚᱛᱚ ᱟᱹᱲᱟᱹᱠᱚ
+pdfjs-find-reached-top = ᱫᱚᱞᱤᱞ ᱨᱮᱭᱟᱜ ᱪᱤᱴ ᱨᱮ ᱥᱮᱴᱮᱨ, ᱞᱟᱛᱟᱨ ᱠᱷᱚᱱ ᱞᱮᱛᱟᱲ
+pdfjs-find-reached-bottom = ᱫᱚᱞᱤᱞ ᱨᱮᱭᱟᱜ ᱢᱩᱪᱟᱹᱫ ᱨᱮ ᱥᱮᱴᱮᱨ, ᱪᱚᱴ ᱠᱷᱚᱱ ᱞᱮᱛᱟᱲ
+pdfjs-find-not-found = ᱛᱚᱯᱚᱞ ᱫᱚᱱᱚᱲ ᱵᱟᱝ ᱧᱟᱢ ᱞᱮᱱᱟ
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = ᱥᱟᱦᱴᱟ ᱚᱥᱟᱨ
+pdfjs-page-scale-fit = ᱥᱟᱦᱴᱟ ᱠᱷᱟᱯ
+pdfjs-page-scale-auto = ᱟᱡᱼᱟᱡ ᱛᱮ ᱦᱩᱰᱤᱧ ᱞᱟᱹᱴᱩ ᱛᱮᱭᱟᱨ
+pdfjs-page-scale-actual = ᱴᱷᱤᱠ ᱢᱟᱨᱟᱝ ᱛᱮᱫ
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = { $page } ᱥᱟᱦᱴᱟ
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF ᱞᱟᱫᱮ ᱡᱚᱦᱚᱜ ᱢᱤᱫ ᱵᱷᱩᱞ ᱦᱩᱭ ᱮᱱᱟ ᱾
+pdfjs-invalid-file-error = ᱵᱟᱝ ᱵᱟᱛᱟᱣ ᱟᱨᱵᱟᱝᱠᱷᱟᱱ ᱰᱤᱜᱟᱹᱣ PDF ᱨᱮᱫᱽ ᱾
+pdfjs-missing-file-error = ᱟᱫᱟᱜ PDF ᱨᱮᱫᱽ ᱾
+pdfjs-unexpected-response-error = ᱵᱟᱝᱵᱩᱡᱷ ᱥᱚᱨᱵᱷᱚᱨ ᱛᱮᱞᱟ ᱾
+pdfjs-rendering-error = ᱥᱟᱦᱴᱟ ᱮᱢ ᱡᱚᱦᱚᱠ ᱢᱤᱫ ᱵᱷᱩᱞ ᱦᱩᱭ ᱮᱱᱟ ᱾
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } ᱢᱚᱱᱛᱚ ᱮᱢ]
+
+## Password
+
+pdfjs-password-label = ᱱᱚᱶᱟ PDF ᱨᱮᱫᱽ ᱡᱷᱤᱡᱽ ᱞᱟᱹᱜᱤᱫ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱟᱫᱮᱨ ᱢᱮ ᱾
+pdfjs-password-invalid = ᱵᱷᱩᱞ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱾ ᱫᱟᱭᱟᱠᱟᱛᱮ ᱫᱩᱦᱲᱟᱹ ᱪᱮᱥᱴᱟᱭ ᱢᱮ ᱾
+pdfjs-password-ok-button = ᱴᱷᱤᱠ
+pdfjs-password-cancel-button = ᱵᱟᱹᱰᱨᱟᱹ
+pdfjs-web-fonts-disabled = ᱣᱮᱵᱽ ᱪᱤᱠᱤ ᱵᱟᱝ ᱦᱩᱭ ᱦᱚᱪᱚ ᱠᱟᱱᱟ : ᱵᱷᱤᱛᱤᱨ ᱛᱷᱟᱯᱚᱱ PDF ᱪᱤᱠᱤ ᱵᱮᱵᱷᱟᱨ ᱵᱟᱝ ᱦᱩᱭ ᱠᱮᱭᱟ ᱾
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = ᱚᱞ
+pdfjs-editor-free-text-button-label = ᱚᱞ
+pdfjs-editor-ink-button =
+ .title = ᱛᱮᱭᱟᱨ
+pdfjs-editor-ink-button-label = ᱛᱮᱭᱟᱨ
+pdfjs-editor-stamp-button =
+ .title = ᱪᱤᱛᱟᱹᱨᱠᱚ ᱥᱮᱞᱮᱫ ᱥᱮ ᱥᱟᱯᱲᱟᱣ ᱢᱮ
+pdfjs-editor-stamp-button-label = ᱪᱤᱛᱟᱹᱨᱠᱚ ᱥᱮᱞᱮᱫ ᱥᱮ ᱥᱟᱯᱲᱟᱣ ᱢᱮ
+# Editor Parameters
+pdfjs-editor-free-text-color-input = ᱨᱚᱝ
+pdfjs-editor-free-text-size-input = ᱢᱟᱯ
+pdfjs-editor-ink-color-input = ᱨᱚᱝ
+pdfjs-editor-ink-thickness-input = ᱢᱚᱴᱟ
+pdfjs-editor-ink-opacity-input = ᱟᱨᱯᱟᱨ
+pdfjs-editor-stamp-add-image-button =
+ .title = ᱪᱤᱛᱟᱹᱨ ᱥᱮᱞᱮᱫ ᱢᱮ
+pdfjs-editor-stamp-add-image-button-label = ᱪᱤᱛᱟᱹᱨ ᱥᱮᱞᱮᱫ ᱢᱮ
+pdfjs-free-text =
+ .aria-label = ᱚᱞ ᱥᱟᱯᱲᱟᱣᱤᱭᱟᱹ
+pdfjs-free-text-default-content = ᱚᱞ ᱮᱛᱦᱚᱵ ᱢᱮ …
+pdfjs-ink =
+ .aria-label = ᱛᱮᱭᱟᱨ ᱥᱟᱯᱲᱟᱣᱤᱭᱟᱹ
+pdfjs-ink-canvas =
+ .aria-label = ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱛᱮᱭᱟᱨ ᱠᱟᱫ ᱪᱤᱛᱟᱹᱨ
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/sc/viewer.ftl b/web/locale/sc/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..a51943c90ec316f62ec700ebd881e563fd918f8d
--- /dev/null
+++ b/web/locale/sc/viewer.ftl
@@ -0,0 +1,290 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Pàgina anteriore
+pdfjs-previous-button-label = S'ischeda chi b'est primu
+pdfjs-next-button =
+ .title = Pàgina imbeniente
+pdfjs-next-button-label = Imbeniente
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Pàgina
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = de { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } de { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Impitica
+pdfjs-zoom-out-button-label = Impitica
+pdfjs-zoom-in-button =
+ .title = Ismànnia
+pdfjs-zoom-in-button-label = Ismànnia
+pdfjs-zoom-select =
+ .title = Ismànnia
+pdfjs-presentation-mode-button =
+ .title = Cola a sa modalidade de presentatzione
+pdfjs-presentation-mode-button-label = Modalidade de presentatzione
+pdfjs-open-file-button =
+ .title = Aberi s'archìviu
+pdfjs-open-file-button-label = Abertu
+pdfjs-print-button =
+ .title = Imprenta
+pdfjs-print-button-label = Imprenta
+pdfjs-save-button =
+ .title = Sarva
+pdfjs-save-button-label = Sarva
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Iscàrriga
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Iscàrriga
+pdfjs-bookmark-button =
+ .title = Pàgina atuale (ammustra s’URL de sa pàgina atuale)
+pdfjs-bookmark-button-label = Pàgina atuale
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Aberi in un’aplicatzione
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Aberi in un’aplicatzione
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Istrumentos
+pdfjs-tools-button-label = Istrumentos
+pdfjs-first-page-button =
+ .title = Bae a sa prima pàgina
+pdfjs-first-page-button-label = Bae a sa prima pàgina
+pdfjs-last-page-button =
+ .title = Bae a s'ùrtima pàgina
+pdfjs-last-page-button-label = Bae a s'ùrtima pàgina
+pdfjs-page-rotate-cw-button =
+ .title = Gira in sensu oràriu
+pdfjs-page-rotate-cw-button-label = Gira in sensu oràriu
+pdfjs-page-rotate-ccw-button =
+ .title = Gira in sensu anti-oràriu
+pdfjs-page-rotate-ccw-button-label = Gira in sensu anti-oràriu
+pdfjs-cursor-text-select-tool-button =
+ .title = Ativa s'aina de seletzione de testu
+pdfjs-cursor-text-select-tool-button-label = Aina de seletzione de testu
+pdfjs-cursor-hand-tool-button =
+ .title = Ativa s'aina de manu
+pdfjs-cursor-hand-tool-button-label = Aina de manu
+pdfjs-scroll-page-button =
+ .title = Imprea s'iscurrimentu de pàgina
+pdfjs-scroll-page-button-label = Iscurrimentu de pàgina
+pdfjs-scroll-vertical-button =
+ .title = Imprea s'iscurrimentu verticale
+pdfjs-scroll-vertical-button-label = Iscurrimentu verticale
+pdfjs-scroll-horizontal-button =
+ .title = Imprea s'iscurrimentu orizontale
+pdfjs-scroll-horizontal-button-label = Iscurrimentu orizontale
+pdfjs-scroll-wrapped-button =
+ .title = Imprea s'iscurrimentu continu
+pdfjs-scroll-wrapped-button-label = Iscurrimentu continu
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Propiedades de su documentu…
+pdfjs-document-properties-button-label = Propiedades de su documentu…
+pdfjs-document-properties-file-name = Nòmine de s'archìviu:
+pdfjs-document-properties-file-size = Mannària de s'archìviu:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Tìtulu:
+pdfjs-document-properties-author = Autoria:
+pdfjs-document-properties-subject = Ogetu:
+pdfjs-document-properties-keywords = Faeddos crae:
+pdfjs-document-properties-creation-date = Data de creatzione:
+pdfjs-document-properties-modification-date = Data de modìfica:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Creatzione:
+pdfjs-document-properties-producer = Produtore de PDF:
+pdfjs-document-properties-version = Versione de PDF:
+pdfjs-document-properties-page-count = Contu de pàginas:
+pdfjs-document-properties-page-size = Mannària de sa pàgina:
+pdfjs-document-properties-page-size-unit-inches = pòddighes
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = verticale
+pdfjs-document-properties-page-size-orientation-landscape = orizontale
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Lìtera
+pdfjs-document-properties-page-size-name-legal = Legale
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Visualizatzione web lestra:
+pdfjs-document-properties-linearized-yes = Eja
+pdfjs-document-properties-linearized-no = Nono
+pdfjs-document-properties-close-button = Serra
+
+## Print
+
+pdfjs-print-progress-message = Aparitzende s'imprenta de su documentu…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Cantzella
+pdfjs-printing-not-supported = Atentzione: s'imprenta no est funtzionende de su totu in custu navigadore.
+pdfjs-printing-not-ready = Atentzione: su PDF no est istadu carrigadu de su totu pro s'imprenta.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Ativa/disativa sa barra laterale
+pdfjs-toggle-sidebar-notification-button =
+ .title = Ativa/disativa sa barra laterale (su documentu cuntenet un'ischema, alligongiados o livellos)
+pdfjs-toggle-sidebar-button-label = Ativa/disativa sa barra laterale
+pdfjs-document-outline-button-label = Ischema de su documentu
+pdfjs-attachments-button =
+ .title = Ammustra alligongiados
+pdfjs-attachments-button-label = Alliongiados
+pdfjs-layers-button =
+ .title = Ammustra livellos (clic dòpiu pro ripristinare totu is livellos a s'istadu predefinidu)
+pdfjs-layers-button-label = Livellos
+pdfjs-thumbs-button =
+ .title = Ammustra miniaturas
+pdfjs-thumbs-button-label = Miniaturas
+pdfjs-current-outline-item-button =
+ .title = Agata s'elementu atuale de s'ischema
+pdfjs-current-outline-item-button-label = Elementu atuale de s'ischema
+pdfjs-findbar-button =
+ .title = Agata in su documentu
+pdfjs-findbar-button-label = Agata
+pdfjs-additional-layers = Livellos additzionales
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Pàgina { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura de sa pàgina { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Agata
+ .placeholder = Agata in su documentu…
+pdfjs-find-previous-button =
+ .title = Agata s'ocurrèntzia pretzedente de sa fràsia
+pdfjs-find-previous-button-label = S'ischeda chi b'est primu
+pdfjs-find-next-button =
+ .title = Agata s'ocurrèntzia imbeniente de sa fràsia
+pdfjs-find-next-button-label = Imbeniente
+pdfjs-find-highlight-checkbox = Evidèntzia totu
+pdfjs-find-match-case-checkbox-label = Distinghe intre majùsculas e minùsculas
+pdfjs-find-match-diacritics-checkbox-label = Respeta is diacrìticos
+pdfjs-find-entire-word-checkbox-label = Faeddos intreos
+pdfjs-find-reached-top = S'est lòmpidu a su cumintzu de su documentu, si sighit dae su bàsciu
+pdfjs-find-reached-bottom = Acabbu de su documentu, si sighit dae s'artu
+pdfjs-find-not-found = Testu no agatadu
+
+## Predefined zoom values
+
+pdfjs-page-scale-auto = Ingrandimentu automàticu
+pdfjs-page-scale-actual = Mannària reale
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Pàgina { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Faddina in sa càrriga de su PDF.
+pdfjs-invalid-file-error = Archìviu PDF non vàlidu o corrùmpidu.
+pdfjs-missing-file-error = Ammancat s'archìviu PDF.
+pdfjs-unexpected-response-error = Risposta imprevista de su serbidore.
+pdfjs-rendering-error = Faddina in sa visualizatzione de sa pàgina.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+
+## Password
+
+pdfjs-password-label = Inserta sa crae pro abèrrere custu archìviu PDF.
+pdfjs-password-invalid = Sa crae no est curreta. Torra a nche proare.
+pdfjs-password-ok-button = Andat bene
+pdfjs-password-cancel-button = Cantzella
+pdfjs-web-fonts-disabled = Is tipografias web sunt disativadas: is tipografias incrustadas a su PDF non podent èssere impreadas.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Testu
+pdfjs-editor-free-text-button-label = Testu
+pdfjs-editor-ink-button =
+ .title = Disinnu
+pdfjs-editor-ink-button-label = Disinnu
+pdfjs-editor-stamp-button =
+ .title = Agiunghe o modìfica immàgines
+pdfjs-editor-stamp-button-label = Agiunghe o modìfica immàgines
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Colore
+pdfjs-editor-free-text-size-input = Mannària
+pdfjs-editor-ink-color-input = Colore
+pdfjs-editor-ink-thickness-input = Grussària
+pdfjs-editor-stamp-add-image-button =
+ .title = Agiunghe un’immàgine
+pdfjs-editor-stamp-add-image-button-label = Agiunghe un’immàgine
+pdfjs-free-text =
+ .aria-label = Editore de testu
+pdfjs-free-text-default-content = Cumintza a iscrìere…
+pdfjs-ink =
+ .aria-label = Editore de disinnos
+pdfjs-ink-canvas =
+ .aria-label = Immàgine creada dae s’utente
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/scn/viewer.ftl b/web/locale/scn/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..a3c7c038c0927951dcf4d615fa249db3235339c3
--- /dev/null
+++ b/web/locale/scn/viewer.ftl
@@ -0,0 +1,74 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-zoom-out-button =
+ .title = Cchiù nicu
+pdfjs-zoom-out-button-label = Cchiù nicu
+pdfjs-zoom-in-button =
+ .title = Cchiù granni
+pdfjs-zoom-in-button-label = Cchiù granni
+
+## Secondary toolbar and context menu
+
+
+## Document properties dialog
+
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Vista web lesta:
+pdfjs-document-properties-linearized-yes = Se
+
+## Print
+
+pdfjs-print-progress-close-button = Sfai
+
+## Tooltips and alt text for side panel toolbar buttons
+
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+
+## Find panel button title and messages
+
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Larghizza dâ pàggina
+
+## PDF page
+
+
+## Loading indicator messages
+
+
+## Annotations
+
+
+## Password
+
+pdfjs-password-cancel-button = Sfai
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/sco/viewer.ftl b/web/locale/sco/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..6f71c47a3ff91b2b3b36ee57adcc78fd867fcc93
--- /dev/null
+++ b/web/locale/sco/viewer.ftl
@@ -0,0 +1,264 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Page Afore
+pdfjs-previous-button-label = Previous
+pdfjs-next-button =
+ .title = Page Efter
+pdfjs-next-button-label = Neist
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Page
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = o { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } o { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zoom Oot
+pdfjs-zoom-out-button-label = Zoom Oot
+pdfjs-zoom-in-button =
+ .title = Zoom In
+pdfjs-zoom-in-button-label = Zoom In
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Flit tae Presentation Mode
+pdfjs-presentation-mode-button-label = Presentation Mode
+pdfjs-open-file-button =
+ .title = Open File
+pdfjs-open-file-button-label = Open
+pdfjs-print-button =
+ .title = Prent
+pdfjs-print-button-label = Prent
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Tools
+pdfjs-tools-button-label = Tools
+pdfjs-first-page-button =
+ .title = Gang tae First Page
+pdfjs-first-page-button-label = Gang tae First Page
+pdfjs-last-page-button =
+ .title = Gang tae Lest Page
+pdfjs-last-page-button-label = Gang tae Lest Page
+pdfjs-page-rotate-cw-button =
+ .title = Rotate Clockwise
+pdfjs-page-rotate-cw-button-label = Rotate Clockwise
+pdfjs-page-rotate-ccw-button =
+ .title = Rotate Coonterclockwise
+pdfjs-page-rotate-ccw-button-label = Rotate Coonterclockwise
+pdfjs-cursor-text-select-tool-button =
+ .title = Enable Text Walin Tool
+pdfjs-cursor-text-select-tool-button-label = Text Walin Tool
+pdfjs-cursor-hand-tool-button =
+ .title = Enable Haun Tool
+pdfjs-cursor-hand-tool-button-label = Haun Tool
+pdfjs-scroll-vertical-button =
+ .title = Yaise Vertical Scrollin
+pdfjs-scroll-vertical-button-label = Vertical Scrollin
+pdfjs-scroll-horizontal-button =
+ .title = Yaise Horizontal Scrollin
+pdfjs-scroll-horizontal-button-label = Horizontal Scrollin
+pdfjs-scroll-wrapped-button =
+ .title = Yaise Wrapped Scrollin
+pdfjs-scroll-wrapped-button-label = Wrapped Scrollin
+pdfjs-spread-none-button =
+ .title = Dinnae jyn page spreids
+pdfjs-spread-none-button-label = Nae Spreids
+pdfjs-spread-odd-button =
+ .title = Jyn page spreids stertin wi odd-numbered pages
+pdfjs-spread-odd-button-label = Odd Spreids
+pdfjs-spread-even-button =
+ .title = Jyn page spreids stertin wi even-numbered pages
+pdfjs-spread-even-button-label = Even Spreids
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Document Properties…
+pdfjs-document-properties-button-label = Document Properties…
+pdfjs-document-properties-file-name = File nemme:
+pdfjs-document-properties-file-size = File size:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = Title:
+pdfjs-document-properties-author = Author:
+pdfjs-document-properties-subject = Subjeck:
+pdfjs-document-properties-keywords = Keywirds:
+pdfjs-document-properties-creation-date = Date o Makkin:
+pdfjs-document-properties-modification-date = Date o Chynges:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Makker:
+pdfjs-document-properties-producer = PDF Producer:
+pdfjs-document-properties-version = PDF Version:
+pdfjs-document-properties-page-count = Page Coont:
+pdfjs-document-properties-page-size = Page Size:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portrait
+pdfjs-document-properties-page-size-orientation-landscape = landscape
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Fast Wab View:
+pdfjs-document-properties-linearized-yes = Aye
+pdfjs-document-properties-linearized-no = Naw
+pdfjs-document-properties-close-button = Sneck
+
+## Print
+
+pdfjs-print-progress-message = Reddin document fur prentin…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Stap
+pdfjs-printing-not-supported = Tak tent: Prentin isnae richt supportit by this stravaiger.
+pdfjs-printing-not-ready = Tak tent: The PDF isnae richt loadit fur prentin.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Toggle Sidebaur
+pdfjs-toggle-sidebar-notification-button =
+ .title = Toggle Sidebaur (document conteens ootline/attachments/layers)
+pdfjs-toggle-sidebar-button-label = Toggle Sidebaur
+pdfjs-document-outline-button =
+ .title = Kythe Document Ootline (double-click fur tae oot-fauld/in-fauld aw items)
+pdfjs-document-outline-button-label = Document Ootline
+pdfjs-attachments-button =
+ .title = Kythe Attachments
+pdfjs-attachments-button-label = Attachments
+pdfjs-layers-button =
+ .title = Kythe Layers (double-click fur tae reset aw layers tae the staunart state)
+pdfjs-layers-button-label = Layers
+pdfjs-thumbs-button =
+ .title = Kythe Thumbnails
+pdfjs-thumbs-button-label = Thumbnails
+pdfjs-current-outline-item-button =
+ .title = Find Current Ootline Item
+pdfjs-current-outline-item-button-label = Current Ootline Item
+pdfjs-findbar-button =
+ .title = Find in Document
+pdfjs-findbar-button-label = Find
+pdfjs-additional-layers = Mair Layers
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Page { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Thumbnail o Page { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Find
+ .placeholder = Find in document…
+pdfjs-find-previous-button =
+ .title = Airt oot the last time this phrase occurred
+pdfjs-find-previous-button-label = Previous
+pdfjs-find-next-button =
+ .title = Airt oot the neist time this phrase occurs
+pdfjs-find-next-button-label = Neist
+pdfjs-find-highlight-checkbox = Highlicht aw
+pdfjs-find-match-case-checkbox-label = Match case
+pdfjs-find-entire-word-checkbox-label = Hale Wirds
+pdfjs-find-reached-top = Raxed tap o document, went on fae the dowp end
+pdfjs-find-reached-bottom = Raxed end o document, went on fae the tap
+pdfjs-find-not-found = Phrase no fund
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Page Width
+pdfjs-page-scale-fit = Page Fit
+pdfjs-page-scale-auto = Automatic Zoom
+pdfjs-page-scale-actual = Actual Size
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Page { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = An mishanter tuik place while loadin the PDF.
+pdfjs-invalid-file-error = No suithfest or camshauchlet PDF file.
+pdfjs-missing-file-error = PDF file tint.
+pdfjs-unexpected-response-error = Unexpectit server repone.
+pdfjs-rendering-error = A mishanter tuik place while renderin the page.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } Annotation]
+
+## Password
+
+pdfjs-password-label = Inpit the passwird fur tae open this PDF file.
+pdfjs-password-invalid = Passwird no suithfest. Gonnae gie it anither shot.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Stap
+pdfjs-web-fonts-disabled = Wab fonts are disabled: cannae yaise embeddit PDF fonts.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/si/viewer.ftl b/web/locale/si/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..283872989487940d0b8d6271c296054413a89ad4
--- /dev/null
+++ b/web/locale/si/viewer.ftl
@@ -0,0 +1,253 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = කලින් පිටුව
+pdfjs-previous-button-label = කලින්
+pdfjs-next-button =
+ .title = ඊළඟ පිටුව
+pdfjs-next-button-label = ඊළඟ
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = පිටුව
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } / { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = කුඩාලනය
+pdfjs-zoom-out-button-label = කුඩාලනය
+pdfjs-zoom-in-button =
+ .title = විශාලනය
+pdfjs-zoom-in-button-label = විශාලනය
+pdfjs-zoom-select =
+ .title = විශාල කරන්න
+pdfjs-presentation-mode-button =
+ .title = සමර්පණ ප්රකාරය වෙත මාරුවන්න
+pdfjs-presentation-mode-button-label = සමර්පණ ප්රකාරය
+pdfjs-open-file-button =
+ .title = ගොනුව අරින්න
+pdfjs-open-file-button-label = අරින්න
+pdfjs-print-button =
+ .title = මුද්රණය
+pdfjs-print-button-label = මුද්රණය
+pdfjs-save-button =
+ .title = සුරකින්න
+pdfjs-save-button-label = සුරකින්න
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = බාගන්න
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = බාගන්න
+pdfjs-bookmark-button-label = පවතින පිටුව
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = යෙදුමෙහි අරින්න
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = යෙදුමෙහි අරින්න
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = මෙවලම්
+pdfjs-tools-button-label = මෙවලම්
+pdfjs-first-page-button =
+ .title = මුල් පිටුවට යන්න
+pdfjs-first-page-button-label = මුල් පිටුවට යන්න
+pdfjs-last-page-button =
+ .title = අවසන් පිටුවට යන්න
+pdfjs-last-page-button-label = අවසන් පිටුවට යන්න
+pdfjs-cursor-text-select-tool-button =
+ .title = පෙළ තේරීමේ මෙවලම සබල කරන්න
+pdfjs-cursor-text-select-tool-button-label = පෙළ තේරීමේ මෙවලම
+pdfjs-cursor-hand-tool-button =
+ .title = අත් මෙවලම සබල කරන්න
+pdfjs-cursor-hand-tool-button-label = අත් මෙවලම
+pdfjs-scroll-page-button =
+ .title = පිටුව අනුචලනය භාවිතය
+pdfjs-scroll-page-button-label = පිටුව අනුචලනය
+pdfjs-scroll-vertical-button =
+ .title = සිරස් අනුචලනය භාවිතය
+pdfjs-scroll-vertical-button-label = සිරස් අනුචලනය
+pdfjs-scroll-horizontal-button =
+ .title = තිරස් අනුචලනය භාවිතය
+pdfjs-scroll-horizontal-button-label = තිරස් අනුචලනය
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = ලේඛනයේ ගුණාංග…
+pdfjs-document-properties-button-label = ලේඛනයේ ගුණාංග…
+pdfjs-document-properties-file-name = ගොනුවේ නම:
+pdfjs-document-properties-file-size = ගොනුවේ ප්රමාණය:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = කි.බ. { $size_kb } (බයිට { $size_b })
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = මෙ.බ. { $size_mb } (බයිට { $size_b })
+pdfjs-document-properties-title = සිරැසිය:
+pdfjs-document-properties-author = කතෘ:
+pdfjs-document-properties-subject = මාතෘකාව:
+pdfjs-document-properties-keywords = මූල පද:
+pdfjs-document-properties-creation-date = සෑදූ දිනය:
+pdfjs-document-properties-modification-date = සංශෝධිත දිනය:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = නිර්මාතෘ:
+pdfjs-document-properties-producer = පීඩීඑෆ් සම්පාදක:
+pdfjs-document-properties-version = පීඩීඑෆ් අනුවාදය:
+pdfjs-document-properties-page-count = පිටු ගණන:
+pdfjs-document-properties-page-size = පිටුවේ තරම:
+pdfjs-document-properties-page-size-unit-inches = අඟල්
+pdfjs-document-properties-page-size-unit-millimeters = මි.මී.
+pdfjs-document-properties-page-size-orientation-portrait = සිරස්
+pdfjs-document-properties-page-size-orientation-landscape = තිරස්
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width }×{ $height }{ $unit }{ $name }{ $orientation }
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = වේගවත් වියමන දැක්ම:
+pdfjs-document-properties-linearized-yes = ඔව්
+pdfjs-document-properties-linearized-no = නැහැ
+pdfjs-document-properties-close-button = වසන්න
+
+## Print
+
+pdfjs-print-progress-message = මුද්රණය සඳහා ලේඛනය සූදානම් වෙමින්…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = අවලංගු කරන්න
+pdfjs-printing-not-supported = අවවාදයයි: මෙම අතිරික්සුව මුද්රණය සඳහා හොඳින් සහාය නොදක්වයි.
+pdfjs-printing-not-ready = අවවාදයයි: මුද්රණයට පීඩීඑෆ් ගොනුව සම්පූර්ණයෙන් පූරණය වී නැත.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-document-outline-button-label = ලේඛනයේ වටසන
+pdfjs-attachments-button =
+ .title = ඇමුණුම් පෙන්වන්න
+pdfjs-attachments-button-label = ඇමුණුම්
+pdfjs-layers-button =
+ .title = ස්තර පෙන්වන්න (සියළු ස්තර පෙරනිමි තත්වයට යළි සැකසීමට දෙවරක් ඔබන්න)
+pdfjs-layers-button-label = ස්තර
+pdfjs-thumbs-button =
+ .title = සිඟිති රූ පෙන්වන්න
+pdfjs-thumbs-button-label = සිඟිති රූ
+pdfjs-findbar-button =
+ .title = ලේඛනයෙහි සොයන්න
+pdfjs-findbar-button-label = සොයන්න
+pdfjs-additional-layers = අතිරේක ස්තර
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = පිටුව { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = පිටුවේ සිඟිත රූව { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = සොයන්න
+ .placeholder = ලේඛනයේ සොයන්න…
+pdfjs-find-previous-button =
+ .title = මෙම වැකිකඩ කලින් යෙදුණු ස්ථානය සොයන්න
+pdfjs-find-previous-button-label = කලින්
+pdfjs-find-next-button =
+ .title = මෙම වැකිකඩ ඊළඟට යෙදෙන ස්ථානය සොයන්න
+pdfjs-find-next-button-label = ඊළඟ
+pdfjs-find-highlight-checkbox = සියල්ල උද්දීපනය
+pdfjs-find-entire-word-checkbox-label = සමස්ත වචන
+pdfjs-find-reached-top = ලේඛනයේ මුදුනට ළඟා විය, පහළ සිට ඉහළට
+pdfjs-find-reached-bottom = ලේඛනයේ අවසානයට ළඟා විය, ඉහළ සිට පහළට
+pdfjs-find-not-found = වැකිකඩ හමු නොවිණි
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = පිටුවේ පළල
+pdfjs-page-scale-auto = ස්වයංක්රීය විශාලනය
+pdfjs-page-scale-actual = සැබෑ ප්රමාණය
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = පිටුව { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = පීඩීඑෆ් පූරණය කිරීමේදී දෝෂයක් සිදු විය.
+pdfjs-invalid-file-error = වලංගු නොවන හෝ හානිවූ පීඩීඑෆ් ගොනුවකි.
+pdfjs-missing-file-error = මඟහැරුණු පීඩීඑෆ් ගොනුවකි.
+pdfjs-unexpected-response-error = අනපේක්ෂිත සේවාදායක ප්රතිචාරයකි.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+
+## Password
+
+pdfjs-password-label = මෙම පීඩීඑෆ් ගොනුව විවෘත කිරීමට මුරපදය යොදන්න.
+pdfjs-password-invalid = වැරදි මුරපදයකි. නැවත උත්සාහ කරන්න.
+pdfjs-password-ok-button = හරි
+pdfjs-password-cancel-button = අවලංගු
+pdfjs-web-fonts-disabled = වියමන අකුරු අබලයි: පීඩීඑෆ් වෙත කාවැද්දූ රුවකුරු භාවිතා කළ නොහැකිය.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = පෙළ
+pdfjs-editor-free-text-button-label = පෙළ
+pdfjs-editor-ink-button =
+ .title = අඳින්න
+pdfjs-editor-ink-button-label = අඳින්න
+# Editor Parameters
+pdfjs-editor-free-text-color-input = වර්ණය
+pdfjs-editor-free-text-size-input = තරම
+pdfjs-editor-ink-color-input = වර්ණය
+pdfjs-editor-ink-thickness-input = ඝණකම
+pdfjs-free-text =
+ .aria-label = වදන් සකසනය
+pdfjs-free-text-default-content = ලිවීීම අරඹන්න…
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/sk/viewer.ftl b/web/locale/sk/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..07a0c5efccd9bb0f7d9f1194d22c6231576e333b
--- /dev/null
+++ b/web/locale/sk/viewer.ftl
@@ -0,0 +1,406 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Predchádzajúca strana
+pdfjs-previous-button-label = Predchádzajúca
+pdfjs-next-button =
+ .title = Nasledujúca strana
+pdfjs-next-button-label = Nasledujúca
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Strana
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = z { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } z { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zmenšiť veľkosť
+pdfjs-zoom-out-button-label = Zmenšiť veľkosť
+pdfjs-zoom-in-button =
+ .title = Zväčšiť veľkosť
+pdfjs-zoom-in-button-label = Zväčšiť veľkosť
+pdfjs-zoom-select =
+ .title = Nastavenie veľkosti
+pdfjs-presentation-mode-button =
+ .title = Prepnúť na režim prezentácie
+pdfjs-presentation-mode-button-label = Režim prezentácie
+pdfjs-open-file-button =
+ .title = Otvoriť súbor
+pdfjs-open-file-button-label = Otvoriť
+pdfjs-print-button =
+ .title = Tlačiť
+pdfjs-print-button-label = Tlačiť
+pdfjs-save-button =
+ .title = Uložiť
+pdfjs-save-button-label = Uložiť
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Stiahnuť
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Stiahnuť
+pdfjs-bookmark-button =
+ .title = Aktuálna stránka (zobraziť adresu URL z aktuálnej stránky)
+pdfjs-bookmark-button-label = Aktuálna stránka
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Otvoriť v aplikácii
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Otvoriť v aplikácii
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Nástroje
+pdfjs-tools-button-label = Nástroje
+pdfjs-first-page-button =
+ .title = Prejsť na prvú stranu
+pdfjs-first-page-button-label = Prejsť na prvú stranu
+pdfjs-last-page-button =
+ .title = Prejsť na poslednú stranu
+pdfjs-last-page-button-label = Prejsť na poslednú stranu
+pdfjs-page-rotate-cw-button =
+ .title = Otočiť v smere hodinových ručičiek
+pdfjs-page-rotate-cw-button-label = Otočiť v smere hodinových ručičiek
+pdfjs-page-rotate-ccw-button =
+ .title = Otočiť proti smeru hodinových ručičiek
+pdfjs-page-rotate-ccw-button-label = Otočiť proti smeru hodinových ručičiek
+pdfjs-cursor-text-select-tool-button =
+ .title = Povoliť výber textu
+pdfjs-cursor-text-select-tool-button-label = Výber textu
+pdfjs-cursor-hand-tool-button =
+ .title = Povoliť nástroj ruka
+pdfjs-cursor-hand-tool-button-label = Nástroj ruka
+pdfjs-scroll-page-button =
+ .title = Použiť rolovanie po stránkach
+pdfjs-scroll-page-button-label = Rolovanie po stránkach
+pdfjs-scroll-vertical-button =
+ .title = Používať zvislé posúvanie
+pdfjs-scroll-vertical-button-label = Zvislé posúvanie
+pdfjs-scroll-horizontal-button =
+ .title = Používať vodorovné posúvanie
+pdfjs-scroll-horizontal-button-label = Vodorovné posúvanie
+pdfjs-scroll-wrapped-button =
+ .title = Použiť postupné posúvanie
+pdfjs-scroll-wrapped-button-label = Postupné posúvanie
+pdfjs-spread-none-button =
+ .title = Nezdružovať stránky
+pdfjs-spread-none-button-label = Žiadne združovanie
+pdfjs-spread-odd-button =
+ .title = Združí stránky a umiestni nepárne stránky vľavo
+pdfjs-spread-odd-button-label = Združiť stránky (nepárne vľavo)
+pdfjs-spread-even-button =
+ .title = Združí stránky a umiestni párne stránky vľavo
+pdfjs-spread-even-button-label = Združiť stránky (párne vľavo)
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Vlastnosti dokumentu…
+pdfjs-document-properties-button-label = Vlastnosti dokumentu…
+pdfjs-document-properties-file-name = Názov súboru:
+pdfjs-document-properties-file-size = Veľkosť súboru:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } bajtov)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtov)
+pdfjs-document-properties-title = Názov:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Predmet:
+pdfjs-document-properties-keywords = Kľúčové slová:
+pdfjs-document-properties-creation-date = Dátum vytvorenia:
+pdfjs-document-properties-modification-date = Dátum úpravy:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Aplikácia:
+pdfjs-document-properties-producer = Tvorca PDF:
+pdfjs-document-properties-version = Verzia PDF:
+pdfjs-document-properties-page-count = Počet strán:
+pdfjs-document-properties-page-size = Veľkosť stránky:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = na výšku
+pdfjs-document-properties-page-size-orientation-landscape = na šírku
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = List
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Rýchle zobrazovanie z webu:
+pdfjs-document-properties-linearized-yes = Áno
+pdfjs-document-properties-linearized-no = Nie
+pdfjs-document-properties-close-button = Zavrieť
+
+## Print
+
+pdfjs-print-progress-message = Príprava dokumentu na tlač…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress } %
+pdfjs-print-progress-close-button = Zrušiť
+pdfjs-printing-not-supported = Upozornenie: tlač nie je v tomto prehliadači plne podporovaná.
+pdfjs-printing-not-ready = Upozornenie: súbor PDF nie je plne načítaný pre tlač.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Prepnúť bočný panel
+pdfjs-toggle-sidebar-notification-button =
+ .title = Prepnúť bočný panel (dokument obsahuje osnovu/prílohy/vrstvy)
+pdfjs-toggle-sidebar-button-label = Prepnúť bočný panel
+pdfjs-document-outline-button =
+ .title = Zobraziť osnovu dokumentu (dvojitým kliknutím rozbalíte/zbalíte všetky položky)
+pdfjs-document-outline-button-label = Osnova dokumentu
+pdfjs-attachments-button =
+ .title = Zobraziť prílohy
+pdfjs-attachments-button-label = Prílohy
+pdfjs-layers-button =
+ .title = Zobraziť vrstvy (dvojitým kliknutím uvediete všetky vrstvy do pôvodného stavu)
+pdfjs-layers-button-label = Vrstvy
+pdfjs-thumbs-button =
+ .title = Zobraziť miniatúry
+pdfjs-thumbs-button-label = Miniatúry
+pdfjs-current-outline-item-button =
+ .title = Nájsť aktuálnu položku v osnove
+pdfjs-current-outline-item-button-label = Aktuálna položka v osnove
+pdfjs-findbar-button =
+ .title = Hľadať v dokumente
+pdfjs-findbar-button-label = Hľadať
+pdfjs-additional-layers = Ďalšie vrstvy
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Strana { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatúra strany { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Hľadať
+ .placeholder = Hľadať v dokumente…
+pdfjs-find-previous-button =
+ .title = Vyhľadať predchádzajúci výskyt reťazca
+pdfjs-find-previous-button-label = Predchádzajúce
+pdfjs-find-next-button =
+ .title = Vyhľadať ďalší výskyt reťazca
+pdfjs-find-next-button-label = Ďalšie
+pdfjs-find-highlight-checkbox = Zvýrazniť všetky
+pdfjs-find-match-case-checkbox-label = Rozlišovať veľkosť písmen
+pdfjs-find-match-diacritics-checkbox-label = Rozlišovať diakritiku
+pdfjs-find-entire-word-checkbox-label = Celé slová
+pdfjs-find-reached-top = Bol dosiahnutý začiatok stránky, pokračuje sa od konca
+pdfjs-find-reached-bottom = Bol dosiahnutý koniec stránky, pokračuje sa od začiatku
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] Výskyt { $current } z { $total }
+ [few] Výskyt { $current } z { $total }
+ [many] Výskyt { $current } z { $total }
+ *[other] Výskyt { $current } z { $total }
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Viac ako { $limit } výskyt
+ [few] Viac ako { $limit } výskyty
+ [many] Viac ako { $limit } výskytov
+ *[other] Viac ako { $limit } výskytov
+ }
+pdfjs-find-not-found = Výraz nebol nájdený
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Na šírku strany
+pdfjs-page-scale-fit = Na veľkosť strany
+pdfjs-page-scale-auto = Automatická veľkosť
+pdfjs-page-scale-actual = Skutočná veľkosť
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale } %
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Strana { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Počas načítavania dokumentu PDF sa vyskytla chyba.
+pdfjs-invalid-file-error = Neplatný alebo poškodený súbor PDF.
+pdfjs-missing-file-error = Chýbajúci súbor PDF.
+pdfjs-unexpected-response-error = Neočakávaná odpoveď zo servera.
+pdfjs-rendering-error = Pri vykresľovaní stránky sa vyskytla chyba.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotácia typu { $type }]
+
+## Password
+
+pdfjs-password-label = Ak chcete otvoriť tento súbor PDF, zadajte jeho heslo.
+pdfjs-password-invalid = Heslo nie je platné. Skúste to znova.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Zrušiť
+pdfjs-web-fonts-disabled = Webové písma sú vypnuté: nie je možné použiť písma vložené do súboru PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Text
+pdfjs-editor-free-text-button-label = Text
+pdfjs-editor-ink-button =
+ .title = Kresliť
+pdfjs-editor-ink-button-label = Kresliť
+pdfjs-editor-stamp-button =
+ .title = Pridať alebo upraviť obrázky
+pdfjs-editor-stamp-button-label = Pridať alebo upraviť obrázky
+pdfjs-editor-highlight-button =
+ .title = Zvýrazniť
+pdfjs-editor-highlight-button-label = Zvýrazniť
+pdfjs-highlight-floating-button =
+ .title = Zvýrazniť
+pdfjs-highlight-floating-button1 =
+ .title = Zvýrazniť
+ .aria-label = Zvýrazniť
+pdfjs-highlight-floating-button-label = Zvýrazniť
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Odstrániť kresbu
+pdfjs-editor-remove-freetext-button =
+ .title = Odstrániť text
+pdfjs-editor-remove-stamp-button =
+ .title = Odstrániť obrázok
+pdfjs-editor-remove-highlight-button =
+ .title = Odstrániť zvýraznenie
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Farba
+pdfjs-editor-free-text-size-input = Veľkosť
+pdfjs-editor-ink-color-input = Farba
+pdfjs-editor-ink-thickness-input = Hrúbka
+pdfjs-editor-ink-opacity-input = Priehľadnosť
+pdfjs-editor-stamp-add-image-button =
+ .title = Pridať obrázok
+pdfjs-editor-stamp-add-image-button-label = Pridať obrázok
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Hrúbka
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Zmeňte hrúbku pre zvýrazňovanie iných položiek ako textu
+pdfjs-free-text =
+ .aria-label = Textový editor
+pdfjs-free-text-default-content = Začnite písať…
+pdfjs-ink =
+ .aria-label = Editor kreslenia
+pdfjs-ink-canvas =
+ .aria-label = Obrázok vytvorený používateľom
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternatívny text
+pdfjs-editor-alt-text-edit-button-label = Upraviť alternatívny text
+pdfjs-editor-alt-text-dialog-label = Vyberte možnosť
+pdfjs-editor-alt-text-dialog-description = Alternatívny text (alt text) pomáha, keď ľudia obrázok nevidia alebo sa nenačítava.
+pdfjs-editor-alt-text-add-description-label = Pridať popis
+pdfjs-editor-alt-text-add-description-description = Zamerajte sa na 1-2 vety, ktoré popisujú predmet, prostredie alebo akcie.
+pdfjs-editor-alt-text-mark-decorative-label = Označiť ako dekoratívny
+pdfjs-editor-alt-text-mark-decorative-description = Používa sa na ozdobné obrázky, ako sú okraje alebo vodoznaky.
+pdfjs-editor-alt-text-cancel-button = Zrušiť
+pdfjs-editor-alt-text-save-button = Uložiť
+pdfjs-editor-alt-text-decorative-tooltip = Označený ako dekoratívny
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Napríklad: „Mladý muž si sadá za stôl, aby sa najedol“
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Ľavý horný roh – zmena veľkosti
+pdfjs-editor-resizer-label-top-middle = Horný stred – zmena veľkosti
+pdfjs-editor-resizer-label-top-right = Pravý horný roh – zmena veľkosti
+pdfjs-editor-resizer-label-middle-right = Vpravo uprostred – zmena veľkosti
+pdfjs-editor-resizer-label-bottom-right = Pravý dolný roh – zmena veľkosti
+pdfjs-editor-resizer-label-bottom-middle = Stred dole – zmena veľkosti
+pdfjs-editor-resizer-label-bottom-left = Ľavý dolný roh – zmena veľkosti
+pdfjs-editor-resizer-label-middle-left = Vľavo uprostred – zmena veľkosti
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Farba zvýraznenia
+pdfjs-editor-colorpicker-button =
+ .title = Zmeniť farbu
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Výber farieb
+pdfjs-editor-colorpicker-yellow =
+ .title = Žltá
+pdfjs-editor-colorpicker-green =
+ .title = Zelená
+pdfjs-editor-colorpicker-blue =
+ .title = Modrá
+pdfjs-editor-colorpicker-pink =
+ .title = Ružová
+pdfjs-editor-colorpicker-red =
+ .title = Červená
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Zobraziť všetko
+pdfjs-editor-highlight-show-all-button =
+ .title = Zobraziť všetko
diff --git a/web/locale/skr/viewer.ftl b/web/locale/skr/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..72afe356ca1c789eeb4fc3a1ebb9d174bd16ca22
--- /dev/null
+++ b/web/locale/skr/viewer.ftl
@@ -0,0 +1,396 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = پچھلا ورقہ
+pdfjs-previous-button-label = پچھلا
+pdfjs-next-button =
+ .title = اڳلا ورقہ
+pdfjs-next-button-label = اڳلا
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = ورقہ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } دا
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } دا { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = زوم آؤٹ
+pdfjs-zoom-out-button-label = زوم آؤٹ
+pdfjs-zoom-in-button =
+ .title = زوم اِن
+pdfjs-zoom-in-button-label = زوم اِن
+pdfjs-zoom-select =
+ .title = زوم
+pdfjs-presentation-mode-button =
+ .title = پریزنٹیشن موڈ تے سوئچ کرو
+pdfjs-presentation-mode-button-label = پریزنٹیشن موڈ
+pdfjs-open-file-button =
+ .title = فائل کھولو
+pdfjs-open-file-button-label = کھولو
+pdfjs-print-button =
+ .title = چھاپو
+pdfjs-print-button-label = چھاپو
+pdfjs-save-button =
+ .title = ہتھیکڑا کرو
+pdfjs-save-button-label = ہتھیکڑا کرو
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = ڈاؤن لوڈ
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = ڈاؤن لوڈ
+pdfjs-bookmark-button =
+ .title = موجودہ ورقہ (موجودہ ورقے کنوں یوآرایل ݙیکھو)
+pdfjs-bookmark-button-label = موجودہ ورقہ
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = اوزار
+pdfjs-tools-button-label = اوزار
+pdfjs-first-page-button =
+ .title = پہلے ورقے تے ونڄو
+pdfjs-first-page-button-label = پہلے ورقے تے ونڄو
+pdfjs-last-page-button =
+ .title = چھیکڑی ورقے تے ونڄو
+pdfjs-last-page-button-label = چھیکڑی ورقے تے ونڄو
+pdfjs-page-rotate-cw-button =
+ .title = گھڑی وانگوں گھماؤ
+pdfjs-page-rotate-cw-button-label = گھڑی وانگوں گھماؤ
+pdfjs-page-rotate-ccw-button =
+ .title = گھڑی تے اُپٹھ گھماؤ
+pdfjs-page-rotate-ccw-button-label = گھڑی تے اُپٹھ گھماؤ
+pdfjs-cursor-text-select-tool-button =
+ .title = متن منتخب کݨ والا آلہ فعال بݨاؤ
+pdfjs-cursor-text-select-tool-button-label = متن منتخب کرݨ والا آلہ
+pdfjs-cursor-hand-tool-button =
+ .title = ہینڈ ٹول فعال بݨاؤ
+pdfjs-cursor-hand-tool-button-label = ہینڈ ٹول
+pdfjs-scroll-page-button =
+ .title = پیج سکرولنگ استعمال کرو
+pdfjs-scroll-page-button-label = پیج سکرولنگ
+pdfjs-scroll-vertical-button =
+ .title = عمودی سکرولنگ استعمال کرو
+pdfjs-scroll-vertical-button-label = عمودی سکرولنگ
+pdfjs-scroll-horizontal-button =
+ .title = افقی سکرولنگ استعمال کرو
+pdfjs-scroll-horizontal-button-label = افقی سکرولنگ
+pdfjs-scroll-wrapped-button =
+ .title = ویڑھی ہوئی سکرولنگ استعمال کرو
+pdfjs-scroll-wrapped-button-label = وہڑھی ہوئی سکرولنگ
+pdfjs-spread-none-button =
+ .title = پیج سپریڈز وِچ شامل نہ تھیوو۔
+pdfjs-spread-none-button-label = کوئی پولھ کائنی
+pdfjs-spread-odd-button =
+ .title = طاق نمبر والے ورقیاں دے نال شروع تھیوݨ والے پیج سپریڈز وِچ شامل تھیوو۔
+pdfjs-spread-odd-button-label = تاک پھیلاؤ
+pdfjs-spread-even-button =
+ .title = جفت نمر والے ورقیاں نال شروع تھیوݨ والے پیج سپریڈز وِ شامل تھیوو۔
+pdfjs-spread-even-button-label = جفت پھیلاؤ
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = دستاویز خواص…
+pdfjs-document-properties-button-label = دستاویز خواص …
+pdfjs-document-properties-file-name = فائل دا ناں:
+pdfjs-document-properties-file-size = فائل دا سائز:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } کے بی ({ $size_b } بائٹس)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } ایم بی ({ $size_b } بائٹس)
+pdfjs-document-properties-title = عنوان:
+pdfjs-document-properties-author = تخلیق کار:
+pdfjs-document-properties-subject = موضوع:
+pdfjs-document-properties-keywords = کلیدی الفاظ:
+pdfjs-document-properties-creation-date = تخلیق دی تاریخ:
+pdfjs-document-properties-modification-date = ترمیم دی تاریخ:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = تخلیق کار:
+pdfjs-document-properties-producer = PDF پیدا کار:
+pdfjs-document-properties-version = PDF ورژن:
+pdfjs-document-properties-page-count = ورقہ شماری:
+pdfjs-document-properties-page-size = ورقہ دی سائز:
+pdfjs-document-properties-page-size-unit-inches = وِچ
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = عمودی انداز
+pdfjs-document-properties-page-size-orientation-landscape = افقى انداز
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = لیٹر
+pdfjs-document-properties-page-size-name-legal = قنونی
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = تکھا ویب نظارہ:
+pdfjs-document-properties-linearized-yes = جیا
+pdfjs-document-properties-linearized-no = کو
+pdfjs-document-properties-close-button = بند کرو
+
+## Print
+
+pdfjs-print-progress-message = چھاپݨ کیتے دستاویز تیار تھیندے پئے ہن …
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = منسوخ کرو
+pdfjs-printing-not-supported = چتاوݨی: چھپائی ایں براؤزر تے پوری طراں معاونت شدہ کائنی۔
+pdfjs-printing-not-ready = چتاوݨی: PDF چھپائی کیتے پوری طراں لوڈ نئیں تھئی۔
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = سائیڈ بار ٹوگل کرو
+pdfjs-toggle-sidebar-notification-button =
+ .title = سائیڈ بار ٹوگل کرو (دستاویز وِچ آؤٹ لائن/ منسلکات/ پرتاں شامل ہن)
+pdfjs-toggle-sidebar-button-label = سائیڈ بار ٹوگل کرو
+pdfjs-document-outline-button =
+ .title = دستاویز دا خاکہ ݙکھاؤ (تمام آئٹمز کوں پھیلاوݨ/سنگوڑݨ کیتے ڈبل کلک کرو)
+pdfjs-document-outline-button-label = دستاویز آؤٹ لائن
+pdfjs-attachments-button =
+ .title = نتھیاں ݙکھاؤ
+pdfjs-attachments-button-label = منسلکات
+pdfjs-layers-button =
+ .title = پرتاں ݙکھاؤ (تمام پرتاں کوں ڈیفالٹ حالت وِچ دوبارہ ترتیب ݙیوݨ کیتے ڈبل کلک کرو)
+pdfjs-layers-button-label = پرتاں
+pdfjs-thumbs-button =
+ .title = تھمبنیل ݙکھاؤ
+pdfjs-thumbs-button-label = تھمبنیلز
+pdfjs-current-outline-item-button =
+ .title = موجودہ آؤٹ لائن آئٹم لبھو
+pdfjs-current-outline-item-button-label = موجودہ آؤٹ لائن آئٹم
+pdfjs-findbar-button =
+ .title = دستاویز وِچ لبھو
+pdfjs-findbar-button-label = لبھو
+pdfjs-additional-layers = اضافی پرتاں
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = ورقہ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = ورقے دا تھمبنیل { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = لبھو
+ .placeholder = دستاویز وِچ لبھو …
+pdfjs-find-previous-button =
+ .title = فقرے دا پچھلا واقعہ لبھو
+pdfjs-find-previous-button-label = پچھلا
+pdfjs-find-next-button =
+ .title = فقرے دا اڳلا واقعہ لبھو
+pdfjs-find-next-button-label = اڳلا
+pdfjs-find-highlight-checkbox = تمام نشابر کرو
+pdfjs-find-match-case-checkbox-label = حروف مشابہ کرو
+pdfjs-find-match-diacritics-checkbox-label = ڈائیکرٹکس مشابہ کرو
+pdfjs-find-entire-word-checkbox-label = تمام الفاظ
+pdfjs-find-reached-top = ورقے دے شروع تے پُج ڳیا، تلوں جاری کیتا ڳیا
+pdfjs-find-reached-bottom = ورقے دے پاند تے پُڄ ڳیا، اُتوں شروع کیتا ڳیا
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $total } وِچوں { $current } مشابہ
+ *[other] { $total } وِچوں { $current } مشابے
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] { $limit } توں ودھ مماثلت۔
+ *[other] { $limit } توں ودھ مماثلتاں۔
+ }
+pdfjs-find-not-found = فقرہ نئیں ملیا
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = ورقے دی چوڑائی
+pdfjs-page-scale-fit = ورقہ فٹنگ
+pdfjs-page-scale-auto = آپوں آپ زوم
+pdfjs-page-scale-actual = اصل میچا
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = ورقہ { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF لوڈ کریندے ویلھے نقص آ ڳیا۔
+pdfjs-invalid-file-error = غلط یا خراب شدہ PDF فائل۔
+pdfjs-missing-file-error = PDF فائل غائب ہے۔
+pdfjs-unexpected-response-error = سرور دا غیر متوقع جواب۔
+pdfjs-rendering-error = ورقہ رینڈر کریندے ویلھے ہک خرابی پیش آڳئی۔
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } تشریح]
+
+## Password
+
+pdfjs-password-label = ایہ PDF فائل کھولݨ کیتے پاس ورڈ درج کرو۔
+pdfjs-password-invalid = غلط پاس ورڈ: براہ مہربانی ولدا کوشش کرو۔
+pdfjs-password-ok-button = ٹھیک ہے
+pdfjs-password-cancel-button = منسوخ کرو
+pdfjs-web-fonts-disabled = ویب فونٹس غیر فعال ہن: ایمبیڈڈ PDF فونٹس استعمال کرݨ کنوں قاصر ہن
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = متن
+pdfjs-editor-free-text-button-label = متن
+pdfjs-editor-ink-button =
+ .title = چھکو
+pdfjs-editor-ink-button-label = چھکو
+pdfjs-editor-stamp-button =
+ .title = تصویراں کوں شامل کرو یا ترمیم کرو
+pdfjs-editor-stamp-button-label = تصویراں کوں شامل کرو یا ترمیم کرو
+pdfjs-editor-highlight-button =
+ .title = نمایاں کرو
+pdfjs-editor-highlight-button-label = نمایاں کرو
+pdfjs-highlight-floating-button =
+ .title = نمایاں کرو
+pdfjs-highlight-floating-button1 =
+ .title = نمایاں کرو
+ .aria-label = نمایاں کرو
+pdfjs-highlight-floating-button-label = نمایاں کرو
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = ڈرائینگ ہٹاؤ
+pdfjs-editor-remove-freetext-button =
+ .title = متن ہٹاؤ
+pdfjs-editor-remove-stamp-button =
+ .title = تصویر ہٹاؤ
+pdfjs-editor-remove-highlight-button =
+ .title = نمایاں ہٹاؤ
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = رنگ
+pdfjs-editor-free-text-size-input = سائز
+pdfjs-editor-ink-color-input = رنگ
+pdfjs-editor-ink-thickness-input = ٹھولھ
+pdfjs-editor-ink-opacity-input = دھندلاپن
+pdfjs-editor-stamp-add-image-button =
+ .title = تصویر شامل کرو
+pdfjs-editor-stamp-add-image-button-label = تصویر شامل کرو
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = مُٹاݨ
+pdfjs-editor-free-highlight-thickness-title =
+ .title = متن توں ان٘ج ٻئے شئیں کوں نمایاں کرݨ ویلے مُٹاݨ کوں بدلو
+pdfjs-free-text =
+ .aria-label = ٹیکسٹ ایڈیٹر
+pdfjs-free-text-default-content = ٹائپنگ شروع کرو …
+pdfjs-ink =
+ .aria-label = ڈرا ایڈیٹر
+pdfjs-ink-canvas =
+ .aria-label = صارف دی بݨائی ہوئی تصویر
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alt متن
+pdfjs-editor-alt-text-edit-button-label = alt متن وِچ ترمیم کرو
+pdfjs-editor-alt-text-dialog-label = ہِک اختیار چُݨو
+pdfjs-editor-alt-text-dialog-description = Alt متن (متبادل متن) اِیں ویلے مَدَت کرین٘دا ہِے جہڑیلے لوک تصویر کوں نِھیں ݙیکھ سڳدے یا جہڑیلے اِیہ لوڈ کائنی تِھین٘دا۔
+pdfjs-editor-alt-text-add-description-label = تفصیل شامل کرو
+pdfjs-editor-alt-text-add-description-description = 1-2 جملیاں دا مقصد جہڑے موضوع، ترتیب، یا اعمال کوں بیان کرین٘دے ہِن۔
+pdfjs-editor-alt-text-mark-decorative-label = آرائشی طور تے نشان زد کرو
+pdfjs-editor-alt-text-mark-decorative-description = اِیہ آرائشی تصویراں کِیتے استعمال تِھین٘دا ہِے، جیویں بارڈر یا واٹر مارکس۔
+pdfjs-editor-alt-text-cancel-button = منسوخ
+pdfjs-editor-alt-text-save-button = محفوظ
+pdfjs-editor-alt-text-decorative-tooltip = آرائشی دے طور تے نشان زد تِھی ڳِیا
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = مثال دے طور تے، "ہِک جؤان کھاݨاں کھاوݨ کِیتے میز اُتّے ٻیٹھا ہِے"
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = اُتلی کَھٻّی نُکّڑ — سائز بدلو
+pdfjs-editor-resizer-label-top-middle = اُتلا وِچلا — سائز بدلو
+pdfjs-editor-resizer-label-top-right = اُتلی سَڄّی نُکَّڑ — سائز بدلو
+pdfjs-editor-resizer-label-middle-right = وِچلا سڄّا — سائز بدلو
+pdfjs-editor-resizer-label-bottom-right = تلوِیں سَڄّی نُکَّڑ — سائز بدلو
+pdfjs-editor-resizer-label-bottom-middle = تلواں وِچلا — سائز بدلو
+pdfjs-editor-resizer-label-bottom-left = تلوِیں کَھٻّی نُکّڑ — سائز بدلو
+pdfjs-editor-resizer-label-middle-left = وِچلا کَھٻّا — سائز بدلو
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = نشابر رنگ
+pdfjs-editor-colorpicker-button =
+ .title = رنگ بدلو
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = رنگ اختیارات
+pdfjs-editor-colorpicker-yellow =
+ .title = پیلا
+pdfjs-editor-colorpicker-green =
+ .title = ساوا
+pdfjs-editor-colorpicker-blue =
+ .title = نیلا
+pdfjs-editor-colorpicker-pink =
+ .title = گلابی
+pdfjs-editor-colorpicker-red =
+ .title = لال
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = سارے ݙکھاؤ
+pdfjs-editor-highlight-show-all-button =
+ .title = سارے ݙکھاؤ
diff --git a/web/locale/sl/viewer.ftl b/web/locale/sl/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..7cda4ec292159b686e602f89e20c053d022506c7
--- /dev/null
+++ b/web/locale/sl/viewer.ftl
@@ -0,0 +1,394 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Prejšnja stran
+pdfjs-previous-button-label = Nazaj
+pdfjs-next-button =
+ .title = Naslednja stran
+pdfjs-next-button-label = Naprej
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Stran
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = od { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } od { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Pomanjšaj
+pdfjs-zoom-out-button-label = Pomanjšaj
+pdfjs-zoom-in-button =
+ .title = Povečaj
+pdfjs-zoom-in-button-label = Povečaj
+pdfjs-zoom-select =
+ .title = Povečava
+pdfjs-presentation-mode-button =
+ .title = Preklopi v način predstavitve
+pdfjs-presentation-mode-button-label = Način predstavitve
+pdfjs-open-file-button =
+ .title = Odpri datoteko
+pdfjs-open-file-button-label = Odpri
+pdfjs-print-button =
+ .title = Natisni
+pdfjs-print-button-label = Natisni
+pdfjs-save-button =
+ .title = Shrani
+pdfjs-save-button-label = Shrani
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Prenesi
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Prenesi
+pdfjs-bookmark-button =
+ .title = Trenutna stran (prikaži URL, ki vodi do trenutne strani)
+pdfjs-bookmark-button-label = Na trenutno stran
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Orodja
+pdfjs-tools-button-label = Orodja
+pdfjs-first-page-button =
+ .title = Pojdi na prvo stran
+pdfjs-first-page-button-label = Pojdi na prvo stran
+pdfjs-last-page-button =
+ .title = Pojdi na zadnjo stran
+pdfjs-last-page-button-label = Pojdi na zadnjo stran
+pdfjs-page-rotate-cw-button =
+ .title = Zavrti v smeri urnega kazalca
+pdfjs-page-rotate-cw-button-label = Zavrti v smeri urnega kazalca
+pdfjs-page-rotate-ccw-button =
+ .title = Zavrti v nasprotni smeri urnega kazalca
+pdfjs-page-rotate-ccw-button-label = Zavrti v nasprotni smeri urnega kazalca
+pdfjs-cursor-text-select-tool-button =
+ .title = Omogoči orodje za izbor besedila
+pdfjs-cursor-text-select-tool-button-label = Orodje za izbor besedila
+pdfjs-cursor-hand-tool-button =
+ .title = Omogoči roko
+pdfjs-cursor-hand-tool-button-label = Roka
+pdfjs-scroll-page-button =
+ .title = Uporabi drsenje po strani
+pdfjs-scroll-page-button-label = Drsenje po strani
+pdfjs-scroll-vertical-button =
+ .title = Uporabi navpično drsenje
+pdfjs-scroll-vertical-button-label = Navpično drsenje
+pdfjs-scroll-horizontal-button =
+ .title = Uporabi vodoravno drsenje
+pdfjs-scroll-horizontal-button-label = Vodoravno drsenje
+pdfjs-scroll-wrapped-button =
+ .title = Uporabi ovito drsenje
+pdfjs-scroll-wrapped-button-label = Ovito drsenje
+pdfjs-spread-none-button =
+ .title = Ne združuj razponov strani
+pdfjs-spread-none-button-label = Brez razponov
+pdfjs-spread-odd-button =
+ .title = Združuj razpone strani z začetkom pri lihih straneh
+pdfjs-spread-odd-button-label = Lihi razponi
+pdfjs-spread-even-button =
+ .title = Združuj razpone strani z začetkom pri sodih straneh
+pdfjs-spread-even-button-label = Sodi razponi
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Lastnosti dokumenta …
+pdfjs-document-properties-button-label = Lastnosti dokumenta …
+pdfjs-document-properties-file-name = Ime datoteke:
+pdfjs-document-properties-file-size = Velikost datoteke:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajtov)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajtov)
+pdfjs-document-properties-title = Ime:
+pdfjs-document-properties-author = Avtor:
+pdfjs-document-properties-subject = Tema:
+pdfjs-document-properties-keywords = Ključne besede:
+pdfjs-document-properties-creation-date = Datum nastanka:
+pdfjs-document-properties-modification-date = Datum spremembe:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Ustvaril:
+pdfjs-document-properties-producer = Izdelovalec PDF:
+pdfjs-document-properties-version = Različica PDF:
+pdfjs-document-properties-page-count = Število strani:
+pdfjs-document-properties-page-size = Velikost strani:
+pdfjs-document-properties-page-size-unit-inches = palcev
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = pokončno
+pdfjs-document-properties-page-size-orientation-landscape = ležeče
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Pismo
+pdfjs-document-properties-page-size-name-legal = Pravno
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Hitri spletni ogled:
+pdfjs-document-properties-linearized-yes = Da
+pdfjs-document-properties-linearized-no = Ne
+pdfjs-document-properties-close-button = Zapri
+
+## Print
+
+pdfjs-print-progress-message = Priprava dokumenta na tiskanje …
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress } %
+pdfjs-print-progress-close-button = Prekliči
+pdfjs-printing-not-supported = Opozorilo: ta brskalnik ne podpira vseh možnosti tiskanja.
+pdfjs-printing-not-ready = Opozorilo: PDF ni v celoti naložen za tiskanje.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Preklopi stransko vrstico
+pdfjs-toggle-sidebar-notification-button =
+ .title = Preklopi stransko vrstico (dokument vsebuje oris/priponke/plasti)
+pdfjs-toggle-sidebar-button-label = Preklopi stransko vrstico
+pdfjs-document-outline-button =
+ .title = Prikaži oris dokumenta (dvokliknite za razširitev/strnitev vseh predmetov)
+pdfjs-document-outline-button-label = Oris dokumenta
+pdfjs-attachments-button =
+ .title = Prikaži priponke
+pdfjs-attachments-button-label = Priponke
+pdfjs-layers-button =
+ .title = Prikaži plasti (dvokliknite za ponastavitev vseh plasti na privzeto stanje)
+pdfjs-layers-button-label = Plasti
+pdfjs-thumbs-button =
+ .title = Prikaži sličice
+pdfjs-thumbs-button-label = Sličice
+pdfjs-current-outline-item-button =
+ .title = Najdi trenutni predmet orisa
+pdfjs-current-outline-item-button-label = Trenutni predmet orisa
+pdfjs-findbar-button =
+ .title = Iskanje po dokumentu
+pdfjs-findbar-button-label = Najdi
+pdfjs-additional-layers = Dodatne plasti
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Stran { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Sličica strani { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Najdi
+ .placeholder = Najdi v dokumentu …
+pdfjs-find-previous-button =
+ .title = Najdi prejšnjo ponovitev iskanega
+pdfjs-find-previous-button-label = Najdi nazaj
+pdfjs-find-next-button =
+ .title = Najdi naslednjo ponovitev iskanega
+pdfjs-find-next-button-label = Najdi naprej
+pdfjs-find-highlight-checkbox = Označi vse
+pdfjs-find-match-case-checkbox-label = Razlikuj velike/male črke
+pdfjs-find-match-diacritics-checkbox-label = Razlikuj diakritične znake
+pdfjs-find-entire-word-checkbox-label = Cele besede
+pdfjs-find-reached-top = Dosežen začetek dokumenta iz smeri konca
+pdfjs-find-reached-bottom = Doseženo konec dokumenta iz smeri začetka
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] Zadetek { $current } od { $total }
+ [two] Zadetek { $current } od { $total }
+ [few] Zadetek { $current } od { $total }
+ *[other] Zadetek { $current } od { $total }
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Več kot { $limit } zadetek
+ [two] Več kot { $limit } zadetka
+ [few] Več kot { $limit } zadetki
+ *[other] Več kot { $limit } zadetkov
+ }
+pdfjs-find-not-found = Iskanega ni mogoče najti
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Širina strani
+pdfjs-page-scale-fit = Prilagodi stran
+pdfjs-page-scale-auto = Samodejno
+pdfjs-page-scale-actual = Dejanska velikost
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale } %
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Stran { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Med nalaganjem datoteke PDF je prišlo do napake.
+pdfjs-invalid-file-error = Neveljavna ali pokvarjena datoteka PDF.
+pdfjs-missing-file-error = Ni datoteke PDF.
+pdfjs-unexpected-response-error = Nepričakovan odgovor strežnika.
+pdfjs-rendering-error = Med pripravljanjem strani je prišlo do napake!
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Opomba vrste { $type }]
+
+## Password
+
+pdfjs-password-label = Vnesite geslo za odpiranje te datoteke PDF.
+pdfjs-password-invalid = Neveljavno geslo. Poskusite znova.
+pdfjs-password-ok-button = V redu
+pdfjs-password-cancel-button = Prekliči
+pdfjs-web-fonts-disabled = Spletne pisave so onemogočene: vgradnih pisav za PDF ni mogoče uporabiti.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Besedilo
+pdfjs-editor-free-text-button-label = Besedilo
+pdfjs-editor-ink-button =
+ .title = Riši
+pdfjs-editor-ink-button-label = Riši
+pdfjs-editor-stamp-button =
+ .title = Dodajanje ali urejanje slik
+pdfjs-editor-stamp-button-label = Dodajanje ali urejanje slik
+pdfjs-editor-highlight-button =
+ .title = Označevalnik
+pdfjs-editor-highlight-button-label = Označevalnik
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Odstrani risbo
+pdfjs-editor-remove-freetext-button =
+ .title = Odstrani besedilo
+pdfjs-editor-remove-stamp-button =
+ .title = Odstrani sliko
+pdfjs-editor-remove-highlight-button =
+ .title = Odstrani označbo
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Barva
+pdfjs-editor-free-text-size-input = Velikost
+pdfjs-editor-ink-color-input = Barva
+pdfjs-editor-ink-thickness-input = Debelina
+pdfjs-editor-ink-opacity-input = Neprosojnost
+pdfjs-editor-stamp-add-image-button =
+ .title = Dodaj sliko
+pdfjs-editor-stamp-add-image-button-label = Dodaj sliko
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Debelina
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Spremeni debelino pri označevanju nebesedilnih elementov
+pdfjs-free-text =
+ .aria-label = Urejevalnik besedila
+pdfjs-free-text-default-content = Začnite tipkati …
+pdfjs-ink =
+ .aria-label = Urejevalnik risanja
+pdfjs-ink-canvas =
+ .aria-label = Uporabnikova slika
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Nadomestno besedilo
+pdfjs-editor-alt-text-edit-button-label = Uredi nadomestno besedilo
+pdfjs-editor-alt-text-dialog-label = Izberite možnost
+pdfjs-editor-alt-text-dialog-description = Nadomestno besedilo se prikaže tistim, ki ne vidijo slike, ali če se ta ne naloži.
+pdfjs-editor-alt-text-add-description-label = Dodaj opis
+pdfjs-editor-alt-text-add-description-description = Poskušajte v enem ali dveh stavkih opisati motiv, okolje ali dejanja.
+pdfjs-editor-alt-text-mark-decorative-label = Označi kot okrasno
+pdfjs-editor-alt-text-mark-decorative-description = Uporablja se za slike, ki služijo samo okrasu, na primer obrobe ali vodne žige.
+pdfjs-editor-alt-text-cancel-button = Prekliči
+pdfjs-editor-alt-text-save-button = Shrani
+pdfjs-editor-alt-text-decorative-tooltip = Označeno kot okrasno
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Na primer: "Mladenič sedi za mizo pri jedi"
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Zgornji levi kot – spremeni velikost
+pdfjs-editor-resizer-label-top-middle = Zgoraj na sredini – spremeni velikost
+pdfjs-editor-resizer-label-top-right = Zgornji desni kot – spremeni velikost
+pdfjs-editor-resizer-label-middle-right = Desno na sredini – spremeni velikost
+pdfjs-editor-resizer-label-bottom-right = Spodnji desni kot – spremeni velikost
+pdfjs-editor-resizer-label-bottom-middle = Spodaj na sredini – spremeni velikost
+pdfjs-editor-resizer-label-bottom-left = Spodnji levi kot – spremeni velikost
+pdfjs-editor-resizer-label-middle-left = Levo na sredini – spremeni velikost
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Barva označbe
+pdfjs-editor-colorpicker-button =
+ .title = Spremeni barvo
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Izbira barve
+pdfjs-editor-colorpicker-yellow =
+ .title = Rumena
+pdfjs-editor-colorpicker-green =
+ .title = Zelena
+pdfjs-editor-colorpicker-blue =
+ .title = Modra
+pdfjs-editor-colorpicker-pink =
+ .title = Roza
+pdfjs-editor-colorpicker-red =
+ .title = Rdeča
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Prikaži vse
+pdfjs-editor-highlight-show-all-button =
+ .title = Prikaži vse
diff --git a/web/locale/son/viewer.ftl b/web/locale/son/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..fa4f6b1fc140319869ee59be58ced99e20d60f88
--- /dev/null
+++ b/web/locale/son/viewer.ftl
@@ -0,0 +1,206 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Moo bisante
+pdfjs-previous-button-label = Bisante
+pdfjs-next-button =
+ .title = Jinehere moo
+pdfjs-next-button-label = Jine
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Moo
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } ra
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } ka hun { $pagesCount }) ra
+pdfjs-zoom-out-button =
+ .title = Nakasandi
+pdfjs-zoom-out-button-label = Nakasandi
+pdfjs-zoom-in-button =
+ .title = Bebbeerandi
+pdfjs-zoom-in-button-label = Bebbeerandi
+pdfjs-zoom-select =
+ .title = Bebbeerandi
+pdfjs-presentation-mode-button =
+ .title = Bere cebeyan alhaali
+pdfjs-presentation-mode-button-label = Cebeyan alhaali
+pdfjs-open-file-button =
+ .title = Tuku feeri
+pdfjs-open-file-button-label = Feeri
+pdfjs-print-button =
+ .title = Kar
+pdfjs-print-button-label = Kar
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Goyjinawey
+pdfjs-tools-button-label = Goyjinawey
+pdfjs-first-page-button =
+ .title = Koy moo jinaa ga
+pdfjs-first-page-button-label = Koy moo jinaa ga
+pdfjs-last-page-button =
+ .title = Koy moo koraa ga
+pdfjs-last-page-button-label = Koy moo koraa ga
+pdfjs-page-rotate-cw-button =
+ .title = Kuubi kanbe guma here
+pdfjs-page-rotate-cw-button-label = Kuubi kanbe guma here
+pdfjs-page-rotate-ccw-button =
+ .title = Kuubi kanbe wowa here
+pdfjs-page-rotate-ccw-button-label = Kuubi kanbe wowa here
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Takadda mayrawey…
+pdfjs-document-properties-button-label = Takadda mayrawey…
+pdfjs-document-properties-file-name = Tuku maa:
+pdfjs-document-properties-file-size = Tuku adadu:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = KB { $size_kb } (cebsu-ize { $size_b })
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = MB { $size_mb } (cebsu-ize { $size_b })
+pdfjs-document-properties-title = Tiiramaa:
+pdfjs-document-properties-author = Hantumkaw:
+pdfjs-document-properties-subject = Dalil:
+pdfjs-document-properties-keywords = Kufalkalimawey:
+pdfjs-document-properties-creation-date = Teeyan han:
+pdfjs-document-properties-modification-date = Barmayan han:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Teekaw:
+pdfjs-document-properties-producer = PDF berandikaw:
+pdfjs-document-properties-version = PDF dumi:
+pdfjs-document-properties-page-count = Moo hinna:
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+
+##
+
+pdfjs-document-properties-close-button = Daabu
+
+## Print
+
+pdfjs-print-progress-message = Goo ma takaddaa soolu k'a kar se…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Naŋ
+pdfjs-printing-not-supported = Yaamar: Karyan ši tee ka timme nda ceecikaa woo.
+pdfjs-printing-not-ready = Yaamar: PDF ši zunbu ka timme karyan še.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Kanjari ceraw zuu
+pdfjs-toggle-sidebar-button-label = Kanjari ceraw zuu
+pdfjs-document-outline-button =
+ .title = Takaddaa korfur alhaaloo cebe (naagu cee hinka ka haya-izey kul hayandi/kankamandi)
+pdfjs-document-outline-button-label = Takadda filla-boŋ
+pdfjs-attachments-button =
+ .title = Hangarey cebe
+pdfjs-attachments-button-label = Hangarey
+pdfjs-thumbs-button =
+ .title = Kabeboy biyey cebe
+pdfjs-thumbs-button-label = Kabeboy biyey
+pdfjs-findbar-button =
+ .title = Ceeci takaddaa ra
+pdfjs-findbar-button-label = Ceeci
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = { $page } moo
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Kabeboy bii { $page } moo še
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Ceeci
+ .placeholder = Ceeci takaddaa ra…
+pdfjs-find-previous-button =
+ .title = Kalimaɲaŋoo bangayri bisantaa ceeci
+pdfjs-find-previous-button-label = Bisante
+pdfjs-find-next-button =
+ .title = Kalimaɲaŋoo hiino bangayroo ceeci
+pdfjs-find-next-button-label = Jine
+pdfjs-find-highlight-checkbox = Ikul šilbay
+pdfjs-find-match-case-checkbox-label = Harfu-beeriyan hawgay
+pdfjs-find-reached-top = A too moŋoo boŋoo, koy jine ka šinitin nda cewoo
+pdfjs-find-reached-bottom = A too moɲoo cewoo, koy jine šintioo ga
+pdfjs-find-not-found = Kalimaɲaa mana duwandi
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Mooo hayyan
+pdfjs-page-scale-fit = Moo sawayan
+pdfjs-page-scale-auto = Boŋše azzaati barmayyan
+pdfjs-page-scale-actual = Adadu cimi
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Firka bangay kaŋ PDF goo ma zumandi.
+pdfjs-invalid-file-error = PDF tuku laala wala laybante.
+pdfjs-missing-file-error = PDF tuku kumante.
+pdfjs-unexpected-response-error = Manti feršikaw tuuruyan maatante.
+pdfjs-rendering-error = Firka bangay kaŋ moɲoo goo ma willandi.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = { $type } maasa-caw]
+
+## Password
+
+pdfjs-password-label = Šennikufal dam ka PDF tukoo woo feeri.
+pdfjs-password-invalid = Šennikufal laalo. Ceeci koyne taare.
+pdfjs-password-ok-button = Ayyo
+pdfjs-password-cancel-button = Naŋ
+pdfjs-web-fonts-disabled = Interneti šigirawey kay: ši hin ka goy nda PDF šigira hurantey.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/sq/viewer.ftl b/web/locale/sq/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..be5b273dc7147bb6716f86adf9bb8c0b20c2650c
--- /dev/null
+++ b/web/locale/sq/viewer.ftl
@@ -0,0 +1,387 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Faqja e Mëparshme
+pdfjs-previous-button-label = E mëparshmja
+pdfjs-next-button =
+ .title = Faqja Pasuese
+pdfjs-next-button-label = Pasuesja
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Faqe
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = nga { $pagesCount } gjithsej
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } nga { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zvogëlojeni
+pdfjs-zoom-out-button-label = Zvogëlojeni
+pdfjs-zoom-in-button =
+ .title = Zmadhojeni
+pdfjs-zoom-in-button-label = Zmadhojini
+pdfjs-zoom-select =
+ .title = Zmadhim/Zvogëlim
+pdfjs-presentation-mode-button =
+ .title = Kalo te Mënyra Paraqitje
+pdfjs-presentation-mode-button-label = Mënyra Paraqitje
+pdfjs-open-file-button =
+ .title = Hapni Kartelë
+pdfjs-open-file-button-label = Hape
+pdfjs-print-button =
+ .title = Shtypje
+pdfjs-print-button-label = Shtype
+pdfjs-save-button =
+ .title = Ruaje
+pdfjs-save-button-label = Ruaje
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Shkarkojeni
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Shkarkoje
+pdfjs-bookmark-button =
+ .title = Faqja e Tanishme (Shihni URL nga Faqja e Tanishme)
+pdfjs-bookmark-button-label = Faqja e Tanishme
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Mjete
+pdfjs-tools-button-label = Mjete
+pdfjs-first-page-button =
+ .title = Kaloni te Faqja e Parë
+pdfjs-first-page-button-label = Kaloni te Faqja e Parë
+pdfjs-last-page-button =
+ .title = Kaloni te Faqja e Fundit
+pdfjs-last-page-button-label = Kaloni te Faqja e Fundit
+pdfjs-page-rotate-cw-button =
+ .title = Rrotullojeni Në Kahun Orar
+pdfjs-page-rotate-cw-button-label = Rrotulloje Në Kahun Orar
+pdfjs-page-rotate-ccw-button =
+ .title = Rrotullojeni Në Kahun Kundërorar
+pdfjs-page-rotate-ccw-button-label = Rrotulloje Në Kahun Kundërorar
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktivizo Mjet Përzgjedhjeje Teksti
+pdfjs-cursor-text-select-tool-button-label = Mjet Përzgjedhjeje Teksti
+pdfjs-cursor-hand-tool-button =
+ .title = Aktivizo Mjetin Dorë
+pdfjs-cursor-hand-tool-button-label = Mjeti Dorë
+pdfjs-scroll-page-button =
+ .title = Përdor Rrëshqitje Në Faqe
+pdfjs-scroll-page-button-label = Rrëshqitje Në Faqe
+pdfjs-scroll-vertical-button =
+ .title = Përdor Rrëshqitje Vertikale
+pdfjs-scroll-vertical-button-label = Rrëshqitje Vertikale
+pdfjs-scroll-horizontal-button =
+ .title = Përdor Rrëshqitje Horizontale
+pdfjs-scroll-horizontal-button-label = Rrëshqitje Horizontale
+pdfjs-scroll-wrapped-button =
+ .title = Përdor Rrëshqitje Me Mbështjellje
+pdfjs-scroll-wrapped-button-label = Rrëshqitje Me Mbështjellje
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Veti Dokumenti…
+pdfjs-document-properties-button-label = Veti Dokumenti…
+pdfjs-document-properties-file-name = Emër kartele:
+pdfjs-document-properties-file-size = Madhësi kartele:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bajte)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bajte)
+pdfjs-document-properties-title = Titull:
+pdfjs-document-properties-author = Autor:
+pdfjs-document-properties-subject = Subjekt:
+pdfjs-document-properties-keywords = Fjalëkyçe:
+pdfjs-document-properties-creation-date = Datë Krijimi:
+pdfjs-document-properties-modification-date = Datë Ndryshimi:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Krijues:
+pdfjs-document-properties-producer = Prodhues PDF-je:
+pdfjs-document-properties-version = Version PDF-je:
+pdfjs-document-properties-page-count = Numër Faqesh:
+pdfjs-document-properties-page-size = Madhësi Faqeje:
+pdfjs-document-properties-page-size-unit-inches = inç
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = portret
+pdfjs-document-properties-page-size-orientation-landscape = së gjeri
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Parje e Shpjetë në Web:
+pdfjs-document-properties-linearized-yes = Po
+pdfjs-document-properties-linearized-no = Jo
+pdfjs-document-properties-close-button = Mbylleni
+
+## Print
+
+pdfjs-print-progress-message = Po përgatitet dokumenti për shtypje…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Anuloje
+pdfjs-printing-not-supported = Kujdes: Shtypja s’mbulohet plotësisht nga ky shfletues.
+pdfjs-printing-not-ready = Kujdes: PDF-ja s’është ngarkuar plotësisht që ta shtypni.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Shfaqni/Fshihni Anështyllën
+pdfjs-toggle-sidebar-notification-button =
+ .title = Hap/Mbyll Anështylë (dokumenti përmban përvijim/nashkëngjitje/shtresa)
+pdfjs-toggle-sidebar-button-label = Shfaq/Fshih Anështyllën
+pdfjs-document-outline-button =
+ .title = Shfaqni Përvijim Dokumenti (dyklikoni që të shfaqen/fshihen krejt elementët)
+pdfjs-document-outline-button-label = Përvijim Dokumenti
+pdfjs-attachments-button =
+ .title = Shfaqni Bashkëngjitje
+pdfjs-attachments-button-label = Bashkëngjitje
+pdfjs-layers-button =
+ .title = Shfaq Shtresa (dyklikoni që të rikthehen krejt shtresat në gjendjen e tyre parazgjedhje)
+pdfjs-layers-button-label = Shtresa
+pdfjs-thumbs-button =
+ .title = Shfaqni Miniatura
+pdfjs-thumbs-button-label = Miniatura
+pdfjs-current-outline-item-button =
+ .title = Gjej Objektin e Tanishëm të Përvijuar
+pdfjs-current-outline-item-button-label = Objekt i Tanishëm i Përvijuar
+pdfjs-findbar-button =
+ .title = Gjeni në Dokument
+pdfjs-findbar-button-label = Gjej
+pdfjs-additional-layers = Shtresa Shtesë
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Faqja { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniaturë e Faqes { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Gjej
+ .placeholder = Gjeni në dokument…
+pdfjs-find-previous-button =
+ .title = Gjeni hasjen e mëparshme të togfjalëshit
+pdfjs-find-previous-button-label = E mëparshmja
+pdfjs-find-next-button =
+ .title = Gjeni hasjen pasuese të togfjalëshit
+pdfjs-find-next-button-label = Pasuesja
+pdfjs-find-highlight-checkbox = Theksoji të tëra
+pdfjs-find-match-case-checkbox-label = Siç Është Shkruar
+pdfjs-find-match-diacritics-checkbox-label = Me Përputhje Me Shenjat Diakritike
+pdfjs-find-entire-word-checkbox-label = Fjalë të Plota
+pdfjs-find-reached-top = U mbërrit në krye të dokumentit, vazhduar prej fundit
+pdfjs-find-reached-bottom = U mbërrit në fund të dokumentit, vazhduar prej kreut
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } nga { $total } përputhje
+ *[other] { $current } nga { $total } përputhje
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Më tepër se { $limit } përputhje
+ *[other] Më tepër se { $limit } përputhje
+ }
+pdfjs-find-not-found = Togfjalësh që s’gjendet
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Gjerësi Faqeje
+pdfjs-page-scale-fit = Sa Nxë Faqja
+pdfjs-page-scale-auto = Zoom i Vetvetishëm
+pdfjs-page-scale-actual = Madhësia Faktike
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Faqja { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ndodhi një gabim gjatë ngarkimit të PDF-së.
+pdfjs-invalid-file-error = Kartelë PDF e pavlefshme ose e dëmtuar.
+pdfjs-missing-file-error = Kartelë PDF që mungon.
+pdfjs-unexpected-response-error = Përgjigje shërbyesi e papritur.
+pdfjs-rendering-error = Ndodhi një gabim gjatë riprodhimit të faqes.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Nënvizim { $type }]
+
+## Password
+
+pdfjs-password-label = Jepni fjalëkalimin që të hapet kjo kartelë PDF.
+pdfjs-password-invalid = Fjalëkalim i pavlefshëm. Ju lutemi, riprovoni.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Anuloje
+pdfjs-web-fonts-disabled = Shkronjat Web janë të çaktivizuara: s’arrihet të përdoren shkronja të trupëzuara në PDF.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Tekst
+pdfjs-editor-free-text-button-label = Tekst
+pdfjs-editor-ink-button =
+ .title = Vizatoni
+pdfjs-editor-ink-button-label = Vizatoni
+pdfjs-editor-stamp-button =
+ .title = Shtoni ose përpunoni figura
+pdfjs-editor-stamp-button-label = Shtoni ose përpunoni figura
+pdfjs-editor-highlight-button =
+ .title = Theksim
+pdfjs-editor-highlight-button-label = Theksoje
+pdfjs-highlight-floating-button =
+ .title = Theksim
+pdfjs-highlight-floating-button1 =
+ .title = Theksim
+ .aria-label = Theksim
+pdfjs-highlight-floating-button-label = Theksim
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Hiq vizatim
+pdfjs-editor-remove-freetext-button =
+ .title = Hiq tekst
+pdfjs-editor-remove-stamp-button =
+ .title = Hiq figurë
+pdfjs-editor-remove-highlight-button =
+ .title = Hiqe theksimin
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Ngjyrë
+pdfjs-editor-free-text-size-input = Madhësi
+pdfjs-editor-ink-color-input = Ngjyrë
+pdfjs-editor-ink-thickness-input = Trashësi
+pdfjs-editor-ink-opacity-input = Patejdukshmëri
+pdfjs-editor-stamp-add-image-button =
+ .title = Shtoni figurë
+pdfjs-editor-stamp-add-image-button-label = Shtoni figurë
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Trashësi
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Ndryshoni trashësinë kur theksoni objekte tjetër nga tekst
+pdfjs-free-text =
+ .aria-label = Përpunues Tekstesh
+pdfjs-free-text-default-content = Filloni të shtypni…
+pdfjs-ink =
+ .aria-label = Përpunues Vizatimesh
+pdfjs-ink-canvas =
+ .aria-label = Figurë e krijuar nga përdoruesi
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Tekst alternativ
+pdfjs-editor-alt-text-edit-button-label = Përpunoni tekst alternativ
+pdfjs-editor-alt-text-dialog-label = Zgjidhni një mundësi
+pdfjs-editor-alt-text-dialog-description = Teksti alt (tekst alternativ) vjen në ndihmë kur njerëzit s’mund të shohin figurën, ose kur ajo nuk ngarkohet.
+pdfjs-editor-alt-text-add-description-label = Shtoni një përshkrim
+pdfjs-editor-alt-text-add-description-description = Synoni për 1-2 togfjalësha që përshkruajnë subjektin, rrethanat apo veprimet.
+pdfjs-editor-alt-text-mark-decorative-label = Vëri shenjë si dekorative
+pdfjs-editor-alt-text-mark-decorative-description = Kjo përdoret për figura zbukuruese, fjala vjen, anë, ose watermark-e.
+pdfjs-editor-alt-text-cancel-button = Anuloje
+pdfjs-editor-alt-text-save-button = Ruaje
+pdfjs-editor-alt-text-decorative-tooltip = Iu vu shenjë si dekorative
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Për shembull, “Një djalosh ulet në një tryezë të hajë”
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Cepi i sipërm majtas — ripërmasojeni
+pdfjs-editor-resizer-label-top-middle = Mesi i pjesës sipër — ripërmasojeni
+pdfjs-editor-resizer-label-top-right = Cepi i sipërm djathtas — ripërmasojeni
+pdfjs-editor-resizer-label-middle-right = Djathtas në mes — ripërmasojeni
+pdfjs-editor-resizer-label-bottom-right = Cepi i poshtëm djathtas — ripërmasojeni
+pdfjs-editor-resizer-label-bottom-middle = Mesi i pjesës poshtë — ripërmasojeni
+pdfjs-editor-resizer-label-bottom-left = Cepi i poshtëm — ripërmasojeni
+pdfjs-editor-resizer-label-middle-left = Majtas në mes — ripërmasojeni
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Ngjyrë theksimi
+pdfjs-editor-colorpicker-button =
+ .title = Ndryshoni ngjyrë
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Zgjedhje ngjyre
+pdfjs-editor-colorpicker-yellow =
+ .title = E verdhë
+pdfjs-editor-colorpicker-green =
+ .title = E gjelbër
+pdfjs-editor-colorpicker-blue =
+ .title = Blu
+pdfjs-editor-colorpicker-pink =
+ .title = Rozë
+pdfjs-editor-colorpicker-red =
+ .title = E kuqe
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Shfaqi krejt
+pdfjs-editor-highlight-show-all-button =
+ .title = Shfaqi krejt
diff --git a/web/locale/sr/viewer.ftl b/web/locale/sr/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..c678d491730a00cb8e3b6b4f7a789b5acd101314
--- /dev/null
+++ b/web/locale/sr/viewer.ftl
@@ -0,0 +1,299 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Претходна страница
+pdfjs-previous-button-label = Претходна
+pdfjs-next-button =
+ .title = Следећа страница
+pdfjs-next-button-label = Следећа
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Страница
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = од { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } од { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Умањи
+pdfjs-zoom-out-button-label = Умањи
+pdfjs-zoom-in-button =
+ .title = Увеличај
+pdfjs-zoom-in-button-label = Увеличај
+pdfjs-zoom-select =
+ .title = Увеличавање
+pdfjs-presentation-mode-button =
+ .title = Промени на приказ у режиму презентације
+pdfjs-presentation-mode-button-label = Режим презентације
+pdfjs-open-file-button =
+ .title = Отвори датотеку
+pdfjs-open-file-button-label = Отвори
+pdfjs-print-button =
+ .title = Штампај
+pdfjs-print-button-label = Штампај
+pdfjs-save-button =
+ .title = Сачувај
+pdfjs-save-button-label = Сачувај
+pdfjs-bookmark-button =
+ .title = Тренутна страница (погледајте URL са тренутне странице)
+pdfjs-bookmark-button-label = Тренутна страница
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Отвори у апликацији
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Отвори у апликацији
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Алатке
+pdfjs-tools-button-label = Алатке
+pdfjs-first-page-button =
+ .title = Иди на прву страницу
+pdfjs-first-page-button-label = Иди на прву страницу
+pdfjs-last-page-button =
+ .title = Иди на последњу страницу
+pdfjs-last-page-button-label = Иди на последњу страницу
+pdfjs-page-rotate-cw-button =
+ .title = Ротирај у смеру казаљке на сату
+pdfjs-page-rotate-cw-button-label = Ротирај у смеру казаљке на сату
+pdfjs-page-rotate-ccw-button =
+ .title = Ротирај у смеру супротном од казаљке на сату
+pdfjs-page-rotate-ccw-button-label = Ротирај у смеру супротном од казаљке на сату
+pdfjs-cursor-text-select-tool-button =
+ .title = Омогући алат за селектовање текста
+pdfjs-cursor-text-select-tool-button-label = Алат за селектовање текста
+pdfjs-cursor-hand-tool-button =
+ .title = Омогући алат за померање
+pdfjs-cursor-hand-tool-button-label = Алат за померање
+pdfjs-scroll-page-button =
+ .title = Користи скроловање по омоту
+pdfjs-scroll-page-button-label = Скроловање странице
+pdfjs-scroll-vertical-button =
+ .title = Користи вертикално скроловање
+pdfjs-scroll-vertical-button-label = Вертикално скроловање
+pdfjs-scroll-horizontal-button =
+ .title = Користи хоризонтално скроловање
+pdfjs-scroll-horizontal-button-label = Хоризонтално скроловање
+pdfjs-scroll-wrapped-button =
+ .title = Користи скроловање по омоту
+pdfjs-scroll-wrapped-button-label = Скроловање по омоту
+pdfjs-spread-none-button =
+ .title = Немој спајати ширења страница
+pdfjs-spread-none-button-label = Без распростирања
+pdfjs-spread-odd-button =
+ .title = Споји ширења страница које почињу непарним бројем
+pdfjs-spread-odd-button-label = Непарна распростирања
+pdfjs-spread-even-button =
+ .title = Споји ширења страница које почињу парним бројем
+pdfjs-spread-even-button-label = Парна распростирања
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Параметри документа…
+pdfjs-document-properties-button-label = Параметри документа…
+pdfjs-document-properties-file-name = Име датотеке:
+pdfjs-document-properties-file-size = Величина датотеке:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } B)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } B)
+pdfjs-document-properties-title = Наслов:
+pdfjs-document-properties-author = Аутор:
+pdfjs-document-properties-subject = Тема:
+pdfjs-document-properties-keywords = Кључне речи:
+pdfjs-document-properties-creation-date = Датум креирања:
+pdfjs-document-properties-modification-date = Датум модификације:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Стваралац:
+pdfjs-document-properties-producer = PDF произвођач:
+pdfjs-document-properties-version = PDF верзија:
+pdfjs-document-properties-page-count = Број страница:
+pdfjs-document-properties-page-size = Величина странице:
+pdfjs-document-properties-page-size-unit-inches = ин
+pdfjs-document-properties-page-size-unit-millimeters = мм
+pdfjs-document-properties-page-size-orientation-portrait = усправно
+pdfjs-document-properties-page-size-orientation-landscape = водоравно
+pdfjs-document-properties-page-size-name-a-three = А3
+pdfjs-document-properties-page-size-name-a-four = А4
+pdfjs-document-properties-page-size-name-letter = Слово
+pdfjs-document-properties-page-size-name-legal = Права
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Брз веб приказ:
+pdfjs-document-properties-linearized-yes = Да
+pdfjs-document-properties-linearized-no = Не
+pdfjs-document-properties-close-button = Затвори
+
+## Print
+
+pdfjs-print-progress-message = Припремам документ за штампање…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Откажи
+pdfjs-printing-not-supported = Упозорење: Штампање није у потпуности подржано у овом прегледачу.
+pdfjs-printing-not-ready = Упозорење: PDF није у потпуности учитан за штампу.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Прикажи додатну палету
+pdfjs-toggle-sidebar-notification-button =
+ .title = Прикажи/сакриј бочну траку (документ садржи контуру/прилоге/слојеве)
+pdfjs-toggle-sidebar-button-label = Прикажи додатну палету
+pdfjs-document-outline-button =
+ .title = Прикажи структуру документа (двоструким кликом проширујете/скупљате све ставке)
+pdfjs-document-outline-button-label = Контура документа
+pdfjs-attachments-button =
+ .title = Прикажи прилоге
+pdfjs-attachments-button-label = Прилози
+pdfjs-layers-button =
+ .title = Прикажи слојеве (дупли клик за враћање свих слојева у подразумевано стање)
+pdfjs-layers-button-label = Слојеви
+pdfjs-thumbs-button =
+ .title = Прикажи сличице
+pdfjs-thumbs-button-label = Сличице
+pdfjs-current-outline-item-button =
+ .title = Пронађите тренутни елемент структуре
+pdfjs-current-outline-item-button-label = Тренутна контура
+pdfjs-findbar-button =
+ .title = Пронађи у документу
+pdfjs-findbar-button-label = Пронађи
+pdfjs-additional-layers = Додатни слојеви
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Страница { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Сличица од странице { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Пронађи
+ .placeholder = Пронађи у документу…
+pdfjs-find-previous-button =
+ .title = Пронађи претходно појављивање фразе
+pdfjs-find-previous-button-label = Претходна
+pdfjs-find-next-button =
+ .title = Пронађи следеће појављивање фразе
+pdfjs-find-next-button-label = Следећа
+pdfjs-find-highlight-checkbox = Истакнути све
+pdfjs-find-match-case-checkbox-label = Подударања
+pdfjs-find-match-diacritics-checkbox-label = Дијакритика
+pdfjs-find-entire-word-checkbox-label = Целе речи
+pdfjs-find-reached-top = Достигнут врх документа, наставио са дна
+pdfjs-find-reached-bottom = Достигнуто дно документа, наставио са врха
+pdfjs-find-not-found = Фраза није пронађена
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Ширина странице
+pdfjs-page-scale-fit = Прилагоди страницу
+pdfjs-page-scale-auto = Аутоматско увеличавање
+pdfjs-page-scale-actual = Стварна величина
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Страница { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Дошло је до грешке приликом учитавања PDF-а.
+pdfjs-invalid-file-error = PDF датотека је неважећа или је оштећена.
+pdfjs-missing-file-error = Недостаје PDF датотека.
+pdfjs-unexpected-response-error = Неочекиван одговор од сервера.
+pdfjs-rendering-error = Дошло је до грешке приликом рендеровања ове странице.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } коментар]
+
+## Password
+
+pdfjs-password-label = Унесите лозинку да бисте отворили овај PDF докуменат.
+pdfjs-password-invalid = Неисправна лозинка. Покушајте поново.
+pdfjs-password-ok-button = У реду
+pdfjs-password-cancel-button = Откажи
+pdfjs-web-fonts-disabled = Веб фонтови су онемогућени: не могу користити уграђене PDF фонтове.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Текст
+pdfjs-editor-free-text-button-label = Текст
+pdfjs-editor-ink-button =
+ .title = Цртај
+pdfjs-editor-ink-button-label = Цртај
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Боја
+pdfjs-editor-free-text-size-input = Величина
+pdfjs-editor-ink-color-input = Боја
+pdfjs-editor-ink-thickness-input = Дебљина
+pdfjs-editor-ink-opacity-input = Опацитет
+pdfjs-free-text =
+ .aria-label = Уређивач текста
+pdfjs-free-text-default-content = Почни куцање…
+pdfjs-ink =
+ .aria-label = Уређивач цртежа
+pdfjs-ink-canvas =
+ .aria-label = Кориснички направљена слика
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/sv-SE/viewer.ftl b/web/locale/sv-SE/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..61d69867dd2569001fed1e2fcb69e5c57a4a32e0
--- /dev/null
+++ b/web/locale/sv-SE/viewer.ftl
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Föregående sida
+pdfjs-previous-button-label = Föregående
+pdfjs-next-button =
+ .title = Nästa sida
+pdfjs-next-button-label = Nästa
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Sida
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = av { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } av { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zooma ut
+pdfjs-zoom-out-button-label = Zooma ut
+pdfjs-zoom-in-button =
+ .title = Zooma in
+pdfjs-zoom-in-button-label = Zooma in
+pdfjs-zoom-select =
+ .title = Zoom
+pdfjs-presentation-mode-button =
+ .title = Byt till presentationsläge
+pdfjs-presentation-mode-button-label = Presentationsläge
+pdfjs-open-file-button =
+ .title = Öppna fil
+pdfjs-open-file-button-label = Öppna
+pdfjs-print-button =
+ .title = Skriv ut
+pdfjs-print-button-label = Skriv ut
+pdfjs-save-button =
+ .title = Spara
+pdfjs-save-button-label = Spara
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Hämta
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Hämta
+pdfjs-bookmark-button =
+ .title = Aktuell sida (Visa URL från aktuell sida)
+pdfjs-bookmark-button-label = Aktuell sida
+# Used in Firefox for Android.
+pdfjs-open-in-app-button =
+ .title = Öppna i app
+# Used in Firefox for Android.
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-open-in-app-button-label = Öppna i app
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Verktyg
+pdfjs-tools-button-label = Verktyg
+pdfjs-first-page-button =
+ .title = Gå till första sidan
+pdfjs-first-page-button-label = Gå till första sidan
+pdfjs-last-page-button =
+ .title = Gå till sista sidan
+pdfjs-last-page-button-label = Gå till sista sidan
+pdfjs-page-rotate-cw-button =
+ .title = Rotera medurs
+pdfjs-page-rotate-cw-button-label = Rotera medurs
+pdfjs-page-rotate-ccw-button =
+ .title = Rotera moturs
+pdfjs-page-rotate-ccw-button-label = Rotera moturs
+pdfjs-cursor-text-select-tool-button =
+ .title = Aktivera textmarkeringsverktyg
+pdfjs-cursor-text-select-tool-button-label = Textmarkeringsverktyg
+pdfjs-cursor-hand-tool-button =
+ .title = Aktivera handverktyg
+pdfjs-cursor-hand-tool-button-label = Handverktyg
+pdfjs-scroll-page-button =
+ .title = Använd sidrullning
+pdfjs-scroll-page-button-label = Sidrullning
+pdfjs-scroll-vertical-button =
+ .title = Använd vertikal rullning
+pdfjs-scroll-vertical-button-label = Vertikal rullning
+pdfjs-scroll-horizontal-button =
+ .title = Använd horisontell rullning
+pdfjs-scroll-horizontal-button-label = Horisontell rullning
+pdfjs-scroll-wrapped-button =
+ .title = Använd överlappande rullning
+pdfjs-scroll-wrapped-button-label = Överlappande rullning
+pdfjs-spread-none-button =
+ .title = Visa enkelsidor
+pdfjs-spread-none-button-label = Enkelsidor
+pdfjs-spread-odd-button =
+ .title = Visa uppslag med olika sidnummer till vänster
+pdfjs-spread-odd-button-label = Uppslag med framsida
+pdfjs-spread-even-button =
+ .title = Visa uppslag med lika sidnummer till vänster
+pdfjs-spread-even-button-label = Uppslag utan framsida
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Dokumentegenskaper…
+pdfjs-document-properties-button-label = Dokumentegenskaper…
+pdfjs-document-properties-file-name = Filnamn:
+pdfjs-document-properties-file-size = Filstorlek:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } kB ({ $size_b } byte)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } byte)
+pdfjs-document-properties-title = Titel:
+pdfjs-document-properties-author = Författare:
+pdfjs-document-properties-subject = Ämne:
+pdfjs-document-properties-keywords = Nyckelord:
+pdfjs-document-properties-creation-date = Skapades:
+pdfjs-document-properties-modification-date = Ändrades:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Skapare:
+pdfjs-document-properties-producer = PDF-producent:
+pdfjs-document-properties-version = PDF-version:
+pdfjs-document-properties-page-count = Sidantal:
+pdfjs-document-properties-page-size = Pappersstorlek:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = porträtt
+pdfjs-document-properties-page-size-orientation-landscape = landskap
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Snabb webbvisning:
+pdfjs-document-properties-linearized-yes = Ja
+pdfjs-document-properties-linearized-no = Nej
+pdfjs-document-properties-close-button = Stäng
+
+## Print
+
+pdfjs-print-progress-message = Förbereder sidor för utskrift…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Avbryt
+pdfjs-printing-not-supported = Varning: Utskrifter stöds inte helt av den här webbläsaren.
+pdfjs-printing-not-ready = Varning: PDF:en är inte klar för utskrift.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Visa/dölj sidofält
+pdfjs-toggle-sidebar-notification-button =
+ .title = Växla sidofält (dokumentet innehåller dokumentstruktur/bilagor/lager)
+pdfjs-toggle-sidebar-button-label = Visa/dölj sidofält
+pdfjs-document-outline-button =
+ .title = Visa dokumentdisposition (dubbelklicka för att expandera/komprimera alla objekt)
+pdfjs-document-outline-button-label = Dokumentöversikt
+pdfjs-attachments-button =
+ .title = Visa Bilagor
+pdfjs-attachments-button-label = Bilagor
+pdfjs-layers-button =
+ .title = Visa lager (dubbelklicka för att återställa alla lager till standardläge)
+pdfjs-layers-button-label = Lager
+pdfjs-thumbs-button =
+ .title = Visa miniatyrer
+pdfjs-thumbs-button-label = Miniatyrer
+pdfjs-current-outline-item-button =
+ .title = Hitta aktuellt dispositionsobjekt
+pdfjs-current-outline-item-button-label = Aktuellt dispositionsobjekt
+pdfjs-findbar-button =
+ .title = Sök i dokument
+pdfjs-findbar-button-label = Sök
+pdfjs-additional-layers = Ytterligare lager
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Sida { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatyr av sida { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Sök
+ .placeholder = Sök i dokument…
+pdfjs-find-previous-button =
+ .title = Hitta föregående förekomst av frasen
+pdfjs-find-previous-button-label = Föregående
+pdfjs-find-next-button =
+ .title = Hitta nästa förekomst av frasen
+pdfjs-find-next-button-label = Nästa
+pdfjs-find-highlight-checkbox = Markera alla
+pdfjs-find-match-case-checkbox-label = Matcha versal/gemen
+pdfjs-find-match-diacritics-checkbox-label = Matcha diakritiska tecken
+pdfjs-find-entire-word-checkbox-label = Hela ord
+pdfjs-find-reached-top = Nådde början av dokumentet, började från slutet
+pdfjs-find-reached-bottom = Nådde slutet på dokumentet, började från början
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } av { $total } match
+ *[other] { $current } av { $total } matchningar
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Mer än { $limit } matchning
+ *[other] Fler än { $limit } matchningar
+ }
+pdfjs-find-not-found = Frasen hittades inte
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Sidbredd
+pdfjs-page-scale-fit = Anpassa sida
+pdfjs-page-scale-auto = Automatisk zoom
+pdfjs-page-scale-actual = Verklig storlek
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Sida { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ett fel uppstod vid laddning av PDF-filen.
+pdfjs-invalid-file-error = Ogiltig eller korrupt PDF-fil.
+pdfjs-missing-file-error = Saknad PDF-fil.
+pdfjs-unexpected-response-error = Oväntat svar från servern.
+pdfjs-rendering-error = Ett fel uppstod vid visning av sidan.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date } { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type }-annotering]
+
+## Password
+
+pdfjs-password-label = Skriv in lösenordet för att öppna PDF-filen.
+pdfjs-password-invalid = Ogiltigt lösenord. Försök igen.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Avbryt
+pdfjs-web-fonts-disabled = Webbtypsnitt är inaktiverade: kan inte använda inbäddade PDF-typsnitt.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Text
+pdfjs-editor-free-text-button-label = Text
+pdfjs-editor-ink-button =
+ .title = Rita
+pdfjs-editor-ink-button-label = Rita
+pdfjs-editor-stamp-button =
+ .title = Lägg till eller redigera bilder
+pdfjs-editor-stamp-button-label = Lägg till eller redigera bilder
+pdfjs-editor-highlight-button =
+ .title = Markera
+pdfjs-editor-highlight-button-label = Markera
+pdfjs-highlight-floating-button =
+ .title = Markera
+pdfjs-highlight-floating-button1 =
+ .title = Markera
+ .aria-label = Markera
+pdfjs-highlight-floating-button-label = Markera
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Ta bort ritning
+pdfjs-editor-remove-freetext-button =
+ .title = Ta bort text
+pdfjs-editor-remove-stamp-button =
+ .title = Ta bort bild
+pdfjs-editor-remove-highlight-button =
+ .title = Ta bort markering
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Färg
+pdfjs-editor-free-text-size-input = Storlek
+pdfjs-editor-ink-color-input = Färg
+pdfjs-editor-ink-thickness-input = Tjocklek
+pdfjs-editor-ink-opacity-input = Opacitet
+pdfjs-editor-stamp-add-image-button =
+ .title = Lägg till bild
+pdfjs-editor-stamp-add-image-button-label = Lägg till bild
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Tjocklek
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Ändra tjocklek när du markerar andra objekt än text
+pdfjs-free-text =
+ .aria-label = Textredigerare
+pdfjs-free-text-default-content = Börja skriva…
+pdfjs-ink =
+ .aria-label = Ritredigerare
+pdfjs-ink-canvas =
+ .aria-label = Användarskapad bild
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Alternativ text
+pdfjs-editor-alt-text-edit-button-label = Redigera alternativ text
+pdfjs-editor-alt-text-dialog-label = Välj ett alternativ
+pdfjs-editor-alt-text-dialog-description = Alt text (alternativ text) hjälper till när människor inte kan se bilden eller när den inte laddas.
+pdfjs-editor-alt-text-add-description-label = Lägg till en beskrivning
+pdfjs-editor-alt-text-add-description-description = Sikta på 1-2 meningar som beskriver ämnet, miljön eller handlingen.
+pdfjs-editor-alt-text-mark-decorative-label = Markera som dekorativ
+pdfjs-editor-alt-text-mark-decorative-description = Detta används för dekorativa bilder, som kanter eller vattenstämplar.
+pdfjs-editor-alt-text-cancel-button = Avbryt
+pdfjs-editor-alt-text-save-button = Spara
+pdfjs-editor-alt-text-decorative-tooltip = Märkt som dekorativ
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Till exempel, "En ung man sätter sig vid ett bord för att äta en måltid"
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Det övre vänstra hörnet — ändra storlek
+pdfjs-editor-resizer-label-top-middle = Överst i mitten — ändra storlek
+pdfjs-editor-resizer-label-top-right = Det övre högra hörnet — ändra storlek
+pdfjs-editor-resizer-label-middle-right = Mitten höger — ändra storlek
+pdfjs-editor-resizer-label-bottom-right = Nedre högra hörnet — ändra storlek
+pdfjs-editor-resizer-label-bottom-middle = Nedre mitten — ändra storlek
+pdfjs-editor-resizer-label-bottom-left = Nedre vänstra hörnet — ändra storlek
+pdfjs-editor-resizer-label-middle-left = Mitten till vänster — ändra storlek
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Markeringsfärg
+pdfjs-editor-colorpicker-button =
+ .title = Ändra färg
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Färgval
+pdfjs-editor-colorpicker-yellow =
+ .title = Gul
+pdfjs-editor-colorpicker-green =
+ .title = Grön
+pdfjs-editor-colorpicker-blue =
+ .title = Blå
+pdfjs-editor-colorpicker-pink =
+ .title = Rosa
+pdfjs-editor-colorpicker-red =
+ .title = Röd
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Visa alla
+pdfjs-editor-highlight-show-all-button =
+ .title = Visa alla
diff --git a/web/locale/szl/viewer.ftl b/web/locale/szl/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..cbf166e452569b704ec42976bebf28a72804dac2
--- /dev/null
+++ b/web/locale/szl/viewer.ftl
@@ -0,0 +1,257 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Piyrwyjszo strōna
+pdfjs-previous-button-label = Piyrwyjszo
+pdfjs-next-button =
+ .title = Nastympno strōna
+pdfjs-next-button-label = Dalij
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Strōna
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = ze { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } ze { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Zmyńsz
+pdfjs-zoom-out-button-label = Zmyńsz
+pdfjs-zoom-in-button =
+ .title = Zwiynksz
+pdfjs-zoom-in-button-label = Zwiynksz
+pdfjs-zoom-select =
+ .title = Srogość
+pdfjs-presentation-mode-button =
+ .title = Przełōncz na tryb prezyntacyje
+pdfjs-presentation-mode-button-label = Tryb prezyntacyje
+pdfjs-open-file-button =
+ .title = Ôdewrzij zbiōr
+pdfjs-open-file-button-label = Ôdewrzij
+pdfjs-print-button =
+ .title = Durkuj
+pdfjs-print-button-label = Durkuj
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Noczynia
+pdfjs-tools-button-label = Noczynia
+pdfjs-first-page-button =
+ .title = Idź ku piyrszyj strōnie
+pdfjs-first-page-button-label = Idź ku piyrszyj strōnie
+pdfjs-last-page-button =
+ .title = Idź ku ôstatnij strōnie
+pdfjs-last-page-button-label = Idź ku ôstatnij strōnie
+pdfjs-page-rotate-cw-button =
+ .title = Zwyrtnij w prawo
+pdfjs-page-rotate-cw-button-label = Zwyrtnij w prawo
+pdfjs-page-rotate-ccw-button =
+ .title = Zwyrtnij w lewo
+pdfjs-page-rotate-ccw-button-label = Zwyrtnij w lewo
+pdfjs-cursor-text-select-tool-button =
+ .title = Załōncz noczynie ôbiyranio tekstu
+pdfjs-cursor-text-select-tool-button-label = Noczynie ôbiyranio tekstu
+pdfjs-cursor-hand-tool-button =
+ .title = Załōncz noczynie rōnczka
+pdfjs-cursor-hand-tool-button-label = Noczynie rōnczka
+pdfjs-scroll-vertical-button =
+ .title = Używej piōnowego przewijanio
+pdfjs-scroll-vertical-button-label = Piōnowe przewijanie
+pdfjs-scroll-horizontal-button =
+ .title = Używej poziōmego przewijanio
+pdfjs-scroll-horizontal-button-label = Poziōme przewijanie
+pdfjs-scroll-wrapped-button =
+ .title = Używej szichtowego przewijanio
+pdfjs-scroll-wrapped-button-label = Szichtowe przewijanie
+pdfjs-spread-none-button =
+ .title = Niy dowej strōn w widoku po dwie
+pdfjs-spread-none-button-label = Po jednyj strōnie
+pdfjs-spread-odd-button =
+ .title = Pokoż strōny po dwie; niyporziste po lewyj
+pdfjs-spread-odd-button-label = Niyporziste po lewyj
+pdfjs-spread-even-button =
+ .title = Pokoż strōny po dwie; porziste po lewyj
+pdfjs-spread-even-button-label = Porziste po lewyj
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Włosności dokumyntu…
+pdfjs-document-properties-button-label = Włosności dokumyntu…
+pdfjs-document-properties-file-name = Miano zbioru:
+pdfjs-document-properties-file-size = Srogość zbioru:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } B)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } B)
+pdfjs-document-properties-title = Tytuł:
+pdfjs-document-properties-author = Autōr:
+pdfjs-document-properties-subject = Tymat:
+pdfjs-document-properties-keywords = Kluczowe słowa:
+pdfjs-document-properties-creation-date = Data zrychtowanio:
+pdfjs-document-properties-modification-date = Data zmiany:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Zrychtowane ôd:
+pdfjs-document-properties-producer = PDF ôd:
+pdfjs-document-properties-version = Wersyjo PDF:
+pdfjs-document-properties-page-count = Wielość strōn:
+pdfjs-document-properties-page-size = Srogość strōny:
+pdfjs-document-properties-page-size-unit-inches = in
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = piōnowo
+pdfjs-document-properties-page-size-orientation-landscape = poziōmo
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Letter
+pdfjs-document-properties-page-size-name-legal = Legal
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Gibki necowy podglōnd:
+pdfjs-document-properties-linearized-yes = Ja
+pdfjs-document-properties-linearized-no = Niy
+pdfjs-document-properties-close-button = Zawrzij
+
+## Print
+
+pdfjs-print-progress-message = Rychtowanie dokumyntu do durku…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Pociep
+pdfjs-printing-not-supported = Pozōr: Ta przeglōndarka niy cołkiym ôbsuguje durk.
+pdfjs-printing-not-ready = Pozōr: Tyn PDF niy ma za tela zaladowany do durku.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Przełōncz posek na rancie
+pdfjs-toggle-sidebar-notification-button =
+ .title = Przełōncz posek na rancie (dokumynt mo struktura/przidowki/warstwy)
+pdfjs-toggle-sidebar-button-label = Przełōncz posek na rancie
+pdfjs-document-outline-button =
+ .title = Pokoż struktura dokumyntu (tuplowane klikniyncie rozszyrzo/swijo wszyskie elymynta)
+pdfjs-document-outline-button-label = Struktura dokumyntu
+pdfjs-attachments-button =
+ .title = Pokoż przidowki
+pdfjs-attachments-button-label = Przidowki
+pdfjs-layers-button =
+ .title = Pokoż warstwy (tuplowane klikniyncie resetuje wszyskie warstwy do bazowego stanu)
+pdfjs-layers-button-label = Warstwy
+pdfjs-thumbs-button =
+ .title = Pokoż miniatury
+pdfjs-thumbs-button-label = Miniatury
+pdfjs-findbar-button =
+ .title = Znojdź w dokumyncie
+pdfjs-findbar-button-label = Znojdź
+pdfjs-additional-layers = Nadbytnie warstwy
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Strōna { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Miniatura strōny { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Znojdź
+ .placeholder = Znojdź w dokumyncie…
+pdfjs-find-previous-button =
+ .title = Znojdź piyrwyjsze pokozanie sie tyj frazy
+pdfjs-find-previous-button-label = Piyrwyjszo
+pdfjs-find-next-button =
+ .title = Znojdź nastympne pokozanie sie tyj frazy
+pdfjs-find-next-button-label = Dalij
+pdfjs-find-highlight-checkbox = Zaznacz wszysko
+pdfjs-find-match-case-checkbox-label = Poznowej srogość liter
+pdfjs-find-entire-word-checkbox-label = Cołke słowa
+pdfjs-find-reached-top = Doszło do samego wiyrchu strōny, dalij ôd spodku
+pdfjs-find-reached-bottom = Doszło do samego spodku strōny, dalij ôd wiyrchu
+pdfjs-find-not-found = Fraza niy znaleziōno
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Szyrzka strōny
+pdfjs-page-scale-fit = Napasowanie strōny
+pdfjs-page-scale-auto = Autōmatyczno srogość
+pdfjs-page-scale-actual = Aktualno srogość
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = Przi ladowaniu PDFa pokozoł sie feler.
+pdfjs-invalid-file-error = Zły abo felerny zbiōr PDF.
+pdfjs-missing-file-error = Chybio zbioru PDF.
+pdfjs-unexpected-response-error = Niyôczekowano ôdpowiydź serwera.
+pdfjs-rendering-error = Przi renderowaniu strōny pokozoł sie feler.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Anotacyjo typu { $type }]
+
+## Password
+
+pdfjs-password-label = Wkludź hasło, coby ôdewrzić tyn zbiōr PDF.
+pdfjs-password-invalid = Hasło je złe. Sprōbuj jeszcze roz.
+pdfjs-password-ok-button = OK
+pdfjs-password-cancel-button = Pociep
+pdfjs-web-fonts-disabled = Necowe fōnty sōm zastawiōne: niy idzie użyć wkludzōnych fōntōw PDF.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/ta/viewer.ftl b/web/locale/ta/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..82cf19703753c0759e13c0cdee7cb06c5ac4e901
--- /dev/null
+++ b/web/locale/ta/viewer.ftl
@@ -0,0 +1,223 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = முந்தைய பக்கம்
+pdfjs-previous-button-label = முந்தையது
+pdfjs-next-button =
+ .title = அடுத்த பக்கம்
+pdfjs-next-button-label = அடுத்து
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = பக்கம்
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = { $pagesCount } இல்
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = { $pagesCount }) இல் ({ $pageNumber }
+pdfjs-zoom-out-button =
+ .title = சிறிதாக்கு
+pdfjs-zoom-out-button-label = சிறிதாக்கு
+pdfjs-zoom-in-button =
+ .title = பெரிதாக்கு
+pdfjs-zoom-in-button-label = பெரிதாக்கு
+pdfjs-zoom-select =
+ .title = பெரிதாக்கு
+pdfjs-presentation-mode-button =
+ .title = விளக்ககாட்சி பயன்முறைக்கு மாறு
+pdfjs-presentation-mode-button-label = விளக்ககாட்சி பயன்முறை
+pdfjs-open-file-button =
+ .title = கோப்பினை திற
+pdfjs-open-file-button-label = திற
+pdfjs-print-button =
+ .title = அச்சிடு
+pdfjs-print-button-label = அச்சிடு
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = கருவிகள்
+pdfjs-tools-button-label = கருவிகள்
+pdfjs-first-page-button =
+ .title = முதல் பக்கத்திற்கு செல்லவும்
+pdfjs-first-page-button-label = முதல் பக்கத்திற்கு செல்லவும்
+pdfjs-last-page-button =
+ .title = கடைசி பக்கத்திற்கு செல்லவும்
+pdfjs-last-page-button-label = கடைசி பக்கத்திற்கு செல்லவும்
+pdfjs-page-rotate-cw-button =
+ .title = வலஞ்சுழியாக சுழற்று
+pdfjs-page-rotate-cw-button-label = வலஞ்சுழியாக சுழற்று
+pdfjs-page-rotate-ccw-button =
+ .title = இடஞ்சுழியாக சுழற்று
+pdfjs-page-rotate-ccw-button-label = இடஞ்சுழியாக சுழற்று
+pdfjs-cursor-text-select-tool-button =
+ .title = உரைத் தெரிவு கருவியைச் செயல்படுத்து
+pdfjs-cursor-text-select-tool-button-label = உரைத் தெரிவு கருவி
+pdfjs-cursor-hand-tool-button =
+ .title = கைக் கருவிக்ச் செயற்படுத்து
+pdfjs-cursor-hand-tool-button-label = கைக்குருவி
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = ஆவண பண்புகள்...
+pdfjs-document-properties-button-label = ஆவண பண்புகள்...
+pdfjs-document-properties-file-name = கோப்பு பெயர்:
+pdfjs-document-properties-file-size = கோப்பின் அளவு:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } கிபை ({ $size_b } பைட்டுகள்)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } மெபை ({ $size_b } பைட்டுகள்)
+pdfjs-document-properties-title = தலைப்பு:
+pdfjs-document-properties-author = எழுதியவர்
+pdfjs-document-properties-subject = பொருள்:
+pdfjs-document-properties-keywords = முக்கிய வார்த்தைகள்:
+pdfjs-document-properties-creation-date = படைத்த தேதி :
+pdfjs-document-properties-modification-date = திருத்திய தேதி:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = உருவாக்குபவர்:
+pdfjs-document-properties-producer = பிடிஎஃப் தயாரிப்பாளர்:
+pdfjs-document-properties-version = PDF பதிப்பு:
+pdfjs-document-properties-page-count = பக்க எண்ணிக்கை:
+pdfjs-document-properties-page-size = பக்க அளவு:
+pdfjs-document-properties-page-size-unit-inches = இதில்
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = நிலைபதிப்பு
+pdfjs-document-properties-page-size-orientation-landscape = நிலைபரப்பு
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = கடிதம்
+pdfjs-document-properties-page-size-name-legal = சட்டபூர்வ
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+pdfjs-document-properties-close-button = மூடுக
+
+## Print
+
+pdfjs-print-progress-message = அச்சிடுவதற்கான ஆவணம் தயாராகிறது...
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = ரத்து
+pdfjs-printing-not-supported = எச்சரிக்கை: இந்த உலாவி அச்சிடுதலை முழுமையாக ஆதரிக்கவில்லை.
+pdfjs-printing-not-ready = எச்சரிக்கை: PDF அச்சிட முழுவதுமாக ஏற்றப்படவில்லை.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = பக்கப் பட்டியை நிலைமாற்று
+pdfjs-toggle-sidebar-button-label = பக்கப் பட்டியை நிலைமாற்று
+pdfjs-document-outline-button =
+ .title = ஆவண அடக்கத்தைக் காட்டு (இருமுறைச் சொடுக்கி அனைத்து உறுப்பிடிகளையும் விரி/சேர்)
+pdfjs-document-outline-button-label = ஆவண வெளிவரை
+pdfjs-attachments-button =
+ .title = இணைப்புகளை காண்பி
+pdfjs-attachments-button-label = இணைப்புகள்
+pdfjs-thumbs-button =
+ .title = சிறுபடங்களைக் காண்பி
+pdfjs-thumbs-button-label = சிறுபடங்கள்
+pdfjs-findbar-button =
+ .title = ஆவணத்தில் கண்டறி
+pdfjs-findbar-button-label = தேடு
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = பக்கம் { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = பக்கத்தின் சிறுபடம் { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = கண்டுபிடி
+ .placeholder = ஆவணத்தில் கண்டறி…
+pdfjs-find-previous-button =
+ .title = இந்த சொற்றொடரின் முந்தைய நிகழ்வை தேடு
+pdfjs-find-previous-button-label = முந்தையது
+pdfjs-find-next-button =
+ .title = இந்த சொற்றொடரின் அடுத்த நிகழ்வை தேடு
+pdfjs-find-next-button-label = அடுத்து
+pdfjs-find-highlight-checkbox = அனைத்தையும் தனிப்படுத்து
+pdfjs-find-match-case-checkbox-label = பேரெழுத்தாக்கத்தை உணர்
+pdfjs-find-reached-top = ஆவணத்தின் மேல் பகுதியை அடைந்தது, அடிப்பக்கத்திலிருந்து தொடர்ந்தது
+pdfjs-find-reached-bottom = ஆவணத்தின் முடிவை அடைந்தது, மேலிருந்து தொடர்ந்தது
+pdfjs-find-not-found = சொற்றொடர் காணவில்லை
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = பக்க அகலம்
+pdfjs-page-scale-fit = பக்கப் பொருத்தம்
+pdfjs-page-scale-auto = தானியக்க பெரிதாக்கல்
+pdfjs-page-scale-actual = உண்மையான அளவு
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF ஐ ஏற்றும் போது ஒரு பிழை ஏற்பட்டது.
+pdfjs-invalid-file-error = செல்லுபடியாகாத அல்லது சிதைந்த PDF கோப்பு.
+pdfjs-missing-file-error = PDF கோப்பு காணவில்லை.
+pdfjs-unexpected-response-error = சேவகன் பதில் எதிர்பாரதது.
+pdfjs-rendering-error = இந்தப் பக்கத்தை காட்சிப்படுத்தும் போது ஒரு பிழை ஏற்பட்டது.
+
+## Annotations
+
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } விளக்கம்]
+
+## Password
+
+pdfjs-password-label = இந்த PDF கோப்பை திறக்க கடவுச்சொல்லை உள்ளிடவும்.
+pdfjs-password-invalid = செல்லுபடியாகாத கடவுச்சொல், தயை செய்து மீண்டும் முயற்சி செய்க.
+pdfjs-password-ok-button = சரி
+pdfjs-password-cancel-button = ரத்து
+pdfjs-web-fonts-disabled = வலை எழுத்துருக்கள் முடக்கப்பட்டுள்ளன: உட்பொதிக்கப்பட்ட PDF எழுத்துருக்களைப் பயன்படுத்த முடியவில்லை.
+
+## Editing
+
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/te/viewer.ftl b/web/locale/te/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..94dc2b8eef22da8fb849e1d1500696e4b2181616
--- /dev/null
+++ b/web/locale/te/viewer.ftl
@@ -0,0 +1,239 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = మునుపటి పేజీ
+pdfjs-previous-button-label = క్రితం
+pdfjs-next-button =
+ .title = తరువాత పేజీ
+pdfjs-next-button-label = తరువాత
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = పేజీ
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = మొత్తం { $pagesCount } లో
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = (మొత్తం { $pagesCount } లో { $pageNumber }వది)
+pdfjs-zoom-out-button =
+ .title = జూమ్ తగ్గించు
+pdfjs-zoom-out-button-label = జూమ్ తగ్గించు
+pdfjs-zoom-in-button =
+ .title = జూమ్ చేయి
+pdfjs-zoom-in-button-label = జూమ్ చేయి
+pdfjs-zoom-select =
+ .title = జూమ్
+pdfjs-presentation-mode-button =
+ .title = ప్రదర్శనా రీతికి మారు
+pdfjs-presentation-mode-button-label = ప్రదర్శనా రీతి
+pdfjs-open-file-button =
+ .title = ఫైల్ తెరువు
+pdfjs-open-file-button-label = తెరువు
+pdfjs-print-button =
+ .title = ముద్రించు
+pdfjs-print-button-label = ముద్రించు
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = పనిముట్లు
+pdfjs-tools-button-label = పనిముట్లు
+pdfjs-first-page-button =
+ .title = మొదటి పేజీకి వెళ్ళు
+pdfjs-first-page-button-label = మొదటి పేజీకి వెళ్ళు
+pdfjs-last-page-button =
+ .title = చివరి పేజీకి వెళ్ళు
+pdfjs-last-page-button-label = చివరి పేజీకి వెళ్ళు
+pdfjs-page-rotate-cw-button =
+ .title = సవ్యదిశలో తిప్పు
+pdfjs-page-rotate-cw-button-label = సవ్యదిశలో తిప్పు
+pdfjs-page-rotate-ccw-button =
+ .title = అపసవ్యదిశలో తిప్పు
+pdfjs-page-rotate-ccw-button-label = అపసవ్యదిశలో తిప్పు
+pdfjs-cursor-text-select-tool-button =
+ .title = టెక్స్ట్ ఎంపిక సాధనాన్ని ప్రారంభించండి
+pdfjs-cursor-text-select-tool-button-label = టెక్స్ట్ ఎంపిక సాధనం
+pdfjs-cursor-hand-tool-button =
+ .title = చేతి సాధనం చేతనించు
+pdfjs-cursor-hand-tool-button-label = చేతి సాధనం
+pdfjs-scroll-vertical-button-label = నిలువు స్క్రోలింగు
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = పత్రము లక్షణాలు...
+pdfjs-document-properties-button-label = పత్రము లక్షణాలు...
+pdfjs-document-properties-file-name = దస్త్రం పేరు:
+pdfjs-document-properties-file-size = దస్త్రం పరిమాణం:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)
+pdfjs-document-properties-title = శీర్షిక:
+pdfjs-document-properties-author = మూలకర్త:
+pdfjs-document-properties-subject = విషయం:
+pdfjs-document-properties-keywords = కీ పదాలు:
+pdfjs-document-properties-creation-date = సృష్టించిన తేదీ:
+pdfjs-document-properties-modification-date = సవరించిన తేదీ:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = సృష్టికర్త:
+pdfjs-document-properties-producer = PDF ఉత్పాదకి:
+pdfjs-document-properties-version = PDF వర్షన్:
+pdfjs-document-properties-page-count = పేజీల సంఖ్య:
+pdfjs-document-properties-page-size = కాగితం పరిమాణం:
+pdfjs-document-properties-page-size-unit-inches = లో
+pdfjs-document-properties-page-size-unit-millimeters = mm
+pdfjs-document-properties-page-size-orientation-portrait = నిలువుచిత్రం
+pdfjs-document-properties-page-size-orientation-landscape = అడ్డచిత్రం
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = లేఖ
+pdfjs-document-properties-page-size-name-legal = చట్టపరమైన
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+pdfjs-document-properties-linearized-yes = అవును
+pdfjs-document-properties-linearized-no = కాదు
+pdfjs-document-properties-close-button = మూసివేయి
+
+## Print
+
+pdfjs-print-progress-message = ముద్రించడానికి పత్రము సిద్ధమవుతున్నది…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = రద్దుచేయి
+pdfjs-printing-not-supported = హెచ్చరిక: ఈ విహారిణి చేత ముద్రణ పూర్తిగా తోడ్పాటు లేదు.
+pdfjs-printing-not-ready = హెచ్చరిక: ముద్రణ కొరకు ఈ PDF పూర్తిగా లోడవలేదు.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = పక్కపట్టీ మార్చు
+pdfjs-toggle-sidebar-button-label = పక్కపట్టీ మార్చు
+pdfjs-document-outline-button =
+ .title = పత్రము రూపము చూపించు (డబుల్ క్లిక్ చేసి అన్ని అంశాలను విస్తరించు/కూల్చు)
+pdfjs-document-outline-button-label = పత్రము అవుట్లైన్
+pdfjs-attachments-button =
+ .title = అనుబంధాలు చూపు
+pdfjs-attachments-button-label = అనుబంధాలు
+pdfjs-layers-button-label = పొరలు
+pdfjs-thumbs-button =
+ .title = థంబ్నైల్స్ చూపు
+pdfjs-thumbs-button-label = థంబ్నైల్స్
+pdfjs-findbar-button =
+ .title = పత్రములో కనుగొనుము
+pdfjs-findbar-button-label = కనుగొను
+pdfjs-additional-layers = అదనపు పొరలు
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = పేజీ { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = { $page } పేజీ నఖచిత్రం
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = కనుగొను
+ .placeholder = పత్రములో కనుగొను…
+pdfjs-find-previous-button =
+ .title = పదం యొక్క ముందు సంభవాన్ని కనుగొను
+pdfjs-find-previous-button-label = మునుపటి
+pdfjs-find-next-button =
+ .title = పదం యొక్క తర్వాతి సంభవాన్ని కనుగొను
+pdfjs-find-next-button-label = తరువాత
+pdfjs-find-highlight-checkbox = అన్నిటిని ఉద్దీపనం చేయుము
+pdfjs-find-match-case-checkbox-label = అక్షరముల తేడాతో పోల్చు
+pdfjs-find-entire-word-checkbox-label = పూర్తి పదాలు
+pdfjs-find-reached-top = పేజీ పైకి చేరుకున్నది, క్రింది నుండి కొనసాగించండి
+pdfjs-find-reached-bottom = పేజీ చివరకు చేరుకున్నది, పైనుండి కొనసాగించండి
+pdfjs-find-not-found = పదబంధం కనబడలేదు
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = పేజీ వెడల్పు
+pdfjs-page-scale-fit = పేజీ అమర్పు
+pdfjs-page-scale-auto = స్వయంచాలక జూమ్
+pdfjs-page-scale-actual = యథార్ధ పరిమాణం
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+
+## Loading indicator messages
+
+pdfjs-loading-error = PDF లోడవుచున్నప్పుడు ఒక దోషం ఎదురైంది.
+pdfjs-invalid-file-error = చెల్లని లేదా పాడైన PDF ఫైలు.
+pdfjs-missing-file-error = దొరకని PDF ఫైలు.
+pdfjs-unexpected-response-error = అనుకోని సర్వర్ స్పందన.
+pdfjs-rendering-error = పేజీను రెండర్ చేయుటలో ఒక దోషం ఎదురైంది.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [{ $type } టీకా]
+
+## Password
+
+pdfjs-password-label = ఈ PDF ఫైల్ తెరుచుటకు సంకేతపదం ప్రవేశపెట్టుము.
+pdfjs-password-invalid = సంకేతపదం చెల్లదు. దయచేసి మళ్ళీ ప్రయత్నించండి.
+pdfjs-password-ok-button = సరే
+pdfjs-password-cancel-button = రద్దుచేయి
+pdfjs-web-fonts-disabled = వెబ్ ఫాంట్లు అచేతనించబడెను: ఎంబెడెడ్ PDF ఫాంట్లు ఉపయోగించలేక పోయింది.
+
+## Editing
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = రంగు
+pdfjs-editor-free-text-size-input = పరిమాణం
+pdfjs-editor-ink-color-input = రంగు
+pdfjs-editor-ink-thickness-input = మందం
+pdfjs-editor-ink-opacity-input = అకిరణ్యత
+
+## Alt-text dialog
+
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
diff --git a/web/locale/tg/viewer.ftl b/web/locale/tg/viewer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..42964701c4886a1b503d40377ff468c5acdd741d
--- /dev/null
+++ b/web/locale/tg/viewer.ftl
@@ -0,0 +1,396 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+## Main toolbar buttons (tooltips and alt text for images)
+
+pdfjs-previous-button =
+ .title = Саҳифаи қаблӣ
+pdfjs-previous-button-label = Қаблӣ
+pdfjs-next-button =
+ .title = Саҳифаи навбатӣ
+pdfjs-next-button-label = Навбатӣ
+# .title: Tooltip for the pageNumber input.
+pdfjs-page-input =
+ .title = Саҳифа
+# Variables:
+# $pagesCount (Number) - the total number of pages in the document
+# This string follows an input field with the number of the page currently displayed.
+pdfjs-of-pages = аз { $pagesCount }
+# Variables:
+# $pageNumber (Number) - the currently visible page
+# $pagesCount (Number) - the total number of pages in the document
+pdfjs-page-of-pages = ({ $pageNumber } аз { $pagesCount })
+pdfjs-zoom-out-button =
+ .title = Хурд кардан
+pdfjs-zoom-out-button-label = Хурд кардан
+pdfjs-zoom-in-button =
+ .title = Калон кардан
+pdfjs-zoom-in-button-label = Калон кардан
+pdfjs-zoom-select =
+ .title = Танзими андоза
+pdfjs-presentation-mode-button =
+ .title = Гузариш ба реҷаи тақдим
+pdfjs-presentation-mode-button-label = Реҷаи тақдим
+pdfjs-open-file-button =
+ .title = Кушодани файл
+pdfjs-open-file-button-label = Кушодан
+pdfjs-print-button =
+ .title = Чоп кардан
+pdfjs-print-button-label = Чоп кардан
+pdfjs-save-button =
+ .title = Нигоҳ доштан
+pdfjs-save-button-label = Нигоҳ доштан
+# Used in Firefox for Android as a tooltip for the download button (“download” is a verb).
+pdfjs-download-button =
+ .title = Боргирӣ кардан
+# Used in Firefox for Android as a label for the download button (“download” is a verb).
+# Length of the translation matters since we are in a mobile context, with limited screen estate.
+pdfjs-download-button-label = Боргирӣ кардан
+pdfjs-bookmark-button =
+ .title = Саҳифаи ҷорӣ (Дидани нишонии URL аз саҳифаи ҷорӣ)
+pdfjs-bookmark-button-label = Саҳифаи ҷорӣ
+
+## Secondary toolbar and context menu
+
+pdfjs-tools-button =
+ .title = Абзорҳо
+pdfjs-tools-button-label = Абзорҳо
+pdfjs-first-page-button =
+ .title = Ба саҳифаи аввал гузаред
+pdfjs-first-page-button-label = Ба саҳифаи аввал гузаред
+pdfjs-last-page-button =
+ .title = Ба саҳифаи охирин гузаред
+pdfjs-last-page-button-label = Ба саҳифаи охирин гузаред
+pdfjs-page-rotate-cw-button =
+ .title = Ба самти ҳаракати ақрабаки соат давр задан
+pdfjs-page-rotate-cw-button-label = Ба самти ҳаракати ақрабаки соат давр задан
+pdfjs-page-rotate-ccw-button =
+ .title = Ба муқобили самти ҳаракати ақрабаки соат давр задан
+pdfjs-page-rotate-ccw-button-label = Ба муқобили самти ҳаракати ақрабаки соат давр задан
+pdfjs-cursor-text-select-tool-button =
+ .title = Фаъол кардани «Абзори интихоби матн»
+pdfjs-cursor-text-select-tool-button-label = Абзори интихоби матн
+pdfjs-cursor-hand-tool-button =
+ .title = Фаъол кардани «Абзори даст»
+pdfjs-cursor-hand-tool-button-label = Абзори даст
+pdfjs-scroll-page-button =
+ .title = Истифодаи варақзанӣ
+pdfjs-scroll-page-button-label = Варақзанӣ
+pdfjs-scroll-vertical-button =
+ .title = Истифодаи варақзании амудӣ
+pdfjs-scroll-vertical-button-label = Варақзании амудӣ
+pdfjs-scroll-horizontal-button =
+ .title = Истифодаи варақзании уфуқӣ
+pdfjs-scroll-horizontal-button-label = Варақзании уфуқӣ
+pdfjs-scroll-wrapped-button =
+ .title = Истифодаи варақзании миқёсбандӣ
+pdfjs-scroll-wrapped-button-label = Варақзании миқёсбандӣ
+pdfjs-spread-none-button =
+ .title = Густариши саҳифаҳо истифода бурда нашавад
+pdfjs-spread-none-button-label = Бе густурдани саҳифаҳо
+pdfjs-spread-odd-button =
+ .title = Густариши саҳифаҳо аз саҳифаҳо бо рақамҳои тоқ оғоз карда мешавад
+pdfjs-spread-odd-button-label = Саҳифаҳои тоқ аз тарафи чап
+pdfjs-spread-even-button =
+ .title = Густариши саҳифаҳо аз саҳифаҳо бо рақамҳои ҷуфт оғоз карда мешавад
+pdfjs-spread-even-button-label = Саҳифаҳои ҷуфт аз тарафи чап
+
+## Document properties dialog
+
+pdfjs-document-properties-button =
+ .title = Хусусиятҳои ҳуҷҷат…
+pdfjs-document-properties-button-label = Хусусиятҳои ҳуҷҷат…
+pdfjs-document-properties-file-name = Номи файл:
+pdfjs-document-properties-file-size = Андозаи файл:
+# Variables:
+# $size_kb (Number) - the PDF file size in kilobytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-kb = { $size_kb } КБ ({ $size_b } байт)
+# Variables:
+# $size_mb (Number) - the PDF file size in megabytes
+# $size_b (Number) - the PDF file size in bytes
+pdfjs-document-properties-mb = { $size_mb } МБ ({ $size_b } байт)
+pdfjs-document-properties-title = Сарлавҳа:
+pdfjs-document-properties-author = Муаллиф:
+pdfjs-document-properties-subject = Мавзуъ:
+pdfjs-document-properties-keywords = Калимаҳои калидӣ:
+pdfjs-document-properties-creation-date = Санаи эҷод:
+pdfjs-document-properties-modification-date = Санаи тағйирот:
+# Variables:
+# $date (Date) - the creation/modification date of the PDF file
+# $time (Time) - the creation/modification time of the PDF file
+pdfjs-document-properties-date-string = { $date }, { $time }
+pdfjs-document-properties-creator = Эҷодкунанда:
+pdfjs-document-properties-producer = Таҳиякунандаи «PDF»:
+pdfjs-document-properties-version = Версияи «PDF»:
+pdfjs-document-properties-page-count = Шумораи саҳифаҳо:
+pdfjs-document-properties-page-size = Андозаи саҳифа:
+pdfjs-document-properties-page-size-unit-inches = дюйм
+pdfjs-document-properties-page-size-unit-millimeters = мм
+pdfjs-document-properties-page-size-orientation-portrait = амудӣ
+pdfjs-document-properties-page-size-orientation-landscape = уфуқӣ
+pdfjs-document-properties-page-size-name-a-three = A3
+pdfjs-document-properties-page-size-name-a-four = A4
+pdfjs-document-properties-page-size-name-letter = Мактуб
+pdfjs-document-properties-page-size-name-legal = Ҳуқуқӣ
+
+## Variables:
+## $width (Number) - the width of the (current) page
+## $height (Number) - the height of the (current) page
+## $unit (String) - the unit of measurement of the (current) page
+## $name (String) - the name of the (current) page
+## $orientation (String) - the orientation of the (current) page
+
+pdfjs-document-properties-page-size-dimension-string = { $width } × { $height } { $unit } ({ $orientation })
+pdfjs-document-properties-page-size-dimension-name-string = { $width } × { $height } { $unit } ({ $name }, { $orientation })
+
+##
+
+# The linearization status of the document; usually called "Fast Web View" in
+# English locales of Adobe software.
+pdfjs-document-properties-linearized = Намоиши тез дар Интернет:
+pdfjs-document-properties-linearized-yes = Ҳа
+pdfjs-document-properties-linearized-no = Не
+pdfjs-document-properties-close-button = Пӯшидан
+
+## Print
+
+pdfjs-print-progress-message = Омодасозии ҳуҷҷат барои чоп…
+# Variables:
+# $progress (Number) - percent value
+pdfjs-print-progress-percent = { $progress }%
+pdfjs-print-progress-close-button = Бекор кардан
+pdfjs-printing-not-supported = Диққат: Чопкунӣ аз тарафи ин браузер ба таври пурра дастгирӣ намешавад.
+pdfjs-printing-not-ready = Диққат: Файли «PDF» барои чопкунӣ пурра бор карда нашуд.
+
+## Tooltips and alt text for side panel toolbar buttons
+
+pdfjs-toggle-sidebar-button =
+ .title = Фаъол кардани навори ҷонибӣ
+pdfjs-toggle-sidebar-notification-button =
+ .title = Фаъол кардани навори ҷонибӣ (ҳуҷҷат дорои сохтор/замимаҳо/қабатҳо мебошад)
+pdfjs-toggle-sidebar-button-label = Фаъол кардани навори ҷонибӣ
+pdfjs-document-outline-button =
+ .title = Намоиш додани сохтори ҳуҷҷат (барои баркушодан/пеҷондани ҳамаи унсурҳо дубора зер кунед)
+pdfjs-document-outline-button-label = Сохтори ҳуҷҷат
+pdfjs-attachments-button =
+ .title = Намоиш додани замимаҳо
+pdfjs-attachments-button-label = Замимаҳо
+pdfjs-layers-button =
+ .title = Намоиш додани қабатҳо (барои барқарор кардани ҳамаи қабатҳо ба вазъияти пешфарз дубора зер кунед)
+pdfjs-layers-button-label = Қабатҳо
+pdfjs-thumbs-button =
+ .title = Намоиш додани тасвирчаҳо
+pdfjs-thumbs-button-label = Тасвирчаҳо
+pdfjs-current-outline-item-button =
+ .title = Ёфтани унсури сохтори ҷорӣ
+pdfjs-current-outline-item-button-label = Унсури сохтори ҷорӣ
+pdfjs-findbar-button =
+ .title = Ёфтан дар ҳуҷҷат
+pdfjs-findbar-button-label = Ёфтан
+pdfjs-additional-layers = Қабатҳои иловагӣ
+
+## Thumbnails panel item (tooltip and alt text for images)
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-title =
+ .title = Саҳифаи { $page }
+# Variables:
+# $page (Number) - the page number
+pdfjs-thumb-page-canvas =
+ .aria-label = Тасвирчаи саҳифаи { $page }
+
+## Find panel button title and messages
+
+pdfjs-find-input =
+ .title = Ёфтан
+ .placeholder = Ёфтан дар ҳуҷҷат…
+pdfjs-find-previous-button =
+ .title = Ҷустуҷӯи мавриди қаблии ибораи пешниҳодшуда
+pdfjs-find-previous-button-label = Қаблӣ
+pdfjs-find-next-button =
+ .title = Ҷустуҷӯи мавриди навбатии ибораи пешниҳодшуда
+pdfjs-find-next-button-label = Навбатӣ
+pdfjs-find-highlight-checkbox = Ҳамаашро бо ранг ҷудо кардан
+pdfjs-find-match-case-checkbox-label = Бо дарназардошти ҳарфҳои хурду калон
+pdfjs-find-match-diacritics-checkbox-label = Бо дарназардошти аломатҳои диакритикӣ
+pdfjs-find-entire-word-checkbox-label = Калимаҳои пурра
+pdfjs-find-reached-top = Ба болои ҳуҷҷат расид, аз поён идома ёфт
+pdfjs-find-reached-bottom = Ба поёни ҳуҷҷат расид, аз боло идома ёфт
+# Variables:
+# $current (Number) - the index of the currently active find result
+# $total (Number) - the total number of matches in the document
+pdfjs-find-match-count =
+ { $total ->
+ [one] { $current } аз { $total } мувофиқат
+ *[other] { $current } аз { $total } мувофиқат
+ }
+# Variables:
+# $limit (Number) - the maximum number of matches
+pdfjs-find-match-count-limit =
+ { $limit ->
+ [one] Зиёда аз { $limit } мувофиқат
+ *[other] Зиёда аз { $limit } мувофиқат
+ }
+pdfjs-find-not-found = Ибора ёфт нашуд
+
+## Predefined zoom values
+
+pdfjs-page-scale-width = Аз рӯи паҳнои саҳифа
+pdfjs-page-scale-fit = Аз рӯи андозаи саҳифа
+pdfjs-page-scale-auto = Андозаи худкор
+pdfjs-page-scale-actual = Андозаи воқеӣ
+# Variables:
+# $scale (Number) - percent value for page scale
+pdfjs-page-scale-percent = { $scale }%
+
+## PDF page
+
+# Variables:
+# $page (Number) - the page number
+pdfjs-page-landmark =
+ .aria-label = Саҳифаи { $page }
+
+## Loading indicator messages
+
+pdfjs-loading-error = Ҳангоми боркунии «PDF» хато ба миён омад.
+pdfjs-invalid-file-error = Файли «PDF» нодуруст ё вайроншуда мебошад.
+pdfjs-missing-file-error = Файли «PDF» ғоиб аст.
+pdfjs-unexpected-response-error = Ҷавоби ногаҳон аз сервер.
+pdfjs-rendering-error = Ҳангоми шаклсозии саҳифа хато ба миён омад.
+
+## Annotations
+
+# Variables:
+# $date (Date) - the modification date of the annotation
+# $time (Time) - the modification time of the annotation
+pdfjs-annotation-date-string = { $date }, { $time }
+# .alt: This is used as a tooltip.
+# Variables:
+# $type (String) - an annotation type from a list defined in the PDF spec
+# (32000-1:2008 Table 169 – Annotation types).
+# Some common types are e.g.: "Check", "Text", "Comment", "Note"
+pdfjs-text-annotation-type =
+ .alt = [Ҳошиянависӣ - { $type }]
+
+## Password
+
+pdfjs-password-label = Барои кушодани ин файли «PDF» ниҳонвожаро ворид кунед.
+pdfjs-password-invalid = Ниҳонвожаи нодуруст. Лутфан, аз нав кӯшиш кунед.
+pdfjs-password-ok-button = ХУБ
+pdfjs-password-cancel-button = Бекор кардан
+pdfjs-web-fonts-disabled = Шрифтҳои интернетӣ ғайрифаъоланд: истифодаи шрифтҳои дарунсохти «PDF» ғайриимкон аст.
+
+## Editing
+
+pdfjs-editor-free-text-button =
+ .title = Матн
+pdfjs-editor-free-text-button-label = Матн
+pdfjs-editor-ink-button =
+ .title = Расмкашӣ
+pdfjs-editor-ink-button-label = Расмкашӣ
+pdfjs-editor-stamp-button =
+ .title = Илова ё таҳрир кардани тасвирҳо
+pdfjs-editor-stamp-button-label = Илова ё таҳрир кардани тасвирҳо
+pdfjs-editor-highlight-button =
+ .title = Ҷудокунӣ
+pdfjs-editor-highlight-button-label = Ҷудокунӣ
+pdfjs-highlight-floating-button =
+ .title = Ҷудокунӣ
+pdfjs-highlight-floating-button1 =
+ .title = Ҷудокунӣ
+ .aria-label = Ҷудокунӣ
+pdfjs-highlight-floating-button-label = Ҷудокунӣ
+
+## Remove button for the various kind of editor.
+
+pdfjs-editor-remove-ink-button =
+ .title = Тоза кардани нақша
+pdfjs-editor-remove-freetext-button =
+ .title = Тоза кардани матн
+pdfjs-editor-remove-stamp-button =
+ .title = Тоза кардани тасвир
+pdfjs-editor-remove-highlight-button =
+ .title = Тоза кардани ҷудокунӣ
+
+##
+
+# Editor Parameters
+pdfjs-editor-free-text-color-input = Ранг
+pdfjs-editor-free-text-size-input = Андоза
+pdfjs-editor-ink-color-input = Ранг
+pdfjs-editor-ink-thickness-input = Ғафсӣ
+pdfjs-editor-ink-opacity-input = Шаффофӣ
+pdfjs-editor-stamp-add-image-button =
+ .title = Илова кардани тасвир
+pdfjs-editor-stamp-add-image-button-label = Илова кардани тасвир
+# This refers to the thickness of the line used for free highlighting (not bound to text)
+pdfjs-editor-free-highlight-thickness-input = Ғафсӣ
+pdfjs-editor-free-highlight-thickness-title =
+ .title = Иваз кардани ғафсӣ ҳангоми ҷудокунии унсурҳо ба ғайр аз матн
+pdfjs-free-text =
+ .aria-label = Муҳаррири матн
+pdfjs-free-text-default-content = Нависед…
+pdfjs-ink =
+ .aria-label = Муҳаррири расмкашӣ
+pdfjs-ink-canvas =
+ .aria-label = Тасвири эҷодкардаи корбар
+
+## Alt-text dialog
+
+# Alternative text (alt text) helps when people can't see the image.
+pdfjs-editor-alt-text-button-label = Матни ивазкунанда
+pdfjs-editor-alt-text-edit-button-label = Таҳрир кардани матни ивазкунанда
+pdfjs-editor-alt-text-dialog-label = Имконеро интихоб намоед
+pdfjs-editor-alt-text-dialog-description = Вақте ки одамон тасвирро дида наметавонанд ё вақте ки тасвир бор карда намешавад, матни иловагӣ (Alt text) кумак мерасонад.
+pdfjs-editor-alt-text-add-description-label = Илова кардани тавсиф
+pdfjs-editor-alt-text-add-description-description = Кӯшиш кунед, ки 1-2 ҷумлаеро нависед, ки ба мавзӯъ, танзим ё амалҳо тавзеҳ медиҳад.
+pdfjs-editor-alt-text-mark-decorative-label = Гузоштан ҳамчун матни ороишӣ
+pdfjs-editor-alt-text-mark-decorative-description = Ин барои тасвирҳои ороишӣ, ба монанди марзҳо ё аломатҳои обӣ, истифода мешавад.
+pdfjs-editor-alt-text-cancel-button = Бекор кардан
+pdfjs-editor-alt-text-save-button = Нигоҳ доштан
+pdfjs-editor-alt-text-decorative-tooltip = Ҳамчун матни ороишӣ гузошта шуд
+# .placeholder: This is a placeholder for the alt text input area
+pdfjs-editor-alt-text-textarea =
+ .placeholder = Барои мисол, «Ман забони тоҷикиро дӯст медорам»
+
+## Editor resizers
+## This is used in an aria label to help to understand the role of the resizer.
+
+pdfjs-editor-resizer-label-top-left = Кунҷи чапи боло — тағйир додани андоза
+pdfjs-editor-resizer-label-top-middle = Канори миёнаи боло — тағйир додани андоза
+pdfjs-editor-resizer-label-top-right = Кунҷи рости боло — тағйир додани андоза
+pdfjs-editor-resizer-label-middle-right = Канори миёнаи рост — тағйир додани андоза
+pdfjs-editor-resizer-label-bottom-right = Кунҷи рости поён — тағйир додани андоза
+pdfjs-editor-resizer-label-bottom-middle = Канори миёнаи поён — тағйир додани андоза
+pdfjs-editor-resizer-label-bottom-left = Кунҷи чапи поён — тағйир додани андоза
+pdfjs-editor-resizer-label-middle-left = Канори миёнаи чап — тағйир додани андоза
+
+## Color picker
+
+# This means "Color used to highlight text"
+pdfjs-editor-highlight-colorpicker-label = Ранги ҷудокунӣ
+pdfjs-editor-colorpicker-button =
+ .title = Иваз кардани ранг
+pdfjs-editor-colorpicker-dropdown =
+ .aria-label = Интихоби ранг
+pdfjs-editor-colorpicker-yellow =
+ .title = Зард
+pdfjs-editor-colorpicker-green =
+ .title = Сабз
+pdfjs-editor-colorpicker-blue =
+ .title = Кабуд
+pdfjs-editor-colorpicker-pink =
+ .title = Гулобӣ
+pdfjs-editor-colorpicker-red =
+ .title = Сурх
+
+## Show all highlights
+## This is a toggle button to show/hide all the highlights.
+
+pdfjs-editor-highlight-show-all-button-label = Ҳамаро намоиш додан
+pdfjs-editor-highlight-show-all-button =
+ .title = Ҳамаро намоиш додан
diff --git a/web/standard_fonts/FoxitDingbats.pfb b/web/standard_fonts/FoxitDingbats.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..30d52963e281dcd7e6dd70555340fc987d3568ef
Binary files /dev/null and b/web/standard_fonts/FoxitDingbats.pfb differ
diff --git a/web/standard_fonts/FoxitFixed.pfb b/web/standard_fonts/FoxitFixed.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..f12dcbce52babb40fb61765aa46ddf47e3846511
Binary files /dev/null and b/web/standard_fonts/FoxitFixed.pfb differ
diff --git a/web/standard_fonts/FoxitFixedBold.pfb b/web/standard_fonts/FoxitFixedBold.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..cf8e24aee5962ebcf7e4d58d932cc3321f3cdd38
Binary files /dev/null and b/web/standard_fonts/FoxitFixedBold.pfb differ
diff --git a/web/standard_fonts/FoxitFixedBoldItalic.pfb b/web/standard_fonts/FoxitFixedBoldItalic.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..d2880017c25716417f837a1c26044a0342fa3f9a
Binary files /dev/null and b/web/standard_fonts/FoxitFixedBoldItalic.pfb differ
diff --git a/web/standard_fonts/FoxitFixedItalic.pfb b/web/standard_fonts/FoxitFixedItalic.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..d71697d4b638737a5545dd0cb28cba8f613e4eec
Binary files /dev/null and b/web/standard_fonts/FoxitFixedItalic.pfb differ
diff --git a/web/standard_fonts/FoxitSerif.pfb b/web/standard_fonts/FoxitSerif.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..3fa682efbb4b1d1941194a5dd8b78f39b33b72b0
Binary files /dev/null and b/web/standard_fonts/FoxitSerif.pfb differ
diff --git a/web/standard_fonts/FoxitSerifBold.pfb b/web/standard_fonts/FoxitSerifBold.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..ff7c6ddecf6b689d93823c4c2a8c41e6e058de46
Binary files /dev/null and b/web/standard_fonts/FoxitSerifBold.pfb differ
diff --git a/web/standard_fonts/FoxitSerifBoldItalic.pfb b/web/standard_fonts/FoxitSerifBoldItalic.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..460231fb88a0083e577ed534760307d7779fd82f
Binary files /dev/null and b/web/standard_fonts/FoxitSerifBoldItalic.pfb differ
diff --git a/web/standard_fonts/FoxitSerifItalic.pfb b/web/standard_fonts/FoxitSerifItalic.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..d03a7c781e1a2b91481103dc3227a6bdca4a461c
Binary files /dev/null and b/web/standard_fonts/FoxitSerifItalic.pfb differ
diff --git a/web/standard_fonts/FoxitSymbol.pfb b/web/standard_fonts/FoxitSymbol.pfb
new file mode 100644
index 0000000000000000000000000000000000000000..c8f9bca78c2f89ce40dff0afe6cdd7068061b3e6
Binary files /dev/null and b/web/standard_fonts/FoxitSymbol.pfb differ
diff --git a/web/standard_fonts/LICENSE_FOXIT b/web/standard_fonts/LICENSE_FOXIT
new file mode 100644
index 0000000000000000000000000000000000000000..8b4ed6dddf22b2fcc9f7f520cd23613b49307fa0
--- /dev/null
+++ b/web/standard_fonts/LICENSE_FOXIT
@@ -0,0 +1,27 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/web/standard_fonts/LICENSE_LIBERATION b/web/standard_fonts/LICENSE_LIBERATION
new file mode 100644
index 0000000000000000000000000000000000000000..aba73e8a403084a93a245ca00e4a0db007886e0a
--- /dev/null
+++ b/web/standard_fonts/LICENSE_LIBERATION
@@ -0,0 +1,102 @@
+Digitized data copyright (c) 2010 Google Corporation
+ with Reserved Font Arimo, Tinos and Cousine.
+Copyright (c) 2012 Red Hat, Inc.
+ with Reserved Font Name Liberation.
+
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+
+PREAMBLE The goals of the Open Font License (OFL) are to stimulate
+worldwide development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to provide
+a free and open framework in which fonts may be shared and improved in
+partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves.
+The fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such.
+This may include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components
+as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting ? in part or in whole ?
+any of the components of the Original Version, by changing formats or
+by porting the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical writer
+or other person who contributed to the Font Software.
+
+
+PERMISSION & CONDITIONS
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,in
+ Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+ redistributed and/or sold with any software, provided that each copy
+ contains the above copyright notice and this license. These can be
+ included either as stand-alone text files, human-readable headers or
+ in the appropriate machine-readable metadata fields within text or
+ binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+ Name(s) unless explicit written permission is granted by the
+ corresponding Copyright Holder. This restriction only applies to the
+ primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+ Software shall not be used to promote, endorse or advertise any
+ Modified Version, except to acknowledge the contribution(s) of the
+ Copyright Holder(s) and the Author(s) or with their explicit written
+ permission.
+
+5) The Font Software, modified or unmodified, in part or in whole, must
+ be distributed entirely under this license, and must not be distributed
+ under any other license. The requirement for fonts to remain under
+ this license does not apply to any document created using the Font
+ Software.
+
+
+
+TERMINATION
+This license becomes null and void if any of the above conditions are not met.
+
+
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
+DEALINGS IN THE FONT SOFTWARE.
+
diff --git a/web/standard_fonts/LiberationSans-Bold.ttf b/web/standard_fonts/LiberationSans-Bold.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..ee2371540417ffbc652745b059effa260430179f
Binary files /dev/null and b/web/standard_fonts/LiberationSans-Bold.ttf differ
diff --git a/web/standard_fonts/LiberationSans-BoldItalic.ttf b/web/standard_fonts/LiberationSans-BoldItalic.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..42b5717ddb92dc6e85edc72fb5c3a798b380902e
Binary files /dev/null and b/web/standard_fonts/LiberationSans-BoldItalic.ttf differ
diff --git a/web/standard_fonts/LiberationSans-Italic.ttf b/web/standard_fonts/LiberationSans-Italic.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..0cf61263494608cbf1fccecce606bc01a247d1e4
Binary files /dev/null and b/web/standard_fonts/LiberationSans-Italic.ttf differ
diff --git a/web/standard_fonts/LiberationSans-Regular.ttf b/web/standard_fonts/LiberationSans-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..366d1489cdde3523e488ed1ee85583618fb0c120
Binary files /dev/null and b/web/standard_fonts/LiberationSans-Regular.ttf differ
diff --git a/web/viewer.css b/web/viewer.css
new file mode 100644
index 0000000000000000000000000000000000000000..95d45b8f6f7d80129e2c9295bf7c9573be76ad50
--- /dev/null
+++ b/web/viewer.css
@@ -0,0 +1,4347 @@
+/* Copyright 2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.dialog{
+ --dialog-bg-color:white;
+ --dialog-border-color:white;
+ --dialog-shadow:0 2px 14px 0 rgb(58 57 68 / 0.2);
+ --text-primary-color:#15141a;
+ --text-secondary-color:#5b5b66;
+ --hover-filter:brightness(0.9);
+ --focus-ring-color:#0060df;
+ --focus-ring-outline:2px solid var(--focus-ring-color);
+
+ --textarea-border-color:#8f8f9d;
+ --textarea-bg-color:white;
+ --textarea-fg-color:var(--text-secondary-color);
+
+ --radio-bg-color:#f0f0f4;
+ --radio-checked-bg-color:#fbfbfe;
+ --radio-border-color:#8f8f9d;
+ --radio-checked-border-color:#0060df;
+
+ --button-secondary-bg-color:#f0f0f4;
+ --button-secondary-fg-color:var(--text-primary-color);
+ --button-secondary-border-color:var(--button-secondary-bg-color);
+ --button-secondary-hover-bg-color:var(--button-secondary-bg-color);
+ --button-secondary-hover-fg-color:var(--button-secondary-fg-color);
+ --button-secondary-hover-border-color:var(--button-secondary-hover-bg-color);
+
+ --button-primary-bg-color:#0060df;
+ --button-primary-fg-color:#fbfbfe;
+ --button-primary-hover-bg-color:var(--button-primary-bg-color);
+ --button-primary-hover-fg-color:var(--button-primary-fg-color);
+ --button-primary-hover-border-color:var(--button-primary-hover-bg-color);
+
+ font:message-box;
+ font-size:13px;
+ font-weight:400;
+ line-height:150%;
+ border-radius:4px;
+ padding:12px 16px;
+ border:1px solid var(--dialog-border-color);
+ background:var(--dialog-bg-color);
+ color:var(--text-primary-color);
+ box-shadow:var(--dialog-shadow);
+}
+
+@media (prefers-color-scheme: dark){
+
+:where(html:not(.is-light)) .dialog{
+ --dialog-bg-color:#1c1b22;
+ --dialog-border-color:#1c1b22;
+ --dialog-shadow:0 2px 14px 0 #15141a;
+ --text-primary-color:#fbfbfe;
+ --text-secondary-color:#cfcfd8;
+ --focus-ring-color:#0df;
+ --hover-filter:brightness(1.4);
+
+ --textarea-bg-color:#42414d;
+
+ --radio-bg-color:#2b2a33;
+ --radio-checked-bg-color:#15141a;
+ --radio-checked-border-color:#0df;
+
+ --button-secondary-bg-color:#2b2a33;
+ --button-primary-bg-color:#0df;
+ --button-primary-fg-color:#15141a;
+}
+ }
+
+:where(html.is-dark) .dialog{
+ --dialog-bg-color:#1c1b22;
+ --dialog-border-color:#1c1b22;
+ --dialog-shadow:0 2px 14px 0 #15141a;
+ --text-primary-color:#fbfbfe;
+ --text-secondary-color:#cfcfd8;
+ --focus-ring-color:#0df;
+ --hover-filter:brightness(1.4);
+
+ --textarea-bg-color:#42414d;
+
+ --radio-bg-color:#2b2a33;
+ --radio-checked-bg-color:#15141a;
+ --radio-checked-border-color:#0df;
+
+ --button-secondary-bg-color:#2b2a33;
+ --button-primary-bg-color:#0df;
+ --button-primary-fg-color:#15141a;
+}
+
+@media screen and (forced-colors: active){
+
+.dialog{
+ --dialog-bg-color:Canvas;
+ --dialog-border-color:CanvasText;
+ --dialog-shadow:none;
+ --text-primary-color:CanvasText;
+ --text-secondary-color:CanvasText;
+ --hover-filter:none;
+ --focus-ring-color:ButtonBorder;
+
+ --textarea-border-color:ButtonBorder;
+ --textarea-bg-color:Field;
+ --textarea-fg-color:ButtonText;
+
+ --radio-bg-color:ButtonFace;
+ --radio-checked-bg-color:ButtonFace;
+ --radio-border-color:ButtonText;
+ --radio-checked-border-color:ButtonText;
+
+ --button-secondary-bg-color:ButtonFace;
+ --button-secondary-fg-color:ButtonText;
+ --button-secondary-border-color:ButtonText;
+ --button-secondary-hover-bg-color:AccentColor;
+ --button-secondary-hover-fg-color:AccentColorText;
+
+ --button-primary-bg-color:ButtonText;
+ --button-primary-fg-color:ButtonFace;
+ --button-primary-hover-bg-color:AccentColor;
+ --button-primary-hover-fg-color:AccentColorText;
+}
+ }
+
+.dialog .mainContainer *:focus-visible{
+ outline:var(--focus-ring-outline);
+ outline-offset:2px;
+ }
+
+.dialog .mainContainer .radio{
+ display:flex;
+ flex-direction:column;
+ align-items:flex-start;
+ gap:4px;
+ }
+
+.dialog .mainContainer .radio > .radioButton{
+ display:flex;
+ gap:8px;
+ align-self:stretch;
+ align-items:center;
+ }
+
+.dialog .mainContainer .radio > .radioButton input{
+ -webkit-appearance:none;
+ -moz-appearance:none;
+ appearance:none;
+ box-sizing:border-box;
+ width:16px;
+ height:16px;
+ border-radius:50%;
+ background-color:var(--radio-bg-color);
+ border:1px solid var(--radio-border-color);
+ }
+
+.dialog .mainContainer .radio > .radioButton input:hover{
+ filter:var(--hover-filter);
+ }
+
+.dialog .mainContainer .radio > .radioButton input:checked{
+ background-color:var(--radio-checked-bg-color);
+ border:4px solid var(--radio-checked-border-color);
+ }
+
+.dialog .mainContainer .radio > .radioLabel{
+ display:flex;
+ padding-inline-start:24px;
+ align-items:flex-start;
+ gap:10px;
+ align-self:stretch;
+ }
+
+.dialog .mainContainer .radio > .radioLabel > span{
+ flex:1 0 0;
+ font-size:11px;
+ color:var(--text-secondary-color);
+ }
+
+.dialog .mainContainer button{
+ border-radius:4px;
+ border:1px solid;
+ font:menu;
+ font-weight:600;
+ padding:4px 16px;
+ width:auto;
+ height:32px;
+ }
+
+.dialog .mainContainer button:hover{
+ cursor:pointer;
+ filter:var(--hover-filter);
+ }
+
+.dialog .mainContainer button.secondaryButton{
+ color:var(--button-secondary-fg-color);
+ background-color:var(--button-secondary-bg-color);
+ border-color:var(--button-secondary-border-color);
+ }
+
+.dialog .mainContainer button.secondaryButton:hover{
+ color:var(--button-secondary-hover-fg-color);
+ background-color:var(--button-secondary-hover-bg-color);
+ border-color:var(--button-secondary-hover-border-color);
+ }
+
+.dialog .mainContainer button.primaryButton{
+ color:var(--button-primary-hover-fg-color);
+ background-color:var(--button-primary-hover-bg-color);
+ border-color:var(--button-primary-hover-border-color);
+ opacity:1;
+ }
+
+.dialog .mainContainer button.primaryButton:hover{
+ color:var(--button-primary-hover-fg-color);
+ background-color:var(--button-primary-hover-bg-color);
+ border-color:var(--button-primary-hover-border-color);
+ }
+
+.dialog .mainContainer textarea{
+ font:inherit;
+ padding:8px;
+ resize:none;
+ margin:0;
+ box-sizing:border-box;
+ border-radius:4px;
+ border:1px solid var(--textarea-border-color);
+ background:var(--textarea-bg-color);
+ color:var(--textarea-fg-color);
+ }
+
+.dialog .mainContainer textarea:focus{
+ outline-offset:0;
+ border-color:transparent;
+ }
+
+.dialog .mainContainer textarea:disabled{
+ pointer-events:none;
+ opacity:0.4;
+ }
+
+.textLayer{
+ position:absolute;
+ text-align:initial;
+ inset:0;
+ overflow:clip;
+ opacity:1;
+ line-height:1;
+ -webkit-text-size-adjust:none;
+ -moz-text-size-adjust:none;
+ text-size-adjust:none;
+ forced-color-adjust:none;
+ transform-origin:0 0;
+ caret-color:CanvasText;
+ z-index:0;
+}
+
+.textLayer.highlighting{
+ touch-action:none;
+ }
+
+.textLayer :is(span, br){
+ color:transparent;
+ position:absolute;
+ white-space:pre;
+ cursor:text;
+ transform-origin:0% 0%;
+ }
+
+.textLayer > :not(.markedContent),
+ .textLayer .markedContent span:not(.markedContent){
+ z-index:1;
+ }
+
+.textLayer span.markedContent{
+ top:0;
+ height:0;
+ }
+
+.textLayer .highlight{
+ --highlight-bg-color:rgb(180 0 170 / 0.25);
+ --highlight-selected-bg-color:rgb(0 100 0 / 0.25);
+ --highlight-backdrop-filter:none;
+ --highlight-selected-backdrop-filter:none;
+
+ margin:-1px;
+ padding:1px;
+ background-color:var(--highlight-bg-color);
+ -webkit-backdrop-filter:var(--highlight-backdrop-filter);
+ backdrop-filter:var(--highlight-backdrop-filter);
+ border-radius:4px;
+ }
+
+@media screen and (forced-colors: active){
+
+.textLayer .highlight{
+ --highlight-bg-color:transparent;
+ --highlight-selected-bg-color:transparent;
+ --highlight-backdrop-filter:var(--hcm-highlight-filter);
+ --highlight-selected-backdrop-filter:var(
+ --hcm-highlight-selected-filter
+ );
+ }
+ }
+
+.textLayer .highlight.appended{
+ position:initial;
+ }
+
+.textLayer .highlight.begin{
+ border-radius:4px 0 0 4px;
+ }
+
+.textLayer .highlight.end{
+ border-radius:0 4px 4px 0;
+ }
+
+.textLayer .highlight.middle{
+ border-radius:0;
+ }
+
+.textLayer .highlight.selected{
+ background-color:var(--highlight-selected-bg-color);
+ -webkit-backdrop-filter:var(--highlight-selected-backdrop-filter);
+ backdrop-filter:var(--highlight-selected-backdrop-filter);
+ }
+
+.textLayer ::-moz-selection{
+ background:rgba(0 0 255 / 0.25);
+ background:color-mix(in srgb, AccentColor, transparent 75%);
+ }
+
+.textLayer ::selection{
+ background:rgba(0 0 255 / 0.25);
+ background:color-mix(in srgb, AccentColor, transparent 75%);
+ }
+
+.textLayer br::-moz-selection{
+ background:transparent;
+ }
+
+.textLayer br::selection{
+ background:transparent;
+ }
+
+.textLayer .endOfContent{
+ display:block;
+ position:absolute;
+ inset:100% 0 0;
+ z-index:0;
+ cursor:default;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+ }
+
+.textLayer .endOfContent.active{
+ top:0;
+ }
+
+.annotationLayer{
+ --annotation-unfocused-field-background:url("data:image/svg+xml;charset=UTF-8,");
+ --input-focus-border-color:Highlight;
+ --input-focus-outline:1px solid Canvas;
+ --input-unfocused-border-color:transparent;
+ --input-disabled-border-color:transparent;
+ --input-hover-border-color:black;
+ --link-outline:none;
+
+ position:absolute;
+ top:0;
+ left:0;
+ pointer-events:none;
+ transform-origin:0 0;
+}
+
+@media screen and (forced-colors: active){
+
+.annotationLayer{
+ --input-focus-border-color:CanvasText;
+ --input-unfocused-border-color:ActiveText;
+ --input-disabled-border-color:GrayText;
+ --input-hover-border-color:Highlight;
+ --link-outline:1.5px solid LinkText;
+}
+
+ .annotationLayer .textWidgetAnnotation :is(input, textarea):required, .annotationLayer .choiceWidgetAnnotation select:required, .annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required{
+ outline:1.5px solid selectedItem;
+ }
+
+ .annotationLayer .linkAnnotation{
+ outline:var(--link-outline);
+ }
+
+ .annotationLayer .linkAnnotation:hover{
+ -webkit-backdrop-filter:var(--hcm-highlight-filter);
+ backdrop-filter:var(--hcm-highlight-filter);
+ }
+
+ .annotationLayer .linkAnnotation > a:hover{
+ opacity:0 !important;
+ background:none !important;
+ box-shadow:none;
+ }
+
+ .annotationLayer .popupAnnotation .popup{
+ outline:calc(1.5px * var(--scale-factor)) solid CanvasText !important;
+ background-color:ButtonFace !important;
+ color:ButtonText !important;
+ }
+
+ .annotationLayer .highlightArea:hover::after{
+ position:absolute;
+ top:0;
+ left:0;
+ width:100%;
+ height:100%;
+ -webkit-backdrop-filter:var(--hcm-highlight-filter);
+ backdrop-filter:var(--hcm-highlight-filter);
+ content:"";
+ pointer-events:none;
+ }
+
+ .annotationLayer .popupAnnotation.focused .popup{
+ outline:calc(3px * var(--scale-factor)) solid Highlight !important;
+ }
+ }
+
+.annotationLayer[data-main-rotation="90"] .norotate{
+ transform:rotate(270deg) translateX(-100%);
+ }
+
+.annotationLayer[data-main-rotation="180"] .norotate{
+ transform:rotate(180deg) translate(-100%, -100%);
+ }
+
+.annotationLayer[data-main-rotation="270"] .norotate{
+ transform:rotate(90deg) translateY(-100%);
+ }
+
+.annotationLayer.disabled section,
+ .annotationLayer.disabled .popup{
+ pointer-events:none;
+ }
+
+.annotationLayer .annotationContent{
+ position:absolute;
+ width:100%;
+ height:100%;
+ pointer-events:none;
+ }
+
+.annotationLayer .annotationContent.freetext{
+ background:transparent;
+ border:none;
+ inset:0;
+ overflow:visible;
+ white-space:nowrap;
+ font:10px sans-serif;
+ line-height:1.35;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+ }
+
+.annotationLayer section{
+ position:absolute;
+ text-align:initial;
+ pointer-events:auto;
+ box-sizing:border-box;
+ transform-origin:0 0;
+ }
+
+.annotationLayer section:has(div.annotationContent) canvas.annotationContent{
+ display:none;
+ }
+
+.annotationLayer :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a{
+ position:absolute;
+ font-size:1em;
+ top:0;
+ left:0;
+ width:100%;
+ height:100%;
+ }
+
+.annotationLayer :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder)
+ > a:hover{
+ opacity:0.2;
+ background-color:rgb(255 255 0);
+ box-shadow:0 2px 10px rgb(255 255 0);
+ }
+
+.annotationLayer .linkAnnotation.hasBorder:hover{
+ background-color:rgb(255 255 0 / 0.2);
+ }
+
+.annotationLayer .hasBorder{
+ background-size:100% 100%;
+ }
+
+.annotationLayer .textAnnotation img{
+ position:absolute;
+ cursor:pointer;
+ width:100%;
+ height:100%;
+ top:0;
+ left:0;
+ }
+
+.annotationLayer .textWidgetAnnotation :is(input, textarea), .annotationLayer .choiceWidgetAnnotation select, .annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input{
+ background-image:var(--annotation-unfocused-field-background);
+ border:2px solid var(--input-unfocused-border-color);
+ box-sizing:border-box;
+ font:calc(9px * var(--scale-factor)) sans-serif;
+ height:100%;
+ margin:0;
+ vertical-align:top;
+ width:100%;
+ }
+
+.annotationLayer .textWidgetAnnotation :is(input, textarea):required, .annotationLayer .choiceWidgetAnnotation select:required, .annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required{
+ outline:1.5px solid red;
+ }
+
+.annotationLayer .choiceWidgetAnnotation select option{
+ padding:0;
+ }
+
+.annotationLayer .buttonWidgetAnnotation.radioButton input{
+ border-radius:50%;
+ }
+
+.annotationLayer .textWidgetAnnotation textarea{
+ resize:none;
+ }
+
+.annotationLayer .textWidgetAnnotation [disabled]:is(input, textarea), .annotationLayer .choiceWidgetAnnotation select[disabled], .annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input[disabled]{
+ background:none;
+ border:2px solid var(--input-disabled-border-color);
+ cursor:not-allowed;
+ }
+
+.annotationLayer .textWidgetAnnotation :is(input, textarea):hover, .annotationLayer .choiceWidgetAnnotation select:hover, .annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:hover{
+ border:2px solid var(--input-hover-border-color);
+ }
+
+.annotationLayer .textWidgetAnnotation :is(input, textarea):hover, .annotationLayer .choiceWidgetAnnotation select:hover, .annotationLayer .buttonWidgetAnnotation.checkBox input:hover{
+ border-radius:2px;
+ }
+
+.annotationLayer .textWidgetAnnotation :is(input, textarea):focus, .annotationLayer .choiceWidgetAnnotation select:focus{
+ background:none;
+ border:2px solid var(--input-focus-border-color);
+ border-radius:2px;
+ outline:var(--input-focus-outline);
+ }
+
+.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) :focus{
+ background-image:none;
+ background-color:transparent;
+ }
+
+.annotationLayer .buttonWidgetAnnotation.checkBox :focus{
+ border:2px solid var(--input-focus-border-color);
+ border-radius:2px;
+ outline:var(--input-focus-outline);
+ }
+
+.annotationLayer .buttonWidgetAnnotation.radioButton :focus{
+ border:2px solid var(--input-focus-border-color);
+ outline:var(--input-focus-outline);
+ }
+
+.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,
+ .annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after,
+ .annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before{
+ background-color:CanvasText;
+ content:"";
+ display:block;
+ position:absolute;
+ }
+
+.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,
+ .annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after{
+ height:80%;
+ left:45%;
+ width:1px;
+ }
+
+.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before{
+ transform:rotate(45deg);
+ }
+
+.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after{
+ transform:rotate(-45deg);
+ }
+
+.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before{
+ border-radius:50%;
+ height:50%;
+ left:25%;
+ top:25%;
+ width:50%;
+ }
+
+.annotationLayer .textWidgetAnnotation input.comb{
+ font-family:monospace;
+ padding-left:2px;
+ padding-right:0;
+ }
+
+.annotationLayer .textWidgetAnnotation input.comb:focus{
+ width:103%;
+ }
+
+.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input{
+ -webkit-appearance:none;
+ -moz-appearance:none;
+ appearance:none;
+ }
+
+.annotationLayer .fileAttachmentAnnotation .popupTriggerArea{
+ height:100%;
+ width:100%;
+ }
+
+.annotationLayer .popupAnnotation{
+ position:absolute;
+ font-size:calc(9px * var(--scale-factor));
+ pointer-events:none;
+ width:-moz-max-content;
+ width:max-content;
+ max-width:45%;
+ height:auto;
+ }
+
+.annotationLayer .popup{
+ background-color:rgb(255 255 153);
+ box-shadow:0 calc(2px * var(--scale-factor)) calc(5px * var(--scale-factor)) rgb(136 136 136);
+ border-radius:calc(2px * var(--scale-factor));
+ outline:1.5px solid rgb(255 255 74);
+ padding:calc(6px * var(--scale-factor));
+ cursor:pointer;
+ font:message-box;
+ white-space:normal;
+ word-wrap:break-word;
+ pointer-events:auto;
+ }
+
+.annotationLayer .popupAnnotation.focused .popup{
+ outline-width:3px;
+ }
+
+.annotationLayer .popup *{
+ font-size:calc(9px * var(--scale-factor));
+ }
+
+.annotationLayer .popup > .header{
+ display:inline-block;
+ }
+
+.annotationLayer .popup > .header h1{
+ display:inline;
+ }
+
+.annotationLayer .popup > .header .popupDate{
+ display:inline-block;
+ margin-left:calc(5px * var(--scale-factor));
+ width:-moz-fit-content;
+ width:fit-content;
+ }
+
+.annotationLayer .popupContent{
+ border-top:1px solid rgb(51 51 51);
+ margin-top:calc(2px * var(--scale-factor));
+ padding-top:calc(2px * var(--scale-factor));
+ }
+
+.annotationLayer .richText > *{
+ white-space:pre-wrap;
+ font-size:calc(9px * var(--scale-factor));
+ }
+
+.annotationLayer .popupTriggerArea{
+ cursor:pointer;
+ }
+
+.annotationLayer section svg{
+ position:absolute;
+ width:100%;
+ height:100%;
+ top:0;
+ left:0;
+ }
+
+.annotationLayer .annotationTextContent{
+ position:absolute;
+ width:100%;
+ height:100%;
+ opacity:0;
+ color:transparent;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+ pointer-events:none;
+ }
+
+.annotationLayer .annotationTextContent span{
+ width:100%;
+ display:inline-block;
+ }
+
+.annotationLayer svg.quadrilateralsContainer{
+ contain:strict;
+ width:0;
+ height:0;
+ position:absolute;
+ top:0;
+ left:0;
+ z-index:-1;
+ }
+
+:root{
+ --xfa-unfocused-field-background:url("data:image/svg+xml;charset=UTF-8,");
+ --xfa-focus-outline:auto;
+}
+
+@media screen and (forced-colors: active){
+ :root{
+ --xfa-focus-outline:2px solid CanvasText;
+ }
+ .xfaLayer *:required{
+ outline:1.5px solid selectedItem;
+ }
+}
+
+.xfaLayer{
+ background-color:transparent;
+}
+
+.xfaLayer .highlight{
+ margin:-1px;
+ padding:1px;
+ background-color:rgb(239 203 237);
+ border-radius:4px;
+}
+
+.xfaLayer .highlight.appended{
+ position:initial;
+}
+
+.xfaLayer .highlight.begin{
+ border-radius:4px 0 0 4px;
+}
+
+.xfaLayer .highlight.end{
+ border-radius:0 4px 4px 0;
+}
+
+.xfaLayer .highlight.middle{
+ border-radius:0;
+}
+
+.xfaLayer .highlight.selected{
+ background-color:rgb(203 223 203);
+}
+
+.xfaPage{
+ overflow:hidden;
+ position:relative;
+}
+
+.xfaContentarea{
+ position:absolute;
+}
+
+.xfaPrintOnly{
+ display:none;
+}
+
+.xfaLayer{
+ position:absolute;
+ text-align:initial;
+ top:0;
+ left:0;
+ transform-origin:0 0;
+ line-height:1.2;
+}
+
+.xfaLayer *{
+ color:inherit;
+ font:inherit;
+ font-style:inherit;
+ font-weight:inherit;
+ font-kerning:inherit;
+ letter-spacing:-0.01px;
+ text-align:inherit;
+ text-decoration:inherit;
+ box-sizing:border-box;
+ background-color:transparent;
+ padding:0;
+ margin:0;
+ pointer-events:auto;
+ line-height:inherit;
+}
+
+.xfaLayer *:required{
+ outline:1.5px solid red;
+}
+
+.xfaLayer div,
+.xfaLayer svg,
+.xfaLayer svg *{
+ pointer-events:none;
+}
+
+.xfaLayer a{
+ color:blue;
+}
+
+.xfaRich li{
+ margin-left:3em;
+}
+
+.xfaFont{
+ color:black;
+ font-weight:normal;
+ font-kerning:none;
+ font-size:10px;
+ font-style:normal;
+ letter-spacing:0;
+ text-decoration:none;
+ vertical-align:0;
+}
+
+.xfaCaption{
+ overflow:hidden;
+ flex:0 0 auto;
+}
+
+.xfaCaptionForCheckButton{
+ overflow:hidden;
+ flex:1 1 auto;
+}
+
+.xfaLabel{
+ height:100%;
+ width:100%;
+}
+
+.xfaLeft{
+ display:flex;
+ flex-direction:row;
+ align-items:center;
+}
+
+.xfaRight{
+ display:flex;
+ flex-direction:row-reverse;
+ align-items:center;
+}
+
+:is(.xfaLeft, .xfaRight) > :is(.xfaCaption, .xfaCaptionForCheckButton){
+ max-height:100%;
+}
+
+.xfaTop{
+ display:flex;
+ flex-direction:column;
+ align-items:flex-start;
+}
+
+.xfaBottom{
+ display:flex;
+ flex-direction:column-reverse;
+ align-items:flex-start;
+}
+
+:is(.xfaTop, .xfaBottom) > :is(.xfaCaption, .xfaCaptionForCheckButton){
+ width:100%;
+}
+
+.xfaBorder{
+ background-color:transparent;
+ position:absolute;
+ pointer-events:none;
+}
+
+.xfaWrapped{
+ width:100%;
+ height:100%;
+}
+
+:is(.xfaTextfield, .xfaSelect):focus{
+ background-image:none;
+ background-color:transparent;
+ outline:var(--xfa-focus-outline);
+ outline-offset:-1px;
+}
+
+:is(.xfaCheckbox, .xfaRadio):focus{
+ outline:var(--xfa-focus-outline);
+}
+
+.xfaTextfield,
+.xfaSelect{
+ height:100%;
+ width:100%;
+ flex:1 1 auto;
+ border:none;
+ resize:none;
+ background-image:var(--xfa-unfocused-field-background);
+}
+
+.xfaSelect{
+ padding-inline:2px;
+}
+
+:is(.xfaTop, .xfaBottom) > :is(.xfaTextfield, .xfaSelect){
+ flex:0 1 auto;
+}
+
+.xfaButton{
+ cursor:pointer;
+ width:100%;
+ height:100%;
+ border:none;
+ text-align:center;
+}
+
+.xfaLink{
+ width:100%;
+ height:100%;
+ position:absolute;
+ top:0;
+ left:0;
+}
+
+.xfaCheckbox,
+.xfaRadio{
+ width:100%;
+ height:100%;
+ flex:0 0 auto;
+ border:none;
+}
+
+.xfaRich{
+ white-space:pre-wrap;
+ width:100%;
+ height:100%;
+}
+
+.xfaImage{
+ -o-object-position:left top;
+ object-position:left top;
+ -o-object-fit:contain;
+ object-fit:contain;
+ width:100%;
+ height:100%;
+}
+
+.xfaLrTb,
+.xfaRlTb,
+.xfaTb{
+ display:flex;
+ flex-direction:column;
+ align-items:stretch;
+}
+
+.xfaLr{
+ display:flex;
+ flex-direction:row;
+ align-items:stretch;
+}
+
+.xfaRl{
+ display:flex;
+ flex-direction:row-reverse;
+ align-items:stretch;
+}
+
+.xfaTb > div{
+ justify-content:left;
+}
+
+.xfaPosition{
+ position:relative;
+}
+
+.xfaArea{
+ position:relative;
+}
+
+.xfaValignMiddle{
+ display:flex;
+ align-items:center;
+}
+
+.xfaTable{
+ display:flex;
+ flex-direction:column;
+ align-items:stretch;
+}
+
+.xfaTable .xfaRow{
+ display:flex;
+ flex-direction:row;
+ align-items:stretch;
+}
+
+.xfaTable .xfaRlRow{
+ display:flex;
+ flex-direction:row-reverse;
+ align-items:stretch;
+ flex:1;
+}
+
+.xfaTable .xfaRlRow > div{
+ flex:1;
+}
+
+:is(.xfaNonInteractive, .xfaDisabled, .xfaReadOnly) :is(input, textarea){
+ background:initial;
+}
+
+@media print{
+ .xfaTextfield,
+ .xfaSelect{
+ background:transparent;
+ }
+
+ .xfaSelect{
+ -webkit-appearance:none;
+ -moz-appearance:none;
+ appearance:none;
+ text-indent:1px;
+ text-overflow:"";
+ }
+}
+
+.canvasWrapper svg{
+ transform:none;
+ }
+
+.canvasWrapper svg[data-main-rotation="90"] mask,
+ .canvasWrapper svg[data-main-rotation="90"] use:not(.clip, .mask){
+ transform:matrix(0, 1, -1, 0, 1, 0);
+ }
+
+.canvasWrapper svg[data-main-rotation="180"] mask,
+ .canvasWrapper svg[data-main-rotation="180"] use:not(.clip, .mask){
+ transform:matrix(-1, 0, 0, -1, 1, 1);
+ }
+
+.canvasWrapper svg[data-main-rotation="270"] mask,
+ .canvasWrapper svg[data-main-rotation="270"] use:not(.clip, .mask){
+ transform:matrix(0, -1, 1, 0, 0, 1);
+ }
+
+.canvasWrapper svg.highlight{
+ --blend-mode:multiply;
+
+ position:absolute;
+ mix-blend-mode:var(--blend-mode);
+ }
+
+@media screen and (forced-colors: active){
+
+.canvasWrapper svg.highlight{
+ --blend-mode:difference;
+ }
+ }
+
+.canvasWrapper svg.highlight:not(.free){
+ fill-rule:evenodd;
+ }
+
+.canvasWrapper svg.highlightOutline{
+ position:absolute;
+ mix-blend-mode:normal;
+ fill-rule:evenodd;
+ fill:none;
+ }
+
+.canvasWrapper svg.highlightOutline.hovered:not(.free):not(.selected){
+ stroke:var(--hover-outline-color);
+ stroke-width:var(--outline-width);
+ }
+
+.canvasWrapper svg.highlightOutline.selected:not(.free) .mainOutline{
+ stroke:var(--outline-around-color);
+ stroke-width:calc(
+ var(--outline-width) + 2 * var(--outline-around-width)
+ );
+ }
+
+.canvasWrapper svg.highlightOutline.selected:not(.free) .secondaryOutline{
+ stroke:var(--outline-color);
+ stroke-width:var(--outline-width);
+ }
+
+.canvasWrapper svg.highlightOutline.free.hovered:not(.selected){
+ stroke:var(--hover-outline-color);
+ stroke-width:calc(2 * var(--outline-width));
+ }
+
+.canvasWrapper svg.highlightOutline.free.selected .mainOutline{
+ stroke:var(--outline-around-color);
+ stroke-width:calc(
+ 2 * (var(--outline-width) + var(--outline-around-width))
+ );
+ }
+
+.canvasWrapper svg.highlightOutline.free.selected .secondaryOutline{
+ stroke:var(--outline-color);
+ stroke-width:calc(2 * var(--outline-width));
+ }
+
+.toggle-button{
+ --button-background-color:#f0f0f4;
+ --button-background-color-hover:#e0e0e6;
+ --button-background-color-active:#cfcfd8;
+ --color-accent-primary:#0060df;
+ --color-accent-primary-hover:#0250bb;
+ --color-accent-primary-active:#054096;
+ --border-interactive-color:#8f8f9d;
+ --border-radius-circle:9999px;
+ --border-width:1px;
+ --size-item-small:16px;
+ --size-item-large:32px;
+ --color-canvas:white;
+
+ --toggle-background-color:var(--button-background-color);
+ --toggle-background-color-hover:var(--button-background-color-hover);
+ --toggle-background-color-active:var(--button-background-color-active);
+ --toggle-background-color-pressed:var(--color-accent-primary);
+ --toggle-background-color-pressed-hover:var(--color-accent-primary-hover);
+ --toggle-background-color-pressed-active:var(--color-accent-primary-active);
+ --toggle-border-color:var(--border-interactive-color);
+ --toggle-border-color-hover:var(--toggle-border-color);
+ --toggle-border-color-active:var(--toggle-border-color);
+ --toggle-border-radius:var(--border-radius-circle);
+ --toggle-border-width:var(--border-width);
+ --toggle-height:var(--size-item-small);
+ --toggle-width:var(--size-item-large);
+ --toggle-dot-background-color:var(--toggle-border-color);
+ --toggle-dot-background-color-hover:var(--toggle-dot-background-color);
+ --toggle-dot-background-color-active:var(--toggle-dot-background-color);
+ --toggle-dot-background-color-on-pressed:var(--color-canvas);
+ --toggle-dot-margin:1px;
+ --toggle-dot-height:calc(
+ var(--toggle-height) - 2 * var(--toggle-dot-margin) - 2 *
+ var(--toggle-border-width)
+ );
+ --toggle-dot-width:var(--toggle-dot-height);
+ --toggle-dot-transform-x:calc(
+ var(--toggle-width) - 4 * var(--toggle-dot-margin) - var(--toggle-dot-width)
+ );
+
+ -webkit-appearance:none;
+
+ -moz-appearance:none;
+
+ appearance:none;
+ padding:0;
+ margin:0;
+ border:var(--toggle-border-width) solid var(--toggle-border-color);
+ height:var(--toggle-height);
+ width:var(--toggle-width);
+ border-radius:var(--toggle-border-radius);
+ background:var(--toggle-background-color);
+ box-sizing:border-box;
+ flex-shrink:0;
+}
+
+@media (prefers-color-scheme: dark){
+
+:where(html:not(.is-light)) .toggle-button{
+ --button-background-color:color-mix(in srgb, currentColor 7%, transparent);
+ --button-background-color-hover:color-mix(
+ in srgb,
+ currentColor 14%,
+ transparent
+ );
+ --button-background-color-active:color-mix(
+ in srgb,
+ currentColor 21%,
+ transparent
+ );
+ --color-accent-primary:#0df;
+ --color-accent-primary-hover:#80ebff;
+ --color-accent-primary-active:#aaf2ff;
+ --border-interactive-color:#bfbfc9;
+ --color-canvas:#1c1b22;
+}
+ }
+
+:where(html.is-dark) .toggle-button{
+ --button-background-color:color-mix(in srgb, currentColor 7%, transparent);
+ --button-background-color-hover:color-mix(
+ in srgb,
+ currentColor 14%,
+ transparent
+ );
+ --button-background-color-active:color-mix(
+ in srgb,
+ currentColor 21%,
+ transparent
+ );
+ --color-accent-primary:#0df;
+ --color-accent-primary-hover:#80ebff;
+ --color-accent-primary-active:#aaf2ff;
+ --border-interactive-color:#bfbfc9;
+ --color-canvas:#1c1b22;
+}
+
+@media (forced-colors: active){
+
+.toggle-button{
+ --color-accent-primary:ButtonText;
+ --color-accent-primary-hover:SelectedItem;
+ --color-accent-primary-active:SelectedItem;
+ --border-interactive-color:ButtonText;
+ --button-background-color:ButtonFace;
+ --border-interactive-color-hover:SelectedItem;
+ --border-interactive-color-active:SelectedItem;
+ --border-interactive-color-disabled:GrayText;
+ --color-canvas:ButtonText;
+}
+ }
+
+.toggle-button:focus-visible{
+ outline:var(--focus-outline);
+ outline-offset:var(--focus-outline-offset);
+ }
+
+.toggle-button:enabled:hover{
+ background:var(--toggle-background-color-hover);
+ border-color:var(--toggle-border-color);
+ }
+
+.toggle-button:enabled:active{
+ background:var(--toggle-background-color-active);
+ border-color:var(--toggle-border-color);
+ }
+
+.toggle-button[aria-pressed="true"]{
+ background:var(--toggle-background-color-pressed);
+ border-color:transparent;
+ }
+
+.toggle-button[aria-pressed="true"]:enabled:hover{
+ background:var(--toggle-background-color-pressed-hover);
+ border-color:transparent;
+ }
+
+.toggle-button[aria-pressed="true"]:enabled:active{
+ background:var(--toggle-background-color-pressed-active);
+ border-color:transparent;
+ }
+
+.toggle-button::before{
+ display:block;
+ content:"";
+ background-color:var(--toggle-dot-background-color);
+ height:var(--toggle-dot-height);
+ width:var(--toggle-dot-width);
+ margin:var(--toggle-dot-margin);
+ border-radius:var(--toggle-border-radius);
+ translate:0;
+ }
+
+.toggle-button[aria-pressed="true"]::before{
+ translate:var(--toggle-dot-transform-x);
+ background-color:var(--toggle-dot-background-color-on-pressed);
+ }
+
+.toggle-button[aria-pressed="true"]:enabled:hover::before,
+ .toggle-button[aria-pressed="true"]:enabled:active::before{
+ background-color:var(--toggle-dot-background-color-on-pressed);
+ }
+
+[dir="rtl"] .toggle-button[aria-pressed="true"]::before{
+ translate:calc(-1 * var(--toggle-dot-transform-x));
+ }
+
+@media (prefers-reduced-motion: no-preference){
+ .toggle-button::before{
+ transition:translate 100ms;
+ }
+ }
+
+@media (prefers-contrast){
+ .toggle-button:enabled:hover{
+ border-color:var(--toggle-border-color-hover);
+ }
+
+ .toggle-button:enabled:active{
+ border-color:var(--toggle-border-color-active);
+ }
+
+ .toggle-button[aria-pressed="true"]:enabled{
+ border-color:var(--toggle-border-color);
+ position:relative;
+ }
+
+ .toggle-button[aria-pressed="true"]:enabled:hover,
+ .toggle-button[aria-pressed="true"]:enabled:hover:active{
+ border-color:var(--toggle-border-color-hover);
+ }
+
+ .toggle-button[aria-pressed="true"]:enabled:active{
+ background-color:var(--toggle-dot-background-color-active);
+ border-color:var(--toggle-dot-background-color-hover);
+ }
+
+ .toggle-button:hover::before,
+ .toggle-button:active::before{
+ background-color:var(--toggle-dot-background-color-hover);
+ }
+ }
+
+@media (forced-colors){
+
+.toggle-button{
+ --toggle-dot-background-color:var(--color-accent-primary);
+ --toggle-dot-background-color-hover:var(--color-accent-primary-hover);
+ --toggle-dot-background-color-active:var(--color-accent-primary-active);
+ --toggle-dot-background-color-on-pressed:var(--button-background-color);
+ --toggle-background-color-disabled:var(--button-background-color-disabled);
+ --toggle-border-color-hover:var(--border-interactive-color-hover);
+ --toggle-border-color-active:var(--border-interactive-color-active);
+ --toggle-border-color-disabled:var(--border-interactive-color-disabled);
+}
+
+ .toggle-button[aria-pressed="true"]:enabled::after{
+ border:1px solid var(--button-background-color);
+ content:"";
+ position:absolute;
+ height:var(--toggle-height);
+ width:var(--toggle-width);
+ display:block;
+ border-radius:var(--toggle-border-radius);
+ inset:-2px;
+ }
+
+ .toggle-button[aria-pressed="true"]:enabled:active::after{
+ border-color:var(--toggle-border-color-active);
+ }
+ }
+
+:root{
+ --outline-width:2px;
+ --outline-color:#0060df;
+ --outline-around-width:1px;
+ --outline-around-color:#f0f0f4;
+ --hover-outline-around-color:var(--outline-around-color);
+ --focus-outline:solid var(--outline-width) var(--outline-color);
+ --unfocus-outline:solid var(--outline-width) transparent;
+ --focus-outline-around:solid var(--outline-around-width) var(--outline-around-color);
+ --hover-outline-color:#8f8f9d;
+ --hover-outline:solid var(--outline-width) var(--hover-outline-color);
+ --hover-outline-around:solid var(--outline-around-width) var(--hover-outline-around-color);
+ --freetext-line-height:1.35;
+ --freetext-padding:2px;
+ --resizer-bg-color:var(--outline-color);
+ --resizer-size:6px;
+ --resizer-shift:calc(
+ 0px - (var(--outline-width) + var(--resizer-size)) / 2 -
+ var(--outline-around-width)
+ );
+ --editorFreeText-editing-cursor:text;
+ --editorInk-editing-cursor:url(images/cursor-editorInk.svg) 0 16, pointer;
+ --editorHighlight-editing-cursor:url(images/cursor-editorTextHighlight.svg) 24 24, text;
+ --editorFreeHighlight-editing-cursor:url(images/cursor-editorFreeHighlight.svg) 1 18, pointer;
+}
+.visuallyHidden{
+ position:absolute;
+ top:0;
+ left:0;
+ border:0;
+ margin:0;
+ padding:0;
+ width:0;
+ height:0;
+ overflow:hidden;
+ white-space:nowrap;
+ font-size:0;
+}
+
+.textLayer.highlighting{
+ cursor:var(--editorFreeHighlight-editing-cursor);
+}
+
+.textLayer.highlighting:not(.free) span{
+ cursor:var(--editorHighlight-editing-cursor);
+ }
+
+.textLayer.highlighting.free span{
+ cursor:var(--editorFreeHighlight-editing-cursor);
+ }
+
+@media (min-resolution: 1.1dppx){
+ :root{
+ --editorFreeText-editing-cursor:url(images/cursor-editorFreeText.svg) 0 16, text;
+ }
+}
+
+@media screen and (forced-colors: active){
+ :root{
+ --outline-color:CanvasText;
+ --outline-around-color:ButtonFace;
+ --resizer-bg-color:ButtonText;
+ --hover-outline-color:Highlight;
+ --hover-outline-around-color:SelectedItemText;
+ }
+}
+
+[data-editor-rotation="90"]{
+ transform:rotate(90deg);
+}
+
+[data-editor-rotation="180"]{
+ transform:rotate(180deg);
+}
+
+[data-editor-rotation="270"]{
+ transform:rotate(270deg);
+}
+
+.annotationEditorLayer{
+ background:transparent;
+ position:absolute;
+ inset:0;
+ font-size:calc(100px * var(--scale-factor));
+ transform-origin:0 0;
+ cursor:auto;
+}
+
+.annotationEditorLayer.waiting{
+ content:"";
+ cursor:wait;
+ position:absolute;
+ inset:0;
+ width:100%;
+ height:100%;
+}
+
+.annotationEditorLayer.disabled{
+ pointer-events:none;
+}
+
+.annotationEditorLayer.freetextEditing{
+ cursor:var(--editorFreeText-editing-cursor);
+}
+
+.annotationEditorLayer.inkEditing{
+ cursor:var(--editorInk-editing-cursor);
+}
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor){
+ position:absolute;
+ background:transparent;
+ z-index:1;
+ transform-origin:0 0;
+ cursor:auto;
+ max-width:100%;
+ max-height:100%;
+ border:var(--unfocus-outline);
+}
+
+.annotationEditorLayer .draggable.selectedEditor:is(.freeTextEditor, .inkEditor, .stampEditor){
+ cursor:move;
+ }
+
+.annotationEditorLayer .moving:is(.freeTextEditor, .inkEditor, .stampEditor){
+ touch-action:none;
+ }
+
+.annotationEditorLayer .selectedEditor:is(.freeTextEditor, .inkEditor, .stampEditor){
+ border:var(--focus-outline);
+ outline:var(--focus-outline-around);
+ }
+
+.annotationEditorLayer .selectedEditor:is(.freeTextEditor, .inkEditor, .stampEditor)::before{
+ content:"";
+ position:absolute;
+ inset:0;
+ border:var(--focus-outline-around);
+ pointer-events:none;
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor):hover:not(.selectedEditor){
+ border:var(--hover-outline);
+ outline:var(--hover-outline-around);
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor):hover:not(.selectedEditor)::before{
+ content:"";
+ position:absolute;
+ inset:0;
+ border:var(--focus-outline-around);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar{
+ --editor-toolbar-delete-image:url(images/editor-toolbar-delete.svg);
+ --editor-toolbar-bg-color:#f0f0f4;
+ --editor-toolbar-highlight-image:url(images/toolbarButton-editorHighlight.svg);
+ --editor-toolbar-fg-color:#2e2e56;
+ --editor-toolbar-border-color:#8f8f9d;
+ --editor-toolbar-hover-border-color:var(--editor-toolbar-border-color);
+ --editor-toolbar-hover-bg-color:#e0e0e6;
+ --editor-toolbar-hover-fg-color:var(--editor-toolbar-fg-color);
+ --editor-toolbar-hover-outline:none;
+ --editor-toolbar-focus-outline-color:#0060df;
+ --editor-toolbar-shadow:0 2px 6px 0 rgb(58 57 68 / 0.2);
+ --editor-toolbar-vert-offset:6px;
+ --editor-toolbar-height:28px;
+ --editor-toolbar-padding:2px;
+
+ display:flex;
+ width:-moz-fit-content;
+ width:fit-content;
+ height:var(--editor-toolbar-height);
+ flex-direction:column;
+ justify-content:center;
+ align-items:center;
+ cursor:default;
+ pointer-events:auto;
+ box-sizing:content-box;
+ padding:var(--editor-toolbar-padding);
+
+ position:absolute;
+ inset-inline-end:0;
+ inset-block-start:calc(100% + var(--editor-toolbar-vert-offset));
+
+ border-radius:6px;
+ background-color:var(--editor-toolbar-bg-color);
+ border:1px solid var(--editor-toolbar-border-color);
+ box-shadow:var(--editor-toolbar-shadow);
+ }
+
+@media (prefers-color-scheme: dark){
+
+:where(html:not(.is-light)) :is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar{
+ --editor-toolbar-bg-color:#2b2a33;
+ --editor-toolbar-fg-color:#fbfbfe;
+ --editor-toolbar-hover-bg-color:#52525e;
+ --editor-toolbar-focus-outline-color:#0df;
+ }
+ }
+
+:where(html.is-dark) :is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar{
+ --editor-toolbar-bg-color:#2b2a33;
+ --editor-toolbar-fg-color:#fbfbfe;
+ --editor-toolbar-hover-bg-color:#52525e;
+ --editor-toolbar-focus-outline-color:#0df;
+ }
+
+@media screen and (forced-colors: active){
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar{
+ --editor-toolbar-bg-color:ButtonFace;
+ --editor-toolbar-fg-color:ButtonText;
+ --editor-toolbar-border-color:ButtonText;
+ --editor-toolbar-hover-border-color:AccentColor;
+ --editor-toolbar-hover-bg-color:ButtonFace;
+ --editor-toolbar-hover-fg-color:AccentColor;
+ --editor-toolbar-hover-outline:2px solid var(--editor-toolbar-hover-border-color);
+ --editor-toolbar-focus-outline-color:ButtonBorder;
+ --editor-toolbar-shadow:none;
+ }
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar.hidden{
+ display:none;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar:has(:focus-visible){
+ border-color:transparent;
+ }
+
+[dir="ltr"] :is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar{
+ transform-origin:100% 0;
+ }
+
+[dir="rtl"] :is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar{
+ transform-origin:0 0;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons{
+ display:flex;
+ justify-content:center;
+ align-items:center;
+ gap:0;
+ height:100%;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .divider{
+ width:1px;
+ height:calc(
+ 2 * var(--editor-toolbar-padding) + var(--editor-toolbar-height)
+ );
+ background-color:var(--editor-toolbar-border-color);
+ display:inline-block;
+ margin-inline:2px;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .highlightButton{
+ width:var(--editor-toolbar-height);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .highlightButton::before{
+ content:"";
+ -webkit-mask-image:var(--editor-toolbar-highlight-image);
+ mask-image:var(--editor-toolbar-highlight-image);
+ -webkit-mask-repeat:no-repeat;
+ mask-repeat:no-repeat;
+ -webkit-mask-position:center;
+ mask-position:center;
+ display:inline-block;
+ background-color:var(--editor-toolbar-fg-color);
+ width:100%;
+ height:100%;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .highlightButton:hover::before{
+ background-color:var(--editor-toolbar-hover-fg-color);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .delete{
+ width:var(--editor-toolbar-height);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .delete::before{
+ content:"";
+ -webkit-mask-image:var(--editor-toolbar-delete-image);
+ mask-image:var(--editor-toolbar-delete-image);
+ -webkit-mask-repeat:no-repeat;
+ mask-repeat:no-repeat;
+ -webkit-mask-position:center;
+ mask-position:center;
+ display:inline-block;
+ background-color:var(--editor-toolbar-fg-color);
+ width:100%;
+ height:100%;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .delete:hover::before{
+ background-color:var(--editor-toolbar-hover-fg-color);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons > *{
+ height:var(--editor-toolbar-height);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons > :not(.divider){
+ border:none;
+ background-color:transparent;
+ cursor:pointer;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons > :not(.divider):hover{
+ border-radius:2px;
+ background-color:var(--editor-toolbar-hover-bg-color);
+ color:var(--editor-toolbar-hover-fg-color);
+ outline:var(--editor-toolbar-hover-outline);
+ outline-offset:1px;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons > :not(.divider):hover:active{
+ outline:none;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons > :not(.divider):focus-visible{
+ border-radius:2px;
+ outline:2px solid var(--editor-toolbar-focus-outline-color);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText{
+ --alt-text-add-image:url(images/altText_add.svg);
+ --alt-text-done-image:url(images/altText_done.svg);
+
+ display:flex;
+ align-items:center;
+ justify-content:center;
+ width:-moz-max-content;
+ width:max-content;
+ padding-inline:8px;
+ pointer-events:all;
+ font:menu;
+ font-weight:590;
+ font-size:12px;
+ color:var(--editor-toolbar-fg-color);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText:disabled{
+ pointer-events:none;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText::before{
+ content:"";
+ -webkit-mask-image:var(--alt-text-add-image);
+ mask-image:var(--alt-text-add-image);
+ -webkit-mask-repeat:no-repeat;
+ mask-repeat:no-repeat;
+ -webkit-mask-position:center;
+ mask-position:center;
+ display:inline-block;
+ width:12px;
+ height:13px;
+ background-color:var(--editor-toolbar-fg-color);
+ margin-inline-end:4px;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText:hover::before{
+ background-color:var(--editor-toolbar-hover-fg-color);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText.done::before{
+ -webkit-mask-image:var(--alt-text-done-image);
+ mask-image:var(--alt-text-done-image);
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText .tooltip{
+ display:none;
+ }
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText .tooltip.show{
+ --alt-text-tooltip-bg:#f0f0f4;
+ --alt-text-tooltip-fg:#15141a;
+ --alt-text-tooltip-border:#8f8f9d;
+ --alt-text-tooltip-shadow:0px 2px 6px 0px rgb(58 57 68 / 0.2);
+
+ display:inline-flex;
+ flex-direction:column;
+ align-items:center;
+ justify-content:center;
+ position:absolute;
+ top:calc(100% + 2px);
+ inset-inline-start:0;
+ padding-block:2px 3px;
+ padding-inline:3px;
+ max-width:300px;
+ width:-moz-max-content;
+ width:max-content;
+ height:auto;
+ font-size:12px;
+
+ border:0.5px solid var(--alt-text-tooltip-border);
+ background:var(--alt-text-tooltip-bg);
+ box-shadow:var(--alt-text-tooltip-shadow);
+ color:var(--alt-text-tooltip-fg);
+
+ pointer-events:none;
+ }
+
+@media (prefers-color-scheme: dark){
+
+:where(html:not(.is-light)) :is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText .tooltip.show{
+ --alt-text-tooltip-bg:#1c1b22;
+ --alt-text-tooltip-fg:#fbfbfe;
+ --alt-text-tooltip-shadow:0px 2px 6px 0px #15141a;
+ }
+ }
+
+:where(html.is-dark) :is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText .tooltip.show{
+ --alt-text-tooltip-bg:#1c1b22;
+ --alt-text-tooltip-fg:#fbfbfe;
+ --alt-text-tooltip-shadow:0px 2px 6px 0px #15141a;
+ }
+
+@media screen and (forced-colors: active){
+
+:is(.annotationEditorLayer
+ :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),.textLayer) .editToolbar .buttons .altText .tooltip.show{
+ --alt-text-tooltip-bg:Canvas;
+ --alt-text-tooltip-fg:CanvasText;
+ --alt-text-tooltip-border:CanvasText;
+ --alt-text-tooltip-shadow:none;
+ }
+ }
+
+.annotationEditorLayer .freeTextEditor{
+ padding:calc(var(--freetext-padding) * var(--scale-factor));
+ width:auto;
+ height:auto;
+ touch-action:none;
+}
+
+.annotationEditorLayer .freeTextEditor .internal{
+ background:transparent;
+ border:none;
+ inset:0;
+ overflow:visible;
+ white-space:nowrap;
+ font:10px sans-serif;
+ line-height:var(--freetext-line-height);
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+}
+
+.annotationEditorLayer .freeTextEditor .overlay{
+ position:absolute;
+ display:none;
+ background:transparent;
+ inset:0;
+ width:100%;
+ height:100%;
+}
+
+.annotationEditorLayer freeTextEditor .overlay.enabled{
+ display:block;
+}
+
+.annotationEditorLayer .freeTextEditor .internal:empty::before{
+ content:attr(default-content);
+ color:gray;
+}
+
+.annotationEditorLayer .freeTextEditor .internal:focus{
+ outline:none;
+ -webkit-user-select:auto;
+ -moz-user-select:auto;
+ user-select:auto;
+}
+
+.annotationEditorLayer .inkEditor{
+ width:100%;
+ height:100%;
+}
+
+.annotationEditorLayer .inkEditor.editing{
+ cursor:inherit;
+}
+
+.annotationEditorLayer .inkEditor .inkEditorCanvas{
+ position:absolute;
+ inset:0;
+ width:100%;
+ height:100%;
+ touch-action:none;
+}
+
+.annotationEditorLayer .stampEditor{
+ width:auto;
+ height:auto;
+}
+
+.annotationEditorLayer .stampEditor canvas{
+ position:absolute;
+ width:100%;
+ height:100%;
+ margin:0;
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers{
+ position:absolute;
+ inset:0;
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers.hidden{
+ display:none;
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers > .resizer{
+ width:var(--resizer-size);
+ height:var(--resizer-size);
+ background:content-box var(--resizer-bg-color);
+ border:var(--focus-outline-around);
+ border-radius:2px;
+ position:absolute;
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers > .resizer.topLeft{
+ top:var(--resizer-shift);
+ left:var(--resizer-shift);
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers > .resizer.topMiddle{
+ top:var(--resizer-shift);
+ left:calc(50% + var(--resizer-shift));
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers > .resizer.topRight{
+ top:var(--resizer-shift);
+ right:var(--resizer-shift);
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers > .resizer.middleRight{
+ top:calc(50% + var(--resizer-shift));
+ right:var(--resizer-shift);
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers > .resizer.bottomRight{
+ bottom:var(--resizer-shift);
+ right:var(--resizer-shift);
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers > .resizer.bottomMiddle{
+ bottom:var(--resizer-shift);
+ left:calc(50% + var(--resizer-shift));
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers > .resizer.bottomLeft{
+ bottom:var(--resizer-shift);
+ left:var(--resizer-shift);
+ }
+
+.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) > .resizers > .resizer.middleLeft{
+ top:calc(50% + var(--resizer-shift));
+ left:var(--resizer-shift);
+ }
+
+.annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topLeft,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topLeft,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topLeft,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topLeft,
+ .annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomRight,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomRight,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomRight,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomRight{
+ cursor:nwse-resize;
+ }
+
+.annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topMiddle,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topMiddle,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topMiddle,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topMiddle,
+ .annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomMiddle,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomMiddle,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomMiddle,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomMiddle{
+ cursor:ns-resize;
+ }
+
+.annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topRight,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topRight,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topRight,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topRight,
+ .annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomLeft,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomLeft,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomLeft,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomLeft{
+ cursor:nesw-resize;
+ }
+
+.annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleRight,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleRight,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleRight,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleRight,
+ .annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleLeft,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleLeft,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleLeft,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleLeft{
+ cursor:ew-resize;
+ }
+
+.annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topLeft,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topLeft,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topLeft,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topLeft,
+ .annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomRight,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomRight,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomRight,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomRight{
+ cursor:nesw-resize;
+ }
+
+.annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topMiddle,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topMiddle,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topMiddle,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topMiddle,
+ .annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomMiddle,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomMiddle,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomMiddle,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomMiddle{
+ cursor:ew-resize;
+ }
+
+.annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topRight,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topRight,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topRight,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topRight,
+ .annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomLeft,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomLeft,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomLeft,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomLeft{
+ cursor:nwse-resize;
+ }
+
+.annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleRight,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleRight,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleRight,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleRight,
+ .annotationEditorLayer[data-main-rotation="0"]
+ :is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleLeft,
+ .annotationEditorLayer[data-main-rotation="90"]
+ :is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleLeft,
+ .annotationEditorLayer[data-main-rotation="180"]
+ :is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleLeft,
+ .annotationEditorLayer[data-main-rotation="270"]
+ :is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleLeft{
+ cursor:ns-resize;
+ }
+
+.annotationEditorLayer
+ :is(
+ [data-main-rotation="0"] [data-editor-rotation="90"],
+ [data-main-rotation="90"] [data-editor-rotation="0"],
+ [data-main-rotation="180"] [data-editor-rotation="270"],
+ [data-main-rotation="270"] [data-editor-rotation="180"]
+ ) .editToolbar{
+ rotate:270deg;
+ }
+
+[dir="ltr"] .annotationEditorLayer
+ :is(
+ [data-main-rotation="0"] [data-editor-rotation="90"],
+ [data-main-rotation="90"] [data-editor-rotation="0"],
+ [data-main-rotation="180"] [data-editor-rotation="270"],
+ [data-main-rotation="270"] [data-editor-rotation="180"]
+ ) .editToolbar{
+ inset-inline-end:calc(0px - var(--editor-toolbar-vert-offset));
+ inset-block-start:0;
+ }
+
+[dir="rtl"] .annotationEditorLayer
+ :is(
+ [data-main-rotation="0"] [data-editor-rotation="90"],
+ [data-main-rotation="90"] [data-editor-rotation="0"],
+ [data-main-rotation="180"] [data-editor-rotation="270"],
+ [data-main-rotation="270"] [data-editor-rotation="180"]
+ ) .editToolbar{
+ inset-inline-end:calc(100% + var(--editor-toolbar-vert-offset));
+ inset-block-start:0;
+ }
+
+.annotationEditorLayer
+ :is(
+ [data-main-rotation="0"] [data-editor-rotation="180"],
+ [data-main-rotation="90"] [data-editor-rotation="90"],
+ [data-main-rotation="180"] [data-editor-rotation="0"],
+ [data-main-rotation="270"] [data-editor-rotation="270"]
+ ) .editToolbar{
+ rotate:180deg;
+ inset-inline-end:100%;
+ inset-block-start:calc(0pc - var(--editor-toolbar-vert-offset));
+ }
+
+.annotationEditorLayer
+ :is(
+ [data-main-rotation="0"] [data-editor-rotation="270"],
+ [data-main-rotation="90"] [data-editor-rotation="180"],
+ [data-main-rotation="180"] [data-editor-rotation="90"],
+ [data-main-rotation="270"] [data-editor-rotation="0"]
+ ) .editToolbar{
+ rotate:90deg;
+ }
+
+[dir="ltr"] .annotationEditorLayer
+ :is(
+ [data-main-rotation="0"] [data-editor-rotation="270"],
+ [data-main-rotation="90"] [data-editor-rotation="180"],
+ [data-main-rotation="180"] [data-editor-rotation="90"],
+ [data-main-rotation="270"] [data-editor-rotation="0"]
+ ) .editToolbar{
+ inset-inline-end:calc(100% + var(--editor-toolbar-vert-offset));
+ inset-block-start:100%;
+ }
+
+[dir="rtl"] .annotationEditorLayer
+ :is(
+ [data-main-rotation="0"] [data-editor-rotation="270"],
+ [data-main-rotation="90"] [data-editor-rotation="180"],
+ [data-main-rotation="180"] [data-editor-rotation="90"],
+ [data-main-rotation="270"] [data-editor-rotation="0"]
+ ) .editToolbar{
+ inset-inline-start:calc(0px - var(--editor-toolbar-vert-offset));
+ inset-block-start:0;
+ }
+
+.dialog.altText::backdrop{
+ -webkit-mask:url(#alttext-manager-mask);
+ mask:url(#alttext-manager-mask);
+ }
+
+.dialog.altText.positioned{
+ margin:0;
+ }
+
+.dialog.altText #altTextContainer{
+ width:300px;
+ height:-moz-fit-content;
+ height:fit-content;
+ display:inline-flex;
+ flex-direction:column;
+ align-items:flex-start;
+ gap:16px;
+ }
+
+.dialog.altText #altTextContainer #overallDescription{
+ display:flex;
+ flex-direction:column;
+ align-items:flex-start;
+ gap:4px;
+ align-self:stretch;
+ }
+
+.dialog.altText #altTextContainer #overallDescription span{
+ align-self:stretch;
+ }
+
+.dialog.altText #altTextContainer #overallDescription .title{
+ font-size:13px;
+ font-style:normal;
+ font-weight:590;
+ }
+
+.dialog.altText #altTextContainer #addDescription{
+ display:flex;
+ flex-direction:column;
+ align-items:stretch;
+ gap:8px;
+ }
+
+.dialog.altText #altTextContainer #addDescription .descriptionArea{
+ flex:1;
+ padding-inline:24px 10px;
+ }
+
+.dialog.altText #altTextContainer #addDescription .descriptionArea textarea{
+ width:100%;
+ min-height:75px;
+ }
+
+.dialog.altText #altTextContainer #buttons{
+ display:flex;
+ justify-content:flex-end;
+ align-items:flex-start;
+ gap:8px;
+ align-self:stretch;
+ }
+
+.colorPicker{
+ --hover-outline-color:#0250bb;
+ --selected-outline-color:#0060df;
+ --swatch-border-color:#cfcfd8;
+}
+
+@media (prefers-color-scheme: dark){
+
+:where(html:not(.is-light)) .colorPicker{
+ --hover-outline-color:#80ebff;
+ --selected-outline-color:#aaf2ff;
+ --swatch-border-color:#52525e;
+}
+ }
+
+:where(html.is-dark) .colorPicker{
+ --hover-outline-color:#80ebff;
+ --selected-outline-color:#aaf2ff;
+ --swatch-border-color:#52525e;
+}
+
+@media screen and (forced-colors: active){
+
+.colorPicker{
+ --hover-outline-color:Highlight;
+ --selected-outline-color:var(--hover-outline-color);
+ --swatch-border-color:ButtonText;
+}
+ }
+
+.colorPicker .swatch{
+ width:16px;
+ height:16px;
+ border:1px solid var(--swatch-border-color);
+ border-radius:100%;
+ outline-offset:2px;
+ box-sizing:border-box;
+ forced-color-adjust:none;
+ }
+
+.colorPicker button:is(:hover, .selected) > .swatch{
+ border:none;
+ }
+
+.annotationEditorLayer[data-main-rotation="0"] .highlightEditor:not(.free) > .editToolbar{
+ rotate:0deg;
+ }
+
+.annotationEditorLayer[data-main-rotation="90"] .highlightEditor:not(.free) > .editToolbar{
+ rotate:270deg;
+ }
+
+.annotationEditorLayer[data-main-rotation="180"] .highlightEditor:not(.free) > .editToolbar{
+ rotate:180deg;
+ }
+
+.annotationEditorLayer[data-main-rotation="270"] .highlightEditor:not(.free) > .editToolbar{
+ rotate:90deg;
+ }
+
+.annotationEditorLayer .highlightEditor{
+ position:absolute;
+ background:transparent;
+ z-index:1;
+ cursor:auto;
+ max-width:100%;
+ max-height:100%;
+ border:none;
+ outline:none;
+ pointer-events:none;
+ transform-origin:0 0;
+ }
+
+.annotationEditorLayer .highlightEditor:not(.free){
+ transform:none;
+ }
+
+.annotationEditorLayer .highlightEditor .internal{
+ position:absolute;
+ top:0;
+ left:0;
+ width:100%;
+ height:100%;
+ pointer-events:auto;
+ }
+
+.annotationEditorLayer .highlightEditor.disabled .internal{
+ pointer-events:none;
+ }
+
+.annotationEditorLayer .highlightEditor.selectedEditor .internal{
+ cursor:pointer;
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar{
+ --editor-toolbar-colorpicker-arrow-image:url(images/toolbarButton-menuArrow.svg);
+
+ transform-origin:center !important;
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker{
+ position:relative;
+ width:auto;
+ display:flex;
+ justify-content:center;
+ align-items:center;
+ gap:4px;
+ padding:4px;
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker::after{
+ content:"";
+ -webkit-mask-image:var(--editor-toolbar-colorpicker-arrow-image);
+ mask-image:var(--editor-toolbar-colorpicker-arrow-image);
+ -webkit-mask-repeat:no-repeat;
+ mask-repeat:no-repeat;
+ -webkit-mask-position:center;
+ mask-position:center;
+ display:inline-block;
+ background-color:var(--editor-toolbar-fg-color);
+ width:12px;
+ height:12px;
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker:hover::after{
+ background-color:var(--editor-toolbar-hover-fg-color);
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker:has(.dropdown:not(.hidden)){
+ background-color:var(--editor-toolbar-hover-bg-color);
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker:has(.dropdown:not(.hidden))::after{
+ scale:-1;
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker .dropdown{
+ position:absolute;
+ display:flex;
+ justify-content:center;
+ align-items:center;
+ flex-direction:column;
+ gap:11px;
+ padding-block:8px;
+ border-radius:6px;
+ background-color:var(--editor-toolbar-bg-color);
+ border:1px solid var(--editor-toolbar-border-color);
+ box-shadow:var(--editor-toolbar-shadow);
+ inset-block-start:calc(100% + 4px);
+ width:calc(100% + 2 * var(--editor-toolbar-padding));
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker .dropdown button{
+ width:100%;
+ height:auto;
+ border:none;
+ cursor:pointer;
+ display:flex;
+ justify-content:center;
+ align-items:center;
+ background:none;
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker .dropdown button:is(:active, :focus-visible){
+ outline:none;
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker .dropdown button > .swatch{
+ outline-offset:2px;
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker .dropdown button[aria-selected="true"] > .swatch{
+ outline:2px solid var(--selected-outline-color);
+ }
+
+.annotationEditorLayer .highlightEditor .editToolbar .buttons .colorPicker .dropdown button:is(:hover, :active, :focus-visible) > .swatch{
+ outline:2px solid var(--hover-outline-color);
+ }
+
+.editorParamsToolbar:has(#highlightParamsToolbarContainer){
+ padding:unset;
+}
+
+#highlightParamsToolbarContainer{
+ height:auto;
+ padding-inline:10px;
+ padding-block:10px 16px;
+ gap:16px;
+ display:flex;
+ flex-direction:column;
+ box-sizing:border-box;
+}
+
+#highlightParamsToolbarContainer .editorParamsLabel{
+ width:-moz-fit-content;
+ width:fit-content;
+ inset-inline-start:0;
+ }
+
+#highlightParamsToolbarContainer .colorPicker{
+ display:flex;
+ flex-direction:column;
+ gap:8px;
+ }
+
+#highlightParamsToolbarContainer .colorPicker .dropdown{
+ display:flex;
+ justify-content:space-between;
+ align-items:center;
+ flex-direction:row;
+ height:auto;
+ }
+
+#highlightParamsToolbarContainer .colorPicker .dropdown button{
+ width:auto;
+ height:auto;
+ border:none;
+ cursor:pointer;
+ display:flex;
+ justify-content:center;
+ align-items:center;
+ background:none;
+ flex:0 0 auto;
+ }
+
+#highlightParamsToolbarContainer .colorPicker .dropdown button .swatch{
+ width:24px;
+ height:24px;
+ }
+
+#highlightParamsToolbarContainer .colorPicker .dropdown button:is(:active, :focus-visible){
+ outline:none;
+ }
+
+#highlightParamsToolbarContainer .colorPicker .dropdown button[aria-selected="true"] > .swatch{
+ outline:2px solid var(--selected-outline-color);
+ }
+
+#highlightParamsToolbarContainer .colorPicker .dropdown button:is(:hover, :active, :focus-visible) > .swatch{
+ outline:2px solid var(--hover-outline-color);
+ }
+
+#highlightParamsToolbarContainer #editorHighlightThickness{
+ display:flex;
+ flex-direction:column;
+ align-items:center;
+ gap:4px;
+ align-self:stretch;
+ }
+
+#highlightParamsToolbarContainer #editorHighlightThickness .editorParamsLabel{
+ width:100%;
+ height:auto;
+ align-self:stretch;
+ }
+
+#highlightParamsToolbarContainer #editorHighlightThickness .thicknessPicker{
+ display:flex;
+ justify-content:space-between;
+ align-items:center;
+ align-self:stretch;
+
+ --example-color:#bfbfc9;
+ }
+
+@media (prefers-color-scheme: dark){
+
+:where(html:not(.is-light)) #highlightParamsToolbarContainer #editorHighlightThickness .thicknessPicker{
+ --example-color:#80808e;
+ }
+ }
+
+:where(html.is-dark) #highlightParamsToolbarContainer #editorHighlightThickness .thicknessPicker{
+ --example-color:#80808e;
+ }
+
+@media screen and (forced-colors: active){
+
+#highlightParamsToolbarContainer #editorHighlightThickness .thicknessPicker{
+ --example-color:CanvasText;
+ }
+ }
+
+:is(#highlightParamsToolbarContainer #editorHighlightThickness .thicknessPicker > .editorParamsSlider[disabled]){
+ opacity:0.4;
+ }
+
+#highlightParamsToolbarContainer #editorHighlightThickness .thicknessPicker::before,
+ #highlightParamsToolbarContainer #editorHighlightThickness .thicknessPicker::after{
+ content:"";
+ width:8px;
+ aspect-ratio:1;
+ display:block;
+ border-radius:100%;
+ background-color:var(--example-color);
+ }
+
+#highlightParamsToolbarContainer #editorHighlightThickness .thicknessPicker::after{
+ width:24px;
+ }
+
+#highlightParamsToolbarContainer #editorHighlightThickness .thicknessPicker .editorParamsSlider{
+ width:unset;
+ height:14px;
+ }
+
+#highlightParamsToolbarContainer #editorHighlightVisibility{
+ display:flex;
+ flex-direction:column;
+ align-items:flex-start;
+ gap:8px;
+ align-self:stretch;
+ }
+
+#highlightParamsToolbarContainer #editorHighlightVisibility .divider{
+ --divider-color:#d7d7db;
+
+ margin-block:4px;
+ width:100%;
+ height:1px;
+ background-color:var(--divider-color);
+ }
+
+@media (prefers-color-scheme: dark){
+
+:where(html:not(.is-light)) #highlightParamsToolbarContainer #editorHighlightVisibility .divider{
+ --divider-color:#8f8f9d;
+ }
+ }
+
+:where(html.is-dark) #highlightParamsToolbarContainer #editorHighlightVisibility .divider{
+ --divider-color:#8f8f9d;
+ }
+
+@media screen and (forced-colors: active){
+
+#highlightParamsToolbarContainer #editorHighlightVisibility .divider{
+ --divider-color:CanvasText;
+ }
+ }
+
+#highlightParamsToolbarContainer #editorHighlightVisibility .toggler{
+ display:flex;
+ justify-content:space-between;
+ align-items:center;
+ align-self:stretch;
+ }
+
+:root{
+ --viewer-container-height:0;
+ --pdfViewer-padding-bottom:0;
+ --page-margin:1px auto -8px;
+ --page-border:9px solid transparent;
+ --spreadHorizontalWrapped-margin-LR:-3.5px;
+ --loading-icon-delay:400ms;
+}
+
+@media screen and (forced-colors: active){
+ :root{
+ --pdfViewer-padding-bottom:9px;
+ --page-margin:8px auto -1px;
+ --page-border:1px solid CanvasText;
+ --spreadHorizontalWrapped-margin-LR:3.5px;
+ }
+}
+
+[data-main-rotation="90"]{
+ transform:rotate(90deg) translateY(-100%);
+}
+[data-main-rotation="180"]{
+ transform:rotate(180deg) translate(-100%, -100%);
+}
+[data-main-rotation="270"]{
+ transform:rotate(270deg) translateX(-100%);
+}
+
+#hiddenCopyElement,
+.hiddenCanvasElement{
+ position:absolute;
+ top:0;
+ left:0;
+ width:0;
+ height:0;
+ display:none;
+}
+
+.pdfViewer{
+ --scale-factor:1;
+
+ padding-bottom:var(--pdfViewer-padding-bottom);
+
+ --hcm-highlight-filter:none;
+ --hcm-highlight-selected-filter:none;
+}
+
+@media screen and (forced-colors: active){
+
+.pdfViewer{
+ --hcm-highlight-filter:invert(100%);
+}
+ }
+
+.pdfViewer .canvasWrapper{
+ overflow:hidden;
+ width:100%;
+ height:100%;
+ }
+
+.pdfViewer .canvasWrapper canvas{
+ margin:0;
+ display:block;
+ }
+
+.pdfViewer .canvasWrapper canvas[hidden]{
+ display:none;
+ }
+
+.pdfViewer .canvasWrapper canvas[zooming]{
+ width:100%;
+ height:100%;
+ }
+
+.pdfViewer .canvasWrapper canvas .structTree{
+ contain:strict;
+ }
+
+.pdfViewer .page{
+ direction:ltr;
+ width:816px;
+ height:1056px;
+ margin:var(--page-margin);
+ position:relative;
+ overflow:visible;
+ border:var(--page-border);
+ background-clip:content-box;
+ background-color:rgb(255 255 255);
+}
+
+.pdfViewer .dummyPage{
+ position:relative;
+ width:0;
+ height:var(--viewer-container-height);
+}
+
+.pdfViewer.noUserSelect{
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+}
+
+.pdfViewer.removePageBorders .page{
+ margin:0 auto 10px;
+ border:none;
+}
+
+.pdfViewer:is(.scrollHorizontal, .scrollWrapped),
+.spread{
+ margin-inline:3.5px;
+ text-align:center;
+}
+
+.pdfViewer.scrollHorizontal,
+.spread{
+ white-space:nowrap;
+}
+
+.pdfViewer.removePageBorders,
+.pdfViewer:is(.scrollHorizontal, .scrollWrapped) .spread{
+ margin-inline:0;
+}
+
+.spread :is(.page, .dummyPage),
+.pdfViewer:is(.scrollHorizontal, .scrollWrapped) :is(.page, .spread){
+ display:inline-block;
+ vertical-align:middle;
+}
+
+.spread .page,
+.pdfViewer:is(.scrollHorizontal, .scrollWrapped) .page{
+ margin-inline:var(--spreadHorizontalWrapped-margin-LR);
+}
+
+.pdfViewer.removePageBorders .spread .page,
+.pdfViewer.removePageBorders:is(.scrollHorizontal, .scrollWrapped) .page{
+ margin-inline:5px;
+}
+
+.pdfViewer .page.loadingIcon::after{
+ position:absolute;
+ top:0;
+ left:0;
+ content:"";
+ width:100%;
+ height:100%;
+ background:url("images/loading-icon.gif") center no-repeat;
+ display:none;
+ transition-property:display;
+ transition-delay:var(--loading-icon-delay);
+ z-index:5;
+ contain:strict;
+}
+
+.pdfViewer .page.loading::after{
+ display:block;
+}
+
+.pdfViewer .page:not(.loading)::after{
+ transition-property:none;
+ display:none;
+}
+
+.pdfPresentationMode .pdfViewer{
+ padding-bottom:0;
+}
+
+.pdfPresentationMode .spread{
+ margin:0;
+}
+
+.pdfPresentationMode .pdfViewer .page{
+ margin:0 auto;
+ border:2px solid transparent;
+}
+
+:root{
+ --dir-factor:1;
+ --inline-start:left;
+ --inline-end:right;
+
+ --sidebar-width:200px;
+ --sidebar-transition-duration:200ms;
+ --sidebar-transition-timing-function:ease;
+
+ --toolbar-icon-opacity:0.7;
+ --doorhanger-icon-opacity:0.9;
+ --editor-toolbar-base-offset:105px;
+
+ --main-color:rgb(12 12 13);
+ --body-bg-color:rgb(212 212 215);
+ --progressBar-color:rgb(10 132 255);
+ --progressBar-bg-color:rgb(221 221 222);
+ --progressBar-blend-color:rgb(116 177 239);
+ --scrollbar-color:auto;
+ --scrollbar-bg-color:auto;
+ --toolbar-icon-bg-color:rgb(0 0 0);
+ --toolbar-icon-hover-bg-color:rgb(0 0 0);
+
+ --sidebar-narrow-bg-color:rgb(212 212 215 / 0.9);
+ --sidebar-toolbar-bg-color:rgb(245 246 247);
+ --toolbar-bg-color:rgb(249 249 250);
+ --toolbar-border-color:rgb(184 184 184);
+ --toolbar-box-shadow:0 1px 0 var(--toolbar-border-color);
+ --toolbar-border-bottom:none;
+ --toolbarSidebar-box-shadow:inset calc(-1px * var(--dir-factor)) 0 0 rgb(0 0 0 / 0.25), 0 1px 0 rgb(0 0 0 / 0.15), 0 0 1px rgb(0 0 0 / 0.1);
+ --toolbarSidebar-border-bottom:none;
+ --button-hover-color:rgb(221 222 223);
+ --toggled-btn-color:rgb(0 0 0);
+ --toggled-btn-bg-color:rgb(0 0 0 / 0.3);
+ --toggled-hover-active-btn-color:rgb(0 0 0 / 0.4);
+ --toggled-hover-btn-outline:none;
+ --dropdown-btn-bg-color:rgb(215 215 219);
+ --dropdown-btn-border:none;
+ --separator-color:rgb(0 0 0 / 0.3);
+ --field-color:rgb(6 6 6);
+ --field-bg-color:rgb(255 255 255);
+ --field-border-color:rgb(187 187 188);
+ --treeitem-color:rgb(0 0 0 / 0.8);
+ --treeitem-bg-color:rgb(0 0 0 / 0.15);
+ --treeitem-hover-color:rgb(0 0 0 / 0.9);
+ --treeitem-selected-color:rgb(0 0 0 / 0.9);
+ --treeitem-selected-bg-color:rgb(0 0 0 / 0.25);
+ --thumbnail-hover-color:rgb(0 0 0 / 0.1);
+ --thumbnail-selected-color:rgb(0 0 0 / 0.2);
+ --doorhanger-bg-color:rgb(255 255 255);
+ --doorhanger-border-color:rgb(12 12 13 / 0.2);
+ --doorhanger-hover-color:rgb(12 12 13);
+ --doorhanger-hover-bg-color:rgb(237 237 237);
+ --doorhanger-separator-color:rgb(222 222 222);
+ --dialog-button-border:none;
+ --dialog-button-bg-color:rgb(12 12 13 / 0.1);
+ --dialog-button-hover-bg-color:rgb(12 12 13 / 0.3);
+
+ --loading-icon:url(images/loading.svg);
+ --treeitem-expanded-icon:url(images/treeitem-expanded.svg);
+ --treeitem-collapsed-icon:url(images/treeitem-collapsed.svg);
+ --toolbarButton-editorFreeText-icon:url(images/toolbarButton-editorFreeText.svg);
+ --toolbarButton-editorHighlight-icon:url(images/toolbarButton-editorHighlight.svg);
+ --toolbarButton-editorInk-icon:url(images/toolbarButton-editorInk.svg);
+ --toolbarButton-editorStamp-icon:url(images/toolbarButton-editorStamp.svg);
+ --toolbarButton-menuArrow-icon:url(images/toolbarButton-menuArrow.svg);
+ --toolbarButton-sidebarToggle-icon:url(images/toolbarButton-sidebarToggle.svg);
+ --toolbarButton-secondaryToolbarToggle-icon:url(images/toolbarButton-secondaryToolbarToggle.svg);
+ --toolbarButton-pageUp-icon:url(images/toolbarButton-pageUp.svg);
+ --toolbarButton-pageDown-icon:url(images/toolbarButton-pageDown.svg);
+ --toolbarButton-zoomOut-icon:url(images/toolbarButton-zoomOut.svg);
+ --toolbarButton-zoomIn-icon:url(images/toolbarButton-zoomIn.svg);
+ --toolbarButton-presentationMode-icon:url(images/toolbarButton-presentationMode.svg);
+ --toolbarButton-print-icon:url(images/toolbarButton-print.svg);
+ --toolbarButton-openFile-icon:url(images/toolbarButton-openFile.svg);
+ --toolbarButton-download-icon:url(images/toolbarButton-download.svg);
+ --toolbarButton-bookmark-icon:url(images/toolbarButton-bookmark.svg);
+ --toolbarButton-viewThumbnail-icon:url(images/toolbarButton-viewThumbnail.svg);
+ --toolbarButton-viewOutline-icon:url(images/toolbarButton-viewOutline.svg);
+ --toolbarButton-viewAttachments-icon:url(images/toolbarButton-viewAttachments.svg);
+ --toolbarButton-viewLayers-icon:url(images/toolbarButton-viewLayers.svg);
+ --toolbarButton-currentOutlineItem-icon:url(images/toolbarButton-currentOutlineItem.svg);
+ --toolbarButton-search-icon:url(images/toolbarButton-search.svg);
+ --findbarButton-previous-icon:url(images/findbarButton-previous.svg);
+ --findbarButton-next-icon:url(images/findbarButton-next.svg);
+ --secondaryToolbarButton-firstPage-icon:url(images/secondaryToolbarButton-firstPage.svg);
+ --secondaryToolbarButton-lastPage-icon:url(images/secondaryToolbarButton-lastPage.svg);
+ --secondaryToolbarButton-rotateCcw-icon:url(images/secondaryToolbarButton-rotateCcw.svg);
+ --secondaryToolbarButton-rotateCw-icon:url(images/secondaryToolbarButton-rotateCw.svg);
+ --secondaryToolbarButton-selectTool-icon:url(images/secondaryToolbarButton-selectTool.svg);
+ --secondaryToolbarButton-handTool-icon:url(images/secondaryToolbarButton-handTool.svg);
+ --secondaryToolbarButton-scrollPage-icon:url(images/secondaryToolbarButton-scrollPage.svg);
+ --secondaryToolbarButton-scrollVertical-icon:url(images/secondaryToolbarButton-scrollVertical.svg);
+ --secondaryToolbarButton-scrollHorizontal-icon:url(images/secondaryToolbarButton-scrollHorizontal.svg);
+ --secondaryToolbarButton-scrollWrapped-icon:url(images/secondaryToolbarButton-scrollWrapped.svg);
+ --secondaryToolbarButton-spreadNone-icon:url(images/secondaryToolbarButton-spreadNone.svg);
+ --secondaryToolbarButton-spreadOdd-icon:url(images/secondaryToolbarButton-spreadOdd.svg);
+ --secondaryToolbarButton-spreadEven-icon:url(images/secondaryToolbarButton-spreadEven.svg);
+ --secondaryToolbarButton-documentProperties-icon:url(images/secondaryToolbarButton-documentProperties.svg);
+ --editorParams-stampAddImage-icon:url(images/toolbarButton-zoomIn.svg);
+}
+
+[dir="rtl"]:root{
+ --dir-factor:-1;
+ --inline-start:right;
+ --inline-end:left;
+}
+
+@media (prefers-color-scheme: dark){
+ :root:where(:not(.is-light)){
+ --main-color:rgb(249 249 250);
+ --body-bg-color:rgb(42 42 46);
+ --progressBar-color:rgb(0 96 223);
+ --progressBar-bg-color:rgb(40 40 43);
+ --progressBar-blend-color:rgb(20 68 133);
+ --scrollbar-color:rgb(121 121 123);
+ --scrollbar-bg-color:rgb(35 35 39);
+ --toolbar-icon-bg-color:rgb(255 255 255);
+ --toolbar-icon-hover-bg-color:rgb(255 255 255);
+
+ --sidebar-narrow-bg-color:rgb(42 42 46 / 0.9);
+ --sidebar-toolbar-bg-color:rgb(50 50 52);
+ --toolbar-bg-color:rgb(56 56 61);
+ --toolbar-border-color:rgb(12 12 13);
+ --button-hover-color:rgb(102 102 103);
+ --toggled-btn-color:rgb(255 255 255);
+ --toggled-btn-bg-color:rgb(0 0 0 / 0.3);
+ --toggled-hover-active-btn-color:rgb(0 0 0 / 0.4);
+ --dropdown-btn-bg-color:rgb(74 74 79);
+ --separator-color:rgb(0 0 0 / 0.3);
+ --field-color:rgb(250 250 250);
+ --field-bg-color:rgb(64 64 68);
+ --field-border-color:rgb(115 115 115);
+ --treeitem-color:rgb(255 255 255 / 0.8);
+ --treeitem-bg-color:rgb(255 255 255 / 0.15);
+ --treeitem-hover-color:rgb(255 255 255 / 0.9);
+ --treeitem-selected-color:rgb(255 255 255 / 0.9);
+ --treeitem-selected-bg-color:rgb(255 255 255 / 0.25);
+ --thumbnail-hover-color:rgb(255 255 255 / 0.1);
+ --thumbnail-selected-color:rgb(255 255 255 / 0.2);
+ --doorhanger-bg-color:rgb(74 74 79);
+ --doorhanger-border-color:rgb(39 39 43);
+ --doorhanger-hover-color:rgb(249 249 250);
+ --doorhanger-hover-bg-color:rgb(93 94 98);
+ --doorhanger-separator-color:rgb(92 92 97);
+ --dialog-button-bg-color:rgb(92 92 97);
+ --dialog-button-hover-bg-color:rgb(115 115 115);
+ }
+}
+
+:root:where(.is-dark){
+ --main-color:rgb(249 249 250);
+ --body-bg-color:rgb(42 42 46);
+ --progressBar-color:rgb(0 96 223);
+ --progressBar-bg-color:rgb(40 40 43);
+ --progressBar-blend-color:rgb(20 68 133);
+ --scrollbar-color:rgb(121 121 123);
+ --scrollbar-bg-color:rgb(35 35 39);
+ --toolbar-icon-bg-color:rgb(255 255 255);
+ --toolbar-icon-hover-bg-color:rgb(255 255 255);
+
+ --sidebar-narrow-bg-color:rgb(42 42 46 / 0.9);
+ --sidebar-toolbar-bg-color:rgb(50 50 52);
+ --toolbar-bg-color:rgb(56 56 61);
+ --toolbar-border-color:rgb(12 12 13);
+ --button-hover-color:rgb(102 102 103);
+ --toggled-btn-color:rgb(255 255 255);
+ --toggled-btn-bg-color:rgb(0 0 0 / 0.3);
+ --toggled-hover-active-btn-color:rgb(0 0 0 / 0.4);
+ --dropdown-btn-bg-color:rgb(74 74 79);
+ --separator-color:rgb(0 0 0 / 0.3);
+ --field-color:rgb(250 250 250);
+ --field-bg-color:rgb(64 64 68);
+ --field-border-color:rgb(115 115 115);
+ --treeitem-color:rgb(255 255 255 / 0.8);
+ --treeitem-bg-color:rgb(255 255 255 / 0.15);
+ --treeitem-hover-color:rgb(255 255 255 / 0.9);
+ --treeitem-selected-color:rgb(255 255 255 / 0.9);
+ --treeitem-selected-bg-color:rgb(255 255 255 / 0.25);
+ --thumbnail-hover-color:rgb(255 255 255 / 0.1);
+ --thumbnail-selected-color:rgb(255 255 255 / 0.2);
+ --doorhanger-bg-color:rgb(74 74 79);
+ --doorhanger-border-color:rgb(39 39 43);
+ --doorhanger-hover-color:rgb(249 249 250);
+ --doorhanger-hover-bg-color:rgb(93 94 98);
+ --doorhanger-separator-color:rgb(92 92 97);
+ --dialog-button-bg-color:rgb(92 92 97);
+ --dialog-button-hover-bg-color:rgb(115 115 115);
+ }
+
+@media screen and (forced-colors: active){
+ :root{
+ --button-hover-color:Highlight;
+ --doorhanger-hover-bg-color:Highlight;
+ --toolbar-icon-opacity:1;
+ --toolbar-icon-bg-color:ButtonText;
+ --toolbar-icon-hover-bg-color:ButtonFace;
+ --toggled-hover-active-btn-color:ButtonText;
+ --toggled-hover-btn-outline:2px solid ButtonBorder;
+ --toolbar-border-color:CanvasText;
+ --toolbar-border-bottom:1px solid var(--toolbar-border-color);
+ --toolbar-box-shadow:none;
+ --toggled-btn-color:HighlightText;
+ --toggled-btn-bg-color:LinkText;
+ --doorhanger-hover-color:ButtonFace;
+ --doorhanger-border-color-whcm:1px solid ButtonText;
+ --doorhanger-triangle-opacity-whcm:0;
+ --dialog-button-border:1px solid Highlight;
+ --dialog-button-hover-bg-color:Highlight;
+ --dialog-button-hover-color:ButtonFace;
+ --dropdown-btn-border:1px solid ButtonText;
+ --field-border-color:ButtonText;
+ --main-color:CanvasText;
+ --separator-color:GrayText;
+ --doorhanger-separator-color:GrayText;
+ --toolbarSidebar-box-shadow:none;
+ --toolbarSidebar-border-bottom:1px solid var(--toolbar-border-color);
+ }
+}
+
+@media screen and (prefers-reduced-motion: reduce){
+ :root{
+ --sidebar-transition-duration:0;
+ }
+}
+
+*{
+ padding:0;
+ margin:0;
+}
+
+html,
+body{
+ height:100%;
+ width:100%;
+}
+
+body{
+ background-color:var(--body-bg-color);
+ scrollbar-color:var(--scrollbar-color) var(--scrollbar-bg-color);
+}
+
+.hidden,
+[hidden]{
+ display:none !important;
+}
+
+#viewerContainer.pdfPresentationMode:fullscreen{
+ top:0;
+ background-color:rgb(0 0 0);
+ width:100%;
+ height:100%;
+ overflow:hidden;
+ cursor:none;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+}
+
+.pdfPresentationMode:fullscreen section:not([data-internal-link]){
+ pointer-events:none;
+}
+
+.pdfPresentationMode:fullscreen .textLayer span{
+ cursor:none;
+}
+
+.pdfPresentationMode.pdfPresentationModeControls > *,
+.pdfPresentationMode.pdfPresentationModeControls .textLayer span{
+ cursor:default;
+}
+
+#outerContainer{
+ width:100%;
+ height:100%;
+ position:relative;
+}
+
+#sidebarContainer{
+ position:absolute;
+ inset-block:32px 0;
+ inset-inline-start:calc(-1 * var(--sidebar-width));
+ width:var(--sidebar-width);
+ visibility:hidden;
+ z-index:100;
+ font:message-box;
+ border-top:1px solid rgb(51 51 51);
+ border-inline-end:var(--doorhanger-border-color-whcm);
+ transition-property:inset-inline-start;
+ transition-duration:var(--sidebar-transition-duration);
+ transition-timing-function:var(--sidebar-transition-timing-function);
+}
+
+#outerContainer:is(.sidebarMoving, .sidebarOpen) #sidebarContainer{
+ visibility:visible;
+}
+#outerContainer.sidebarOpen #sidebarContainer{
+ inset-inline-start:0;
+}
+
+#mainContainer{
+ position:absolute;
+ inset:0;
+ min-width:350px;
+}
+
+#sidebarContent{
+ inset-block:32px 0;
+ inset-inline-start:0;
+ overflow:auto;
+ position:absolute;
+ width:100%;
+ box-shadow:inset calc(-1px * var(--dir-factor)) 0 0 rgb(0 0 0 / 0.25);
+}
+
+#viewerContainer{
+ overflow:auto;
+ position:absolute;
+ inset:32px 0 0;
+ outline:none;
+}
+#viewerContainer:not(.pdfPresentationMode){
+ transition-duration:var(--sidebar-transition-duration);
+ transition-timing-function:var(--sidebar-transition-timing-function);
+}
+
+#outerContainer.sidebarOpen #viewerContainer:not(.pdfPresentationMode){
+ inset-inline-start:var(--sidebar-width);
+ transition-property:inset-inline-start;
+}
+
+.toolbar{
+ position:relative;
+ inset-inline:0;
+ z-index:9999;
+ cursor:default;
+ font:message-box;
+}
+
+:is(.toolbar, .editorParamsToolbar, .findbar, #sidebarContainer)
+ :is(input, button, select),
+.secondaryToolbar :is(input, button, a, select){
+ outline:none;
+ font:message-box;
+}
+
+#toolbarContainer{
+ width:100%;
+}
+
+#toolbarSidebar{
+ width:100%;
+ height:32px;
+ background-color:var(--sidebar-toolbar-bg-color);
+ box-shadow:var(--toolbarSidebar-box-shadow);
+ border-bottom:var(--toolbarSidebar-border-bottom);
+}
+
+#sidebarResizer{
+ position:absolute;
+ inset-block:0;
+ inset-inline-end:-6px;
+ width:6px;
+ z-index:200;
+ cursor:ew-resize;
+}
+
+#toolbarContainer,
+.findbar,
+.secondaryToolbar,
+.editorParamsToolbar{
+ position:relative;
+ height:32px;
+ background-color:var(--toolbar-bg-color);
+ box-shadow:var(--toolbar-box-shadow);
+ border-bottom:var(--toolbar-border-bottom);
+}
+
+#toolbarViewer{
+ height:32px;
+}
+
+#loadingBar{
+ --progressBar-percent:0%;
+ --progressBar-end-offset:0;
+
+ position:absolute;
+ inset-inline:0 var(--progressBar-end-offset);
+ height:4px;
+ background-color:var(--progressBar-bg-color);
+ border-bottom:1px solid var(--toolbar-border-color);
+ transition-property:inset-inline-start;
+ transition-duration:var(--sidebar-transition-duration);
+ transition-timing-function:var(--sidebar-transition-timing-function);
+}
+
+#outerContainer.sidebarOpen #loadingBar{
+ inset-inline-start:var(--sidebar-width);
+}
+
+#loadingBar .progress{
+ position:absolute;
+ top:0;
+ inset-inline-start:0;
+ width:100%;
+ transform:scaleX(var(--progressBar-percent));
+ transform-origin:calc(50% - 50% * var(--dir-factor)) 0;
+ height:100%;
+ background-color:var(--progressBar-color);
+ overflow:hidden;
+ transition:transform 200ms;
+}
+
+@keyframes progressIndeterminate{
+ 0%{
+ transform:translateX(calc(-142px * var(--dir-factor)));
+ }
+ 100%{
+ transform:translateX(0);
+ }
+}
+
+#loadingBar.indeterminate .progress{
+ transform:none;
+ background-color:var(--progressBar-bg-color);
+ transition:none;
+}
+
+#loadingBar.indeterminate .progress .glimmer{
+ position:absolute;
+ top:0;
+ inset-inline-start:0;
+ height:100%;
+ width:calc(100% + 150px);
+ background:repeating-linear-gradient(
+ 135deg,
+ var(--progressBar-blend-color) 0,
+ var(--progressBar-bg-color) 5px,
+ var(--progressBar-bg-color) 45px,
+ var(--progressBar-color) 55px,
+ var(--progressBar-color) 95px,
+ var(--progressBar-blend-color) 100px
+ );
+ animation:progressIndeterminate 1s linear infinite;
+}
+
+#outerContainer.sidebarResizing
+ :is(#sidebarContainer, #viewerContainer, #loadingBar){
+ transition-duration:0s;
+}
+
+.findbar,
+.secondaryToolbar,
+.editorParamsToolbar{
+ top:32px;
+ position:absolute;
+ z-index:30000;
+ height:auto;
+ padding:0 4px;
+ margin:4px 2px;
+ font:message-box;
+ font-size:12px;
+ line-height:14px;
+ text-align:left;
+ cursor:default;
+}
+
+.findbar{
+ inset-inline-start:64px;
+ min-width:300px;
+ background-color:var(--toolbar-bg-color);
+}
+.findbar > div{
+ height:32px;
+}
+.findbar > div#findbarInputContainer{
+ margin-inline-end:4px;
+}
+.findbar.wrapContainers > div,
+.findbar.wrapContainers > div#findbarMessageContainer > *{
+ clear:both;
+}
+.findbar.wrapContainers > div#findbarMessageContainer{
+ height:auto;
+}
+
+.findbar input[type="checkbox"]{
+ pointer-events:none;
+}
+
+.findbar label{
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+}
+
+.findbar label:hover,
+.findbar input:focus-visible + label{
+ color:var(--toggled-btn-color);
+ background-color:var(--button-hover-color);
+}
+
+.findbar .toolbarField[type="checkbox"]:checked + .toolbarLabel{
+ background-color:var(--toggled-btn-bg-color) !important;
+ color:var(--toggled-btn-color);
+}
+
+#findInput{
+ width:200px;
+}
+
+#findInput::-moz-placeholder{
+ font-style:normal;
+ }
+
+#findInput::placeholder{
+ font-style:normal;
+ }
+
+.loadingInput:has(> #findInput[data-status="pending"])::after{
+ display:block;
+ visibility:visible;
+ }
+
+#findInput[data-status="notFound"]{
+ background-color:rgb(255 102 102);
+ }
+
+.secondaryToolbar,
+.editorParamsToolbar{
+ padding:6px 0 10px;
+ inset-inline-end:4px;
+ height:auto;
+ background-color:var(--doorhanger-bg-color);
+}
+
+.editorParamsToolbarContainer{
+ width:220px;
+ margin-bottom:-4px;
+}
+
+.editorParamsToolbarContainer > .editorParamsSetter{
+ min-height:26px;
+ display:flex;
+ align-items:center;
+ justify-content:space-between;
+ padding-inline:10px;
+}
+
+.editorParamsToolbarContainer .editorParamsLabel{
+ padding-inline-end:10px;
+ flex:none;
+ font:menu;
+ font-size:13px;
+ font-style:normal;
+ font-weight:400;
+ line-height:150%;
+ color:var(--main-color);
+}
+
+.editorParamsToolbarContainer .editorParamsColor{
+ width:32px;
+ height:32px;
+ flex:none;
+}
+
+.editorParamsToolbarContainer .editorParamsSlider{
+ background-color:transparent;
+ width:90px;
+ flex:0 1 0;
+}
+
+.editorParamsToolbarContainer .editorParamsSlider::-moz-range-progress{
+ background-color:black;
+}
+
+.editorParamsToolbarContainer .editorParamsSlider::-webkit-slider-runnable-track,
+.editorParamsToolbarContainer .editorParamsSlider::-moz-range-track{
+ background-color:black;
+}
+
+.editorParamsToolbarContainer .editorParamsSlider::-webkit-slider-thumb,
+.editorParamsToolbarContainer .editorParamsSlider::-moz-range-thumb{
+ background-color:white;
+}
+
+#secondaryToolbarButtonContainer{
+ max-width:220px;
+ min-height:26px;
+ max-height:calc(var(--viewer-container-height) - 40px);
+ overflow-y:auto;
+ margin-bottom:-4px;
+}
+
+#editorStampParamsToolbar{
+ inset-inline-end:calc(var(--editor-toolbar-base-offset) + 0px);
+}
+
+#editorInkParamsToolbar{
+ inset-inline-end:calc(var(--editor-toolbar-base-offset) + 28px);
+}
+
+#editorFreeTextParamsToolbar{
+ inset-inline-end:calc(var(--editor-toolbar-base-offset) + 56px);
+}
+
+#editorHighlightParamsToolbar{
+ inset-inline-end:calc(var(--editor-toolbar-base-offset) + 84px);
+}
+
+#editorStampAddImage::before{
+ -webkit-mask-image:var(--editorParams-stampAddImage-icon);
+ mask-image:var(--editorParams-stampAddImage-icon);
+}
+
+.doorHanger,
+.doorHangerRight{
+ border-radius:2px;
+ box-shadow:0 1px 5px var(--doorhanger-border-color), 0 0 0 1px var(--doorhanger-border-color);
+ border:var(--doorhanger-border-color-whcm);
+}
+:is(.doorHanger, .doorHangerRight)::after,
+:is(.doorHanger, .doorHangerRight)::before{
+ bottom:100%;
+ border:8px solid rgb(0 0 0 / 0);
+ content:" ";
+ height:0;
+ width:0;
+ position:absolute;
+ pointer-events:none;
+ opacity:var(--doorhanger-triangle-opacity-whcm);
+}
+.doorHanger::after{
+ inset-inline-start:10px;
+ margin-inline-start:-8px;
+ border-bottom-color:var(--toolbar-bg-color);
+}
+.doorHangerRight::after{
+ inset-inline-end:10px;
+ margin-inline-end:-8px;
+ border-bottom-color:var(--doorhanger-bg-color);
+}
+:is(.doorHanger, .doorHangerRight)::before{
+ border-bottom-color:var(--doorhanger-border-color);
+ border-width:9px;
+}
+.doorHanger::before{
+ inset-inline-start:10px;
+ margin-inline-start:-9px;
+}
+.doorHangerRight::before{
+ inset-inline-end:10px;
+ margin-inline-end:-9px;
+}
+
+#findResultsCount{
+ background-color:rgb(217 217 217);
+ color:rgb(82 82 82);
+ text-align:center;
+ padding:4px 5px;
+ margin:5px;
+}
+
+#findMsg[data-status="notFound"]{
+ font-weight:bold;
+}
+
+:is(#findResultsCount, #findMsg):empty{
+ display:none;
+}
+
+#toolbarViewerMiddle{
+ position:absolute;
+ left:50%;
+ transform:translateX(-50%);
+}
+
+#toolbarViewerLeft,
+#toolbarSidebarLeft{
+ float:var(--inline-start);
+}
+#toolbarViewerRight,
+#toolbarSidebarRight{
+ float:var(--inline-end);
+}
+
+#toolbarViewerLeft > *,
+#toolbarViewerMiddle > *,
+#toolbarViewerRight > *,
+#toolbarSidebarLeft *,
+#toolbarSidebarRight *,
+.findbar *{
+ position:relative;
+ float:var(--inline-start);
+}
+
+#toolbarViewerLeft{
+ padding-inline-start:1px;
+}
+#toolbarViewerRight{
+ padding-inline-end:1px;
+}
+#toolbarSidebarRight{
+ padding-inline-end:2px;
+}
+
+.splitToolbarButton{
+ margin:2px;
+ display:inline-block;
+}
+.splitToolbarButton > .toolbarButton{
+ float:var(--inline-start);
+}
+
+.toolbarButton,
+.secondaryToolbarButton,
+.dialogButton{
+ border:none;
+ background:none;
+ width:28px;
+ height:28px;
+ outline:none;
+}
+
+.dialogButton:is(:hover, :focus-visible){
+ background-color:var(--dialog-button-hover-bg-color);
+}
+
+.dialogButton:is(:hover, :focus-visible) > span{
+ color:var(--dialog-button-hover-color);
+}
+
+.toolbarButton > span{
+ display:inline-block;
+ width:0;
+ height:0;
+ overflow:hidden;
+}
+
+:is(.toolbarButton, .secondaryToolbarButton, .dialogButton)[disabled]{
+ opacity:0.5;
+}
+
+.splitToolbarButton > .toolbarButton:is(:hover, :focus-visible),
+.dropdownToolbarButton:hover{
+ background-color:var(--button-hover-color);
+}
+.splitToolbarButton > .toolbarButton{
+ position:relative;
+ margin:0;
+}
+#toolbarSidebar .splitToolbarButton > .toolbarButton{
+ margin-inline-end:2px;
+}
+
+.splitToolbarButtonSeparator{
+ float:var(--inline-start);
+ margin:4px 0;
+ width:1px;
+ height:20px;
+ background-color:var(--separator-color);
+}
+
+.toolbarButton,
+.dropdownToolbarButton,
+.secondaryToolbarButton,
+.dialogButton{
+ min-width:16px;
+ margin:2px 1px;
+ padding:2px 6px 0;
+ border:none;
+ border-radius:2px;
+ color:var(--main-color);
+ font-size:12px;
+ line-height:14px;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+ cursor:default;
+ box-sizing:border-box;
+}
+
+.toolbarButton:is(:hover, :focus-visible){
+ background-color:var(--button-hover-color);
+}
+.secondaryToolbarButton:is(:hover, :focus-visible){
+ background-color:var(--doorhanger-hover-bg-color);
+ color:var(--doorhanger-hover-color);
+}
+
+:is(.toolbarButton, .secondaryToolbarButton).toggled,
+.splitToolbarButton.toggled > .toolbarButton.toggled{
+ background-color:var(--toggled-btn-bg-color);
+ color:var(--toggled-btn-color);
+}
+
+:is(.toolbarButton, .secondaryToolbarButton).toggled:hover,
+.splitToolbarButton.toggled > .toolbarButton.toggled:hover{
+ outline:var(--toggled-hover-btn-outline) !important;
+}
+
+:is(.toolbarButton, .secondaryToolbarButton).toggled::before{
+ background-color:var(--toggled-btn-color);
+}
+
+:is(.toolbarButton, .secondaryToolbarButton).toggled:hover:active,
+.splitToolbarButton.toggled > .toolbarButton.toggled:hover:active{
+ background-color:var(--toggled-hover-active-btn-color);
+}
+
+.dropdownToolbarButton{
+ display:flex;
+ width:-moz-fit-content;
+ width:fit-content;
+ min-width:140px;
+ padding:0;
+ background-color:var(--dropdown-btn-bg-color);
+ border:var(--dropdown-btn-border);
+}
+.dropdownToolbarButton::after{
+ top:6px;
+ inset-inline-end:6px;
+ pointer-events:none;
+ -webkit-mask-image:var(--toolbarButton-menuArrow-icon);
+ mask-image:var(--toolbarButton-menuArrow-icon);
+}
+
+.dropdownToolbarButton > select{
+ -webkit-appearance:none;
+ -moz-appearance:none;
+ appearance:none;
+ width:inherit;
+ min-width:inherit;
+ height:28px;
+ font-size:12px;
+ color:var(--main-color);
+ margin:0;
+ padding-block:1px 2px;
+ padding-inline:6px 38px;
+ border:none;
+ background-color:var(--dropdown-btn-bg-color);
+}
+.dropdownToolbarButton > select:is(:hover, :focus-visible){
+ background-color:var(--button-hover-color);
+ color:var(--toggled-btn-color);
+}
+.dropdownToolbarButton > select > option{
+ background:var(--doorhanger-bg-color);
+ color:var(--main-color);
+}
+
+.toolbarButtonSpacer{
+ width:30px;
+ display:inline-block;
+ height:1px;
+}
+
+:is(.toolbarButton, .secondaryToolbarButton, .treeItemToggler)::before,
+.dropdownToolbarButton::after{
+ position:absolute;
+ display:inline-block;
+ width:16px;
+ height:16px;
+
+ content:"";
+ background-color:var(--toolbar-icon-bg-color);
+ -webkit-mask-size:cover;
+ mask-size:cover;
+}
+
+.dropdownToolbarButton:is(:hover, :focus-visible, :active)::after{
+ background-color:var(--toolbar-icon-hover-bg-color);
+}
+
+.toolbarButton::before{
+ opacity:var(--toolbar-icon-opacity);
+ top:6px;
+ left:6px;
+}
+
+.toolbarButton:is(:hover, :focus-visible)::before,
+.secondaryToolbarButton:is(:hover, :focus-visible)::before{
+ background-color:var(--toolbar-icon-hover-bg-color);
+}
+
+.secondaryToolbarButton::before{
+ opacity:var(--doorhanger-icon-opacity);
+ top:5px;
+ inset-inline-start:12px;
+}
+
+#sidebarToggle::before{
+ -webkit-mask-image:var(--toolbarButton-sidebarToggle-icon);
+ mask-image:var(--toolbarButton-sidebarToggle-icon);
+ transform:scaleX(var(--dir-factor));
+}
+
+#secondaryToolbarToggle::before{
+ -webkit-mask-image:var(--toolbarButton-secondaryToolbarToggle-icon);
+ mask-image:var(--toolbarButton-secondaryToolbarToggle-icon);
+ transform:scaleX(var(--dir-factor));
+}
+
+#findPrevious::before{
+ -webkit-mask-image:var(--findbarButton-previous-icon);
+ mask-image:var(--findbarButton-previous-icon);
+}
+
+#findNext::before{
+ -webkit-mask-image:var(--findbarButton-next-icon);
+ mask-image:var(--findbarButton-next-icon);
+}
+
+#previous::before{
+ -webkit-mask-image:var(--toolbarButton-pageUp-icon);
+ mask-image:var(--toolbarButton-pageUp-icon);
+}
+
+#next::before{
+ -webkit-mask-image:var(--toolbarButton-pageDown-icon);
+ mask-image:var(--toolbarButton-pageDown-icon);
+}
+
+#zoomOut::before{
+ -webkit-mask-image:var(--toolbarButton-zoomOut-icon);
+ mask-image:var(--toolbarButton-zoomOut-icon);
+}
+
+#zoomIn::before{
+ -webkit-mask-image:var(--toolbarButton-zoomIn-icon);
+ mask-image:var(--toolbarButton-zoomIn-icon);
+}
+
+#presentationMode::before{
+ -webkit-mask-image:var(--toolbarButton-presentationMode-icon);
+ mask-image:var(--toolbarButton-presentationMode-icon);
+}
+
+#editorFreeText::before{
+ -webkit-mask-image:var(--toolbarButton-editorFreeText-icon);
+ mask-image:var(--toolbarButton-editorFreeText-icon);
+}
+
+#editorHighlight::before{
+ -webkit-mask-image:var(--toolbarButton-editorHighlight-icon);
+ mask-image:var(--toolbarButton-editorHighlight-icon);
+}
+
+#editorInk::before{
+ -webkit-mask-image:var(--toolbarButton-editorInk-icon);
+ mask-image:var(--toolbarButton-editorInk-icon);
+}
+
+#editorStamp::before{
+ -webkit-mask-image:var(--toolbarButton-editorStamp-icon);
+ mask-image:var(--toolbarButton-editorStamp-icon);
+}
+
+:is(#print, #secondaryPrint)::before{
+ -webkit-mask-image:var(--toolbarButton-print-icon);
+ mask-image:var(--toolbarButton-print-icon);
+}
+
+#secondaryOpenFile::before{
+ -webkit-mask-image:var(--toolbarButton-openFile-icon);
+ mask-image:var(--toolbarButton-openFile-icon);
+}
+
+:is(#download, #secondaryDownload)::before{
+ -webkit-mask-image:var(--toolbarButton-download-icon);
+ mask-image:var(--toolbarButton-download-icon);
+}
+
+a.secondaryToolbarButton{
+ padding-top:5px;
+ text-decoration:none;
+}
+a:is(.toolbarButton, .secondaryToolbarButton)[href="#"]{
+ opacity:0.5;
+ pointer-events:none;
+}
+
+#viewBookmark::before{
+ -webkit-mask-image:var(--toolbarButton-bookmark-icon);
+ mask-image:var(--toolbarButton-bookmark-icon);
+}
+
+#viewThumbnail::before{
+ -webkit-mask-image:var(--toolbarButton-viewThumbnail-icon);
+ mask-image:var(--toolbarButton-viewThumbnail-icon);
+}
+
+#viewOutline::before{
+ -webkit-mask-image:var(--toolbarButton-viewOutline-icon);
+ mask-image:var(--toolbarButton-viewOutline-icon);
+ transform:scaleX(var(--dir-factor));
+}
+
+#viewAttachments::before{
+ -webkit-mask-image:var(--toolbarButton-viewAttachments-icon);
+ mask-image:var(--toolbarButton-viewAttachments-icon);
+}
+
+#viewLayers::before{
+ -webkit-mask-image:var(--toolbarButton-viewLayers-icon);
+ mask-image:var(--toolbarButton-viewLayers-icon);
+}
+
+#currentOutlineItem::before{
+ -webkit-mask-image:var(--toolbarButton-currentOutlineItem-icon);
+ mask-image:var(--toolbarButton-currentOutlineItem-icon);
+ transform:scaleX(var(--dir-factor));
+}
+
+#viewFind::before{
+ -webkit-mask-image:var(--toolbarButton-search-icon);
+ mask-image:var(--toolbarButton-search-icon);
+}
+
+.pdfSidebarNotification::after{
+ position:absolute;
+ display:inline-block;
+ top:2px;
+ inset-inline-end:2px;
+ content:"";
+ background-color:rgb(112 219 85);
+ height:9px;
+ width:9px;
+ border-radius:50%;
+}
+
+.secondaryToolbarButton{
+ position:relative;
+ margin:0;
+ padding:0 0 1px;
+ padding-inline-start:36px;
+ height:auto;
+ min-height:26px;
+ width:auto;
+ min-width:100%;
+ text-align:start;
+ white-space:normal;
+ border-radius:0;
+ box-sizing:border-box;
+ display:inline-block;
+}
+.secondaryToolbarButton > span{
+ padding-inline-end:4px;
+}
+
+#firstPage::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-firstPage-icon);
+ mask-image:var(--secondaryToolbarButton-firstPage-icon);
+}
+
+#lastPage::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-lastPage-icon);
+ mask-image:var(--secondaryToolbarButton-lastPage-icon);
+}
+
+#pageRotateCcw::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-rotateCcw-icon);
+ mask-image:var(--secondaryToolbarButton-rotateCcw-icon);
+}
+
+#pageRotateCw::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-rotateCw-icon);
+ mask-image:var(--secondaryToolbarButton-rotateCw-icon);
+}
+
+#cursorSelectTool::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-selectTool-icon);
+ mask-image:var(--secondaryToolbarButton-selectTool-icon);
+}
+
+#cursorHandTool::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-handTool-icon);
+ mask-image:var(--secondaryToolbarButton-handTool-icon);
+}
+
+#scrollPage::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-scrollPage-icon);
+ mask-image:var(--secondaryToolbarButton-scrollPage-icon);
+}
+
+#scrollVertical::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-scrollVertical-icon);
+ mask-image:var(--secondaryToolbarButton-scrollVertical-icon);
+}
+
+#scrollHorizontal::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon);
+ mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon);
+}
+
+#scrollWrapped::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-scrollWrapped-icon);
+ mask-image:var(--secondaryToolbarButton-scrollWrapped-icon);
+}
+
+#spreadNone::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-spreadNone-icon);
+ mask-image:var(--secondaryToolbarButton-spreadNone-icon);
+}
+
+#spreadOdd::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-spreadOdd-icon);
+ mask-image:var(--secondaryToolbarButton-spreadOdd-icon);
+}
+
+#spreadEven::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-spreadEven-icon);
+ mask-image:var(--secondaryToolbarButton-spreadEven-icon);
+}
+
+#documentProperties::before{
+ -webkit-mask-image:var(--secondaryToolbarButton-documentProperties-icon);
+ mask-image:var(--secondaryToolbarButton-documentProperties-icon);
+}
+
+.verticalToolbarSeparator{
+ display:block;
+ margin:5px 2px;
+ width:1px;
+ height:22px;
+ background-color:var(--separator-color);
+}
+.horizontalToolbarSeparator{
+ display:block;
+ margin:6px 0;
+ height:1px;
+ width:100%;
+ background-color:var(--doorhanger-separator-color);
+}
+
+.toolbarField{
+ padding:4px 7px;
+ margin:3px 0;
+ border-radius:2px;
+ background-color:var(--field-bg-color);
+ background-clip:padding-box;
+ border:1px solid var(--field-border-color);
+ box-shadow:none;
+ color:var(--field-color);
+ font-size:12px;
+ line-height:16px;
+ outline:none;
+}
+
+.toolbarField[type="checkbox"]{
+ opacity:0;
+ position:absolute !important;
+ left:0;
+ margin:10px 0 3px;
+ margin-inline-start:7px;
+}
+
+#pageNumber{
+ -moz-appearance:textfield;
+ text-align:end;
+ width:40px;
+ background-size:0 0;
+ transition-property:none;
+}
+
+#pageNumber::-webkit-inner-spin-button{
+ -webkit-appearance:none;
+ }
+
+.loadingInput:has(> #pageNumber.loading)::after{
+ display:block;
+ visibility:visible;
+
+ transition-property:visibility;
+ transition-delay:var(--loading-icon-delay);
+ }
+
+.loadingInput::after{
+ position:absolute;
+ visibility:hidden;
+ display:none;
+ top:calc(50% - 8px);
+ width:16px;
+ height:16px;
+
+ content:"";
+ background-color:var(--toolbar-icon-bg-color);
+ -webkit-mask-size:cover;
+ mask-size:cover;
+ -webkit-mask-image:var(--loading-icon);
+ mask-image:var(--loading-icon);
+ }
+
+.loadingInput.start::after{
+ inset-inline-start:4px;
+ }
+
+.loadingInput.end::after{
+ inset-inline-end:4px;
+ }
+
+.toolbarField:focus{
+ border-color:#0a84ff;
+}
+
+.toolbarLabel{
+ min-width:16px;
+ padding:7px;
+ margin:2px;
+ border-radius:2px;
+ color:var(--main-color);
+ font-size:12px;
+ line-height:14px;
+ text-align:left;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+ cursor:default;
+}
+
+#numPages.toolbarLabel{
+ padding-inline-start:3px;
+}
+
+#thumbnailView,
+#outlineView,
+#attachmentsView,
+#layersView{
+ position:absolute;
+ width:calc(100% - 8px);
+ inset-block:0;
+ padding:4px 4px 0;
+ overflow:auto;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+}
+#thumbnailView{
+ width:calc(100% - 60px);
+ padding:10px 30px 0;
+}
+
+#thumbnailView > a:is(:active, :focus){
+ outline:0;
+}
+
+.thumbnail{
+ --thumbnail-width:0;
+ --thumbnail-height:0;
+
+ float:var(--inline-start);
+ width:var(--thumbnail-width);
+ height:var(--thumbnail-height);
+ margin:0 10px 5px;
+ padding:1px;
+ border:7px solid transparent;
+ border-radius:2px;
+}
+
+#thumbnailView > a:last-of-type > .thumbnail{
+ margin-bottom:10px;
+}
+
+a:focus > .thumbnail,
+.thumbnail:hover{
+ border-color:var(--thumbnail-hover-color);
+}
+.thumbnail.selected{
+ border-color:var(--thumbnail-selected-color) !important;
+}
+
+.thumbnailImage{
+ width:var(--thumbnail-width);
+ height:var(--thumbnail-height);
+ opacity:0.9;
+}
+a:focus > .thumbnail > .thumbnailImage,
+.thumbnail:hover > .thumbnailImage{
+ opacity:0.95;
+}
+.thumbnail.selected > .thumbnailImage{
+ opacity:1 !important;
+}
+
+.thumbnail:not([data-loaded]) > .thumbnailImage{
+ width:calc(var(--thumbnail-width) - 2px);
+ height:calc(var(--thumbnail-height) - 2px);
+ border:1px dashed rgb(132 132 132);
+}
+
+.treeWithDeepNesting > .treeItem,
+.treeItem > .treeItems{
+ margin-inline-start:20px;
+}
+
+.treeItem > a{
+ text-decoration:none;
+ display:inline-block;
+ min-width:calc(100% - 4px);
+ height:auto;
+ margin-bottom:1px;
+ padding:2px 0 5px;
+ padding-inline-start:4px;
+ border-radius:2px;
+ color:var(--treeitem-color);
+ font-size:13px;
+ line-height:15px;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ user-select:none;
+ white-space:normal;
+ cursor:pointer;
+}
+
+#layersView .treeItem > a *{
+ cursor:pointer;
+}
+#layersView .treeItem > a > label{
+ padding-inline-start:4px;
+}
+#layersView .treeItem > a > label > input{
+ float:var(--inline-start);
+ margin-top:1px;
+}
+
+.treeItemToggler{
+ position:relative;
+ float:var(--inline-start);
+ height:0;
+ width:0;
+ color:rgb(255 255 255 / 0.5);
+}
+.treeItemToggler::before{
+ inset-inline-end:4px;
+ -webkit-mask-image:var(--treeitem-expanded-icon);
+ mask-image:var(--treeitem-expanded-icon);
+}
+.treeItemToggler.treeItemsHidden::before{
+ -webkit-mask-image:var(--treeitem-collapsed-icon);
+ mask-image:var(--treeitem-collapsed-icon);
+ transform:scaleX(var(--dir-factor));
+}
+.treeItemToggler.treeItemsHidden ~ .treeItems{
+ display:none;
+}
+
+.treeItem.selected > a{
+ background-color:var(--treeitem-selected-bg-color);
+ color:var(--treeitem-selected-color);
+}
+
+.treeItemToggler:hover,
+.treeItemToggler:hover + a,
+.treeItemToggler:hover ~ .treeItems,
+.treeItem > a:hover{
+ background-color:var(--treeitem-bg-color);
+ background-clip:padding-box;
+ border-radius:2px;
+ color:var(--treeitem-hover-color);
+}
+
+#outlineOptionsContainer{
+ display:none;
+}
+
+#sidebarContainer:has(#outlineView:not(.hidden)) #outlineOptionsContainer{
+ display:inherit;
+ }
+
+.dialogButton{
+ width:auto;
+ margin:3px 4px 2px !important;
+ padding:2px 11px;
+ color:var(--main-color);
+ background-color:var(--dialog-button-bg-color);
+ border:var(--dialog-button-border) !important;
+}
+
+dialog{
+ margin:auto;
+ padding:15px;
+ border-spacing:4px;
+ color:var(--main-color);
+ font:message-box;
+ font-size:12px;
+ line-height:14px;
+ background-color:var(--doorhanger-bg-color);
+ border:1px solid rgb(0 0 0 / 0.5);
+ border-radius:4px;
+ box-shadow:0 1px 4px rgb(0 0 0 / 0.3);
+}
+dialog::backdrop{
+ background-color:rgb(0 0 0 / 0.2);
+}
+
+dialog > .row{
+ display:table-row;
+}
+
+dialog > .row > *{
+ display:table-cell;
+}
+
+dialog .toolbarField{
+ margin:5px 0;
+}
+
+dialog .separator{
+ display:block;
+ margin:4px 0;
+ height:1px;
+ width:100%;
+ background-color:var(--separator-color);
+}
+
+dialog .buttonRow{
+ text-align:center;
+ vertical-align:middle;
+}
+
+dialog :link{
+ color:rgb(255 255 255);
+}
+
+#passwordDialog{
+ text-align:center;
+}
+#passwordDialog .toolbarField{
+ width:200px;
+}
+
+#documentPropertiesDialog{
+ text-align:left;
+}
+#documentPropertiesDialog .row > *{
+ min-width:100px;
+ text-align:start;
+}
+#documentPropertiesDialog .row > span{
+ width:125px;
+ word-wrap:break-word;
+}
+#documentPropertiesDialog .row > p{
+ max-width:225px;
+ word-wrap:break-word;
+}
+#documentPropertiesDialog .buttonRow{
+ margin-top:10px;
+}
+
+.grab-to-pan-grab{
+ cursor:grab !important;
+}
+.grab-to-pan-grab
+ *:not(input):not(textarea):not(button):not(select):not(:link){
+ cursor:inherit !important;
+}
+.grab-to-pan-grab:active,
+.grab-to-pan-grabbing{
+ cursor:grabbing !important;
+}
+.grab-to-pan-grabbing{
+ position:fixed;
+ background:rgb(0 0 0 / 0);
+ display:block;
+ inset:0;
+ overflow:hidden;
+ z-index:50000;
+}
+
+@page{
+ margin:0;
+}
+
+#printContainer{
+ display:none;
+}
+
+@media print{
+ body{
+ background:rgb(0 0 0 / 0) none;
+ }
+ body[data-pdfjsprinting] #outerContainer{
+ display:none;
+ }
+ body[data-pdfjsprinting] #printContainer{
+ display:block;
+ }
+ #printContainer{
+ height:100%;
+ }
+ #printContainer > .printedPage{
+ page-break-after:always;
+ page-break-inside:avoid;
+ height:100%;
+ width:100%;
+
+ display:flex;
+ flex-direction:column;
+ justify-content:center;
+ align-items:center;
+ }
+
+ #printContainer > .xfaPrintedPage .xfaPage{
+ position:absolute;
+ }
+
+ #printContainer > .xfaPrintedPage{
+ page-break-after:always;
+ page-break-inside:avoid;
+ width:100%;
+ height:100%;
+ position:relative;
+ }
+
+ #printContainer > .printedPage :is(canvas, img){
+ max-width:100%;
+ max-height:100%;
+
+ direction:ltr;
+ display:block;
+ }
+}
+
+.visibleMediumView{
+ display:none;
+}
+
+@media all and (max-width: 900px){
+ #toolbarViewerMiddle{
+ display:table;
+ margin:auto;
+ left:auto;
+ position:inherit;
+ transform:none;
+ }
+}
+
+@media all and (max-width: 840px){
+ #sidebarContainer{
+ background-color:var(--sidebar-narrow-bg-color);
+ }
+ #outerContainer.sidebarOpen #viewerContainer{
+ inset-inline-start:0 !important;
+ }
+}
+
+@media all and (max-width: 750px){
+ :root{
+ --editor-toolbar-base-offset:40px;
+ }
+ #outerContainer .hiddenMediumView{
+ display:none;
+ }
+ #outerContainer .visibleMediumView{
+ display:inherit;
+ }
+}
+
+@media all and (max-width: 690px){
+ .hiddenSmallView,
+ .hiddenSmallView *{
+ display:none;
+ }
+ .toolbarButtonSpacer{
+ width:0;
+ }
+ .findbar{
+ inset-inline-start:34px;
+ }
+}
+
+@media all and (max-width: 560px){
+ #scaleSelectContainer{
+ display:none;
+ }
+}
diff --git a/web/viewer.html b/web/viewer.html
new file mode 100644
index 0000000000000000000000000000000000000000..badac98dce86a0f2a18d479f1948c0f2354dcfcc
--- /dev/null
+++ b/web/viewer.html
@@ -0,0 +1,508 @@
+
+
+
+
+
+
+
+ PDF.js viewer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/viewer.mjs b/web/viewer.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..bae2df5bf8d160e18c9af5d3c0824177e00e9cae
--- /dev/null
+++ b/web/viewer.mjs
@@ -0,0 +1,14441 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * JavaScript code in this page
+ *
+ * Copyright 2023 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @licend The above is the entire license notice for the
+ * JavaScript code in this page
+ */
+
+/******/ // The require scope
+/******/ var __webpack_require__ = {};
+/******/
+/************************************************************************/
+/******/ /* webpack/runtime/define property getters */
+/******/ (() => {
+/******/ // define getter functions for harmony exports
+/******/ __webpack_require__.d = (exports, definition) => {
+/******/ for(var key in definition) {
+/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
+/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
+/******/ }
+/******/ }
+/******/ };
+/******/ })();
+/******/
+/******/ /* webpack/runtime/hasOwnProperty shorthand */
+/******/ (() => {
+/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
+/******/ })();
+/******/
+/************************************************************************/
+var __webpack_exports__ = {};
+
+// EXPORTS
+__webpack_require__.d(__webpack_exports__, {
+ PDFViewerApplication: () => (/* reexport */ PDFViewerApplication),
+ PDFViewerApplicationConstants: () => (/* binding */ AppConstants),
+ PDFViewerApplicationOptions: () => (/* reexport */ AppOptions)
+});
+
+;// CONCATENATED MODULE: ./web/ui_utils.js
+const DEFAULT_SCALE_VALUE = "auto";
+const DEFAULT_SCALE = 1.0;
+const DEFAULT_SCALE_DELTA = 1.1;
+const MIN_SCALE = 0.1;
+const MAX_SCALE = 10.0;
+const UNKNOWN_SCALE = 0;
+const MAX_AUTO_SCALE = 1.25;
+const SCROLLBAR_PADDING = 40;
+const VERTICAL_PADDING = 5;
+const RenderingStates = {
+ INITIAL: 0,
+ RUNNING: 1,
+ PAUSED: 2,
+ FINISHED: 3
+};
+const PresentationModeState = {
+ UNKNOWN: 0,
+ NORMAL: 1,
+ CHANGING: 2,
+ FULLSCREEN: 3
+};
+const SidebarView = {
+ UNKNOWN: -1,
+ NONE: 0,
+ THUMBS: 1,
+ OUTLINE: 2,
+ ATTACHMENTS: 3,
+ LAYERS: 4
+};
+const TextLayerMode = {
+ DISABLE: 0,
+ ENABLE: 1,
+ ENABLE_PERMISSIONS: 2
+};
+const ScrollMode = {
+ UNKNOWN: -1,
+ VERTICAL: 0,
+ HORIZONTAL: 1,
+ WRAPPED: 2,
+ PAGE: 3
+};
+const SpreadMode = {
+ UNKNOWN: -1,
+ NONE: 0,
+ ODD: 1,
+ EVEN: 2
+};
+const CursorTool = {
+ SELECT: 0,
+ HAND: 1,
+ ZOOM: 2
+};
+const AutoPrintRegExp = /\bprint\s*\(/;
+class OutputScale {
+ constructor() {
+ const pixelRatio = window.devicePixelRatio || 1;
+ this.sx = pixelRatio;
+ this.sy = pixelRatio;
+ }
+ get scaled() {
+ return this.sx !== 1 || this.sy !== 1;
+ }
+}
+function scrollIntoView(element, spot, scrollMatches = false) {
+ let parent = element.offsetParent;
+ if (!parent) {
+ console.error("offsetParent is not set -- cannot scroll");
+ return;
+ }
+ let offsetY = element.offsetTop + element.clientTop;
+ let offsetX = element.offsetLeft + element.clientLeft;
+ while (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth || scrollMatches && (parent.classList.contains("markedContent") || getComputedStyle(parent).overflow === "hidden")) {
+ offsetY += parent.offsetTop;
+ offsetX += parent.offsetLeft;
+ parent = parent.offsetParent;
+ if (!parent) {
+ return;
+ }
+ }
+ if (spot) {
+ if (spot.top !== undefined) {
+ offsetY += spot.top;
+ }
+ if (spot.left !== undefined) {
+ offsetX += spot.left;
+ parent.scrollLeft = offsetX;
+ }
+ }
+ parent.scrollTop = offsetY;
+}
+function watchScroll(viewAreaElement, callback) {
+ const debounceScroll = function (evt) {
+ if (rAF) {
+ return;
+ }
+ rAF = window.requestAnimationFrame(function viewAreaElementScrolled() {
+ rAF = null;
+ const currentX = viewAreaElement.scrollLeft;
+ const lastX = state.lastX;
+ if (currentX !== lastX) {
+ state.right = currentX > lastX;
+ }
+ state.lastX = currentX;
+ const currentY = viewAreaElement.scrollTop;
+ const lastY = state.lastY;
+ if (currentY !== lastY) {
+ state.down = currentY > lastY;
+ }
+ state.lastY = currentY;
+ callback(state);
+ });
+ };
+ const state = {
+ right: true,
+ down: true,
+ lastX: viewAreaElement.scrollLeft,
+ lastY: viewAreaElement.scrollTop,
+ _eventHandler: debounceScroll
+ };
+ let rAF = null;
+ viewAreaElement.addEventListener("scroll", debounceScroll, true);
+ return state;
+}
+function parseQueryString(query) {
+ const params = new Map();
+ for (const [key, value] of new URLSearchParams(query)) {
+ params.set(key.toLowerCase(), value);
+ }
+ return params;
+}
+const InvisibleCharsRegExp = /[\x00-\x1F]/g;
+function removeNullCharacters(str, replaceInvisible = false) {
+ if (!InvisibleCharsRegExp.test(str)) {
+ return str;
+ }
+ if (replaceInvisible) {
+ return str.replaceAll(InvisibleCharsRegExp, m => m === "\x00" ? "" : " ");
+ }
+ return str.replaceAll("\x00", "");
+}
+function binarySearchFirstItem(items, condition, start = 0) {
+ let minIndex = start;
+ let maxIndex = items.length - 1;
+ if (maxIndex < 0 || !condition(items[maxIndex])) {
+ return items.length;
+ }
+ if (condition(items[minIndex])) {
+ return minIndex;
+ }
+ while (minIndex < maxIndex) {
+ const currentIndex = minIndex + maxIndex >> 1;
+ const currentItem = items[currentIndex];
+ if (condition(currentItem)) {
+ maxIndex = currentIndex;
+ } else {
+ minIndex = currentIndex + 1;
+ }
+ }
+ return minIndex;
+}
+function approximateFraction(x) {
+ if (Math.floor(x) === x) {
+ return [x, 1];
+ }
+ const xinv = 1 / x;
+ const limit = 8;
+ if (xinv > limit) {
+ return [1, limit];
+ } else if (Math.floor(xinv) === xinv) {
+ return [1, xinv];
+ }
+ const x_ = x > 1 ? xinv : x;
+ let a = 0,
+ b = 1,
+ c = 1,
+ d = 1;
+ while (true) {
+ const p = a + c,
+ q = b + d;
+ if (q > limit) {
+ break;
+ }
+ if (x_ <= p / q) {
+ c = p;
+ d = q;
+ } else {
+ a = p;
+ b = q;
+ }
+ }
+ let result;
+ if (x_ - a / b < c / d - x_) {
+ result = x_ === x ? [a, b] : [b, a];
+ } else {
+ result = x_ === x ? [c, d] : [d, c];
+ }
+ return result;
+}
+function roundToDivide(x, div) {
+ const r = x % div;
+ return r === 0 ? x : Math.round(x - r + div);
+}
+function getPageSizeInches({
+ view,
+ userUnit,
+ rotate
+}) {
+ const [x1, y1, x2, y2] = view;
+ const changeOrientation = rotate % 180 !== 0;
+ const width = (x2 - x1) / 72 * userUnit;
+ const height = (y2 - y1) / 72 * userUnit;
+ return {
+ width: changeOrientation ? height : width,
+ height: changeOrientation ? width : height
+ };
+}
+function backtrackBeforeAllVisibleElements(index, views, top) {
+ if (index < 2) {
+ return index;
+ }
+ let elt = views[index].div;
+ let pageTop = elt.offsetTop + elt.clientTop;
+ if (pageTop >= top) {
+ elt = views[index - 1].div;
+ pageTop = elt.offsetTop + elt.clientTop;
+ }
+ for (let i = index - 2; i >= 0; --i) {
+ elt = views[i].div;
+ if (elt.offsetTop + elt.clientTop + elt.clientHeight <= pageTop) {
+ break;
+ }
+ index = i;
+ }
+ return index;
+}
+function getVisibleElements({
+ scrollEl,
+ views,
+ sortByVisibility = false,
+ horizontal = false,
+ rtl = false
+}) {
+ const top = scrollEl.scrollTop,
+ bottom = top + scrollEl.clientHeight;
+ const left = scrollEl.scrollLeft,
+ right = left + scrollEl.clientWidth;
+ function isElementBottomAfterViewTop(view) {
+ const element = view.div;
+ const elementBottom = element.offsetTop + element.clientTop + element.clientHeight;
+ return elementBottom > top;
+ }
+ function isElementNextAfterViewHorizontally(view) {
+ const element = view.div;
+ const elementLeft = element.offsetLeft + element.clientLeft;
+ const elementRight = elementLeft + element.clientWidth;
+ return rtl ? elementLeft < right : elementRight > left;
+ }
+ const visible = [],
+ ids = new Set(),
+ numViews = views.length;
+ let firstVisibleElementInd = binarySearchFirstItem(views, horizontal ? isElementNextAfterViewHorizontally : isElementBottomAfterViewTop);
+ if (firstVisibleElementInd > 0 && firstVisibleElementInd < numViews && !horizontal) {
+ firstVisibleElementInd = backtrackBeforeAllVisibleElements(firstVisibleElementInd, views, top);
+ }
+ let lastEdge = horizontal ? right : -1;
+ for (let i = firstVisibleElementInd; i < numViews; i++) {
+ const view = views[i],
+ element = view.div;
+ const currentWidth = element.offsetLeft + element.clientLeft;
+ const currentHeight = element.offsetTop + element.clientTop;
+ const viewWidth = element.clientWidth,
+ viewHeight = element.clientHeight;
+ const viewRight = currentWidth + viewWidth;
+ const viewBottom = currentHeight + viewHeight;
+ if (lastEdge === -1) {
+ if (viewBottom >= bottom) {
+ lastEdge = viewBottom;
+ }
+ } else if ((horizontal ? currentWidth : currentHeight) > lastEdge) {
+ break;
+ }
+ if (viewBottom <= top || currentHeight >= bottom || viewRight <= left || currentWidth >= right) {
+ continue;
+ }
+ const hiddenHeight = Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom);
+ const hiddenWidth = Math.max(0, left - currentWidth) + Math.max(0, viewRight - right);
+ const fractionHeight = (viewHeight - hiddenHeight) / viewHeight,
+ fractionWidth = (viewWidth - hiddenWidth) / viewWidth;
+ const percent = fractionHeight * fractionWidth * 100 | 0;
+ visible.push({
+ id: view.id,
+ x: currentWidth,
+ y: currentHeight,
+ view,
+ percent,
+ widthPercent: fractionWidth * 100 | 0
+ });
+ ids.add(view.id);
+ }
+ const first = visible[0],
+ last = visible.at(-1);
+ if (sortByVisibility) {
+ visible.sort(function (a, b) {
+ const pc = a.percent - b.percent;
+ if (Math.abs(pc) > 0.001) {
+ return -pc;
+ }
+ return a.id - b.id;
+ });
+ }
+ return {
+ first,
+ last,
+ views: visible,
+ ids
+ };
+}
+function normalizeWheelEventDirection(evt) {
+ let delta = Math.hypot(evt.deltaX, evt.deltaY);
+ const angle = Math.atan2(evt.deltaY, evt.deltaX);
+ if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) {
+ delta = -delta;
+ }
+ return delta;
+}
+function normalizeWheelEventDelta(evt) {
+ const deltaMode = evt.deltaMode;
+ let delta = normalizeWheelEventDirection(evt);
+ const MOUSE_PIXELS_PER_LINE = 30;
+ const MOUSE_LINES_PER_PAGE = 30;
+ if (deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
+ delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE;
+ } else if (deltaMode === WheelEvent.DOM_DELTA_LINE) {
+ delta /= MOUSE_LINES_PER_PAGE;
+ }
+ return delta;
+}
+function isValidRotation(angle) {
+ return Number.isInteger(angle) && angle % 90 === 0;
+}
+function isValidScrollMode(mode) {
+ return Number.isInteger(mode) && Object.values(ScrollMode).includes(mode) && mode !== ScrollMode.UNKNOWN;
+}
+function isValidSpreadMode(mode) {
+ return Number.isInteger(mode) && Object.values(SpreadMode).includes(mode) && mode !== SpreadMode.UNKNOWN;
+}
+function isPortraitOrientation(size) {
+ return size.width <= size.height;
+}
+const animationStarted = new Promise(function (resolve) {
+ window.requestAnimationFrame(resolve);
+});
+const docStyle = document.documentElement.style;
+function clamp(v, min, max) {
+ return Math.min(Math.max(v, min), max);
+}
+class ProgressBar {
+ #classList = null;
+ #disableAutoFetchTimeout = null;
+ #percent = 0;
+ #style = null;
+ #visible = true;
+ constructor(bar) {
+ this.#classList = bar.classList;
+ this.#style = bar.style;
+ }
+ get percent() {
+ return this.#percent;
+ }
+ set percent(val) {
+ this.#percent = clamp(val, 0, 100);
+ if (isNaN(val)) {
+ this.#classList.add("indeterminate");
+ return;
+ }
+ this.#classList.remove("indeterminate");
+ this.#style.setProperty("--progressBar-percent", `${this.#percent}%`);
+ }
+ setWidth(viewer) {
+ if (!viewer) {
+ return;
+ }
+ const container = viewer.parentNode;
+ const scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
+ if (scrollbarWidth > 0) {
+ this.#style.setProperty("--progressBar-end-offset", `${scrollbarWidth}px`);
+ }
+ }
+ setDisableAutoFetch(delay = 5000) {
+ if (isNaN(this.#percent)) {
+ return;
+ }
+ if (this.#disableAutoFetchTimeout) {
+ clearTimeout(this.#disableAutoFetchTimeout);
+ }
+ this.show();
+ this.#disableAutoFetchTimeout = setTimeout(() => {
+ this.#disableAutoFetchTimeout = null;
+ this.hide();
+ }, delay);
+ }
+ hide() {
+ if (!this.#visible) {
+ return;
+ }
+ this.#visible = false;
+ this.#classList.add("hidden");
+ }
+ show() {
+ if (this.#visible) {
+ return;
+ }
+ this.#visible = true;
+ this.#classList.remove("hidden");
+ }
+}
+function getActiveOrFocusedElement() {
+ let curRoot = document;
+ let curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
+ while (curActiveOrFocused?.shadowRoot) {
+ curRoot = curActiveOrFocused.shadowRoot;
+ curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
+ }
+ return curActiveOrFocused;
+}
+function apiPageLayoutToViewerModes(layout) {
+ let scrollMode = ScrollMode.VERTICAL,
+ spreadMode = SpreadMode.NONE;
+ switch (layout) {
+ case "SinglePage":
+ scrollMode = ScrollMode.PAGE;
+ break;
+ case "OneColumn":
+ break;
+ case "TwoPageLeft":
+ scrollMode = ScrollMode.PAGE;
+ case "TwoColumnLeft":
+ spreadMode = SpreadMode.ODD;
+ break;
+ case "TwoPageRight":
+ scrollMode = ScrollMode.PAGE;
+ case "TwoColumnRight":
+ spreadMode = SpreadMode.EVEN;
+ break;
+ }
+ return {
+ scrollMode,
+ spreadMode
+ };
+}
+function apiPageModeToSidebarView(mode) {
+ switch (mode) {
+ case "UseNone":
+ return SidebarView.NONE;
+ case "UseThumbs":
+ return SidebarView.THUMBS;
+ case "UseOutlines":
+ return SidebarView.OUTLINE;
+ case "UseAttachments":
+ return SidebarView.ATTACHMENTS;
+ case "UseOC":
+ return SidebarView.LAYERS;
+ }
+ return SidebarView.NONE;
+}
+function toggleCheckedBtn(button, toggle, view = null) {
+ button.classList.toggle("toggled", toggle);
+ button.setAttribute("aria-checked", toggle);
+ view?.classList.toggle("hidden", !toggle);
+}
+function toggleExpandedBtn(button, toggle, view = null) {
+ button.classList.toggle("toggled", toggle);
+ button.setAttribute("aria-expanded", toggle);
+ view?.classList.toggle("hidden", !toggle);
+}
+
+;// CONCATENATED MODULE: ./web/app_options.js
+{
+ var compatibilityParams = Object.create(null);
+ const userAgent = navigator.userAgent || "";
+ const platform = navigator.platform || "";
+ const maxTouchPoints = navigator.maxTouchPoints || 1;
+ const isAndroid = /Android/.test(userAgent);
+ const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) || platform === "MacIntel" && maxTouchPoints > 1;
+ (function checkCanvasSizeLimitation() {
+ if (isIOS || isAndroid) {
+ compatibilityParams.maxCanvasPixels = 5242880;
+ }
+ })();
+}
+const OptionKind = {
+ BROWSER: 0x01,
+ VIEWER: 0x02,
+ API: 0x04,
+ WORKER: 0x08,
+ PREFERENCE: 0x80
+};
+const defaultOptions = {
+ canvasMaxAreaInBytes: {
+ value: -1,
+ kind: OptionKind.BROWSER + OptionKind.API
+ },
+ isInAutomation: {
+ value: false,
+ kind: OptionKind.BROWSER
+ },
+ supportsCaretBrowsingMode: {
+ value: false,
+ kind: OptionKind.BROWSER
+ },
+ supportsDocumentFonts: {
+ value: true,
+ kind: OptionKind.BROWSER
+ },
+ supportsIntegratedFind: {
+ value: false,
+ kind: OptionKind.BROWSER
+ },
+ supportsMouseWheelZoomCtrlKey: {
+ value: true,
+ kind: OptionKind.BROWSER
+ },
+ supportsMouseWheelZoomMetaKey: {
+ value: true,
+ kind: OptionKind.BROWSER
+ },
+ supportsPinchToZoom: {
+ value: true,
+ kind: OptionKind.BROWSER
+ },
+ annotationEditorMode: {
+ value: 0,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ annotationMode: {
+ value: 2,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ cursorToolOnLoad: {
+ value: 0,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ debuggerSrc: {
+ value: "./debugger.mjs",
+ kind: OptionKind.VIEWER
+ },
+ defaultZoomDelay: {
+ value: 400,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ defaultZoomValue: {
+ value: "",
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ disableHistory: {
+ value: false,
+ kind: OptionKind.VIEWER
+ },
+ disablePageLabels: {
+ value: false,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ enableHighlightEditor: {
+ value: false,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ enableHighlightFloatingButton: {
+ value: false,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ enableML: {
+ value: false,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ enablePermissions: {
+ value: false,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ enablePrintAutoRotate: {
+ value: true,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ enableScripting: {
+ value: true,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ enableStampEditor: {
+ value: true,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ externalLinkRel: {
+ value: "noopener noreferrer nofollow",
+ kind: OptionKind.VIEWER
+ },
+ externalLinkTarget: {
+ value: 0,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ highlightEditorColors: {
+ value: "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F",
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ historyUpdateUrl: {
+ value: false,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ ignoreDestinationZoom: {
+ value: false,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ imageResourcesPath: {
+ value: "./images/",
+ kind: OptionKind.VIEWER
+ },
+ maxCanvasPixels: {
+ value: 2 ** 25,
+ kind: OptionKind.VIEWER
+ },
+ forcePageColors: {
+ value: false,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ pageColorsBackground: {
+ value: "Canvas",
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ pageColorsForeground: {
+ value: "CanvasText",
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ pdfBugEnabled: {
+ value: false,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ printResolution: {
+ value: 150,
+ kind: OptionKind.VIEWER
+ },
+ sidebarViewOnLoad: {
+ value: -1,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ scrollModeOnLoad: {
+ value: -1,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ spreadModeOnLoad: {
+ value: -1,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ textLayerMode: {
+ value: 1,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ viewOnLoad: {
+ value: 0,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ },
+ cMapPacked: {
+ value: true,
+ kind: OptionKind.API
+ },
+ cMapUrl: {
+ value: "../web/cmaps/",
+ kind: OptionKind.API
+ },
+ disableAutoFetch: {
+ value: false,
+ kind: OptionKind.API + OptionKind.PREFERENCE
+ },
+ disableFontFace: {
+ value: false,
+ kind: OptionKind.API + OptionKind.PREFERENCE
+ },
+ disableRange: {
+ value: false,
+ kind: OptionKind.API + OptionKind.PREFERENCE
+ },
+ disableStream: {
+ value: false,
+ kind: OptionKind.API + OptionKind.PREFERENCE
+ },
+ docBaseUrl: {
+ value: "",
+ kind: OptionKind.API
+ },
+ enableXfa: {
+ value: true,
+ kind: OptionKind.API + OptionKind.PREFERENCE
+ },
+ fontExtraProperties: {
+ value: false,
+ kind: OptionKind.API
+ },
+ isEvalSupported: {
+ value: true,
+ kind: OptionKind.API
+ },
+ isOffscreenCanvasSupported: {
+ value: true,
+ kind: OptionKind.API
+ },
+ maxImageSize: {
+ value: -1,
+ kind: OptionKind.API
+ },
+ pdfBug: {
+ value: false,
+ kind: OptionKind.API
+ },
+ standardFontDataUrl: {
+ value: "../web/standard_fonts/",
+ kind: OptionKind.API
+ },
+ verbosity: {
+ value: 1,
+ kind: OptionKind.API
+ },
+ workerPort: {
+ value: null,
+ kind: OptionKind.WORKER
+ },
+ workerSrc: {
+ value: "../build/pdf.worker.mjs",
+ kind: OptionKind.WORKER
+ }
+};
+{
+ defaultOptions.defaultUrl = {
+ value: "compressed.tracemonkey-pldi-09.pdf",
+ kind: OptionKind.VIEWER
+ };
+ defaultOptions.sandboxBundleSrc = {
+ value: "../build/pdf.sandbox.mjs",
+ kind: OptionKind.VIEWER
+ };
+ defaultOptions.viewerCssTheme = {
+ value: 0,
+ kind: OptionKind.VIEWER + OptionKind.PREFERENCE
+ };
+}
+{
+ defaultOptions.disablePreferences = {
+ value: false,
+ kind: OptionKind.VIEWER
+ };
+ defaultOptions.locale = {
+ value: navigator.language || "en-US",
+ kind: OptionKind.VIEWER
+ };
+}
+const userOptions = Object.create(null);
+{
+ for (const name in compatibilityParams) {
+ userOptions[name] = compatibilityParams[name];
+ }
+}
+class AppOptions {
+ constructor() {
+ throw new Error("Cannot initialize AppOptions.");
+ }
+ static get(name) {
+ return userOptions[name] ?? defaultOptions[name]?.value ?? undefined;
+ }
+ static getAll(kind = null, defaultOnly = false) {
+ const options = Object.create(null);
+ for (const name in defaultOptions) {
+ const defaultOption = defaultOptions[name];
+ if (kind && !(kind & defaultOption.kind)) {
+ continue;
+ }
+ options[name] = defaultOnly ? defaultOption.value : userOptions[name] ?? defaultOption.value;
+ }
+ return options;
+ }
+ static set(name, value) {
+ userOptions[name] = value;
+ }
+ static setAll(options, init = false) {
+ if (init) {
+ if (this.get("disablePreferences")) {
+ return;
+ }
+ for (const name in userOptions) {
+ if (compatibilityParams[name] !== undefined) {
+ continue;
+ }
+ console.warn("setAll: The Preferences may override manually set AppOptions; " + 'please use the "disablePreferences"-option in order to prevent that.');
+ break;
+ }
+ }
+ for (const name in options) {
+ userOptions[name] = options[name];
+ }
+ }
+ static remove(name) {
+ delete userOptions[name];
+ const val = compatibilityParams[name];
+ if (val !== undefined) {
+ userOptions[name] = val;
+ }
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_link_service.js
+
+const DEFAULT_LINK_REL = "noopener noreferrer nofollow";
+const LinkTarget = {
+ NONE: 0,
+ SELF: 1,
+ BLANK: 2,
+ PARENT: 3,
+ TOP: 4
+};
+class PDFLinkService {
+ externalLinkEnabled = true;
+ constructor({
+ eventBus,
+ externalLinkTarget = null,
+ externalLinkRel = null,
+ ignoreDestinationZoom = false
+ } = {}) {
+ this.eventBus = eventBus;
+ this.externalLinkTarget = externalLinkTarget;
+ this.externalLinkRel = externalLinkRel;
+ this._ignoreDestinationZoom = ignoreDestinationZoom;
+ this.baseUrl = null;
+ this.pdfDocument = null;
+ this.pdfViewer = null;
+ this.pdfHistory = null;
+ }
+ setDocument(pdfDocument, baseUrl = null) {
+ this.baseUrl = baseUrl;
+ this.pdfDocument = pdfDocument;
+ }
+ setViewer(pdfViewer) {
+ this.pdfViewer = pdfViewer;
+ }
+ setHistory(pdfHistory) {
+ this.pdfHistory = pdfHistory;
+ }
+ get pagesCount() {
+ return this.pdfDocument ? this.pdfDocument.numPages : 0;
+ }
+ get page() {
+ return this.pdfDocument ? this.pdfViewer.currentPageNumber : 1;
+ }
+ set page(value) {
+ if (this.pdfDocument) {
+ this.pdfViewer.currentPageNumber = value;
+ }
+ }
+ get rotation() {
+ return this.pdfDocument ? this.pdfViewer.pagesRotation : 0;
+ }
+ set rotation(value) {
+ if (this.pdfDocument) {
+ this.pdfViewer.pagesRotation = value;
+ }
+ }
+ get isInPresentationMode() {
+ return this.pdfDocument ? this.pdfViewer.isInPresentationMode : false;
+ }
+ async goToDestination(dest) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ let namedDest, explicitDest, pageNumber;
+ if (typeof dest === "string") {
+ namedDest = dest;
+ explicitDest = await this.pdfDocument.getDestination(dest);
+ } else {
+ namedDest = null;
+ explicitDest = await dest;
+ }
+ if (!Array.isArray(explicitDest)) {
+ console.error(`goToDestination: "${explicitDest}" is not a valid destination array, for dest="${dest}".`);
+ return;
+ }
+ const [destRef] = explicitDest;
+ if (destRef && typeof destRef === "object") {
+ pageNumber = this.pdfDocument.cachedPageNumber(destRef);
+ if (!pageNumber) {
+ try {
+ pageNumber = (await this.pdfDocument.getPageIndex(destRef)) + 1;
+ } catch {
+ console.error(`goToDestination: "${destRef}" is not a valid page reference, for dest="${dest}".`);
+ return;
+ }
+ }
+ } else if (Number.isInteger(destRef)) {
+ pageNumber = destRef + 1;
+ }
+ if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
+ console.error(`goToDestination: "${pageNumber}" is not a valid page number, for dest="${dest}".`);
+ return;
+ }
+ if (this.pdfHistory) {
+ this.pdfHistory.pushCurrentPosition();
+ this.pdfHistory.push({
+ namedDest,
+ explicitDest,
+ pageNumber
+ });
+ }
+ this.pdfViewer.scrollPageIntoView({
+ pageNumber,
+ destArray: explicitDest,
+ ignoreDestinationZoom: this._ignoreDestinationZoom
+ });
+ }
+ goToPage(val) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ const pageNumber = typeof val === "string" && this.pdfViewer.pageLabelToPageNumber(val) || val | 0;
+ if (!(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.pagesCount)) {
+ console.error(`PDFLinkService.goToPage: "${val}" is not a valid page.`);
+ return;
+ }
+ if (this.pdfHistory) {
+ this.pdfHistory.pushCurrentPosition();
+ this.pdfHistory.pushPage(pageNumber);
+ }
+ this.pdfViewer.scrollPageIntoView({
+ pageNumber
+ });
+ }
+ addLinkAttributes(link, url, newWindow = false) {
+ if (!url || typeof url !== "string") {
+ throw new Error('A valid "url" parameter must provided.');
+ }
+ const target = newWindow ? LinkTarget.BLANK : this.externalLinkTarget,
+ rel = this.externalLinkRel;
+ if (this.externalLinkEnabled) {
+ link.href = link.title = url;
+ } else {
+ link.href = "";
+ link.title = `Disabled: ${url}`;
+ link.onclick = () => false;
+ }
+ let targetStr = "";
+ switch (target) {
+ case LinkTarget.NONE:
+ break;
+ case LinkTarget.SELF:
+ targetStr = "_self";
+ break;
+ case LinkTarget.BLANK:
+ targetStr = "_blank";
+ break;
+ case LinkTarget.PARENT:
+ targetStr = "_parent";
+ break;
+ case LinkTarget.TOP:
+ targetStr = "_top";
+ break;
+ }
+ link.target = targetStr;
+ link.rel = typeof rel === "string" ? rel : DEFAULT_LINK_REL;
+ }
+ getDestinationHash(dest) {
+ if (typeof dest === "string") {
+ if (dest.length > 0) {
+ return this.getAnchorUrl("#" + escape(dest));
+ }
+ } else if (Array.isArray(dest)) {
+ const str = JSON.stringify(dest);
+ if (str.length > 0) {
+ return this.getAnchorUrl("#" + escape(str));
+ }
+ }
+ return this.getAnchorUrl("");
+ }
+ getAnchorUrl(anchor) {
+ return this.baseUrl ? this.baseUrl + anchor : anchor;
+ }
+ setHash(hash) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ let pageNumber, dest;
+ if (hash.includes("=")) {
+ const params = parseQueryString(hash);
+ if (params.has("search")) {
+ const query = params.get("search").replaceAll('"', ""),
+ phrase = params.get("phrase") === "true";
+ this.eventBus.dispatch("findfromurlhash", {
+ source: this,
+ query: phrase ? query : query.match(/\S+/g)
+ });
+ }
+ if (params.has("page")) {
+ pageNumber = params.get("page") | 0 || 1;
+ }
+ if (params.has("zoom")) {
+ const zoomArgs = params.get("zoom").split(",");
+ const zoomArg = zoomArgs[0];
+ const zoomArgNumber = parseFloat(zoomArg);
+ if (!zoomArg.includes("Fit")) {
+ dest = [null, {
+ name: "XYZ"
+ }, zoomArgs.length > 1 ? zoomArgs[1] | 0 : null, zoomArgs.length > 2 ? zoomArgs[2] | 0 : null, zoomArgNumber ? zoomArgNumber / 100 : zoomArg];
+ } else if (zoomArg === "Fit" || zoomArg === "FitB") {
+ dest = [null, {
+ name: zoomArg
+ }];
+ } else if (zoomArg === "FitH" || zoomArg === "FitBH" || zoomArg === "FitV" || zoomArg === "FitBV") {
+ dest = [null, {
+ name: zoomArg
+ }, zoomArgs.length > 1 ? zoomArgs[1] | 0 : null];
+ } else if (zoomArg === "FitR") {
+ if (zoomArgs.length !== 5) {
+ console.error('PDFLinkService.setHash: Not enough parameters for "FitR".');
+ } else {
+ dest = [null, {
+ name: zoomArg
+ }, zoomArgs[1] | 0, zoomArgs[2] | 0, zoomArgs[3] | 0, zoomArgs[4] | 0];
+ }
+ } else {
+ console.error(`PDFLinkService.setHash: "${zoomArg}" is not a valid zoom value.`);
+ }
+ }
+ if (dest) {
+ this.pdfViewer.scrollPageIntoView({
+ pageNumber: pageNumber || this.page,
+ destArray: dest,
+ allowNegativeOffset: true
+ });
+ } else if (pageNumber) {
+ this.page = pageNumber;
+ }
+ if (params.has("pagemode")) {
+ this.eventBus.dispatch("pagemode", {
+ source: this,
+ mode: params.get("pagemode")
+ });
+ }
+ if (params.has("nameddest")) {
+ this.goToDestination(params.get("nameddest"));
+ }
+ return;
+ }
+ dest = unescape(hash);
+ try {
+ dest = JSON.parse(dest);
+ if (!Array.isArray(dest)) {
+ dest = dest.toString();
+ }
+ } catch {}
+ if (typeof dest === "string" || PDFLinkService.#isValidExplicitDest(dest)) {
+ this.goToDestination(dest);
+ return;
+ }
+ console.error(`PDFLinkService.setHash: "${unescape(hash)}" is not a valid destination.`);
+ }
+ executeNamedAction(action) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ switch (action) {
+ case "GoBack":
+ this.pdfHistory?.back();
+ break;
+ case "GoForward":
+ this.pdfHistory?.forward();
+ break;
+ case "NextPage":
+ this.pdfViewer.nextPage();
+ break;
+ case "PrevPage":
+ this.pdfViewer.previousPage();
+ break;
+ case "LastPage":
+ this.page = this.pagesCount;
+ break;
+ case "FirstPage":
+ this.page = 1;
+ break;
+ default:
+ break;
+ }
+ this.eventBus.dispatch("namedaction", {
+ source: this,
+ action
+ });
+ }
+ async executeSetOCGState(action) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ const pdfDocument = this.pdfDocument,
+ optionalContentConfig = await this.pdfViewer.optionalContentConfigPromise;
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ optionalContentConfig.setOCGState(action);
+ this.pdfViewer.optionalContentConfigPromise = Promise.resolve(optionalContentConfig);
+ }
+ static #isValidExplicitDest(dest) {
+ if (!Array.isArray(dest) || dest.length < 2) {
+ return false;
+ }
+ const [page, zoom, ...args] = dest;
+ if (!(typeof page === "object" && Number.isInteger(page?.num) && Number.isInteger(page?.gen)) && !Number.isInteger(page)) {
+ return false;
+ }
+ if (!(typeof zoom === "object" && typeof zoom?.name === "string")) {
+ return false;
+ }
+ let allowNull = true;
+ switch (zoom.name) {
+ case "XYZ":
+ if (args.length !== 3) {
+ return false;
+ }
+ break;
+ case "Fit":
+ case "FitB":
+ return args.length === 0;
+ case "FitH":
+ case "FitBH":
+ case "FitV":
+ case "FitBV":
+ if (args.length !== 1) {
+ return false;
+ }
+ break;
+ case "FitR":
+ if (args.length !== 4) {
+ return false;
+ }
+ allowNull = false;
+ break;
+ default:
+ return false;
+ }
+ for (const arg of args) {
+ if (!(typeof arg === "number" || allowNull && arg === null)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+class SimpleLinkService extends PDFLinkService {
+ setDocument(pdfDocument, baseUrl = null) {}
+}
+
+;// CONCATENATED MODULE: ./web/pdfjs.js
+const {
+ AbortException,
+ AnnotationEditorLayer,
+ AnnotationEditorParamsType,
+ AnnotationEditorType,
+ AnnotationEditorUIManager,
+ AnnotationLayer,
+ AnnotationMode,
+ build,
+ CMapCompressionType,
+ ColorPicker,
+ createValidAbsoluteUrl,
+ DOMSVGFactory,
+ DrawLayer,
+ FeatureTest,
+ fetchData,
+ getDocument,
+ getFilenameFromUrl,
+ getPdfFilenameFromUrl: pdfjs_getPdfFilenameFromUrl,
+ getXfaPageViewport,
+ GlobalWorkerOptions,
+ ImageKind,
+ InvalidPDFException,
+ isDataScheme,
+ isPdfFile,
+ MissingPDFException,
+ noContextMenu,
+ normalizeUnicode,
+ OPS,
+ Outliner,
+ PasswordResponses,
+ PDFDataRangeTransport,
+ PDFDateString,
+ PDFWorker,
+ PermissionFlag,
+ PixelsPerInch,
+ RenderingCancelledException,
+ renderTextLayer,
+ setLayerDimensions,
+ shadow,
+ TextLayer,
+ UnexpectedResponseException,
+ updateTextLayer,
+ Util,
+ VerbosityLevel,
+ version,
+ XfaLayer
+} = globalThis.pdfjsLib;
+
+;// CONCATENATED MODULE: ./web/event_utils.js
+const WaitOnType = {
+ EVENT: "event",
+ TIMEOUT: "timeout"
+};
+async function waitOnEventOrTimeout({
+ target,
+ name,
+ delay = 0
+}) {
+ if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) {
+ throw new Error("waitOnEventOrTimeout - invalid parameters.");
+ }
+ const {
+ promise,
+ resolve
+ } = Promise.withResolvers();
+ const ac = new AbortController();
+ function handler(type) {
+ ac.abort();
+ clearTimeout(timeout);
+ resolve(type);
+ }
+ const evtMethod = target instanceof EventBus ? "_on" : "addEventListener";
+ target[evtMethod](name, handler.bind(null, WaitOnType.EVENT), {
+ signal: ac.signal
+ });
+ const timeout = setTimeout(handler.bind(null, WaitOnType.TIMEOUT), delay);
+ return promise;
+}
+class EventBus {
+ #listeners = Object.create(null);
+ on(eventName, listener, options = null) {
+ this._on(eventName, listener, {
+ external: true,
+ once: options?.once,
+ signal: options?.signal
+ });
+ }
+ off(eventName, listener, options = null) {
+ this._off(eventName, listener);
+ }
+ dispatch(eventName, data) {
+ const eventListeners = this.#listeners[eventName];
+ if (!eventListeners || eventListeners.length === 0) {
+ return;
+ }
+ let externalListeners;
+ for (const {
+ listener,
+ external,
+ once
+ } of eventListeners.slice(0)) {
+ if (once) {
+ this._off(eventName, listener);
+ }
+ if (external) {
+ (externalListeners ||= []).push(listener);
+ continue;
+ }
+ listener(data);
+ }
+ if (externalListeners) {
+ for (const listener of externalListeners) {
+ listener(data);
+ }
+ externalListeners = null;
+ }
+ }
+ _on(eventName, listener, options = null) {
+ let rmAbort = null;
+ if (options?.signal instanceof AbortSignal) {
+ const {
+ signal
+ } = options;
+ if (signal.aborted) {
+ console.error("Cannot use an `aborted` signal.");
+ return;
+ }
+ const onAbort = () => this._off(eventName, listener);
+ rmAbort = () => signal.removeEventListener("abort", onAbort);
+ signal.addEventListener("abort", onAbort);
+ }
+ const eventListeners = this.#listeners[eventName] ||= [];
+ eventListeners.push({
+ listener,
+ external: options?.external === true,
+ once: options?.once === true,
+ rmAbort
+ });
+ }
+ _off(eventName, listener, options = null) {
+ const eventListeners = this.#listeners[eventName];
+ if (!eventListeners) {
+ return;
+ }
+ for (let i = 0, ii = eventListeners.length; i < ii; i++) {
+ const evt = eventListeners[i];
+ if (evt.listener === listener) {
+ evt.rmAbort?.();
+ eventListeners.splice(i, 1);
+ return;
+ }
+ }
+ }
+}
+class AutomationEventBus extends EventBus {
+ dispatch(eventName, data) {
+ throw new Error("Not implemented: AutomationEventBus.dispatch");
+ }
+}
+
+;// CONCATENATED MODULE: ./web/external_services.js
+class BaseExternalServices {
+ constructor() {
+ if (this.constructor === BaseExternalServices) {
+ throw new Error("Cannot initialize BaseExternalServices.");
+ }
+ }
+ updateFindControlState(data) {}
+ updateFindMatchesCount(data) {}
+ initPassiveLoading() {}
+ reportTelemetry(data) {}
+ async createL10n() {
+ throw new Error("Not implemented: createL10n");
+ }
+ createScripting() {
+ throw new Error("Not implemented: createScripting");
+ }
+ updateEditorStates(data) {
+ throw new Error("Not implemented: updateEditorStates");
+ }
+ async getNimbusExperimentData() {}
+}
+
+;// CONCATENATED MODULE: ./web/preferences.js
+
+class BasePreferences {
+ #browserDefaults = Object.freeze({
+ canvasMaxAreaInBytes: -1,
+ isInAutomation: false,
+ supportsCaretBrowsingMode: false,
+ supportsDocumentFonts: true,
+ supportsIntegratedFind: false,
+ supportsMouseWheelZoomCtrlKey: true,
+ supportsMouseWheelZoomMetaKey: true,
+ supportsPinchToZoom: true
+ });
+ #defaults = Object.freeze({
+ annotationEditorMode: 0,
+ annotationMode: 2,
+ cursorToolOnLoad: 0,
+ defaultZoomDelay: 400,
+ defaultZoomValue: "",
+ disablePageLabels: false,
+ enableHighlightEditor: false,
+ enableHighlightFloatingButton: false,
+ enableML: false,
+ enablePermissions: false,
+ enablePrintAutoRotate: true,
+ enableScripting: true,
+ enableStampEditor: true,
+ externalLinkTarget: 0,
+ highlightEditorColors: "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F",
+ historyUpdateUrl: false,
+ ignoreDestinationZoom: false,
+ forcePageColors: false,
+ pageColorsBackground: "Canvas",
+ pageColorsForeground: "CanvasText",
+ pdfBugEnabled: false,
+ sidebarViewOnLoad: -1,
+ scrollModeOnLoad: -1,
+ spreadModeOnLoad: -1,
+ textLayerMode: 1,
+ viewOnLoad: 0,
+ disableAutoFetch: false,
+ disableFontFace: false,
+ disableRange: false,
+ disableStream: false,
+ enableXfa: true,
+ viewerCssTheme: 0
+ });
+ #prefs = Object.create(null);
+ #initializedPromise = null;
+ constructor() {
+ if (this.constructor === BasePreferences) {
+ throw new Error("Cannot initialize BasePreferences.");
+ }
+ this.#initializedPromise = this._readFromStorage(this.#defaults).then(({
+ browserPrefs,
+ prefs
+ }) => {
+ const options = Object.create(null);
+ for (const [name, val] of Object.entries(this.#browserDefaults)) {
+ const prefVal = browserPrefs?.[name];
+ options[name] = typeof prefVal === typeof val ? prefVal : val;
+ }
+ for (const [name, val] of Object.entries(this.#defaults)) {
+ const prefVal = prefs?.[name];
+ options[name] = this.#prefs[name] = typeof prefVal === typeof val ? prefVal : val;
+ }
+ AppOptions.setAll(options, true);
+ });
+ }
+ async _writeToStorage(prefObj) {
+ throw new Error("Not implemented: _writeToStorage");
+ }
+ async _readFromStorage(prefObj) {
+ throw new Error("Not implemented: _readFromStorage");
+ }
+ #updatePref({
+ name,
+ value
+ }) {
+ throw new Error("Not implemented: #updatePref");
+ }
+ async reset() {
+ await this.#initializedPromise;
+ const oldPrefs = structuredClone(this.#prefs);
+ this.#prefs = Object.create(null);
+ try {
+ await this._writeToStorage(this.#defaults);
+ } catch (reason) {
+ this.#prefs = oldPrefs;
+ throw reason;
+ }
+ }
+ async set(name, value) {
+ await this.#initializedPromise;
+ const defaultValue = this.#defaults[name],
+ oldPrefs = structuredClone(this.#prefs);
+ if (defaultValue === undefined) {
+ throw new Error(`Set preference: "${name}" is undefined.`);
+ } else if (value === undefined) {
+ throw new Error("Set preference: no value is specified.");
+ }
+ const valueType = typeof value,
+ defaultType = typeof defaultValue;
+ if (valueType !== defaultType) {
+ if (valueType === "number" && defaultType === "string") {
+ value = value.toString();
+ } else {
+ throw new Error(`Set preference: "${value}" is a ${valueType}, expected a ${defaultType}.`);
+ }
+ } else if (valueType === "number" && !Number.isInteger(value)) {
+ throw new Error(`Set preference: "${value}" must be an integer.`);
+ }
+ this.#prefs[name] = value;
+ try {
+ await this._writeToStorage(this.#prefs);
+ } catch (reason) {
+ this.#prefs = oldPrefs;
+ throw reason;
+ }
+ }
+ async get(name) {
+ await this.#initializedPromise;
+ const defaultValue = this.#defaults[name];
+ if (defaultValue === undefined) {
+ throw new Error(`Get preference: "${name}" is undefined.`);
+ }
+ return this.#prefs[name] ?? defaultValue;
+ }
+ get initializedPromise() {
+ return this.#initializedPromise;
+ }
+}
+
+;// CONCATENATED MODULE: ./node_modules/@fluent/bundle/esm/types.js
+class FluentType {
+ constructor(value) {
+ this.value = value;
+ }
+ valueOf() {
+ return this.value;
+ }
+}
+class FluentNone extends FluentType {
+ constructor(value = "???") {
+ super(value);
+ }
+ toString(scope) {
+ return `{${this.value}}`;
+ }
+}
+class FluentNumber extends FluentType {
+ constructor(value, opts = {}) {
+ super(value);
+ this.opts = opts;
+ }
+ toString(scope) {
+ try {
+ const nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts);
+ return nf.format(this.value);
+ } catch (err) {
+ scope.reportError(err);
+ return this.value.toString(10);
+ }
+ }
+}
+class FluentDateTime extends FluentType {
+ constructor(value, opts = {}) {
+ super(value);
+ this.opts = opts;
+ }
+ toString(scope) {
+ try {
+ const dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts);
+ return dtf.format(this.value);
+ } catch (err) {
+ scope.reportError(err);
+ return new Date(this.value).toISOString();
+ }
+ }
+}
+;// CONCATENATED MODULE: ./node_modules/@fluent/bundle/esm/resolver.js
+
+const MAX_PLACEABLES = 100;
+const FSI = "\u2068";
+const PDI = "\u2069";
+function match(scope, selector, key) {
+ if (key === selector) {
+ return true;
+ }
+ if (key instanceof FluentNumber && selector instanceof FluentNumber && key.value === selector.value) {
+ return true;
+ }
+ if (selector instanceof FluentNumber && typeof key === "string") {
+ let category = scope.memoizeIntlObject(Intl.PluralRules, selector.opts).select(selector.value);
+ if (key === category) {
+ return true;
+ }
+ }
+ return false;
+}
+function getDefault(scope, variants, star) {
+ if (variants[star]) {
+ return resolvePattern(scope, variants[star].value);
+ }
+ scope.reportError(new RangeError("No default"));
+ return new FluentNone();
+}
+function getArguments(scope, args) {
+ const positional = [];
+ const named = Object.create(null);
+ for (const arg of args) {
+ if (arg.type === "narg") {
+ named[arg.name] = resolveExpression(scope, arg.value);
+ } else {
+ positional.push(resolveExpression(scope, arg));
+ }
+ }
+ return {
+ positional,
+ named
+ };
+}
+function resolveExpression(scope, expr) {
+ switch (expr.type) {
+ case "str":
+ return expr.value;
+ case "num":
+ return new FluentNumber(expr.value, {
+ minimumFractionDigits: expr.precision
+ });
+ case "var":
+ return resolveVariableReference(scope, expr);
+ case "mesg":
+ return resolveMessageReference(scope, expr);
+ case "term":
+ return resolveTermReference(scope, expr);
+ case "func":
+ return resolveFunctionReference(scope, expr);
+ case "select":
+ return resolveSelectExpression(scope, expr);
+ default:
+ return new FluentNone();
+ }
+}
+function resolveVariableReference(scope, {
+ name
+}) {
+ let arg;
+ if (scope.params) {
+ if (Object.prototype.hasOwnProperty.call(scope.params, name)) {
+ arg = scope.params[name];
+ } else {
+ return new FluentNone(`$${name}`);
+ }
+ } else if (scope.args && Object.prototype.hasOwnProperty.call(scope.args, name)) {
+ arg = scope.args[name];
+ } else {
+ scope.reportError(new ReferenceError(`Unknown variable: $${name}`));
+ return new FluentNone(`$${name}`);
+ }
+ if (arg instanceof FluentType) {
+ return arg;
+ }
+ switch (typeof arg) {
+ case "string":
+ return arg;
+ case "number":
+ return new FluentNumber(arg);
+ case "object":
+ if (arg instanceof Date) {
+ return new FluentDateTime(arg.getTime());
+ }
+ default:
+ scope.reportError(new TypeError(`Variable type not supported: $${name}, ${typeof arg}`));
+ return new FluentNone(`$${name}`);
+ }
+}
+function resolveMessageReference(scope, {
+ name,
+ attr
+}) {
+ const message = scope.bundle._messages.get(name);
+ if (!message) {
+ scope.reportError(new ReferenceError(`Unknown message: ${name}`));
+ return new FluentNone(name);
+ }
+ if (attr) {
+ const attribute = message.attributes[attr];
+ if (attribute) {
+ return resolvePattern(scope, attribute);
+ }
+ scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`));
+ return new FluentNone(`${name}.${attr}`);
+ }
+ if (message.value) {
+ return resolvePattern(scope, message.value);
+ }
+ scope.reportError(new ReferenceError(`No value: ${name}`));
+ return new FluentNone(name);
+}
+function resolveTermReference(scope, {
+ name,
+ attr,
+ args
+}) {
+ const id = `-${name}`;
+ const term = scope.bundle._terms.get(id);
+ if (!term) {
+ scope.reportError(new ReferenceError(`Unknown term: ${id}`));
+ return new FluentNone(id);
+ }
+ if (attr) {
+ const attribute = term.attributes[attr];
+ if (attribute) {
+ scope.params = getArguments(scope, args).named;
+ const resolved = resolvePattern(scope, attribute);
+ scope.params = null;
+ return resolved;
+ }
+ scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`));
+ return new FluentNone(`${id}.${attr}`);
+ }
+ scope.params = getArguments(scope, args).named;
+ const resolved = resolvePattern(scope, term.value);
+ scope.params = null;
+ return resolved;
+}
+function resolveFunctionReference(scope, {
+ name,
+ args
+}) {
+ let func = scope.bundle._functions[name];
+ if (!func) {
+ scope.reportError(new ReferenceError(`Unknown function: ${name}()`));
+ return new FluentNone(`${name}()`);
+ }
+ if (typeof func !== "function") {
+ scope.reportError(new TypeError(`Function ${name}() is not callable`));
+ return new FluentNone(`${name}()`);
+ }
+ try {
+ let resolved = getArguments(scope, args);
+ return func(resolved.positional, resolved.named);
+ } catch (err) {
+ scope.reportError(err);
+ return new FluentNone(`${name}()`);
+ }
+}
+function resolveSelectExpression(scope, {
+ selector,
+ variants,
+ star
+}) {
+ let sel = resolveExpression(scope, selector);
+ if (sel instanceof FluentNone) {
+ return getDefault(scope, variants, star);
+ }
+ for (const variant of variants) {
+ const key = resolveExpression(scope, variant.key);
+ if (match(scope, sel, key)) {
+ return resolvePattern(scope, variant.value);
+ }
+ }
+ return getDefault(scope, variants, star);
+}
+function resolveComplexPattern(scope, ptn) {
+ if (scope.dirty.has(ptn)) {
+ scope.reportError(new RangeError("Cyclic reference"));
+ return new FluentNone();
+ }
+ scope.dirty.add(ptn);
+ const result = [];
+ const useIsolating = scope.bundle._useIsolating && ptn.length > 1;
+ for (const elem of ptn) {
+ if (typeof elem === "string") {
+ result.push(scope.bundle._transform(elem));
+ continue;
+ }
+ scope.placeables++;
+ if (scope.placeables > MAX_PLACEABLES) {
+ scope.dirty.delete(ptn);
+ throw new RangeError(`Too many placeables expanded: ${scope.placeables}, ` + `max allowed is ${MAX_PLACEABLES}`);
+ }
+ if (useIsolating) {
+ result.push(FSI);
+ }
+ result.push(resolveExpression(scope, elem).toString(scope));
+ if (useIsolating) {
+ result.push(PDI);
+ }
+ }
+ scope.dirty.delete(ptn);
+ return result.join("");
+}
+function resolvePattern(scope, value) {
+ if (typeof value === "string") {
+ return scope.bundle._transform(value);
+ }
+ return resolveComplexPattern(scope, value);
+}
+;// CONCATENATED MODULE: ./node_modules/@fluent/bundle/esm/scope.js
+class Scope {
+ constructor(bundle, errors, args) {
+ this.dirty = new WeakSet();
+ this.params = null;
+ this.placeables = 0;
+ this.bundle = bundle;
+ this.errors = errors;
+ this.args = args;
+ }
+ reportError(error) {
+ if (!this.errors || !(error instanceof Error)) {
+ throw error;
+ }
+ this.errors.push(error);
+ }
+ memoizeIntlObject(ctor, opts) {
+ let cache = this.bundle._intls.get(ctor);
+ if (!cache) {
+ cache = {};
+ this.bundle._intls.set(ctor, cache);
+ }
+ let id = JSON.stringify(opts);
+ if (!cache[id]) {
+ cache[id] = new ctor(this.bundle.locales, opts);
+ }
+ return cache[id];
+ }
+}
+;// CONCATENATED MODULE: ./node_modules/@fluent/bundle/esm/builtins.js
+
+function values(opts, allowed) {
+ const unwrapped = Object.create(null);
+ for (const [name, opt] of Object.entries(opts)) {
+ if (allowed.includes(name)) {
+ unwrapped[name] = opt.valueOf();
+ }
+ }
+ return unwrapped;
+}
+const NUMBER_ALLOWED = ["unitDisplay", "currencyDisplay", "useGrouping", "minimumIntegerDigits", "minimumFractionDigits", "maximumFractionDigits", "minimumSignificantDigits", "maximumSignificantDigits"];
+function NUMBER(args, opts) {
+ let arg = args[0];
+ if (arg instanceof FluentNone) {
+ return new FluentNone(`NUMBER(${arg.valueOf()})`);
+ }
+ if (arg instanceof FluentNumber) {
+ return new FluentNumber(arg.valueOf(), {
+ ...arg.opts,
+ ...values(opts, NUMBER_ALLOWED)
+ });
+ }
+ if (arg instanceof FluentDateTime) {
+ return new FluentNumber(arg.valueOf(), {
+ ...values(opts, NUMBER_ALLOWED)
+ });
+ }
+ throw new TypeError("Invalid argument to NUMBER");
+}
+const DATETIME_ALLOWED = ["dateStyle", "timeStyle", "fractionalSecondDigits", "dayPeriod", "hour12", "weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"];
+function DATETIME(args, opts) {
+ let arg = args[0];
+ if (arg instanceof FluentNone) {
+ return new FluentNone(`DATETIME(${arg.valueOf()})`);
+ }
+ if (arg instanceof FluentDateTime) {
+ return new FluentDateTime(arg.valueOf(), {
+ ...arg.opts,
+ ...values(opts, DATETIME_ALLOWED)
+ });
+ }
+ if (arg instanceof FluentNumber) {
+ return new FluentDateTime(arg.valueOf(), {
+ ...values(opts, DATETIME_ALLOWED)
+ });
+ }
+ throw new TypeError("Invalid argument to DATETIME");
+}
+;// CONCATENATED MODULE: ./node_modules/@fluent/bundle/esm/memoizer.js
+const cache = new Map();
+function getMemoizerForLocale(locales) {
+ const stringLocale = Array.isArray(locales) ? locales.join(" ") : locales;
+ let memoizer = cache.get(stringLocale);
+ if (memoizer === undefined) {
+ memoizer = new Map();
+ cache.set(stringLocale, memoizer);
+ }
+ return memoizer;
+}
+;// CONCATENATED MODULE: ./node_modules/@fluent/bundle/esm/bundle.js
+
+
+
+
+
+class FluentBundle {
+ constructor(locales, {
+ functions,
+ useIsolating = true,
+ transform = v => v
+ } = {}) {
+ this._terms = new Map();
+ this._messages = new Map();
+ this.locales = Array.isArray(locales) ? locales : [locales];
+ this._functions = {
+ NUMBER: NUMBER,
+ DATETIME: DATETIME,
+ ...functions
+ };
+ this._useIsolating = useIsolating;
+ this._transform = transform;
+ this._intls = getMemoizerForLocale(locales);
+ }
+ hasMessage(id) {
+ return this._messages.has(id);
+ }
+ getMessage(id) {
+ return this._messages.get(id);
+ }
+ addResource(res, {
+ allowOverrides = false
+ } = {}) {
+ const errors = [];
+ for (let i = 0; i < res.body.length; i++) {
+ let entry = res.body[i];
+ if (entry.id.startsWith("-")) {
+ if (allowOverrides === false && this._terms.has(entry.id)) {
+ errors.push(new Error(`Attempt to override an existing term: "${entry.id}"`));
+ continue;
+ }
+ this._terms.set(entry.id, entry);
+ } else {
+ if (allowOverrides === false && this._messages.has(entry.id)) {
+ errors.push(new Error(`Attempt to override an existing message: "${entry.id}"`));
+ continue;
+ }
+ this._messages.set(entry.id, entry);
+ }
+ }
+ return errors;
+ }
+ formatPattern(pattern, args = null, errors = null) {
+ if (typeof pattern === "string") {
+ return this._transform(pattern);
+ }
+ let scope = new Scope(this, errors, args);
+ try {
+ let value = resolveComplexPattern(scope, pattern);
+ return value.toString(scope);
+ } catch (err) {
+ if (scope.errors && err instanceof Error) {
+ scope.errors.push(err);
+ return new FluentNone().toString(scope);
+ }
+ throw err;
+ }
+ }
+}
+;// CONCATENATED MODULE: ./node_modules/@fluent/bundle/esm/resource.js
+const RE_MESSAGE_START = /^(-?[a-zA-Z][\w-]*) *= */gm;
+const RE_ATTRIBUTE_START = /\.([a-zA-Z][\w-]*) *= */y;
+const RE_VARIANT_START = /\*?\[/y;
+const RE_NUMBER_LITERAL = /(-?[0-9]+(?:\.([0-9]+))?)/y;
+const RE_IDENTIFIER = /([a-zA-Z][\w-]*)/y;
+const RE_REFERENCE = /([$-])?([a-zA-Z][\w-]*)(?:\.([a-zA-Z][\w-]*))?/y;
+const RE_FUNCTION_NAME = /^[A-Z][A-Z0-9_-]*$/;
+const RE_TEXT_RUN = /([^{}\n\r]+)/y;
+const RE_STRING_RUN = /([^\\"\n\r]*)/y;
+const RE_STRING_ESCAPE = /\\([\\"])/y;
+const RE_UNICODE_ESCAPE = /\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{6})/y;
+const RE_LEADING_NEWLINES = /^\n+/;
+const RE_TRAILING_SPACES = / +$/;
+const RE_BLANK_LINES = / *\r?\n/g;
+const RE_INDENT = /( *)$/;
+const TOKEN_BRACE_OPEN = /{\s*/y;
+const TOKEN_BRACE_CLOSE = /\s*}/y;
+const TOKEN_BRACKET_OPEN = /\[\s*/y;
+const TOKEN_BRACKET_CLOSE = /\s*] */y;
+const TOKEN_PAREN_OPEN = /\s*\(\s*/y;
+const TOKEN_ARROW = /\s*->\s*/y;
+const TOKEN_COLON = /\s*:\s*/y;
+const TOKEN_COMMA = /\s*,?\s*/y;
+const TOKEN_BLANK = /\s+/y;
+class FluentResource {
+ constructor(source) {
+ this.body = [];
+ RE_MESSAGE_START.lastIndex = 0;
+ let cursor = 0;
+ while (true) {
+ let next = RE_MESSAGE_START.exec(source);
+ if (next === null) {
+ break;
+ }
+ cursor = RE_MESSAGE_START.lastIndex;
+ try {
+ this.body.push(parseMessage(next[1]));
+ } catch (err) {
+ if (err instanceof SyntaxError) {
+ continue;
+ }
+ throw err;
+ }
+ }
+ function test(re) {
+ re.lastIndex = cursor;
+ return re.test(source);
+ }
+ function consumeChar(char, errorClass) {
+ if (source[cursor] === char) {
+ cursor++;
+ return true;
+ }
+ if (errorClass) {
+ throw new errorClass(`Expected ${char}`);
+ }
+ return false;
+ }
+ function consumeToken(re, errorClass) {
+ if (test(re)) {
+ cursor = re.lastIndex;
+ return true;
+ }
+ if (errorClass) {
+ throw new errorClass(`Expected ${re.toString()}`);
+ }
+ return false;
+ }
+ function match(re) {
+ re.lastIndex = cursor;
+ let result = re.exec(source);
+ if (result === null) {
+ throw new SyntaxError(`Expected ${re.toString()}`);
+ }
+ cursor = re.lastIndex;
+ return result;
+ }
+ function match1(re) {
+ return match(re)[1];
+ }
+ function parseMessage(id) {
+ let value = parsePattern();
+ let attributes = parseAttributes();
+ if (value === null && Object.keys(attributes).length === 0) {
+ throw new SyntaxError("Expected message value or attributes");
+ }
+ return {
+ id,
+ value,
+ attributes
+ };
+ }
+ function parseAttributes() {
+ let attrs = Object.create(null);
+ while (test(RE_ATTRIBUTE_START)) {
+ let name = match1(RE_ATTRIBUTE_START);
+ let value = parsePattern();
+ if (value === null) {
+ throw new SyntaxError("Expected attribute value");
+ }
+ attrs[name] = value;
+ }
+ return attrs;
+ }
+ function parsePattern() {
+ let first;
+ if (test(RE_TEXT_RUN)) {
+ first = match1(RE_TEXT_RUN);
+ }
+ if (source[cursor] === "{" || source[cursor] === "}") {
+ return parsePatternElements(first ? [first] : [], Infinity);
+ }
+ let indent = parseIndent();
+ if (indent) {
+ if (first) {
+ return parsePatternElements([first, indent], indent.length);
+ }
+ indent.value = trim(indent.value, RE_LEADING_NEWLINES);
+ return parsePatternElements([indent], indent.length);
+ }
+ if (first) {
+ return trim(first, RE_TRAILING_SPACES);
+ }
+ return null;
+ }
+ function parsePatternElements(elements = [], commonIndent) {
+ while (true) {
+ if (test(RE_TEXT_RUN)) {
+ elements.push(match1(RE_TEXT_RUN));
+ continue;
+ }
+ if (source[cursor] === "{") {
+ elements.push(parsePlaceable());
+ continue;
+ }
+ if (source[cursor] === "}") {
+ throw new SyntaxError("Unbalanced closing brace");
+ }
+ let indent = parseIndent();
+ if (indent) {
+ elements.push(indent);
+ commonIndent = Math.min(commonIndent, indent.length);
+ continue;
+ }
+ break;
+ }
+ let lastIndex = elements.length - 1;
+ let lastElement = elements[lastIndex];
+ if (typeof lastElement === "string") {
+ elements[lastIndex] = trim(lastElement, RE_TRAILING_SPACES);
+ }
+ let baked = [];
+ for (let element of elements) {
+ if (element instanceof Indent) {
+ element = element.value.slice(0, element.value.length - commonIndent);
+ }
+ if (element) {
+ baked.push(element);
+ }
+ }
+ return baked;
+ }
+ function parsePlaceable() {
+ consumeToken(TOKEN_BRACE_OPEN, SyntaxError);
+ let selector = parseInlineExpression();
+ if (consumeToken(TOKEN_BRACE_CLOSE)) {
+ return selector;
+ }
+ if (consumeToken(TOKEN_ARROW)) {
+ let variants = parseVariants();
+ consumeToken(TOKEN_BRACE_CLOSE, SyntaxError);
+ return {
+ type: "select",
+ selector,
+ ...variants
+ };
+ }
+ throw new SyntaxError("Unclosed placeable");
+ }
+ function parseInlineExpression() {
+ if (source[cursor] === "{") {
+ return parsePlaceable();
+ }
+ if (test(RE_REFERENCE)) {
+ let [, sigil, name, attr = null] = match(RE_REFERENCE);
+ if (sigil === "$") {
+ return {
+ type: "var",
+ name
+ };
+ }
+ if (consumeToken(TOKEN_PAREN_OPEN)) {
+ let args = parseArguments();
+ if (sigil === "-") {
+ return {
+ type: "term",
+ name,
+ attr,
+ args
+ };
+ }
+ if (RE_FUNCTION_NAME.test(name)) {
+ return {
+ type: "func",
+ name,
+ args
+ };
+ }
+ throw new SyntaxError("Function names must be all upper-case");
+ }
+ if (sigil === "-") {
+ return {
+ type: "term",
+ name,
+ attr,
+ args: []
+ };
+ }
+ return {
+ type: "mesg",
+ name,
+ attr
+ };
+ }
+ return parseLiteral();
+ }
+ function parseArguments() {
+ let args = [];
+ while (true) {
+ switch (source[cursor]) {
+ case ")":
+ cursor++;
+ return args;
+ case undefined:
+ throw new SyntaxError("Unclosed argument list");
+ }
+ args.push(parseArgument());
+ consumeToken(TOKEN_COMMA);
+ }
+ }
+ function parseArgument() {
+ let expr = parseInlineExpression();
+ if (expr.type !== "mesg") {
+ return expr;
+ }
+ if (consumeToken(TOKEN_COLON)) {
+ return {
+ type: "narg",
+ name: expr.name,
+ value: parseLiteral()
+ };
+ }
+ return expr;
+ }
+ function parseVariants() {
+ let variants = [];
+ let count = 0;
+ let star;
+ while (test(RE_VARIANT_START)) {
+ if (consumeChar("*")) {
+ star = count;
+ }
+ let key = parseVariantKey();
+ let value = parsePattern();
+ if (value === null) {
+ throw new SyntaxError("Expected variant value");
+ }
+ variants[count++] = {
+ key,
+ value
+ };
+ }
+ if (count === 0) {
+ return null;
+ }
+ if (star === undefined) {
+ throw new SyntaxError("Expected default variant");
+ }
+ return {
+ variants,
+ star
+ };
+ }
+ function parseVariantKey() {
+ consumeToken(TOKEN_BRACKET_OPEN, SyntaxError);
+ let key;
+ if (test(RE_NUMBER_LITERAL)) {
+ key = parseNumberLiteral();
+ } else {
+ key = {
+ type: "str",
+ value: match1(RE_IDENTIFIER)
+ };
+ }
+ consumeToken(TOKEN_BRACKET_CLOSE, SyntaxError);
+ return key;
+ }
+ function parseLiteral() {
+ if (test(RE_NUMBER_LITERAL)) {
+ return parseNumberLiteral();
+ }
+ if (source[cursor] === '"') {
+ return parseStringLiteral();
+ }
+ throw new SyntaxError("Invalid expression");
+ }
+ function parseNumberLiteral() {
+ let [, value, fraction = ""] = match(RE_NUMBER_LITERAL);
+ let precision = fraction.length;
+ return {
+ type: "num",
+ value: parseFloat(value),
+ precision
+ };
+ }
+ function parseStringLiteral() {
+ consumeChar('"', SyntaxError);
+ let value = "";
+ while (true) {
+ value += match1(RE_STRING_RUN);
+ if (source[cursor] === "\\") {
+ value += parseEscapeSequence();
+ continue;
+ }
+ if (consumeChar('"')) {
+ return {
+ type: "str",
+ value
+ };
+ }
+ throw new SyntaxError("Unclosed string literal");
+ }
+ }
+ function parseEscapeSequence() {
+ if (test(RE_STRING_ESCAPE)) {
+ return match1(RE_STRING_ESCAPE);
+ }
+ if (test(RE_UNICODE_ESCAPE)) {
+ let [, codepoint4, codepoint6] = match(RE_UNICODE_ESCAPE);
+ let codepoint = parseInt(codepoint4 || codepoint6, 16);
+ return codepoint <= 0xd7ff || 0xe000 <= codepoint ? String.fromCodePoint(codepoint) : "�";
+ }
+ throw new SyntaxError("Unknown escape sequence");
+ }
+ function parseIndent() {
+ let start = cursor;
+ consumeToken(TOKEN_BLANK);
+ switch (source[cursor]) {
+ case ".":
+ case "[":
+ case "*":
+ case "}":
+ case undefined:
+ return false;
+ case "{":
+ return makeIndent(source.slice(start, cursor));
+ }
+ if (source[cursor - 1] === " ") {
+ return makeIndent(source.slice(start, cursor));
+ }
+ return false;
+ }
+ function trim(text, re) {
+ return text.replace(re, "");
+ }
+ function makeIndent(blank) {
+ let value = blank.replace(RE_BLANK_LINES, "\n");
+ let length = RE_INDENT.exec(blank)[1].length;
+ return new Indent(value, length);
+ }
+ }
+}
+class Indent {
+ constructor(value, length) {
+ this.value = value;
+ this.length = length;
+ }
+}
+;// CONCATENATED MODULE: ./node_modules/@fluent/bundle/esm/index.js
+
+
+
+;// CONCATENATED MODULE: ./node_modules/@fluent/dom/esm/overlay.js
+const reOverlay = /<|?\w+;/;
+const TEXT_LEVEL_ELEMENTS = {
+ "http://www.w3.org/1999/xhtml": ["em", "strong", "small", "s", "cite", "q", "dfn", "abbr", "data", "time", "code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u", "mark", "bdi", "bdo", "span", "br", "wbr"]
+};
+const LOCALIZABLE_ATTRIBUTES = {
+ "http://www.w3.org/1999/xhtml": {
+ global: ["title", "aria-label", "aria-valuetext"],
+ a: ["download"],
+ area: ["download", "alt"],
+ input: ["alt", "placeholder"],
+ menuitem: ["label"],
+ menu: ["label"],
+ optgroup: ["label"],
+ option: ["label"],
+ track: ["label"],
+ img: ["alt"],
+ textarea: ["placeholder"],
+ th: ["abbr"]
+ },
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul": {
+ global: ["accesskey", "aria-label", "aria-valuetext", "label", "title", "tooltiptext"],
+ description: ["value"],
+ key: ["key", "keycode"],
+ label: ["value"],
+ textbox: ["placeholder", "value"]
+ }
+};
+function translateElement(element, translation) {
+ const {
+ value
+ } = translation;
+ if (typeof value === "string") {
+ if (element.localName === "title" && element.namespaceURI === "http://www.w3.org/1999/xhtml") {
+ element.textContent = value;
+ } else if (!reOverlay.test(value)) {
+ element.textContent = value;
+ } else {
+ const templateElement = element.ownerDocument.createElementNS("http://www.w3.org/1999/xhtml", "template");
+ templateElement.innerHTML = value;
+ overlayChildNodes(templateElement.content, element);
+ }
+ }
+ overlayAttributes(translation, element);
+}
+function overlayChildNodes(fromFragment, toElement) {
+ for (const childNode of fromFragment.childNodes) {
+ if (childNode.nodeType === childNode.TEXT_NODE) {
+ continue;
+ }
+ if (childNode.hasAttribute("data-l10n-name")) {
+ const sanitized = getNodeForNamedElement(toElement, childNode);
+ fromFragment.replaceChild(sanitized, childNode);
+ continue;
+ }
+ if (isElementAllowed(childNode)) {
+ const sanitized = createSanitizedElement(childNode);
+ fromFragment.replaceChild(sanitized, childNode);
+ continue;
+ }
+ console.warn(`An element of forbidden type "${childNode.localName}" was found in ` + "the translation. Only safe text-level elements and elements with " + "data-l10n-name are allowed.");
+ fromFragment.replaceChild(createTextNodeFromTextContent(childNode), childNode);
+ }
+ toElement.textContent = "";
+ toElement.appendChild(fromFragment);
+}
+function hasAttribute(attributes, name) {
+ if (!attributes) {
+ return false;
+ }
+ for (let attr of attributes) {
+ if (attr.name === name) {
+ return true;
+ }
+ }
+ return false;
+}
+function overlayAttributes(fromElement, toElement) {
+ const explicitlyAllowed = toElement.hasAttribute("data-l10n-attrs") ? toElement.getAttribute("data-l10n-attrs").split(",").map(i => i.trim()) : null;
+ for (const attr of Array.from(toElement.attributes)) {
+ if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && !hasAttribute(fromElement.attributes, attr.name)) {
+ toElement.removeAttribute(attr.name);
+ }
+ }
+ if (!fromElement.attributes) {
+ return;
+ }
+ for (const attr of Array.from(fromElement.attributes)) {
+ if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && toElement.getAttribute(attr.name) !== attr.value) {
+ toElement.setAttribute(attr.name, attr.value);
+ }
+ }
+}
+function getNodeForNamedElement(sourceElement, translatedChild) {
+ const childName = translatedChild.getAttribute("data-l10n-name");
+ const sourceChild = sourceElement.querySelector(`[data-l10n-name="${childName}"]`);
+ if (!sourceChild) {
+ console.warn(`An element named "${childName}" wasn't found in the source.`);
+ return createTextNodeFromTextContent(translatedChild);
+ }
+ if (sourceChild.localName !== translatedChild.localName) {
+ console.warn(`An element named "${childName}" was found in the translation ` + `but its type ${translatedChild.localName} didn't match the ` + `element found in the source (${sourceChild.localName}).`);
+ return createTextNodeFromTextContent(translatedChild);
+ }
+ sourceElement.removeChild(sourceChild);
+ const clone = sourceChild.cloneNode(false);
+ return shallowPopulateUsing(translatedChild, clone);
+}
+function createSanitizedElement(element) {
+ const clone = element.ownerDocument.createElement(element.localName);
+ return shallowPopulateUsing(element, clone);
+}
+function createTextNodeFromTextContent(element) {
+ return element.ownerDocument.createTextNode(element.textContent);
+}
+function isElementAllowed(element) {
+ const allowed = TEXT_LEVEL_ELEMENTS[element.namespaceURI];
+ return allowed && allowed.includes(element.localName);
+}
+function isAttrNameLocalizable(name, element, explicitlyAllowed = null) {
+ if (explicitlyAllowed && explicitlyAllowed.includes(name)) {
+ return true;
+ }
+ const allowed = LOCALIZABLE_ATTRIBUTES[element.namespaceURI];
+ if (!allowed) {
+ return false;
+ }
+ const attrName = name.toLowerCase();
+ const elemName = element.localName;
+ if (allowed.global.includes(attrName)) {
+ return true;
+ }
+ if (!allowed[elemName]) {
+ return false;
+ }
+ if (allowed[elemName].includes(attrName)) {
+ return true;
+ }
+ if (element.namespaceURI === "http://www.w3.org/1999/xhtml" && elemName === "input" && attrName === "value") {
+ const type = element.type.toLowerCase();
+ if (type === "submit" || type === "button" || type === "reset") {
+ return true;
+ }
+ }
+ return false;
+}
+function shallowPopulateUsing(fromElement, toElement) {
+ toElement.textContent = fromElement.textContent;
+ overlayAttributes(fromElement, toElement);
+ return toElement;
+}
+;// CONCATENATED MODULE: ./node_modules/cached-iterable/src/cached_iterable.mjs
+class CachedIterable extends Array {
+ static from(iterable) {
+ if (iterable instanceof this) {
+ return iterable;
+ }
+ return new this(iterable);
+ }
+}
+;// CONCATENATED MODULE: ./node_modules/cached-iterable/src/cached_sync_iterable.mjs
+
+class CachedSyncIterable extends CachedIterable {
+ constructor(iterable) {
+ super();
+ if (Symbol.iterator in Object(iterable)) {
+ this.iterator = iterable[Symbol.iterator]();
+ } else {
+ throw new TypeError("Argument must implement the iteration protocol.");
+ }
+ }
+ [Symbol.iterator]() {
+ const cached = this;
+ let cur = 0;
+ return {
+ next() {
+ if (cached.length <= cur) {
+ cached.push(cached.iterator.next());
+ }
+ return cached[cur++];
+ }
+ };
+ }
+ touchNext(count = 1) {
+ let idx = 0;
+ while (idx++ < count) {
+ const last = this[this.length - 1];
+ if (last && last.done) {
+ break;
+ }
+ this.push(this.iterator.next());
+ }
+ return this[this.length - 1];
+ }
+}
+;// CONCATENATED MODULE: ./node_modules/cached-iterable/src/cached_async_iterable.mjs
+
+class CachedAsyncIterable extends CachedIterable {
+ constructor(iterable) {
+ super();
+ if (Symbol.asyncIterator in Object(iterable)) {
+ this.iterator = iterable[Symbol.asyncIterator]();
+ } else if (Symbol.iterator in Object(iterable)) {
+ this.iterator = iterable[Symbol.iterator]();
+ } else {
+ throw new TypeError("Argument must implement the iteration protocol.");
+ }
+ }
+ [Symbol.asyncIterator]() {
+ const cached = this;
+ let cur = 0;
+ return {
+ async next() {
+ if (cached.length <= cur) {
+ cached.push(cached.iterator.next());
+ }
+ return cached[cur++];
+ }
+ };
+ }
+ async touchNext(count = 1) {
+ let idx = 0;
+ while (idx++ < count) {
+ const last = this[this.length - 1];
+ if (last && (await last).done) {
+ break;
+ }
+ this.push(this.iterator.next());
+ }
+ return this[this.length - 1];
+ }
+}
+;// CONCATENATED MODULE: ./node_modules/cached-iterable/src/index.mjs
+
+
+;// CONCATENATED MODULE: ./node_modules/@fluent/dom/esm/localization.js
+
+class Localization {
+ constructor(resourceIds = [], generateBundles) {
+ this.resourceIds = resourceIds;
+ this.generateBundles = generateBundles;
+ this.onChange(true);
+ }
+ addResourceIds(resourceIds, eager = false) {
+ this.resourceIds.push(...resourceIds);
+ this.onChange(eager);
+ return this.resourceIds.length;
+ }
+ removeResourceIds(resourceIds) {
+ this.resourceIds = this.resourceIds.filter(r => !resourceIds.includes(r));
+ this.onChange();
+ return this.resourceIds.length;
+ }
+ async formatWithFallback(keys, method) {
+ const translations = [];
+ let hasAtLeastOneBundle = false;
+ for await (const bundle of this.bundles) {
+ hasAtLeastOneBundle = true;
+ const missingIds = keysFromBundle(method, bundle, keys, translations);
+ if (missingIds.size === 0) {
+ break;
+ }
+ if (typeof console !== "undefined") {
+ const locale = bundle.locales[0];
+ const ids = Array.from(missingIds).join(", ");
+ console.warn(`[fluent] Missing translations in ${locale}: ${ids}`);
+ }
+ }
+ if (!hasAtLeastOneBundle && typeof console !== "undefined") {
+ console.warn(`[fluent] Request for keys failed because no resource bundles got generated.
+ keys: ${JSON.stringify(keys)}.
+ resourceIds: ${JSON.stringify(this.resourceIds)}.`);
+ }
+ return translations;
+ }
+ formatMessages(keys) {
+ return this.formatWithFallback(keys, messageFromBundle);
+ }
+ formatValues(keys) {
+ return this.formatWithFallback(keys, valueFromBundle);
+ }
+ async formatValue(id, args) {
+ const [val] = await this.formatValues([{
+ id,
+ args
+ }]);
+ return val;
+ }
+ handleEvent() {
+ this.onChange();
+ }
+ onChange(eager = false) {
+ this.bundles = CachedAsyncIterable.from(this.generateBundles(this.resourceIds));
+ if (eager) {
+ this.bundles.touchNext(2);
+ }
+ }
+}
+function valueFromBundle(bundle, errors, message, args) {
+ if (message.value) {
+ return bundle.formatPattern(message.value, args, errors);
+ }
+ return null;
+}
+function messageFromBundle(bundle, errors, message, args) {
+ const formatted = {
+ value: null,
+ attributes: null
+ };
+ if (message.value) {
+ formatted.value = bundle.formatPattern(message.value, args, errors);
+ }
+ let attrNames = Object.keys(message.attributes);
+ if (attrNames.length > 0) {
+ formatted.attributes = new Array(attrNames.length);
+ for (let [i, name] of attrNames.entries()) {
+ let value = bundle.formatPattern(message.attributes[name], args, errors);
+ formatted.attributes[i] = {
+ name,
+ value
+ };
+ }
+ }
+ return formatted;
+}
+function keysFromBundle(method, bundle, keys, translations) {
+ const messageErrors = [];
+ const missingIds = new Set();
+ keys.forEach(({
+ id,
+ args
+ }, i) => {
+ if (translations[i] !== undefined) {
+ return;
+ }
+ let message = bundle.getMessage(id);
+ if (message) {
+ messageErrors.length = 0;
+ translations[i] = method(bundle, messageErrors, message, args);
+ if (messageErrors.length > 0 && typeof console !== "undefined") {
+ const locale = bundle.locales[0];
+ const errors = messageErrors.join(", ");
+ console.warn(`[fluent][resolver] errors in ${locale}/${id}: ${errors}.`);
+ }
+ } else {
+ missingIds.add(id);
+ }
+ });
+ return missingIds;
+}
+;// CONCATENATED MODULE: ./node_modules/@fluent/dom/esm/dom_localization.js
+
+
+const L10NID_ATTR_NAME = "data-l10n-id";
+const L10NARGS_ATTR_NAME = "data-l10n-args";
+const L10N_ELEMENT_QUERY = `[${L10NID_ATTR_NAME}]`;
+class DOMLocalization extends Localization {
+ constructor(resourceIds, generateBundles) {
+ super(resourceIds, generateBundles);
+ this.roots = new Set();
+ this.pendingrAF = null;
+ this.pendingElements = new Set();
+ this.windowElement = null;
+ this.mutationObserver = null;
+ this.observerConfig = {
+ attributes: true,
+ characterData: false,
+ childList: true,
+ subtree: true,
+ attributeFilter: [L10NID_ATTR_NAME, L10NARGS_ATTR_NAME]
+ };
+ }
+ onChange(eager = false) {
+ super.onChange(eager);
+ if (this.roots) {
+ this.translateRoots();
+ }
+ }
+ setAttributes(element, id, args) {
+ element.setAttribute(L10NID_ATTR_NAME, id);
+ if (args) {
+ element.setAttribute(L10NARGS_ATTR_NAME, JSON.stringify(args));
+ } else {
+ element.removeAttribute(L10NARGS_ATTR_NAME);
+ }
+ return element;
+ }
+ getAttributes(element) {
+ return {
+ id: element.getAttribute(L10NID_ATTR_NAME),
+ args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null)
+ };
+ }
+ connectRoot(newRoot) {
+ for (const root of this.roots) {
+ if (root === newRoot || root.contains(newRoot) || newRoot.contains(root)) {
+ throw new Error("Cannot add a root that overlaps with existing root.");
+ }
+ }
+ if (this.windowElement) {
+ if (this.windowElement !== newRoot.ownerDocument.defaultView) {
+ throw new Error(`Cannot connect a root:
+ DOMLocalization already has a root from a different window.`);
+ }
+ } else {
+ this.windowElement = newRoot.ownerDocument.defaultView;
+ this.mutationObserver = new this.windowElement.MutationObserver(mutations => this.translateMutations(mutations));
+ }
+ this.roots.add(newRoot);
+ this.mutationObserver.observe(newRoot, this.observerConfig);
+ }
+ disconnectRoot(root) {
+ this.roots.delete(root);
+ this.pauseObserving();
+ if (this.roots.size === 0) {
+ this.mutationObserver = null;
+ this.windowElement = null;
+ this.pendingrAF = null;
+ this.pendingElements.clear();
+ return true;
+ }
+ this.resumeObserving();
+ return false;
+ }
+ translateRoots() {
+ const roots = Array.from(this.roots);
+ return Promise.all(roots.map(root => this.translateFragment(root)));
+ }
+ pauseObserving() {
+ if (!this.mutationObserver) {
+ return;
+ }
+ this.translateMutations(this.mutationObserver.takeRecords());
+ this.mutationObserver.disconnect();
+ }
+ resumeObserving() {
+ if (!this.mutationObserver) {
+ return;
+ }
+ for (const root of this.roots) {
+ this.mutationObserver.observe(root, this.observerConfig);
+ }
+ }
+ translateMutations(mutations) {
+ for (const mutation of mutations) {
+ switch (mutation.type) {
+ case "attributes":
+ if (mutation.target.hasAttribute("data-l10n-id")) {
+ this.pendingElements.add(mutation.target);
+ }
+ break;
+ case "childList":
+ for (const addedNode of mutation.addedNodes) {
+ if (addedNode.nodeType === addedNode.ELEMENT_NODE) {
+ if (addedNode.childElementCount) {
+ for (const element of this.getTranslatables(addedNode)) {
+ this.pendingElements.add(element);
+ }
+ } else if (addedNode.hasAttribute(L10NID_ATTR_NAME)) {
+ this.pendingElements.add(addedNode);
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (this.pendingElements.size > 0) {
+ if (this.pendingrAF === null) {
+ this.pendingrAF = this.windowElement.requestAnimationFrame(() => {
+ this.translateElements(Array.from(this.pendingElements));
+ this.pendingElements.clear();
+ this.pendingrAF = null;
+ });
+ }
+ }
+ }
+ translateFragment(frag) {
+ return this.translateElements(this.getTranslatables(frag));
+ }
+ async translateElements(elements) {
+ if (!elements.length) {
+ return undefined;
+ }
+ const keys = elements.map(this.getKeysForElement);
+ const translations = await this.formatMessages(keys);
+ return this.applyTranslations(elements, translations);
+ }
+ applyTranslations(elements, translations) {
+ this.pauseObserving();
+ for (let i = 0; i < elements.length; i++) {
+ if (translations[i] !== undefined) {
+ translateElement(elements[i], translations[i]);
+ }
+ }
+ this.resumeObserving();
+ }
+ getTranslatables(element) {
+ const nodes = Array.from(element.querySelectorAll(L10N_ELEMENT_QUERY));
+ if (typeof element.hasAttribute === "function" && element.hasAttribute(L10NID_ATTR_NAME)) {
+ nodes.push(element);
+ }
+ return nodes;
+ }
+ getKeysForElement(element) {
+ return {
+ id: element.getAttribute(L10NID_ATTR_NAME),
+ args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null)
+ };
+ }
+}
+;// CONCATENATED MODULE: ./node_modules/@fluent/dom/esm/index.js
+
+
+;// CONCATENATED MODULE: ./web/l10n.js
+class L10n {
+ #dir;
+ #lang;
+ #l10n;
+ constructor({
+ lang,
+ isRTL
+ }, l10n = null) {
+ this.#lang = L10n.#fixupLangCode(lang);
+ this.#l10n = l10n;
+ this.#dir = isRTL ?? L10n.#isRTL(this.#lang) ? "rtl" : "ltr";
+ }
+ _setL10n(l10n) {
+ this.#l10n = l10n;
+ }
+ getLanguage() {
+ return this.#lang;
+ }
+ getDirection() {
+ return this.#dir;
+ }
+ async get(ids, args = null, fallback) {
+ if (Array.isArray(ids)) {
+ ids = ids.map(id => ({
+ id
+ }));
+ const messages = await this.#l10n.formatMessages(ids);
+ return messages.map(message => message.value);
+ }
+ const messages = await this.#l10n.formatMessages([{
+ id: ids,
+ args
+ }]);
+ return messages?.[0].value || fallback;
+ }
+ async translate(element) {
+ try {
+ this.#l10n.connectRoot(element);
+ await this.#l10n.translateRoots();
+ } catch {}
+ }
+ pause() {
+ this.#l10n.pauseObserving();
+ }
+ resume() {
+ this.#l10n.resumeObserving();
+ }
+ static #fixupLangCode(langCode) {
+ langCode = langCode?.toLowerCase() || "en-us";
+ const PARTIAL_LANG_CODES = {
+ en: "en-us",
+ es: "es-es",
+ fy: "fy-nl",
+ ga: "ga-ie",
+ gu: "gu-in",
+ hi: "hi-in",
+ hy: "hy-am",
+ nb: "nb-no",
+ ne: "ne-np",
+ nn: "nn-no",
+ pa: "pa-in",
+ pt: "pt-pt",
+ sv: "sv-se",
+ zh: "zh-cn"
+ };
+ return PARTIAL_LANG_CODES[langCode] || langCode;
+ }
+ static #isRTL(lang) {
+ const shortCode = lang.split("-", 1)[0];
+ return ["ar", "he", "fa", "ps", "ur"].includes(shortCode);
+ }
+}
+const GenericL10n = null;
+
+;// CONCATENATED MODULE: ./web/genericl10n.js
+
+
+
+
+function createBundle(lang, text) {
+ const resource = new FluentResource(text);
+ const bundle = new FluentBundle(lang);
+ const errors = bundle.addResource(resource);
+ if (errors.length) {
+ console.error("L10n errors", errors);
+ }
+ return bundle;
+}
+class genericl10n_GenericL10n extends L10n {
+ constructor(lang) {
+ super({
+ lang
+ });
+ const generateBundles = !lang ? genericl10n_GenericL10n.#generateBundlesFallback.bind(genericl10n_GenericL10n, this.getLanguage()) : genericl10n_GenericL10n.#generateBundles.bind(genericl10n_GenericL10n, "en-us", this.getLanguage());
+ this._setL10n(new DOMLocalization([], generateBundles));
+ }
+ static async *#generateBundles(defaultLang, baseLang) {
+ const {
+ baseURL,
+ paths
+ } = await this.#getPaths();
+ const langs = [baseLang];
+ if (defaultLang !== baseLang) {
+ const shortLang = baseLang.split("-", 1)[0];
+ if (shortLang !== baseLang) {
+ langs.push(shortLang);
+ }
+ langs.push(defaultLang);
+ }
+ for (const lang of langs) {
+ const bundle = await this.#createBundle(lang, baseURL, paths);
+ if (bundle) {
+ yield bundle;
+ }
+ if (lang === "en-us") {
+ yield this.#createBundleFallback(lang);
+ }
+ }
+ }
+ static async #createBundle(lang, baseURL, paths) {
+ const path = paths[lang];
+ if (!path) {
+ return null;
+ }
+ const url = new URL(path, baseURL);
+ const text = await fetchData(url, "text");
+ return createBundle(lang, text);
+ }
+ static async #getPaths() {
+ try {
+ const {
+ href
+ } = document.querySelector(`link[type="application/l10n"]`);
+ const paths = await fetchData(href, "json");
+ return {
+ baseURL: href.replace(/[^/]*$/, "") || "./",
+ paths
+ };
+ } catch {}
+ return {
+ baseURL: "./",
+ paths: Object.create(null)
+ };
+ }
+ static async *#generateBundlesFallback(lang) {
+ yield this.#createBundleFallback(lang);
+ }
+ static async #createBundleFallback(lang) {
+ const text = "pdfjs-previous-button =\n .title = Previous Page\npdfjs-previous-button-label = Previous\npdfjs-next-button =\n .title = Next Page\npdfjs-next-button-label = Next\npdfjs-page-input =\n .title = Page\npdfjs-of-pages = of { $pagesCount }\npdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })\npdfjs-zoom-out-button =\n .title = Zoom Out\npdfjs-zoom-out-button-label = Zoom Out\npdfjs-zoom-in-button =\n .title = Zoom In\npdfjs-zoom-in-button-label = Zoom In\npdfjs-zoom-select =\n .title = Zoom\npdfjs-presentation-mode-button =\n .title = Switch to Presentation Mode\npdfjs-presentation-mode-button-label = Presentation Mode\npdfjs-open-file-button =\n .title = Open File\npdfjs-open-file-button-label = Open\npdfjs-print-button =\n .title = Print\npdfjs-print-button-label = Print\npdfjs-save-button =\n .title = Save\npdfjs-save-button-label = Save\npdfjs-download-button =\n .title = Download\npdfjs-download-button-label = Download\npdfjs-bookmark-button =\n .title = Current Page (View URL from Current Page)\npdfjs-bookmark-button-label = Current Page\npdfjs-tools-button =\n .title = Tools\npdfjs-tools-button-label = Tools\npdfjs-first-page-button =\n .title = Go to First Page\npdfjs-first-page-button-label = Go to First Page\npdfjs-last-page-button =\n .title = Go to Last Page\npdfjs-last-page-button-label = Go to Last Page\npdfjs-page-rotate-cw-button =\n .title = Rotate Clockwise\npdfjs-page-rotate-cw-button-label = Rotate Clockwise\npdfjs-page-rotate-ccw-button =\n .title = Rotate Counterclockwise\npdfjs-page-rotate-ccw-button-label = Rotate Counterclockwise\npdfjs-cursor-text-select-tool-button =\n .title = Enable Text Selection Tool\npdfjs-cursor-text-select-tool-button-label = Text Selection Tool\npdfjs-cursor-hand-tool-button =\n .title = Enable Hand Tool\npdfjs-cursor-hand-tool-button-label = Hand Tool\npdfjs-scroll-page-button =\n .title = Use Page Scrolling\npdfjs-scroll-page-button-label = Page Scrolling\npdfjs-scroll-vertical-button =\n .title = Use Vertical Scrolling\npdfjs-scroll-vertical-button-label = Vertical Scrolling\npdfjs-scroll-horizontal-button =\n .title = Use Horizontal Scrolling\npdfjs-scroll-horizontal-button-label = Horizontal Scrolling\npdfjs-scroll-wrapped-button =\n .title = Use Wrapped Scrolling\npdfjs-scroll-wrapped-button-label = Wrapped Scrolling\npdfjs-spread-none-button =\n .title = Do not join page spreads\npdfjs-spread-none-button-label = No Spreads\npdfjs-spread-odd-button =\n .title = Join page spreads starting with odd-numbered pages\npdfjs-spread-odd-button-label = Odd Spreads\npdfjs-spread-even-button =\n .title = Join page spreads starting with even-numbered pages\npdfjs-spread-even-button-label = Even Spreads\npdfjs-document-properties-button =\n .title = Document Properties\u2026\npdfjs-document-properties-button-label = Document Properties\u2026\npdfjs-document-properties-file-name = File name:\npdfjs-document-properties-file-size = File size:\npdfjs-document-properties-kb = { $size_kb } KB ({ $size_b } bytes)\npdfjs-document-properties-mb = { $size_mb } MB ({ $size_b } bytes)\npdfjs-document-properties-title = Title:\npdfjs-document-properties-author = Author:\npdfjs-document-properties-subject = Subject:\npdfjs-document-properties-keywords = Keywords:\npdfjs-document-properties-creation-date = Creation Date:\npdfjs-document-properties-modification-date = Modification Date:\npdfjs-document-properties-date-string = { $date }, { $time }\npdfjs-document-properties-creator = Creator:\npdfjs-document-properties-producer = PDF Producer:\npdfjs-document-properties-version = PDF Version:\npdfjs-document-properties-page-count = Page Count:\npdfjs-document-properties-page-size = Page Size:\npdfjs-document-properties-page-size-unit-inches = in\npdfjs-document-properties-page-size-unit-millimeters = mm\npdfjs-document-properties-page-size-orientation-portrait = portrait\npdfjs-document-properties-page-size-orientation-landscape = landscape\npdfjs-document-properties-page-size-name-a-three = A3\npdfjs-document-properties-page-size-name-a-four = A4\npdfjs-document-properties-page-size-name-letter = Letter\npdfjs-document-properties-page-size-name-legal = Legal\npdfjs-document-properties-page-size-dimension-string = { $width } \xD7 { $height } { $unit } ({ $orientation })\npdfjs-document-properties-page-size-dimension-name-string = { $width } \xD7 { $height } { $unit } ({ $name }, { $orientation })\npdfjs-document-properties-linearized = Fast Web View:\npdfjs-document-properties-linearized-yes = Yes\npdfjs-document-properties-linearized-no = No\npdfjs-document-properties-close-button = Close\npdfjs-print-progress-message = Preparing document for printing\u2026\npdfjs-print-progress-percent = { $progress }%\npdfjs-print-progress-close-button = Cancel\npdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser.\npdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing.\npdfjs-toggle-sidebar-button =\n .title = Toggle Sidebar\npdfjs-toggle-sidebar-notification-button =\n .title = Toggle Sidebar (document contains outline/attachments/layers)\npdfjs-toggle-sidebar-button-label = Toggle Sidebar\npdfjs-document-outline-button =\n .title = Show Document Outline (double-click to expand/collapse all items)\npdfjs-document-outline-button-label = Document Outline\npdfjs-attachments-button =\n .title = Show Attachments\npdfjs-attachments-button-label = Attachments\npdfjs-layers-button =\n .title = Show Layers (double-click to reset all layers to the default state)\npdfjs-layers-button-label = Layers\npdfjs-thumbs-button =\n .title = Show Thumbnails\npdfjs-thumbs-button-label = Thumbnails\npdfjs-current-outline-item-button =\n .title = Find Current Outline Item\npdfjs-current-outline-item-button-label = Current Outline Item\npdfjs-findbar-button =\n .title = Find in Document\npdfjs-findbar-button-label = Find\npdfjs-additional-layers = Additional Layers\npdfjs-thumb-page-title =\n .title = Page { $page }\npdfjs-thumb-page-canvas =\n .aria-label = Thumbnail of Page { $page }\npdfjs-find-input =\n .title = Find\n .placeholder = Find in document\u2026\npdfjs-find-previous-button =\n .title = Find the previous occurrence of the phrase\npdfjs-find-previous-button-label = Previous\npdfjs-find-next-button =\n .title = Find the next occurrence of the phrase\npdfjs-find-next-button-label = Next\npdfjs-find-highlight-checkbox = Highlight All\npdfjs-find-match-case-checkbox-label = Match Case\npdfjs-find-match-diacritics-checkbox-label = Match Diacritics\npdfjs-find-entire-word-checkbox-label = Whole Words\npdfjs-find-reached-top = Reached top of document, continued from bottom\npdfjs-find-reached-bottom = Reached end of document, continued from top\npdfjs-find-match-count =\n { $total ->\n [one] { $current } of { $total } match\n *[other] { $current } of { $total } matches\n }\npdfjs-find-match-count-limit =\n { $limit ->\n [one] More than { $limit } match\n *[other] More than { $limit } matches\n }\npdfjs-find-not-found = Phrase not found\npdfjs-page-scale-width = Page Width\npdfjs-page-scale-fit = Page Fit\npdfjs-page-scale-auto = Automatic Zoom\npdfjs-page-scale-actual = Actual Size\npdfjs-page-scale-percent = { $scale }%\npdfjs-page-landmark =\n .aria-label = Page { $page }\npdfjs-loading-error = An error occurred while loading the PDF.\npdfjs-invalid-file-error = Invalid or corrupted PDF file.\npdfjs-missing-file-error = Missing PDF file.\npdfjs-unexpected-response-error = Unexpected server response.\npdfjs-rendering-error = An error occurred while rendering the page.\npdfjs-annotation-date-string = { $date }, { $time }\npdfjs-text-annotation-type =\n .alt = [{ $type } Annotation]\npdfjs-password-label = Enter the password to open this PDF file.\npdfjs-password-invalid = Invalid password. Please try again.\npdfjs-password-ok-button = OK\npdfjs-password-cancel-button = Cancel\npdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts.\npdfjs-editor-free-text-button =\n .title = Text\npdfjs-editor-free-text-button-label = Text\npdfjs-editor-ink-button =\n .title = Draw\npdfjs-editor-ink-button-label = Draw\npdfjs-editor-stamp-button =\n .title = Add or edit images\npdfjs-editor-stamp-button-label = Add or edit images\npdfjs-editor-highlight-button =\n .title = Highlight\npdfjs-editor-highlight-button-label = Highlight\npdfjs-highlight-floating-button1 =\n .title = Highlight\n .aria-label = Highlight\npdfjs-highlight-floating-button-label = Highlight\npdfjs-editor-remove-ink-button =\n .title = Remove drawing\npdfjs-editor-remove-freetext-button =\n .title = Remove text\npdfjs-editor-remove-stamp-button =\n .title = Remove image\npdfjs-editor-remove-highlight-button =\n .title = Remove highlight\npdfjs-editor-free-text-color-input = Color\npdfjs-editor-free-text-size-input = Size\npdfjs-editor-ink-color-input = Color\npdfjs-editor-ink-thickness-input = Thickness\npdfjs-editor-ink-opacity-input = Opacity\npdfjs-editor-stamp-add-image-button =\n .title = Add image\npdfjs-editor-stamp-add-image-button-label = Add image\npdfjs-editor-free-highlight-thickness-input = Thickness\npdfjs-editor-free-highlight-thickness-title =\n .title = Change thickness when highlighting items other than text\npdfjs-free-text =\n .aria-label = Text Editor\npdfjs-free-text-default-content = Start typing\u2026\npdfjs-ink =\n .aria-label = Draw Editor\npdfjs-ink-canvas =\n .aria-label = User-created image\npdfjs-editor-alt-text-button-label = Alt text\npdfjs-editor-alt-text-edit-button-label = Edit alt text\npdfjs-editor-alt-text-dialog-label = Choose an option\npdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can\u2019t see the image or when it doesn\u2019t load.\npdfjs-editor-alt-text-add-description-label = Add a description\npdfjs-editor-alt-text-add-description-description = Aim for 1-2 sentences that describe the subject, setting, or actions.\npdfjs-editor-alt-text-mark-decorative-label = Mark as decorative\npdfjs-editor-alt-text-mark-decorative-description = This is used for ornamental images, like borders or watermarks.\npdfjs-editor-alt-text-cancel-button = Cancel\npdfjs-editor-alt-text-save-button = Save\npdfjs-editor-alt-text-decorative-tooltip = Marked as decorative\npdfjs-editor-alt-text-textarea =\n .placeholder = For example, \u201CA young man sits down at a table to eat a meal\u201D\npdfjs-editor-resizer-label-top-left = Top left corner \u2014 resize\npdfjs-editor-resizer-label-top-middle = Top middle \u2014 resize\npdfjs-editor-resizer-label-top-right = Top right corner \u2014 resize\npdfjs-editor-resizer-label-middle-right = Middle right \u2014 resize\npdfjs-editor-resizer-label-bottom-right = Bottom right corner \u2014 resize\npdfjs-editor-resizer-label-bottom-middle = Bottom middle \u2014 resize\npdfjs-editor-resizer-label-bottom-left = Bottom left corner \u2014 resize\npdfjs-editor-resizer-label-middle-left = Middle left \u2014 resize\npdfjs-editor-highlight-colorpicker-label = Highlight color\npdfjs-editor-colorpicker-button =\n .title = Change color\npdfjs-editor-colorpicker-dropdown =\n .aria-label = Color choices\npdfjs-editor-colorpicker-yellow =\n .title = Yellow\npdfjs-editor-colorpicker-green =\n .title = Green\npdfjs-editor-colorpicker-blue =\n .title = Blue\npdfjs-editor-colorpicker-pink =\n .title = Pink\npdfjs-editor-colorpicker-red =\n .title = Red\npdfjs-editor-highlight-show-all-button-label = Show all\npdfjs-editor-highlight-show-all-button =\n .title = Show all";
+ return createBundle(lang, text);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/generic_scripting.js
+
+async function docProperties(pdfDocument) {
+ const url = "",
+ baseUrl = url.split("#", 1)[0];
+ let {
+ info,
+ metadata,
+ contentDispositionFilename,
+ contentLength
+ } = await pdfDocument.getMetadata();
+ if (!contentLength) {
+ const {
+ length
+ } = await pdfDocument.getDownloadInfo();
+ contentLength = length;
+ }
+ return {
+ ...info,
+ baseURL: baseUrl,
+ filesize: contentLength,
+ filename: contentDispositionFilename || getPdfFilenameFromUrl(url),
+ metadata: metadata?.getRaw(),
+ authors: metadata?.get("dc:creator"),
+ numPages: pdfDocument.numPages,
+ URL: url
+ };
+}
+class GenericScripting {
+ constructor(sandboxBundleSrc) {
+ this._ready = new Promise((resolve, reject) => {
+ const sandbox = import( /*webpackIgnore: true*/sandboxBundleSrc);
+ sandbox.then(pdfjsSandbox => {
+ resolve(pdfjsSandbox.QuickJSSandbox());
+ }).catch(reject);
+ });
+ }
+ async createSandbox(data) {
+ const sandbox = await this._ready;
+ sandbox.create(data);
+ }
+ async dispatchEventInSandbox(event) {
+ const sandbox = await this._ready;
+ setTimeout(() => sandbox.dispatchEvent(event), 0);
+ }
+ async destroySandbox() {
+ const sandbox = await this._ready;
+ sandbox.nukeSandbox();
+ }
+}
+
+;// CONCATENATED MODULE: ./web/genericcom.js
+
+
+
+
+
+function initCom(app) {}
+class Preferences extends BasePreferences {
+ async _writeToStorage(prefObj) {
+ localStorage.setItem("pdfjs.preferences", JSON.stringify(prefObj));
+ }
+ async _readFromStorage(prefObj) {
+ return {
+ prefs: JSON.parse(localStorage.getItem("pdfjs.preferences"))
+ };
+ }
+}
+class ExternalServices extends BaseExternalServices {
+ async createL10n() {
+ return new genericl10n_GenericL10n(AppOptions.get("locale"));
+ }
+ createScripting() {
+ return new GenericScripting(AppOptions.get("sandboxBundleSrc"));
+ }
+}
+class MLManager {
+ async guess() {
+ return null;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/alt_text_manager.js
+
+class AltTextManager {
+ #boundUpdateUIState = this.#updateUIState.bind(this);
+ #boundSetPosition = this.#setPosition.bind(this);
+ #boundOnClick = this.#onClick.bind(this);
+ #currentEditor = null;
+ #cancelButton;
+ #dialog;
+ #eventBus;
+ #hasUsedPointer = false;
+ #optionDescription;
+ #optionDecorative;
+ #overlayManager;
+ #saveButton;
+ #textarea;
+ #uiManager;
+ #previousAltText = null;
+ #svgElement = null;
+ #rectElement = null;
+ #container;
+ #telemetryData = null;
+ constructor({
+ dialog,
+ optionDescription,
+ optionDecorative,
+ textarea,
+ cancelButton,
+ saveButton
+ }, container, overlayManager, eventBus) {
+ this.#dialog = dialog;
+ this.#optionDescription = optionDescription;
+ this.#optionDecorative = optionDecorative;
+ this.#textarea = textarea;
+ this.#cancelButton = cancelButton;
+ this.#saveButton = saveButton;
+ this.#overlayManager = overlayManager;
+ this.#eventBus = eventBus;
+ this.#container = container;
+ dialog.addEventListener("close", this.#close.bind(this));
+ dialog.addEventListener("contextmenu", event => {
+ if (event.target !== this.#textarea) {
+ event.preventDefault();
+ }
+ });
+ cancelButton.addEventListener("click", this.#finish.bind(this));
+ saveButton.addEventListener("click", this.#save.bind(this));
+ optionDescription.addEventListener("change", this.#boundUpdateUIState);
+ optionDecorative.addEventListener("change", this.#boundUpdateUIState);
+ this.#overlayManager.register(dialog);
+ }
+ get _elements() {
+ return shadow(this, "_elements", [this.#optionDescription, this.#optionDecorative, this.#textarea, this.#saveButton, this.#cancelButton]);
+ }
+ #createSVGElement() {
+ if (this.#svgElement) {
+ return;
+ }
+ const svgFactory = new DOMSVGFactory();
+ const svg = this.#svgElement = svgFactory.createElement("svg");
+ svg.setAttribute("width", "0");
+ svg.setAttribute("height", "0");
+ const defs = svgFactory.createElement("defs");
+ svg.append(defs);
+ const mask = svgFactory.createElement("mask");
+ defs.append(mask);
+ mask.setAttribute("id", "alttext-manager-mask");
+ mask.setAttribute("maskContentUnits", "objectBoundingBox");
+ let rect = svgFactory.createElement("rect");
+ mask.append(rect);
+ rect.setAttribute("fill", "white");
+ rect.setAttribute("width", "1");
+ rect.setAttribute("height", "1");
+ rect.setAttribute("x", "0");
+ rect.setAttribute("y", "0");
+ rect = this.#rectElement = svgFactory.createElement("rect");
+ mask.append(rect);
+ rect.setAttribute("fill", "black");
+ this.#dialog.append(svg);
+ }
+ async editAltText(uiManager, editor) {
+ if (this.#currentEditor || !editor) {
+ return;
+ }
+ this.#createSVGElement();
+ this.#hasUsedPointer = false;
+ for (const element of this._elements) {
+ element.addEventListener("click", this.#boundOnClick);
+ }
+ const {
+ altText,
+ decorative
+ } = editor.altTextData;
+ if (decorative === true) {
+ this.#optionDecorative.checked = true;
+ this.#optionDescription.checked = false;
+ } else {
+ this.#optionDecorative.checked = false;
+ this.#optionDescription.checked = true;
+ }
+ this.#previousAltText = this.#textarea.value = altText?.trim() || "";
+ this.#updateUIState();
+ this.#currentEditor = editor;
+ this.#uiManager = uiManager;
+ this.#uiManager.removeEditListeners();
+ this.#eventBus._on("resize", this.#boundSetPosition);
+ try {
+ await this.#overlayManager.open(this.#dialog);
+ this.#setPosition();
+ } catch (ex) {
+ this.#close();
+ throw ex;
+ }
+ }
+ #setPosition() {
+ if (!this.#currentEditor) {
+ return;
+ }
+ const dialog = this.#dialog;
+ const {
+ style
+ } = dialog;
+ const {
+ x: containerX,
+ y: containerY,
+ width: containerW,
+ height: containerH
+ } = this.#container.getBoundingClientRect();
+ const {
+ innerWidth: windowW,
+ innerHeight: windowH
+ } = window;
+ const {
+ width: dialogW,
+ height: dialogH
+ } = dialog.getBoundingClientRect();
+ const {
+ x,
+ y,
+ width,
+ height
+ } = this.#currentEditor.getClientDimensions();
+ const MARGIN = 10;
+ const isLTR = this.#uiManager.direction === "ltr";
+ const xs = Math.max(x, containerX);
+ const xe = Math.min(x + width, containerX + containerW);
+ const ys = Math.max(y, containerY);
+ const ye = Math.min(y + height, containerY + containerH);
+ this.#rectElement.setAttribute("width", `${(xe - xs) / windowW}`);
+ this.#rectElement.setAttribute("height", `${(ye - ys) / windowH}`);
+ this.#rectElement.setAttribute("x", `${xs / windowW}`);
+ this.#rectElement.setAttribute("y", `${ys / windowH}`);
+ let left = null;
+ let top = Math.max(y, 0);
+ top += Math.min(windowH - (top + dialogH), 0);
+ if (isLTR) {
+ if (x + width + MARGIN + dialogW < windowW) {
+ left = x + width + MARGIN;
+ } else if (x > dialogW + MARGIN) {
+ left = x - dialogW - MARGIN;
+ }
+ } else if (x > dialogW + MARGIN) {
+ left = x - dialogW - MARGIN;
+ } else if (x + width + MARGIN + dialogW < windowW) {
+ left = x + width + MARGIN;
+ }
+ if (left === null) {
+ top = null;
+ left = Math.max(x, 0);
+ left += Math.min(windowW - (left + dialogW), 0);
+ if (y > dialogH + MARGIN) {
+ top = y - dialogH - MARGIN;
+ } else if (y + height + MARGIN + dialogH < windowH) {
+ top = y + height + MARGIN;
+ }
+ }
+ if (top !== null) {
+ dialog.classList.add("positioned");
+ if (isLTR) {
+ style.left = `${left}px`;
+ } else {
+ style.right = `${windowW - left - dialogW}px`;
+ }
+ style.top = `${top}px`;
+ } else {
+ dialog.classList.remove("positioned");
+ style.left = "";
+ style.top = "";
+ }
+ }
+ #finish() {
+ if (this.#overlayManager.active === this.#dialog) {
+ this.#overlayManager.close(this.#dialog);
+ }
+ }
+ #close() {
+ this.#currentEditor._reportTelemetry(this.#telemetryData || {
+ action: "alt_text_cancel",
+ alt_text_keyboard: !this.#hasUsedPointer
+ });
+ this.#telemetryData = null;
+ this.#removeOnClickListeners();
+ this.#uiManager?.addEditListeners();
+ this.#eventBus._off("resize", this.#boundSetPosition);
+ this.#currentEditor.altTextFinish();
+ this.#currentEditor = null;
+ this.#uiManager = null;
+ }
+ #updateUIState() {
+ this.#textarea.disabled = this.#optionDecorative.checked;
+ }
+ #save() {
+ const altText = this.#textarea.value.trim();
+ const decorative = this.#optionDecorative.checked;
+ this.#currentEditor.altTextData = {
+ altText,
+ decorative
+ };
+ this.#telemetryData = {
+ action: "alt_text_save",
+ alt_text_description: !!altText,
+ alt_text_edit: !!this.#previousAltText && this.#previousAltText !== altText,
+ alt_text_decorative: decorative,
+ alt_text_keyboard: !this.#hasUsedPointer
+ };
+ this.#finish();
+ }
+ #onClick(evt) {
+ if (evt.detail === 0) {
+ return;
+ }
+ this.#hasUsedPointer = true;
+ this.#removeOnClickListeners();
+ }
+ #removeOnClickListeners() {
+ for (const element of this._elements) {
+ element.removeEventListener("click", this.#boundOnClick);
+ }
+ }
+ destroy() {
+ this.#uiManager = null;
+ this.#finish();
+ this.#svgElement?.remove();
+ this.#svgElement = this.#rectElement = null;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/annotation_editor_params.js
+
+class AnnotationEditorParams {
+ constructor(options, eventBus) {
+ this.eventBus = eventBus;
+ this.#bindListeners(options);
+ }
+ #bindListeners({
+ editorFreeTextFontSize,
+ editorFreeTextColor,
+ editorInkColor,
+ editorInkThickness,
+ editorInkOpacity,
+ editorStampAddImage,
+ editorFreeHighlightThickness,
+ editorHighlightShowAll
+ }) {
+ const dispatchEvent = (typeStr, value) => {
+ this.eventBus.dispatch("switchannotationeditorparams", {
+ source: this,
+ type: AnnotationEditorParamsType[typeStr],
+ value
+ });
+ };
+ editorFreeTextFontSize.addEventListener("input", function () {
+ dispatchEvent("FREETEXT_SIZE", this.valueAsNumber);
+ });
+ editorFreeTextColor.addEventListener("input", function () {
+ dispatchEvent("FREETEXT_COLOR", this.value);
+ });
+ editorInkColor.addEventListener("input", function () {
+ dispatchEvent("INK_COLOR", this.value);
+ });
+ editorInkThickness.addEventListener("input", function () {
+ dispatchEvent("INK_THICKNESS", this.valueAsNumber);
+ });
+ editorInkOpacity.addEventListener("input", function () {
+ dispatchEvent("INK_OPACITY", this.valueAsNumber);
+ });
+ editorStampAddImage.addEventListener("click", () => {
+ dispatchEvent("CREATE");
+ });
+ editorFreeHighlightThickness.addEventListener("input", function () {
+ dispatchEvent("HIGHLIGHT_THICKNESS", this.valueAsNumber);
+ });
+ editorHighlightShowAll.addEventListener("click", function () {
+ const checked = this.getAttribute("aria-pressed") === "true";
+ this.setAttribute("aria-pressed", !checked);
+ dispatchEvent("HIGHLIGHT_SHOW_ALL", !checked);
+ });
+ this.eventBus._on("annotationeditorparamschanged", evt => {
+ for (const [type, value] of evt.details) {
+ switch (type) {
+ case AnnotationEditorParamsType.FREETEXT_SIZE:
+ editorFreeTextFontSize.value = value;
+ break;
+ case AnnotationEditorParamsType.FREETEXT_COLOR:
+ editorFreeTextColor.value = value;
+ break;
+ case AnnotationEditorParamsType.INK_COLOR:
+ editorInkColor.value = value;
+ break;
+ case AnnotationEditorParamsType.INK_THICKNESS:
+ editorInkThickness.value = value;
+ break;
+ case AnnotationEditorParamsType.INK_OPACITY:
+ editorInkOpacity.value = value;
+ break;
+ case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
+ editorFreeHighlightThickness.value = value;
+ break;
+ case AnnotationEditorParamsType.HIGHLIGHT_FREE:
+ editorFreeHighlightThickness.disabled = !value;
+ break;
+ case AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL:
+ editorHighlightShowAll.setAttribute("aria-pressed", value);
+ break;
+ }
+ }
+ });
+ }
+}
+
+;// CONCATENATED MODULE: ./web/caret_browsing.js
+const PRECISION = 1e-1;
+class CaretBrowsingMode {
+ #mainContainer;
+ #toolBarHeight;
+ #viewerContainer;
+ constructor(mainContainer, viewerContainer, toolbarContainer) {
+ this.#mainContainer = mainContainer;
+ this.#viewerContainer = viewerContainer;
+ this.#toolBarHeight = toolbarContainer?.getBoundingClientRect().height ?? 0;
+ }
+ #isOnSameLine(rect1, rect2) {
+ const top1 = rect1.y;
+ const bot1 = rect1.bottom;
+ const mid1 = rect1.y + rect1.height / 2;
+ const top2 = rect2.y;
+ const bot2 = rect2.bottom;
+ const mid2 = rect2.y + rect2.height / 2;
+ return top1 <= mid2 && mid2 <= bot1 || top2 <= mid1 && mid1 <= bot2;
+ }
+ #isUnderOver(rect, x, y, isUp) {
+ const midY = rect.y + rect.height / 2;
+ return (isUp ? y >= midY : y <= midY) && rect.x - PRECISION <= x && x <= rect.right + PRECISION;
+ }
+ #isVisible(rect) {
+ return rect.top >= this.#toolBarHeight && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
+ }
+ #getCaretPosition(selection, isUp) {
+ const {
+ focusNode,
+ focusOffset
+ } = selection;
+ const range = document.createRange();
+ range.setStart(focusNode, focusOffset);
+ range.setEnd(focusNode, focusOffset);
+ const rect = range.getBoundingClientRect();
+ return [rect.x, isUp ? rect.top : rect.bottom];
+ }
+ static #caretPositionFromPoint(x, y) {
+ if (!document.caretPositionFromPoint) {
+ const {
+ startContainer: offsetNode,
+ startOffset: offset
+ } = document.caretRangeFromPoint(x, y);
+ return {
+ offsetNode,
+ offset
+ };
+ }
+ return document.caretPositionFromPoint(x, y);
+ }
+ #setCaretPositionHelper(selection, caretX, select, element, rect) {
+ rect ||= element.getBoundingClientRect();
+ if (caretX <= rect.x + PRECISION) {
+ if (select) {
+ selection.extend(element.firstChild, 0);
+ } else {
+ selection.setPosition(element.firstChild, 0);
+ }
+ return;
+ }
+ if (rect.right - PRECISION <= caretX) {
+ const {
+ lastChild
+ } = element;
+ if (select) {
+ selection.extend(lastChild, lastChild.length);
+ } else {
+ selection.setPosition(lastChild, lastChild.length);
+ }
+ return;
+ }
+ const midY = rect.y + rect.height / 2;
+ let caretPosition = CaretBrowsingMode.#caretPositionFromPoint(caretX, midY);
+ let parentElement = caretPosition.offsetNode?.parentElement;
+ if (parentElement && parentElement !== element) {
+ const elementsAtPoint = document.elementsFromPoint(caretX, midY);
+ const savedVisibilities = [];
+ for (const el of elementsAtPoint) {
+ if (el === element) {
+ break;
+ }
+ const {
+ style
+ } = el;
+ savedVisibilities.push([el, style.visibility]);
+ style.visibility = "hidden";
+ }
+ caretPosition = CaretBrowsingMode.#caretPositionFromPoint(caretX, midY);
+ parentElement = caretPosition.offsetNode?.parentElement;
+ for (const [el, visibility] of savedVisibilities) {
+ el.style.visibility = visibility;
+ }
+ }
+ if (parentElement !== element) {
+ if (select) {
+ selection.extend(element.firstChild, 0);
+ } else {
+ selection.setPosition(element.firstChild, 0);
+ }
+ return;
+ }
+ if (select) {
+ selection.extend(caretPosition.offsetNode, caretPosition.offset);
+ } else {
+ selection.setPosition(caretPosition.offsetNode, caretPosition.offset);
+ }
+ }
+ #setCaretPosition(select, selection, newLineElement, newLineElementRect, caretX) {
+ if (this.#isVisible(newLineElementRect)) {
+ this.#setCaretPositionHelper(selection, caretX, select, newLineElement, newLineElementRect);
+ return;
+ }
+ this.#mainContainer.addEventListener("scrollend", this.#setCaretPositionHelper.bind(this, selection, caretX, select, newLineElement, null), {
+ once: true
+ });
+ newLineElement.scrollIntoView();
+ }
+ #getNodeOnNextPage(textLayer, isUp) {
+ while (true) {
+ const page = textLayer.closest(".page");
+ const pageNumber = parseInt(page.getAttribute("data-page-number"));
+ const nextPage = isUp ? pageNumber - 1 : pageNumber + 1;
+ textLayer = this.#viewerContainer.querySelector(`.page[data-page-number="${nextPage}"] .textLayer`);
+ if (!textLayer) {
+ return null;
+ }
+ const walker = document.createTreeWalker(textLayer, NodeFilter.SHOW_TEXT);
+ const node = isUp ? walker.lastChild() : walker.firstChild();
+ if (node) {
+ return node;
+ }
+ }
+ }
+ moveCaret(isUp, select) {
+ const selection = document.getSelection();
+ if (selection.rangeCount === 0) {
+ return;
+ }
+ const {
+ focusNode
+ } = selection;
+ const focusElement = focusNode.nodeType !== Node.ELEMENT_NODE ? focusNode.parentElement : focusNode;
+ const root = focusElement.closest(".textLayer");
+ if (!root) {
+ return;
+ }
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
+ walker.currentNode = focusNode;
+ const focusRect = focusElement.getBoundingClientRect();
+ let newLineElement = null;
+ const nodeIterator = (isUp ? walker.previousSibling : walker.nextSibling).bind(walker);
+ while (nodeIterator()) {
+ const element = walker.currentNode.parentElement;
+ if (!this.#isOnSameLine(focusRect, element.getBoundingClientRect())) {
+ newLineElement = element;
+ break;
+ }
+ }
+ if (!newLineElement) {
+ const node = this.#getNodeOnNextPage(root, isUp);
+ if (!node) {
+ return;
+ }
+ if (select) {
+ const lastNode = (isUp ? walker.firstChild() : walker.lastChild()) || focusNode;
+ selection.extend(lastNode, isUp ? 0 : lastNode.length);
+ const range = document.createRange();
+ range.setStart(node, isUp ? node.length : 0);
+ range.setEnd(node, isUp ? node.length : 0);
+ selection.addRange(range);
+ return;
+ }
+ const [caretX] = this.#getCaretPosition(selection, isUp);
+ const {
+ parentElement
+ } = node;
+ this.#setCaretPosition(select, selection, parentElement, parentElement.getBoundingClientRect(), caretX);
+ return;
+ }
+ const [caretX, caretY] = this.#getCaretPosition(selection, isUp);
+ const newLineElementRect = newLineElement.getBoundingClientRect();
+ if (this.#isUnderOver(newLineElementRect, caretX, caretY, isUp)) {
+ this.#setCaretPosition(select, selection, newLineElement, newLineElementRect, caretX);
+ return;
+ }
+ while (nodeIterator()) {
+ const element = walker.currentNode.parentElement;
+ const elementRect = element.getBoundingClientRect();
+ if (!this.#isOnSameLine(newLineElementRect, elementRect)) {
+ break;
+ }
+ if (this.#isUnderOver(elementRect, caretX, caretY, isUp)) {
+ this.#setCaretPosition(select, selection, element, elementRect, caretX);
+ return;
+ }
+ }
+ this.#setCaretPosition(select, selection, newLineElement, newLineElementRect, caretX);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/download_manager.js
+
+function download(blobUrl, filename) {
+ const a = document.createElement("a");
+ if (!a.click) {
+ throw new Error('DownloadManager: "a.click()" is not supported.');
+ }
+ a.href = blobUrl;
+ a.target = "_parent";
+ if ("download" in a) {
+ a.download = filename;
+ }
+ (document.body || document.documentElement).append(a);
+ a.click();
+ a.remove();
+}
+class DownloadManager {
+ #openBlobUrls = new WeakMap();
+ downloadUrl(url, filename, _options) {
+ if (!createValidAbsoluteUrl(url, "http://example.com")) {
+ console.error(`downloadUrl - not a valid URL: ${url}`);
+ return;
+ }
+ download(url + "#pdfjs.action=download", filename);
+ }
+ downloadData(data, filename, contentType) {
+ const blobUrl = URL.createObjectURL(new Blob([data], {
+ type: contentType
+ }));
+ download(blobUrl, filename);
+ }
+ openOrDownloadData(data, filename, dest = null) {
+ const isPdfData = isPdfFile(filename);
+ const contentType = isPdfData ? "application/pdf" : "";
+ if (isPdfData) {
+ let blobUrl = this.#openBlobUrls.get(data);
+ if (!blobUrl) {
+ blobUrl = URL.createObjectURL(new Blob([data], {
+ type: contentType
+ }));
+ this.#openBlobUrls.set(data, blobUrl);
+ }
+ let viewerUrl;
+ viewerUrl = "?file=" + encodeURIComponent(blobUrl + "#" + filename);
+ if (dest) {
+ viewerUrl += `#${escape(dest)}`;
+ }
+ try {
+ window.open(viewerUrl);
+ return true;
+ } catch (ex) {
+ console.error(`openOrDownloadData: ${ex}`);
+ URL.revokeObjectURL(blobUrl);
+ this.#openBlobUrls.delete(data);
+ }
+ }
+ this.downloadData(data, filename, contentType);
+ return false;
+ }
+ download(blob, url, filename, _options) {
+ const blobUrl = URL.createObjectURL(blob);
+ download(blobUrl, filename);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/overlay_manager.js
+class OverlayManager {
+ #overlays = new WeakMap();
+ #active = null;
+ get active() {
+ return this.#active;
+ }
+ async register(dialog, canForceClose = false) {
+ if (typeof dialog !== "object") {
+ throw new Error("Not enough parameters.");
+ } else if (this.#overlays.has(dialog)) {
+ throw new Error("The overlay is already registered.");
+ }
+ this.#overlays.set(dialog, {
+ canForceClose
+ });
+ dialog.addEventListener("cancel", evt => {
+ this.#active = null;
+ });
+ }
+ async open(dialog) {
+ if (!this.#overlays.has(dialog)) {
+ throw new Error("The overlay does not exist.");
+ } else if (this.#active) {
+ if (this.#active === dialog) {
+ throw new Error("The overlay is already active.");
+ } else if (this.#overlays.get(dialog).canForceClose) {
+ await this.close();
+ } else {
+ throw new Error("Another overlay is currently active.");
+ }
+ }
+ this.#active = dialog;
+ dialog.showModal();
+ }
+ async close(dialog = this.#active) {
+ if (!this.#overlays.has(dialog)) {
+ throw new Error("The overlay does not exist.");
+ } else if (!this.#active) {
+ throw new Error("The overlay is currently not active.");
+ } else if (this.#active !== dialog) {
+ throw new Error("Another overlay is currently active.");
+ }
+ dialog.close();
+ this.#active = null;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/password_prompt.js
+
+class PasswordPrompt {
+ #activeCapability = null;
+ #updateCallback = null;
+ #reason = null;
+ constructor(options, overlayManager, isViewerEmbedded = false) {
+ this.dialog = options.dialog;
+ this.label = options.label;
+ this.input = options.input;
+ this.submitButton = options.submitButton;
+ this.cancelButton = options.cancelButton;
+ this.overlayManager = overlayManager;
+ this._isViewerEmbedded = isViewerEmbedded;
+ this.submitButton.addEventListener("click", this.#verify.bind(this));
+ this.cancelButton.addEventListener("click", this.close.bind(this));
+ this.input.addEventListener("keydown", e => {
+ if (e.keyCode === 13) {
+ this.#verify();
+ }
+ });
+ this.overlayManager.register(this.dialog, true);
+ this.dialog.addEventListener("close", this.#cancel.bind(this));
+ }
+ async open() {
+ await this.#activeCapability?.promise;
+ this.#activeCapability = Promise.withResolvers();
+ try {
+ await this.overlayManager.open(this.dialog);
+ } catch (ex) {
+ this.#activeCapability.resolve();
+ throw ex;
+ }
+ const passwordIncorrect = this.#reason === PasswordResponses.INCORRECT_PASSWORD;
+ if (!this._isViewerEmbedded || passwordIncorrect) {
+ this.input.focus();
+ }
+ this.label.setAttribute("data-l10n-id", `pdfjs-password-${passwordIncorrect ? "invalid" : "label"}`);
+ }
+ async close() {
+ if (this.overlayManager.active === this.dialog) {
+ this.overlayManager.close(this.dialog);
+ }
+ }
+ #verify() {
+ const password = this.input.value;
+ if (password?.length > 0) {
+ this.#invokeCallback(password);
+ }
+ }
+ #cancel() {
+ this.#invokeCallback(new Error("PasswordPrompt cancelled."));
+ this.#activeCapability.resolve();
+ }
+ #invokeCallback(password) {
+ if (!this.#updateCallback) {
+ return;
+ }
+ this.close();
+ this.input.value = "";
+ this.#updateCallback(password);
+ this.#updateCallback = null;
+ }
+ async setUpdateCallback(updateCallback, reason) {
+ if (this.#activeCapability) {
+ await this.#activeCapability.promise;
+ }
+ this.#updateCallback = updateCallback;
+ this.#reason = reason;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/base_tree_viewer.js
+
+const TREEITEM_OFFSET_TOP = -100;
+const TREEITEM_SELECTED_CLASS = "selected";
+class BaseTreeViewer {
+ constructor(options) {
+ if (this.constructor === BaseTreeViewer) {
+ throw new Error("Cannot initialize BaseTreeViewer.");
+ }
+ this.container = options.container;
+ this.eventBus = options.eventBus;
+ this._l10n = options.l10n;
+ this.reset();
+ }
+ reset() {
+ this._pdfDocument = null;
+ this._lastToggleIsShow = true;
+ this._currentTreeItem = null;
+ this.container.textContent = "";
+ this.container.classList.remove("treeWithDeepNesting");
+ }
+ _dispatchEvent(count) {
+ throw new Error("Not implemented: _dispatchEvent");
+ }
+ _bindLink(element, params) {
+ throw new Error("Not implemented: _bindLink");
+ }
+ _normalizeTextContent(str) {
+ return removeNullCharacters(str, true) || "\u2013";
+ }
+ _addToggleButton(div, hidden = false) {
+ const toggler = document.createElement("div");
+ toggler.className = "treeItemToggler";
+ if (hidden) {
+ toggler.classList.add("treeItemsHidden");
+ }
+ toggler.onclick = evt => {
+ evt.stopPropagation();
+ toggler.classList.toggle("treeItemsHidden");
+ if (evt.shiftKey) {
+ const shouldShowAll = !toggler.classList.contains("treeItemsHidden");
+ this._toggleTreeItem(div, shouldShowAll);
+ }
+ };
+ div.prepend(toggler);
+ }
+ _toggleTreeItem(root, show = false) {
+ this._l10n.pause();
+ this._lastToggleIsShow = show;
+ for (const toggler of root.querySelectorAll(".treeItemToggler")) {
+ toggler.classList.toggle("treeItemsHidden", !show);
+ }
+ this._l10n.resume();
+ }
+ _toggleAllTreeItems() {
+ this._toggleTreeItem(this.container, !this._lastToggleIsShow);
+ }
+ _finishRendering(fragment, count, hasAnyNesting = false) {
+ if (hasAnyNesting) {
+ this.container.classList.add("treeWithDeepNesting");
+ this._lastToggleIsShow = !fragment.querySelector(".treeItemsHidden");
+ }
+ this._l10n.pause();
+ this.container.append(fragment);
+ this._l10n.resume();
+ this._dispatchEvent(count);
+ }
+ render(params) {
+ throw new Error("Not implemented: render");
+ }
+ _updateCurrentTreeItem(treeItem = null) {
+ if (this._currentTreeItem) {
+ this._currentTreeItem.classList.remove(TREEITEM_SELECTED_CLASS);
+ this._currentTreeItem = null;
+ }
+ if (treeItem) {
+ treeItem.classList.add(TREEITEM_SELECTED_CLASS);
+ this._currentTreeItem = treeItem;
+ }
+ }
+ _scrollToCurrentTreeItem(treeItem) {
+ if (!treeItem) {
+ return;
+ }
+ this._l10n.pause();
+ let currentNode = treeItem.parentNode;
+ while (currentNode && currentNode !== this.container) {
+ if (currentNode.classList.contains("treeItem")) {
+ const toggler = currentNode.firstElementChild;
+ toggler?.classList.remove("treeItemsHidden");
+ }
+ currentNode = currentNode.parentNode;
+ }
+ this._l10n.resume();
+ this._updateCurrentTreeItem(treeItem);
+ this.container.scrollTo(treeItem.offsetLeft, treeItem.offsetTop + TREEITEM_OFFSET_TOP);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_attachment_viewer.js
+
+
+class PDFAttachmentViewer extends BaseTreeViewer {
+ constructor(options) {
+ super(options);
+ this.downloadManager = options.downloadManager;
+ this.eventBus._on("fileattachmentannotation", this.#appendAttachment.bind(this));
+ }
+ reset(keepRenderedCapability = false) {
+ super.reset();
+ this._attachments = null;
+ if (!keepRenderedCapability) {
+ this._renderedCapability = Promise.withResolvers();
+ }
+ this._pendingDispatchEvent = false;
+ }
+ async _dispatchEvent(attachmentsCount) {
+ this._renderedCapability.resolve();
+ if (attachmentsCount === 0 && !this._pendingDispatchEvent) {
+ this._pendingDispatchEvent = true;
+ await waitOnEventOrTimeout({
+ target: this.eventBus,
+ name: "annotationlayerrendered",
+ delay: 1000
+ });
+ if (!this._pendingDispatchEvent) {
+ return;
+ }
+ }
+ this._pendingDispatchEvent = false;
+ this.eventBus.dispatch("attachmentsloaded", {
+ source: this,
+ attachmentsCount
+ });
+ }
+ _bindLink(element, {
+ content,
+ description,
+ filename
+ }) {
+ if (description) {
+ element.title = description;
+ }
+ element.onclick = () => {
+ this.downloadManager.openOrDownloadData(content, filename);
+ return false;
+ };
+ }
+ render({
+ attachments,
+ keepRenderedCapability = false
+ }) {
+ if (this._attachments) {
+ this.reset(keepRenderedCapability);
+ }
+ this._attachments = attachments || null;
+ if (!attachments) {
+ this._dispatchEvent(0);
+ return;
+ }
+ const fragment = document.createDocumentFragment();
+ let attachmentsCount = 0;
+ for (const name in attachments) {
+ const item = attachments[name];
+ const div = document.createElement("div");
+ div.className = "treeItem";
+ const element = document.createElement("a");
+ this._bindLink(element, item);
+ element.textContent = this._normalizeTextContent(item.filename);
+ div.append(element);
+ fragment.append(div);
+ attachmentsCount++;
+ }
+ this._finishRendering(fragment, attachmentsCount);
+ }
+ #appendAttachment(item) {
+ const renderedPromise = this._renderedCapability.promise;
+ renderedPromise.then(() => {
+ if (renderedPromise !== this._renderedCapability.promise) {
+ return;
+ }
+ const attachments = this._attachments || Object.create(null);
+ for (const name in attachments) {
+ if (item.filename === name) {
+ return;
+ }
+ }
+ attachments[item.filename] = item;
+ this.render({
+ attachments,
+ keepRenderedCapability: true
+ });
+ });
+ }
+}
+
+;// CONCATENATED MODULE: ./web/grab_to_pan.js
+const CSS_CLASS_GRAB = "grab-to-pan-grab";
+class GrabToPan {
+ constructor({
+ element
+ }) {
+ this.element = element;
+ this.document = element.ownerDocument;
+ this.activate = this.activate.bind(this);
+ this.deactivate = this.deactivate.bind(this);
+ this.toggle = this.toggle.bind(this);
+ this._onMouseDown = this.#onMouseDown.bind(this);
+ this._onMouseMove = this.#onMouseMove.bind(this);
+ this._endPan = this.#endPan.bind(this);
+ const overlay = this.overlay = document.createElement("div");
+ overlay.className = "grab-to-pan-grabbing";
+ }
+ activate() {
+ if (!this.active) {
+ this.active = true;
+ this.element.addEventListener("mousedown", this._onMouseDown, true);
+ this.element.classList.add(CSS_CLASS_GRAB);
+ }
+ }
+ deactivate() {
+ if (this.active) {
+ this.active = false;
+ this.element.removeEventListener("mousedown", this._onMouseDown, true);
+ this._endPan();
+ this.element.classList.remove(CSS_CLASS_GRAB);
+ }
+ }
+ toggle() {
+ if (this.active) {
+ this.deactivate();
+ } else {
+ this.activate();
+ }
+ }
+ ignoreTarget(node) {
+ return node.matches("a[href], a[href] *, input, textarea, button, button *, select, option");
+ }
+ #onMouseDown(event) {
+ if (event.button !== 0 || this.ignoreTarget(event.target)) {
+ return;
+ }
+ if (event.originalTarget) {
+ try {
+ event.originalTarget.tagName;
+ } catch {
+ return;
+ }
+ }
+ this.scrollLeftStart = this.element.scrollLeft;
+ this.scrollTopStart = this.element.scrollTop;
+ this.clientXStart = event.clientX;
+ this.clientYStart = event.clientY;
+ this.document.addEventListener("mousemove", this._onMouseMove, true);
+ this.document.addEventListener("mouseup", this._endPan, true);
+ this.element.addEventListener("scroll", this._endPan, true);
+ event.preventDefault();
+ event.stopPropagation();
+ const focusedElement = document.activeElement;
+ if (focusedElement && !focusedElement.contains(event.target)) {
+ focusedElement.blur();
+ }
+ }
+ #onMouseMove(event) {
+ this.element.removeEventListener("scroll", this._endPan, true);
+ if (!(event.buttons & 1)) {
+ this._endPan();
+ return;
+ }
+ const xDiff = event.clientX - this.clientXStart;
+ const yDiff = event.clientY - this.clientYStart;
+ this.element.scrollTo({
+ top: this.scrollTopStart - yDiff,
+ left: this.scrollLeftStart - xDiff,
+ behavior: "instant"
+ });
+ if (!this.overlay.parentNode) {
+ document.body.append(this.overlay);
+ }
+ }
+ #endPan() {
+ this.element.removeEventListener("scroll", this._endPan, true);
+ this.document.removeEventListener("mousemove", this._onMouseMove, true);
+ this.document.removeEventListener("mouseup", this._endPan, true);
+ this.overlay.remove();
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_cursor_tools.js
+
+
+
+class PDFCursorTools {
+ #active = CursorTool.SELECT;
+ #prevActive = null;
+ constructor({
+ container,
+ eventBus,
+ cursorToolOnLoad = CursorTool.SELECT
+ }) {
+ this.container = container;
+ this.eventBus = eventBus;
+ this.#addEventListeners();
+ Promise.resolve().then(() => {
+ this.switchTool(cursorToolOnLoad);
+ });
+ }
+ get activeTool() {
+ return this.#active;
+ }
+ switchTool(tool) {
+ if (this.#prevActive !== null) {
+ return;
+ }
+ if (tool === this.#active) {
+ return;
+ }
+ const disableActiveTool = () => {
+ switch (this.#active) {
+ case CursorTool.SELECT:
+ break;
+ case CursorTool.HAND:
+ this._handTool.deactivate();
+ break;
+ case CursorTool.ZOOM:
+ }
+ };
+ switch (tool) {
+ case CursorTool.SELECT:
+ disableActiveTool();
+ break;
+ case CursorTool.HAND:
+ disableActiveTool();
+ this._handTool.activate();
+ break;
+ case CursorTool.ZOOM:
+ default:
+ console.error(`switchTool: "${tool}" is an unsupported value.`);
+ return;
+ }
+ this.#active = tool;
+ this.eventBus.dispatch("cursortoolchanged", {
+ source: this,
+ tool
+ });
+ }
+ #addEventListeners() {
+ this.eventBus._on("switchcursortool", evt => {
+ if (!evt.reset) {
+ this.switchTool(evt.tool);
+ } else if (this.#prevActive !== null) {
+ annotationEditorMode = AnnotationEditorType.NONE;
+ presentationModeState = PresentationModeState.NORMAL;
+ enableActive();
+ }
+ });
+ let annotationEditorMode = AnnotationEditorType.NONE,
+ presentationModeState = PresentationModeState.NORMAL;
+ const disableActive = () => {
+ const prevActive = this.#active;
+ this.switchTool(CursorTool.SELECT);
+ this.#prevActive ??= prevActive;
+ };
+ const enableActive = () => {
+ const prevActive = this.#prevActive;
+ if (prevActive !== null && annotationEditorMode === AnnotationEditorType.NONE && presentationModeState === PresentationModeState.NORMAL) {
+ this.#prevActive = null;
+ this.switchTool(prevActive);
+ }
+ };
+ this.eventBus._on("annotationeditormodechanged", ({
+ mode
+ }) => {
+ annotationEditorMode = mode;
+ if (mode === AnnotationEditorType.NONE) {
+ enableActive();
+ } else {
+ disableActive();
+ }
+ });
+ this.eventBus._on("presentationmodechanged", ({
+ state
+ }) => {
+ presentationModeState = state;
+ if (state === PresentationModeState.NORMAL) {
+ enableActive();
+ } else if (state === PresentationModeState.FULLSCREEN) {
+ disableActive();
+ }
+ });
+ }
+ get _handTool() {
+ return shadow(this, "_handTool", new GrabToPan({
+ element: this.container
+ }));
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_document_properties.js
+
+
+const DEFAULT_FIELD_CONTENT = "-";
+const NON_METRIC_LOCALES = ["en-us", "en-lr", "my"];
+const US_PAGE_NAMES = {
+ "8.5x11": "letter",
+ "8.5x14": "legal"
+};
+const METRIC_PAGE_NAMES = {
+ "297x420": "a-three",
+ "210x297": "a-four"
+};
+function getPageName(size, isPortrait, pageNames) {
+ const width = isPortrait ? size.width : size.height;
+ const height = isPortrait ? size.height : size.width;
+ return pageNames[`${width}x${height}`];
+}
+class PDFDocumentProperties {
+ #fieldData = null;
+ constructor({
+ dialog,
+ fields,
+ closeButton
+ }, overlayManager, eventBus, l10n, fileNameLookup) {
+ this.dialog = dialog;
+ this.fields = fields;
+ this.overlayManager = overlayManager;
+ this.l10n = l10n;
+ this._fileNameLookup = fileNameLookup;
+ this.#reset();
+ closeButton.addEventListener("click", this.close.bind(this));
+ this.overlayManager.register(this.dialog);
+ eventBus._on("pagechanging", evt => {
+ this._currentPageNumber = evt.pageNumber;
+ });
+ eventBus._on("rotationchanging", evt => {
+ this._pagesRotation = evt.pagesRotation;
+ });
+ this._isNonMetricLocale = NON_METRIC_LOCALES.includes(l10n.getLanguage());
+ }
+ async open() {
+ await Promise.all([this.overlayManager.open(this.dialog), this._dataAvailableCapability.promise]);
+ const currentPageNumber = this._currentPageNumber;
+ const pagesRotation = this._pagesRotation;
+ if (this.#fieldData && currentPageNumber === this.#fieldData._currentPageNumber && pagesRotation === this.#fieldData._pagesRotation) {
+ this.#updateUI();
+ return;
+ }
+ const {
+ info,
+ contentLength
+ } = await this.pdfDocument.getMetadata();
+ const [fileName, fileSize, creationDate, modificationDate, pageSize, isLinearized] = await Promise.all([this._fileNameLookup(), this.#parseFileSize(contentLength), this.#parseDate(info.CreationDate), this.#parseDate(info.ModDate), this.pdfDocument.getPage(currentPageNumber).then(pdfPage => {
+ return this.#parsePageSize(getPageSizeInches(pdfPage), pagesRotation);
+ }), this.#parseLinearization(info.IsLinearized)]);
+ this.#fieldData = Object.freeze({
+ fileName,
+ fileSize,
+ title: info.Title,
+ author: info.Author,
+ subject: info.Subject,
+ keywords: info.Keywords,
+ creationDate,
+ modificationDate,
+ creator: info.Creator,
+ producer: info.Producer,
+ version: info.PDFFormatVersion,
+ pageCount: this.pdfDocument.numPages,
+ pageSize,
+ linearized: isLinearized,
+ _currentPageNumber: currentPageNumber,
+ _pagesRotation: pagesRotation
+ });
+ this.#updateUI();
+ const {
+ length
+ } = await this.pdfDocument.getDownloadInfo();
+ if (contentLength === length) {
+ return;
+ }
+ const data = Object.assign(Object.create(null), this.#fieldData);
+ data.fileSize = await this.#parseFileSize(length);
+ this.#fieldData = Object.freeze(data);
+ this.#updateUI();
+ }
+ async close() {
+ this.overlayManager.close(this.dialog);
+ }
+ setDocument(pdfDocument) {
+ if (this.pdfDocument) {
+ this.#reset();
+ this.#updateUI(true);
+ }
+ if (!pdfDocument) {
+ return;
+ }
+ this.pdfDocument = pdfDocument;
+ this._dataAvailableCapability.resolve();
+ }
+ #reset() {
+ this.pdfDocument = null;
+ this.#fieldData = null;
+ this._dataAvailableCapability = Promise.withResolvers();
+ this._currentPageNumber = 1;
+ this._pagesRotation = 0;
+ }
+ #updateUI(reset = false) {
+ if (reset || !this.#fieldData) {
+ for (const id in this.fields) {
+ this.fields[id].textContent = DEFAULT_FIELD_CONTENT;
+ }
+ return;
+ }
+ if (this.overlayManager.active !== this.dialog) {
+ return;
+ }
+ for (const id in this.fields) {
+ const content = this.#fieldData[id];
+ this.fields[id].textContent = content || content === 0 ? content : DEFAULT_FIELD_CONTENT;
+ }
+ }
+ async #parseFileSize(fileSize = 0) {
+ const kb = fileSize / 1024,
+ mb = kb / 1024;
+ if (!kb) {
+ return undefined;
+ }
+ return this.l10n.get(`pdfjs-document-properties-${mb >= 1 ? "mb" : "kb"}`, {
+ size_mb: mb >= 1 && (+mb.toPrecision(3)).toLocaleString(),
+ size_kb: mb < 1 && (+kb.toPrecision(3)).toLocaleString(),
+ size_b: fileSize.toLocaleString()
+ });
+ }
+ async #parsePageSize(pageSizeInches, pagesRotation) {
+ if (!pageSizeInches) {
+ return undefined;
+ }
+ if (pagesRotation % 180 !== 0) {
+ pageSizeInches = {
+ width: pageSizeInches.height,
+ height: pageSizeInches.width
+ };
+ }
+ const isPortrait = isPortraitOrientation(pageSizeInches);
+ let sizeInches = {
+ width: Math.round(pageSizeInches.width * 100) / 100,
+ height: Math.round(pageSizeInches.height * 100) / 100
+ };
+ let sizeMillimeters = {
+ width: Math.round(pageSizeInches.width * 25.4 * 10) / 10,
+ height: Math.round(pageSizeInches.height * 25.4 * 10) / 10
+ };
+ let rawName = getPageName(sizeInches, isPortrait, US_PAGE_NAMES) || getPageName(sizeMillimeters, isPortrait, METRIC_PAGE_NAMES);
+ if (!rawName && !(Number.isInteger(sizeMillimeters.width) && Number.isInteger(sizeMillimeters.height))) {
+ const exactMillimeters = {
+ width: pageSizeInches.width * 25.4,
+ height: pageSizeInches.height * 25.4
+ };
+ const intMillimeters = {
+ width: Math.round(sizeMillimeters.width),
+ height: Math.round(sizeMillimeters.height)
+ };
+ if (Math.abs(exactMillimeters.width - intMillimeters.width) < 0.1 && Math.abs(exactMillimeters.height - intMillimeters.height) < 0.1) {
+ rawName = getPageName(intMillimeters, isPortrait, METRIC_PAGE_NAMES);
+ if (rawName) {
+ sizeInches = {
+ width: Math.round(intMillimeters.width / 25.4 * 100) / 100,
+ height: Math.round(intMillimeters.height / 25.4 * 100) / 100
+ };
+ sizeMillimeters = intMillimeters;
+ }
+ }
+ }
+ const [{
+ width,
+ height
+ }, unit, name, orientation] = await Promise.all([this._isNonMetricLocale ? sizeInches : sizeMillimeters, this.l10n.get(`pdfjs-document-properties-page-size-unit-${this._isNonMetricLocale ? "inches" : "millimeters"}`), rawName && this.l10n.get(`pdfjs-document-properties-page-size-name-${rawName}`), this.l10n.get(`pdfjs-document-properties-page-size-orientation-${isPortrait ? "portrait" : "landscape"}`)]);
+ return this.l10n.get(`pdfjs-document-properties-page-size-dimension-${name ? "name-" : ""}string`, {
+ width: width.toLocaleString(),
+ height: height.toLocaleString(),
+ unit,
+ name,
+ orientation
+ });
+ }
+ async #parseDate(inputDate) {
+ const dateObject = PDFDateString.toDateObject(inputDate);
+ if (!dateObject) {
+ return undefined;
+ }
+ return this.l10n.get("pdfjs-document-properties-date-string", {
+ date: dateObject.toLocaleDateString(),
+ time: dateObject.toLocaleTimeString()
+ });
+ }
+ #parseLinearization(isLinearized) {
+ return this.l10n.get(`pdfjs-document-properties-linearized-${isLinearized ? "yes" : "no"}`);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_find_utils.js
+const CharacterType = {
+ SPACE: 0,
+ ALPHA_LETTER: 1,
+ PUNCT: 2,
+ HAN_LETTER: 3,
+ KATAKANA_LETTER: 4,
+ HIRAGANA_LETTER: 5,
+ HALFWIDTH_KATAKANA_LETTER: 6,
+ THAI_LETTER: 7
+};
+function isAlphabeticalScript(charCode) {
+ return charCode < 0x2e80;
+}
+function isAscii(charCode) {
+ return (charCode & 0xff80) === 0;
+}
+function isAsciiAlpha(charCode) {
+ return charCode >= 0x61 && charCode <= 0x7a || charCode >= 0x41 && charCode <= 0x5a;
+}
+function isAsciiDigit(charCode) {
+ return charCode >= 0x30 && charCode <= 0x39;
+}
+function isAsciiSpace(charCode) {
+ return charCode === 0x20 || charCode === 0x09 || charCode === 0x0d || charCode === 0x0a;
+}
+function isHan(charCode) {
+ return charCode >= 0x3400 && charCode <= 0x9fff || charCode >= 0xf900 && charCode <= 0xfaff;
+}
+function isKatakana(charCode) {
+ return charCode >= 0x30a0 && charCode <= 0x30ff;
+}
+function isHiragana(charCode) {
+ return charCode >= 0x3040 && charCode <= 0x309f;
+}
+function isHalfwidthKatakana(charCode) {
+ return charCode >= 0xff60 && charCode <= 0xff9f;
+}
+function isThai(charCode) {
+ return (charCode & 0xff80) === 0x0e00;
+}
+function getCharacterType(charCode) {
+ if (isAlphabeticalScript(charCode)) {
+ if (isAscii(charCode)) {
+ if (isAsciiSpace(charCode)) {
+ return CharacterType.SPACE;
+ } else if (isAsciiAlpha(charCode) || isAsciiDigit(charCode) || charCode === 0x5f) {
+ return CharacterType.ALPHA_LETTER;
+ }
+ return CharacterType.PUNCT;
+ } else if (isThai(charCode)) {
+ return CharacterType.THAI_LETTER;
+ } else if (charCode === 0xa0) {
+ return CharacterType.SPACE;
+ }
+ return CharacterType.ALPHA_LETTER;
+ }
+ if (isHan(charCode)) {
+ return CharacterType.HAN_LETTER;
+ } else if (isKatakana(charCode)) {
+ return CharacterType.KATAKANA_LETTER;
+ } else if (isHiragana(charCode)) {
+ return CharacterType.HIRAGANA_LETTER;
+ } else if (isHalfwidthKatakana(charCode)) {
+ return CharacterType.HALFWIDTH_KATAKANA_LETTER;
+ }
+ return CharacterType.ALPHA_LETTER;
+}
+let NormalizeWithNFKC;
+function getNormalizeWithNFKC() {
+ NormalizeWithNFKC ||= ` ¨ª¯²-µ¸-º¼-¾IJ-ijĿ-ŀʼnſDŽ-njDZ-dzʰ-ʸ˘-˝ˠ-ˤʹͺ;΄-΅·ϐ-ϖϰ-ϲϴ-ϵϹևٵ-ٸक़-य़ড়-ঢ়য়ਲ਼ਸ਼ਖ਼-ਜ਼ਫ਼ଡ଼-ଢ଼ำຳໜ-ໝ༌གྷཌྷདྷབྷཛྷཀྵჼᴬ-ᴮᴰ-ᴺᴼ-ᵍᵏ-ᵪᵸᶛ-ᶿẚ-ẛάέήίόύώΆ᾽-῁ΈΉ῍-῏ΐΊ῝-῟ΰΎ῭-`ΌΏ´-῾ - ‑‗․-… ″-‴‶-‷‼‾⁇-⁉⁗ ⁰-ⁱ⁴-₎ₐ-ₜ₨℀-℃℅-ℇ℉-ℓℕ-№ℙ-ℝ℠-™ℤΩℨK-ℭℯ-ℱℳ-ℹ℻-⅀ⅅ-ⅉ⅐-ⅿ↉∬-∭∯-∰〈-〉①-⓪⨌⩴-⩶⫝̸ⱼ-ⱽⵯ⺟⻳⼀-⿕ 〶〸-〺゛-゜ゟヿㄱ-ㆎ㆒-㆟㈀-㈞㈠-㉇㉐-㉾㊀-㏿ꚜ-ꚝꝰꟲ-ꟴꟸ-ꟹꭜ-ꭟꭩ豈-嗀塚晴凞-羽蘒諸逸-都飯-舘並-龎ff-stﬓ-ﬗיִײַ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-﷼︐-︙︰-﹄﹇-﹒﹔-﹦﹨-﹫ﹰ-ﹲﹴﹶ-ﻼ!-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ¢-₩`;
+ return NormalizeWithNFKC;
+}
+
+;// CONCATENATED MODULE: ./web/pdf_find_controller.js
+
+
+const FindState = {
+ FOUND: 0,
+ NOT_FOUND: 1,
+ WRAPPED: 2,
+ PENDING: 3
+};
+const FIND_TIMEOUT = 250;
+const MATCH_SCROLL_OFFSET_TOP = -50;
+const MATCH_SCROLL_OFFSET_LEFT = -400;
+const CHARACTERS_TO_NORMALIZE = {
+ "\u2010": "-",
+ "\u2018": "'",
+ "\u2019": "'",
+ "\u201A": "'",
+ "\u201B": "'",
+ "\u201C": '"',
+ "\u201D": '"',
+ "\u201E": '"',
+ "\u201F": '"',
+ "\u00BC": "1/4",
+ "\u00BD": "1/2",
+ "\u00BE": "3/4"
+};
+const DIACRITICS_EXCEPTION = new Set([0x3099, 0x309a, 0x094d, 0x09cd, 0x0a4d, 0x0acd, 0x0b4d, 0x0bcd, 0x0c4d, 0x0ccd, 0x0d3b, 0x0d3c, 0x0d4d, 0x0dca, 0x0e3a, 0x0eba, 0x0f84, 0x1039, 0x103a, 0x1714, 0x1734, 0x17d2, 0x1a60, 0x1b44, 0x1baa, 0x1bab, 0x1bf2, 0x1bf3, 0x2d7f, 0xa806, 0xa82c, 0xa8c4, 0xa953, 0xa9c0, 0xaaf6, 0xabed, 0x0c56, 0x0f71, 0x0f72, 0x0f7a, 0x0f7b, 0x0f7c, 0x0f7d, 0x0f80, 0x0f74]);
+let DIACRITICS_EXCEPTION_STR;
+const DIACRITICS_REG_EXP = /\p{M}+/gu;
+const SPECIAL_CHARS_REG_EXP = /([.*+?^${}()|[\]\\])|(\p{P})|(\s+)|(\p{M})|(\p{L})/gu;
+const NOT_DIACRITIC_FROM_END_REG_EXP = /([^\p{M}])\p{M}*$/u;
+const NOT_DIACRITIC_FROM_START_REG_EXP = /^\p{M}*([^\p{M}])/u;
+const SYLLABLES_REG_EXP = /[\uAC00-\uD7AF\uFA6C\uFACF-\uFAD1\uFAD5-\uFAD7]+/g;
+const SYLLABLES_LENGTHS = new Map();
+const FIRST_CHAR_SYLLABLES_REG_EXP = "[\\u1100-\\u1112\\ud7a4-\\ud7af\\ud84a\\ud84c\\ud850\\ud854\\ud857\\ud85f]";
+const NFKC_CHARS_TO_NORMALIZE = new Map();
+let noSyllablesRegExp = null;
+let withSyllablesRegExp = null;
+function normalize(text) {
+ const syllablePositions = [];
+ let m;
+ while ((m = SYLLABLES_REG_EXP.exec(text)) !== null) {
+ let {
+ index
+ } = m;
+ for (const char of m[0]) {
+ let len = SYLLABLES_LENGTHS.get(char);
+ if (!len) {
+ len = char.normalize("NFD").length;
+ SYLLABLES_LENGTHS.set(char, len);
+ }
+ syllablePositions.push([len, index++]);
+ }
+ }
+ let normalizationRegex;
+ if (syllablePositions.length === 0 && noSyllablesRegExp) {
+ normalizationRegex = noSyllablesRegExp;
+ } else if (syllablePositions.length > 0 && withSyllablesRegExp) {
+ normalizationRegex = withSyllablesRegExp;
+ } else {
+ const replace = Object.keys(CHARACTERS_TO_NORMALIZE).join("");
+ const toNormalizeWithNFKC = getNormalizeWithNFKC();
+ const CJK = "(?:\\p{Ideographic}|[\u3040-\u30FF])";
+ const HKDiacritics = "(?:\u3099|\u309A)";
+ const regexp = `([${replace}])|([${toNormalizeWithNFKC}])|(${HKDiacritics}\\n)|(\\p{M}+(?:-\\n)?)|(\\S-\\n)|(${CJK}\\n)|(\\n)`;
+ if (syllablePositions.length === 0) {
+ normalizationRegex = noSyllablesRegExp = new RegExp(regexp + "|(\\u0000)", "gum");
+ } else {
+ normalizationRegex = withSyllablesRegExp = new RegExp(regexp + `|(${FIRST_CHAR_SYLLABLES_REG_EXP})`, "gum");
+ }
+ }
+ const rawDiacriticsPositions = [];
+ while ((m = DIACRITICS_REG_EXP.exec(text)) !== null) {
+ rawDiacriticsPositions.push([m[0].length, m.index]);
+ }
+ let normalized = text.normalize("NFD");
+ const positions = [[0, 0]];
+ let rawDiacriticsIndex = 0;
+ let syllableIndex = 0;
+ let shift = 0;
+ let shiftOrigin = 0;
+ let eol = 0;
+ let hasDiacritics = false;
+ normalized = normalized.replace(normalizationRegex, (match, p1, p2, p3, p4, p5, p6, p7, p8, i) => {
+ i -= shiftOrigin;
+ if (p1) {
+ const replacement = CHARACTERS_TO_NORMALIZE[p1];
+ const jj = replacement.length;
+ for (let j = 1; j < jj; j++) {
+ positions.push([i - shift + j, shift - j]);
+ }
+ shift -= jj - 1;
+ return replacement;
+ }
+ if (p2) {
+ let replacement = NFKC_CHARS_TO_NORMALIZE.get(p2);
+ if (!replacement) {
+ replacement = p2.normalize("NFKC");
+ NFKC_CHARS_TO_NORMALIZE.set(p2, replacement);
+ }
+ const jj = replacement.length;
+ for (let j = 1; j < jj; j++) {
+ positions.push([i - shift + j, shift - j]);
+ }
+ shift -= jj - 1;
+ return replacement;
+ }
+ if (p3) {
+ hasDiacritics = true;
+ if (i + eol === rawDiacriticsPositions[rawDiacriticsIndex]?.[1]) {
+ ++rawDiacriticsIndex;
+ } else {
+ positions.push([i - 1 - shift + 1, shift - 1]);
+ shift -= 1;
+ shiftOrigin += 1;
+ }
+ positions.push([i - shift + 1, shift]);
+ shiftOrigin += 1;
+ eol += 1;
+ return p3.charAt(0);
+ }
+ if (p4) {
+ const hasTrailingDashEOL = p4.endsWith("\n");
+ const len = hasTrailingDashEOL ? p4.length - 2 : p4.length;
+ hasDiacritics = true;
+ let jj = len;
+ if (i + eol === rawDiacriticsPositions[rawDiacriticsIndex]?.[1]) {
+ jj -= rawDiacriticsPositions[rawDiacriticsIndex][0];
+ ++rawDiacriticsIndex;
+ }
+ for (let j = 1; j <= jj; j++) {
+ positions.push([i - 1 - shift + j, shift - j]);
+ }
+ shift -= jj;
+ shiftOrigin += jj;
+ if (hasTrailingDashEOL) {
+ i += len - 1;
+ positions.push([i - shift + 1, 1 + shift]);
+ shift += 1;
+ shiftOrigin += 1;
+ eol += 1;
+ return p4.slice(0, len);
+ }
+ return p4;
+ }
+ if (p5) {
+ const len = p5.length - 2;
+ positions.push([i - shift + len, 1 + shift]);
+ shift += 1;
+ shiftOrigin += 1;
+ eol += 1;
+ return p5.slice(0, -2);
+ }
+ if (p6) {
+ const len = p6.length - 1;
+ positions.push([i - shift + len, shift]);
+ shiftOrigin += 1;
+ eol += 1;
+ return p6.slice(0, -1);
+ }
+ if (p7) {
+ positions.push([i - shift + 1, shift - 1]);
+ shift -= 1;
+ shiftOrigin += 1;
+ eol += 1;
+ return " ";
+ }
+ if (i + eol === syllablePositions[syllableIndex]?.[1]) {
+ const newCharLen = syllablePositions[syllableIndex][0] - 1;
+ ++syllableIndex;
+ for (let j = 1; j <= newCharLen; j++) {
+ positions.push([i - (shift - j), shift - j]);
+ }
+ shift -= newCharLen;
+ shiftOrigin += newCharLen;
+ }
+ return p8;
+ });
+ positions.push([normalized.length, shift]);
+ return [normalized, positions, hasDiacritics];
+}
+function getOriginalIndex(diffs, pos, len) {
+ if (!diffs) {
+ return [pos, len];
+ }
+ const start = pos;
+ const end = pos + len - 1;
+ let i = binarySearchFirstItem(diffs, x => x[0] >= start);
+ if (diffs[i][0] > start) {
+ --i;
+ }
+ let j = binarySearchFirstItem(diffs, x => x[0] >= end, i);
+ if (diffs[j][0] > end) {
+ --j;
+ }
+ const oldStart = start + diffs[i][1];
+ const oldEnd = end + diffs[j][1];
+ const oldLen = oldEnd + 1 - oldStart;
+ return [oldStart, oldLen];
+}
+class PDFFindController {
+ #state = null;
+ #updateMatchesCountOnProgress = true;
+ #visitedPagesCount = 0;
+ constructor({
+ linkService,
+ eventBus,
+ updateMatchesCountOnProgress = true
+ }) {
+ this._linkService = linkService;
+ this._eventBus = eventBus;
+ this.#updateMatchesCountOnProgress = updateMatchesCountOnProgress;
+ this.onIsPageVisible = null;
+ this.#reset();
+ eventBus._on("find", this.#onFind.bind(this));
+ eventBus._on("findbarclose", this.#onFindBarClose.bind(this));
+ }
+ get highlightMatches() {
+ return this._highlightMatches;
+ }
+ get pageMatches() {
+ return this._pageMatches;
+ }
+ get pageMatchesLength() {
+ return this._pageMatchesLength;
+ }
+ get selected() {
+ return this._selected;
+ }
+ get state() {
+ return this.#state;
+ }
+ setDocument(pdfDocument) {
+ if (this._pdfDocument) {
+ this.#reset();
+ }
+ if (!pdfDocument) {
+ return;
+ }
+ this._pdfDocument = pdfDocument;
+ this._firstPageCapability.resolve();
+ }
+ #onFind(state) {
+ if (!state) {
+ return;
+ }
+ const pdfDocument = this._pdfDocument;
+ const {
+ type
+ } = state;
+ if (this.#state === null || this.#shouldDirtyMatch(state)) {
+ this._dirtyMatch = true;
+ }
+ this.#state = state;
+ if (type !== "highlightallchange") {
+ this.#updateUIState(FindState.PENDING);
+ }
+ this._firstPageCapability.promise.then(() => {
+ if (!this._pdfDocument || pdfDocument && this._pdfDocument !== pdfDocument) {
+ return;
+ }
+ this.#extractText();
+ const findbarClosed = !this._highlightMatches;
+ const pendingTimeout = !!this._findTimeout;
+ if (this._findTimeout) {
+ clearTimeout(this._findTimeout);
+ this._findTimeout = null;
+ }
+ if (!type) {
+ this._findTimeout = setTimeout(() => {
+ this.#nextMatch();
+ this._findTimeout = null;
+ }, FIND_TIMEOUT);
+ } else if (this._dirtyMatch) {
+ this.#nextMatch();
+ } else if (type === "again") {
+ this.#nextMatch();
+ if (findbarClosed && this.#state.highlightAll) {
+ this.#updateAllPages();
+ }
+ } else if (type === "highlightallchange") {
+ if (pendingTimeout) {
+ this.#nextMatch();
+ } else {
+ this._highlightMatches = true;
+ }
+ this.#updateAllPages();
+ } else {
+ this.#nextMatch();
+ }
+ });
+ }
+ scrollMatchIntoView({
+ element = null,
+ selectedLeft = 0,
+ pageIndex = -1,
+ matchIndex = -1
+ }) {
+ if (!this._scrollMatches || !element) {
+ return;
+ } else if (matchIndex === -1 || matchIndex !== this._selected.matchIdx) {
+ return;
+ } else if (pageIndex === -1 || pageIndex !== this._selected.pageIdx) {
+ return;
+ }
+ this._scrollMatches = false;
+ const spot = {
+ top: MATCH_SCROLL_OFFSET_TOP,
+ left: selectedLeft + MATCH_SCROLL_OFFSET_LEFT
+ };
+ scrollIntoView(element, spot, true);
+ }
+ #reset() {
+ this._highlightMatches = false;
+ this._scrollMatches = false;
+ this._pdfDocument = null;
+ this._pageMatches = [];
+ this._pageMatchesLength = [];
+ this.#visitedPagesCount = 0;
+ this.#state = null;
+ this._selected = {
+ pageIdx: -1,
+ matchIdx: -1
+ };
+ this._offset = {
+ pageIdx: null,
+ matchIdx: null,
+ wrapped: false
+ };
+ this._extractTextPromises = [];
+ this._pageContents = [];
+ this._pageDiffs = [];
+ this._hasDiacritics = [];
+ this._matchesCountTotal = 0;
+ this._pagesToSearch = null;
+ this._pendingFindMatches = new Set();
+ this._resumePageIdx = null;
+ this._dirtyMatch = false;
+ clearTimeout(this._findTimeout);
+ this._findTimeout = null;
+ this._firstPageCapability = Promise.withResolvers();
+ }
+ get #query() {
+ const {
+ query
+ } = this.#state;
+ if (typeof query === "string") {
+ if (query !== this._rawQuery) {
+ this._rawQuery = query;
+ [this._normalizedQuery] = normalize(query);
+ }
+ return this._normalizedQuery;
+ }
+ return (query || []).filter(q => !!q).map(q => normalize(q)[0]);
+ }
+ #shouldDirtyMatch(state) {
+ const newQuery = state.query,
+ prevQuery = this.#state.query;
+ const newType = typeof newQuery,
+ prevType = typeof prevQuery;
+ if (newType !== prevType) {
+ return true;
+ }
+ if (newType === "string") {
+ if (newQuery !== prevQuery) {
+ return true;
+ }
+ } else if (JSON.stringify(newQuery) !== JSON.stringify(prevQuery)) {
+ return true;
+ }
+ switch (state.type) {
+ case "again":
+ const pageNumber = this._selected.pageIdx + 1;
+ const linkService = this._linkService;
+ return pageNumber >= 1 && pageNumber <= linkService.pagesCount && pageNumber !== linkService.page && !(this.onIsPageVisible?.(pageNumber) ?? true);
+ case "highlightallchange":
+ return false;
+ }
+ return true;
+ }
+ #isEntireWord(content, startIdx, length) {
+ let match = content.slice(0, startIdx).match(NOT_DIACRITIC_FROM_END_REG_EXP);
+ if (match) {
+ const first = content.charCodeAt(startIdx);
+ const limit = match[1].charCodeAt(0);
+ if (getCharacterType(first) === getCharacterType(limit)) {
+ return false;
+ }
+ }
+ match = content.slice(startIdx + length).match(NOT_DIACRITIC_FROM_START_REG_EXP);
+ if (match) {
+ const last = content.charCodeAt(startIdx + length - 1);
+ const limit = match[1].charCodeAt(0);
+ if (getCharacterType(last) === getCharacterType(limit)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ #calculateRegExpMatch(query, entireWord, pageIndex, pageContent) {
+ const matches = this._pageMatches[pageIndex] = [];
+ const matchesLength = this._pageMatchesLength[pageIndex] = [];
+ if (!query) {
+ return;
+ }
+ const diffs = this._pageDiffs[pageIndex];
+ let match;
+ while ((match = query.exec(pageContent)) !== null) {
+ if (entireWord && !this.#isEntireWord(pageContent, match.index, match[0].length)) {
+ continue;
+ }
+ const [matchPos, matchLen] = getOriginalIndex(diffs, match.index, match[0].length);
+ if (matchLen) {
+ matches.push(matchPos);
+ matchesLength.push(matchLen);
+ }
+ }
+ }
+ #convertToRegExpString(query, hasDiacritics) {
+ const {
+ matchDiacritics
+ } = this.#state;
+ let isUnicode = false;
+ query = query.replaceAll(SPECIAL_CHARS_REG_EXP, (match, p1, p2, p3, p4, p5) => {
+ if (p1) {
+ return `[ ]*\\${p1}[ ]*`;
+ }
+ if (p2) {
+ return `[ ]*${p2}[ ]*`;
+ }
+ if (p3) {
+ return "[ ]+";
+ }
+ if (matchDiacritics) {
+ return p4 || p5;
+ }
+ if (p4) {
+ return DIACRITICS_EXCEPTION.has(p4.charCodeAt(0)) ? p4 : "";
+ }
+ if (hasDiacritics) {
+ isUnicode = true;
+ return `${p5}\\p{M}*`;
+ }
+ return p5;
+ });
+ const trailingSpaces = "[ ]*";
+ if (query.endsWith(trailingSpaces)) {
+ query = query.slice(0, query.length - trailingSpaces.length);
+ }
+ if (matchDiacritics) {
+ if (hasDiacritics) {
+ DIACRITICS_EXCEPTION_STR ||= String.fromCharCode(...DIACRITICS_EXCEPTION);
+ isUnicode = true;
+ query = `${query}(?=[${DIACRITICS_EXCEPTION_STR}]|[^\\p{M}]|$)`;
+ }
+ }
+ return [isUnicode, query];
+ }
+ #calculateMatch(pageIndex) {
+ let query = this.#query;
+ if (query.length === 0) {
+ return;
+ }
+ const {
+ caseSensitive,
+ entireWord
+ } = this.#state;
+ const pageContent = this._pageContents[pageIndex];
+ const hasDiacritics = this._hasDiacritics[pageIndex];
+ let isUnicode = false;
+ if (typeof query === "string") {
+ [isUnicode, query] = this.#convertToRegExpString(query, hasDiacritics);
+ } else {
+ query = query.sort().reverse().map(q => {
+ const [isUnicodePart, queryPart] = this.#convertToRegExpString(q, hasDiacritics);
+ isUnicode ||= isUnicodePart;
+ return `(${queryPart})`;
+ }).join("|");
+ }
+ const flags = `g${isUnicode ? "u" : ""}${caseSensitive ? "" : "i"}`;
+ query = query ? new RegExp(query, flags) : null;
+ this.#calculateRegExpMatch(query, entireWord, pageIndex, pageContent);
+ if (this.#state.highlightAll) {
+ this.#updatePage(pageIndex);
+ }
+ if (this._resumePageIdx === pageIndex) {
+ this._resumePageIdx = null;
+ this.#nextPageMatch();
+ }
+ const pageMatchesCount = this._pageMatches[pageIndex].length;
+ this._matchesCountTotal += pageMatchesCount;
+ if (this.#updateMatchesCountOnProgress) {
+ if (pageMatchesCount > 0) {
+ this.#updateUIResultsCount();
+ }
+ } else if (++this.#visitedPagesCount === this._linkService.pagesCount) {
+ this.#updateUIResultsCount();
+ }
+ }
+ #extractText() {
+ if (this._extractTextPromises.length > 0) {
+ return;
+ }
+ let deferred = Promise.resolve();
+ const textOptions = {
+ disableNormalization: true
+ };
+ for (let i = 0, ii = this._linkService.pagesCount; i < ii; i++) {
+ const {
+ promise,
+ resolve
+ } = Promise.withResolvers();
+ this._extractTextPromises[i] = promise;
+ deferred = deferred.then(() => {
+ return this._pdfDocument.getPage(i + 1).then(pdfPage => pdfPage.getTextContent(textOptions)).then(textContent => {
+ const strBuf = [];
+ for (const textItem of textContent.items) {
+ strBuf.push(textItem.str);
+ if (textItem.hasEOL) {
+ strBuf.push("\n");
+ }
+ }
+ [this._pageContents[i], this._pageDiffs[i], this._hasDiacritics[i]] = normalize(strBuf.join(""));
+ resolve();
+ }, reason => {
+ console.error(`Unable to get text content for page ${i + 1}`, reason);
+ this._pageContents[i] = "";
+ this._pageDiffs[i] = null;
+ this._hasDiacritics[i] = false;
+ resolve();
+ });
+ });
+ }
+ }
+ #updatePage(index) {
+ if (this._scrollMatches && this._selected.pageIdx === index) {
+ this._linkService.page = index + 1;
+ }
+ this._eventBus.dispatch("updatetextlayermatches", {
+ source: this,
+ pageIndex: index
+ });
+ }
+ #updateAllPages() {
+ this._eventBus.dispatch("updatetextlayermatches", {
+ source: this,
+ pageIndex: -1
+ });
+ }
+ #nextMatch() {
+ const previous = this.#state.findPrevious;
+ const currentPageIndex = this._linkService.page - 1;
+ const numPages = this._linkService.pagesCount;
+ this._highlightMatches = true;
+ if (this._dirtyMatch) {
+ this._dirtyMatch = false;
+ this._selected.pageIdx = this._selected.matchIdx = -1;
+ this._offset.pageIdx = currentPageIndex;
+ this._offset.matchIdx = null;
+ this._offset.wrapped = false;
+ this._resumePageIdx = null;
+ this._pageMatches.length = 0;
+ this._pageMatchesLength.length = 0;
+ this.#visitedPagesCount = 0;
+ this._matchesCountTotal = 0;
+ this.#updateAllPages();
+ for (let i = 0; i < numPages; i++) {
+ if (this._pendingFindMatches.has(i)) {
+ continue;
+ }
+ this._pendingFindMatches.add(i);
+ this._extractTextPromises[i].then(() => {
+ this._pendingFindMatches.delete(i);
+ this.#calculateMatch(i);
+ });
+ }
+ }
+ const query = this.#query;
+ if (query.length === 0) {
+ this.#updateUIState(FindState.FOUND);
+ return;
+ }
+ if (this._resumePageIdx) {
+ return;
+ }
+ const offset = this._offset;
+ this._pagesToSearch = numPages;
+ if (offset.matchIdx !== null) {
+ const numPageMatches = this._pageMatches[offset.pageIdx].length;
+ if (!previous && offset.matchIdx + 1 < numPageMatches || previous && offset.matchIdx > 0) {
+ offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1;
+ this.#updateMatch(true);
+ return;
+ }
+ this.#advanceOffsetPage(previous);
+ }
+ this.#nextPageMatch();
+ }
+ #matchesReady(matches) {
+ const offset = this._offset;
+ const numMatches = matches.length;
+ const previous = this.#state.findPrevious;
+ if (numMatches) {
+ offset.matchIdx = previous ? numMatches - 1 : 0;
+ this.#updateMatch(true);
+ return true;
+ }
+ this.#advanceOffsetPage(previous);
+ if (offset.wrapped) {
+ offset.matchIdx = null;
+ if (this._pagesToSearch < 0) {
+ this.#updateMatch(false);
+ return true;
+ }
+ }
+ return false;
+ }
+ #nextPageMatch() {
+ if (this._resumePageIdx !== null) {
+ console.error("There can only be one pending page.");
+ }
+ let matches = null;
+ do {
+ const pageIdx = this._offset.pageIdx;
+ matches = this._pageMatches[pageIdx];
+ if (!matches) {
+ this._resumePageIdx = pageIdx;
+ break;
+ }
+ } while (!this.#matchesReady(matches));
+ }
+ #advanceOffsetPage(previous) {
+ const offset = this._offset;
+ const numPages = this._linkService.pagesCount;
+ offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1;
+ offset.matchIdx = null;
+ this._pagesToSearch--;
+ if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
+ offset.pageIdx = previous ? numPages - 1 : 0;
+ offset.wrapped = true;
+ }
+ }
+ #updateMatch(found = false) {
+ let state = FindState.NOT_FOUND;
+ const wrapped = this._offset.wrapped;
+ this._offset.wrapped = false;
+ if (found) {
+ const previousPage = this._selected.pageIdx;
+ this._selected.pageIdx = this._offset.pageIdx;
+ this._selected.matchIdx = this._offset.matchIdx;
+ state = wrapped ? FindState.WRAPPED : FindState.FOUND;
+ if (previousPage !== -1 && previousPage !== this._selected.pageIdx) {
+ this.#updatePage(previousPage);
+ }
+ }
+ this.#updateUIState(state, this.#state.findPrevious);
+ if (this._selected.pageIdx !== -1) {
+ this._scrollMatches = true;
+ this.#updatePage(this._selected.pageIdx);
+ }
+ }
+ #onFindBarClose(evt) {
+ const pdfDocument = this._pdfDocument;
+ this._firstPageCapability.promise.then(() => {
+ if (!this._pdfDocument || pdfDocument && this._pdfDocument !== pdfDocument) {
+ return;
+ }
+ if (this._findTimeout) {
+ clearTimeout(this._findTimeout);
+ this._findTimeout = null;
+ }
+ if (this._resumePageIdx) {
+ this._resumePageIdx = null;
+ this._dirtyMatch = true;
+ }
+ this.#updateUIState(FindState.FOUND);
+ this._highlightMatches = false;
+ this.#updateAllPages();
+ });
+ }
+ #requestMatchesCount() {
+ const {
+ pageIdx,
+ matchIdx
+ } = this._selected;
+ let current = 0,
+ total = this._matchesCountTotal;
+ if (matchIdx !== -1) {
+ for (let i = 0; i < pageIdx; i++) {
+ current += this._pageMatches[i]?.length || 0;
+ }
+ current += matchIdx + 1;
+ }
+ if (current < 1 || current > total) {
+ current = total = 0;
+ }
+ return {
+ current,
+ total
+ };
+ }
+ #updateUIResultsCount() {
+ this._eventBus.dispatch("updatefindmatchescount", {
+ source: this,
+ matchesCount: this.#requestMatchesCount()
+ });
+ }
+ #updateUIState(state, previous = false) {
+ if (!this.#updateMatchesCountOnProgress && (this.#visitedPagesCount !== this._linkService.pagesCount || state === FindState.PENDING)) {
+ return;
+ }
+ this._eventBus.dispatch("updatefindcontrolstate", {
+ source: this,
+ state,
+ previous,
+ matchesCount: this.#requestMatchesCount(),
+ rawQuery: this.#state?.query ?? null
+ });
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_find_bar.js
+
+
+const MATCHES_COUNT_LIMIT = 1000;
+class PDFFindBar {
+ #resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this));
+ constructor(options, eventBus) {
+ this.opened = false;
+ this.bar = options.bar;
+ this.toggleButton = options.toggleButton;
+ this.findField = options.findField;
+ this.highlightAll = options.highlightAllCheckbox;
+ this.caseSensitive = options.caseSensitiveCheckbox;
+ this.matchDiacritics = options.matchDiacriticsCheckbox;
+ this.entireWord = options.entireWordCheckbox;
+ this.findMsg = options.findMsg;
+ this.findResultsCount = options.findResultsCount;
+ this.findPreviousButton = options.findPreviousButton;
+ this.findNextButton = options.findNextButton;
+ this.eventBus = eventBus;
+ this.toggleButton.addEventListener("click", () => {
+ this.toggle();
+ });
+ this.findField.addEventListener("input", () => {
+ this.dispatchEvent("");
+ });
+ this.bar.addEventListener("keydown", e => {
+ switch (e.keyCode) {
+ case 13:
+ if (e.target === this.findField) {
+ this.dispatchEvent("again", e.shiftKey);
+ }
+ break;
+ case 27:
+ this.close();
+ break;
+ }
+ });
+ this.findPreviousButton.addEventListener("click", () => {
+ this.dispatchEvent("again", true);
+ });
+ this.findNextButton.addEventListener("click", () => {
+ this.dispatchEvent("again", false);
+ });
+ this.highlightAll.addEventListener("click", () => {
+ this.dispatchEvent("highlightallchange");
+ });
+ this.caseSensitive.addEventListener("click", () => {
+ this.dispatchEvent("casesensitivitychange");
+ });
+ this.entireWord.addEventListener("click", () => {
+ this.dispatchEvent("entirewordchange");
+ });
+ this.matchDiacritics.addEventListener("click", () => {
+ this.dispatchEvent("diacriticmatchingchange");
+ });
+ }
+ reset() {
+ this.updateUIState();
+ }
+ dispatchEvent(type, findPrev = false) {
+ this.eventBus.dispatch("find", {
+ source: this,
+ type,
+ query: this.findField.value,
+ caseSensitive: this.caseSensitive.checked,
+ entireWord: this.entireWord.checked,
+ highlightAll: this.highlightAll.checked,
+ findPrevious: findPrev,
+ matchDiacritics: this.matchDiacritics.checked
+ });
+ }
+ updateUIState(state, previous, matchesCount) {
+ const {
+ findField,
+ findMsg
+ } = this;
+ let findMsgId = "",
+ status = "";
+ switch (state) {
+ case FindState.FOUND:
+ break;
+ case FindState.PENDING:
+ status = "pending";
+ break;
+ case FindState.NOT_FOUND:
+ findMsgId = "pdfjs-find-not-found";
+ status = "notFound";
+ break;
+ case FindState.WRAPPED:
+ findMsgId = `pdfjs-find-reached-${previous ? "top" : "bottom"}`;
+ break;
+ }
+ findField.setAttribute("data-status", status);
+ findField.setAttribute("aria-invalid", state === FindState.NOT_FOUND);
+ findMsg.setAttribute("data-status", status);
+ if (findMsgId) {
+ findMsg.setAttribute("data-l10n-id", findMsgId);
+ } else {
+ findMsg.removeAttribute("data-l10n-id");
+ findMsg.textContent = "";
+ }
+ this.updateResultsCount(matchesCount);
+ }
+ updateResultsCount({
+ current = 0,
+ total = 0
+ } = {}) {
+ const {
+ findResultsCount
+ } = this;
+ if (total > 0) {
+ const limit = MATCHES_COUNT_LIMIT;
+ findResultsCount.setAttribute("data-l10n-id", `pdfjs-find-match-count${total > limit ? "-limit" : ""}`);
+ findResultsCount.setAttribute("data-l10n-args", JSON.stringify({
+ limit,
+ current,
+ total
+ }));
+ } else {
+ findResultsCount.removeAttribute("data-l10n-id");
+ findResultsCount.textContent = "";
+ }
+ }
+ open() {
+ if (!this.opened) {
+ this.#resizeObserver.observe(this.bar.parentNode);
+ this.#resizeObserver.observe(this.bar);
+ this.opened = true;
+ toggleExpandedBtn(this.toggleButton, true, this.bar);
+ }
+ this.findField.select();
+ this.findField.focus();
+ }
+ close() {
+ if (!this.opened) {
+ return;
+ }
+ this.#resizeObserver.disconnect();
+ this.opened = false;
+ toggleExpandedBtn(this.toggleButton, false, this.bar);
+ this.eventBus.dispatch("findbarclose", {
+ source: this
+ });
+ }
+ toggle() {
+ if (this.opened) {
+ this.close();
+ } else {
+ this.open();
+ }
+ }
+ #resizeObserverCallback(entries) {
+ const {
+ bar
+ } = this;
+ bar.classList.remove("wrapContainers");
+ const findbarHeight = bar.clientHeight;
+ const inputContainerHeight = bar.firstElementChild.clientHeight;
+ if (findbarHeight > inputContainerHeight) {
+ bar.classList.add("wrapContainers");
+ }
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_history.js
+
+
+const HASH_CHANGE_TIMEOUT = 1000;
+const POSITION_UPDATED_THRESHOLD = 50;
+const UPDATE_VIEWAREA_TIMEOUT = 1000;
+function getCurrentHash() {
+ return document.location.hash;
+}
+class PDFHistory {
+ #eventAbortController = null;
+ constructor({
+ linkService,
+ eventBus
+ }) {
+ this.linkService = linkService;
+ this.eventBus = eventBus;
+ this._initialized = false;
+ this._fingerprint = "";
+ this.reset();
+ this.eventBus._on("pagesinit", () => {
+ this._isPagesLoaded = false;
+ this.eventBus._on("pagesloaded", evt => {
+ this._isPagesLoaded = !!evt.pagesCount;
+ }, {
+ once: true
+ });
+ });
+ }
+ initialize({
+ fingerprint,
+ resetHistory = false,
+ updateUrl = false
+ }) {
+ if (!fingerprint || typeof fingerprint !== "string") {
+ console.error('PDFHistory.initialize: The "fingerprint" must be a non-empty string.');
+ return;
+ }
+ if (this._initialized) {
+ this.reset();
+ }
+ const reInitialized = this._fingerprint !== "" && this._fingerprint !== fingerprint;
+ this._fingerprint = fingerprint;
+ this._updateUrl = updateUrl === true;
+ this._initialized = true;
+ this.#bindEvents();
+ const state = window.history.state;
+ this._popStateInProgress = false;
+ this._blockHashChange = 0;
+ this._currentHash = getCurrentHash();
+ this._numPositionUpdates = 0;
+ this._uid = this._maxUid = 0;
+ this._destination = null;
+ this._position = null;
+ if (!this.#isValidState(state, true) || resetHistory) {
+ const {
+ hash,
+ page,
+ rotation
+ } = this.#parseCurrentHash(true);
+ if (!hash || reInitialized || resetHistory) {
+ this.#pushOrReplaceState(null, true);
+ return;
+ }
+ this.#pushOrReplaceState({
+ hash,
+ page,
+ rotation
+ }, true);
+ return;
+ }
+ const destination = state.destination;
+ this.#updateInternalState(destination, state.uid, true);
+ if (destination.rotation !== undefined) {
+ this._initialRotation = destination.rotation;
+ }
+ if (destination.dest) {
+ this._initialBookmark = JSON.stringify(destination.dest);
+ this._destination.page = null;
+ } else if (destination.hash) {
+ this._initialBookmark = destination.hash;
+ } else if (destination.page) {
+ this._initialBookmark = `page=${destination.page}`;
+ }
+ }
+ reset() {
+ if (this._initialized) {
+ this.#pageHide();
+ this._initialized = false;
+ this.#unbindEvents();
+ }
+ if (this._updateViewareaTimeout) {
+ clearTimeout(this._updateViewareaTimeout);
+ this._updateViewareaTimeout = null;
+ }
+ this._initialBookmark = null;
+ this._initialRotation = null;
+ }
+ push({
+ namedDest = null,
+ explicitDest,
+ pageNumber
+ }) {
+ if (!this._initialized) {
+ return;
+ }
+ if (namedDest && typeof namedDest !== "string") {
+ console.error("PDFHistory.push: " + `"${namedDest}" is not a valid namedDest parameter.`);
+ return;
+ } else if (!Array.isArray(explicitDest)) {
+ console.error("PDFHistory.push: " + `"${explicitDest}" is not a valid explicitDest parameter.`);
+ return;
+ } else if (!this.#isValidPage(pageNumber)) {
+ if (pageNumber !== null || this._destination) {
+ console.error("PDFHistory.push: " + `"${pageNumber}" is not a valid pageNumber parameter.`);
+ return;
+ }
+ }
+ const hash = namedDest || JSON.stringify(explicitDest);
+ if (!hash) {
+ return;
+ }
+ let forceReplace = false;
+ if (this._destination && (isDestHashesEqual(this._destination.hash, hash) || isDestArraysEqual(this._destination.dest, explicitDest))) {
+ if (this._destination.page) {
+ return;
+ }
+ forceReplace = true;
+ }
+ if (this._popStateInProgress && !forceReplace) {
+ return;
+ }
+ this.#pushOrReplaceState({
+ dest: explicitDest,
+ hash,
+ page: pageNumber,
+ rotation: this.linkService.rotation
+ }, forceReplace);
+ if (!this._popStateInProgress) {
+ this._popStateInProgress = true;
+ Promise.resolve().then(() => {
+ this._popStateInProgress = false;
+ });
+ }
+ }
+ pushPage(pageNumber) {
+ if (!this._initialized) {
+ return;
+ }
+ if (!this.#isValidPage(pageNumber)) {
+ console.error(`PDFHistory.pushPage: "${pageNumber}" is not a valid page number.`);
+ return;
+ }
+ if (this._destination?.page === pageNumber) {
+ return;
+ }
+ if (this._popStateInProgress) {
+ return;
+ }
+ this.#pushOrReplaceState({
+ dest: null,
+ hash: `page=${pageNumber}`,
+ page: pageNumber,
+ rotation: this.linkService.rotation
+ });
+ if (!this._popStateInProgress) {
+ this._popStateInProgress = true;
+ Promise.resolve().then(() => {
+ this._popStateInProgress = false;
+ });
+ }
+ }
+ pushCurrentPosition() {
+ if (!this._initialized || this._popStateInProgress) {
+ return;
+ }
+ this.#tryPushCurrentPosition();
+ }
+ back() {
+ if (!this._initialized || this._popStateInProgress) {
+ return;
+ }
+ const state = window.history.state;
+ if (this.#isValidState(state) && state.uid > 0) {
+ window.history.back();
+ }
+ }
+ forward() {
+ if (!this._initialized || this._popStateInProgress) {
+ return;
+ }
+ const state = window.history.state;
+ if (this.#isValidState(state) && state.uid < this._maxUid) {
+ window.history.forward();
+ }
+ }
+ get popStateInProgress() {
+ return this._initialized && (this._popStateInProgress || this._blockHashChange > 0);
+ }
+ get initialBookmark() {
+ return this._initialized ? this._initialBookmark : null;
+ }
+ get initialRotation() {
+ return this._initialized ? this._initialRotation : null;
+ }
+ #pushOrReplaceState(destination, forceReplace = false) {
+ const shouldReplace = forceReplace || !this._destination;
+ const newState = {
+ fingerprint: this._fingerprint,
+ uid: shouldReplace ? this._uid : this._uid + 1,
+ destination
+ };
+ this.#updateInternalState(destination, newState.uid);
+ let newUrl;
+ if (this._updateUrl && destination?.hash) {
+ const baseUrl = document.location.href.split("#", 1)[0];
+ if (!baseUrl.startsWith("file://")) {
+ newUrl = `${baseUrl}#${destination.hash}`;
+ }
+ }
+ if (shouldReplace) {
+ window.history.replaceState(newState, "", newUrl);
+ } else {
+ window.history.pushState(newState, "", newUrl);
+ }
+ }
+ #tryPushCurrentPosition(temporary = false) {
+ if (!this._position) {
+ return;
+ }
+ let position = this._position;
+ if (temporary) {
+ position = Object.assign(Object.create(null), this._position);
+ position.temporary = true;
+ }
+ if (!this._destination) {
+ this.#pushOrReplaceState(position);
+ return;
+ }
+ if (this._destination.temporary) {
+ this.#pushOrReplaceState(position, true);
+ return;
+ }
+ if (this._destination.hash === position.hash) {
+ return;
+ }
+ if (!this._destination.page && (POSITION_UPDATED_THRESHOLD <= 0 || this._numPositionUpdates <= POSITION_UPDATED_THRESHOLD)) {
+ return;
+ }
+ let forceReplace = false;
+ if (this._destination.page >= position.first && this._destination.page <= position.page) {
+ if (this._destination.dest !== undefined || !this._destination.first) {
+ return;
+ }
+ forceReplace = true;
+ }
+ this.#pushOrReplaceState(position, forceReplace);
+ }
+ #isValidPage(val) {
+ return Number.isInteger(val) && val > 0 && val <= this.linkService.pagesCount;
+ }
+ #isValidState(state, checkReload = false) {
+ if (!state) {
+ return false;
+ }
+ if (state.fingerprint !== this._fingerprint) {
+ if (checkReload) {
+ if (typeof state.fingerprint !== "string" || state.fingerprint.length !== this._fingerprint.length) {
+ return false;
+ }
+ const [perfEntry] = performance.getEntriesByType("navigation");
+ if (perfEntry?.type !== "reload") {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ if (!Number.isInteger(state.uid) || state.uid < 0) {
+ return false;
+ }
+ if (state.destination === null || typeof state.destination !== "object") {
+ return false;
+ }
+ return true;
+ }
+ #updateInternalState(destination, uid, removeTemporary = false) {
+ if (this._updateViewareaTimeout) {
+ clearTimeout(this._updateViewareaTimeout);
+ this._updateViewareaTimeout = null;
+ }
+ if (removeTemporary && destination?.temporary) {
+ delete destination.temporary;
+ }
+ this._destination = destination;
+ this._uid = uid;
+ this._maxUid = Math.max(this._maxUid, uid);
+ this._numPositionUpdates = 0;
+ }
+ #parseCurrentHash(checkNameddest = false) {
+ const hash = unescape(getCurrentHash()).substring(1);
+ const params = parseQueryString(hash);
+ const nameddest = params.get("nameddest") || "";
+ let page = params.get("page") | 0;
+ if (!this.#isValidPage(page) || checkNameddest && nameddest.length > 0) {
+ page = null;
+ }
+ return {
+ hash,
+ page,
+ rotation: this.linkService.rotation
+ };
+ }
+ #updateViewarea({
+ location
+ }) {
+ if (this._updateViewareaTimeout) {
+ clearTimeout(this._updateViewareaTimeout);
+ this._updateViewareaTimeout = null;
+ }
+ this._position = {
+ hash: location.pdfOpenParams.substring(1),
+ page: this.linkService.page,
+ first: location.pageNumber,
+ rotation: location.rotation
+ };
+ if (this._popStateInProgress) {
+ return;
+ }
+ if (POSITION_UPDATED_THRESHOLD > 0 && this._isPagesLoaded && this._destination && !this._destination.page) {
+ this._numPositionUpdates++;
+ }
+ if (UPDATE_VIEWAREA_TIMEOUT > 0) {
+ this._updateViewareaTimeout = setTimeout(() => {
+ if (!this._popStateInProgress) {
+ this.#tryPushCurrentPosition(true);
+ }
+ this._updateViewareaTimeout = null;
+ }, UPDATE_VIEWAREA_TIMEOUT);
+ }
+ }
+ #popState({
+ state
+ }) {
+ const newHash = getCurrentHash(),
+ hashChanged = this._currentHash !== newHash;
+ this._currentHash = newHash;
+ if (!state) {
+ this._uid++;
+ const {
+ hash,
+ page,
+ rotation
+ } = this.#parseCurrentHash();
+ this.#pushOrReplaceState({
+ hash,
+ page,
+ rotation
+ }, true);
+ return;
+ }
+ if (!this.#isValidState(state)) {
+ return;
+ }
+ this._popStateInProgress = true;
+ if (hashChanged) {
+ this._blockHashChange++;
+ waitOnEventOrTimeout({
+ target: window,
+ name: "hashchange",
+ delay: HASH_CHANGE_TIMEOUT
+ }).then(() => {
+ this._blockHashChange--;
+ });
+ }
+ const destination = state.destination;
+ this.#updateInternalState(destination, state.uid, true);
+ if (isValidRotation(destination.rotation)) {
+ this.linkService.rotation = destination.rotation;
+ }
+ if (destination.dest) {
+ this.linkService.goToDestination(destination.dest);
+ } else if (destination.hash) {
+ this.linkService.setHash(destination.hash);
+ } else if (destination.page) {
+ this.linkService.page = destination.page;
+ }
+ Promise.resolve().then(() => {
+ this._popStateInProgress = false;
+ });
+ }
+ #pageHide() {
+ if (!this._destination || this._destination.temporary) {
+ this.#tryPushCurrentPosition();
+ }
+ }
+ #bindEvents() {
+ if (this.#eventAbortController) {
+ return;
+ }
+ this.#eventAbortController = new AbortController();
+ const {
+ signal
+ } = this.#eventAbortController;
+ this.eventBus._on("updateviewarea", this.#updateViewarea.bind(this), {
+ signal
+ });
+ window.addEventListener("popstate", this.#popState.bind(this), {
+ signal
+ });
+ window.addEventListener("pagehide", this.#pageHide.bind(this), {
+ signal
+ });
+ }
+ #unbindEvents() {
+ this.#eventAbortController?.abort();
+ this.#eventAbortController = null;
+ }
+}
+function isDestHashesEqual(destHash, pushHash) {
+ if (typeof destHash !== "string" || typeof pushHash !== "string") {
+ return false;
+ }
+ if (destHash === pushHash) {
+ return true;
+ }
+ const nameddest = parseQueryString(destHash).get("nameddest");
+ if (nameddest === pushHash) {
+ return true;
+ }
+ return false;
+}
+function isDestArraysEqual(firstDest, secondDest) {
+ function isEntryEqual(first, second) {
+ if (typeof first !== typeof second) {
+ return false;
+ }
+ if (Array.isArray(first) || Array.isArray(second)) {
+ return false;
+ }
+ if (first !== null && typeof first === "object" && second !== null) {
+ if (Object.keys(first).length !== Object.keys(second).length) {
+ return false;
+ }
+ for (const key in first) {
+ if (!isEntryEqual(first[key], second[key])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return first === second || Number.isNaN(first) && Number.isNaN(second);
+ }
+ if (!(Array.isArray(firstDest) && Array.isArray(secondDest))) {
+ return false;
+ }
+ if (firstDest.length !== secondDest.length) {
+ return false;
+ }
+ for (let i = 0, ii = firstDest.length; i < ii; i++) {
+ if (!isEntryEqual(firstDest[i], secondDest[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+;// CONCATENATED MODULE: ./web/pdf_layer_viewer.js
+
+class PDFLayerViewer extends BaseTreeViewer {
+ constructor(options) {
+ super(options);
+ this.eventBus._on("optionalcontentconfigchanged", evt => {
+ this.#updateLayers(evt.promise);
+ });
+ this.eventBus._on("resetlayers", () => {
+ this.#updateLayers();
+ });
+ this.eventBus._on("togglelayerstree", this._toggleAllTreeItems.bind(this));
+ }
+ reset() {
+ super.reset();
+ this._optionalContentConfig = null;
+ this._optionalContentHash = null;
+ }
+ _dispatchEvent(layersCount) {
+ this.eventBus.dispatch("layersloaded", {
+ source: this,
+ layersCount
+ });
+ }
+ _bindLink(element, {
+ groupId,
+ input
+ }) {
+ const setVisibility = () => {
+ this._optionalContentConfig.setVisibility(groupId, input.checked);
+ this._optionalContentHash = this._optionalContentConfig.getHash();
+ this.eventBus.dispatch("optionalcontentconfig", {
+ source: this,
+ promise: Promise.resolve(this._optionalContentConfig)
+ });
+ };
+ element.onclick = evt => {
+ if (evt.target === input) {
+ setVisibility();
+ return true;
+ } else if (evt.target !== element) {
+ return true;
+ }
+ input.checked = !input.checked;
+ setVisibility();
+ return false;
+ };
+ }
+ async _setNestedName(element, {
+ name = null
+ }) {
+ if (typeof name === "string") {
+ element.textContent = this._normalizeTextContent(name);
+ return;
+ }
+ element.textContent = await this._l10n.get("pdfjs-additional-layers");
+ element.style.fontStyle = "italic";
+ }
+ _addToggleButton(div, {
+ name = null
+ }) {
+ super._addToggleButton(div, name === null);
+ }
+ _toggleAllTreeItems() {
+ if (!this._optionalContentConfig) {
+ return;
+ }
+ super._toggleAllTreeItems();
+ }
+ render({
+ optionalContentConfig,
+ pdfDocument
+ }) {
+ if (this._optionalContentConfig) {
+ this.reset();
+ }
+ this._optionalContentConfig = optionalContentConfig || null;
+ this._pdfDocument = pdfDocument || null;
+ const groups = optionalContentConfig?.getOrder();
+ if (!groups) {
+ this._dispatchEvent(0);
+ return;
+ }
+ this._optionalContentHash = optionalContentConfig.getHash();
+ const fragment = document.createDocumentFragment(),
+ queue = [{
+ parent: fragment,
+ groups
+ }];
+ let layersCount = 0,
+ hasAnyNesting = false;
+ while (queue.length > 0) {
+ const levelData = queue.shift();
+ for (const groupId of levelData.groups) {
+ const div = document.createElement("div");
+ div.className = "treeItem";
+ const element = document.createElement("a");
+ div.append(element);
+ if (typeof groupId === "object") {
+ hasAnyNesting = true;
+ this._addToggleButton(div, groupId);
+ this._setNestedName(element, groupId);
+ const itemsDiv = document.createElement("div");
+ itemsDiv.className = "treeItems";
+ div.append(itemsDiv);
+ queue.push({
+ parent: itemsDiv,
+ groups: groupId.order
+ });
+ } else {
+ const group = optionalContentConfig.getGroup(groupId);
+ const input = document.createElement("input");
+ this._bindLink(element, {
+ groupId,
+ input
+ });
+ input.type = "checkbox";
+ input.checked = group.visible;
+ const label = document.createElement("label");
+ label.textContent = this._normalizeTextContent(group.name);
+ label.append(input);
+ element.append(label);
+ layersCount++;
+ }
+ levelData.parent.append(div);
+ }
+ }
+ this._finishRendering(fragment, layersCount, hasAnyNesting);
+ }
+ async #updateLayers(promise = null) {
+ if (!this._optionalContentConfig) {
+ return;
+ }
+ const pdfDocument = this._pdfDocument;
+ const optionalContentConfig = await (promise || pdfDocument.getOptionalContentConfig({
+ intent: "display"
+ }));
+ if (pdfDocument !== this._pdfDocument) {
+ return;
+ }
+ if (promise) {
+ if (optionalContentConfig.getHash() === this._optionalContentHash) {
+ return;
+ }
+ } else {
+ this.eventBus.dispatch("optionalcontentconfig", {
+ source: this,
+ promise: Promise.resolve(optionalContentConfig)
+ });
+ }
+ this.render({
+ optionalContentConfig,
+ pdfDocument: this._pdfDocument
+ });
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_outline_viewer.js
+
+
+class PDFOutlineViewer extends BaseTreeViewer {
+ constructor(options) {
+ super(options);
+ this.linkService = options.linkService;
+ this.downloadManager = options.downloadManager;
+ this.eventBus._on("toggleoutlinetree", this._toggleAllTreeItems.bind(this));
+ this.eventBus._on("currentoutlineitem", this._currentOutlineItem.bind(this));
+ this.eventBus._on("pagechanging", evt => {
+ this._currentPageNumber = evt.pageNumber;
+ });
+ this.eventBus._on("pagesloaded", evt => {
+ this._isPagesLoaded = !!evt.pagesCount;
+ this._currentOutlineItemCapability?.resolve(this._isPagesLoaded);
+ });
+ this.eventBus._on("sidebarviewchanged", evt => {
+ this._sidebarView = evt.view;
+ });
+ }
+ reset() {
+ super.reset();
+ this._outline = null;
+ this._pageNumberToDestHashCapability = null;
+ this._currentPageNumber = 1;
+ this._isPagesLoaded = null;
+ this._currentOutlineItemCapability?.resolve(false);
+ this._currentOutlineItemCapability = null;
+ }
+ _dispatchEvent(outlineCount) {
+ this._currentOutlineItemCapability = Promise.withResolvers();
+ if (outlineCount === 0 || this._pdfDocument?.loadingParams.disableAutoFetch) {
+ this._currentOutlineItemCapability.resolve(false);
+ } else if (this._isPagesLoaded !== null) {
+ this._currentOutlineItemCapability.resolve(this._isPagesLoaded);
+ }
+ this.eventBus.dispatch("outlineloaded", {
+ source: this,
+ outlineCount,
+ currentOutlineItemPromise: this._currentOutlineItemCapability.promise
+ });
+ }
+ _bindLink(element, {
+ url,
+ newWindow,
+ action,
+ attachment,
+ dest,
+ setOCGState
+ }) {
+ const {
+ linkService
+ } = this;
+ if (url) {
+ linkService.addLinkAttributes(element, url, newWindow);
+ return;
+ }
+ if (action) {
+ element.href = linkService.getAnchorUrl("");
+ element.onclick = () => {
+ linkService.executeNamedAction(action);
+ return false;
+ };
+ return;
+ }
+ if (attachment) {
+ element.href = linkService.getAnchorUrl("");
+ element.onclick = () => {
+ this.downloadManager.openOrDownloadData(attachment.content, attachment.filename);
+ return false;
+ };
+ return;
+ }
+ if (setOCGState) {
+ element.href = linkService.getAnchorUrl("");
+ element.onclick = () => {
+ linkService.executeSetOCGState(setOCGState);
+ return false;
+ };
+ return;
+ }
+ element.href = linkService.getDestinationHash(dest);
+ element.onclick = evt => {
+ this._updateCurrentTreeItem(evt.target.parentNode);
+ if (dest) {
+ linkService.goToDestination(dest);
+ }
+ return false;
+ };
+ }
+ _setStyles(element, {
+ bold,
+ italic
+ }) {
+ if (bold) {
+ element.style.fontWeight = "bold";
+ }
+ if (italic) {
+ element.style.fontStyle = "italic";
+ }
+ }
+ _addToggleButton(div, {
+ count,
+ items
+ }) {
+ let hidden = false;
+ if (count < 0) {
+ let totalCount = items.length;
+ if (totalCount > 0) {
+ const queue = [...items];
+ while (queue.length > 0) {
+ const {
+ count: nestedCount,
+ items: nestedItems
+ } = queue.shift();
+ if (nestedCount > 0 && nestedItems.length > 0) {
+ totalCount += nestedItems.length;
+ queue.push(...nestedItems);
+ }
+ }
+ }
+ if (Math.abs(count) === totalCount) {
+ hidden = true;
+ }
+ }
+ super._addToggleButton(div, hidden);
+ }
+ _toggleAllTreeItems() {
+ if (!this._outline) {
+ return;
+ }
+ super._toggleAllTreeItems();
+ }
+ render({
+ outline,
+ pdfDocument
+ }) {
+ if (this._outline) {
+ this.reset();
+ }
+ this._outline = outline || null;
+ this._pdfDocument = pdfDocument || null;
+ if (!outline) {
+ this._dispatchEvent(0);
+ return;
+ }
+ const fragment = document.createDocumentFragment();
+ const queue = [{
+ parent: fragment,
+ items: outline
+ }];
+ let outlineCount = 0,
+ hasAnyNesting = false;
+ while (queue.length > 0) {
+ const levelData = queue.shift();
+ for (const item of levelData.items) {
+ const div = document.createElement("div");
+ div.className = "treeItem";
+ const element = document.createElement("a");
+ this._bindLink(element, item);
+ this._setStyles(element, item);
+ element.textContent = this._normalizeTextContent(item.title);
+ div.append(element);
+ if (item.items.length > 0) {
+ hasAnyNesting = true;
+ this._addToggleButton(div, item);
+ const itemsDiv = document.createElement("div");
+ itemsDiv.className = "treeItems";
+ div.append(itemsDiv);
+ queue.push({
+ parent: itemsDiv,
+ items: item.items
+ });
+ }
+ levelData.parent.append(div);
+ outlineCount++;
+ }
+ }
+ this._finishRendering(fragment, outlineCount, hasAnyNesting);
+ }
+ async _currentOutlineItem() {
+ if (!this._isPagesLoaded) {
+ throw new Error("_currentOutlineItem: All pages have not been loaded.");
+ }
+ if (!this._outline || !this._pdfDocument) {
+ return;
+ }
+ const pageNumberToDestHash = await this._getPageNumberToDestHash(this._pdfDocument);
+ if (!pageNumberToDestHash) {
+ return;
+ }
+ this._updateCurrentTreeItem(null);
+ if (this._sidebarView !== SidebarView.OUTLINE) {
+ return;
+ }
+ for (let i = this._currentPageNumber; i > 0; i--) {
+ const destHash = pageNumberToDestHash.get(i);
+ if (!destHash) {
+ continue;
+ }
+ const linkElement = this.container.querySelector(`a[href="${destHash}"]`);
+ if (!linkElement) {
+ continue;
+ }
+ this._scrollToCurrentTreeItem(linkElement.parentNode);
+ break;
+ }
+ }
+ async _getPageNumberToDestHash(pdfDocument) {
+ if (this._pageNumberToDestHashCapability) {
+ return this._pageNumberToDestHashCapability.promise;
+ }
+ this._pageNumberToDestHashCapability = Promise.withResolvers();
+ const pageNumberToDestHash = new Map(),
+ pageNumberNesting = new Map();
+ const queue = [{
+ nesting: 0,
+ items: this._outline
+ }];
+ while (queue.length > 0) {
+ const levelData = queue.shift(),
+ currentNesting = levelData.nesting;
+ for (const {
+ dest,
+ items
+ } of levelData.items) {
+ let explicitDest, pageNumber;
+ if (typeof dest === "string") {
+ explicitDest = await pdfDocument.getDestination(dest);
+ if (pdfDocument !== this._pdfDocument) {
+ return null;
+ }
+ } else {
+ explicitDest = dest;
+ }
+ if (Array.isArray(explicitDest)) {
+ const [destRef] = explicitDest;
+ if (destRef && typeof destRef === "object") {
+ pageNumber = pdfDocument.cachedPageNumber(destRef);
+ } else if (Number.isInteger(destRef)) {
+ pageNumber = destRef + 1;
+ }
+ if (Number.isInteger(pageNumber) && (!pageNumberToDestHash.has(pageNumber) || currentNesting > pageNumberNesting.get(pageNumber))) {
+ const destHash = this.linkService.getDestinationHash(dest);
+ pageNumberToDestHash.set(pageNumber, destHash);
+ pageNumberNesting.set(pageNumber, currentNesting);
+ }
+ }
+ if (items.length > 0) {
+ queue.push({
+ nesting: currentNesting + 1,
+ items
+ });
+ }
+ }
+ }
+ this._pageNumberToDestHashCapability.resolve(pageNumberToDestHash.size > 0 ? pageNumberToDestHash : null);
+ return this._pageNumberToDestHashCapability.promise;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_presentation_mode.js
+
+
+const DELAY_BEFORE_HIDING_CONTROLS = 3000;
+const ACTIVE_SELECTOR = "pdfPresentationMode";
+const CONTROLS_SELECTOR = "pdfPresentationModeControls";
+const MOUSE_SCROLL_COOLDOWN_TIME = 50;
+const PAGE_SWITCH_THRESHOLD = 0.1;
+const SWIPE_MIN_DISTANCE_THRESHOLD = 50;
+const SWIPE_ANGLE_THRESHOLD = Math.PI / 6;
+class PDFPresentationMode {
+ #state = PresentationModeState.UNKNOWN;
+ #args = null;
+ #fullscreenChangeAbortController = null;
+ #windowAbortController = null;
+ constructor({
+ container,
+ pdfViewer,
+ eventBus
+ }) {
+ this.container = container;
+ this.pdfViewer = pdfViewer;
+ this.eventBus = eventBus;
+ this.contextMenuOpen = false;
+ this.mouseScrollTimeStamp = 0;
+ this.mouseScrollDelta = 0;
+ this.touchSwipeState = null;
+ }
+ async request() {
+ const {
+ container,
+ pdfViewer
+ } = this;
+ if (this.active || !pdfViewer.pagesCount || !container.requestFullscreen) {
+ return false;
+ }
+ this.#addFullscreenChangeListeners();
+ this.#notifyStateChange(PresentationModeState.CHANGING);
+ const promise = container.requestFullscreen();
+ this.#args = {
+ pageNumber: pdfViewer.currentPageNumber,
+ scaleValue: pdfViewer.currentScaleValue,
+ scrollMode: pdfViewer.scrollMode,
+ spreadMode: null,
+ annotationEditorMode: null
+ };
+ if (pdfViewer.spreadMode !== SpreadMode.NONE && !(pdfViewer.pageViewsReady && pdfViewer.hasEqualPageSizes)) {
+ console.warn("Ignoring Spread modes when entering PresentationMode, " + "since the document may contain varying page sizes.");
+ this.#args.spreadMode = pdfViewer.spreadMode;
+ }
+ if (pdfViewer.annotationEditorMode !== AnnotationEditorType.DISABLE) {
+ this.#args.annotationEditorMode = pdfViewer.annotationEditorMode;
+ }
+ try {
+ await promise;
+ pdfViewer.focus();
+ return true;
+ } catch {
+ this.#removeFullscreenChangeListeners();
+ this.#notifyStateChange(PresentationModeState.NORMAL);
+ }
+ return false;
+ }
+ get active() {
+ return this.#state === PresentationModeState.CHANGING || this.#state === PresentationModeState.FULLSCREEN;
+ }
+ #mouseWheel(evt) {
+ if (!this.active) {
+ return;
+ }
+ evt.preventDefault();
+ const delta = normalizeWheelEventDelta(evt);
+ const currentTime = Date.now();
+ const storedTime = this.mouseScrollTimeStamp;
+ if (currentTime > storedTime && currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
+ return;
+ }
+ if (this.mouseScrollDelta > 0 && delta < 0 || this.mouseScrollDelta < 0 && delta > 0) {
+ this.#resetMouseScrollState();
+ }
+ this.mouseScrollDelta += delta;
+ if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) {
+ const totalDelta = this.mouseScrollDelta;
+ this.#resetMouseScrollState();
+ const success = totalDelta > 0 ? this.pdfViewer.previousPage() : this.pdfViewer.nextPage();
+ if (success) {
+ this.mouseScrollTimeStamp = currentTime;
+ }
+ }
+ }
+ #notifyStateChange(state) {
+ this.#state = state;
+ this.eventBus.dispatch("presentationmodechanged", {
+ source: this,
+ state
+ });
+ }
+ #enter() {
+ this.#notifyStateChange(PresentationModeState.FULLSCREEN);
+ this.container.classList.add(ACTIVE_SELECTOR);
+ setTimeout(() => {
+ this.pdfViewer.scrollMode = ScrollMode.PAGE;
+ if (this.#args.spreadMode !== null) {
+ this.pdfViewer.spreadMode = SpreadMode.NONE;
+ }
+ this.pdfViewer.currentPageNumber = this.#args.pageNumber;
+ this.pdfViewer.currentScaleValue = "page-fit";
+ if (this.#args.annotationEditorMode !== null) {
+ this.pdfViewer.annotationEditorMode = {
+ mode: AnnotationEditorType.NONE
+ };
+ }
+ }, 0);
+ this.#addWindowListeners();
+ this.#showControls();
+ this.contextMenuOpen = false;
+ document.getSelection().empty();
+ }
+ #exit() {
+ const pageNumber = this.pdfViewer.currentPageNumber;
+ this.container.classList.remove(ACTIVE_SELECTOR);
+ setTimeout(() => {
+ this.#removeFullscreenChangeListeners();
+ this.#notifyStateChange(PresentationModeState.NORMAL);
+ this.pdfViewer.scrollMode = this.#args.scrollMode;
+ if (this.#args.spreadMode !== null) {
+ this.pdfViewer.spreadMode = this.#args.spreadMode;
+ }
+ this.pdfViewer.currentScaleValue = this.#args.scaleValue;
+ this.pdfViewer.currentPageNumber = pageNumber;
+ if (this.#args.annotationEditorMode !== null) {
+ this.pdfViewer.annotationEditorMode = {
+ mode: this.#args.annotationEditorMode
+ };
+ }
+ this.#args = null;
+ }, 0);
+ this.#removeWindowListeners();
+ this.#hideControls();
+ this.#resetMouseScrollState();
+ this.contextMenuOpen = false;
+ }
+ #mouseDown(evt) {
+ if (this.contextMenuOpen) {
+ this.contextMenuOpen = false;
+ evt.preventDefault();
+ return;
+ }
+ if (evt.button !== 0) {
+ return;
+ }
+ if (evt.target.href && evt.target.parentNode?.hasAttribute("data-internal-link")) {
+ return;
+ }
+ evt.preventDefault();
+ if (evt.shiftKey) {
+ this.pdfViewer.previousPage();
+ } else {
+ this.pdfViewer.nextPage();
+ }
+ }
+ #contextMenu() {
+ this.contextMenuOpen = true;
+ }
+ #showControls() {
+ if (this.controlsTimeout) {
+ clearTimeout(this.controlsTimeout);
+ } else {
+ this.container.classList.add(CONTROLS_SELECTOR);
+ }
+ this.controlsTimeout = setTimeout(() => {
+ this.container.classList.remove(CONTROLS_SELECTOR);
+ delete this.controlsTimeout;
+ }, DELAY_BEFORE_HIDING_CONTROLS);
+ }
+ #hideControls() {
+ if (!this.controlsTimeout) {
+ return;
+ }
+ clearTimeout(this.controlsTimeout);
+ this.container.classList.remove(CONTROLS_SELECTOR);
+ delete this.controlsTimeout;
+ }
+ #resetMouseScrollState() {
+ this.mouseScrollTimeStamp = 0;
+ this.mouseScrollDelta = 0;
+ }
+ #touchSwipe(evt) {
+ if (!this.active) {
+ return;
+ }
+ if (evt.touches.length > 1) {
+ this.touchSwipeState = null;
+ return;
+ }
+ switch (evt.type) {
+ case "touchstart":
+ this.touchSwipeState = {
+ startX: evt.touches[0].pageX,
+ startY: evt.touches[0].pageY,
+ endX: evt.touches[0].pageX,
+ endY: evt.touches[0].pageY
+ };
+ break;
+ case "touchmove":
+ if (this.touchSwipeState === null) {
+ return;
+ }
+ this.touchSwipeState.endX = evt.touches[0].pageX;
+ this.touchSwipeState.endY = evt.touches[0].pageY;
+ evt.preventDefault();
+ break;
+ case "touchend":
+ if (this.touchSwipeState === null) {
+ return;
+ }
+ let delta = 0;
+ const dx = this.touchSwipeState.endX - this.touchSwipeState.startX;
+ const dy = this.touchSwipeState.endY - this.touchSwipeState.startY;
+ const absAngle = Math.abs(Math.atan2(dy, dx));
+ if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && (absAngle <= SWIPE_ANGLE_THRESHOLD || absAngle >= Math.PI - SWIPE_ANGLE_THRESHOLD)) {
+ delta = dx;
+ } else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && Math.abs(absAngle - Math.PI / 2) <= SWIPE_ANGLE_THRESHOLD) {
+ delta = dy;
+ }
+ if (delta > 0) {
+ this.pdfViewer.previousPage();
+ } else if (delta < 0) {
+ this.pdfViewer.nextPage();
+ }
+ break;
+ }
+ }
+ #addWindowListeners() {
+ if (this.#windowAbortController) {
+ return;
+ }
+ this.#windowAbortController = new AbortController();
+ const {
+ signal
+ } = this.#windowAbortController;
+ const touchSwipeBind = this.#touchSwipe.bind(this);
+ window.addEventListener("mousemove", this.#showControls.bind(this), {
+ signal
+ });
+ window.addEventListener("mousedown", this.#mouseDown.bind(this), {
+ signal
+ });
+ window.addEventListener("wheel", this.#mouseWheel.bind(this), {
+ passive: false,
+ signal
+ });
+ window.addEventListener("keydown", this.#resetMouseScrollState.bind(this), {
+ signal
+ });
+ window.addEventListener("contextmenu", this.#contextMenu.bind(this), {
+ signal
+ });
+ window.addEventListener("touchstart", touchSwipeBind, {
+ signal
+ });
+ window.addEventListener("touchmove", touchSwipeBind, {
+ signal
+ });
+ window.addEventListener("touchend", touchSwipeBind, {
+ signal
+ });
+ }
+ #removeWindowListeners() {
+ this.#windowAbortController?.abort();
+ this.#windowAbortController = null;
+ }
+ #addFullscreenChangeListeners() {
+ if (this.#fullscreenChangeAbortController) {
+ return;
+ }
+ this.#fullscreenChangeAbortController = new AbortController();
+ window.addEventListener("fullscreenchange", () => {
+ if (document.fullscreenElement) {
+ this.#enter();
+ } else {
+ this.#exit();
+ }
+ }, {
+ signal: this.#fullscreenChangeAbortController.signal
+ });
+ }
+ #removeFullscreenChangeListeners() {
+ this.#fullscreenChangeAbortController?.abort();
+ this.#fullscreenChangeAbortController = null;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/xfa_layer_builder.js
+
+class XfaLayerBuilder {
+ constructor({
+ pdfPage,
+ annotationStorage = null,
+ linkService,
+ xfaHtml = null
+ }) {
+ this.pdfPage = pdfPage;
+ this.annotationStorage = annotationStorage;
+ this.linkService = linkService;
+ this.xfaHtml = xfaHtml;
+ this.div = null;
+ this._cancelled = false;
+ }
+ async render(viewport, intent = "display") {
+ if (intent === "print") {
+ const parameters = {
+ viewport: viewport.clone({
+ dontFlip: true
+ }),
+ div: this.div,
+ xfaHtml: this.xfaHtml,
+ annotationStorage: this.annotationStorage,
+ linkService: this.linkService,
+ intent
+ };
+ this.div = document.createElement("div");
+ parameters.div = this.div;
+ return XfaLayer.render(parameters);
+ }
+ const xfaHtml = await this.pdfPage.getXfa();
+ if (this._cancelled || !xfaHtml) {
+ return {
+ textDivs: []
+ };
+ }
+ const parameters = {
+ viewport: viewport.clone({
+ dontFlip: true
+ }),
+ div: this.div,
+ xfaHtml,
+ annotationStorage: this.annotationStorage,
+ linkService: this.linkService,
+ intent
+ };
+ if (this.div) {
+ return XfaLayer.update(parameters);
+ }
+ this.div = document.createElement("div");
+ parameters.div = this.div;
+ return XfaLayer.render(parameters);
+ }
+ cancel() {
+ this._cancelled = true;
+ }
+ hide() {
+ if (!this.div) {
+ return;
+ }
+ this.div.hidden = true;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/print_utils.js
+
+
+
+function getXfaHtmlForPrinting(printContainer, pdfDocument) {
+ const xfaHtml = pdfDocument.allXfaHtml;
+ const linkService = new SimpleLinkService();
+ const scale = Math.round(PixelsPerInch.PDF_TO_CSS_UNITS * 100) / 100;
+ for (const xfaPage of xfaHtml.children) {
+ const page = document.createElement("div");
+ page.className = "xfaPrintedPage";
+ printContainer.append(page);
+ const builder = new XfaLayerBuilder({
+ pdfPage: null,
+ annotationStorage: pdfDocument.annotationStorage,
+ linkService,
+ xfaHtml: xfaPage
+ });
+ const viewport = getXfaPageViewport(xfaPage, {
+ scale
+ });
+ builder.render(viewport, "print");
+ page.append(builder.div);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_print_service.js
+
+
+let activeService = null;
+let dialog = null;
+let overlayManager = null;
+let viewerApp = {
+ initialized: false
+};
+function renderPage(activeServiceOnEntry, pdfDocument, pageNumber, size, printResolution, optionalContentConfigPromise, printAnnotationStoragePromise) {
+ const scratchCanvas = activeService.scratchCanvas;
+ const PRINT_UNITS = printResolution / PixelsPerInch.PDF;
+ scratchCanvas.width = Math.floor(size.width * PRINT_UNITS);
+ scratchCanvas.height = Math.floor(size.height * PRINT_UNITS);
+ const ctx = scratchCanvas.getContext("2d");
+ ctx.save();
+ ctx.fillStyle = "rgb(255, 255, 255)";
+ ctx.fillRect(0, 0, scratchCanvas.width, scratchCanvas.height);
+ ctx.restore();
+ return Promise.all([pdfDocument.getPage(pageNumber), printAnnotationStoragePromise]).then(function ([pdfPage, printAnnotationStorage]) {
+ const renderContext = {
+ canvasContext: ctx,
+ transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
+ viewport: pdfPage.getViewport({
+ scale: 1,
+ rotation: size.rotation
+ }),
+ intent: "print",
+ annotationMode: AnnotationMode.ENABLE_STORAGE,
+ optionalContentConfigPromise,
+ printAnnotationStorage
+ };
+ const renderTask = pdfPage.render(renderContext);
+ return renderTask.promise.catch(reason => {
+ if (!(reason instanceof RenderingCancelledException)) {
+ console.error(reason);
+ }
+ throw reason;
+ });
+ });
+}
+class PDFPrintService {
+ constructor({
+ pdfDocument,
+ pagesOverview,
+ printContainer,
+ printResolution,
+ printAnnotationStoragePromise = null
+ }) {
+ this.pdfDocument = pdfDocument;
+ this.pagesOverview = pagesOverview;
+ this.printContainer = printContainer;
+ this._printResolution = printResolution || 150;
+ this._optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
+ intent: "print"
+ });
+ this._printAnnotationStoragePromise = printAnnotationStoragePromise || Promise.resolve();
+ this.currentPage = -1;
+ this.scratchCanvas = document.createElement("canvas");
+ }
+ layout() {
+ this.throwIfInactive();
+ const body = document.querySelector("body");
+ body.setAttribute("data-pdfjsprinting", true);
+ const {
+ width,
+ height
+ } = this.pagesOverview[0];
+ const hasEqualPageSizes = this.pagesOverview.every(size => size.width === width && size.height === height);
+ if (!hasEqualPageSizes) {
+ console.warn("Not all pages have the same size. The printed result may be incorrect!");
+ }
+ this.pageStyleSheet = document.createElement("style");
+ this.pageStyleSheet.textContent = `@page { size: ${width}pt ${height}pt;}`;
+ body.append(this.pageStyleSheet);
+ }
+ destroy() {
+ if (activeService !== this) {
+ return;
+ }
+ this.printContainer.textContent = "";
+ const body = document.querySelector("body");
+ body.removeAttribute("data-pdfjsprinting");
+ if (this.pageStyleSheet) {
+ this.pageStyleSheet.remove();
+ this.pageStyleSheet = null;
+ }
+ this.scratchCanvas.width = this.scratchCanvas.height = 0;
+ this.scratchCanvas = null;
+ activeService = null;
+ ensureOverlay().then(function () {
+ if (overlayManager.active === dialog) {
+ overlayManager.close(dialog);
+ }
+ });
+ }
+ renderPages() {
+ if (this.pdfDocument.isPureXfa) {
+ getXfaHtmlForPrinting(this.printContainer, this.pdfDocument);
+ return Promise.resolve();
+ }
+ const pageCount = this.pagesOverview.length;
+ const renderNextPage = (resolve, reject) => {
+ this.throwIfInactive();
+ if (++this.currentPage >= pageCount) {
+ renderProgress(pageCount, pageCount);
+ resolve();
+ return;
+ }
+ const index = this.currentPage;
+ renderProgress(index, pageCount);
+ renderPage(this, this.pdfDocument, index + 1, this.pagesOverview[index], this._printResolution, this._optionalContentConfigPromise, this._printAnnotationStoragePromise).then(this.useRenderedPage.bind(this)).then(function () {
+ renderNextPage(resolve, reject);
+ }, reject);
+ };
+ return new Promise(renderNextPage);
+ }
+ useRenderedPage() {
+ this.throwIfInactive();
+ const img = document.createElement("img");
+ const scratchCanvas = this.scratchCanvas;
+ if ("toBlob" in scratchCanvas) {
+ scratchCanvas.toBlob(function (blob) {
+ img.src = URL.createObjectURL(blob);
+ });
+ } else {
+ img.src = scratchCanvas.toDataURL();
+ }
+ const wrapper = document.createElement("div");
+ wrapper.className = "printedPage";
+ wrapper.append(img);
+ this.printContainer.append(wrapper);
+ return new Promise(function (resolve, reject) {
+ img.onload = resolve;
+ img.onerror = reject;
+ });
+ }
+ performPrint() {
+ this.throwIfInactive();
+ return new Promise(resolve => {
+ setTimeout(() => {
+ if (!this.active) {
+ resolve();
+ return;
+ }
+ print.call(window);
+ setTimeout(resolve, 20);
+ }, 0);
+ });
+ }
+ get active() {
+ return this === activeService;
+ }
+ throwIfInactive() {
+ if (!this.active) {
+ throw new Error("This print request was cancelled or completed.");
+ }
+ }
+}
+const print = window.print;
+window.print = function () {
+ if (activeService) {
+ console.warn("Ignored window.print() because of a pending print job.");
+ return;
+ }
+ ensureOverlay().then(function () {
+ if (activeService) {
+ overlayManager.open(dialog);
+ }
+ });
+ try {
+ dispatchEvent("beforeprint");
+ } finally {
+ if (!activeService) {
+ console.error("Expected print service to be initialized.");
+ ensureOverlay().then(function () {
+ if (overlayManager.active === dialog) {
+ overlayManager.close(dialog);
+ }
+ });
+ return;
+ }
+ const activeServiceOnEntry = activeService;
+ activeService.renderPages().then(function () {
+ return activeServiceOnEntry.performPrint();
+ }).catch(function () {}).then(function () {
+ if (activeServiceOnEntry.active) {
+ abort();
+ }
+ });
+ }
+};
+function dispatchEvent(eventType) {
+ const event = new CustomEvent(eventType, {
+ bubbles: false,
+ cancelable: false,
+ detail: "custom"
+ });
+ window.dispatchEvent(event);
+}
+function abort() {
+ if (activeService) {
+ activeService.destroy();
+ dispatchEvent("afterprint");
+ }
+}
+function renderProgress(index, total) {
+ dialog ||= document.getElementById("printServiceDialog");
+ const progress = Math.round(100 * index / total);
+ const progressBar = dialog.querySelector("progress");
+ const progressPerc = dialog.querySelector(".relative-progress");
+ progressBar.value = progress;
+ progressPerc.setAttribute("data-l10n-args", JSON.stringify({
+ progress
+ }));
+}
+window.addEventListener("keydown", function (event) {
+ if (event.keyCode === 80 && (event.ctrlKey || event.metaKey) && !event.altKey && (!event.shiftKey || window.chrome || window.opera)) {
+ window.print();
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+}, true);
+if ("onbeforeprint" in window) {
+ const stopPropagationIfNeeded = function (event) {
+ if (event.detail !== "custom") {
+ event.stopImmediatePropagation();
+ }
+ };
+ window.addEventListener("beforeprint", stopPropagationIfNeeded);
+ window.addEventListener("afterprint", stopPropagationIfNeeded);
+}
+let overlayPromise;
+function ensureOverlay() {
+ if (!overlayPromise) {
+ overlayManager = viewerApp.overlayManager;
+ if (!overlayManager) {
+ throw new Error("The overlay manager has not yet been initialized.");
+ }
+ dialog ||= document.getElementById("printServiceDialog");
+ overlayPromise = overlayManager.register(dialog, true);
+ document.getElementById("printCancel").onclick = abort;
+ dialog.addEventListener("close", abort);
+ }
+ return overlayPromise;
+}
+class PDFPrintServiceFactory {
+ static initGlobals(app) {
+ viewerApp = app;
+ }
+ static get supportsPrinting() {
+ return shadow(this, "supportsPrinting", true);
+ }
+ static createPrintService(params) {
+ if (activeService) {
+ throw new Error("The print service is created and active.");
+ }
+ return activeService = new PDFPrintService(params);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_rendering_queue.js
+
+
+const CLEANUP_TIMEOUT = 30000;
+class PDFRenderingQueue {
+ constructor() {
+ this.pdfViewer = null;
+ this.pdfThumbnailViewer = null;
+ this.onIdle = null;
+ this.highestPriorityPage = null;
+ this.idleTimeout = null;
+ this.printing = false;
+ this.isThumbnailViewEnabled = false;
+ Object.defineProperty(this, "hasViewer", {
+ value: () => !!this.pdfViewer
+ });
+ }
+ setViewer(pdfViewer) {
+ this.pdfViewer = pdfViewer;
+ }
+ setThumbnailViewer(pdfThumbnailViewer) {
+ this.pdfThumbnailViewer = pdfThumbnailViewer;
+ }
+ isHighestPriority(view) {
+ return this.highestPriorityPage === view.renderingId;
+ }
+ renderHighestPriority(currentlyVisiblePages) {
+ if (this.idleTimeout) {
+ clearTimeout(this.idleTimeout);
+ this.idleTimeout = null;
+ }
+ if (this.pdfViewer.forceRendering(currentlyVisiblePages)) {
+ return;
+ }
+ if (this.isThumbnailViewEnabled && this.pdfThumbnailViewer?.forceRendering()) {
+ return;
+ }
+ if (this.printing) {
+ return;
+ }
+ if (this.onIdle) {
+ this.idleTimeout = setTimeout(this.onIdle.bind(this), CLEANUP_TIMEOUT);
+ }
+ }
+ getHighestPriority(visible, views, scrolledDown, preRenderExtra = false) {
+ const visibleViews = visible.views,
+ numVisible = visibleViews.length;
+ if (numVisible === 0) {
+ return null;
+ }
+ for (let i = 0; i < numVisible; i++) {
+ const view = visibleViews[i].view;
+ if (!this.isViewFinished(view)) {
+ return view;
+ }
+ }
+ const firstId = visible.first.id,
+ lastId = visible.last.id;
+ if (lastId - firstId + 1 > numVisible) {
+ const visibleIds = visible.ids;
+ for (let i = 1, ii = lastId - firstId; i < ii; i++) {
+ const holeId = scrolledDown ? firstId + i : lastId - i;
+ if (visibleIds.has(holeId)) {
+ continue;
+ }
+ const holeView = views[holeId - 1];
+ if (!this.isViewFinished(holeView)) {
+ return holeView;
+ }
+ }
+ }
+ let preRenderIndex = scrolledDown ? lastId : firstId - 2;
+ let preRenderView = views[preRenderIndex];
+ if (preRenderView && !this.isViewFinished(preRenderView)) {
+ return preRenderView;
+ }
+ if (preRenderExtra) {
+ preRenderIndex += scrolledDown ? 1 : -1;
+ preRenderView = views[preRenderIndex];
+ if (preRenderView && !this.isViewFinished(preRenderView)) {
+ return preRenderView;
+ }
+ }
+ return null;
+ }
+ isViewFinished(view) {
+ return view.renderingState === RenderingStates.FINISHED;
+ }
+ renderView(view) {
+ switch (view.renderingState) {
+ case RenderingStates.FINISHED:
+ return false;
+ case RenderingStates.PAUSED:
+ this.highestPriorityPage = view.renderingId;
+ view.resume();
+ break;
+ case RenderingStates.RUNNING:
+ this.highestPriorityPage = view.renderingId;
+ break;
+ case RenderingStates.INITIAL:
+ this.highestPriorityPage = view.renderingId;
+ view.draw().finally(() => {
+ this.renderHighestPriority();
+ }).catch(reason => {
+ if (reason instanceof RenderingCancelledException) {
+ return;
+ }
+ console.error(`renderView: "${reason}"`);
+ });
+ break;
+ }
+ return true;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_scripting_manager.js
+
+
+class PDFScriptingManager {
+ #closeCapability = null;
+ #destroyCapability = null;
+ #docProperties = null;
+ #eventAbortController = null;
+ #eventBus = null;
+ #externalServices = null;
+ #pdfDocument = null;
+ #pdfViewer = null;
+ #ready = false;
+ #scripting = null;
+ #willPrintCapability = null;
+ constructor({
+ eventBus,
+ externalServices = null,
+ docProperties = null
+ }) {
+ this.#eventBus = eventBus;
+ this.#externalServices = externalServices;
+ this.#docProperties = docProperties;
+ }
+ setViewer(pdfViewer) {
+ this.#pdfViewer = pdfViewer;
+ }
+ async setDocument(pdfDocument) {
+ if (this.#pdfDocument) {
+ await this.#destroyScripting();
+ }
+ this.#pdfDocument = pdfDocument;
+ if (!pdfDocument) {
+ return;
+ }
+ const [objects, calculationOrder, docActions] = await Promise.all([pdfDocument.getFieldObjects(), pdfDocument.getCalculationOrderIds(), pdfDocument.getJSActions()]);
+ if (!objects && !docActions) {
+ await this.#destroyScripting();
+ return;
+ }
+ if (pdfDocument !== this.#pdfDocument) {
+ return;
+ }
+ try {
+ this.#scripting = this.#initScripting();
+ } catch (error) {
+ console.error(`setDocument: "${error.message}".`);
+ await this.#destroyScripting();
+ return;
+ }
+ const eventBus = this.#eventBus;
+ this.#eventAbortController = new AbortController();
+ const {
+ signal
+ } = this.#eventAbortController;
+ eventBus._on("updatefromsandbox", event => {
+ if (event?.source === window) {
+ this.#updateFromSandbox(event.detail);
+ }
+ }, {
+ signal
+ });
+ eventBus._on("dispatcheventinsandbox", event => {
+ this.#scripting?.dispatchEventInSandbox(event.detail);
+ }, {
+ signal
+ });
+ eventBus._on("pagechanging", ({
+ pageNumber,
+ previous
+ }) => {
+ if (pageNumber === previous) {
+ return;
+ }
+ this.#dispatchPageClose(previous);
+ this.#dispatchPageOpen(pageNumber);
+ }, {
+ signal
+ });
+ eventBus._on("pagerendered", ({
+ pageNumber
+ }) => {
+ if (!this._pageOpenPending.has(pageNumber)) {
+ return;
+ }
+ if (pageNumber !== this.#pdfViewer.currentPageNumber) {
+ return;
+ }
+ this.#dispatchPageOpen(pageNumber);
+ }, {
+ signal
+ });
+ eventBus._on("pagesdestroy", async () => {
+ await this.#dispatchPageClose(this.#pdfViewer.currentPageNumber);
+ await this.#scripting?.dispatchEventInSandbox({
+ id: "doc",
+ name: "WillClose"
+ });
+ this.#closeCapability?.resolve();
+ }, {
+ signal
+ });
+ try {
+ const docProperties = await this.#docProperties(pdfDocument);
+ if (pdfDocument !== this.#pdfDocument) {
+ return;
+ }
+ await this.#scripting.createSandbox({
+ objects,
+ calculationOrder,
+ appInfo: {
+ platform: navigator.platform,
+ language: navigator.language
+ },
+ docInfo: {
+ ...docProperties,
+ actions: docActions
+ }
+ });
+ eventBus.dispatch("sandboxcreated", {
+ source: this
+ });
+ } catch (error) {
+ console.error(`setDocument: "${error.message}".`);
+ await this.#destroyScripting();
+ return;
+ }
+ await this.#scripting?.dispatchEventInSandbox({
+ id: "doc",
+ name: "Open"
+ });
+ await this.#dispatchPageOpen(this.#pdfViewer.currentPageNumber, true);
+ Promise.resolve().then(() => {
+ if (pdfDocument === this.#pdfDocument) {
+ this.#ready = true;
+ }
+ });
+ }
+ async dispatchWillSave() {
+ return this.#scripting?.dispatchEventInSandbox({
+ id: "doc",
+ name: "WillSave"
+ });
+ }
+ async dispatchDidSave() {
+ return this.#scripting?.dispatchEventInSandbox({
+ id: "doc",
+ name: "DidSave"
+ });
+ }
+ async dispatchWillPrint() {
+ if (!this.#scripting) {
+ return;
+ }
+ await this.#willPrintCapability?.promise;
+ this.#willPrintCapability = Promise.withResolvers();
+ try {
+ await this.#scripting.dispatchEventInSandbox({
+ id: "doc",
+ name: "WillPrint"
+ });
+ } catch (ex) {
+ this.#willPrintCapability.resolve();
+ this.#willPrintCapability = null;
+ throw ex;
+ }
+ await this.#willPrintCapability.promise;
+ }
+ async dispatchDidPrint() {
+ return this.#scripting?.dispatchEventInSandbox({
+ id: "doc",
+ name: "DidPrint"
+ });
+ }
+ get destroyPromise() {
+ return this.#destroyCapability?.promise || null;
+ }
+ get ready() {
+ return this.#ready;
+ }
+ get _pageOpenPending() {
+ return shadow(this, "_pageOpenPending", new Set());
+ }
+ get _visitedPages() {
+ return shadow(this, "_visitedPages", new Map());
+ }
+ async #updateFromSandbox(detail) {
+ const pdfViewer = this.#pdfViewer;
+ const isInPresentationMode = pdfViewer.isInPresentationMode || pdfViewer.isChangingPresentationMode;
+ const {
+ id,
+ siblings,
+ command,
+ value
+ } = detail;
+ if (!id) {
+ switch (command) {
+ case "clear":
+ console.clear();
+ break;
+ case "error":
+ console.error(value);
+ break;
+ case "layout":
+ if (!isInPresentationMode) {
+ const modes = apiPageLayoutToViewerModes(value);
+ pdfViewer.spreadMode = modes.spreadMode;
+ }
+ break;
+ case "page-num":
+ pdfViewer.currentPageNumber = value + 1;
+ break;
+ case "print":
+ await pdfViewer.pagesPromise;
+ this.#eventBus.dispatch("print", {
+ source: this
+ });
+ break;
+ case "println":
+ console.log(value);
+ break;
+ case "zoom":
+ if (!isInPresentationMode) {
+ pdfViewer.currentScaleValue = value;
+ }
+ break;
+ case "SaveAs":
+ this.#eventBus.dispatch("download", {
+ source: this
+ });
+ break;
+ case "FirstPage":
+ pdfViewer.currentPageNumber = 1;
+ break;
+ case "LastPage":
+ pdfViewer.currentPageNumber = pdfViewer.pagesCount;
+ break;
+ case "NextPage":
+ pdfViewer.nextPage();
+ break;
+ case "PrevPage":
+ pdfViewer.previousPage();
+ break;
+ case "ZoomViewIn":
+ if (!isInPresentationMode) {
+ pdfViewer.increaseScale();
+ }
+ break;
+ case "ZoomViewOut":
+ if (!isInPresentationMode) {
+ pdfViewer.decreaseScale();
+ }
+ break;
+ case "WillPrintFinished":
+ this.#willPrintCapability?.resolve();
+ this.#willPrintCapability = null;
+ break;
+ }
+ return;
+ }
+ if (isInPresentationMode && detail.focus) {
+ return;
+ }
+ delete detail.id;
+ delete detail.siblings;
+ const ids = siblings ? [id, ...siblings] : [id];
+ for (const elementId of ids) {
+ const element = document.querySelector(`[data-element-id="${elementId}"]`);
+ if (element) {
+ element.dispatchEvent(new CustomEvent("updatefromsandbox", {
+ detail
+ }));
+ } else {
+ this.#pdfDocument?.annotationStorage.setValue(elementId, detail);
+ }
+ }
+ }
+ async #dispatchPageOpen(pageNumber, initialize = false) {
+ const pdfDocument = this.#pdfDocument,
+ visitedPages = this._visitedPages;
+ if (initialize) {
+ this.#closeCapability = Promise.withResolvers();
+ }
+ if (!this.#closeCapability) {
+ return;
+ }
+ const pageView = this.#pdfViewer.getPageView(pageNumber - 1);
+ if (pageView?.renderingState !== RenderingStates.FINISHED) {
+ this._pageOpenPending.add(pageNumber);
+ return;
+ }
+ this._pageOpenPending.delete(pageNumber);
+ const actionsPromise = (async () => {
+ const actions = await (!visitedPages.has(pageNumber) ? pageView.pdfPage?.getJSActions() : null);
+ if (pdfDocument !== this.#pdfDocument) {
+ return;
+ }
+ await this.#scripting?.dispatchEventInSandbox({
+ id: "page",
+ name: "PageOpen",
+ pageNumber,
+ actions
+ });
+ })();
+ visitedPages.set(pageNumber, actionsPromise);
+ }
+ async #dispatchPageClose(pageNumber) {
+ const pdfDocument = this.#pdfDocument,
+ visitedPages = this._visitedPages;
+ if (!this.#closeCapability) {
+ return;
+ }
+ if (this._pageOpenPending.has(pageNumber)) {
+ return;
+ }
+ const actionsPromise = visitedPages.get(pageNumber);
+ if (!actionsPromise) {
+ return;
+ }
+ visitedPages.set(pageNumber, null);
+ await actionsPromise;
+ if (pdfDocument !== this.#pdfDocument) {
+ return;
+ }
+ await this.#scripting?.dispatchEventInSandbox({
+ id: "page",
+ name: "PageClose",
+ pageNumber
+ });
+ }
+ #initScripting() {
+ this.#destroyCapability = Promise.withResolvers();
+ if (this.#scripting) {
+ throw new Error("#initScripting: Scripting already exists.");
+ }
+ return this.#externalServices.createScripting();
+ }
+ async #destroyScripting() {
+ if (!this.#scripting) {
+ this.#pdfDocument = null;
+ this.#destroyCapability?.resolve();
+ return;
+ }
+ if (this.#closeCapability) {
+ await Promise.race([this.#closeCapability.promise, new Promise(resolve => {
+ setTimeout(resolve, 1000);
+ })]).catch(() => {});
+ this.#closeCapability = null;
+ }
+ this.#pdfDocument = null;
+ try {
+ await this.#scripting.destroySandbox();
+ } catch {}
+ this.#willPrintCapability?.reject(new Error("Scripting destroyed."));
+ this.#willPrintCapability = null;
+ this.#eventAbortController?.abort();
+ this.#eventAbortController = null;
+ this._pageOpenPending.clear();
+ this._visitedPages.clear();
+ this.#scripting = null;
+ this.#ready = false;
+ this.#destroyCapability?.resolve();
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_sidebar.js
+
+const SIDEBAR_WIDTH_VAR = "--sidebar-width";
+const SIDEBAR_MIN_WIDTH = 200;
+const SIDEBAR_RESIZING_CLASS = "sidebarResizing";
+const UI_NOTIFICATION_CLASS = "pdfSidebarNotification";
+class PDFSidebar {
+ #isRTL = false;
+ #mouseMoveBound = this.#mouseMove.bind(this);
+ #mouseUpBound = this.#mouseUp.bind(this);
+ #outerContainerWidth = null;
+ #width = null;
+ constructor({
+ elements,
+ eventBus,
+ l10n
+ }) {
+ this.isOpen = false;
+ this.active = SidebarView.THUMBS;
+ this.isInitialViewSet = false;
+ this.isInitialEventDispatched = false;
+ this.onToggled = null;
+ this.onUpdateThumbnails = null;
+ this.outerContainer = elements.outerContainer;
+ this.sidebarContainer = elements.sidebarContainer;
+ this.toggleButton = elements.toggleButton;
+ this.resizer = elements.resizer;
+ this.thumbnailButton = elements.thumbnailButton;
+ this.outlineButton = elements.outlineButton;
+ this.attachmentsButton = elements.attachmentsButton;
+ this.layersButton = elements.layersButton;
+ this.thumbnailView = elements.thumbnailView;
+ this.outlineView = elements.outlineView;
+ this.attachmentsView = elements.attachmentsView;
+ this.layersView = elements.layersView;
+ this._currentOutlineItemButton = elements.currentOutlineItemButton;
+ this.eventBus = eventBus;
+ this.#isRTL = l10n.getDirection() === "rtl";
+ this.#addEventListeners();
+ }
+ reset() {
+ this.isInitialViewSet = false;
+ this.isInitialEventDispatched = false;
+ this.#hideUINotification(true);
+ this.switchView(SidebarView.THUMBS);
+ this.outlineButton.disabled = false;
+ this.attachmentsButton.disabled = false;
+ this.layersButton.disabled = false;
+ this._currentOutlineItemButton.disabled = true;
+ }
+ get visibleView() {
+ return this.isOpen ? this.active : SidebarView.NONE;
+ }
+ setInitialView(view = SidebarView.NONE) {
+ if (this.isInitialViewSet) {
+ return;
+ }
+ this.isInitialViewSet = true;
+ if (view === SidebarView.NONE || view === SidebarView.UNKNOWN) {
+ this.#dispatchEvent();
+ return;
+ }
+ this.switchView(view, true);
+ if (!this.isInitialEventDispatched) {
+ this.#dispatchEvent();
+ }
+ }
+ switchView(view, forceOpen = false) {
+ const isViewChanged = view !== this.active;
+ let forceRendering = false;
+ switch (view) {
+ case SidebarView.NONE:
+ if (this.isOpen) {
+ this.close();
+ }
+ return;
+ case SidebarView.THUMBS:
+ if (this.isOpen && isViewChanged) {
+ forceRendering = true;
+ }
+ break;
+ case SidebarView.OUTLINE:
+ if (this.outlineButton.disabled) {
+ return;
+ }
+ break;
+ case SidebarView.ATTACHMENTS:
+ if (this.attachmentsButton.disabled) {
+ return;
+ }
+ break;
+ case SidebarView.LAYERS:
+ if (this.layersButton.disabled) {
+ return;
+ }
+ break;
+ default:
+ console.error(`PDFSidebar.switchView: "${view}" is not a valid view.`);
+ return;
+ }
+ this.active = view;
+ toggleCheckedBtn(this.thumbnailButton, view === SidebarView.THUMBS, this.thumbnailView);
+ toggleCheckedBtn(this.outlineButton, view === SidebarView.OUTLINE, this.outlineView);
+ toggleCheckedBtn(this.attachmentsButton, view === SidebarView.ATTACHMENTS, this.attachmentsView);
+ toggleCheckedBtn(this.layersButton, view === SidebarView.LAYERS, this.layersView);
+ if (forceOpen && !this.isOpen) {
+ this.open();
+ return;
+ }
+ if (forceRendering) {
+ this.onUpdateThumbnails();
+ this.onToggled();
+ }
+ if (isViewChanged) {
+ this.#dispatchEvent();
+ }
+ }
+ open() {
+ if (this.isOpen) {
+ return;
+ }
+ this.isOpen = true;
+ toggleExpandedBtn(this.toggleButton, true);
+ this.outerContainer.classList.add("sidebarMoving", "sidebarOpen");
+ if (this.active === SidebarView.THUMBS) {
+ this.onUpdateThumbnails();
+ }
+ this.onToggled();
+ this.#dispatchEvent();
+ this.#hideUINotification();
+ }
+ close(evt = null) {
+ if (!this.isOpen) {
+ return;
+ }
+ this.isOpen = false;
+ toggleExpandedBtn(this.toggleButton, false);
+ this.outerContainer.classList.add("sidebarMoving");
+ this.outerContainer.classList.remove("sidebarOpen");
+ this.onToggled();
+ this.#dispatchEvent();
+ if (evt?.detail > 0) {
+ this.toggleButton.blur();
+ }
+ }
+ toggle(evt = null) {
+ if (this.isOpen) {
+ this.close(evt);
+ } else {
+ this.open();
+ }
+ }
+ #dispatchEvent() {
+ if (this.isInitialViewSet) {
+ this.isInitialEventDispatched ||= true;
+ }
+ this.eventBus.dispatch("sidebarviewchanged", {
+ source: this,
+ view: this.visibleView
+ });
+ }
+ #showUINotification() {
+ this.toggleButton.setAttribute("data-l10n-id", "pdfjs-toggle-sidebar-notification-button");
+ if (!this.isOpen) {
+ this.toggleButton.classList.add(UI_NOTIFICATION_CLASS);
+ }
+ }
+ #hideUINotification(reset = false) {
+ if (this.isOpen || reset) {
+ this.toggleButton.classList.remove(UI_NOTIFICATION_CLASS);
+ }
+ if (reset) {
+ this.toggleButton.setAttribute("data-l10n-id", "pdfjs-toggle-sidebar-button");
+ }
+ }
+ #addEventListeners() {
+ this.sidebarContainer.addEventListener("transitionend", evt => {
+ if (evt.target === this.sidebarContainer) {
+ this.outerContainer.classList.remove("sidebarMoving");
+ this.eventBus.dispatch("resize", {
+ source: this
+ });
+ }
+ });
+ this.toggleButton.addEventListener("click", evt => {
+ this.toggle(evt);
+ });
+ this.thumbnailButton.addEventListener("click", () => {
+ this.switchView(SidebarView.THUMBS);
+ });
+ this.outlineButton.addEventListener("click", () => {
+ this.switchView(SidebarView.OUTLINE);
+ });
+ this.outlineButton.addEventListener("dblclick", () => {
+ this.eventBus.dispatch("toggleoutlinetree", {
+ source: this
+ });
+ });
+ this.attachmentsButton.addEventListener("click", () => {
+ this.switchView(SidebarView.ATTACHMENTS);
+ });
+ this.layersButton.addEventListener("click", () => {
+ this.switchView(SidebarView.LAYERS);
+ });
+ this.layersButton.addEventListener("dblclick", () => {
+ this.eventBus.dispatch("resetlayers", {
+ source: this
+ });
+ });
+ this._currentOutlineItemButton.addEventListener("click", () => {
+ this.eventBus.dispatch("currentoutlineitem", {
+ source: this
+ });
+ });
+ const onTreeLoaded = (count, button, view) => {
+ button.disabled = !count;
+ if (count) {
+ this.#showUINotification();
+ } else if (this.active === view) {
+ this.switchView(SidebarView.THUMBS);
+ }
+ };
+ this.eventBus._on("outlineloaded", evt => {
+ onTreeLoaded(evt.outlineCount, this.outlineButton, SidebarView.OUTLINE);
+ evt.currentOutlineItemPromise.then(enabled => {
+ if (!this.isInitialViewSet) {
+ return;
+ }
+ this._currentOutlineItemButton.disabled = !enabled;
+ });
+ });
+ this.eventBus._on("attachmentsloaded", evt => {
+ onTreeLoaded(evt.attachmentsCount, this.attachmentsButton, SidebarView.ATTACHMENTS);
+ });
+ this.eventBus._on("layersloaded", evt => {
+ onTreeLoaded(evt.layersCount, this.layersButton, SidebarView.LAYERS);
+ });
+ this.eventBus._on("presentationmodechanged", evt => {
+ if (evt.state === PresentationModeState.NORMAL && this.visibleView === SidebarView.THUMBS) {
+ this.onUpdateThumbnails();
+ }
+ });
+ this.resizer.addEventListener("mousedown", evt => {
+ if (evt.button !== 0) {
+ return;
+ }
+ this.outerContainer.classList.add(SIDEBAR_RESIZING_CLASS);
+ window.addEventListener("mousemove", this.#mouseMoveBound);
+ window.addEventListener("mouseup", this.#mouseUpBound);
+ });
+ this.eventBus._on("resize", evt => {
+ if (evt.source !== window) {
+ return;
+ }
+ this.#outerContainerWidth = null;
+ if (!this.#width) {
+ return;
+ }
+ if (!this.isOpen) {
+ this.#updateWidth(this.#width);
+ return;
+ }
+ this.outerContainer.classList.add(SIDEBAR_RESIZING_CLASS);
+ const updated = this.#updateWidth(this.#width);
+ Promise.resolve().then(() => {
+ this.outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS);
+ if (updated) {
+ this.eventBus.dispatch("resize", {
+ source: this
+ });
+ }
+ });
+ });
+ }
+ get outerContainerWidth() {
+ return this.#outerContainerWidth ||= this.outerContainer.clientWidth;
+ }
+ #updateWidth(width = 0) {
+ const maxWidth = Math.floor(this.outerContainerWidth / 2);
+ if (width > maxWidth) {
+ width = maxWidth;
+ }
+ if (width < SIDEBAR_MIN_WIDTH) {
+ width = SIDEBAR_MIN_WIDTH;
+ }
+ if (width === this.#width) {
+ return false;
+ }
+ this.#width = width;
+ docStyle.setProperty(SIDEBAR_WIDTH_VAR, `${width}px`);
+ return true;
+ }
+ #mouseMove(evt) {
+ let width = evt.clientX;
+ if (this.#isRTL) {
+ width = this.outerContainerWidth - width;
+ }
+ this.#updateWidth(width);
+ }
+ #mouseUp(evt) {
+ this.outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS);
+ this.eventBus.dispatch("resize", {
+ source: this
+ });
+ window.removeEventListener("mousemove", this.#mouseMoveBound);
+ window.removeEventListener("mouseup", this.#mouseUpBound);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_thumbnail_view.js
+
+
+const DRAW_UPSCALE_FACTOR = 2;
+const MAX_NUM_SCALING_STEPS = 3;
+const THUMBNAIL_WIDTH = 98;
+class TempImageFactory {
+ static #tempCanvas = null;
+ static getCanvas(width, height) {
+ const tempCanvas = this.#tempCanvas ||= document.createElement("canvas");
+ tempCanvas.width = width;
+ tempCanvas.height = height;
+ const ctx = tempCanvas.getContext("2d", {
+ alpha: false
+ });
+ ctx.save();
+ ctx.fillStyle = "rgb(255, 255, 255)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+ return [tempCanvas, tempCanvas.getContext("2d")];
+ }
+ static destroyCanvas() {
+ const tempCanvas = this.#tempCanvas;
+ if (tempCanvas) {
+ tempCanvas.width = 0;
+ tempCanvas.height = 0;
+ }
+ this.#tempCanvas = null;
+ }
+}
+class PDFThumbnailView {
+ constructor({
+ container,
+ eventBus,
+ id,
+ defaultViewport,
+ optionalContentConfigPromise,
+ linkService,
+ renderingQueue,
+ pageColors
+ }) {
+ this.id = id;
+ this.renderingId = "thumbnail" + id;
+ this.pageLabel = null;
+ this.pdfPage = null;
+ this.rotation = 0;
+ this.viewport = defaultViewport;
+ this.pdfPageRotate = defaultViewport.rotation;
+ this._optionalContentConfigPromise = optionalContentConfigPromise || null;
+ this.pageColors = pageColors || null;
+ this.eventBus = eventBus;
+ this.linkService = linkService;
+ this.renderingQueue = renderingQueue;
+ this.renderTask = null;
+ this.renderingState = RenderingStates.INITIAL;
+ this.resume = null;
+ const anchor = document.createElement("a");
+ anchor.href = linkService.getAnchorUrl("#page=" + id);
+ anchor.setAttribute("data-l10n-id", "pdfjs-thumb-page-title");
+ anchor.setAttribute("data-l10n-args", this.#pageL10nArgs);
+ anchor.onclick = function () {
+ linkService.goToPage(id);
+ return false;
+ };
+ this.anchor = anchor;
+ const div = document.createElement("div");
+ div.className = "thumbnail";
+ div.setAttribute("data-page-number", this.id);
+ this.div = div;
+ this.#updateDims();
+ const img = document.createElement("div");
+ img.className = "thumbnailImage";
+ this._placeholderImg = img;
+ div.append(img);
+ anchor.append(div);
+ container.append(anchor);
+ }
+ #updateDims() {
+ const {
+ width,
+ height
+ } = this.viewport;
+ const ratio = width / height;
+ this.canvasWidth = THUMBNAIL_WIDTH;
+ this.canvasHeight = this.canvasWidth / ratio | 0;
+ this.scale = this.canvasWidth / width;
+ const {
+ style
+ } = this.div;
+ style.setProperty("--thumbnail-width", `${this.canvasWidth}px`);
+ style.setProperty("--thumbnail-height", `${this.canvasHeight}px`);
+ }
+ setPdfPage(pdfPage) {
+ this.pdfPage = pdfPage;
+ this.pdfPageRotate = pdfPage.rotate;
+ const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
+ this.viewport = pdfPage.getViewport({
+ scale: 1,
+ rotation: totalRotation
+ });
+ this.reset();
+ }
+ reset() {
+ this.cancelRendering();
+ this.renderingState = RenderingStates.INITIAL;
+ this.div.removeAttribute("data-loaded");
+ this.image?.replaceWith(this._placeholderImg);
+ this.#updateDims();
+ if (this.image) {
+ this.image.removeAttribute("src");
+ delete this.image;
+ }
+ }
+ update({
+ rotation = null
+ }) {
+ if (typeof rotation === "number") {
+ this.rotation = rotation;
+ }
+ const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
+ this.viewport = this.viewport.clone({
+ scale: 1,
+ rotation: totalRotation
+ });
+ this.reset();
+ }
+ cancelRendering() {
+ if (this.renderTask) {
+ this.renderTask.cancel();
+ this.renderTask = null;
+ }
+ this.resume = null;
+ }
+ #getPageDrawContext(upscaleFactor = 1) {
+ const canvas = document.createElement("canvas");
+ const ctx = canvas.getContext("2d", {
+ alpha: false
+ });
+ const outputScale = new OutputScale();
+ canvas.width = upscaleFactor * this.canvasWidth * outputScale.sx | 0;
+ canvas.height = upscaleFactor * this.canvasHeight * outputScale.sy | 0;
+ const transform = outputScale.scaled ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0] : null;
+ return {
+ ctx,
+ canvas,
+ transform
+ };
+ }
+ #convertCanvasToImage(canvas) {
+ if (this.renderingState !== RenderingStates.FINISHED) {
+ throw new Error("#convertCanvasToImage: Rendering has not finished.");
+ }
+ const reducedCanvas = this.#reduceImage(canvas);
+ const image = document.createElement("img");
+ image.className = "thumbnailImage";
+ image.setAttribute("data-l10n-id", "pdfjs-thumb-page-canvas");
+ image.setAttribute("data-l10n-args", this.#pageL10nArgs);
+ image.src = reducedCanvas.toDataURL();
+ this.image = image;
+ this.div.setAttribute("data-loaded", true);
+ this._placeholderImg.replaceWith(image);
+ reducedCanvas.width = 0;
+ reducedCanvas.height = 0;
+ }
+ async #finishRenderTask(renderTask, canvas, error = null) {
+ if (renderTask === this.renderTask) {
+ this.renderTask = null;
+ }
+ if (error instanceof RenderingCancelledException) {
+ return;
+ }
+ this.renderingState = RenderingStates.FINISHED;
+ this.#convertCanvasToImage(canvas);
+ if (error) {
+ throw error;
+ }
+ }
+ async draw() {
+ if (this.renderingState !== RenderingStates.INITIAL) {
+ console.error("Must be in new state before drawing");
+ return undefined;
+ }
+ const {
+ pdfPage
+ } = this;
+ if (!pdfPage) {
+ this.renderingState = RenderingStates.FINISHED;
+ throw new Error("pdfPage is not loaded");
+ }
+ this.renderingState = RenderingStates.RUNNING;
+ const {
+ ctx,
+ canvas,
+ transform
+ } = this.#getPageDrawContext(DRAW_UPSCALE_FACTOR);
+ const drawViewport = this.viewport.clone({
+ scale: DRAW_UPSCALE_FACTOR * this.scale
+ });
+ const renderContinueCallback = cont => {
+ if (!this.renderingQueue.isHighestPriority(this)) {
+ this.renderingState = RenderingStates.PAUSED;
+ this.resume = () => {
+ this.renderingState = RenderingStates.RUNNING;
+ cont();
+ };
+ return;
+ }
+ cont();
+ };
+ const renderContext = {
+ canvasContext: ctx,
+ transform,
+ viewport: drawViewport,
+ optionalContentConfigPromise: this._optionalContentConfigPromise,
+ pageColors: this.pageColors
+ };
+ const renderTask = this.renderTask = pdfPage.render(renderContext);
+ renderTask.onContinue = renderContinueCallback;
+ const resultPromise = renderTask.promise.then(() => this.#finishRenderTask(renderTask, canvas), error => this.#finishRenderTask(renderTask, canvas, error));
+ resultPromise.finally(() => {
+ canvas.width = 0;
+ canvas.height = 0;
+ this.eventBus.dispatch("thumbnailrendered", {
+ source: this,
+ pageNumber: this.id,
+ pdfPage: this.pdfPage
+ });
+ });
+ return resultPromise;
+ }
+ setImage(pageView) {
+ if (this.renderingState !== RenderingStates.INITIAL) {
+ return;
+ }
+ const {
+ thumbnailCanvas: canvas,
+ pdfPage,
+ scale
+ } = pageView;
+ if (!canvas) {
+ return;
+ }
+ if (!this.pdfPage) {
+ this.setPdfPage(pdfPage);
+ }
+ if (scale < this.scale) {
+ return;
+ }
+ this.renderingState = RenderingStates.FINISHED;
+ this.#convertCanvasToImage(canvas);
+ }
+ #reduceImage(img) {
+ const {
+ ctx,
+ canvas
+ } = this.#getPageDrawContext();
+ if (img.width <= 2 * canvas.width) {
+ ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
+ return canvas;
+ }
+ let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS;
+ let reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS;
+ const [reducedImage, reducedImageCtx] = TempImageFactory.getCanvas(reducedWidth, reducedHeight);
+ while (reducedWidth > img.width || reducedHeight > img.height) {
+ reducedWidth >>= 1;
+ reducedHeight >>= 1;
+ }
+ reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, reducedWidth, reducedHeight);
+ while (reducedWidth > 2 * canvas.width) {
+ reducedImageCtx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, reducedWidth >> 1, reducedHeight >> 1);
+ reducedWidth >>= 1;
+ reducedHeight >>= 1;
+ }
+ ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, canvas.width, canvas.height);
+ return canvas;
+ }
+ get #pageL10nArgs() {
+ return JSON.stringify({
+ page: this.pageLabel ?? this.id
+ });
+ }
+ setPageLabel(label) {
+ this.pageLabel = typeof label === "string" ? label : null;
+ this.anchor.setAttribute("data-l10n-args", this.#pageL10nArgs);
+ if (this.renderingState !== RenderingStates.FINISHED) {
+ return;
+ }
+ this.image?.setAttribute("data-l10n-args", this.#pageL10nArgs);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_thumbnail_viewer.js
+
+
+const THUMBNAIL_SCROLL_MARGIN = -19;
+const THUMBNAIL_SELECTED_CLASS = "selected";
+class PDFThumbnailViewer {
+ constructor({
+ container,
+ eventBus,
+ linkService,
+ renderingQueue,
+ pageColors
+ }) {
+ this.container = container;
+ this.eventBus = eventBus;
+ this.linkService = linkService;
+ this.renderingQueue = renderingQueue;
+ this.pageColors = pageColors || null;
+ this.scroll = watchScroll(this.container, this.#scrollUpdated.bind(this));
+ this.#resetView();
+ }
+ #scrollUpdated() {
+ this.renderingQueue.renderHighestPriority();
+ }
+ getThumbnail(index) {
+ return this._thumbnails[index];
+ }
+ #getVisibleThumbs() {
+ return getVisibleElements({
+ scrollEl: this.container,
+ views: this._thumbnails
+ });
+ }
+ scrollThumbnailIntoView(pageNumber) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ const thumbnailView = this._thumbnails[pageNumber - 1];
+ if (!thumbnailView) {
+ console.error('scrollThumbnailIntoView: Invalid "pageNumber" parameter.');
+ return;
+ }
+ if (pageNumber !== this._currentPageNumber) {
+ const prevThumbnailView = this._thumbnails[this._currentPageNumber - 1];
+ prevThumbnailView.div.classList.remove(THUMBNAIL_SELECTED_CLASS);
+ thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
+ }
+ const {
+ first,
+ last,
+ views
+ } = this.#getVisibleThumbs();
+ if (views.length > 0) {
+ let shouldScroll = false;
+ if (pageNumber <= first.id || pageNumber >= last.id) {
+ shouldScroll = true;
+ } else {
+ for (const {
+ id,
+ percent
+ } of views) {
+ if (id !== pageNumber) {
+ continue;
+ }
+ shouldScroll = percent < 100;
+ break;
+ }
+ }
+ if (shouldScroll) {
+ scrollIntoView(thumbnailView.div, {
+ top: THUMBNAIL_SCROLL_MARGIN
+ });
+ }
+ }
+ this._currentPageNumber = pageNumber;
+ }
+ get pagesRotation() {
+ return this._pagesRotation;
+ }
+ set pagesRotation(rotation) {
+ if (!isValidRotation(rotation)) {
+ throw new Error("Invalid thumbnails rotation angle.");
+ }
+ if (!this.pdfDocument) {
+ return;
+ }
+ if (this._pagesRotation === rotation) {
+ return;
+ }
+ this._pagesRotation = rotation;
+ const updateArgs = {
+ rotation
+ };
+ for (const thumbnail of this._thumbnails) {
+ thumbnail.update(updateArgs);
+ }
+ }
+ cleanup() {
+ for (const thumbnail of this._thumbnails) {
+ if (thumbnail.renderingState !== RenderingStates.FINISHED) {
+ thumbnail.reset();
+ }
+ }
+ TempImageFactory.destroyCanvas();
+ }
+ #resetView() {
+ this._thumbnails = [];
+ this._currentPageNumber = 1;
+ this._pageLabels = null;
+ this._pagesRotation = 0;
+ this.container.textContent = "";
+ }
+ setDocument(pdfDocument) {
+ if (this.pdfDocument) {
+ this.#cancelRendering();
+ this.#resetView();
+ }
+ this.pdfDocument = pdfDocument;
+ if (!pdfDocument) {
+ return;
+ }
+ const firstPagePromise = pdfDocument.getPage(1);
+ const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
+ intent: "display"
+ });
+ firstPagePromise.then(firstPdfPage => {
+ const pagesCount = pdfDocument.numPages;
+ const viewport = firstPdfPage.getViewport({
+ scale: 1
+ });
+ for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
+ const thumbnail = new PDFThumbnailView({
+ container: this.container,
+ eventBus: this.eventBus,
+ id: pageNum,
+ defaultViewport: viewport.clone(),
+ optionalContentConfigPromise,
+ linkService: this.linkService,
+ renderingQueue: this.renderingQueue,
+ pageColors: this.pageColors
+ });
+ this._thumbnails.push(thumbnail);
+ }
+ this._thumbnails[0]?.setPdfPage(firstPdfPage);
+ const thumbnailView = this._thumbnails[this._currentPageNumber - 1];
+ thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
+ }).catch(reason => {
+ console.error("Unable to initialize thumbnail viewer", reason);
+ });
+ }
+ #cancelRendering() {
+ for (const thumbnail of this._thumbnails) {
+ thumbnail.cancelRendering();
+ }
+ }
+ setPageLabels(labels) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ if (!labels) {
+ this._pageLabels = null;
+ } else if (!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)) {
+ this._pageLabels = null;
+ console.error("PDFThumbnailViewer_setPageLabels: Invalid page labels.");
+ } else {
+ this._pageLabels = labels;
+ }
+ for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
+ this._thumbnails[i].setPageLabel(this._pageLabels?.[i] ?? null);
+ }
+ }
+ async #ensurePdfPageLoaded(thumbView) {
+ if (thumbView.pdfPage) {
+ return thumbView.pdfPage;
+ }
+ try {
+ const pdfPage = await this.pdfDocument.getPage(thumbView.id);
+ if (!thumbView.pdfPage) {
+ thumbView.setPdfPage(pdfPage);
+ }
+ return pdfPage;
+ } catch (reason) {
+ console.error("Unable to get page for thumb view", reason);
+ return null;
+ }
+ }
+ #getScrollAhead(visible) {
+ if (visible.first?.id === 1) {
+ return true;
+ } else if (visible.last?.id === this._thumbnails.length) {
+ return false;
+ }
+ return this.scroll.down;
+ }
+ forceRendering() {
+ const visibleThumbs = this.#getVisibleThumbs();
+ const scrollAhead = this.#getScrollAhead(visibleThumbs);
+ const thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, this._thumbnails, scrollAhead);
+ if (thumbView) {
+ this.#ensurePdfPageLoaded(thumbView).then(() => {
+ this.renderingQueue.renderView(thumbView);
+ });
+ return true;
+ }
+ return false;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/annotation_editor_layer_builder.js
+
+
+class AnnotationEditorLayerBuilder {
+ #annotationLayer = null;
+ #drawLayer = null;
+ #onAppend = null;
+ #textLayer = null;
+ #uiManager;
+ constructor(options) {
+ this.pdfPage = options.pdfPage;
+ this.accessibilityManager = options.accessibilityManager;
+ this.l10n = options.l10n;
+ this.l10n ||= new genericl10n_GenericL10n();
+ this.annotationEditorLayer = null;
+ this.div = null;
+ this._cancelled = false;
+ this.#uiManager = options.uiManager;
+ this.#annotationLayer = options.annotationLayer || null;
+ this.#textLayer = options.textLayer || null;
+ this.#drawLayer = options.drawLayer || null;
+ this.#onAppend = options.onAppend || null;
+ }
+ async render(viewport, intent = "display") {
+ if (intent !== "display") {
+ return;
+ }
+ if (this._cancelled) {
+ return;
+ }
+ const clonedViewport = viewport.clone({
+ dontFlip: true
+ });
+ if (this.div) {
+ this.annotationEditorLayer.update({
+ viewport: clonedViewport
+ });
+ this.show();
+ return;
+ }
+ const div = this.div = document.createElement("div");
+ div.className = "annotationEditorLayer";
+ div.hidden = true;
+ div.dir = this.#uiManager.direction;
+ this.#onAppend?.(div);
+ this.annotationEditorLayer = new AnnotationEditorLayer({
+ uiManager: this.#uiManager,
+ div,
+ accessibilityManager: this.accessibilityManager,
+ pageIndex: this.pdfPage.pageNumber - 1,
+ l10n: this.l10n,
+ viewport: clonedViewport,
+ annotationLayer: this.#annotationLayer,
+ textLayer: this.#textLayer,
+ drawLayer: this.#drawLayer
+ });
+ const parameters = {
+ viewport: clonedViewport,
+ div,
+ annotations: null,
+ intent
+ };
+ this.annotationEditorLayer.render(parameters);
+ this.show();
+ }
+ cancel() {
+ this._cancelled = true;
+ if (!this.div) {
+ return;
+ }
+ this.annotationEditorLayer.destroy();
+ }
+ hide() {
+ if (!this.div) {
+ return;
+ }
+ this.div.hidden = true;
+ }
+ show() {
+ if (!this.div || this.annotationEditorLayer.isInvisible) {
+ return;
+ }
+ this.div.hidden = false;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/annotation_layer_builder.js
+
+
+class AnnotationLayerBuilder {
+ #onAppend = null;
+ #eventAbortController = null;
+ constructor({
+ pdfPage,
+ linkService,
+ downloadManager,
+ annotationStorage = null,
+ imageResourcesPath = "",
+ renderForms = true,
+ enableScripting = false,
+ hasJSActionsPromise = null,
+ fieldObjectsPromise = null,
+ annotationCanvasMap = null,
+ accessibilityManager = null,
+ annotationEditorUIManager = null,
+ onAppend = null
+ }) {
+ this.pdfPage = pdfPage;
+ this.linkService = linkService;
+ this.downloadManager = downloadManager;
+ this.imageResourcesPath = imageResourcesPath;
+ this.renderForms = renderForms;
+ this.annotationStorage = annotationStorage;
+ this.enableScripting = enableScripting;
+ this._hasJSActionsPromise = hasJSActionsPromise || Promise.resolve(false);
+ this._fieldObjectsPromise = fieldObjectsPromise || Promise.resolve(null);
+ this._annotationCanvasMap = annotationCanvasMap;
+ this._accessibilityManager = accessibilityManager;
+ this._annotationEditorUIManager = annotationEditorUIManager;
+ this.#onAppend = onAppend;
+ this.annotationLayer = null;
+ this.div = null;
+ this._cancelled = false;
+ this._eventBus = linkService.eventBus;
+ }
+ async render(viewport, intent = "display") {
+ if (this.div) {
+ if (this._cancelled || !this.annotationLayer) {
+ return;
+ }
+ this.annotationLayer.update({
+ viewport: viewport.clone({
+ dontFlip: true
+ })
+ });
+ return;
+ }
+ const [annotations, hasJSActions, fieldObjects] = await Promise.all([this.pdfPage.getAnnotations({
+ intent
+ }), this._hasJSActionsPromise, this._fieldObjectsPromise]);
+ if (this._cancelled) {
+ return;
+ }
+ const div = this.div = document.createElement("div");
+ div.className = "annotationLayer";
+ this.#onAppend?.(div);
+ if (annotations.length === 0) {
+ this.hide();
+ return;
+ }
+ this.annotationLayer = new AnnotationLayer({
+ div,
+ accessibilityManager: this._accessibilityManager,
+ annotationCanvasMap: this._annotationCanvasMap,
+ annotationEditorUIManager: this._annotationEditorUIManager,
+ page: this.pdfPage,
+ viewport: viewport.clone({
+ dontFlip: true
+ })
+ });
+ await this.annotationLayer.render({
+ annotations,
+ imageResourcesPath: this.imageResourcesPath,
+ renderForms: this.renderForms,
+ linkService: this.linkService,
+ downloadManager: this.downloadManager,
+ annotationStorage: this.annotationStorage,
+ enableScripting: this.enableScripting,
+ hasJSActions,
+ fieldObjects
+ });
+ if (this.linkService.isInPresentationMode) {
+ this.#updatePresentationModeState(PresentationModeState.FULLSCREEN);
+ }
+ if (!this.#eventAbortController) {
+ this.#eventAbortController = new AbortController();
+ this._eventBus?._on("presentationmodechanged", evt => {
+ this.#updatePresentationModeState(evt.state);
+ }, {
+ signal: this.#eventAbortController.signal
+ });
+ }
+ }
+ cancel() {
+ this._cancelled = true;
+ this.#eventAbortController?.abort();
+ this.#eventAbortController = null;
+ }
+ hide() {
+ if (!this.div) {
+ return;
+ }
+ this.div.hidden = true;
+ }
+ #updatePresentationModeState(state) {
+ if (!this.div) {
+ return;
+ }
+ let disableFormElements = false;
+ switch (state) {
+ case PresentationModeState.FULLSCREEN:
+ disableFormElements = true;
+ break;
+ case PresentationModeState.NORMAL:
+ break;
+ default:
+ return;
+ }
+ for (const section of this.div.childNodes) {
+ if (section.hasAttribute("data-internal-link")) {
+ continue;
+ }
+ section.inert = disableFormElements;
+ }
+ }
+}
+
+;// CONCATENATED MODULE: ./web/draw_layer_builder.js
+
+class DrawLayerBuilder {
+ #drawLayer = null;
+ constructor(options) {
+ this.pageIndex = options.pageIndex;
+ }
+ async render(intent = "display") {
+ if (intent !== "display" || this.#drawLayer || this._cancelled) {
+ return;
+ }
+ this.#drawLayer = new DrawLayer({
+ pageIndex: this.pageIndex
+ });
+ }
+ cancel() {
+ this._cancelled = true;
+ if (!this.#drawLayer) {
+ return;
+ }
+ this.#drawLayer.destroy();
+ this.#drawLayer = null;
+ }
+ setParent(parent) {
+ this.#drawLayer?.setParent(parent);
+ }
+ getDrawLayer() {
+ return this.#drawLayer;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/struct_tree_layer_builder.js
+
+const PDF_ROLE_TO_HTML_ROLE = {
+ Document: null,
+ DocumentFragment: null,
+ Part: "group",
+ Sect: "group",
+ Div: "group",
+ Aside: "note",
+ NonStruct: "none",
+ P: null,
+ H: "heading",
+ Title: null,
+ FENote: "note",
+ Sub: "group",
+ Lbl: null,
+ Span: null,
+ Em: null,
+ Strong: null,
+ Link: "link",
+ Annot: "note",
+ Form: "form",
+ Ruby: null,
+ RB: null,
+ RT: null,
+ RP: null,
+ Warichu: null,
+ WT: null,
+ WP: null,
+ L: "list",
+ LI: "listitem",
+ LBody: null,
+ Table: "table",
+ TR: "row",
+ TH: "columnheader",
+ TD: "cell",
+ THead: "columnheader",
+ TBody: null,
+ TFoot: null,
+ Caption: null,
+ Figure: "figure",
+ Formula: null,
+ Artifact: null
+};
+const HEADING_PATTERN = /^H(\d+)$/;
+class StructTreeLayerBuilder {
+ #treeDom = undefined;
+ get renderingDone() {
+ return this.#treeDom !== undefined;
+ }
+ render(structTree) {
+ if (this.#treeDom !== undefined) {
+ return this.#treeDom;
+ }
+ const treeDom = this.#walk(structTree);
+ treeDom?.classList.add("structTree");
+ return this.#treeDom = treeDom;
+ }
+ hide() {
+ if (this.#treeDom && !this.#treeDom.hidden) {
+ this.#treeDom.hidden = true;
+ }
+ }
+ show() {
+ if (this.#treeDom?.hidden) {
+ this.#treeDom.hidden = false;
+ }
+ }
+ #setAttributes(structElement, htmlElement) {
+ const {
+ alt,
+ id,
+ lang
+ } = structElement;
+ if (alt !== undefined) {
+ htmlElement.setAttribute("aria-label", removeNullCharacters(alt));
+ }
+ if (id !== undefined) {
+ htmlElement.setAttribute("aria-owns", id);
+ }
+ if (lang !== undefined) {
+ htmlElement.setAttribute("lang", removeNullCharacters(lang, true));
+ }
+ }
+ #walk(node) {
+ if (!node) {
+ return null;
+ }
+ const element = document.createElement("span");
+ if ("role" in node) {
+ const {
+ role
+ } = node;
+ const match = role.match(HEADING_PATTERN);
+ if (match) {
+ element.setAttribute("role", "heading");
+ element.setAttribute("aria-level", match[1]);
+ } else if (PDF_ROLE_TO_HTML_ROLE[role]) {
+ element.setAttribute("role", PDF_ROLE_TO_HTML_ROLE[role]);
+ }
+ }
+ this.#setAttributes(node, element);
+ if (node.children) {
+ if (node.children.length === 1 && "id" in node.children[0]) {
+ this.#setAttributes(node.children[0], element);
+ } else {
+ for (const kid of node.children) {
+ element.append(this.#walk(kid));
+ }
+ }
+ }
+ return element;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/text_accessibility.js
+
+class TextAccessibilityManager {
+ #enabled = false;
+ #textChildren = null;
+ #textNodes = new Map();
+ #waitingElements = new Map();
+ setTextMapping(textDivs) {
+ this.#textChildren = textDivs;
+ }
+ static #compareElementPositions(e1, e2) {
+ const rect1 = e1.getBoundingClientRect();
+ const rect2 = e2.getBoundingClientRect();
+ if (rect1.width === 0 && rect1.height === 0) {
+ return +1;
+ }
+ if (rect2.width === 0 && rect2.height === 0) {
+ return -1;
+ }
+ const top1 = rect1.y;
+ const bot1 = rect1.y + rect1.height;
+ const mid1 = rect1.y + rect1.height / 2;
+ const top2 = rect2.y;
+ const bot2 = rect2.y + rect2.height;
+ const mid2 = rect2.y + rect2.height / 2;
+ if (mid1 <= top2 && mid2 >= bot1) {
+ return -1;
+ }
+ if (mid2 <= top1 && mid1 >= bot2) {
+ return +1;
+ }
+ const centerX1 = rect1.x + rect1.width / 2;
+ const centerX2 = rect2.x + rect2.width / 2;
+ return centerX1 - centerX2;
+ }
+ enable() {
+ if (this.#enabled) {
+ throw new Error("TextAccessibilityManager is already enabled.");
+ }
+ if (!this.#textChildren) {
+ throw new Error("Text divs and strings have not been set.");
+ }
+ this.#enabled = true;
+ this.#textChildren = this.#textChildren.slice();
+ this.#textChildren.sort(TextAccessibilityManager.#compareElementPositions);
+ if (this.#textNodes.size > 0) {
+ const textChildren = this.#textChildren;
+ for (const [id, nodeIndex] of this.#textNodes) {
+ const element = document.getElementById(id);
+ if (!element) {
+ this.#textNodes.delete(id);
+ continue;
+ }
+ this.#addIdToAriaOwns(id, textChildren[nodeIndex]);
+ }
+ }
+ for (const [element, isRemovable] of this.#waitingElements) {
+ this.addPointerInTextLayer(element, isRemovable);
+ }
+ this.#waitingElements.clear();
+ }
+ disable() {
+ if (!this.#enabled) {
+ return;
+ }
+ this.#waitingElements.clear();
+ this.#textChildren = null;
+ this.#enabled = false;
+ }
+ removePointerInTextLayer(element) {
+ if (!this.#enabled) {
+ this.#waitingElements.delete(element);
+ return;
+ }
+ const children = this.#textChildren;
+ if (!children || children.length === 0) {
+ return;
+ }
+ const {
+ id
+ } = element;
+ const nodeIndex = this.#textNodes.get(id);
+ if (nodeIndex === undefined) {
+ return;
+ }
+ const node = children[nodeIndex];
+ this.#textNodes.delete(id);
+ let owns = node.getAttribute("aria-owns");
+ if (owns?.includes(id)) {
+ owns = owns.split(" ").filter(x => x !== id).join(" ");
+ if (owns) {
+ node.setAttribute("aria-owns", owns);
+ } else {
+ node.removeAttribute("aria-owns");
+ node.setAttribute("role", "presentation");
+ }
+ }
+ }
+ #addIdToAriaOwns(id, node) {
+ const owns = node.getAttribute("aria-owns");
+ if (!owns?.includes(id)) {
+ node.setAttribute("aria-owns", owns ? `${owns} ${id}` : id);
+ }
+ node.removeAttribute("role");
+ }
+ addPointerInTextLayer(element, isRemovable) {
+ const {
+ id
+ } = element;
+ if (!id) {
+ return null;
+ }
+ if (!this.#enabled) {
+ this.#waitingElements.set(element, isRemovable);
+ return null;
+ }
+ if (isRemovable) {
+ this.removePointerInTextLayer(element);
+ }
+ const children = this.#textChildren;
+ if (!children || children.length === 0) {
+ return null;
+ }
+ const index = binarySearchFirstItem(children, node => TextAccessibilityManager.#compareElementPositions(element, node) < 0);
+ const nodeIndex = Math.max(0, index - 1);
+ const child = children[nodeIndex];
+ this.#addIdToAriaOwns(id, child);
+ this.#textNodes.set(id, nodeIndex);
+ const parent = child.parentNode;
+ return parent?.classList.contains("markedContent") ? parent.id : null;
+ }
+ moveElementInDOM(container, element, contentElement, isRemovable) {
+ const id = this.addPointerInTextLayer(contentElement, isRemovable);
+ if (!container.hasChildNodes()) {
+ container.append(element);
+ return id;
+ }
+ const children = Array.from(container.childNodes).filter(node => node !== element);
+ if (children.length === 0) {
+ return id;
+ }
+ const elementToCompare = contentElement || element;
+ const index = binarySearchFirstItem(children, node => TextAccessibilityManager.#compareElementPositions(elementToCompare, node) < 0);
+ if (index === 0) {
+ children[0].before(element);
+ } else {
+ children[index - 1].after(element);
+ }
+ return id;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/text_highlighter.js
+class TextHighlighter {
+ #eventAbortController = null;
+ constructor({
+ findController,
+ eventBus,
+ pageIndex
+ }) {
+ this.findController = findController;
+ this.matches = [];
+ this.eventBus = eventBus;
+ this.pageIdx = pageIndex;
+ this.textDivs = null;
+ this.textContentItemsStr = null;
+ this.enabled = false;
+ }
+ setTextMapping(divs, texts) {
+ this.textDivs = divs;
+ this.textContentItemsStr = texts;
+ }
+ enable() {
+ if (!this.textDivs || !this.textContentItemsStr) {
+ throw new Error("Text divs and strings have not been set.");
+ }
+ if (this.enabled) {
+ throw new Error("TextHighlighter is already enabled.");
+ }
+ this.enabled = true;
+ if (!this.#eventAbortController) {
+ this.#eventAbortController = new AbortController();
+ this.eventBus._on("updatetextlayermatches", evt => {
+ if (evt.pageIndex === this.pageIdx || evt.pageIndex === -1) {
+ this._updateMatches();
+ }
+ }, {
+ signal: this.#eventAbortController.signal
+ });
+ }
+ this._updateMatches();
+ }
+ disable() {
+ if (!this.enabled) {
+ return;
+ }
+ this.enabled = false;
+ this.#eventAbortController?.abort();
+ this.#eventAbortController = null;
+ this._updateMatches(true);
+ }
+ _convertMatches(matches, matchesLength) {
+ if (!matches) {
+ return [];
+ }
+ const {
+ textContentItemsStr
+ } = this;
+ let i = 0,
+ iIndex = 0;
+ const end = textContentItemsStr.length - 1;
+ const result = [];
+ for (let m = 0, mm = matches.length; m < mm; m++) {
+ let matchIdx = matches[m];
+ while (i !== end && matchIdx >= iIndex + textContentItemsStr[i].length) {
+ iIndex += textContentItemsStr[i].length;
+ i++;
+ }
+ if (i === textContentItemsStr.length) {
+ console.error("Could not find a matching mapping");
+ }
+ const match = {
+ begin: {
+ divIdx: i,
+ offset: matchIdx - iIndex
+ }
+ };
+ matchIdx += matchesLength[m];
+ while (i !== end && matchIdx > iIndex + textContentItemsStr[i].length) {
+ iIndex += textContentItemsStr[i].length;
+ i++;
+ }
+ match.end = {
+ divIdx: i,
+ offset: matchIdx - iIndex
+ };
+ result.push(match);
+ }
+ return result;
+ }
+ _renderMatches(matches) {
+ if (matches.length === 0) {
+ return;
+ }
+ const {
+ findController,
+ pageIdx
+ } = this;
+ const {
+ textContentItemsStr,
+ textDivs
+ } = this;
+ const isSelectedPage = pageIdx === findController.selected.pageIdx;
+ const selectedMatchIdx = findController.selected.matchIdx;
+ const highlightAll = findController.state.highlightAll;
+ let prevEnd = null;
+ const infinity = {
+ divIdx: -1,
+ offset: undefined
+ };
+ function beginText(begin, className) {
+ const divIdx = begin.divIdx;
+ textDivs[divIdx].textContent = "";
+ return appendTextToDiv(divIdx, 0, begin.offset, className);
+ }
+ function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
+ let div = textDivs[divIdx];
+ if (div.nodeType === Node.TEXT_NODE) {
+ const span = document.createElement("span");
+ div.before(span);
+ span.append(div);
+ textDivs[divIdx] = span;
+ div = span;
+ }
+ const content = textContentItemsStr[divIdx].substring(fromOffset, toOffset);
+ const node = document.createTextNode(content);
+ if (className) {
+ const span = document.createElement("span");
+ span.className = `${className} appended`;
+ span.append(node);
+ div.append(span);
+ return className.includes("selected") ? span.offsetLeft : 0;
+ }
+ div.append(node);
+ return 0;
+ }
+ let i0 = selectedMatchIdx,
+ i1 = i0 + 1;
+ if (highlightAll) {
+ i0 = 0;
+ i1 = matches.length;
+ } else if (!isSelectedPage) {
+ return;
+ }
+ let lastDivIdx = -1;
+ let lastOffset = -1;
+ for (let i = i0; i < i1; i++) {
+ const match = matches[i];
+ const begin = match.begin;
+ if (begin.divIdx === lastDivIdx && begin.offset === lastOffset) {
+ continue;
+ }
+ lastDivIdx = begin.divIdx;
+ lastOffset = begin.offset;
+ const end = match.end;
+ const isSelected = isSelectedPage && i === selectedMatchIdx;
+ const highlightSuffix = isSelected ? " selected" : "";
+ let selectedLeft = 0;
+ if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
+ if (prevEnd !== null) {
+ appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
+ }
+ beginText(begin);
+ } else {
+ appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
+ }
+ if (begin.divIdx === end.divIdx) {
+ selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, end.offset, "highlight" + highlightSuffix);
+ } else {
+ selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, infinity.offset, "highlight begin" + highlightSuffix);
+ for (let n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
+ textDivs[n0].className = "highlight middle" + highlightSuffix;
+ }
+ beginText(end, "highlight end" + highlightSuffix);
+ }
+ prevEnd = end;
+ if (isSelected) {
+ findController.scrollMatchIntoView({
+ element: textDivs[begin.divIdx],
+ selectedLeft,
+ pageIndex: pageIdx,
+ matchIndex: selectedMatchIdx
+ });
+ }
+ }
+ if (prevEnd) {
+ appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
+ }
+ }
+ _updateMatches(reset = false) {
+ if (!this.enabled && !reset) {
+ return;
+ }
+ const {
+ findController,
+ matches,
+ pageIdx
+ } = this;
+ const {
+ textContentItemsStr,
+ textDivs
+ } = this;
+ let clearedUntilDivIdx = -1;
+ for (const match of matches) {
+ const begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
+ for (let n = begin, end = match.end.divIdx; n <= end; n++) {
+ const div = textDivs[n];
+ div.textContent = textContentItemsStr[n];
+ div.className = "";
+ }
+ clearedUntilDivIdx = match.end.divIdx + 1;
+ }
+ if (!findController?.highlightMatches || reset) {
+ return;
+ }
+ const pageMatches = findController.pageMatches[pageIdx] || null;
+ const pageMatchesLength = findController.pageMatchesLength[pageIdx] || null;
+ this.matches = this._convertMatches(pageMatches, pageMatchesLength);
+ this._renderMatches(this.matches);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/text_layer_builder.js
+
+
+class TextLayerBuilder {
+ #enablePermissions = false;
+ #onAppend = null;
+ #renderingDone = false;
+ #textLayer = null;
+ static #textLayers = new Map();
+ static #selectionChangeAbortController = null;
+ constructor({
+ pdfPage,
+ highlighter = null,
+ accessibilityManager = null,
+ enablePermissions = false,
+ onAppend = null
+ }) {
+ this.pdfPage = pdfPage;
+ this.highlighter = highlighter;
+ this.accessibilityManager = accessibilityManager;
+ this.#enablePermissions = enablePermissions === true;
+ this.#onAppend = onAppend;
+ this.div = document.createElement("div");
+ this.div.tabIndex = 0;
+ this.div.className = "textLayer";
+ }
+ #finishRendering() {
+ this.#renderingDone = true;
+ const endOfContent = document.createElement("div");
+ endOfContent.className = "endOfContent";
+ this.div.append(endOfContent);
+ this.#bindMouse(endOfContent);
+ }
+ async render(viewport, textContentParams = null) {
+ if (this.#renderingDone && this.#textLayer) {
+ this.#textLayer.update({
+ viewport,
+ onBefore: this.hide.bind(this)
+ });
+ this.show();
+ return;
+ }
+ this.cancel();
+ this.#textLayer = new TextLayer({
+ textContentSource: this.pdfPage.streamTextContent(textContentParams || {
+ includeMarkedContent: true,
+ disableNormalization: true
+ }),
+ container: this.div,
+ viewport
+ });
+ const {
+ textDivs,
+ textContentItemsStr
+ } = this.#textLayer;
+ this.highlighter?.setTextMapping(textDivs, textContentItemsStr);
+ this.accessibilityManager?.setTextMapping(textDivs);
+ await this.#textLayer.render();
+ this.#finishRendering();
+ this.#onAppend?.(this.div);
+ this.highlighter?.enable();
+ this.accessibilityManager?.enable();
+ }
+ hide() {
+ if (!this.div.hidden && this.#renderingDone) {
+ this.highlighter?.disable();
+ this.div.hidden = true;
+ }
+ }
+ show() {
+ if (this.div.hidden && this.#renderingDone) {
+ this.div.hidden = false;
+ this.highlighter?.enable();
+ }
+ }
+ cancel() {
+ this.#textLayer?.cancel();
+ this.#textLayer = null;
+ this.highlighter?.disable();
+ this.accessibilityManager?.disable();
+ TextLayerBuilder.#removeGlobalSelectionListener(this.div);
+ }
+ #bindMouse(end) {
+ const {
+ div
+ } = this;
+ div.addEventListener("mousedown", evt => {
+ end.classList.add("active");
+ });
+ div.addEventListener("copy", event => {
+ if (!this.#enablePermissions) {
+ const selection = document.getSelection();
+ event.clipboardData.setData("text/plain", removeNullCharacters(normalizeUnicode(selection.toString())));
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ });
+ TextLayerBuilder.#textLayers.set(div, end);
+ TextLayerBuilder.#enableGlobalSelectionListener();
+ }
+ static #removeGlobalSelectionListener(textLayerDiv) {
+ this.#textLayers.delete(textLayerDiv);
+ if (this.#textLayers.size === 0) {
+ this.#selectionChangeAbortController?.abort();
+ this.#selectionChangeAbortController = null;
+ }
+ }
+ static #enableGlobalSelectionListener() {
+ if (this.#selectionChangeAbortController) {
+ return;
+ }
+ this.#selectionChangeAbortController = new AbortController();
+ const {
+ signal
+ } = this.#selectionChangeAbortController;
+ const reset = (end, textLayer) => {
+ textLayer.append(end);
+ end.style.width = "";
+ end.style.height = "";
+ end.classList.remove("active");
+ };
+ document.addEventListener("pointerup", () => {
+ this.#textLayers.forEach(reset);
+ }, {
+ signal
+ });
+ var isFirefox, prevRange;
+ document.addEventListener("selectionchange", () => {
+ const selection = document.getSelection();
+ if (selection.rangeCount === 0) {
+ this.#textLayers.forEach(reset);
+ return;
+ }
+ const activeTextLayers = new Set();
+ for (let i = 0; i < selection.rangeCount; i++) {
+ const range = selection.getRangeAt(i);
+ for (const textLayerDiv of this.#textLayers.keys()) {
+ if (!activeTextLayers.has(textLayerDiv) && range.intersectsNode(textLayerDiv)) {
+ activeTextLayers.add(textLayerDiv);
+ }
+ }
+ }
+ for (const [textLayerDiv, endDiv] of this.#textLayers) {
+ if (activeTextLayers.has(textLayerDiv)) {
+ endDiv.classList.add("active");
+ } else {
+ reset(endDiv, textLayerDiv);
+ }
+ }
+ isFirefox ??= getComputedStyle(this.#textLayers.values().next().value).getPropertyValue("-moz-user-select") === "none";
+ if (isFirefox) {
+ return;
+ }
+ const range = selection.getRangeAt(0);
+ const modifyStart = prevRange && (range.compareBoundaryPoints(Range.END_TO_END, prevRange) === 0 || range.compareBoundaryPoints(Range.START_TO_END, prevRange) === 0);
+ let anchor = modifyStart ? range.startContainer : range.endContainer;
+ if (anchor.nodeType === Node.TEXT_NODE) {
+ anchor = anchor.parentNode;
+ }
+ const parentTextLayer = anchor.parentElement.closest(".textLayer");
+ const endDiv = this.#textLayers.get(parentTextLayer);
+ if (endDiv) {
+ endDiv.style.width = parentTextLayer.style.width;
+ endDiv.style.height = parentTextLayer.style.height;
+ anchor.parentElement.insertBefore(endDiv, modifyStart ? anchor : anchor.nextSibling);
+ }
+ prevRange = range.cloneRange();
+ }, {
+ signal
+ });
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_page_view.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+const DEFAULT_LAYER_PROPERTIES = null;
+const LAYERS_ORDER = new Map([["canvasWrapper", 0], ["textLayer", 1], ["annotationLayer", 2], ["annotationEditorLayer", 3], ["xfaLayer", 3]]);
+class PDFPageView {
+ #annotationMode = AnnotationMode.ENABLE_FORMS;
+ #hasRestrictedScaling = false;
+ #layerProperties = null;
+ #loadingId = null;
+ #previousRotation = null;
+ #renderError = null;
+ #renderingState = RenderingStates.INITIAL;
+ #textLayerMode = TextLayerMode.ENABLE;
+ #useThumbnailCanvas = {
+ directDrawing: true,
+ initialOptionalContent: true,
+ regularAnnotations: true
+ };
+ #viewportMap = new WeakMap();
+ #layers = [null, null, null, null];
+ constructor(options) {
+ const container = options.container;
+ const defaultViewport = options.defaultViewport;
+ this.id = options.id;
+ this.renderingId = "page" + this.id;
+ this.#layerProperties = options.layerProperties || DEFAULT_LAYER_PROPERTIES;
+ this.pdfPage = null;
+ this.pageLabel = null;
+ this.rotation = 0;
+ this.scale = options.scale || DEFAULT_SCALE;
+ this.viewport = defaultViewport;
+ this.pdfPageRotate = defaultViewport.rotation;
+ this._optionalContentConfigPromise = options.optionalContentConfigPromise || null;
+ this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
+ this.#annotationMode = options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
+ this.imageResourcesPath = options.imageResourcesPath || "";
+ this.maxCanvasPixels = options.maxCanvasPixels ?? AppOptions.get("maxCanvasPixels");
+ this.pageColors = options.pageColors || null;
+ this.eventBus = options.eventBus;
+ this.renderingQueue = options.renderingQueue;
+ this.l10n = options.l10n;
+ this.l10n ||= new genericl10n_GenericL10n();
+ this.renderTask = null;
+ this.resume = null;
+ this._isStandalone = !this.renderingQueue?.hasViewer();
+ this._container = container;
+ this._annotationCanvasMap = null;
+ this.annotationLayer = null;
+ this.annotationEditorLayer = null;
+ this.textLayer = null;
+ this.zoomLayer = null;
+ this.xfaLayer = null;
+ this.structTreeLayer = null;
+ this.drawLayer = null;
+ const div = document.createElement("div");
+ div.className = "page";
+ div.setAttribute("data-page-number", this.id);
+ div.setAttribute("role", "region");
+ div.setAttribute("data-l10n-id", "pdfjs-page-landmark");
+ div.setAttribute("data-l10n-args", JSON.stringify({
+ page: this.id
+ }));
+ this.div = div;
+ this.#setDimensions();
+ container?.append(div);
+ if (this._isStandalone) {
+ container?.style.setProperty("--scale-factor", this.scale * PixelsPerInch.PDF_TO_CSS_UNITS);
+ const {
+ optionalContentConfigPromise
+ } = options;
+ if (optionalContentConfigPromise) {
+ optionalContentConfigPromise.then(optionalContentConfig => {
+ if (optionalContentConfigPromise !== this._optionalContentConfigPromise) {
+ return;
+ }
+ this.#useThumbnailCanvas.initialOptionalContent = optionalContentConfig.hasInitialVisibility;
+ });
+ }
+ if (!options.l10n) {
+ this.l10n.translate(this.div);
+ }
+ }
+ }
+ #addLayer(div, name) {
+ const pos = LAYERS_ORDER.get(name);
+ const oldDiv = this.#layers[pos];
+ this.#layers[pos] = div;
+ if (oldDiv) {
+ oldDiv.replaceWith(div);
+ return;
+ }
+ for (let i = pos - 1; i >= 0; i--) {
+ const layer = this.#layers[i];
+ if (layer) {
+ layer.after(div);
+ return;
+ }
+ }
+ this.div.prepend(div);
+ }
+ get renderingState() {
+ return this.#renderingState;
+ }
+ set renderingState(state) {
+ if (state === this.#renderingState) {
+ return;
+ }
+ this.#renderingState = state;
+ if (this.#loadingId) {
+ clearTimeout(this.#loadingId);
+ this.#loadingId = null;
+ }
+ switch (state) {
+ case RenderingStates.PAUSED:
+ this.div.classList.remove("loading");
+ break;
+ case RenderingStates.RUNNING:
+ this.div.classList.add("loadingIcon");
+ this.#loadingId = setTimeout(() => {
+ this.div.classList.add("loading");
+ this.#loadingId = null;
+ }, 0);
+ break;
+ case RenderingStates.INITIAL:
+ case RenderingStates.FINISHED:
+ this.div.classList.remove("loadingIcon", "loading");
+ break;
+ }
+ }
+ #setDimensions() {
+ const {
+ viewport
+ } = this;
+ if (this.pdfPage) {
+ if (this.#previousRotation === viewport.rotation) {
+ return;
+ }
+ this.#previousRotation = viewport.rotation;
+ }
+ setLayerDimensions(this.div, viewport, true, false);
+ }
+ setPdfPage(pdfPage) {
+ if (this._isStandalone && (this.pageColors?.foreground === "CanvasText" || this.pageColors?.background === "Canvas")) {
+ this._container?.style.setProperty("--hcm-highlight-filter", pdfPage.filterFactory.addHighlightHCMFilter("highlight", "CanvasText", "Canvas", "HighlightText", "Highlight"));
+ this._container?.style.setProperty("--hcm-highlight-selected-filter", pdfPage.filterFactory.addHighlightHCMFilter("highlight_selected", "CanvasText", "Canvas", "HighlightText", "Highlight"));
+ }
+ this.pdfPage = pdfPage;
+ this.pdfPageRotate = pdfPage.rotate;
+ const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
+ this.viewport = pdfPage.getViewport({
+ scale: this.scale * PixelsPerInch.PDF_TO_CSS_UNITS,
+ rotation: totalRotation
+ });
+ this.#setDimensions();
+ this.reset();
+ }
+ destroy() {
+ this.reset();
+ this.pdfPage?.cleanup();
+ }
+ get _textHighlighter() {
+ return shadow(this, "_textHighlighter", new TextHighlighter({
+ pageIndex: this.id - 1,
+ eventBus: this.eventBus,
+ findController: this.#layerProperties.findController
+ }));
+ }
+ #dispatchLayerRendered(name, error) {
+ this.eventBus.dispatch(name, {
+ source: this,
+ pageNumber: this.id,
+ error
+ });
+ }
+ async #renderAnnotationLayer() {
+ let error = null;
+ try {
+ await this.annotationLayer.render(this.viewport, "display");
+ } catch (ex) {
+ console.error(`#renderAnnotationLayer: "${ex}".`);
+ error = ex;
+ } finally {
+ this.#dispatchLayerRendered("annotationlayerrendered", error);
+ }
+ }
+ async #renderAnnotationEditorLayer() {
+ let error = null;
+ try {
+ await this.annotationEditorLayer.render(this.viewport, "display");
+ } catch (ex) {
+ console.error(`#renderAnnotationEditorLayer: "${ex}".`);
+ error = ex;
+ } finally {
+ this.#dispatchLayerRendered("annotationeditorlayerrendered", error);
+ }
+ }
+ async #renderDrawLayer() {
+ try {
+ await this.drawLayer.render("display");
+ } catch (ex) {
+ console.error(`#renderDrawLayer: "${ex}".`);
+ }
+ }
+ async #renderXfaLayer() {
+ let error = null;
+ try {
+ const result = await this.xfaLayer.render(this.viewport, "display");
+ if (result?.textDivs && this._textHighlighter) {
+ this.#buildXfaTextContentItems(result.textDivs);
+ }
+ } catch (ex) {
+ console.error(`#renderXfaLayer: "${ex}".`);
+ error = ex;
+ } finally {
+ if (this.xfaLayer?.div) {
+ this.l10n.pause();
+ this.#addLayer(this.xfaLayer.div, "xfaLayer");
+ this.l10n.resume();
+ }
+ this.#dispatchLayerRendered("xfalayerrendered", error);
+ }
+ }
+ async #renderTextLayer() {
+ if (!this.textLayer) {
+ return;
+ }
+ let error = null;
+ try {
+ await this.textLayer.render(this.viewport);
+ } catch (ex) {
+ if (ex instanceof AbortException) {
+ return;
+ }
+ console.error(`#renderTextLayer: "${ex}".`);
+ error = ex;
+ }
+ this.#dispatchLayerRendered("textlayerrendered", error);
+ this.#renderStructTreeLayer();
+ }
+ async #renderStructTreeLayer() {
+ if (!this.textLayer) {
+ return;
+ }
+ this.structTreeLayer ||= new StructTreeLayerBuilder();
+ const tree = await (!this.structTreeLayer.renderingDone ? this.pdfPage.getStructTree() : null);
+ const treeDom = this.structTreeLayer?.render(tree);
+ if (treeDom) {
+ this.l10n.pause();
+ this.canvas?.append(treeDom);
+ this.l10n.resume();
+ }
+ this.structTreeLayer?.show();
+ }
+ async #buildXfaTextContentItems(textDivs) {
+ const text = await this.pdfPage.getTextContent();
+ const items = [];
+ for (const item of text.items) {
+ items.push(item.str);
+ }
+ this._textHighlighter.setTextMapping(textDivs, items);
+ this._textHighlighter.enable();
+ }
+ _resetZoomLayer(removeFromDOM = false) {
+ if (!this.zoomLayer) {
+ return;
+ }
+ const zoomLayerCanvas = this.zoomLayer.firstChild;
+ this.#viewportMap.delete(zoomLayerCanvas);
+ zoomLayerCanvas.width = 0;
+ zoomLayerCanvas.height = 0;
+ if (removeFromDOM) {
+ this.zoomLayer.remove();
+ }
+ this.zoomLayer = null;
+ }
+ reset({
+ keepZoomLayer = false,
+ keepAnnotationLayer = false,
+ keepAnnotationEditorLayer = false,
+ keepXfaLayer = false,
+ keepTextLayer = false
+ } = {}) {
+ this.cancelRendering({
+ keepAnnotationLayer,
+ keepAnnotationEditorLayer,
+ keepXfaLayer,
+ keepTextLayer
+ });
+ this.renderingState = RenderingStates.INITIAL;
+ const div = this.div;
+ const childNodes = div.childNodes,
+ zoomLayerNode = keepZoomLayer && this.zoomLayer || null,
+ annotationLayerNode = keepAnnotationLayer && this.annotationLayer?.div || null,
+ annotationEditorLayerNode = keepAnnotationEditorLayer && this.annotationEditorLayer?.div || null,
+ xfaLayerNode = keepXfaLayer && this.xfaLayer?.div || null,
+ textLayerNode = keepTextLayer && this.textLayer?.div || null;
+ for (let i = childNodes.length - 1; i >= 0; i--) {
+ const node = childNodes[i];
+ switch (node) {
+ case zoomLayerNode:
+ case annotationLayerNode:
+ case annotationEditorLayerNode:
+ case xfaLayerNode:
+ case textLayerNode:
+ continue;
+ }
+ node.remove();
+ const layerIndex = this.#layers.indexOf(node);
+ if (layerIndex >= 0) {
+ this.#layers[layerIndex] = null;
+ }
+ }
+ div.removeAttribute("data-loaded");
+ if (annotationLayerNode) {
+ this.annotationLayer.hide();
+ }
+ if (annotationEditorLayerNode) {
+ this.annotationEditorLayer.hide();
+ }
+ if (xfaLayerNode) {
+ this.xfaLayer.hide();
+ }
+ if (textLayerNode) {
+ this.textLayer.hide();
+ }
+ this.structTreeLayer?.hide();
+ if (!zoomLayerNode) {
+ if (this.canvas) {
+ this.#viewportMap.delete(this.canvas);
+ this.canvas.width = 0;
+ this.canvas.height = 0;
+ delete this.canvas;
+ }
+ this._resetZoomLayer();
+ }
+ }
+ update({
+ scale = 0,
+ rotation = null,
+ optionalContentConfigPromise = null,
+ drawingDelay = -1
+ }) {
+ this.scale = scale || this.scale;
+ if (typeof rotation === "number") {
+ this.rotation = rotation;
+ }
+ if (optionalContentConfigPromise instanceof Promise) {
+ this._optionalContentConfigPromise = optionalContentConfigPromise;
+ optionalContentConfigPromise.then(optionalContentConfig => {
+ if (optionalContentConfigPromise !== this._optionalContentConfigPromise) {
+ return;
+ }
+ this.#useThumbnailCanvas.initialOptionalContent = optionalContentConfig.hasInitialVisibility;
+ });
+ }
+ this.#useThumbnailCanvas.directDrawing = true;
+ const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
+ this.viewport = this.viewport.clone({
+ scale: this.scale * PixelsPerInch.PDF_TO_CSS_UNITS,
+ rotation: totalRotation
+ });
+ this.#setDimensions();
+ if (this._isStandalone) {
+ this._container?.style.setProperty("--scale-factor", this.viewport.scale);
+ }
+ if (this.canvas) {
+ let onlyCssZoom = false;
+ if (this.#hasRestrictedScaling) {
+ if (this.maxCanvasPixels === 0) {
+ onlyCssZoom = true;
+ } else if (this.maxCanvasPixels > 0) {
+ const {
+ width,
+ height
+ } = this.viewport;
+ const {
+ sx,
+ sy
+ } = this.outputScale;
+ onlyCssZoom = (Math.floor(width) * sx | 0) * (Math.floor(height) * sy | 0) > this.maxCanvasPixels;
+ }
+ }
+ const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000;
+ if (postponeDrawing || onlyCssZoom) {
+ if (postponeDrawing && !onlyCssZoom && this.renderingState !== RenderingStates.FINISHED) {
+ this.cancelRendering({
+ keepZoomLayer: true,
+ keepAnnotationLayer: true,
+ keepAnnotationEditorLayer: true,
+ keepXfaLayer: true,
+ keepTextLayer: true,
+ cancelExtraDelay: drawingDelay
+ });
+ this.renderingState = RenderingStates.FINISHED;
+ this.#useThumbnailCanvas.directDrawing = false;
+ }
+ this.cssTransform({
+ target: this.canvas,
+ redrawAnnotationLayer: true,
+ redrawAnnotationEditorLayer: true,
+ redrawXfaLayer: true,
+ redrawTextLayer: !postponeDrawing,
+ hideTextLayer: postponeDrawing
+ });
+ if (postponeDrawing) {
+ return;
+ }
+ this.eventBus.dispatch("pagerendered", {
+ source: this,
+ pageNumber: this.id,
+ cssTransform: true,
+ timestamp: performance.now(),
+ error: this.#renderError
+ });
+ return;
+ }
+ if (!this.zoomLayer && !this.canvas.hidden) {
+ this.zoomLayer = this.canvas.parentNode;
+ this.zoomLayer.style.position = "absolute";
+ }
+ }
+ if (this.zoomLayer) {
+ this.cssTransform({
+ target: this.zoomLayer.firstChild
+ });
+ }
+ this.reset({
+ keepZoomLayer: true,
+ keepAnnotationLayer: true,
+ keepAnnotationEditorLayer: true,
+ keepXfaLayer: true,
+ keepTextLayer: true
+ });
+ }
+ cancelRendering({
+ keepAnnotationLayer = false,
+ keepAnnotationEditorLayer = false,
+ keepXfaLayer = false,
+ keepTextLayer = false,
+ cancelExtraDelay = 0
+ } = {}) {
+ if (this.renderTask) {
+ this.renderTask.cancel(cancelExtraDelay);
+ this.renderTask = null;
+ }
+ this.resume = null;
+ if (this.textLayer && (!keepTextLayer || !this.textLayer.div)) {
+ this.textLayer.cancel();
+ this.textLayer = null;
+ }
+ if (this.structTreeLayer && !this.textLayer) {
+ this.structTreeLayer = null;
+ }
+ if (this.annotationLayer && (!keepAnnotationLayer || !this.annotationLayer.div)) {
+ this.annotationLayer.cancel();
+ this.annotationLayer = null;
+ this._annotationCanvasMap = null;
+ }
+ if (this.annotationEditorLayer && (!keepAnnotationEditorLayer || !this.annotationEditorLayer.div)) {
+ if (this.drawLayer) {
+ this.drawLayer.cancel();
+ this.drawLayer = null;
+ }
+ this.annotationEditorLayer.cancel();
+ this.annotationEditorLayer = null;
+ }
+ if (this.xfaLayer && (!keepXfaLayer || !this.xfaLayer.div)) {
+ this.xfaLayer.cancel();
+ this.xfaLayer = null;
+ this._textHighlighter?.disable();
+ }
+ }
+ cssTransform({
+ target,
+ redrawAnnotationLayer = false,
+ redrawAnnotationEditorLayer = false,
+ redrawXfaLayer = false,
+ redrawTextLayer = false,
+ hideTextLayer = false
+ }) {
+ if (!target.hasAttribute("zooming")) {
+ target.setAttribute("zooming", true);
+ const {
+ style
+ } = target;
+ style.width = style.height = "";
+ }
+ const originalViewport = this.#viewportMap.get(target);
+ if (this.viewport !== originalViewport) {
+ const relativeRotation = this.viewport.rotation - originalViewport.rotation;
+ const absRotation = Math.abs(relativeRotation);
+ let scaleX = 1,
+ scaleY = 1;
+ if (absRotation === 90 || absRotation === 270) {
+ const {
+ width,
+ height
+ } = this.viewport;
+ scaleX = height / width;
+ scaleY = width / height;
+ }
+ target.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX}, ${scaleY})`;
+ }
+ if (redrawAnnotationLayer && this.annotationLayer) {
+ this.#renderAnnotationLayer();
+ }
+ if (redrawAnnotationEditorLayer && this.annotationEditorLayer) {
+ if (this.drawLayer) {
+ this.#renderDrawLayer();
+ }
+ this.#renderAnnotationEditorLayer();
+ }
+ if (redrawXfaLayer && this.xfaLayer) {
+ this.#renderXfaLayer();
+ }
+ if (this.textLayer) {
+ if (hideTextLayer) {
+ this.textLayer.hide();
+ this.structTreeLayer?.hide();
+ } else if (redrawTextLayer) {
+ this.#renderTextLayer();
+ }
+ }
+ }
+ get width() {
+ return this.viewport.width;
+ }
+ get height() {
+ return this.viewport.height;
+ }
+ getPagePoint(x, y) {
+ return this.viewport.convertToPdfPoint(x, y);
+ }
+ async #finishRenderTask(renderTask, error = null) {
+ if (renderTask === this.renderTask) {
+ this.renderTask = null;
+ }
+ if (error instanceof RenderingCancelledException) {
+ this.#renderError = null;
+ return;
+ }
+ this.#renderError = error;
+ this.renderingState = RenderingStates.FINISHED;
+ this._resetZoomLayer(true);
+ this.#useThumbnailCanvas.regularAnnotations = !renderTask.separateAnnots;
+ this.eventBus.dispatch("pagerendered", {
+ source: this,
+ pageNumber: this.id,
+ cssTransform: false,
+ timestamp: performance.now(),
+ error: this.#renderError
+ });
+ if (error) {
+ throw error;
+ }
+ }
+ async draw() {
+ if (this.renderingState !== RenderingStates.INITIAL) {
+ console.error("Must be in new state before drawing");
+ this.reset();
+ }
+ const {
+ div,
+ l10n,
+ pageColors,
+ pdfPage,
+ viewport
+ } = this;
+ if (!pdfPage) {
+ this.renderingState = RenderingStates.FINISHED;
+ throw new Error("pdfPage is not loaded");
+ }
+ this.renderingState = RenderingStates.RUNNING;
+ const canvasWrapper = document.createElement("div");
+ canvasWrapper.classList.add("canvasWrapper");
+ this.#addLayer(canvasWrapper, "canvasWrapper");
+ if (!this.textLayer && this.#textLayerMode !== TextLayerMode.DISABLE && !pdfPage.isPureXfa) {
+ this._accessibilityManager ||= new TextAccessibilityManager();
+ this.textLayer = new TextLayerBuilder({
+ pdfPage,
+ highlighter: this._textHighlighter,
+ accessibilityManager: this._accessibilityManager,
+ enablePermissions: this.#textLayerMode === TextLayerMode.ENABLE_PERMISSIONS,
+ onAppend: textLayerDiv => {
+ this.l10n.pause();
+ this.#addLayer(textLayerDiv, "textLayer");
+ this.l10n.resume();
+ }
+ });
+ }
+ if (!this.annotationLayer && this.#annotationMode !== AnnotationMode.DISABLE) {
+ const {
+ annotationStorage,
+ annotationEditorUIManager,
+ downloadManager,
+ enableScripting,
+ fieldObjectsPromise,
+ hasJSActionsPromise,
+ linkService
+ } = this.#layerProperties;
+ this._annotationCanvasMap ||= new Map();
+ this.annotationLayer = new AnnotationLayerBuilder({
+ pdfPage,
+ annotationStorage,
+ imageResourcesPath: this.imageResourcesPath,
+ renderForms: this.#annotationMode === AnnotationMode.ENABLE_FORMS,
+ linkService,
+ downloadManager,
+ enableScripting,
+ hasJSActionsPromise,
+ fieldObjectsPromise,
+ annotationCanvasMap: this._annotationCanvasMap,
+ accessibilityManager: this._accessibilityManager,
+ annotationEditorUIManager,
+ onAppend: annotationLayerDiv => {
+ this.#addLayer(annotationLayerDiv, "annotationLayer");
+ }
+ });
+ }
+ const renderContinueCallback = cont => {
+ showCanvas?.(false);
+ if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) {
+ this.renderingState = RenderingStates.PAUSED;
+ this.resume = () => {
+ this.renderingState = RenderingStates.RUNNING;
+ cont();
+ };
+ return;
+ }
+ cont();
+ };
+ const {
+ width,
+ height
+ } = viewport;
+ const canvas = document.createElement("canvas");
+ canvas.setAttribute("role", "presentation");
+ canvas.hidden = true;
+ const hasHCM = !!(pageColors?.background && pageColors?.foreground);
+ let showCanvas = isLastShow => {
+ if (!hasHCM || isLastShow) {
+ canvas.hidden = false;
+ showCanvas = null;
+ }
+ };
+ canvasWrapper.append(canvas);
+ this.canvas = canvas;
+ const ctx = canvas.getContext("2d", {
+ alpha: false
+ });
+ const outputScale = this.outputScale = new OutputScale();
+ if (this.maxCanvasPixels === 0) {
+ const invScale = 1 / this.scale;
+ outputScale.sx *= invScale;
+ outputScale.sy *= invScale;
+ this.#hasRestrictedScaling = true;
+ } else if (this.maxCanvasPixels > 0) {
+ const pixelsInViewport = width * height;
+ const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport);
+ if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
+ outputScale.sx = maxScale;
+ outputScale.sy = maxScale;
+ this.#hasRestrictedScaling = true;
+ } else {
+ this.#hasRestrictedScaling = false;
+ }
+ }
+ const sfx = approximateFraction(outputScale.sx);
+ const sfy = approximateFraction(outputScale.sy);
+ canvas.width = roundToDivide(width * outputScale.sx, sfx[0]);
+ canvas.height = roundToDivide(height * outputScale.sy, sfy[0]);
+ const {
+ style
+ } = canvas;
+ style.width = roundToDivide(width, sfx[1]) + "px";
+ style.height = roundToDivide(height, sfy[1]) + "px";
+ this.#viewportMap.set(canvas, viewport);
+ const transform = outputScale.scaled ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0] : null;
+ const renderContext = {
+ canvasContext: ctx,
+ transform,
+ viewport,
+ annotationMode: this.#annotationMode,
+ optionalContentConfigPromise: this._optionalContentConfigPromise,
+ annotationCanvasMap: this._annotationCanvasMap,
+ pageColors
+ };
+ const renderTask = this.renderTask = pdfPage.render(renderContext);
+ renderTask.onContinue = renderContinueCallback;
+ const resultPromise = renderTask.promise.then(async () => {
+ showCanvas?.(true);
+ await this.#finishRenderTask(renderTask);
+ this.#renderTextLayer();
+ if (this.annotationLayer) {
+ await this.#renderAnnotationLayer();
+ }
+ const {
+ annotationEditorUIManager
+ } = this.#layerProperties;
+ if (!annotationEditorUIManager) {
+ return;
+ }
+ this.drawLayer ||= new DrawLayerBuilder({
+ pageIndex: this.id
+ });
+ await this.#renderDrawLayer();
+ this.drawLayer.setParent(canvasWrapper);
+ if (!this.annotationEditorLayer) {
+ this.annotationEditorLayer = new AnnotationEditorLayerBuilder({
+ uiManager: annotationEditorUIManager,
+ pdfPage,
+ l10n,
+ accessibilityManager: this._accessibilityManager,
+ annotationLayer: this.annotationLayer?.annotationLayer,
+ textLayer: this.textLayer,
+ drawLayer: this.drawLayer.getDrawLayer(),
+ onAppend: annotationEditorLayerDiv => {
+ this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer");
+ }
+ });
+ }
+ this.#renderAnnotationEditorLayer();
+ }, error => {
+ if (!(error instanceof RenderingCancelledException)) {
+ showCanvas?.(true);
+ }
+ return this.#finishRenderTask(renderTask, error);
+ });
+ if (pdfPage.isPureXfa) {
+ if (!this.xfaLayer) {
+ const {
+ annotationStorage,
+ linkService
+ } = this.#layerProperties;
+ this.xfaLayer = new XfaLayerBuilder({
+ pdfPage,
+ annotationStorage,
+ linkService
+ });
+ }
+ this.#renderXfaLayer();
+ }
+ div.setAttribute("data-loaded", true);
+ this.eventBus.dispatch("pagerender", {
+ source: this,
+ pageNumber: this.id
+ });
+ return resultPromise;
+ }
+ setPageLabel(label) {
+ this.pageLabel = typeof label === "string" ? label : null;
+ this.div.setAttribute("data-l10n-args", JSON.stringify({
+ page: this.pageLabel ?? this.id
+ }));
+ if (this.pageLabel !== null) {
+ this.div.setAttribute("data-page-label", this.pageLabel);
+ } else {
+ this.div.removeAttribute("data-page-label");
+ }
+ }
+ get thumbnailCanvas() {
+ const {
+ directDrawing,
+ initialOptionalContent,
+ regularAnnotations
+ } = this.#useThumbnailCanvas;
+ return directDrawing && initialOptionalContent && regularAnnotations ? this.canvas : null;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/pdf_viewer.js
+
+
+
+
+
+
+const DEFAULT_CACHE_SIZE = 10;
+const PagesCountLimit = {
+ FORCE_SCROLL_MODE_PAGE: 15000,
+ FORCE_LAZY_PAGE_INIT: 7500,
+ PAUSE_EAGER_PAGE_INIT: 250
+};
+function isValidAnnotationEditorMode(mode) {
+ return Object.values(AnnotationEditorType).includes(mode) && mode !== AnnotationEditorType.DISABLE;
+}
+class PDFPageViewBuffer {
+ #buf = new Set();
+ #size = 0;
+ constructor(size) {
+ this.#size = size;
+ }
+ push(view) {
+ const buf = this.#buf;
+ if (buf.has(view)) {
+ buf.delete(view);
+ }
+ buf.add(view);
+ if (buf.size > this.#size) {
+ this.#destroyFirstView();
+ }
+ }
+ resize(newSize, idsToKeep = null) {
+ this.#size = newSize;
+ const buf = this.#buf;
+ if (idsToKeep) {
+ const ii = buf.size;
+ let i = 1;
+ for (const view of buf) {
+ if (idsToKeep.has(view.id)) {
+ buf.delete(view);
+ buf.add(view);
+ }
+ if (++i > ii) {
+ break;
+ }
+ }
+ }
+ while (buf.size > this.#size) {
+ this.#destroyFirstView();
+ }
+ }
+ has(view) {
+ return this.#buf.has(view);
+ }
+ [Symbol.iterator]() {
+ return this.#buf.keys();
+ }
+ #destroyFirstView() {
+ const firstView = this.#buf.keys().next().value;
+ firstView?.destroy();
+ this.#buf.delete(firstView);
+ }
+}
+class PDFViewer {
+ #buffer = null;
+ #altTextManager = null;
+ #annotationEditorHighlightColors = null;
+ #annotationEditorMode = AnnotationEditorType.NONE;
+ #annotationEditorUIManager = null;
+ #annotationMode = AnnotationMode.ENABLE_FORMS;
+ #containerTopLeft = null;
+ #enableHighlightFloatingButton = false;
+ #enablePermissions = false;
+ #eventAbortController = null;
+ #mlManager = null;
+ #getAllTextInProgress = false;
+ #hiddenCopyElement = null;
+ #interruptCopyCondition = false;
+ #previousContainerHeight = 0;
+ #resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this));
+ #scrollModePageState = null;
+ #scaleTimeoutId = null;
+ #textLayerMode = TextLayerMode.ENABLE;
+ constructor(options) {
+ const viewerVersion = "4.3.136";
+ if (version !== viewerVersion) {
+ throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`);
+ }
+ this.container = options.container;
+ this.viewer = options.viewer || options.container.firstElementChild;
+ if (this.container?.tagName !== "DIV" || this.viewer?.tagName !== "DIV") {
+ throw new Error("Invalid `container` and/or `viewer` option.");
+ }
+ if (this.container.offsetParent && getComputedStyle(this.container).position !== "absolute") {
+ throw new Error("The `container` must be absolutely positioned.");
+ }
+ this.#resizeObserver.observe(this.container);
+ this.eventBus = options.eventBus;
+ this.linkService = options.linkService || new SimpleLinkService();
+ this.downloadManager = options.downloadManager || null;
+ this.findController = options.findController || null;
+ this.#altTextManager = options.altTextManager || null;
+ if (this.findController) {
+ this.findController.onIsPageVisible = pageNumber => this._getVisiblePages().ids.has(pageNumber);
+ }
+ this._scriptingManager = options.scriptingManager || null;
+ this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
+ this.#annotationMode = options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
+ this.#annotationEditorMode = options.annotationEditorMode ?? AnnotationEditorType.NONE;
+ this.#annotationEditorHighlightColors = options.annotationEditorHighlightColors || null;
+ this.#enableHighlightFloatingButton = options.enableHighlightFloatingButton === true;
+ this.imageResourcesPath = options.imageResourcesPath || "";
+ this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
+ this.removePageBorders = options.removePageBorders || false;
+ this.maxCanvasPixels = options.maxCanvasPixels;
+ this.l10n = options.l10n;
+ this.l10n ||= new genericl10n_GenericL10n();
+ this.#enablePermissions = options.enablePermissions || false;
+ this.pageColors = options.pageColors || null;
+ this.#mlManager = options.mlManager || null;
+ this.defaultRenderingQueue = !options.renderingQueue;
+ if (this.defaultRenderingQueue) {
+ this.renderingQueue = new PDFRenderingQueue();
+ this.renderingQueue.setViewer(this);
+ } else {
+ this.renderingQueue = options.renderingQueue;
+ }
+ this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this));
+ this.presentationModeState = PresentationModeState.UNKNOWN;
+ this._resetView();
+ if (this.removePageBorders) {
+ this.viewer.classList.add("removePageBorders");
+ }
+ this.#updateContainerHeightCss();
+ this.eventBus._on("thumbnailrendered", ({
+ pageNumber,
+ pdfPage
+ }) => {
+ const pageView = this._pages[pageNumber - 1];
+ if (!this.#buffer.has(pageView)) {
+ pdfPage?.cleanup();
+ }
+ });
+ if (!options.l10n) {
+ this.l10n.translate(this.container);
+ }
+ }
+ get pagesCount() {
+ return this._pages.length;
+ }
+ getPageView(index) {
+ return this._pages[index];
+ }
+ getCachedPageViews() {
+ return new Set(this.#buffer);
+ }
+ get pageViewsReady() {
+ return this._pages.every(pageView => pageView?.pdfPage);
+ }
+ get renderForms() {
+ return this.#annotationMode === AnnotationMode.ENABLE_FORMS;
+ }
+ get enableScripting() {
+ return !!this._scriptingManager;
+ }
+ get currentPageNumber() {
+ return this._currentPageNumber;
+ }
+ set currentPageNumber(val) {
+ if (!Number.isInteger(val)) {
+ throw new Error("Invalid page number.");
+ }
+ if (!this.pdfDocument) {
+ return;
+ }
+ if (!this._setCurrentPageNumber(val, true)) {
+ console.error(`currentPageNumber: "${val}" is not a valid page.`);
+ }
+ }
+ _setCurrentPageNumber(val, resetCurrentPageView = false) {
+ if (this._currentPageNumber === val) {
+ if (resetCurrentPageView) {
+ this.#resetCurrentPageView();
+ }
+ return true;
+ }
+ if (!(0 < val && val <= this.pagesCount)) {
+ return false;
+ }
+ const previous = this._currentPageNumber;
+ this._currentPageNumber = val;
+ this.eventBus.dispatch("pagechanging", {
+ source: this,
+ pageNumber: val,
+ pageLabel: this._pageLabels?.[val - 1] ?? null,
+ previous
+ });
+ if (resetCurrentPageView) {
+ this.#resetCurrentPageView();
+ }
+ return true;
+ }
+ get currentPageLabel() {
+ return this._pageLabels?.[this._currentPageNumber - 1] ?? null;
+ }
+ set currentPageLabel(val) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ let page = val | 0;
+ if (this._pageLabels) {
+ const i = this._pageLabels.indexOf(val);
+ if (i >= 0) {
+ page = i + 1;
+ }
+ }
+ if (!this._setCurrentPageNumber(page, true)) {
+ console.error(`currentPageLabel: "${val}" is not a valid page.`);
+ }
+ }
+ get currentScale() {
+ return this._currentScale !== UNKNOWN_SCALE ? this._currentScale : DEFAULT_SCALE;
+ }
+ set currentScale(val) {
+ if (isNaN(val)) {
+ throw new Error("Invalid numeric scale.");
+ }
+ if (!this.pdfDocument) {
+ return;
+ }
+ this.#setScale(val, {
+ noScroll: false
+ });
+ }
+ get currentScaleValue() {
+ return this._currentScaleValue;
+ }
+ set currentScaleValue(val) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ this.#setScale(val, {
+ noScroll: false
+ });
+ }
+ get pagesRotation() {
+ return this._pagesRotation;
+ }
+ set pagesRotation(rotation) {
+ if (!isValidRotation(rotation)) {
+ throw new Error("Invalid pages rotation angle.");
+ }
+ if (!this.pdfDocument) {
+ return;
+ }
+ rotation %= 360;
+ if (rotation < 0) {
+ rotation += 360;
+ }
+ if (this._pagesRotation === rotation) {
+ return;
+ }
+ this._pagesRotation = rotation;
+ const pageNumber = this._currentPageNumber;
+ this.refresh(true, {
+ rotation
+ });
+ if (this._currentScaleValue) {
+ this.#setScale(this._currentScaleValue, {
+ noScroll: true
+ });
+ }
+ this.eventBus.dispatch("rotationchanging", {
+ source: this,
+ pagesRotation: rotation,
+ pageNumber
+ });
+ if (this.defaultRenderingQueue) {
+ this.update();
+ }
+ }
+ get firstPagePromise() {
+ return this.pdfDocument ? this._firstPageCapability.promise : null;
+ }
+ get onePageRendered() {
+ return this.pdfDocument ? this._onePageRenderedCapability.promise : null;
+ }
+ get pagesPromise() {
+ return this.pdfDocument ? this._pagesCapability.promise : null;
+ }
+ get _layerProperties() {
+ const self = this;
+ return shadow(this, "_layerProperties", {
+ get annotationEditorUIManager() {
+ return self.#annotationEditorUIManager;
+ },
+ get annotationStorage() {
+ return self.pdfDocument?.annotationStorage;
+ },
+ get downloadManager() {
+ return self.downloadManager;
+ },
+ get enableScripting() {
+ return !!self._scriptingManager;
+ },
+ get fieldObjectsPromise() {
+ return self.pdfDocument?.getFieldObjects();
+ },
+ get findController() {
+ return self.findController;
+ },
+ get hasJSActionsPromise() {
+ return self.pdfDocument?.hasJSActions();
+ },
+ get linkService() {
+ return self.linkService;
+ }
+ });
+ }
+ #initializePermissions(permissions) {
+ const params = {
+ annotationEditorMode: this.#annotationEditorMode,
+ annotationMode: this.#annotationMode,
+ textLayerMode: this.#textLayerMode
+ };
+ if (!permissions) {
+ return params;
+ }
+ if (!permissions.includes(PermissionFlag.COPY) && this.#textLayerMode === TextLayerMode.ENABLE) {
+ params.textLayerMode = TextLayerMode.ENABLE_PERMISSIONS;
+ }
+ if (!permissions.includes(PermissionFlag.MODIFY_CONTENTS)) {
+ params.annotationEditorMode = AnnotationEditorType.DISABLE;
+ }
+ if (!permissions.includes(PermissionFlag.MODIFY_ANNOTATIONS) && !permissions.includes(PermissionFlag.FILL_INTERACTIVE_FORMS) && this.#annotationMode === AnnotationMode.ENABLE_FORMS) {
+ params.annotationMode = AnnotationMode.ENABLE;
+ }
+ return params;
+ }
+ async #onePageRenderedOrForceFetch(signal) {
+ if (document.visibilityState === "hidden" || !this.container.offsetParent || this._getVisiblePages().views.length === 0) {
+ return;
+ }
+ const hiddenCapability = Promise.withResolvers();
+ function onVisibilityChange() {
+ if (document.visibilityState === "hidden") {
+ hiddenCapability.resolve();
+ }
+ }
+ document.addEventListener("visibilitychange", onVisibilityChange, {
+ signal
+ });
+ await Promise.race([this._onePageRenderedCapability.promise, hiddenCapability.promise]);
+ document.removeEventListener("visibilitychange", onVisibilityChange);
+ }
+ async getAllText() {
+ const texts = [];
+ const buffer = [];
+ for (let pageNum = 1, pagesCount = this.pdfDocument.numPages; pageNum <= pagesCount; ++pageNum) {
+ if (this.#interruptCopyCondition) {
+ return null;
+ }
+ buffer.length = 0;
+ const page = await this.pdfDocument.getPage(pageNum);
+ const {
+ items
+ } = await page.getTextContent();
+ for (const item of items) {
+ if (item.str) {
+ buffer.push(item.str);
+ }
+ if (item.hasEOL) {
+ buffer.push("\n");
+ }
+ }
+ texts.push(removeNullCharacters(buffer.join("")));
+ }
+ return texts.join("\n");
+ }
+ #copyCallback(textLayerMode, event) {
+ const selection = document.getSelection();
+ const {
+ focusNode,
+ anchorNode
+ } = selection;
+ if (anchorNode && focusNode && selection.containsNode(this.#hiddenCopyElement)) {
+ if (this.#getAllTextInProgress || textLayerMode === TextLayerMode.ENABLE_PERMISSIONS) {
+ event.preventDefault();
+ event.stopPropagation();
+ return;
+ }
+ this.#getAllTextInProgress = true;
+ const savedCursor = this.container.style.cursor;
+ this.container.style.cursor = "wait";
+ const interruptCopy = ev => this.#interruptCopyCondition = ev.key === "Escape";
+ window.addEventListener("keydown", interruptCopy);
+ this.getAllText().then(async text => {
+ if (text !== null) {
+ await navigator.clipboard.writeText(text);
+ }
+ }).catch(reason => {
+ console.warn(`Something goes wrong when extracting the text: ${reason.message}`);
+ }).finally(() => {
+ this.#getAllTextInProgress = false;
+ this.#interruptCopyCondition = false;
+ window.removeEventListener("keydown", interruptCopy);
+ this.container.style.cursor = savedCursor;
+ });
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ setDocument(pdfDocument) {
+ if (this.pdfDocument) {
+ this.eventBus.dispatch("pagesdestroy", {
+ source: this
+ });
+ this._cancelRendering();
+ this._resetView();
+ this.findController?.setDocument(null);
+ this._scriptingManager?.setDocument(null);
+ if (this.#annotationEditorUIManager) {
+ this.#annotationEditorUIManager.destroy();
+ this.#annotationEditorUIManager = null;
+ }
+ }
+ this.pdfDocument = pdfDocument;
+ if (!pdfDocument) {
+ return;
+ }
+ const pagesCount = pdfDocument.numPages;
+ const firstPagePromise = pdfDocument.getPage(1);
+ const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
+ intent: "display"
+ });
+ const permissionsPromise = this.#enablePermissions ? pdfDocument.getPermissions() : Promise.resolve();
+ const {
+ eventBus,
+ pageColors,
+ viewer
+ } = this;
+ this.#eventAbortController = new AbortController();
+ const {
+ signal
+ } = this.#eventAbortController;
+ if (pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) {
+ console.warn("Forcing PAGE-scrolling for performance reasons, given the length of the document.");
+ const mode = this._scrollMode = ScrollMode.PAGE;
+ eventBus.dispatch("scrollmodechanged", {
+ source: this,
+ mode
+ });
+ }
+ this._pagesCapability.promise.then(() => {
+ eventBus.dispatch("pagesloaded", {
+ source: this,
+ pagesCount
+ });
+ }, () => {});
+ const onBeforeDraw = evt => {
+ const pageView = this._pages[evt.pageNumber - 1];
+ if (!pageView) {
+ return;
+ }
+ this.#buffer.push(pageView);
+ };
+ eventBus._on("pagerender", onBeforeDraw, {
+ signal
+ });
+ const onAfterDraw = evt => {
+ if (evt.cssTransform) {
+ return;
+ }
+ this._onePageRenderedCapability.resolve({
+ timestamp: evt.timestamp
+ });
+ eventBus._off("pagerendered", onAfterDraw);
+ };
+ eventBus._on("pagerendered", onAfterDraw, {
+ signal
+ });
+ Promise.all([firstPagePromise, permissionsPromise]).then(([firstPdfPage, permissions]) => {
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ this._firstPageCapability.resolve(firstPdfPage);
+ this._optionalContentConfigPromise = optionalContentConfigPromise;
+ const {
+ annotationEditorMode,
+ annotationMode,
+ textLayerMode
+ } = this.#initializePermissions(permissions);
+ if (textLayerMode !== TextLayerMode.DISABLE) {
+ const element = this.#hiddenCopyElement = document.createElement("div");
+ element.id = "hiddenCopyElement";
+ viewer.before(element);
+ }
+ if (annotationEditorMode !== AnnotationEditorType.DISABLE) {
+ const mode = annotationEditorMode;
+ if (pdfDocument.isPureXfa) {
+ console.warn("Warning: XFA-editing is not implemented.");
+ } else if (isValidAnnotationEditorMode(mode)) {
+ this.#annotationEditorUIManager = new AnnotationEditorUIManager(this.container, viewer, this.#altTextManager, eventBus, pdfDocument, pageColors, this.#annotationEditorHighlightColors, this.#enableHighlightFloatingButton, this.#mlManager);
+ eventBus.dispatch("annotationeditoruimanager", {
+ source: this,
+ uiManager: this.#annotationEditorUIManager
+ });
+ if (mode !== AnnotationEditorType.NONE) {
+ this.#annotationEditorUIManager.updateMode(mode);
+ }
+ } else {
+ console.error(`Invalid AnnotationEditor mode: ${mode}`);
+ }
+ }
+ const viewerElement = this._scrollMode === ScrollMode.PAGE ? null : viewer;
+ const scale = this.currentScale;
+ const viewport = firstPdfPage.getViewport({
+ scale: scale * PixelsPerInch.PDF_TO_CSS_UNITS
+ });
+ viewer.style.setProperty("--scale-factor", viewport.scale);
+ if (pageColors?.foreground === "CanvasText" || pageColors?.background === "Canvas") {
+ viewer.style.setProperty("--hcm-highlight-filter", pdfDocument.filterFactory.addHighlightHCMFilter("highlight", "CanvasText", "Canvas", "HighlightText", "Highlight"));
+ viewer.style.setProperty("--hcm-highlight-selected-filter", pdfDocument.filterFactory.addHighlightHCMFilter("highlight_selected", "CanvasText", "Canvas", "HighlightText", "ButtonText"));
+ }
+ for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
+ const pageView = new PDFPageView({
+ container: viewerElement,
+ eventBus,
+ id: pageNum,
+ scale,
+ defaultViewport: viewport.clone(),
+ optionalContentConfigPromise,
+ renderingQueue: this.renderingQueue,
+ textLayerMode,
+ annotationMode,
+ imageResourcesPath: this.imageResourcesPath,
+ maxCanvasPixels: this.maxCanvasPixels,
+ pageColors,
+ l10n: this.l10n,
+ layerProperties: this._layerProperties
+ });
+ this._pages.push(pageView);
+ }
+ this._pages[0]?.setPdfPage(firstPdfPage);
+ if (this._scrollMode === ScrollMode.PAGE) {
+ this.#ensurePageViewVisible();
+ } else if (this._spreadMode !== SpreadMode.NONE) {
+ this._updateSpreadMode();
+ }
+ this.#onePageRenderedOrForceFetch(signal).then(async () => {
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ this.findController?.setDocument(pdfDocument);
+ this._scriptingManager?.setDocument(pdfDocument);
+ if (this.#hiddenCopyElement) {
+ document.addEventListener("copy", this.#copyCallback.bind(this, textLayerMode), {
+ signal
+ });
+ }
+ if (this.#annotationEditorUIManager) {
+ eventBus.dispatch("annotationeditormodechanged", {
+ source: this,
+ mode: this.#annotationEditorMode
+ });
+ }
+ if (pdfDocument.loadingParams.disableAutoFetch || pagesCount > PagesCountLimit.FORCE_LAZY_PAGE_INIT) {
+ this._pagesCapability.resolve();
+ return;
+ }
+ let getPagesLeft = pagesCount - 1;
+ if (getPagesLeft <= 0) {
+ this._pagesCapability.resolve();
+ return;
+ }
+ for (let pageNum = 2; pageNum <= pagesCount; ++pageNum) {
+ const promise = pdfDocument.getPage(pageNum).then(pdfPage => {
+ const pageView = this._pages[pageNum - 1];
+ if (!pageView.pdfPage) {
+ pageView.setPdfPage(pdfPage);
+ }
+ if (--getPagesLeft === 0) {
+ this._pagesCapability.resolve();
+ }
+ }, reason => {
+ console.error(`Unable to get page ${pageNum} to initialize viewer`, reason);
+ if (--getPagesLeft === 0) {
+ this._pagesCapability.resolve();
+ }
+ });
+ if (pageNum % PagesCountLimit.PAUSE_EAGER_PAGE_INIT === 0) {
+ await promise;
+ }
+ }
+ });
+ eventBus.dispatch("pagesinit", {
+ source: this
+ });
+ pdfDocument.getMetadata().then(({
+ info
+ }) => {
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ if (info.Language) {
+ viewer.lang = info.Language;
+ }
+ });
+ if (this.defaultRenderingQueue) {
+ this.update();
+ }
+ }).catch(reason => {
+ console.error("Unable to initialize viewer", reason);
+ this._pagesCapability.reject(reason);
+ });
+ }
+ setPageLabels(labels) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ if (!labels) {
+ this._pageLabels = null;
+ } else if (!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)) {
+ this._pageLabels = null;
+ console.error(`setPageLabels: Invalid page labels.`);
+ } else {
+ this._pageLabels = labels;
+ }
+ for (let i = 0, ii = this._pages.length; i < ii; i++) {
+ this._pages[i].setPageLabel(this._pageLabels?.[i] ?? null);
+ }
+ }
+ _resetView() {
+ this._pages = [];
+ this._currentPageNumber = 1;
+ this._currentScale = UNKNOWN_SCALE;
+ this._currentScaleValue = null;
+ this._pageLabels = null;
+ this.#buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE);
+ this._location = null;
+ this._pagesRotation = 0;
+ this._optionalContentConfigPromise = null;
+ this._firstPageCapability = Promise.withResolvers();
+ this._onePageRenderedCapability = Promise.withResolvers();
+ this._pagesCapability = Promise.withResolvers();
+ this._scrollMode = ScrollMode.VERTICAL;
+ this._previousScrollMode = ScrollMode.UNKNOWN;
+ this._spreadMode = SpreadMode.NONE;
+ this.#scrollModePageState = {
+ previousPageNumber: 1,
+ scrollDown: true,
+ pages: []
+ };
+ this.#eventAbortController?.abort();
+ this.#eventAbortController = null;
+ this.viewer.textContent = "";
+ this._updateScrollMode();
+ this.viewer.removeAttribute("lang");
+ this.#hiddenCopyElement?.remove();
+ this.#hiddenCopyElement = null;
+ }
+ #ensurePageViewVisible() {
+ if (this._scrollMode !== ScrollMode.PAGE) {
+ throw new Error("#ensurePageViewVisible: Invalid scrollMode value.");
+ }
+ const pageNumber = this._currentPageNumber,
+ state = this.#scrollModePageState,
+ viewer = this.viewer;
+ viewer.textContent = "";
+ state.pages.length = 0;
+ if (this._spreadMode === SpreadMode.NONE && !this.isInPresentationMode) {
+ const pageView = this._pages[pageNumber - 1];
+ viewer.append(pageView.div);
+ state.pages.push(pageView);
+ } else {
+ const pageIndexSet = new Set(),
+ parity = this._spreadMode - 1;
+ if (parity === -1) {
+ pageIndexSet.add(pageNumber - 1);
+ } else if (pageNumber % 2 !== parity) {
+ pageIndexSet.add(pageNumber - 1);
+ pageIndexSet.add(pageNumber);
+ } else {
+ pageIndexSet.add(pageNumber - 2);
+ pageIndexSet.add(pageNumber - 1);
+ }
+ const spread = document.createElement("div");
+ spread.className = "spread";
+ if (this.isInPresentationMode) {
+ const dummyPage = document.createElement("div");
+ dummyPage.className = "dummyPage";
+ spread.append(dummyPage);
+ }
+ for (const i of pageIndexSet) {
+ const pageView = this._pages[i];
+ if (!pageView) {
+ continue;
+ }
+ spread.append(pageView.div);
+ state.pages.push(pageView);
+ }
+ viewer.append(spread);
+ }
+ state.scrollDown = pageNumber >= state.previousPageNumber;
+ state.previousPageNumber = pageNumber;
+ }
+ _scrollUpdate() {
+ if (this.pagesCount === 0) {
+ return;
+ }
+ this.update();
+ }
+ #scrollIntoView(pageView, pageSpot = null) {
+ const {
+ div,
+ id
+ } = pageView;
+ if (this._currentPageNumber !== id) {
+ this._setCurrentPageNumber(id);
+ }
+ if (this._scrollMode === ScrollMode.PAGE) {
+ this.#ensurePageViewVisible();
+ this.update();
+ }
+ if (!pageSpot && !this.isInPresentationMode) {
+ const left = div.offsetLeft + div.clientLeft,
+ right = left + div.clientWidth;
+ const {
+ scrollLeft,
+ clientWidth
+ } = this.container;
+ if (this._scrollMode === ScrollMode.HORIZONTAL || left < scrollLeft || right > scrollLeft + clientWidth) {
+ pageSpot = {
+ left: 0,
+ top: 0
+ };
+ }
+ }
+ scrollIntoView(div, pageSpot);
+ if (!this._currentScaleValue && this._location) {
+ this._location = null;
+ }
+ }
+ #isSameScale(newScale) {
+ return newScale === this._currentScale || Math.abs(newScale - this._currentScale) < 1e-15;
+ }
+ #setScaleUpdatePages(newScale, newValue, {
+ noScroll = false,
+ preset = false,
+ drawingDelay = -1,
+ origin = null
+ }) {
+ this._currentScaleValue = newValue.toString();
+ if (this.#isSameScale(newScale)) {
+ if (preset) {
+ this.eventBus.dispatch("scalechanging", {
+ source: this,
+ scale: newScale,
+ presetValue: newValue
+ });
+ }
+ return;
+ }
+ this.viewer.style.setProperty("--scale-factor", newScale * PixelsPerInch.PDF_TO_CSS_UNITS);
+ const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000;
+ this.refresh(true, {
+ scale: newScale,
+ drawingDelay: postponeDrawing ? drawingDelay : -1
+ });
+ if (postponeDrawing) {
+ this.#scaleTimeoutId = setTimeout(() => {
+ this.#scaleTimeoutId = null;
+ this.refresh();
+ }, drawingDelay);
+ }
+ const previousScale = this._currentScale;
+ this._currentScale = newScale;
+ if (!noScroll) {
+ let page = this._currentPageNumber,
+ dest;
+ if (this._location && !(this.isInPresentationMode || this.isChangingPresentationMode)) {
+ page = this._location.pageNumber;
+ dest = [null, {
+ name: "XYZ"
+ }, this._location.left, this._location.top, null];
+ }
+ this.scrollPageIntoView({
+ pageNumber: page,
+ destArray: dest,
+ allowNegativeOffset: true
+ });
+ if (Array.isArray(origin)) {
+ const scaleDiff = newScale / previousScale - 1;
+ const [top, left] = this.containerTopLeft;
+ this.container.scrollLeft += (origin[0] - left) * scaleDiff;
+ this.container.scrollTop += (origin[1] - top) * scaleDiff;
+ }
+ }
+ this.eventBus.dispatch("scalechanging", {
+ source: this,
+ scale: newScale,
+ presetValue: preset ? newValue : undefined
+ });
+ if (this.defaultRenderingQueue) {
+ this.update();
+ }
+ }
+ get #pageWidthScaleFactor() {
+ if (this._spreadMode !== SpreadMode.NONE && this._scrollMode !== ScrollMode.HORIZONTAL) {
+ return 2;
+ }
+ return 1;
+ }
+ #setScale(value, options) {
+ let scale = parseFloat(value);
+ if (scale > 0) {
+ options.preset = false;
+ this.#setScaleUpdatePages(scale, value, options);
+ } else {
+ const currentPage = this._pages[this._currentPageNumber - 1];
+ if (!currentPage) {
+ return;
+ }
+ let hPadding = SCROLLBAR_PADDING,
+ vPadding = VERTICAL_PADDING;
+ if (this.isInPresentationMode) {
+ hPadding = vPadding = 4;
+ if (this._spreadMode !== SpreadMode.NONE) {
+ hPadding *= 2;
+ }
+ } else if (this.removePageBorders) {
+ hPadding = vPadding = 0;
+ } else if (this._scrollMode === ScrollMode.HORIZONTAL) {
+ [hPadding, vPadding] = [vPadding, hPadding];
+ }
+ const pageWidthScale = (this.container.clientWidth - hPadding) / currentPage.width * currentPage.scale / this.#pageWidthScaleFactor;
+ const pageHeightScale = (this.container.clientHeight - vPadding) / currentPage.height * currentPage.scale;
+ switch (value) {
+ case "page-actual":
+ scale = 1;
+ break;
+ case "page-width":
+ scale = pageWidthScale;
+ break;
+ case "page-height":
+ scale = pageHeightScale;
+ break;
+ case "page-fit":
+ scale = Math.min(pageWidthScale, pageHeightScale);
+ break;
+ case "auto":
+ const horizontalScale = isPortraitOrientation(currentPage) ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
+ scale = Math.min(MAX_AUTO_SCALE, horizontalScale);
+ break;
+ default:
+ console.error(`#setScale: "${value}" is an unknown zoom value.`);
+ return;
+ }
+ options.preset = true;
+ this.#setScaleUpdatePages(scale, value, options);
+ }
+ }
+ #resetCurrentPageView() {
+ const pageView = this._pages[this._currentPageNumber - 1];
+ if (this.isInPresentationMode) {
+ this.#setScale(this._currentScaleValue, {
+ noScroll: true
+ });
+ }
+ this.#scrollIntoView(pageView);
+ }
+ pageLabelToPageNumber(label) {
+ if (!this._pageLabels) {
+ return null;
+ }
+ const i = this._pageLabels.indexOf(label);
+ if (i < 0) {
+ return null;
+ }
+ return i + 1;
+ }
+ scrollPageIntoView({
+ pageNumber,
+ destArray = null,
+ allowNegativeOffset = false,
+ ignoreDestinationZoom = false
+ }) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ const pageView = Number.isInteger(pageNumber) && this._pages[pageNumber - 1];
+ if (!pageView) {
+ console.error(`scrollPageIntoView: "${pageNumber}" is not a valid pageNumber parameter.`);
+ return;
+ }
+ if (this.isInPresentationMode || !destArray) {
+ this._setCurrentPageNumber(pageNumber, true);
+ return;
+ }
+ let x = 0,
+ y = 0;
+ let width = 0,
+ height = 0,
+ widthScale,
+ heightScale;
+ const changeOrientation = pageView.rotation % 180 !== 0;
+ const pageWidth = (changeOrientation ? pageView.height : pageView.width) / pageView.scale / PixelsPerInch.PDF_TO_CSS_UNITS;
+ const pageHeight = (changeOrientation ? pageView.width : pageView.height) / pageView.scale / PixelsPerInch.PDF_TO_CSS_UNITS;
+ let scale = 0;
+ switch (destArray[1].name) {
+ case "XYZ":
+ x = destArray[2];
+ y = destArray[3];
+ scale = destArray[4];
+ x = x !== null ? x : 0;
+ y = y !== null ? y : pageHeight;
+ break;
+ case "Fit":
+ case "FitB":
+ scale = "page-fit";
+ break;
+ case "FitH":
+ case "FitBH":
+ y = destArray[2];
+ scale = "page-width";
+ if (y === null && this._location) {
+ x = this._location.left;
+ y = this._location.top;
+ } else if (typeof y !== "number" || y < 0) {
+ y = pageHeight;
+ }
+ break;
+ case "FitV":
+ case "FitBV":
+ x = destArray[2];
+ width = pageWidth;
+ height = pageHeight;
+ scale = "page-height";
+ break;
+ case "FitR":
+ x = destArray[2];
+ y = destArray[3];
+ width = destArray[4] - x;
+ height = destArray[5] - y;
+ let hPadding = SCROLLBAR_PADDING,
+ vPadding = VERTICAL_PADDING;
+ if (this.removePageBorders) {
+ hPadding = vPadding = 0;
+ }
+ widthScale = (this.container.clientWidth - hPadding) / width / PixelsPerInch.PDF_TO_CSS_UNITS;
+ heightScale = (this.container.clientHeight - vPadding) / height / PixelsPerInch.PDF_TO_CSS_UNITS;
+ scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
+ break;
+ default:
+ console.error(`scrollPageIntoView: "${destArray[1].name}" is not a valid destination type.`);
+ return;
+ }
+ if (!ignoreDestinationZoom) {
+ if (scale && scale !== this._currentScale) {
+ this.currentScaleValue = scale;
+ } else if (this._currentScale === UNKNOWN_SCALE) {
+ this.currentScaleValue = DEFAULT_SCALE_VALUE;
+ }
+ }
+ if (scale === "page-fit" && !destArray[4]) {
+ this.#scrollIntoView(pageView);
+ return;
+ }
+ const boundingRect = [pageView.viewport.convertToViewportPoint(x, y), pageView.viewport.convertToViewportPoint(x + width, y + height)];
+ let left = Math.min(boundingRect[0][0], boundingRect[1][0]);
+ let top = Math.min(boundingRect[0][1], boundingRect[1][1]);
+ if (!allowNegativeOffset) {
+ left = Math.max(left, 0);
+ top = Math.max(top, 0);
+ }
+ this.#scrollIntoView(pageView, {
+ left,
+ top
+ });
+ }
+ _updateLocation(firstPage) {
+ const currentScale = this._currentScale;
+ const currentScaleValue = this._currentScaleValue;
+ const normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ? Math.round(currentScale * 10000) / 100 : currentScaleValue;
+ const pageNumber = firstPage.id;
+ const currentPageView = this._pages[pageNumber - 1];
+ const container = this.container;
+ const topLeft = currentPageView.getPagePoint(container.scrollLeft - firstPage.x, container.scrollTop - firstPage.y);
+ const intLeft = Math.round(topLeft[0]);
+ const intTop = Math.round(topLeft[1]);
+ let pdfOpenParams = `#page=${pageNumber}`;
+ if (!this.isInPresentationMode) {
+ pdfOpenParams += `&zoom=${normalizedScaleValue},${intLeft},${intTop}`;
+ }
+ this._location = {
+ pageNumber,
+ scale: normalizedScaleValue,
+ top: intTop,
+ left: intLeft,
+ rotation: this._pagesRotation,
+ pdfOpenParams
+ };
+ }
+ update() {
+ const visible = this._getVisiblePages();
+ const visiblePages = visible.views,
+ numVisiblePages = visiblePages.length;
+ if (numVisiblePages === 0) {
+ return;
+ }
+ const newCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1);
+ this.#buffer.resize(newCacheSize, visible.ids);
+ this.renderingQueue.renderHighestPriority(visible);
+ const isSimpleLayout = this._spreadMode === SpreadMode.NONE && (this._scrollMode === ScrollMode.PAGE || this._scrollMode === ScrollMode.VERTICAL);
+ const currentId = this._currentPageNumber;
+ let stillFullyVisible = false;
+ for (const page of visiblePages) {
+ if (page.percent < 100) {
+ break;
+ }
+ if (page.id === currentId && isSimpleLayout) {
+ stillFullyVisible = true;
+ break;
+ }
+ }
+ this._setCurrentPageNumber(stillFullyVisible ? currentId : visiblePages[0].id);
+ this._updateLocation(visible.first);
+ this.eventBus.dispatch("updateviewarea", {
+ source: this,
+ location: this._location
+ });
+ }
+ containsElement(element) {
+ return this.container.contains(element);
+ }
+ focus() {
+ this.container.focus();
+ }
+ get _isContainerRtl() {
+ return getComputedStyle(this.container).direction === "rtl";
+ }
+ get isInPresentationMode() {
+ return this.presentationModeState === PresentationModeState.FULLSCREEN;
+ }
+ get isChangingPresentationMode() {
+ return this.presentationModeState === PresentationModeState.CHANGING;
+ }
+ get isHorizontalScrollbarEnabled() {
+ return this.isInPresentationMode ? false : this.container.scrollWidth > this.container.clientWidth;
+ }
+ get isVerticalScrollbarEnabled() {
+ return this.isInPresentationMode ? false : this.container.scrollHeight > this.container.clientHeight;
+ }
+ _getVisiblePages() {
+ const views = this._scrollMode === ScrollMode.PAGE ? this.#scrollModePageState.pages : this._pages,
+ horizontal = this._scrollMode === ScrollMode.HORIZONTAL,
+ rtl = horizontal && this._isContainerRtl;
+ return getVisibleElements({
+ scrollEl: this.container,
+ views,
+ sortByVisibility: true,
+ horizontal,
+ rtl
+ });
+ }
+ cleanup() {
+ for (const pageView of this._pages) {
+ if (pageView.renderingState !== RenderingStates.FINISHED) {
+ pageView.reset();
+ }
+ }
+ }
+ _cancelRendering() {
+ for (const pageView of this._pages) {
+ pageView.cancelRendering();
+ }
+ }
+ async #ensurePdfPageLoaded(pageView) {
+ if (pageView.pdfPage) {
+ return pageView.pdfPage;
+ }
+ try {
+ const pdfPage = await this.pdfDocument.getPage(pageView.id);
+ if (!pageView.pdfPage) {
+ pageView.setPdfPage(pdfPage);
+ }
+ return pdfPage;
+ } catch (reason) {
+ console.error("Unable to get page for page view", reason);
+ return null;
+ }
+ }
+ #getScrollAhead(visible) {
+ if (visible.first?.id === 1) {
+ return true;
+ } else if (visible.last?.id === this.pagesCount) {
+ return false;
+ }
+ switch (this._scrollMode) {
+ case ScrollMode.PAGE:
+ return this.#scrollModePageState.scrollDown;
+ case ScrollMode.HORIZONTAL:
+ return this.scroll.right;
+ }
+ return this.scroll.down;
+ }
+ forceRendering(currentlyVisiblePages) {
+ const visiblePages = currentlyVisiblePages || this._getVisiblePages();
+ const scrollAhead = this.#getScrollAhead(visiblePages);
+ const preRenderExtra = this._spreadMode !== SpreadMode.NONE && this._scrollMode !== ScrollMode.HORIZONTAL;
+ const pageView = this.renderingQueue.getHighestPriority(visiblePages, this._pages, scrollAhead, preRenderExtra);
+ if (pageView) {
+ this.#ensurePdfPageLoaded(pageView).then(() => {
+ this.renderingQueue.renderView(pageView);
+ });
+ return true;
+ }
+ return false;
+ }
+ get hasEqualPageSizes() {
+ const firstPageView = this._pages[0];
+ for (let i = 1, ii = this._pages.length; i < ii; ++i) {
+ const pageView = this._pages[i];
+ if (pageView.width !== firstPageView.width || pageView.height !== firstPageView.height) {
+ return false;
+ }
+ }
+ return true;
+ }
+ getPagesOverview() {
+ let initialOrientation;
+ return this._pages.map(pageView => {
+ const viewport = pageView.pdfPage.getViewport({
+ scale: 1
+ });
+ const orientation = isPortraitOrientation(viewport);
+ if (initialOrientation === undefined) {
+ initialOrientation = orientation;
+ } else if (this.enablePrintAutoRotate && orientation !== initialOrientation) {
+ return {
+ width: viewport.height,
+ height: viewport.width,
+ rotation: (viewport.rotation - 90) % 360
+ };
+ }
+ return {
+ width: viewport.width,
+ height: viewport.height,
+ rotation: viewport.rotation
+ };
+ });
+ }
+ get optionalContentConfigPromise() {
+ if (!this.pdfDocument) {
+ return Promise.resolve(null);
+ }
+ if (!this._optionalContentConfigPromise) {
+ console.error("optionalContentConfigPromise: Not initialized yet.");
+ return this.pdfDocument.getOptionalContentConfig({
+ intent: "display"
+ });
+ }
+ return this._optionalContentConfigPromise;
+ }
+ set optionalContentConfigPromise(promise) {
+ if (!(promise instanceof Promise)) {
+ throw new Error(`Invalid optionalContentConfigPromise: ${promise}`);
+ }
+ if (!this.pdfDocument) {
+ return;
+ }
+ if (!this._optionalContentConfigPromise) {
+ return;
+ }
+ this._optionalContentConfigPromise = promise;
+ this.refresh(false, {
+ optionalContentConfigPromise: promise
+ });
+ this.eventBus.dispatch("optionalcontentconfigchanged", {
+ source: this,
+ promise
+ });
+ }
+ get scrollMode() {
+ return this._scrollMode;
+ }
+ set scrollMode(mode) {
+ if (this._scrollMode === mode) {
+ return;
+ }
+ if (!isValidScrollMode(mode)) {
+ throw new Error(`Invalid scroll mode: ${mode}`);
+ }
+ if (this.pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) {
+ return;
+ }
+ this._previousScrollMode = this._scrollMode;
+ this._scrollMode = mode;
+ this.eventBus.dispatch("scrollmodechanged", {
+ source: this,
+ mode
+ });
+ this._updateScrollMode(this._currentPageNumber);
+ }
+ _updateScrollMode(pageNumber = null) {
+ const scrollMode = this._scrollMode,
+ viewer = this.viewer;
+ viewer.classList.toggle("scrollHorizontal", scrollMode === ScrollMode.HORIZONTAL);
+ viewer.classList.toggle("scrollWrapped", scrollMode === ScrollMode.WRAPPED);
+ if (!this.pdfDocument || !pageNumber) {
+ return;
+ }
+ if (scrollMode === ScrollMode.PAGE) {
+ this.#ensurePageViewVisible();
+ } else if (this._previousScrollMode === ScrollMode.PAGE) {
+ this._updateSpreadMode();
+ }
+ if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
+ this.#setScale(this._currentScaleValue, {
+ noScroll: true
+ });
+ }
+ this._setCurrentPageNumber(pageNumber, true);
+ this.update();
+ }
+ get spreadMode() {
+ return this._spreadMode;
+ }
+ set spreadMode(mode) {
+ if (this._spreadMode === mode) {
+ return;
+ }
+ if (!isValidSpreadMode(mode)) {
+ throw new Error(`Invalid spread mode: ${mode}`);
+ }
+ this._spreadMode = mode;
+ this.eventBus.dispatch("spreadmodechanged", {
+ source: this,
+ mode
+ });
+ this._updateSpreadMode(this._currentPageNumber);
+ }
+ _updateSpreadMode(pageNumber = null) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ const viewer = this.viewer,
+ pages = this._pages;
+ if (this._scrollMode === ScrollMode.PAGE) {
+ this.#ensurePageViewVisible();
+ } else {
+ viewer.textContent = "";
+ if (this._spreadMode === SpreadMode.NONE) {
+ for (const pageView of this._pages) {
+ viewer.append(pageView.div);
+ }
+ } else {
+ const parity = this._spreadMode - 1;
+ let spread = null;
+ for (let i = 0, ii = pages.length; i < ii; ++i) {
+ if (spread === null) {
+ spread = document.createElement("div");
+ spread.className = "spread";
+ viewer.append(spread);
+ } else if (i % 2 === parity) {
+ spread = spread.cloneNode(false);
+ viewer.append(spread);
+ }
+ spread.append(pages[i].div);
+ }
+ }
+ }
+ if (!pageNumber) {
+ return;
+ }
+ if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
+ this.#setScale(this._currentScaleValue, {
+ noScroll: true
+ });
+ }
+ this._setCurrentPageNumber(pageNumber, true);
+ this.update();
+ }
+ _getPageAdvance(currentPageNumber, previous = false) {
+ switch (this._scrollMode) {
+ case ScrollMode.WRAPPED:
+ {
+ const {
+ views
+ } = this._getVisiblePages(),
+ pageLayout = new Map();
+ for (const {
+ id,
+ y,
+ percent,
+ widthPercent
+ } of views) {
+ if (percent === 0 || widthPercent < 100) {
+ continue;
+ }
+ let yArray = pageLayout.get(y);
+ if (!yArray) {
+ pageLayout.set(y, yArray ||= []);
+ }
+ yArray.push(id);
+ }
+ for (const yArray of pageLayout.values()) {
+ const currentIndex = yArray.indexOf(currentPageNumber);
+ if (currentIndex === -1) {
+ continue;
+ }
+ const numPages = yArray.length;
+ if (numPages === 1) {
+ break;
+ }
+ if (previous) {
+ for (let i = currentIndex - 1, ii = 0; i >= ii; i--) {
+ const currentId = yArray[i],
+ expectedId = yArray[i + 1] - 1;
+ if (currentId < expectedId) {
+ return currentPageNumber - expectedId;
+ }
+ }
+ } else {
+ for (let i = currentIndex + 1, ii = numPages; i < ii; i++) {
+ const currentId = yArray[i],
+ expectedId = yArray[i - 1] + 1;
+ if (currentId > expectedId) {
+ return expectedId - currentPageNumber;
+ }
+ }
+ }
+ if (previous) {
+ const firstId = yArray[0];
+ if (firstId < currentPageNumber) {
+ return currentPageNumber - firstId + 1;
+ }
+ } else {
+ const lastId = yArray[numPages - 1];
+ if (lastId > currentPageNumber) {
+ return lastId - currentPageNumber + 1;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ case ScrollMode.HORIZONTAL:
+ {
+ break;
+ }
+ case ScrollMode.PAGE:
+ case ScrollMode.VERTICAL:
+ {
+ if (this._spreadMode === SpreadMode.NONE) {
+ break;
+ }
+ const parity = this._spreadMode - 1;
+ if (previous && currentPageNumber % 2 !== parity) {
+ break;
+ } else if (!previous && currentPageNumber % 2 === parity) {
+ break;
+ }
+ const {
+ views
+ } = this._getVisiblePages(),
+ expectedId = previous ? currentPageNumber - 1 : currentPageNumber + 1;
+ for (const {
+ id,
+ percent,
+ widthPercent
+ } of views) {
+ if (id !== expectedId) {
+ continue;
+ }
+ if (percent > 0 && widthPercent === 100) {
+ return 2;
+ }
+ break;
+ }
+ break;
+ }
+ }
+ return 1;
+ }
+ nextPage() {
+ const currentPageNumber = this._currentPageNumber,
+ pagesCount = this.pagesCount;
+ if (currentPageNumber >= pagesCount) {
+ return false;
+ }
+ const advance = this._getPageAdvance(currentPageNumber, false) || 1;
+ this.currentPageNumber = Math.min(currentPageNumber + advance, pagesCount);
+ return true;
+ }
+ previousPage() {
+ const currentPageNumber = this._currentPageNumber;
+ if (currentPageNumber <= 1) {
+ return false;
+ }
+ const advance = this._getPageAdvance(currentPageNumber, true) || 1;
+ this.currentPageNumber = Math.max(currentPageNumber - advance, 1);
+ return true;
+ }
+ updateScale({
+ drawingDelay,
+ scaleFactor = null,
+ steps = null,
+ origin
+ }) {
+ if (steps === null && scaleFactor === null) {
+ throw new Error("Invalid updateScale options: either `steps` or `scaleFactor` must be provided.");
+ }
+ if (!this.pdfDocument) {
+ return;
+ }
+ let newScale = this._currentScale;
+ if (scaleFactor > 0 && scaleFactor !== 1) {
+ newScale = Math.round(newScale * scaleFactor * 100) / 100;
+ } else if (steps) {
+ const delta = steps > 0 ? DEFAULT_SCALE_DELTA : 1 / DEFAULT_SCALE_DELTA;
+ const round = steps > 0 ? Math.ceil : Math.floor;
+ steps = Math.abs(steps);
+ do {
+ newScale = round((newScale * delta).toFixed(2) * 10) / 10;
+ } while (--steps > 0);
+ }
+ newScale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, newScale));
+ this.#setScale(newScale, {
+ noScroll: false,
+ drawingDelay,
+ origin
+ });
+ }
+ increaseScale(options = {}) {
+ this.updateScale({
+ ...options,
+ steps: options.steps ?? 1
+ });
+ }
+ decreaseScale(options = {}) {
+ this.updateScale({
+ ...options,
+ steps: -(options.steps ?? 1)
+ });
+ }
+ #updateContainerHeightCss(height = this.container.clientHeight) {
+ if (height !== this.#previousContainerHeight) {
+ this.#previousContainerHeight = height;
+ docStyle.setProperty("--viewer-container-height", `${height}px`);
+ }
+ }
+ #resizeObserverCallback(entries) {
+ for (const entry of entries) {
+ if (entry.target === this.container) {
+ this.#updateContainerHeightCss(Math.floor(entry.borderBoxSize[0].blockSize));
+ this.#containerTopLeft = null;
+ break;
+ }
+ }
+ }
+ get containerTopLeft() {
+ return this.#containerTopLeft ||= [this.container.offsetTop, this.container.offsetLeft];
+ }
+ get annotationEditorMode() {
+ return this.#annotationEditorUIManager ? this.#annotationEditorMode : AnnotationEditorType.DISABLE;
+ }
+ set annotationEditorMode({
+ mode,
+ editId = null,
+ isFromKeyboard = false
+ }) {
+ if (!this.#annotationEditorUIManager) {
+ throw new Error(`The AnnotationEditor is not enabled.`);
+ }
+ if (this.#annotationEditorMode === mode) {
+ return;
+ }
+ if (!isValidAnnotationEditorMode(mode)) {
+ throw new Error(`Invalid AnnotationEditor mode: ${mode}`);
+ }
+ if (!this.pdfDocument) {
+ return;
+ }
+ this.#annotationEditorMode = mode;
+ this.eventBus.dispatch("annotationeditormodechanged", {
+ source: this,
+ mode
+ });
+ this.#annotationEditorUIManager.updateMode(mode, editId, isFromKeyboard);
+ }
+ set annotationEditorParams({
+ type,
+ value
+ }) {
+ if (!this.#annotationEditorUIManager) {
+ throw new Error(`The AnnotationEditor is not enabled.`);
+ }
+ this.#annotationEditorUIManager.updateParams(type, value);
+ }
+ refresh(noUpdate = false, updateArgs = Object.create(null)) {
+ if (!this.pdfDocument) {
+ return;
+ }
+ for (const pageView of this._pages) {
+ pageView.update(updateArgs);
+ }
+ if (this.#scaleTimeoutId !== null) {
+ clearTimeout(this.#scaleTimeoutId);
+ this.#scaleTimeoutId = null;
+ }
+ if (!noUpdate) {
+ this.update();
+ }
+ }
+}
+
+;// CONCATENATED MODULE: ./web/secondary_toolbar.js
+
+
+class SecondaryToolbar {
+ #opts;
+ constructor(options, eventBus) {
+ this.#opts = options;
+ const buttons = [{
+ element: options.presentationModeButton,
+ eventName: "presentationmode",
+ close: true
+ }, {
+ element: options.printButton,
+ eventName: "print",
+ close: true
+ }, {
+ element: options.downloadButton,
+ eventName: "download",
+ close: true
+ }, {
+ element: options.viewBookmarkButton,
+ eventName: null,
+ close: true
+ }, {
+ element: options.firstPageButton,
+ eventName: "firstpage",
+ close: true
+ }, {
+ element: options.lastPageButton,
+ eventName: "lastpage",
+ close: true
+ }, {
+ element: options.pageRotateCwButton,
+ eventName: "rotatecw",
+ close: false
+ }, {
+ element: options.pageRotateCcwButton,
+ eventName: "rotateccw",
+ close: false
+ }, {
+ element: options.cursorSelectToolButton,
+ eventName: "switchcursortool",
+ eventDetails: {
+ tool: CursorTool.SELECT
+ },
+ close: true
+ }, {
+ element: options.cursorHandToolButton,
+ eventName: "switchcursortool",
+ eventDetails: {
+ tool: CursorTool.HAND
+ },
+ close: true
+ }, {
+ element: options.scrollPageButton,
+ eventName: "switchscrollmode",
+ eventDetails: {
+ mode: ScrollMode.PAGE
+ },
+ close: true
+ }, {
+ element: options.scrollVerticalButton,
+ eventName: "switchscrollmode",
+ eventDetails: {
+ mode: ScrollMode.VERTICAL
+ },
+ close: true
+ }, {
+ element: options.scrollHorizontalButton,
+ eventName: "switchscrollmode",
+ eventDetails: {
+ mode: ScrollMode.HORIZONTAL
+ },
+ close: true
+ }, {
+ element: options.scrollWrappedButton,
+ eventName: "switchscrollmode",
+ eventDetails: {
+ mode: ScrollMode.WRAPPED
+ },
+ close: true
+ }, {
+ element: options.spreadNoneButton,
+ eventName: "switchspreadmode",
+ eventDetails: {
+ mode: SpreadMode.NONE
+ },
+ close: true
+ }, {
+ element: options.spreadOddButton,
+ eventName: "switchspreadmode",
+ eventDetails: {
+ mode: SpreadMode.ODD
+ },
+ close: true
+ }, {
+ element: options.spreadEvenButton,
+ eventName: "switchspreadmode",
+ eventDetails: {
+ mode: SpreadMode.EVEN
+ },
+ close: true
+ }, {
+ element: options.documentPropertiesButton,
+ eventName: "documentproperties",
+ close: true
+ }];
+ buttons.push({
+ element: options.openFileButton,
+ eventName: "openfile",
+ close: true
+ });
+ this.eventBus = eventBus;
+ this.opened = false;
+ this.#bindListeners(buttons);
+ this.reset();
+ }
+ get isOpen() {
+ return this.opened;
+ }
+ setPageNumber(pageNumber) {
+ this.pageNumber = pageNumber;
+ this.#updateUIState();
+ }
+ setPagesCount(pagesCount) {
+ this.pagesCount = pagesCount;
+ this.#updateUIState();
+ }
+ reset() {
+ this.pageNumber = 0;
+ this.pagesCount = 0;
+ this.#updateUIState();
+ this.eventBus.dispatch("switchcursortool", {
+ source: this,
+ reset: true
+ });
+ this.#scrollModeChanged({
+ mode: ScrollMode.VERTICAL
+ });
+ this.#spreadModeChanged({
+ mode: SpreadMode.NONE
+ });
+ }
+ #updateUIState() {
+ const {
+ firstPageButton,
+ lastPageButton,
+ pageRotateCwButton,
+ pageRotateCcwButton
+ } = this.#opts;
+ firstPageButton.disabled = this.pageNumber <= 1;
+ lastPageButton.disabled = this.pageNumber >= this.pagesCount;
+ pageRotateCwButton.disabled = this.pagesCount === 0;
+ pageRotateCcwButton.disabled = this.pagesCount === 0;
+ }
+ #bindListeners(buttons) {
+ const {
+ eventBus
+ } = this;
+ const {
+ toggleButton
+ } = this.#opts;
+ toggleButton.addEventListener("click", this.toggle.bind(this));
+ for (const {
+ element,
+ eventName,
+ close,
+ eventDetails
+ } of buttons) {
+ element.addEventListener("click", evt => {
+ if (eventName !== null) {
+ eventBus.dispatch(eventName, {
+ source: this,
+ ...eventDetails
+ });
+ }
+ if (close) {
+ this.close();
+ }
+ eventBus.dispatch("reporttelemetry", {
+ source: this,
+ details: {
+ type: "buttons",
+ data: {
+ id: element.id
+ }
+ }
+ });
+ });
+ }
+ eventBus._on("cursortoolchanged", this.#cursorToolChanged.bind(this));
+ eventBus._on("scrollmodechanged", this.#scrollModeChanged.bind(this));
+ eventBus._on("spreadmodechanged", this.#spreadModeChanged.bind(this));
+ }
+ #cursorToolChanged({
+ tool
+ }) {
+ const {
+ cursorSelectToolButton,
+ cursorHandToolButton
+ } = this.#opts;
+ toggleCheckedBtn(cursorSelectToolButton, tool === CursorTool.SELECT);
+ toggleCheckedBtn(cursorHandToolButton, tool === CursorTool.HAND);
+ }
+ #scrollModeChanged({
+ mode
+ }) {
+ const {
+ scrollPageButton,
+ scrollVerticalButton,
+ scrollHorizontalButton,
+ scrollWrappedButton,
+ spreadNoneButton,
+ spreadOddButton,
+ spreadEvenButton
+ } = this.#opts;
+ toggleCheckedBtn(scrollPageButton, mode === ScrollMode.PAGE);
+ toggleCheckedBtn(scrollVerticalButton, mode === ScrollMode.VERTICAL);
+ toggleCheckedBtn(scrollHorizontalButton, mode === ScrollMode.HORIZONTAL);
+ toggleCheckedBtn(scrollWrappedButton, mode === ScrollMode.WRAPPED);
+ const forceScrollModePage = this.pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE;
+ scrollPageButton.disabled = forceScrollModePage;
+ scrollVerticalButton.disabled = forceScrollModePage;
+ scrollHorizontalButton.disabled = forceScrollModePage;
+ scrollWrappedButton.disabled = forceScrollModePage;
+ const isHorizontal = mode === ScrollMode.HORIZONTAL;
+ spreadNoneButton.disabled = isHorizontal;
+ spreadOddButton.disabled = isHorizontal;
+ spreadEvenButton.disabled = isHorizontal;
+ }
+ #spreadModeChanged({
+ mode
+ }) {
+ const {
+ spreadNoneButton,
+ spreadOddButton,
+ spreadEvenButton
+ } = this.#opts;
+ toggleCheckedBtn(spreadNoneButton, mode === SpreadMode.NONE);
+ toggleCheckedBtn(spreadOddButton, mode === SpreadMode.ODD);
+ toggleCheckedBtn(spreadEvenButton, mode === SpreadMode.EVEN);
+ }
+ open() {
+ if (this.opened) {
+ return;
+ }
+ this.opened = true;
+ const {
+ toggleButton,
+ toolbar
+ } = this.#opts;
+ toggleExpandedBtn(toggleButton, true, toolbar);
+ }
+ close() {
+ if (!this.opened) {
+ return;
+ }
+ this.opened = false;
+ const {
+ toggleButton,
+ toolbar
+ } = this.#opts;
+ toggleExpandedBtn(toggleButton, false, toolbar);
+ }
+ toggle() {
+ if (this.opened) {
+ this.close();
+ } else {
+ this.open();
+ }
+ }
+}
+
+;// CONCATENATED MODULE: ./web/toolbar.js
+
+
+class Toolbar {
+ #opts;
+ constructor(options, eventBus) {
+ this.#opts = options;
+ this.eventBus = eventBus;
+ const buttons = [{
+ element: options.previous,
+ eventName: "previouspage"
+ }, {
+ element: options.next,
+ eventName: "nextpage"
+ }, {
+ element: options.zoomIn,
+ eventName: "zoomin"
+ }, {
+ element: options.zoomOut,
+ eventName: "zoomout"
+ }, {
+ element: options.print,
+ eventName: "print"
+ }, {
+ element: options.download,
+ eventName: "download"
+ }, {
+ element: options.editorFreeTextButton,
+ eventName: "switchannotationeditormode",
+ eventDetails: {
+ get mode() {
+ const {
+ classList
+ } = options.editorFreeTextButton;
+ return classList.contains("toggled") ? AnnotationEditorType.NONE : AnnotationEditorType.FREETEXT;
+ }
+ }
+ }, {
+ element: options.editorHighlightButton,
+ eventName: "switchannotationeditormode",
+ eventDetails: {
+ get mode() {
+ const {
+ classList
+ } = options.editorHighlightButton;
+ return classList.contains("toggled") ? AnnotationEditorType.NONE : AnnotationEditorType.HIGHLIGHT;
+ }
+ }
+ }, {
+ element: options.editorInkButton,
+ eventName: "switchannotationeditormode",
+ eventDetails: {
+ get mode() {
+ const {
+ classList
+ } = options.editorInkButton;
+ return classList.contains("toggled") ? AnnotationEditorType.NONE : AnnotationEditorType.INK;
+ }
+ }
+ }, {
+ element: options.editorStampButton,
+ eventName: "switchannotationeditormode",
+ eventDetails: {
+ get mode() {
+ const {
+ classList
+ } = options.editorStampButton;
+ return classList.contains("toggled") ? AnnotationEditorType.NONE : AnnotationEditorType.STAMP;
+ }
+ }
+ }];
+ this.#bindListeners(buttons);
+ if (options.editorHighlightColorPicker) {
+ eventBus._on("annotationeditoruimanager", ({
+ uiManager
+ }) => {
+ this.#setAnnotationEditorUIManager(uiManager, options.editorHighlightColorPicker);
+ }, {
+ once: true
+ });
+ }
+ eventBus._on("showannotationeditorui", ({
+ mode
+ }) => {
+ switch (mode) {
+ case AnnotationEditorType.HIGHLIGHT:
+ options.editorHighlightButton.click();
+ break;
+ }
+ });
+ this.reset();
+ }
+ #setAnnotationEditorUIManager(uiManager, parentContainer) {
+ const colorPicker = new ColorPicker({
+ uiManager
+ });
+ uiManager.setMainHighlightColorPicker(colorPicker);
+ parentContainer.append(colorPicker.renderMainDropdown());
+ }
+ setPageNumber(pageNumber, pageLabel) {
+ this.pageNumber = pageNumber;
+ this.pageLabel = pageLabel;
+ this.#updateUIState(false);
+ }
+ setPagesCount(pagesCount, hasPageLabels) {
+ this.pagesCount = pagesCount;
+ this.hasPageLabels = hasPageLabels;
+ this.#updateUIState(true);
+ }
+ setPageScale(pageScaleValue, pageScale) {
+ this.pageScaleValue = (pageScaleValue || pageScale).toString();
+ this.pageScale = pageScale;
+ this.#updateUIState(false);
+ }
+ reset() {
+ this.pageNumber = 0;
+ this.pageLabel = null;
+ this.hasPageLabels = false;
+ this.pagesCount = 0;
+ this.pageScaleValue = DEFAULT_SCALE_VALUE;
+ this.pageScale = DEFAULT_SCALE;
+ this.#updateUIState(true);
+ this.updateLoadingIndicatorState();
+ this.#editorModeChanged({
+ mode: AnnotationEditorType.DISABLE
+ });
+ }
+ #bindListeners(buttons) {
+ const {
+ eventBus
+ } = this;
+ const {
+ pageNumber,
+ scaleSelect
+ } = this.#opts;
+ const self = this;
+ for (const {
+ element,
+ eventName,
+ eventDetails
+ } of buttons) {
+ element.addEventListener("click", evt => {
+ if (eventName !== null) {
+ eventBus.dispatch(eventName, {
+ source: this,
+ ...eventDetails,
+ isFromKeyboard: evt.detail === 0
+ });
+ }
+ });
+ }
+ pageNumber.addEventListener("click", function () {
+ this.select();
+ });
+ pageNumber.addEventListener("change", function () {
+ eventBus.dispatch("pagenumberchanged", {
+ source: self,
+ value: this.value
+ });
+ });
+ scaleSelect.addEventListener("change", function () {
+ if (this.value === "custom") {
+ return;
+ }
+ eventBus.dispatch("scalechanged", {
+ source: self,
+ value: this.value
+ });
+ });
+ scaleSelect.addEventListener("click", function ({
+ target
+ }) {
+ if (this.value === self.pageScaleValue && target.tagName.toUpperCase() === "OPTION") {
+ this.blur();
+ }
+ });
+ scaleSelect.oncontextmenu = noContextMenu;
+ eventBus._on("annotationeditormodechanged", this.#editorModeChanged.bind(this));
+ }
+ #editorModeChanged({
+ mode
+ }) {
+ const {
+ editorFreeTextButton,
+ editorFreeTextParamsToolbar,
+ editorHighlightButton,
+ editorHighlightParamsToolbar,
+ editorInkButton,
+ editorInkParamsToolbar,
+ editorStampButton,
+ editorStampParamsToolbar
+ } = this.#opts;
+ toggleCheckedBtn(editorFreeTextButton, mode === AnnotationEditorType.FREETEXT, editorFreeTextParamsToolbar);
+ toggleCheckedBtn(editorHighlightButton, mode === AnnotationEditorType.HIGHLIGHT, editorHighlightParamsToolbar);
+ toggleCheckedBtn(editorInkButton, mode === AnnotationEditorType.INK, editorInkParamsToolbar);
+ toggleCheckedBtn(editorStampButton, mode === AnnotationEditorType.STAMP, editorStampParamsToolbar);
+ const isDisable = mode === AnnotationEditorType.DISABLE;
+ editorFreeTextButton.disabled = isDisable;
+ editorHighlightButton.disabled = isDisable;
+ editorInkButton.disabled = isDisable;
+ editorStampButton.disabled = isDisable;
+ }
+ #updateUIState(resetNumPages = false) {
+ const {
+ pageNumber,
+ pagesCount,
+ pageScaleValue,
+ pageScale
+ } = this;
+ const opts = this.#opts;
+ if (resetNumPages) {
+ if (this.hasPageLabels) {
+ opts.pageNumber.type = "text";
+ opts.numPages.setAttribute("data-l10n-id", "pdfjs-page-of-pages");
+ } else {
+ opts.pageNumber.type = "number";
+ opts.numPages.setAttribute("data-l10n-id", "pdfjs-of-pages");
+ opts.numPages.setAttribute("data-l10n-args", JSON.stringify({
+ pagesCount
+ }));
+ }
+ opts.pageNumber.max = pagesCount;
+ }
+ if (this.hasPageLabels) {
+ opts.pageNumber.value = this.pageLabel;
+ opts.numPages.setAttribute("data-l10n-args", JSON.stringify({
+ pageNumber,
+ pagesCount
+ }));
+ } else {
+ opts.pageNumber.value = pageNumber;
+ }
+ opts.previous.disabled = pageNumber <= 1;
+ opts.next.disabled = pageNumber >= pagesCount;
+ opts.zoomOut.disabled = pageScale <= MIN_SCALE;
+ opts.zoomIn.disabled = pageScale >= MAX_SCALE;
+ let predefinedValueFound = false;
+ for (const option of opts.scaleSelect.options) {
+ if (option.value !== pageScaleValue) {
+ option.selected = false;
+ continue;
+ }
+ option.selected = true;
+ predefinedValueFound = true;
+ }
+ if (!predefinedValueFound) {
+ opts.customScaleOption.selected = true;
+ opts.customScaleOption.setAttribute("data-l10n-args", JSON.stringify({
+ scale: Math.round(pageScale * 10000) / 100
+ }));
+ }
+ }
+ updateLoadingIndicatorState(loading = false) {
+ const {
+ pageNumber
+ } = this.#opts;
+ pageNumber.classList.toggle("loading", loading);
+ }
+}
+
+;// CONCATENATED MODULE: ./web/view_history.js
+const DEFAULT_VIEW_HISTORY_CACHE_SIZE = 20;
+class ViewHistory {
+ constructor(fingerprint, cacheSize = DEFAULT_VIEW_HISTORY_CACHE_SIZE) {
+ this.fingerprint = fingerprint;
+ this.cacheSize = cacheSize;
+ this._initializedPromise = this._readFromStorage().then(databaseStr => {
+ const database = JSON.parse(databaseStr || "{}");
+ let index = -1;
+ if (!Array.isArray(database.files)) {
+ database.files = [];
+ } else {
+ while (database.files.length >= this.cacheSize) {
+ database.files.shift();
+ }
+ for (let i = 0, ii = database.files.length; i < ii; i++) {
+ const branch = database.files[i];
+ if (branch.fingerprint === this.fingerprint) {
+ index = i;
+ break;
+ }
+ }
+ }
+ if (index === -1) {
+ index = database.files.push({
+ fingerprint: this.fingerprint
+ }) - 1;
+ }
+ this.file = database.files[index];
+ this.database = database;
+ });
+ }
+ async _writeToStorage() {
+ const databaseStr = JSON.stringify(this.database);
+ localStorage.setItem("pdfjs.history", databaseStr);
+ }
+ async _readFromStorage() {
+ return localStorage.getItem("pdfjs.history");
+ }
+ async set(name, val) {
+ await this._initializedPromise;
+ this.file[name] = val;
+ return this._writeToStorage();
+ }
+ async setMultiple(properties) {
+ await this._initializedPromise;
+ for (const name in properties) {
+ this.file[name] = properties[name];
+ }
+ return this._writeToStorage();
+ }
+ async get(name, defaultValue) {
+ await this._initializedPromise;
+ const val = this.file[name];
+ return val !== undefined ? val : defaultValue;
+ }
+ async getMultiple(properties) {
+ await this._initializedPromise;
+ const values = Object.create(null);
+ for (const name in properties) {
+ const val = this.file[name];
+ values[name] = val !== undefined ? val : properties[name];
+ }
+ return values;
+ }
+}
+
+;// CONCATENATED MODULE: ./web/app.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+const FORCE_PAGES_LOADED_TIMEOUT = 10000;
+const WHEEL_ZOOM_DISABLED_TIMEOUT = 1000;
+const ViewOnLoad = {
+ UNKNOWN: -1,
+ PREVIOUS: 0,
+ INITIAL: 1
+};
+const PDFViewerApplication = {
+ initialBookmark: document.location.hash.substring(1),
+ _initializedCapability: {
+ ...Promise.withResolvers(),
+ settled: false
+ },
+ appConfig: null,
+ pdfDocument: null,
+ pdfLoadingTask: null,
+ printService: null,
+ pdfViewer: null,
+ pdfThumbnailViewer: null,
+ pdfRenderingQueue: null,
+ pdfPresentationMode: null,
+ pdfDocumentProperties: null,
+ pdfLinkService: null,
+ pdfHistory: null,
+ pdfSidebar: null,
+ pdfOutlineViewer: null,
+ pdfAttachmentViewer: null,
+ pdfLayerViewer: null,
+ pdfCursorTools: null,
+ pdfScriptingManager: null,
+ store: null,
+ downloadManager: null,
+ overlayManager: null,
+ preferences: null,
+ toolbar: null,
+ secondaryToolbar: null,
+ eventBus: null,
+ l10n: null,
+ annotationEditorParams: null,
+ isInitialViewSet: false,
+ downloadComplete: false,
+ isViewerEmbedded: window.parent !== window,
+ url: "",
+ baseUrl: "",
+ _downloadUrl: "",
+ _eventBusAbortController: null,
+ _windowAbortController: null,
+ documentInfo: null,
+ metadata: null,
+ _contentDispositionFilename: null,
+ _contentLength: null,
+ _saveInProgress: false,
+ _wheelUnusedTicks: 0,
+ _wheelUnusedFactor: 1,
+ _touchUnusedTicks: 0,
+ _touchUnusedFactor: 1,
+ _PDFBug: null,
+ _hasAnnotationEditors: false,
+ _title: document.title,
+ _printAnnotationStoragePromise: null,
+ _touchInfo: null,
+ _isCtrlKeyDown: false,
+ _nimbusDataPromise: null,
+ _caretBrowsing: null,
+ _isScrolling: false,
+ async initialize(appConfig) {
+ let l10nPromise;
+ this.appConfig = appConfig;
+ try {
+ await this.preferences.initializedPromise;
+ } catch (ex) {
+ console.error(`initialize: "${ex.message}".`);
+ }
+ if (AppOptions.get("pdfBugEnabled")) {
+ await this._parseHashParams();
+ }
+ let mode;
+ switch (AppOptions.get("viewerCssTheme")) {
+ case 1:
+ mode = "is-light";
+ break;
+ case 2:
+ mode = "is-dark";
+ break;
+ }
+ if (mode) {
+ document.documentElement.classList.add(mode);
+ }
+ l10nPromise = this.externalServices.createL10n();
+ this.l10n = await l10nPromise;
+ document.getElementsByTagName("html")[0].dir = this.l10n.getDirection();
+ this.l10n.translate(appConfig.appContainer || document.documentElement);
+ if (this.isViewerEmbedded && AppOptions.get("externalLinkTarget") === LinkTarget.NONE) {
+ AppOptions.set("externalLinkTarget", LinkTarget.TOP);
+ }
+ await this._initializeViewerComponents();
+ this.bindEvents();
+ this.bindWindowEvents();
+ this._initializedCapability.settled = true;
+ this._initializedCapability.resolve();
+ },
+ async _parseHashParams() {
+ const hash = document.location.hash.substring(1);
+ if (!hash) {
+ return;
+ }
+ const {
+ mainContainer,
+ viewerContainer
+ } = this.appConfig,
+ params = parseQueryString(hash);
+ const loadPDFBug = async () => {
+ if (this._PDFBug) {
+ return;
+ }
+ const {
+ PDFBug
+ } = await import( /*webpackIgnore: true*/AppOptions.get("debuggerSrc"));
+ this._PDFBug = PDFBug;
+ };
+ if (params.get("disableworker") === "true") {
+ try {
+ GlobalWorkerOptions.workerSrc ||= AppOptions.get("workerSrc");
+ await import( /*webpackIgnore: true*/PDFWorker.workerSrc);
+ } catch (ex) {
+ console.error(`_parseHashParams: "${ex.message}".`);
+ }
+ }
+ if (params.has("disablerange")) {
+ AppOptions.set("disableRange", params.get("disablerange") === "true");
+ }
+ if (params.has("disablestream")) {
+ AppOptions.set("disableStream", params.get("disablestream") === "true");
+ }
+ if (params.has("disableautofetch")) {
+ AppOptions.set("disableAutoFetch", params.get("disableautofetch") === "true");
+ }
+ if (params.has("disablefontface")) {
+ AppOptions.set("disableFontFace", params.get("disablefontface") === "true");
+ }
+ if (params.has("disablehistory")) {
+ AppOptions.set("disableHistory", params.get("disablehistory") === "true");
+ }
+ if (params.has("verbosity")) {
+ AppOptions.set("verbosity", params.get("verbosity") | 0);
+ }
+ if (params.has("textlayer")) {
+ switch (params.get("textlayer")) {
+ case "off":
+ AppOptions.set("textLayerMode", TextLayerMode.DISABLE);
+ break;
+ case "visible":
+ case "shadow":
+ case "hover":
+ viewerContainer.classList.add(`textLayer-${params.get("textlayer")}`);
+ try {
+ await loadPDFBug();
+ this._PDFBug.loadCSS();
+ } catch (ex) {
+ console.error(`_parseHashParams: "${ex.message}".`);
+ }
+ break;
+ }
+ }
+ if (params.has("pdfbug")) {
+ AppOptions.setAll({
+ pdfBug: true,
+ fontExtraProperties: true
+ });
+ const enabled = params.get("pdfbug").split(",");
+ try {
+ await loadPDFBug();
+ this._PDFBug.init(mainContainer, enabled);
+ } catch (ex) {
+ console.error(`_parseHashParams: "${ex.message}".`);
+ }
+ }
+ if (params.has("locale")) {
+ AppOptions.set("locale", params.get("locale"));
+ }
+ },
+ async _initializeViewerComponents() {
+ const {
+ appConfig,
+ externalServices,
+ l10n
+ } = this;
+ const eventBus = AppOptions.get("isInAutomation") ? new AutomationEventBus() : new EventBus();
+ this.eventBus = eventBus;
+ this.overlayManager = new OverlayManager();
+ const pdfRenderingQueue = new PDFRenderingQueue();
+ pdfRenderingQueue.onIdle = this._cleanup.bind(this);
+ this.pdfRenderingQueue = pdfRenderingQueue;
+ const pdfLinkService = new PDFLinkService({
+ eventBus,
+ externalLinkTarget: AppOptions.get("externalLinkTarget"),
+ externalLinkRel: AppOptions.get("externalLinkRel"),
+ ignoreDestinationZoom: AppOptions.get("ignoreDestinationZoom")
+ });
+ this.pdfLinkService = pdfLinkService;
+ const downloadManager = this.downloadManager = new DownloadManager();
+ const findController = new PDFFindController({
+ linkService: pdfLinkService,
+ eventBus,
+ updateMatchesCountOnProgress: true
+ });
+ this.findController = findController;
+ const pdfScriptingManager = new PDFScriptingManager({
+ eventBus,
+ externalServices,
+ docProperties: this._scriptingDocProperties.bind(this)
+ });
+ this.pdfScriptingManager = pdfScriptingManager;
+ const container = appConfig.mainContainer,
+ viewer = appConfig.viewerContainer;
+ const annotationEditorMode = AppOptions.get("annotationEditorMode");
+ const pageColors = AppOptions.get("forcePageColors") || window.matchMedia("(forced-colors: active)").matches ? {
+ background: AppOptions.get("pageColorsBackground"),
+ foreground: AppOptions.get("pageColorsForeground")
+ } : null;
+ const altTextManager = appConfig.altTextDialog ? new AltTextManager(appConfig.altTextDialog, container, this.overlayManager, eventBus) : null;
+ const pdfViewer = new PDFViewer({
+ container,
+ viewer,
+ eventBus,
+ renderingQueue: pdfRenderingQueue,
+ linkService: pdfLinkService,
+ downloadManager,
+ altTextManager,
+ findController,
+ scriptingManager: AppOptions.get("enableScripting") && pdfScriptingManager,
+ l10n,
+ textLayerMode: AppOptions.get("textLayerMode"),
+ annotationMode: AppOptions.get("annotationMode"),
+ annotationEditorMode,
+ annotationEditorHighlightColors: AppOptions.get("highlightEditorColors"),
+ enableHighlightFloatingButton: AppOptions.get("enableHighlightFloatingButton"),
+ imageResourcesPath: AppOptions.get("imageResourcesPath"),
+ enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
+ maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
+ enablePermissions: AppOptions.get("enablePermissions"),
+ pageColors,
+ mlManager: this.mlManager
+ });
+ this.pdfViewer = pdfViewer;
+ pdfRenderingQueue.setViewer(pdfViewer);
+ pdfLinkService.setViewer(pdfViewer);
+ pdfScriptingManager.setViewer(pdfViewer);
+ if (appConfig.sidebar?.thumbnailView) {
+ this.pdfThumbnailViewer = new PDFThumbnailViewer({
+ container: appConfig.sidebar.thumbnailView,
+ eventBus,
+ renderingQueue: pdfRenderingQueue,
+ linkService: pdfLinkService,
+ pageColors
+ });
+ pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
+ }
+ if (!this.isViewerEmbedded && !AppOptions.get("disableHistory")) {
+ this.pdfHistory = new PDFHistory({
+ linkService: pdfLinkService,
+ eventBus
+ });
+ pdfLinkService.setHistory(this.pdfHistory);
+ }
+ if (!this.supportsIntegratedFind && appConfig.findBar) {
+ this.findBar = new PDFFindBar(appConfig.findBar, eventBus);
+ }
+ if (appConfig.annotationEditorParams) {
+ if (annotationEditorMode !== AnnotationEditorType.DISABLE) {
+ if (AppOptions.get("enableStampEditor")) {
+ appConfig.toolbar?.editorStampButton?.classList.remove("hidden");
+ }
+ const editorHighlightButton = appConfig.toolbar?.editorHighlightButton;
+ if (editorHighlightButton && AppOptions.get("enableHighlightEditor")) {
+ editorHighlightButton.hidden = false;
+ }
+ this.annotationEditorParams = new AnnotationEditorParams(appConfig.annotationEditorParams, eventBus);
+ } else {
+ for (const id of ["editorModeButtons", "editorModeSeparator"]) {
+ document.getElementById(id)?.classList.add("hidden");
+ }
+ }
+ }
+ if (appConfig.documentProperties) {
+ this.pdfDocumentProperties = new PDFDocumentProperties(appConfig.documentProperties, this.overlayManager, eventBus, l10n, () => this._docFilename);
+ }
+ if (appConfig.secondaryToolbar?.cursorHandToolButton) {
+ this.pdfCursorTools = new PDFCursorTools({
+ container,
+ eventBus,
+ cursorToolOnLoad: AppOptions.get("cursorToolOnLoad")
+ });
+ }
+ if (appConfig.toolbar) {
+ this.toolbar = new Toolbar(appConfig.toolbar, eventBus);
+ }
+ if (appConfig.secondaryToolbar) {
+ this.secondaryToolbar = new SecondaryToolbar(appConfig.secondaryToolbar, eventBus);
+ }
+ if (this.supportsFullscreen && appConfig.secondaryToolbar?.presentationModeButton) {
+ this.pdfPresentationMode = new PDFPresentationMode({
+ container,
+ pdfViewer,
+ eventBus
+ });
+ }
+ if (appConfig.passwordOverlay) {
+ this.passwordPrompt = new PasswordPrompt(appConfig.passwordOverlay, this.overlayManager, this.isViewerEmbedded);
+ }
+ if (appConfig.sidebar?.outlineView) {
+ this.pdfOutlineViewer = new PDFOutlineViewer({
+ container: appConfig.sidebar.outlineView,
+ eventBus,
+ l10n,
+ linkService: pdfLinkService,
+ downloadManager
+ });
+ }
+ if (appConfig.sidebar?.attachmentsView) {
+ this.pdfAttachmentViewer = new PDFAttachmentViewer({
+ container: appConfig.sidebar.attachmentsView,
+ eventBus,
+ l10n,
+ downloadManager
+ });
+ }
+ if (appConfig.sidebar?.layersView) {
+ this.pdfLayerViewer = new PDFLayerViewer({
+ container: appConfig.sidebar.layersView,
+ eventBus,
+ l10n
+ });
+ }
+ if (appConfig.sidebar) {
+ this.pdfSidebar = new PDFSidebar({
+ elements: appConfig.sidebar,
+ eventBus,
+ l10n
+ });
+ this.pdfSidebar.onToggled = this.forceRendering.bind(this);
+ this.pdfSidebar.onUpdateThumbnails = () => {
+ for (const pageView of pdfViewer.getCachedPageViews()) {
+ if (pageView.renderingState === RenderingStates.FINISHED) {
+ this.pdfThumbnailViewer.getThumbnail(pageView.id - 1)?.setImage(pageView);
+ }
+ }
+ this.pdfThumbnailViewer.scrollThumbnailIntoView(pdfViewer.currentPageNumber);
+ };
+ }
+ },
+ async run(config) {
+ this.preferences = new Preferences();
+ await this.initialize(config);
+ const {
+ appConfig,
+ eventBus
+ } = this;
+ let file;
+ const queryString = document.location.search.substring(1);
+ const params = parseQueryString(queryString);
+ file = params.get("file") ?? AppOptions.get("defaultUrl");
+ validateFileURL(file);
+ const fileInput = this._openFileInput = document.createElement("input");
+ fileInput.id = "fileInput";
+ fileInput.hidden = true;
+ fileInput.type = "file";
+ fileInput.value = null;
+ document.body.append(fileInput);
+ fileInput.addEventListener("change", function (evt) {
+ const {
+ files
+ } = evt.target;
+ if (!files || files.length === 0) {
+ return;
+ }
+ eventBus.dispatch("fileinputchange", {
+ source: this,
+ fileInput: evt.target
+ });
+ });
+ appConfig.mainContainer.addEventListener("dragover", function (evt) {
+ evt.preventDefault();
+ evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === "copy" ? "copy" : "move";
+ });
+ appConfig.mainContainer.addEventListener("drop", function (evt) {
+ evt.preventDefault();
+ const {
+ files
+ } = evt.dataTransfer;
+ if (!files || files.length === 0) {
+ return;
+ }
+ eventBus.dispatch("fileinputchange", {
+ source: this,
+ fileInput: evt.dataTransfer
+ });
+ });
+ if (!AppOptions.get("supportsDocumentFonts")) {
+ AppOptions.set("disableFontFace", true);
+ this.l10n.get("pdfjs-web-fonts-disabled").then(msg => {
+ console.warn(msg);
+ });
+ }
+ if (!this.supportsPrinting) {
+ appConfig.toolbar?.print?.classList.add("hidden");
+ appConfig.secondaryToolbar?.printButton.classList.add("hidden");
+ }
+ if (!this.supportsFullscreen) {
+ appConfig.secondaryToolbar?.presentationModeButton.classList.add("hidden");
+ }
+ if (this.supportsIntegratedFind) {
+ appConfig.toolbar?.viewFind?.classList.add("hidden");
+ }
+ if (file) {
+ this.open({
+ url: file
+ });
+ } else {
+ this._hideViewBookmark();
+ }
+ },
+ get externalServices() {
+ return shadow(this, "externalServices", new ExternalServices());
+ },
+ get mlManager() {
+ return shadow(this, "mlManager", AppOptions.get("enableML") === true ? new MLManager() : null);
+ },
+ get initialized() {
+ return this._initializedCapability.settled;
+ },
+ get initializedPromise() {
+ return this._initializedCapability.promise;
+ },
+ updateZoom(steps, scaleFactor, origin) {
+ if (this.pdfViewer.isInPresentationMode) {
+ return;
+ }
+ this.pdfViewer.updateScale({
+ drawingDelay: AppOptions.get("defaultZoomDelay"),
+ steps,
+ scaleFactor,
+ origin
+ });
+ },
+ zoomIn() {
+ this.updateZoom(1);
+ },
+ zoomOut() {
+ this.updateZoom(-1);
+ },
+ zoomReset() {
+ if (this.pdfViewer.isInPresentationMode) {
+ return;
+ }
+ this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
+ },
+ get pagesCount() {
+ return this.pdfDocument ? this.pdfDocument.numPages : 0;
+ },
+ get page() {
+ return this.pdfViewer.currentPageNumber;
+ },
+ set page(val) {
+ this.pdfViewer.currentPageNumber = val;
+ },
+ get supportsPrinting() {
+ return PDFPrintServiceFactory.supportsPrinting;
+ },
+ get supportsFullscreen() {
+ return shadow(this, "supportsFullscreen", document.fullscreenEnabled);
+ },
+ get supportsPinchToZoom() {
+ return shadow(this, "supportsPinchToZoom", AppOptions.get("supportsPinchToZoom"));
+ },
+ get supportsIntegratedFind() {
+ return shadow(this, "supportsIntegratedFind", AppOptions.get("supportsIntegratedFind"));
+ },
+ get loadingBar() {
+ const barElement = document.getElementById("loadingBar");
+ const bar = barElement ? new ProgressBar(barElement) : null;
+ return shadow(this, "loadingBar", bar);
+ },
+ get supportsMouseWheelZoomCtrlKey() {
+ return shadow(this, "supportsMouseWheelZoomCtrlKey", AppOptions.get("supportsMouseWheelZoomCtrlKey"));
+ },
+ get supportsMouseWheelZoomMetaKey() {
+ return shadow(this, "supportsMouseWheelZoomMetaKey", AppOptions.get("supportsMouseWheelZoomMetaKey"));
+ },
+ get supportsCaretBrowsingMode() {
+ return AppOptions.get("supportsCaretBrowsingMode");
+ },
+ moveCaret(isUp, select) {
+ this._caretBrowsing ||= new CaretBrowsingMode(this.appConfig.mainContainer, this.appConfig.viewerContainer, this.appConfig.toolbar?.container);
+ this._caretBrowsing.moveCaret(isUp, select);
+ },
+ setTitleUsingUrl(url = "", downloadUrl = null) {
+ this.url = url;
+ this.baseUrl = url.split("#", 1)[0];
+ if (downloadUrl) {
+ this._downloadUrl = downloadUrl === url ? this.baseUrl : downloadUrl.split("#", 1)[0];
+ }
+ if (isDataScheme(url)) {
+ this._hideViewBookmark();
+ }
+ let title = pdfjs_getPdfFilenameFromUrl(url, "");
+ if (!title) {
+ try {
+ title = decodeURIComponent(getFilenameFromUrl(url)) || url;
+ } catch {
+ title = url;
+ }
+ }
+ this.setTitle(title);
+ },
+ setTitle(title = this._title) {
+ this._title = title;
+ if (this.isViewerEmbedded) {
+ return;
+ }
+ const editorIndicator = this._hasAnnotationEditors && !this.pdfRenderingQueue.printing;
+ document.title = `${editorIndicator ? "* " : ""}${title}`;
+ },
+ get _docFilename() {
+ return this._contentDispositionFilename || pdfjs_getPdfFilenameFromUrl(this.url);
+ },
+ _hideViewBookmark() {
+ const {
+ secondaryToolbar
+ } = this.appConfig;
+ secondaryToolbar?.viewBookmarkButton.classList.add("hidden");
+ if (secondaryToolbar?.presentationModeButton.classList.contains("hidden")) {
+ document.getElementById("viewBookmarkSeparator")?.classList.add("hidden");
+ }
+ },
+ async close() {
+ this._unblockDocumentLoadEvent();
+ this._hideViewBookmark();
+ if (!this.pdfLoadingTask) {
+ return;
+ }
+ if (this.pdfDocument?.annotationStorage.size > 0 && this._annotationStorageModified) {
+ try {
+ await this.save();
+ } catch {}
+ }
+ const promises = [];
+ promises.push(this.pdfLoadingTask.destroy());
+ this.pdfLoadingTask = null;
+ if (this.pdfDocument) {
+ this.pdfDocument = null;
+ this.pdfThumbnailViewer?.setDocument(null);
+ this.pdfViewer.setDocument(null);
+ this.pdfLinkService.setDocument(null);
+ this.pdfDocumentProperties?.setDocument(null);
+ }
+ this.pdfLinkService.externalLinkEnabled = true;
+ this.store = null;
+ this.isInitialViewSet = false;
+ this.downloadComplete = false;
+ this.url = "";
+ this.baseUrl = "";
+ this._downloadUrl = "";
+ this.documentInfo = null;
+ this.metadata = null;
+ this._contentDispositionFilename = null;
+ this._contentLength = null;
+ this._saveInProgress = false;
+ this._hasAnnotationEditors = false;
+ promises.push(this.pdfScriptingManager.destroyPromise, this.passwordPrompt.close());
+ this.setTitle();
+ this.pdfSidebar?.reset();
+ this.pdfOutlineViewer?.reset();
+ this.pdfAttachmentViewer?.reset();
+ this.pdfLayerViewer?.reset();
+ this.pdfHistory?.reset();
+ this.findBar?.reset();
+ this.toolbar?.reset();
+ this.secondaryToolbar?.reset();
+ this._PDFBug?.cleanup();
+ await Promise.all(promises);
+ },
+ async open(args) {
+ if (this.pdfLoadingTask) {
+ await this.close();
+ }
+ const workerParams = AppOptions.getAll(OptionKind.WORKER);
+ Object.assign(GlobalWorkerOptions, workerParams);
+ if (args.url) {
+ this.setTitleUsingUrl(args.originalUrl || args.url, args.url);
+ }
+ const apiParams = AppOptions.getAll(OptionKind.API);
+ const loadingTask = getDocument({
+ ...apiParams,
+ ...args
+ });
+ this.pdfLoadingTask = loadingTask;
+ loadingTask.onPassword = (updateCallback, reason) => {
+ if (this.isViewerEmbedded) {
+ this._unblockDocumentLoadEvent();
+ }
+ this.pdfLinkService.externalLinkEnabled = false;
+ this.passwordPrompt.setUpdateCallback(updateCallback, reason);
+ this.passwordPrompt.open();
+ };
+ loadingTask.onProgress = ({
+ loaded,
+ total
+ }) => {
+ this.progress(loaded / total);
+ };
+ return loadingTask.promise.then(pdfDocument => {
+ this.load(pdfDocument);
+ }, reason => {
+ if (loadingTask !== this.pdfLoadingTask) {
+ return undefined;
+ }
+ let key = "pdfjs-loading-error";
+ if (reason instanceof InvalidPDFException) {
+ key = "pdfjs-invalid-file-error";
+ } else if (reason instanceof MissingPDFException) {
+ key = "pdfjs-missing-file-error";
+ } else if (reason instanceof UnexpectedResponseException) {
+ key = "pdfjs-unexpected-response-error";
+ }
+ return this._documentError(key, {
+ message: reason.message
+ }).then(() => {
+ throw reason;
+ });
+ });
+ },
+ _ensureDownloadComplete() {
+ if (this.pdfDocument && this.downloadComplete) {
+ return;
+ }
+ throw new Error("PDF document not downloaded.");
+ },
+ async download(options = {}) {
+ const url = this._downloadUrl,
+ filename = this._docFilename;
+ try {
+ this._ensureDownloadComplete();
+ const data = await this.pdfDocument.getData();
+ const blob = new Blob([data], {
+ type: "application/pdf"
+ });
+ await this.downloadManager.download(blob, url, filename, options);
+ } catch {
+ await this.downloadManager.downloadUrl(url, filename, options);
+ }
+ },
+ async save(options = {}) {
+ if (this._saveInProgress) {
+ return;
+ }
+ this._saveInProgress = true;
+ await this.pdfScriptingManager.dispatchWillSave();
+ const url = this._downloadUrl,
+ filename = this._docFilename;
+ try {
+ this._ensureDownloadComplete();
+ const data = await this.pdfDocument.saveDocument();
+ const blob = new Blob([data], {
+ type: "application/pdf"
+ });
+ await this.downloadManager.download(blob, url, filename, options);
+ } catch (reason) {
+ console.error(`Error when saving the document: ${reason.message}`);
+ await this.download(options);
+ } finally {
+ await this.pdfScriptingManager.dispatchDidSave();
+ this._saveInProgress = false;
+ }
+ if (this._hasAnnotationEditors) {
+ this.externalServices.reportTelemetry({
+ type: "editing",
+ data: {
+ type: "save",
+ stats: this.pdfDocument?.annotationStorage.editorStats
+ }
+ });
+ }
+ },
+ downloadOrSave(options = {}) {
+ if (this.pdfDocument?.annotationStorage.size > 0) {
+ this.save(options);
+ } else {
+ this.download(options);
+ }
+ },
+ async _documentError(key, moreInfo = null) {
+ this._unblockDocumentLoadEvent();
+ const message = await this._otherError(key || "pdfjs-loading-error", moreInfo);
+ this.eventBus.dispatch("documenterror", {
+ source: this,
+ message,
+ reason: moreInfo?.message ?? null
+ });
+ },
+ async _otherError(key, moreInfo = null) {
+ const message = await this.l10n.get(key);
+ const moreInfoText = [`PDF.js v${version || "?"} (build: ${build || "?"})`];
+ if (moreInfo) {
+ moreInfoText.push(`Message: ${moreInfo.message}`);
+ if (moreInfo.stack) {
+ moreInfoText.push(`Stack: ${moreInfo.stack}`);
+ } else {
+ if (moreInfo.filename) {
+ moreInfoText.push(`File: ${moreInfo.filename}`);
+ }
+ if (moreInfo.lineNumber) {
+ moreInfoText.push(`Line: ${moreInfo.lineNumber}`);
+ }
+ }
+ }
+ console.error(`${message}\n\n${moreInfoText.join("\n")}`);
+ return message;
+ },
+ progress(level) {
+ if (!this.loadingBar || this.downloadComplete) {
+ return;
+ }
+ const percent = Math.round(level * 100);
+ if (percent <= this.loadingBar.percent) {
+ return;
+ }
+ this.loadingBar.percent = percent;
+ if (this.pdfDocument?.loadingParams.disableAutoFetch ?? AppOptions.get("disableAutoFetch")) {
+ this.loadingBar.setDisableAutoFetch();
+ }
+ },
+ load(pdfDocument) {
+ this.pdfDocument = pdfDocument;
+ pdfDocument.getDownloadInfo().then(({
+ length
+ }) => {
+ this._contentLength = length;
+ this.downloadComplete = true;
+ this.loadingBar?.hide();
+ firstPagePromise.then(() => {
+ this.eventBus.dispatch("documentloaded", {
+ source: this
+ });
+ });
+ });
+ const pageLayoutPromise = pdfDocument.getPageLayout().catch(() => {});
+ const pageModePromise = pdfDocument.getPageMode().catch(() => {});
+ const openActionPromise = pdfDocument.getOpenAction().catch(() => {});
+ this.toolbar?.setPagesCount(pdfDocument.numPages, false);
+ this.secondaryToolbar?.setPagesCount(pdfDocument.numPages);
+ this.pdfLinkService.setDocument(pdfDocument);
+ this.pdfDocumentProperties?.setDocument(pdfDocument);
+ const pdfViewer = this.pdfViewer;
+ pdfViewer.setDocument(pdfDocument);
+ const {
+ firstPagePromise,
+ onePageRendered,
+ pagesPromise
+ } = pdfViewer;
+ this.pdfThumbnailViewer?.setDocument(pdfDocument);
+ const storedPromise = (this.store = new ViewHistory(pdfDocument.fingerprints[0])).getMultiple({
+ page: null,
+ zoom: DEFAULT_SCALE_VALUE,
+ scrollLeft: "0",
+ scrollTop: "0",
+ rotation: null,
+ sidebarView: SidebarView.UNKNOWN,
+ scrollMode: ScrollMode.UNKNOWN,
+ spreadMode: SpreadMode.UNKNOWN
+ }).catch(() => {});
+ firstPagePromise.then(pdfPage => {
+ this.loadingBar?.setWidth(this.appConfig.viewerContainer);
+ this._initializeAnnotationStorageCallbacks(pdfDocument);
+ Promise.all([animationStarted, storedPromise, pageLayoutPromise, pageModePromise, openActionPromise]).then(async ([timeStamp, stored, pageLayout, pageMode, openAction]) => {
+ const viewOnLoad = AppOptions.get("viewOnLoad");
+ this._initializePdfHistory({
+ fingerprint: pdfDocument.fingerprints[0],
+ viewOnLoad,
+ initialDest: openAction?.dest
+ });
+ const initialBookmark = this.initialBookmark;
+ const zoom = AppOptions.get("defaultZoomValue");
+ let hash = zoom ? `zoom=${zoom}` : null;
+ let rotation = null;
+ let sidebarView = AppOptions.get("sidebarViewOnLoad");
+ let scrollMode = AppOptions.get("scrollModeOnLoad");
+ let spreadMode = AppOptions.get("spreadModeOnLoad");
+ if (stored?.page && viewOnLoad !== ViewOnLoad.INITIAL) {
+ hash = `page=${stored.page}&zoom=${zoom || stored.zoom},` + `${stored.scrollLeft},${stored.scrollTop}`;
+ rotation = parseInt(stored.rotation, 10);
+ if (sidebarView === SidebarView.UNKNOWN) {
+ sidebarView = stored.sidebarView | 0;
+ }
+ if (scrollMode === ScrollMode.UNKNOWN) {
+ scrollMode = stored.scrollMode | 0;
+ }
+ if (spreadMode === SpreadMode.UNKNOWN) {
+ spreadMode = stored.spreadMode | 0;
+ }
+ }
+ if (pageMode && sidebarView === SidebarView.UNKNOWN) {
+ sidebarView = apiPageModeToSidebarView(pageMode);
+ }
+ if (pageLayout && scrollMode === ScrollMode.UNKNOWN && spreadMode === SpreadMode.UNKNOWN) {
+ const modes = apiPageLayoutToViewerModes(pageLayout);
+ spreadMode = modes.spreadMode;
+ }
+ this.setInitialView(hash, {
+ rotation,
+ sidebarView,
+ scrollMode,
+ spreadMode
+ });
+ this.eventBus.dispatch("documentinit", {
+ source: this
+ });
+ if (!this.isViewerEmbedded) {
+ pdfViewer.focus();
+ }
+ await Promise.race([pagesPromise, new Promise(resolve => {
+ setTimeout(resolve, FORCE_PAGES_LOADED_TIMEOUT);
+ })]);
+ if (!initialBookmark && !hash) {
+ return;
+ }
+ if (pdfViewer.hasEqualPageSizes) {
+ return;
+ }
+ this.initialBookmark = initialBookmark;
+ pdfViewer.currentScaleValue = pdfViewer.currentScaleValue;
+ this.setInitialView(hash);
+ }).catch(() => {
+ this.setInitialView();
+ }).then(function () {
+ pdfViewer.update();
+ });
+ });
+ pagesPromise.then(() => {
+ this._unblockDocumentLoadEvent();
+ this._initializeAutoPrint(pdfDocument, openActionPromise);
+ }, reason => {
+ this._documentError("pdfjs-loading-error", {
+ message: reason.message
+ });
+ });
+ onePageRendered.then(data => {
+ this.externalServices.reportTelemetry({
+ type: "pageInfo",
+ timestamp: data.timestamp
+ });
+ if (this.pdfOutlineViewer) {
+ pdfDocument.getOutline().then(outline => {
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ this.pdfOutlineViewer.render({
+ outline,
+ pdfDocument
+ });
+ });
+ }
+ if (this.pdfAttachmentViewer) {
+ pdfDocument.getAttachments().then(attachments => {
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ this.pdfAttachmentViewer.render({
+ attachments
+ });
+ });
+ }
+ if (this.pdfLayerViewer) {
+ pdfViewer.optionalContentConfigPromise.then(optionalContentConfig => {
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ this.pdfLayerViewer.render({
+ optionalContentConfig,
+ pdfDocument
+ });
+ });
+ }
+ });
+ this._initializePageLabels(pdfDocument);
+ this._initializeMetadata(pdfDocument);
+ },
+ async _scriptingDocProperties(pdfDocument) {
+ if (!this.documentInfo) {
+ await new Promise(resolve => {
+ this.eventBus._on("metadataloaded", resolve, {
+ once: true
+ });
+ });
+ if (pdfDocument !== this.pdfDocument) {
+ return null;
+ }
+ }
+ if (!this._contentLength) {
+ await new Promise(resolve => {
+ this.eventBus._on("documentloaded", resolve, {
+ once: true
+ });
+ });
+ if (pdfDocument !== this.pdfDocument) {
+ return null;
+ }
+ }
+ return {
+ ...this.documentInfo,
+ baseURL: this.baseUrl,
+ filesize: this._contentLength,
+ filename: this._docFilename,
+ metadata: this.metadata?.getRaw(),
+ authors: this.metadata?.get("dc:creator"),
+ numPages: this.pagesCount,
+ URL: this.url
+ };
+ },
+ async _initializeAutoPrint(pdfDocument, openActionPromise) {
+ const [openAction, jsActions] = await Promise.all([openActionPromise, this.pdfViewer.enableScripting ? null : pdfDocument.getJSActions()]);
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ let triggerAutoPrint = openAction?.action === "Print";
+ if (jsActions) {
+ console.warn("Warning: JavaScript support is not enabled");
+ for (const name in jsActions) {
+ if (triggerAutoPrint) {
+ break;
+ }
+ switch (name) {
+ case "WillClose":
+ case "WillSave":
+ case "DidSave":
+ case "WillPrint":
+ case "DidPrint":
+ continue;
+ }
+ triggerAutoPrint = jsActions[name].some(js => AutoPrintRegExp.test(js));
+ }
+ }
+ if (triggerAutoPrint) {
+ this.triggerPrinting();
+ }
+ },
+ async _initializeMetadata(pdfDocument) {
+ const {
+ info,
+ metadata,
+ contentDispositionFilename,
+ contentLength
+ } = await pdfDocument.getMetadata();
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ this.documentInfo = info;
+ this.metadata = metadata;
+ this._contentDispositionFilename ??= contentDispositionFilename;
+ this._contentLength ??= contentLength;
+ console.log(`PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` + `${(info.Producer || "-").trim()} / ${(info.Creator || "-").trim()}] ` + `(PDF.js: ${version || "?"} [${build || "?"}])`);
+ let pdfTitle = info.Title;
+ const metadataTitle = metadata?.get("dc:title");
+ if (metadataTitle) {
+ if (metadataTitle !== "Untitled" && !/[\uFFF0-\uFFFF]/g.test(metadataTitle)) {
+ pdfTitle = metadataTitle;
+ }
+ }
+ if (pdfTitle) {
+ this.setTitle(`${pdfTitle} - ${this._contentDispositionFilename || this._title}`);
+ } else if (this._contentDispositionFilename) {
+ this.setTitle(this._contentDispositionFilename);
+ }
+ if (info.IsXFAPresent && !info.IsAcroFormPresent && !pdfDocument.isPureXfa) {
+ if (pdfDocument.loadingParams.enableXfa) {
+ console.warn("Warning: XFA Foreground documents are not supported");
+ } else {
+ console.warn("Warning: XFA support is not enabled");
+ }
+ } else if ((info.IsAcroFormPresent || info.IsXFAPresent) && !this.pdfViewer.renderForms) {
+ console.warn("Warning: Interactive form support is not enabled");
+ }
+ if (info.IsSignaturesPresent) {
+ console.warn("Warning: Digital signatures validation is not supported");
+ }
+ this.eventBus.dispatch("metadataloaded", {
+ source: this
+ });
+ },
+ async _initializePageLabels(pdfDocument) {
+ const labels = await pdfDocument.getPageLabels();
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ if (!labels || AppOptions.get("disablePageLabels")) {
+ return;
+ }
+ const numLabels = labels.length;
+ let standardLabels = 0,
+ emptyLabels = 0;
+ for (let i = 0; i < numLabels; i++) {
+ const label = labels[i];
+ if (label === (i + 1).toString()) {
+ standardLabels++;
+ } else if (label === "") {
+ emptyLabels++;
+ } else {
+ break;
+ }
+ }
+ if (standardLabels >= numLabels || emptyLabels >= numLabels) {
+ return;
+ }
+ const {
+ pdfViewer,
+ pdfThumbnailViewer,
+ toolbar
+ } = this;
+ pdfViewer.setPageLabels(labels);
+ pdfThumbnailViewer?.setPageLabels(labels);
+ toolbar?.setPagesCount(numLabels, true);
+ toolbar?.setPageNumber(pdfViewer.currentPageNumber, pdfViewer.currentPageLabel);
+ },
+ _initializePdfHistory({
+ fingerprint,
+ viewOnLoad,
+ initialDest = null
+ }) {
+ if (!this.pdfHistory) {
+ return;
+ }
+ this.pdfHistory.initialize({
+ fingerprint,
+ resetHistory: viewOnLoad === ViewOnLoad.INITIAL,
+ updateUrl: AppOptions.get("historyUpdateUrl")
+ });
+ if (this.pdfHistory.initialBookmark) {
+ this.initialBookmark = this.pdfHistory.initialBookmark;
+ this.initialRotation = this.pdfHistory.initialRotation;
+ }
+ if (initialDest && !this.initialBookmark && viewOnLoad === ViewOnLoad.UNKNOWN) {
+ this.initialBookmark = JSON.stringify(initialDest);
+ this.pdfHistory.push({
+ explicitDest: initialDest,
+ pageNumber: null
+ });
+ }
+ },
+ _initializeAnnotationStorageCallbacks(pdfDocument) {
+ if (pdfDocument !== this.pdfDocument) {
+ return;
+ }
+ const {
+ annotationStorage
+ } = pdfDocument;
+ annotationStorage.onSetModified = () => {
+ window.addEventListener("beforeunload", beforeUnload);
+ this._annotationStorageModified = true;
+ };
+ annotationStorage.onResetModified = () => {
+ window.removeEventListener("beforeunload", beforeUnload);
+ delete this._annotationStorageModified;
+ };
+ annotationStorage.onAnnotationEditor = typeStr => {
+ this._hasAnnotationEditors = !!typeStr;
+ this.setTitle();
+ };
+ },
+ setInitialView(storedHash, {
+ rotation,
+ sidebarView,
+ scrollMode,
+ spreadMode
+ } = {}) {
+ const setRotation = angle => {
+ if (isValidRotation(angle)) {
+ this.pdfViewer.pagesRotation = angle;
+ }
+ };
+ const setViewerModes = (scroll, spread) => {
+ if (isValidScrollMode(scroll)) {
+ this.pdfViewer.scrollMode = scroll;
+ }
+ if (isValidSpreadMode(spread)) {
+ this.pdfViewer.spreadMode = spread;
+ }
+ };
+ this.isInitialViewSet = true;
+ this.pdfSidebar?.setInitialView(sidebarView);
+ setViewerModes(scrollMode, spreadMode);
+ if (this.initialBookmark) {
+ setRotation(this.initialRotation);
+ delete this.initialRotation;
+ this.pdfLinkService.setHash(this.initialBookmark);
+ this.initialBookmark = null;
+ } else if (storedHash) {
+ setRotation(rotation);
+ this.pdfLinkService.setHash(storedHash);
+ }
+ this.toolbar?.setPageNumber(this.pdfViewer.currentPageNumber, this.pdfViewer.currentPageLabel);
+ this.secondaryToolbar?.setPageNumber(this.pdfViewer.currentPageNumber);
+ if (!this.pdfViewer.currentScaleValue) {
+ this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
+ }
+ },
+ _cleanup() {
+ if (!this.pdfDocument) {
+ return;
+ }
+ this.pdfViewer.cleanup();
+ this.pdfThumbnailViewer?.cleanup();
+ this.pdfDocument.cleanup(AppOptions.get("fontExtraProperties"));
+ },
+ forceRendering() {
+ this.pdfRenderingQueue.printing = !!this.printService;
+ this.pdfRenderingQueue.isThumbnailViewEnabled = this.pdfSidebar?.visibleView === SidebarView.THUMBS;
+ this.pdfRenderingQueue.renderHighestPriority();
+ },
+ beforePrint() {
+ this._printAnnotationStoragePromise = this.pdfScriptingManager.dispatchWillPrint().catch(() => {}).then(() => this.pdfDocument?.annotationStorage.print);
+ if (this.printService) {
+ return;
+ }
+ if (!this.supportsPrinting) {
+ this._otherError("pdfjs-printing-not-supported");
+ return;
+ }
+ if (!this.pdfViewer.pageViewsReady) {
+ this.l10n.get("pdfjs-printing-not-ready").then(msg => {
+ window.alert(msg);
+ });
+ return;
+ }
+ this.printService = PDFPrintServiceFactory.createPrintService({
+ pdfDocument: this.pdfDocument,
+ pagesOverview: this.pdfViewer.getPagesOverview(),
+ printContainer: this.appConfig.printContainer,
+ printResolution: AppOptions.get("printResolution"),
+ printAnnotationStoragePromise: this._printAnnotationStoragePromise
+ });
+ this.forceRendering();
+ this.setTitle();
+ this.printService.layout();
+ if (this._hasAnnotationEditors) {
+ this.externalServices.reportTelemetry({
+ type: "editing",
+ data: {
+ type: "print",
+ stats: this.pdfDocument?.annotationStorage.editorStats
+ }
+ });
+ }
+ },
+ afterPrint() {
+ if (this._printAnnotationStoragePromise) {
+ this._printAnnotationStoragePromise.then(() => {
+ this.pdfScriptingManager.dispatchDidPrint();
+ });
+ this._printAnnotationStoragePromise = null;
+ }
+ if (this.printService) {
+ this.printService.destroy();
+ this.printService = null;
+ this.pdfDocument?.annotationStorage.resetModified();
+ }
+ this.forceRendering();
+ this.setTitle();
+ },
+ rotatePages(delta) {
+ this.pdfViewer.pagesRotation += delta;
+ },
+ requestPresentationMode() {
+ this.pdfPresentationMode?.request();
+ },
+ triggerPrinting() {
+ if (!this.supportsPrinting) {
+ return;
+ }
+ window.print();
+ },
+ bindEvents() {
+ if (this._eventBusAbortController) {
+ return;
+ }
+ this._eventBusAbortController = new AbortController();
+ const {
+ eventBus,
+ _eventBusAbortController: {
+ signal
+ }
+ } = this;
+ eventBus._on("resize", webViewerResize, {
+ signal
+ });
+ eventBus._on("hashchange", webViewerHashchange, {
+ signal
+ });
+ eventBus._on("beforeprint", this.beforePrint.bind(this), {
+ signal
+ });
+ eventBus._on("afterprint", this.afterPrint.bind(this), {
+ signal
+ });
+ eventBus._on("pagerender", webViewerPageRender, {
+ signal
+ });
+ eventBus._on("pagerendered", webViewerPageRendered, {
+ signal
+ });
+ eventBus._on("updateviewarea", webViewerUpdateViewarea, {
+ signal
+ });
+ eventBus._on("pagechanging", webViewerPageChanging, {
+ signal
+ });
+ eventBus._on("scalechanging", webViewerScaleChanging, {
+ signal
+ });
+ eventBus._on("rotationchanging", webViewerRotationChanging, {
+ signal
+ });
+ eventBus._on("sidebarviewchanged", webViewerSidebarViewChanged, {
+ signal
+ });
+ eventBus._on("pagemode", webViewerPageMode, {
+ signal
+ });
+ eventBus._on("namedaction", webViewerNamedAction, {
+ signal
+ });
+ eventBus._on("presentationmodechanged", webViewerPresentationModeChanged, {
+ signal
+ });
+ eventBus._on("presentationmode", webViewerPresentationMode, {
+ signal
+ });
+ eventBus._on("switchannotationeditormode", webViewerSwitchAnnotationEditorMode, {
+ signal
+ });
+ eventBus._on("switchannotationeditorparams", webViewerSwitchAnnotationEditorParams, {
+ signal
+ });
+ eventBus._on("print", webViewerPrint, {
+ signal
+ });
+ eventBus._on("download", webViewerDownload, {
+ signal
+ });
+ eventBus._on("firstpage", webViewerFirstPage, {
+ signal
+ });
+ eventBus._on("lastpage", webViewerLastPage, {
+ signal
+ });
+ eventBus._on("nextpage", webViewerNextPage, {
+ signal
+ });
+ eventBus._on("previouspage", webViewerPreviousPage, {
+ signal
+ });
+ eventBus._on("zoomin", webViewerZoomIn, {
+ signal
+ });
+ eventBus._on("zoomout", webViewerZoomOut, {
+ signal
+ });
+ eventBus._on("zoomreset", webViewerZoomReset, {
+ signal
+ });
+ eventBus._on("pagenumberchanged", webViewerPageNumberChanged, {
+ signal
+ });
+ eventBus._on("scalechanged", webViewerScaleChanged, {
+ signal
+ });
+ eventBus._on("rotatecw", webViewerRotateCw, {
+ signal
+ });
+ eventBus._on("rotateccw", webViewerRotateCcw, {
+ signal
+ });
+ eventBus._on("optionalcontentconfig", webViewerOptionalContentConfig, {
+ signal
+ });
+ eventBus._on("switchscrollmode", webViewerSwitchScrollMode, {
+ signal
+ });
+ eventBus._on("scrollmodechanged", webViewerScrollModeChanged, {
+ signal
+ });
+ eventBus._on("switchspreadmode", webViewerSwitchSpreadMode, {
+ signal
+ });
+ eventBus._on("spreadmodechanged", webViewerSpreadModeChanged, {
+ signal
+ });
+ eventBus._on("documentproperties", webViewerDocumentProperties, {
+ signal
+ });
+ eventBus._on("findfromurlhash", webViewerFindFromUrlHash, {
+ signal
+ });
+ eventBus._on("updatefindmatchescount", webViewerUpdateFindMatchesCount, {
+ signal
+ });
+ eventBus._on("updatefindcontrolstate", webViewerUpdateFindControlState, {
+ signal
+ });
+ eventBus._on("fileinputchange", webViewerFileInputChange, {
+ signal
+ });
+ eventBus._on("openfile", webViewerOpenFile, {
+ signal
+ });
+ },
+ bindWindowEvents() {
+ if (this._windowAbortController) {
+ return;
+ }
+ this._windowAbortController = new AbortController();
+ const {
+ eventBus,
+ appConfig: {
+ mainContainer
+ },
+ _windowAbortController: {
+ signal
+ }
+ } = this;
+ function addWindowResolutionChange(evt = null) {
+ if (evt) {
+ webViewerResolutionChange(evt);
+ }
+ const mediaQueryList = window.matchMedia(`(resolution: ${window.devicePixelRatio || 1}dppx)`);
+ mediaQueryList.addEventListener("change", addWindowResolutionChange, {
+ once: true,
+ signal
+ });
+ }
+ addWindowResolutionChange();
+ window.addEventListener("visibilitychange", webViewerVisibilityChange, {
+ signal
+ });
+ window.addEventListener("wheel", webViewerWheel, {
+ passive: false,
+ signal
+ });
+ window.addEventListener("touchstart", webViewerTouchStart, {
+ passive: false,
+ signal
+ });
+ window.addEventListener("touchmove", webViewerTouchMove, {
+ passive: false,
+ signal
+ });
+ window.addEventListener("touchend", webViewerTouchEnd, {
+ passive: false,
+ signal
+ });
+ window.addEventListener("click", webViewerClick, {
+ signal
+ });
+ window.addEventListener("keydown", webViewerKeyDown, {
+ signal
+ });
+ window.addEventListener("keyup", webViewerKeyUp, {
+ signal
+ });
+ window.addEventListener("resize", () => {
+ eventBus.dispatch("resize", {
+ source: window
+ });
+ }, {
+ signal
+ });
+ window.addEventListener("hashchange", () => {
+ eventBus.dispatch("hashchange", {
+ source: window,
+ hash: document.location.hash.substring(1)
+ });
+ }, {
+ signal
+ });
+ window.addEventListener("beforeprint", () => {
+ eventBus.dispatch("beforeprint", {
+ source: window
+ });
+ }, {
+ signal
+ });
+ window.addEventListener("afterprint", () => {
+ eventBus.dispatch("afterprint", {
+ source: window
+ });
+ }, {
+ signal
+ });
+ window.addEventListener("updatefromsandbox", event => {
+ eventBus.dispatch("updatefromsandbox", {
+ source: window,
+ detail: event.detail
+ });
+ }, {
+ signal
+ });
+ if (!("onscrollend" in document.documentElement)) {
+ return;
+ }
+ ({
+ scrollTop: this._lastScrollTop,
+ scrollLeft: this._lastScrollLeft
+ } = mainContainer);
+ const scrollend = () => {
+ ({
+ scrollTop: this._lastScrollTop,
+ scrollLeft: this._lastScrollLeft
+ } = mainContainer);
+ this._isScrolling = false;
+ mainContainer.addEventListener("scroll", scroll, {
+ passive: true,
+ signal
+ });
+ mainContainer.removeEventListener("scrollend", scrollend);
+ mainContainer.removeEventListener("blur", scrollend);
+ };
+ const scroll = () => {
+ if (this._isCtrlKeyDown) {
+ return;
+ }
+ if (this._lastScrollTop === mainContainer.scrollTop && this._lastScrollLeft === mainContainer.scrollLeft) {
+ return;
+ }
+ mainContainer.removeEventListener("scroll", scroll, {
+ passive: true
+ });
+ this._isScrolling = true;
+ mainContainer.addEventListener("scrollend", scrollend, {
+ signal
+ });
+ mainContainer.addEventListener("blur", scrollend, {
+ signal
+ });
+ };
+ mainContainer.addEventListener("scroll", scroll, {
+ passive: true,
+ signal
+ });
+ },
+ unbindEvents() {
+ this._eventBusAbortController?.abort();
+ this._eventBusAbortController = null;
+ },
+ unbindWindowEvents() {
+ this._windowAbortController?.abort();
+ this._windowAbortController = null;
+ },
+ _accumulateTicks(ticks, prop) {
+ if (this[prop] > 0 && ticks < 0 || this[prop] < 0 && ticks > 0) {
+ this[prop] = 0;
+ }
+ this[prop] += ticks;
+ const wholeTicks = Math.trunc(this[prop]);
+ this[prop] -= wholeTicks;
+ return wholeTicks;
+ },
+ _accumulateFactor(previousScale, factor, prop) {
+ if (factor === 1) {
+ return 1;
+ }
+ if (this[prop] > 1 && factor < 1 || this[prop] < 1 && factor > 1) {
+ this[prop] = 1;
+ }
+ const newFactor = Math.floor(previousScale * factor * this[prop] * 100) / (100 * previousScale);
+ this[prop] = factor / newFactor;
+ return newFactor;
+ },
+ _unblockDocumentLoadEvent() {
+ document.blockUnblockOnload?.(false);
+ this._unblockDocumentLoadEvent = () => {};
+ },
+ get scriptingReady() {
+ return this.pdfScriptingManager.ready;
+ }
+};
+initCom(PDFViewerApplication);
+{
+ PDFPrintServiceFactory.initGlobals(PDFViewerApplication);
+}
+{
+ const HOSTED_VIEWER_ORIGINS = ["null", "http://mozilla.github.io", "https://mozilla.github.io", "https://isidoresong.us.kg"];
+ var validateFileURL = function (file) {
+ if (!file) {
+ return;
+ }
+ try {
+ const viewerOrigin = new URL(window.location.href).origin || "null";
+ if (HOSTED_VIEWER_ORIGINS.includes(viewerOrigin)) {
+ return;
+ }
+ const fileOrigin = new URL(file, window.location.href).origin;
+ console.log(fileOrigin)
+ console.log(viewerOrigin)
+ if (fileOrigin !== viewerOrigin) {
+ throw new Error("file origin does not match viewer's");
+ }
+ } catch (ex) {
+ PDFViewerApplication._documentError("pdfjs-loading-error", {
+ message: ex.message
+ });
+ throw ex;
+ }
+ };
+}
+function webViewerPageRender({
+ pageNumber
+}) {
+ if (pageNumber === PDFViewerApplication.page) {
+ PDFViewerApplication.toolbar?.updateLoadingIndicatorState(true);
+ }
+}
+function webViewerPageRendered({
+ pageNumber,
+ error
+}) {
+ if (pageNumber === PDFViewerApplication.page) {
+ PDFViewerApplication.toolbar?.updateLoadingIndicatorState(false);
+ }
+ if (PDFViewerApplication.pdfSidebar?.visibleView === SidebarView.THUMBS) {
+ const pageView = PDFViewerApplication.pdfViewer.getPageView(pageNumber - 1);
+ const thumbnailView = PDFViewerApplication.pdfThumbnailViewer?.getThumbnail(pageNumber - 1);
+ if (pageView) {
+ thumbnailView?.setImage(pageView);
+ }
+ }
+ if (error) {
+ PDFViewerApplication._otherError("pdfjs-rendering-error", error);
+ }
+}
+function webViewerPageMode({
+ mode
+}) {
+ let view;
+ switch (mode) {
+ case "thumbs":
+ view = SidebarView.THUMBS;
+ break;
+ case "bookmarks":
+ case "outline":
+ view = SidebarView.OUTLINE;
+ break;
+ case "attachments":
+ view = SidebarView.ATTACHMENTS;
+ break;
+ case "layers":
+ view = SidebarView.LAYERS;
+ break;
+ case "none":
+ view = SidebarView.NONE;
+ break;
+ default:
+ console.error('Invalid "pagemode" hash parameter: ' + mode);
+ return;
+ }
+ PDFViewerApplication.pdfSidebar?.switchView(view, true);
+}
+function webViewerNamedAction(evt) {
+ switch (evt.action) {
+ case "GoToPage":
+ PDFViewerApplication.appConfig.toolbar?.pageNumber.select();
+ break;
+ case "Find":
+ if (!PDFViewerApplication.supportsIntegratedFind) {
+ PDFViewerApplication.findBar?.toggle();
+ }
+ break;
+ case "Print":
+ PDFViewerApplication.triggerPrinting();
+ break;
+ case "SaveAs":
+ PDFViewerApplication.downloadOrSave();
+ break;
+ }
+}
+function webViewerPresentationModeChanged(evt) {
+ PDFViewerApplication.pdfViewer.presentationModeState = evt.state;
+}
+function webViewerSidebarViewChanged({
+ view
+}) {
+ PDFViewerApplication.pdfRenderingQueue.isThumbnailViewEnabled = view === SidebarView.THUMBS;
+ if (PDFViewerApplication.isInitialViewSet) {
+ PDFViewerApplication.store?.set("sidebarView", view).catch(() => {});
+ }
+}
+function webViewerUpdateViewarea({
+ location
+}) {
+ if (PDFViewerApplication.isInitialViewSet) {
+ PDFViewerApplication.store?.setMultiple({
+ page: location.pageNumber,
+ zoom: location.scale,
+ scrollLeft: location.left,
+ scrollTop: location.top,
+ rotation: location.rotation
+ }).catch(() => {});
+ }
+ if (PDFViewerApplication.appConfig.secondaryToolbar) {
+ const href = PDFViewerApplication.pdfLinkService.getAnchorUrl(location.pdfOpenParams);
+ PDFViewerApplication.appConfig.secondaryToolbar.viewBookmarkButton.href = href;
+ }
+}
+function webViewerScrollModeChanged(evt) {
+ if (PDFViewerApplication.isInitialViewSet && !PDFViewerApplication.pdfViewer.isInPresentationMode) {
+ PDFViewerApplication.store?.set("scrollMode", evt.mode).catch(() => {});
+ }
+}
+function webViewerSpreadModeChanged(evt) {
+ if (PDFViewerApplication.isInitialViewSet && !PDFViewerApplication.pdfViewer.isInPresentationMode) {
+ PDFViewerApplication.store?.set("spreadMode", evt.mode).catch(() => {});
+ }
+}
+function webViewerResize() {
+ const {
+ pdfDocument,
+ pdfViewer,
+ pdfRenderingQueue
+ } = PDFViewerApplication;
+ if (pdfRenderingQueue.printing && window.matchMedia("print").matches) {
+ return;
+ }
+ if (!pdfDocument) {
+ return;
+ }
+ const currentScaleValue = pdfViewer.currentScaleValue;
+ if (currentScaleValue === "auto" || currentScaleValue === "page-fit" || currentScaleValue === "page-width") {
+ pdfViewer.currentScaleValue = currentScaleValue;
+ }
+ pdfViewer.update();
+}
+function webViewerHashchange(evt) {
+ const hash = evt.hash;
+ if (!hash) {
+ return;
+ }
+ if (!PDFViewerApplication.isInitialViewSet) {
+ PDFViewerApplication.initialBookmark = hash;
+ } else if (!PDFViewerApplication.pdfHistory?.popStateInProgress) {
+ PDFViewerApplication.pdfLinkService.setHash(hash);
+ }
+}
+{
+ var webViewerFileInputChange = function (evt) {
+ if (PDFViewerApplication.pdfViewer?.isInPresentationMode) {
+ return;
+ }
+ const file = evt.fileInput.files[0];
+ PDFViewerApplication.open({
+ url: URL.createObjectURL(file),
+ originalUrl: file.name
+ });
+ };
+ var webViewerOpenFile = function (evt) {
+ PDFViewerApplication._openFileInput?.click();
+ };
+}
+function webViewerPresentationMode() {
+ PDFViewerApplication.requestPresentationMode();
+}
+function webViewerSwitchAnnotationEditorMode(evt) {
+ PDFViewerApplication.pdfViewer.annotationEditorMode = evt;
+}
+function webViewerSwitchAnnotationEditorParams(evt) {
+ PDFViewerApplication.pdfViewer.annotationEditorParams = evt;
+}
+function webViewerPrint() {
+ PDFViewerApplication.triggerPrinting();
+}
+function webViewerDownload() {
+ PDFViewerApplication.downloadOrSave();
+}
+function webViewerFirstPage() {
+ PDFViewerApplication.page = 1;
+}
+function webViewerLastPage() {
+ PDFViewerApplication.page = PDFViewerApplication.pagesCount;
+}
+function webViewerNextPage() {
+ PDFViewerApplication.pdfViewer.nextPage();
+}
+function webViewerPreviousPage() {
+ PDFViewerApplication.pdfViewer.previousPage();
+}
+function webViewerZoomIn() {
+ PDFViewerApplication.zoomIn();
+}
+function webViewerZoomOut() {
+ PDFViewerApplication.zoomOut();
+}
+function webViewerZoomReset() {
+ PDFViewerApplication.zoomReset();
+}
+function webViewerPageNumberChanged(evt) {
+ const pdfViewer = PDFViewerApplication.pdfViewer;
+ if (evt.value !== "") {
+ PDFViewerApplication.pdfLinkService.goToPage(evt.value);
+ }
+ if (evt.value !== pdfViewer.currentPageNumber.toString() && evt.value !== pdfViewer.currentPageLabel) {
+ PDFViewerApplication.toolbar?.setPageNumber(pdfViewer.currentPageNumber, pdfViewer.currentPageLabel);
+ }
+}
+function webViewerScaleChanged(evt) {
+ PDFViewerApplication.pdfViewer.currentScaleValue = evt.value;
+}
+function webViewerRotateCw() {
+ PDFViewerApplication.rotatePages(90);
+}
+function webViewerRotateCcw() {
+ PDFViewerApplication.rotatePages(-90);
+}
+function webViewerOptionalContentConfig(evt) {
+ PDFViewerApplication.pdfViewer.optionalContentConfigPromise = evt.promise;
+}
+function webViewerSwitchScrollMode(evt) {
+ PDFViewerApplication.pdfViewer.scrollMode = evt.mode;
+}
+function webViewerSwitchSpreadMode(evt) {
+ PDFViewerApplication.pdfViewer.spreadMode = evt.mode;
+}
+function webViewerDocumentProperties() {
+ PDFViewerApplication.pdfDocumentProperties?.open();
+}
+function webViewerFindFromUrlHash(evt) {
+ PDFViewerApplication.eventBus.dispatch("find", {
+ source: evt.source,
+ type: "",
+ query: evt.query,
+ caseSensitive: false,
+ entireWord: false,
+ highlightAll: true,
+ findPrevious: false,
+ matchDiacritics: true
+ });
+}
+function webViewerUpdateFindMatchesCount({
+ matchesCount
+}) {
+ if (PDFViewerApplication.supportsIntegratedFind) {
+ PDFViewerApplication.externalServices.updateFindMatchesCount(matchesCount);
+ } else {
+ PDFViewerApplication.findBar?.updateResultsCount(matchesCount);
+ }
+}
+function webViewerUpdateFindControlState({
+ state,
+ previous,
+ matchesCount,
+ rawQuery
+}) {
+ if (PDFViewerApplication.supportsIntegratedFind) {
+ PDFViewerApplication.externalServices.updateFindControlState({
+ result: state,
+ findPrevious: previous,
+ matchesCount,
+ rawQuery
+ });
+ } else {
+ PDFViewerApplication.findBar?.updateUIState(state, previous, matchesCount);
+ }
+}
+function webViewerScaleChanging(evt) {
+ PDFViewerApplication.toolbar?.setPageScale(evt.presetValue, evt.scale);
+ PDFViewerApplication.pdfViewer.update();
+}
+function webViewerRotationChanging(evt) {
+ if (PDFViewerApplication.pdfThumbnailViewer) {
+ PDFViewerApplication.pdfThumbnailViewer.pagesRotation = evt.pagesRotation;
+ }
+ PDFViewerApplication.forceRendering();
+ PDFViewerApplication.pdfViewer.currentPageNumber = evt.pageNumber;
+}
+function webViewerPageChanging({
+ pageNumber,
+ pageLabel
+}) {
+ PDFViewerApplication.toolbar?.setPageNumber(pageNumber, pageLabel);
+ PDFViewerApplication.secondaryToolbar?.setPageNumber(pageNumber);
+ if (PDFViewerApplication.pdfSidebar?.visibleView === SidebarView.THUMBS) {
+ PDFViewerApplication.pdfThumbnailViewer?.scrollThumbnailIntoView(pageNumber);
+ }
+ const currentPage = PDFViewerApplication.pdfViewer.getPageView(pageNumber - 1);
+ PDFViewerApplication.toolbar?.updateLoadingIndicatorState(currentPage?.renderingState === RenderingStates.RUNNING);
+}
+function webViewerResolutionChange(evt) {
+ PDFViewerApplication.pdfViewer.refresh();
+}
+function webViewerVisibilityChange(evt) {
+ if (document.visibilityState === "visible") {
+ setZoomDisabledTimeout();
+ }
+}
+let zoomDisabledTimeout = null;
+function setZoomDisabledTimeout() {
+ if (zoomDisabledTimeout) {
+ clearTimeout(zoomDisabledTimeout);
+ }
+ zoomDisabledTimeout = setTimeout(function () {
+ zoomDisabledTimeout = null;
+ }, WHEEL_ZOOM_DISABLED_TIMEOUT);
+}
+function webViewerWheel(evt) {
+ const {
+ pdfViewer,
+ supportsMouseWheelZoomCtrlKey,
+ supportsMouseWheelZoomMetaKey,
+ supportsPinchToZoom
+ } = PDFViewerApplication;
+ if (pdfViewer.isInPresentationMode) {
+ return;
+ }
+ const deltaMode = evt.deltaMode;
+ let scaleFactor = Math.exp(-evt.deltaY / 100);
+ const isBuiltInMac = false;
+ const isPinchToZoom = evt.ctrlKey && !PDFViewerApplication._isCtrlKeyDown && deltaMode === WheelEvent.DOM_DELTA_PIXEL && evt.deltaX === 0 && (Math.abs(scaleFactor - 1) < 0.05 || isBuiltInMac) && evt.deltaZ === 0;
+ const origin = [evt.clientX, evt.clientY];
+ if (isPinchToZoom || evt.ctrlKey && supportsMouseWheelZoomCtrlKey || evt.metaKey && supportsMouseWheelZoomMetaKey) {
+ evt.preventDefault();
+ if (PDFViewerApplication._isScrolling || zoomDisabledTimeout || document.visibilityState === "hidden" || PDFViewerApplication.overlayManager.active) {
+ return;
+ }
+ if (isPinchToZoom && supportsPinchToZoom) {
+ scaleFactor = PDFViewerApplication._accumulateFactor(pdfViewer.currentScale, scaleFactor, "_wheelUnusedFactor");
+ PDFViewerApplication.updateZoom(null, scaleFactor, origin);
+ } else {
+ const delta = normalizeWheelEventDirection(evt);
+ let ticks = 0;
+ if (deltaMode === WheelEvent.DOM_DELTA_LINE || deltaMode === WheelEvent.DOM_DELTA_PAGE) {
+ if (Math.abs(delta) >= 1) {
+ ticks = Math.sign(delta);
+ } else {
+ ticks = PDFViewerApplication._accumulateTicks(delta, "_wheelUnusedTicks");
+ }
+ } else {
+ const PIXELS_PER_LINE_SCALE = 30;
+ ticks = PDFViewerApplication._accumulateTicks(delta / PIXELS_PER_LINE_SCALE, "_wheelUnusedTicks");
+ }
+ PDFViewerApplication.updateZoom(ticks, null, origin);
+ }
+ }
+}
+function webViewerTouchStart(evt) {
+ if (PDFViewerApplication.pdfViewer.isInPresentationMode || evt.touches.length < 2) {
+ return;
+ }
+ evt.preventDefault();
+ if (evt.touches.length !== 2 || PDFViewerApplication.overlayManager.active) {
+ PDFViewerApplication._touchInfo = null;
+ return;
+ }
+ let [touch0, touch1] = evt.touches;
+ if (touch0.identifier > touch1.identifier) {
+ [touch0, touch1] = [touch1, touch0];
+ }
+ PDFViewerApplication._touchInfo = {
+ touch0X: touch0.pageX,
+ touch0Y: touch0.pageY,
+ touch1X: touch1.pageX,
+ touch1Y: touch1.pageY
+ };
+}
+function webViewerTouchMove(evt) {
+ if (!PDFViewerApplication._touchInfo || evt.touches.length !== 2) {
+ return;
+ }
+ const {
+ pdfViewer,
+ _touchInfo,
+ supportsPinchToZoom
+ } = PDFViewerApplication;
+ let [touch0, touch1] = evt.touches;
+ if (touch0.identifier > touch1.identifier) {
+ [touch0, touch1] = [touch1, touch0];
+ }
+ const {
+ pageX: page0X,
+ pageY: page0Y
+ } = touch0;
+ const {
+ pageX: page1X,
+ pageY: page1Y
+ } = touch1;
+ const {
+ touch0X: pTouch0X,
+ touch0Y: pTouch0Y,
+ touch1X: pTouch1X,
+ touch1Y: pTouch1Y
+ } = _touchInfo;
+ if (Math.abs(pTouch0X - page0X) <= 1 && Math.abs(pTouch0Y - page0Y) <= 1 && Math.abs(pTouch1X - page1X) <= 1 && Math.abs(pTouch1Y - page1Y) <= 1) {
+ return;
+ }
+ _touchInfo.touch0X = page0X;
+ _touchInfo.touch0Y = page0Y;
+ _touchInfo.touch1X = page1X;
+ _touchInfo.touch1Y = page1Y;
+ if (pTouch0X === page0X && pTouch0Y === page0Y) {
+ const v1X = pTouch1X - page0X;
+ const v1Y = pTouch1Y - page0Y;
+ const v2X = page1X - page0X;
+ const v2Y = page1Y - page0Y;
+ const det = v1X * v2Y - v1Y * v2X;
+ if (Math.abs(det) > 0.02 * Math.hypot(v1X, v1Y) * Math.hypot(v2X, v2Y)) {
+ return;
+ }
+ } else if (pTouch1X === page1X && pTouch1Y === page1Y) {
+ const v1X = pTouch0X - page1X;
+ const v1Y = pTouch0Y - page1Y;
+ const v2X = page0X - page1X;
+ const v2Y = page0Y - page1Y;
+ const det = v1X * v2Y - v1Y * v2X;
+ if (Math.abs(det) > 0.02 * Math.hypot(v1X, v1Y) * Math.hypot(v2X, v2Y)) {
+ return;
+ }
+ } else {
+ const diff0X = page0X - pTouch0X;
+ const diff1X = page1X - pTouch1X;
+ const diff0Y = page0Y - pTouch0Y;
+ const diff1Y = page1Y - pTouch1Y;
+ const dotProduct = diff0X * diff1X + diff0Y * diff1Y;
+ if (dotProduct >= 0) {
+ return;
+ }
+ }
+ evt.preventDefault();
+ const origin = [(page0X + page1X) / 2, (page0Y + page1Y) / 2];
+ const distance = Math.hypot(page0X - page1X, page0Y - page1Y) || 1;
+ const pDistance = Math.hypot(pTouch0X - pTouch1X, pTouch0Y - pTouch1Y) || 1;
+ if (supportsPinchToZoom) {
+ const newScaleFactor = PDFViewerApplication._accumulateFactor(pdfViewer.currentScale, distance / pDistance, "_touchUnusedFactor");
+ PDFViewerApplication.updateZoom(null, newScaleFactor, origin);
+ } else {
+ const PIXELS_PER_LINE_SCALE = 30;
+ const ticks = PDFViewerApplication._accumulateTicks((distance - pDistance) / PIXELS_PER_LINE_SCALE, "_touchUnusedTicks");
+ PDFViewerApplication.updateZoom(ticks, null, origin);
+ }
+}
+function webViewerTouchEnd(evt) {
+ if (!PDFViewerApplication._touchInfo) {
+ return;
+ }
+ evt.preventDefault();
+ PDFViewerApplication._touchInfo = null;
+ PDFViewerApplication._touchUnusedTicks = 0;
+ PDFViewerApplication._touchUnusedFactor = 1;
+}
+function webViewerClick(evt) {
+ if (!PDFViewerApplication.secondaryToolbar?.isOpen) {
+ return;
+ }
+ const appConfig = PDFViewerApplication.appConfig;
+ if (PDFViewerApplication.pdfViewer.containsElement(evt.target) || appConfig.toolbar?.container.contains(evt.target) && evt.target !== appConfig.secondaryToolbar?.toggleButton) {
+ PDFViewerApplication.secondaryToolbar.close();
+ }
+}
+function webViewerKeyUp(evt) {
+ if (evt.key === "Control") {
+ PDFViewerApplication._isCtrlKeyDown = false;
+ }
+}
+function webViewerKeyDown(evt) {
+ PDFViewerApplication._isCtrlKeyDown = evt.key === "Control";
+ if (PDFViewerApplication.overlayManager.active) {
+ return;
+ }
+ const {
+ eventBus,
+ pdfViewer
+ } = PDFViewerApplication;
+ const isViewerInPresentationMode = pdfViewer.isInPresentationMode;
+ let handled = false,
+ ensureViewerFocused = false;
+ const cmd = (evt.ctrlKey ? 1 : 0) | (evt.altKey ? 2 : 0) | (evt.shiftKey ? 4 : 0) | (evt.metaKey ? 8 : 0);
+ if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
+ switch (evt.keyCode) {
+ case 70:
+ if (!PDFViewerApplication.supportsIntegratedFind && !evt.shiftKey) {
+ PDFViewerApplication.findBar?.open();
+ handled = true;
+ }
+ break;
+ case 71:
+ if (!PDFViewerApplication.supportsIntegratedFind) {
+ const {
+ state
+ } = PDFViewerApplication.findController;
+ if (state) {
+ const newState = {
+ source: window,
+ type: "again",
+ findPrevious: cmd === 5 || cmd === 12
+ };
+ eventBus.dispatch("find", {
+ ...state,
+ ...newState
+ });
+ }
+ handled = true;
+ }
+ break;
+ case 61:
+ case 107:
+ case 187:
+ case 171:
+ PDFViewerApplication.zoomIn();
+ handled = true;
+ break;
+ case 173:
+ case 109:
+ case 189:
+ PDFViewerApplication.zoomOut();
+ handled = true;
+ break;
+ case 48:
+ case 96:
+ if (!isViewerInPresentationMode) {
+ setTimeout(function () {
+ PDFViewerApplication.zoomReset();
+ });
+ handled = false;
+ }
+ break;
+ case 38:
+ if (isViewerInPresentationMode || PDFViewerApplication.page > 1) {
+ PDFViewerApplication.page = 1;
+ handled = true;
+ ensureViewerFocused = true;
+ }
+ break;
+ case 40:
+ if (isViewerInPresentationMode || PDFViewerApplication.page < PDFViewerApplication.pagesCount) {
+ PDFViewerApplication.page = PDFViewerApplication.pagesCount;
+ handled = true;
+ ensureViewerFocused = true;
+ }
+ break;
+ }
+ }
+ if (cmd === 1 || cmd === 8) {
+ switch (evt.keyCode) {
+ case 83:
+ eventBus.dispatch("download", {
+ source: window
+ });
+ handled = true;
+ break;
+ case 79:
+ {
+ eventBus.dispatch("openfile", {
+ source: window
+ });
+ handled = true;
+ }
+ break;
+ }
+ }
+ if (cmd === 3 || cmd === 10) {
+ switch (evt.keyCode) {
+ case 80:
+ PDFViewerApplication.requestPresentationMode();
+ handled = true;
+ PDFViewerApplication.externalServices.reportTelemetry({
+ type: "buttons",
+ data: {
+ id: "presentationModeKeyboard"
+ }
+ });
+ break;
+ case 71:
+ if (PDFViewerApplication.appConfig.toolbar) {
+ PDFViewerApplication.appConfig.toolbar.pageNumber.select();
+ handled = true;
+ }
+ break;
+ }
+ }
+ if (handled) {
+ if (ensureViewerFocused && !isViewerInPresentationMode) {
+ pdfViewer.focus();
+ }
+ evt.preventDefault();
+ return;
+ }
+ const curElement = getActiveOrFocusedElement();
+ const curElementTagName = curElement?.tagName.toUpperCase();
+ if (curElementTagName === "INPUT" || curElementTagName === "TEXTAREA" || curElementTagName === "SELECT" || curElementTagName === "BUTTON" && (evt.keyCode === 13 || evt.keyCode === 32) || curElement?.isContentEditable) {
+ if (evt.keyCode !== 27) {
+ return;
+ }
+ }
+ if (cmd === 0) {
+ let turnPage = 0,
+ turnOnlyIfPageFit = false;
+ switch (evt.keyCode) {
+ case 38:
+ if (PDFViewerApplication.supportsCaretBrowsingMode) {
+ PDFViewerApplication.moveCaret(true, false);
+ handled = true;
+ break;
+ }
+ case 33:
+ if (pdfViewer.isVerticalScrollbarEnabled) {
+ turnOnlyIfPageFit = true;
+ }
+ turnPage = -1;
+ break;
+ case 8:
+ if (!isViewerInPresentationMode) {
+ turnOnlyIfPageFit = true;
+ }
+ turnPage = -1;
+ break;
+ case 37:
+ if (PDFViewerApplication.supportsCaretBrowsingMode) {
+ return;
+ }
+ if (pdfViewer.isHorizontalScrollbarEnabled) {
+ turnOnlyIfPageFit = true;
+ }
+ case 75:
+ case 80:
+ turnPage = -1;
+ break;
+ case 27:
+ if (PDFViewerApplication.secondaryToolbar?.isOpen) {
+ PDFViewerApplication.secondaryToolbar.close();
+ handled = true;
+ }
+ if (!PDFViewerApplication.supportsIntegratedFind && PDFViewerApplication.findBar?.opened) {
+ PDFViewerApplication.findBar.close();
+ handled = true;
+ }
+ break;
+ case 40:
+ if (PDFViewerApplication.supportsCaretBrowsingMode) {
+ PDFViewerApplication.moveCaret(false, false);
+ handled = true;
+ break;
+ }
+ case 34:
+ if (pdfViewer.isVerticalScrollbarEnabled) {
+ turnOnlyIfPageFit = true;
+ }
+ turnPage = 1;
+ break;
+ case 13:
+ case 32:
+ if (!isViewerInPresentationMode) {
+ turnOnlyIfPageFit = true;
+ }
+ turnPage = 1;
+ break;
+ case 39:
+ if (PDFViewerApplication.supportsCaretBrowsingMode) {
+ return;
+ }
+ if (pdfViewer.isHorizontalScrollbarEnabled) {
+ turnOnlyIfPageFit = true;
+ }
+ case 74:
+ case 78:
+ turnPage = 1;
+ break;
+ case 36:
+ if (isViewerInPresentationMode || PDFViewerApplication.page > 1) {
+ PDFViewerApplication.page = 1;
+ handled = true;
+ ensureViewerFocused = true;
+ }
+ break;
+ case 35:
+ if (isViewerInPresentationMode || PDFViewerApplication.page < PDFViewerApplication.pagesCount) {
+ PDFViewerApplication.page = PDFViewerApplication.pagesCount;
+ handled = true;
+ ensureViewerFocused = true;
+ }
+ break;
+ case 83:
+ PDFViewerApplication.pdfCursorTools?.switchTool(CursorTool.SELECT);
+ break;
+ case 72:
+ PDFViewerApplication.pdfCursorTools?.switchTool(CursorTool.HAND);
+ break;
+ case 82:
+ PDFViewerApplication.rotatePages(90);
+ break;
+ case 115:
+ PDFViewerApplication.pdfSidebar?.toggle();
+ break;
+ }
+ if (turnPage !== 0 && (!turnOnlyIfPageFit || pdfViewer.currentScaleValue === "page-fit")) {
+ if (turnPage > 0) {
+ pdfViewer.nextPage();
+ } else {
+ pdfViewer.previousPage();
+ }
+ handled = true;
+ }
+ }
+ if (cmd === 4) {
+ switch (evt.keyCode) {
+ case 13:
+ case 32:
+ if (!isViewerInPresentationMode && pdfViewer.currentScaleValue !== "page-fit") {
+ break;
+ }
+ pdfViewer.previousPage();
+ handled = true;
+ break;
+ case 38:
+ PDFViewerApplication.moveCaret(true, true);
+ handled = true;
+ break;
+ case 40:
+ PDFViewerApplication.moveCaret(false, true);
+ handled = true;
+ break;
+ case 82:
+ PDFViewerApplication.rotatePages(-90);
+ break;
+ }
+ }
+ if (!handled && !isViewerInPresentationMode) {
+ if (evt.keyCode >= 33 && evt.keyCode <= 40 || evt.keyCode === 32 && curElementTagName !== "BUTTON") {
+ ensureViewerFocused = true;
+ }
+ }
+ if (ensureViewerFocused && !pdfViewer.containsElement(curElement)) {
+ pdfViewer.focus();
+ }
+ if (handled) {
+ evt.preventDefault();
+ }
+}
+function beforeUnload(evt) {
+ evt.preventDefault();
+ evt.returnValue = "";
+ return false;
+}
+function webViewerAnnotationEditorStatesChanged(data) {
+ PDFViewerApplication.externalServices.updateEditorStates(data);
+}
+function webViewerReportTelemetry({
+ details
+}) {
+ PDFViewerApplication.externalServices.reportTelemetry(details);
+}
+
+;// CONCATENATED MODULE: ./web/viewer.js
+
+
+
+
+const pdfjsVersion = "4.3.136";
+const pdfjsBuild = "0cec64437";
+const AppConstants = {
+ LinkTarget: LinkTarget,
+ RenderingStates: RenderingStates,
+ ScrollMode: ScrollMode,
+ SpreadMode: SpreadMode
+};
+window.PDFViewerApplication = PDFViewerApplication;
+window.PDFViewerApplicationConstants = AppConstants;
+window.PDFViewerApplicationOptions = AppOptions;
+function getViewerConfiguration() {
+ return {
+ appContainer: document.body,
+ mainContainer: document.getElementById("viewerContainer"),
+ viewerContainer: document.getElementById("viewer"),
+ toolbar: {
+ container: document.getElementById("toolbarViewer"),
+ numPages: document.getElementById("numPages"),
+ pageNumber: document.getElementById("pageNumber"),
+ scaleSelect: document.getElementById("scaleSelect"),
+ customScaleOption: document.getElementById("customScaleOption"),
+ previous: document.getElementById("previous"),
+ next: document.getElementById("next"),
+ zoomIn: document.getElementById("zoomIn"),
+ zoomOut: document.getElementById("zoomOut"),
+ viewFind: document.getElementById("viewFind"),
+ print: document.getElementById("print"),
+ editorFreeTextButton: document.getElementById("editorFreeText"),
+ editorFreeTextParamsToolbar: document.getElementById("editorFreeTextParamsToolbar"),
+ editorHighlightButton: document.getElementById("editorHighlight"),
+ editorHighlightParamsToolbar: document.getElementById("editorHighlightParamsToolbar"),
+ editorHighlightColorPicker: document.getElementById("editorHighlightColorPicker"),
+ editorInkButton: document.getElementById("editorInk"),
+ editorInkParamsToolbar: document.getElementById("editorInkParamsToolbar"),
+ editorStampButton: document.getElementById("editorStamp"),
+ editorStampParamsToolbar: document.getElementById("editorStampParamsToolbar"),
+ download: document.getElementById("download")
+ },
+ secondaryToolbar: {
+ toolbar: document.getElementById("secondaryToolbar"),
+ toggleButton: document.getElementById("secondaryToolbarToggle"),
+ presentationModeButton: document.getElementById("presentationMode"),
+ openFileButton: document.getElementById("secondaryOpenFile"),
+ printButton: document.getElementById("secondaryPrint"),
+ downloadButton: document.getElementById("secondaryDownload"),
+ viewBookmarkButton: document.getElementById("viewBookmark"),
+ firstPageButton: document.getElementById("firstPage"),
+ lastPageButton: document.getElementById("lastPage"),
+ pageRotateCwButton: document.getElementById("pageRotateCw"),
+ pageRotateCcwButton: document.getElementById("pageRotateCcw"),
+ cursorSelectToolButton: document.getElementById("cursorSelectTool"),
+ cursorHandToolButton: document.getElementById("cursorHandTool"),
+ scrollPageButton: document.getElementById("scrollPage"),
+ scrollVerticalButton: document.getElementById("scrollVertical"),
+ scrollHorizontalButton: document.getElementById("scrollHorizontal"),
+ scrollWrappedButton: document.getElementById("scrollWrapped"),
+ spreadNoneButton: document.getElementById("spreadNone"),
+ spreadOddButton: document.getElementById("spreadOdd"),
+ spreadEvenButton: document.getElementById("spreadEven"),
+ documentPropertiesButton: document.getElementById("documentProperties")
+ },
+ sidebar: {
+ outerContainer: document.getElementById("outerContainer"),
+ sidebarContainer: document.getElementById("sidebarContainer"),
+ toggleButton: document.getElementById("sidebarToggle"),
+ resizer: document.getElementById("sidebarResizer"),
+ thumbnailButton: document.getElementById("viewThumbnail"),
+ outlineButton: document.getElementById("viewOutline"),
+ attachmentsButton: document.getElementById("viewAttachments"),
+ layersButton: document.getElementById("viewLayers"),
+ thumbnailView: document.getElementById("thumbnailView"),
+ outlineView: document.getElementById("outlineView"),
+ attachmentsView: document.getElementById("attachmentsView"),
+ layersView: document.getElementById("layersView"),
+ currentOutlineItemButton: document.getElementById("currentOutlineItem")
+ },
+ findBar: {
+ bar: document.getElementById("findbar"),
+ toggleButton: document.getElementById("viewFind"),
+ findField: document.getElementById("findInput"),
+ highlightAllCheckbox: document.getElementById("findHighlightAll"),
+ caseSensitiveCheckbox: document.getElementById("findMatchCase"),
+ matchDiacriticsCheckbox: document.getElementById("findMatchDiacritics"),
+ entireWordCheckbox: document.getElementById("findEntireWord"),
+ findMsg: document.getElementById("findMsg"),
+ findResultsCount: document.getElementById("findResultsCount"),
+ findPreviousButton: document.getElementById("findPrevious"),
+ findNextButton: document.getElementById("findNext")
+ },
+ passwordOverlay: {
+ dialog: document.getElementById("passwordDialog"),
+ label: document.getElementById("passwordText"),
+ input: document.getElementById("password"),
+ submitButton: document.getElementById("passwordSubmit"),
+ cancelButton: document.getElementById("passwordCancel")
+ },
+ documentProperties: {
+ dialog: document.getElementById("documentPropertiesDialog"),
+ closeButton: document.getElementById("documentPropertiesClose"),
+ fields: {
+ fileName: document.getElementById("fileNameField"),
+ fileSize: document.getElementById("fileSizeField"),
+ title: document.getElementById("titleField"),
+ author: document.getElementById("authorField"),
+ subject: document.getElementById("subjectField"),
+ keywords: document.getElementById("keywordsField"),
+ creationDate: document.getElementById("creationDateField"),
+ modificationDate: document.getElementById("modificationDateField"),
+ creator: document.getElementById("creatorField"),
+ producer: document.getElementById("producerField"),
+ version: document.getElementById("versionField"),
+ pageCount: document.getElementById("pageCountField"),
+ pageSize: document.getElementById("pageSizeField"),
+ linearized: document.getElementById("linearizedField")
+ }
+ },
+ altTextDialog: {
+ dialog: document.getElementById("altTextDialog"),
+ optionDescription: document.getElementById("descriptionButton"),
+ optionDecorative: document.getElementById("decorativeButton"),
+ textarea: document.getElementById("descriptionTextarea"),
+ cancelButton: document.getElementById("altTextCancel"),
+ saveButton: document.getElementById("altTextSave")
+ },
+ annotationEditorParams: {
+ editorFreeTextFontSize: document.getElementById("editorFreeTextFontSize"),
+ editorFreeTextColor: document.getElementById("editorFreeTextColor"),
+ editorInkColor: document.getElementById("editorInkColor"),
+ editorInkThickness: document.getElementById("editorInkThickness"),
+ editorInkOpacity: document.getElementById("editorInkOpacity"),
+ editorStampAddImage: document.getElementById("editorStampAddImage"),
+ editorFreeHighlightThickness: document.getElementById("editorFreeHighlightThickness"),
+ editorHighlightShowAll: document.getElementById("editorHighlightShowAll")
+ },
+ printContainer: document.getElementById("printContainer")
+ };
+}
+function webViewerLoad() {
+ const config = getViewerConfiguration();
+ const event = new CustomEvent("webviewerloaded", {
+ bubbles: true,
+ cancelable: true,
+ detail: {
+ source: window
+ }
+ });
+ try {
+ parent.document.dispatchEvent(event);
+ } catch (ex) {
+ console.error(`webviewerloaded: ${ex}`);
+ document.dispatchEvent(event);
+ }
+ PDFViewerApplication.run(config);
+}
+document.blockUnblockOnload?.(true);
+if (document.readyState === "interactive" || document.readyState === "complete") {
+ webViewerLoad();
+} else {
+ document.addEventListener("DOMContentLoaded", webViewerLoad, true);
+}
+
+var __webpack_exports__PDFViewerApplication = __webpack_exports__.PDFViewerApplication;
+var __webpack_exports__PDFViewerApplicationConstants = __webpack_exports__.PDFViewerApplicationConstants;
+var __webpack_exports__PDFViewerApplicationOptions = __webpack_exports__.PDFViewerApplicationOptions;
+export { __webpack_exports__PDFViewerApplication as PDFViewerApplication, __webpack_exports__PDFViewerApplicationConstants as PDFViewerApplicationConstants, __webpack_exports__PDFViewerApplicationOptions as PDFViewerApplicationOptions };
+
+//# sourceMappingURL=viewer.mjs.map
\ No newline at end of file
diff --git "a/web/\343\200\220\345\233\276\347\211\207\347\211\210\343\200\221\351\200\232\347\224\250\351\202\200\350\257\267\345\207\275.pdf" "b/web/\343\200\220\345\233\276\347\211\207\347\211\210\343\200\221\351\200\232\347\224\250\351\202\200\350\257\267\345\207\275.pdf"
new file mode 100644
index 0000000000000000000000000000000000000000..ff1be6e3db4a48451c4950c1c91480432e36561a
Binary files /dev/null and "b/web/\343\200\220\345\233\276\347\211\207\347\211\210\343\200\221\351\200\232\347\224\250\351\202\200\350\257\267\345\207\275.pdf" differ
diff --git "a/\343\200\220\345\233\276\347\211\207\347\211\210\343\200\221\351\200\232\347\224\250\351\202\200\350\257\267\345\207\275.pdf" "b/\343\200\220\345\233\276\347\211\207\347\211\210\343\200\221\351\200\232\347\224\250\351\202\200\350\257\267\345\207\275.pdf"
new file mode 100644
index 0000000000000000000000000000000000000000..ff1be6e3db4a48451c4950c1c91480432e36561a
Binary files /dev/null and "b/\343\200\220\345\233\276\347\211\207\347\211\210\343\200\221\351\200\232\347\224\250\351\202\200\350\257\267\345\207\275.pdf" differ