annotation_layer.js 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2021 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.AnnotationLayer = void 0;
  27. var _display_utils = require("./display_utils.js");
  28. var _util = require("../shared/util.js");
  29. var _annotation_storage = require("./annotation_storage.js");
  30. var _scripting_utils = require("../shared/scripting_utils.js");
  31. class AnnotationElementFactory {
  32. static create(parameters) {
  33. const subtype = parameters.data.annotationType;
  34. switch (subtype) {
  35. case _util.AnnotationType.LINK:
  36. return new LinkAnnotationElement(parameters);
  37. case _util.AnnotationType.TEXT:
  38. return new TextAnnotationElement(parameters);
  39. case _util.AnnotationType.WIDGET:
  40. const fieldType = parameters.data.fieldType;
  41. switch (fieldType) {
  42. case "Tx":
  43. return new TextWidgetAnnotationElement(parameters);
  44. case "Btn":
  45. if (parameters.data.radioButton) {
  46. return new RadioButtonWidgetAnnotationElement(parameters);
  47. } else if (parameters.data.checkBox) {
  48. return new CheckboxWidgetAnnotationElement(parameters);
  49. }
  50. return new PushButtonWidgetAnnotationElement(parameters);
  51. case "Ch":
  52. return new ChoiceWidgetAnnotationElement(parameters);
  53. }
  54. return new WidgetAnnotationElement(parameters);
  55. case _util.AnnotationType.POPUP:
  56. return new PopupAnnotationElement(parameters);
  57. case _util.AnnotationType.FREETEXT:
  58. return new FreeTextAnnotationElement(parameters);
  59. case _util.AnnotationType.LINE:
  60. return new LineAnnotationElement(parameters);
  61. case _util.AnnotationType.SQUARE:
  62. return new SquareAnnotationElement(parameters);
  63. case _util.AnnotationType.CIRCLE:
  64. return new CircleAnnotationElement(parameters);
  65. case _util.AnnotationType.POLYLINE:
  66. return new PolylineAnnotationElement(parameters);
  67. case _util.AnnotationType.CARET:
  68. return new CaretAnnotationElement(parameters);
  69. case _util.AnnotationType.INK:
  70. return new InkAnnotationElement(parameters);
  71. case _util.AnnotationType.POLYGON:
  72. return new PolygonAnnotationElement(parameters);
  73. case _util.AnnotationType.HIGHLIGHT:
  74. return new HighlightAnnotationElement(parameters);
  75. case _util.AnnotationType.UNDERLINE:
  76. return new UnderlineAnnotationElement(parameters);
  77. case _util.AnnotationType.SQUIGGLY:
  78. return new SquigglyAnnotationElement(parameters);
  79. case _util.AnnotationType.STRIKEOUT:
  80. return new StrikeOutAnnotationElement(parameters);
  81. case _util.AnnotationType.STAMP:
  82. return new StampAnnotationElement(parameters);
  83. case _util.AnnotationType.FILEATTACHMENT:
  84. return new FileAttachmentAnnotationElement(parameters);
  85. default:
  86. return new AnnotationElement(parameters);
  87. }
  88. }
  89. }
  90. class AnnotationElement {
  91. constructor(parameters, {
  92. isRenderable = false,
  93. ignoreBorder = false,
  94. createQuadrilaterals = false
  95. } = {}) {
  96. this.isRenderable = isRenderable;
  97. this.data = parameters.data;
  98. this.layer = parameters.layer;
  99. this.page = parameters.page;
  100. this.viewport = parameters.viewport;
  101. this.linkService = parameters.linkService;
  102. this.downloadManager = parameters.downloadManager;
  103. this.imageResourcesPath = parameters.imageResourcesPath;
  104. this.renderInteractiveForms = parameters.renderInteractiveForms;
  105. this.svgFactory = parameters.svgFactory;
  106. this.annotationStorage = parameters.annotationStorage;
  107. this.enableScripting = parameters.enableScripting;
  108. this.hasJSActions = parameters.hasJSActions;
  109. this._mouseState = parameters.mouseState;
  110. if (isRenderable) {
  111. this.container = this._createContainer(ignoreBorder);
  112. }
  113. if (createQuadrilaterals) {
  114. this.quadrilaterals = this._createQuadrilaterals(ignoreBorder);
  115. }
  116. }
  117. _createContainer(ignoreBorder = false) {
  118. const data = this.data,
  119. page = this.page,
  120. viewport = this.viewport;
  121. const container = document.createElement("section");
  122. let width = data.rect[2] - data.rect[0];
  123. let height = data.rect[3] - data.rect[1];
  124. container.setAttribute("data-annotation-id", data.id);
  125. const rect = _util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]);
  126. container.style.transform = `matrix(${viewport.transform.join(",")})`;
  127. container.style.transformOrigin = `${-rect[0]}px ${-rect[1]}px`;
  128. if (!ignoreBorder && data.borderStyle.width > 0) {
  129. container.style.borderWidth = `${data.borderStyle.width}px`;
  130. if (data.borderStyle.style !== _util.AnnotationBorderStyleType.UNDERLINE) {
  131. width -= 2 * data.borderStyle.width;
  132. height -= 2 * data.borderStyle.width;
  133. }
  134. const horizontalRadius = data.borderStyle.horizontalCornerRadius;
  135. const verticalRadius = data.borderStyle.verticalCornerRadius;
  136. if (horizontalRadius > 0 || verticalRadius > 0) {
  137. const radius = `${horizontalRadius}px / ${verticalRadius}px`;
  138. container.style.borderRadius = radius;
  139. }
  140. switch (data.borderStyle.style) {
  141. case _util.AnnotationBorderStyleType.SOLID:
  142. container.style.borderStyle = "solid";
  143. break;
  144. case _util.AnnotationBorderStyleType.DASHED:
  145. container.style.borderStyle = "dashed";
  146. break;
  147. case _util.AnnotationBorderStyleType.BEVELED:
  148. (0, _util.warn)("Unimplemented border style: beveled");
  149. break;
  150. case _util.AnnotationBorderStyleType.INSET:
  151. (0, _util.warn)("Unimplemented border style: inset");
  152. break;
  153. case _util.AnnotationBorderStyleType.UNDERLINE:
  154. container.style.borderBottomStyle = "solid";
  155. break;
  156. default:
  157. break;
  158. }
  159. if (data.color) {
  160. container.style.borderColor = _util.Util.makeHexColor(data.color[0] | 0, data.color[1] | 0, data.color[2] | 0);
  161. } else {
  162. container.style.borderWidth = 0;
  163. }
  164. }
  165. container.style.left = `${rect[0]}px`;
  166. container.style.top = `${rect[1]}px`;
  167. container.style.width = `${width}px`;
  168. container.style.height = `${height}px`;
  169. return container;
  170. }
  171. _createQuadrilaterals(ignoreBorder = false) {
  172. if (!this.data.quadPoints) {
  173. return null;
  174. }
  175. const quadrilaterals = [];
  176. const savedRect = this.data.rect;
  177. for (const quadPoint of this.data.quadPoints) {
  178. this.data.rect = [quadPoint[2].x, quadPoint[2].y, quadPoint[1].x, quadPoint[1].y];
  179. quadrilaterals.push(this._createContainer(ignoreBorder));
  180. }
  181. this.data.rect = savedRect;
  182. return quadrilaterals;
  183. }
  184. _createPopup(trigger, data) {
  185. let container = this.container;
  186. if (this.quadrilaterals) {
  187. trigger = trigger || this.quadrilaterals;
  188. container = this.quadrilaterals[0];
  189. }
  190. if (!trigger) {
  191. trigger = document.createElement("div");
  192. trigger.style.height = container.style.height;
  193. trigger.style.width = container.style.width;
  194. container.appendChild(trigger);
  195. }
  196. const popupElement = new PopupElement({
  197. container,
  198. trigger,
  199. color: data.color,
  200. title: data.title,
  201. modificationDate: data.modificationDate,
  202. contents: data.contents,
  203. hideWrapper: true
  204. });
  205. const popup = popupElement.render();
  206. popup.style.left = container.style.width;
  207. container.appendChild(popup);
  208. }
  209. _renderQuadrilaterals(className) {
  210. for (const quadrilateral of this.quadrilaterals) {
  211. quadrilateral.className = className;
  212. }
  213. return this.quadrilaterals;
  214. }
  215. render() {
  216. (0, _util.unreachable)("Abstract method `AnnotationElement.render` called");
  217. }
  218. }
  219. class LinkAnnotationElement extends AnnotationElement {
  220. constructor(parameters) {
  221. const isRenderable = !!(parameters.data.url || parameters.data.dest || parameters.data.action || parameters.data.isTooltipOnly || parameters.data.actions && (parameters.data.actions.Action || parameters.data.actions["Mouse Up"] || parameters.data.actions["Mouse Down"]));
  222. super(parameters, {
  223. isRenderable,
  224. createQuadrilaterals: true
  225. });
  226. }
  227. render() {
  228. const {
  229. data,
  230. linkService
  231. } = this;
  232. const link = document.createElement("a");
  233. if (data.url) {
  234. (0, _display_utils.addLinkAttributes)(link, {
  235. url: data.url,
  236. target: data.newWindow ? _display_utils.LinkTarget.BLANK : linkService.externalLinkTarget,
  237. rel: linkService.externalLinkRel,
  238. enabled: linkService.externalLinkEnabled
  239. });
  240. } else if (data.action) {
  241. this._bindNamedAction(link, data.action);
  242. } else if (data.dest) {
  243. this._bindLink(link, data.dest);
  244. } else if (data.actions && (data.actions.Action || data.actions["Mouse Up"] || data.actions["Mouse Down"]) && this.enableScripting && this.hasJSActions) {
  245. this._bindJSAction(link, data);
  246. } else {
  247. this._bindLink(link, "");
  248. }
  249. if (this.quadrilaterals) {
  250. return this._renderQuadrilaterals("linkAnnotation").map((quadrilateral, index) => {
  251. const linkElement = index === 0 ? link : link.cloneNode();
  252. quadrilateral.appendChild(linkElement);
  253. return quadrilateral;
  254. });
  255. }
  256. this.container.className = "linkAnnotation";
  257. this.container.appendChild(link);
  258. return this.container;
  259. }
  260. _bindLink(link, destination) {
  261. link.href = this.linkService.getDestinationHash(destination);
  262. link.onclick = () => {
  263. if (destination) {
  264. this.linkService.goToDestination(destination);
  265. }
  266. return false;
  267. };
  268. if (destination || destination === "") {
  269. link.className = "internalLink";
  270. }
  271. }
  272. _bindNamedAction(link, action) {
  273. link.href = this.linkService.getAnchorUrl("");
  274. link.onclick = () => {
  275. this.linkService.executeNamedAction(action);
  276. return false;
  277. };
  278. link.className = "internalLink";
  279. }
  280. _bindJSAction(link, data) {
  281. link.href = this.linkService.getAnchorUrl("");
  282. const map = new Map([["Action", "onclick"], ["Mouse Up", "onmouseup"], ["Mouse Down", "onmousedown"]]);
  283. for (const name of Object.keys(data.actions)) {
  284. const jsName = map.get(name);
  285. if (!jsName) {
  286. continue;
  287. }
  288. link[jsName] = () => {
  289. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  290. source: this,
  291. detail: {
  292. id: data.id,
  293. name
  294. }
  295. });
  296. return false;
  297. };
  298. }
  299. link.className = "internalLink";
  300. }
  301. }
  302. class TextAnnotationElement extends AnnotationElement {
  303. constructor(parameters) {
  304. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  305. super(parameters, {
  306. isRenderable
  307. });
  308. }
  309. render() {
  310. this.container.className = "textAnnotation";
  311. const image = document.createElement("img");
  312. image.style.height = this.container.style.height;
  313. image.style.width = this.container.style.width;
  314. image.src = this.imageResourcesPath + "annotation-" + this.data.name.toLowerCase() + ".svg";
  315. image.alt = "[{{type}} Annotation]";
  316. image.dataset.l10nId = "text_annotation_type";
  317. image.dataset.l10nArgs = JSON.stringify({
  318. type: this.data.name
  319. });
  320. if (!this.data.hasPopup) {
  321. this._createPopup(image, this.data);
  322. }
  323. this.container.appendChild(image);
  324. return this.container;
  325. }
  326. }
  327. class WidgetAnnotationElement extends AnnotationElement {
  328. render() {
  329. if (this.data.alternativeText) {
  330. this.container.title = this.data.alternativeText;
  331. }
  332. return this.container;
  333. }
  334. _getKeyModifier(event) {
  335. return navigator.platform.includes("Win") && event.ctrlKey || navigator.platform.includes("Mac") && event.metaKey;
  336. }
  337. _setEventListener(element, baseName, eventName, valueGetter) {
  338. if (baseName.includes("mouse")) {
  339. element.addEventListener(baseName, event => {
  340. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  341. source: this,
  342. detail: {
  343. id: this.data.id,
  344. name: eventName,
  345. value: valueGetter(event),
  346. shift: event.shiftKey,
  347. modifier: this._getKeyModifier(event)
  348. }
  349. });
  350. });
  351. } else {
  352. element.addEventListener(baseName, event => {
  353. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  354. source: this,
  355. detail: {
  356. id: this.data.id,
  357. name: eventName,
  358. value: event.target.checked
  359. }
  360. });
  361. });
  362. }
  363. }
  364. _setEventListeners(element, names, getter) {
  365. for (const [baseName, eventName] of names) {
  366. if (eventName === "Action" || this.data.actions?.[eventName]) {
  367. this._setEventListener(element, baseName, eventName, getter);
  368. }
  369. }
  370. }
  371. _dispatchEventFromSandbox(actions, jsEvent) {
  372. const setColor = (jsName, styleName, event) => {
  373. const color = event.detail[jsName];
  374. event.target.style[styleName] = _scripting_utils.ColorConverters[`${color[0]}_HTML`](color.slice(1));
  375. };
  376. const commonActions = {
  377. display: event => {
  378. const hidden = event.detail.display % 2 === 1;
  379. event.target.style.visibility = hidden ? "hidden" : "visible";
  380. this.annotationStorage.setValue(this.data.id, {
  381. hidden,
  382. print: event.detail.display === 0 || event.detail.display === 3
  383. });
  384. },
  385. print: event => {
  386. this.annotationStorage.setValue(this.data.id, {
  387. print: event.detail.print
  388. });
  389. },
  390. hidden: event => {
  391. event.target.style.visibility = event.detail.hidden ? "hidden" : "visible";
  392. this.annotationStorage.setValue(this.data.id, {
  393. hidden: event.detail.hidden
  394. });
  395. },
  396. focus: event => {
  397. setTimeout(() => event.target.focus({
  398. preventScroll: false
  399. }), 0);
  400. },
  401. userName: event => {
  402. event.target.title = event.detail.userName;
  403. },
  404. readonly: event => {
  405. if (event.detail.readonly) {
  406. event.target.setAttribute("readonly", "");
  407. } else {
  408. event.target.removeAttribute("readonly");
  409. }
  410. },
  411. required: event => {
  412. if (event.detail.required) {
  413. event.target.setAttribute("required", "");
  414. } else {
  415. event.target.removeAttribute("required");
  416. }
  417. },
  418. bgColor: event => {
  419. setColor("bgColor", "backgroundColor", event);
  420. },
  421. fillColor: event => {
  422. setColor("fillColor", "backgroundColor", event);
  423. },
  424. fgColor: event => {
  425. setColor("fgColor", "color", event);
  426. },
  427. textColor: event => {
  428. setColor("textColor", "color", event);
  429. },
  430. borderColor: event => {
  431. setColor("borderColor", "borderColor", event);
  432. },
  433. strokeColor: event => {
  434. setColor("strokeColor", "borderColor", event);
  435. }
  436. };
  437. for (const name of Object.keys(jsEvent.detail)) {
  438. const action = actions[name] || commonActions[name];
  439. if (action) {
  440. action(jsEvent);
  441. }
  442. }
  443. }
  444. }
  445. class TextWidgetAnnotationElement extends WidgetAnnotationElement {
  446. constructor(parameters) {
  447. const isRenderable = parameters.renderInteractiveForms || !parameters.data.hasAppearance && !!parameters.data.fieldValue;
  448. super(parameters, {
  449. isRenderable
  450. });
  451. }
  452. setPropertyOnSiblings(base, key, value, keyInStorage) {
  453. const storage = this.annotationStorage;
  454. for (const element of document.getElementsByName(base.name)) {
  455. if (element !== base) {
  456. element[key] = value;
  457. const data = Object.create(null);
  458. data[keyInStorage] = value;
  459. storage.setValue(element.getAttribute("id"), data);
  460. }
  461. }
  462. }
  463. render() {
  464. const storage = this.annotationStorage;
  465. const id = this.data.id;
  466. this.container.className = "textWidgetAnnotation";
  467. let element = null;
  468. if (this.renderInteractiveForms) {
  469. const storedData = storage.getValue(id, {
  470. value: this.data.fieldValue,
  471. valueAsString: this.data.fieldValue
  472. });
  473. const textContent = storedData.valueAsString || storedData.value || "";
  474. const elementData = {
  475. userValue: null,
  476. formattedValue: null,
  477. beforeInputSelectionRange: null,
  478. beforeInputValue: null
  479. };
  480. if (this.data.multiLine) {
  481. element = document.createElement("textarea");
  482. element.textContent = textContent;
  483. } else {
  484. element = document.createElement("input");
  485. element.type = "text";
  486. element.setAttribute("value", textContent);
  487. }
  488. elementData.userValue = textContent;
  489. element.setAttribute("id", id);
  490. element.addEventListener("input", event => {
  491. storage.setValue(id, {
  492. value: event.target.value
  493. });
  494. this.setPropertyOnSiblings(element, "value", event.target.value, "value");
  495. });
  496. let blurListener = event => {
  497. if (elementData.formattedValue) {
  498. event.target.value = elementData.formattedValue;
  499. }
  500. event.target.scrollLeft = 0;
  501. elementData.beforeInputSelectionRange = null;
  502. };
  503. if (this.enableScripting && this.hasJSActions) {
  504. element.addEventListener("focus", event => {
  505. if (elementData.userValue) {
  506. event.target.value = elementData.userValue;
  507. }
  508. });
  509. element.addEventListener("updatefromsandbox", jsEvent => {
  510. const actions = {
  511. value(event) {
  512. elementData.userValue = event.detail.value || "";
  513. storage.setValue(id, {
  514. value: elementData.userValue.toString()
  515. });
  516. if (!elementData.formattedValue) {
  517. event.target.value = elementData.userValue;
  518. }
  519. },
  520. valueAsString(event) {
  521. elementData.formattedValue = event.detail.valueAsString || "";
  522. if (event.target !== document.activeElement) {
  523. event.target.value = elementData.formattedValue;
  524. }
  525. storage.setValue(id, {
  526. formattedValue: elementData.formattedValue
  527. });
  528. },
  529. selRange(event) {
  530. const [selStart, selEnd] = event.detail.selRange;
  531. if (selStart >= 0 && selEnd < event.target.value.length) {
  532. event.target.setSelectionRange(selStart, selEnd);
  533. }
  534. }
  535. };
  536. this._dispatchEventFromSandbox(actions, jsEvent);
  537. });
  538. element.addEventListener("keydown", event => {
  539. elementData.beforeInputValue = event.target.value;
  540. let commitKey = -1;
  541. if (event.key === "Escape") {
  542. commitKey = 0;
  543. } else if (event.key === "Enter") {
  544. commitKey = 2;
  545. } else if (event.key === "Tab") {
  546. commitKey = 3;
  547. }
  548. if (commitKey === -1) {
  549. return;
  550. }
  551. elementData.userValue = event.target.value;
  552. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  553. source: this,
  554. detail: {
  555. id,
  556. name: "Keystroke",
  557. value: event.target.value,
  558. willCommit: true,
  559. commitKey,
  560. selStart: event.target.selectionStart,
  561. selEnd: event.target.selectionEnd
  562. }
  563. });
  564. });
  565. const _blurListener = blurListener;
  566. blurListener = null;
  567. element.addEventListener("blur", event => {
  568. if (this._mouseState.isDown) {
  569. elementData.userValue = event.target.value;
  570. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  571. source: this,
  572. detail: {
  573. id,
  574. name: "Keystroke",
  575. value: event.target.value,
  576. willCommit: true,
  577. commitKey: 1,
  578. selStart: event.target.selectionStart,
  579. selEnd: event.target.selectionEnd
  580. }
  581. });
  582. }
  583. _blurListener(event);
  584. });
  585. element.addEventListener("mousedown", event => {
  586. elementData.beforeInputValue = event.target.value;
  587. elementData.beforeInputSelectionRange = null;
  588. });
  589. element.addEventListener("keyup", event => {
  590. if (event.target.selectionStart === event.target.selectionEnd) {
  591. elementData.beforeInputSelectionRange = null;
  592. }
  593. });
  594. element.addEventListener("select", event => {
  595. elementData.beforeInputSelectionRange = [event.target.selectionStart, event.target.selectionEnd];
  596. });
  597. if (this.data.actions?.Keystroke) {
  598. element.addEventListener("input", event => {
  599. let selStart = -1;
  600. let selEnd = -1;
  601. if (elementData.beforeInputSelectionRange) {
  602. [selStart, selEnd] = elementData.beforeInputSelectionRange;
  603. }
  604. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  605. source: this,
  606. detail: {
  607. id,
  608. name: "Keystroke",
  609. value: elementData.beforeInputValue,
  610. change: event.data,
  611. willCommit: false,
  612. selStart,
  613. selEnd
  614. }
  615. });
  616. });
  617. }
  618. this._setEventListeners(element, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.value);
  619. }
  620. if (blurListener) {
  621. element.addEventListener("blur", blurListener);
  622. }
  623. element.disabled = this.data.readOnly;
  624. element.name = this.data.fieldName;
  625. if (this.data.maxLen !== null) {
  626. element.maxLength = this.data.maxLen;
  627. }
  628. if (this.data.comb) {
  629. const fieldWidth = this.data.rect[2] - this.data.rect[0];
  630. const combWidth = fieldWidth / this.data.maxLen;
  631. element.classList.add("comb");
  632. element.style.letterSpacing = `calc(${combWidth}px - 1ch)`;
  633. }
  634. } else {
  635. element = document.createElement("div");
  636. element.textContent = this.data.fieldValue;
  637. element.style.verticalAlign = "middle";
  638. element.style.display = "table-cell";
  639. }
  640. this._setTextStyle(element);
  641. this.container.appendChild(element);
  642. return this.container;
  643. }
  644. _setTextStyle(element) {
  645. const TEXT_ALIGNMENT = ["left", "center", "right"];
  646. const {
  647. fontSize,
  648. fontColor
  649. } = this.data.defaultAppearanceData;
  650. const style = element.style;
  651. if (fontSize) {
  652. style.fontSize = `${fontSize}px`;
  653. }
  654. style.color = _util.Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]);
  655. if (this.data.textAlignment !== null) {
  656. style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];
  657. }
  658. }
  659. }
  660. class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
  661. constructor(parameters) {
  662. super(parameters, {
  663. isRenderable: parameters.renderInteractiveForms
  664. });
  665. }
  666. render() {
  667. const storage = this.annotationStorage;
  668. const data = this.data;
  669. const id = data.id;
  670. let value = storage.getValue(id, {
  671. value: data.fieldValue && (data.exportValue && data.exportValue === data.fieldValue || !data.exportValue && data.fieldValue !== "Off")
  672. }).value;
  673. if (typeof value === "string") {
  674. value = value !== "Off";
  675. storage.setValue(id, {
  676. value
  677. });
  678. }
  679. this.container.className = "buttonWidgetAnnotation checkBox";
  680. const element = document.createElement("input");
  681. element.disabled = data.readOnly;
  682. element.type = "checkbox";
  683. element.name = this.data.fieldName;
  684. if (value) {
  685. element.setAttribute("checked", true);
  686. }
  687. element.setAttribute("id", id);
  688. element.addEventListener("change", function (event) {
  689. const name = event.target.name;
  690. for (const checkbox of document.getElementsByName(name)) {
  691. if (checkbox !== event.target) {
  692. checkbox.checked = false;
  693. storage.setValue(checkbox.parentNode.getAttribute("data-annotation-id"), {
  694. value: false
  695. });
  696. }
  697. }
  698. storage.setValue(id, {
  699. value: event.target.checked
  700. });
  701. });
  702. if (this.enableScripting && this.hasJSActions) {
  703. element.addEventListener("updatefromsandbox", jsEvent => {
  704. const actions = {
  705. value(event) {
  706. event.target.checked = event.detail.value !== "Off";
  707. storage.setValue(id, {
  708. value: event.target.checked
  709. });
  710. }
  711. };
  712. this._dispatchEventFromSandbox(actions, jsEvent);
  713. });
  714. this._setEventListeners(element, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked);
  715. }
  716. this.container.appendChild(element);
  717. return this.container;
  718. }
  719. }
  720. class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
  721. constructor(parameters) {
  722. super(parameters, {
  723. isRenderable: parameters.renderInteractiveForms
  724. });
  725. }
  726. render() {
  727. this.container.className = "buttonWidgetAnnotation radioButton";
  728. const storage = this.annotationStorage;
  729. const data = this.data;
  730. const id = data.id;
  731. let value = storage.getValue(id, {
  732. value: data.fieldValue === data.buttonValue
  733. }).value;
  734. if (typeof value === "string") {
  735. value = value !== data.buttonValue;
  736. storage.setValue(id, {
  737. value
  738. });
  739. }
  740. const element = document.createElement("input");
  741. element.disabled = data.readOnly;
  742. element.type = "radio";
  743. element.name = data.fieldName;
  744. if (value) {
  745. element.setAttribute("checked", true);
  746. }
  747. element.setAttribute("id", id);
  748. element.addEventListener("change", function (event) {
  749. const {
  750. target
  751. } = event;
  752. for (const radio of document.getElementsByName(target.name)) {
  753. if (radio !== target) {
  754. storage.setValue(radio.getAttribute("id"), {
  755. value: false
  756. });
  757. }
  758. }
  759. storage.setValue(id, {
  760. value: target.checked
  761. });
  762. });
  763. if (this.enableScripting && this.hasJSActions) {
  764. const pdfButtonValue = data.buttonValue;
  765. element.addEventListener("updatefromsandbox", jsEvent => {
  766. const actions = {
  767. value(event) {
  768. const checked = pdfButtonValue === event.detail.value;
  769. for (const radio of document.getElementsByName(event.target.name)) {
  770. const radioId = radio.getAttribute("id");
  771. radio.checked = radioId === id && checked;
  772. storage.setValue(radioId, {
  773. value: radio.checked
  774. });
  775. }
  776. }
  777. };
  778. this._dispatchEventFromSandbox(actions, jsEvent);
  779. });
  780. this._setEventListeners(element, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked);
  781. }
  782. this.container.appendChild(element);
  783. return this.container;
  784. }
  785. }
  786. class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
  787. render() {
  788. const container = super.render();
  789. container.className = "buttonWidgetAnnotation pushButton";
  790. if (this.data.alternativeText) {
  791. container.title = this.data.alternativeText;
  792. }
  793. return container;
  794. }
  795. }
  796. class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
  797. constructor(parameters) {
  798. super(parameters, {
  799. isRenderable: parameters.renderInteractiveForms
  800. });
  801. }
  802. render() {
  803. this.container.className = "choiceWidgetAnnotation";
  804. const storage = this.annotationStorage;
  805. const id = this.data.id;
  806. storage.getValue(id, {
  807. value: this.data.fieldValue.length > 0 ? this.data.fieldValue[0] : undefined
  808. });
  809. const selectElement = document.createElement("select");
  810. selectElement.disabled = this.data.readOnly;
  811. selectElement.name = this.data.fieldName;
  812. selectElement.setAttribute("id", id);
  813. if (!this.data.combo) {
  814. selectElement.size = this.data.options.length;
  815. if (this.data.multiSelect) {
  816. selectElement.multiple = true;
  817. }
  818. }
  819. for (const option of this.data.options) {
  820. const optionElement = document.createElement("option");
  821. optionElement.textContent = option.displayValue;
  822. optionElement.value = option.exportValue;
  823. if (this.data.fieldValue.includes(option.exportValue)) {
  824. optionElement.setAttribute("selected", true);
  825. }
  826. selectElement.appendChild(optionElement);
  827. }
  828. const getValue = (event, isExport) => {
  829. const name = isExport ? "value" : "textContent";
  830. const options = event.target.options;
  831. if (!event.target.multiple) {
  832. return options.selectedIndex === -1 ? null : options[options.selectedIndex][name];
  833. }
  834. return Array.prototype.filter.call(options, option => option.selected).map(option => option[name]);
  835. };
  836. const getItems = event => {
  837. const options = event.target.options;
  838. return Array.prototype.map.call(options, option => {
  839. return {
  840. displayValue: option.textContent,
  841. exportValue: option.value
  842. };
  843. });
  844. };
  845. if (this.enableScripting && this.hasJSActions) {
  846. selectElement.addEventListener("updatefromsandbox", jsEvent => {
  847. const actions = {
  848. value(event) {
  849. const options = selectElement.options;
  850. const value = event.detail.value;
  851. const values = new Set(Array.isArray(value) ? value : [value]);
  852. Array.prototype.forEach.call(options, option => {
  853. option.selected = values.has(option.value);
  854. });
  855. storage.setValue(id, {
  856. value: getValue(event, true)
  857. });
  858. },
  859. multipleSelection(event) {
  860. selectElement.multiple = true;
  861. },
  862. remove(event) {
  863. const options = selectElement.options;
  864. const index = event.detail.remove;
  865. options[index].selected = false;
  866. selectElement.remove(index);
  867. if (options.length > 0) {
  868. const i = Array.prototype.findIndex.call(options, option => option.selected);
  869. if (i === -1) {
  870. options[0].selected = true;
  871. }
  872. }
  873. storage.setValue(id, {
  874. value: getValue(event, true),
  875. items: getItems(event)
  876. });
  877. },
  878. clear(event) {
  879. while (selectElement.length !== 0) {
  880. selectElement.remove(0);
  881. }
  882. storage.setValue(id, {
  883. value: null,
  884. items: []
  885. });
  886. },
  887. insert(event) {
  888. const {
  889. index,
  890. displayValue,
  891. exportValue
  892. } = event.detail.insert;
  893. const optionElement = document.createElement("option");
  894. optionElement.textContent = displayValue;
  895. optionElement.value = exportValue;
  896. selectElement.insertBefore(optionElement, selectElement.children[index]);
  897. storage.setValue(id, {
  898. value: getValue(event, true),
  899. items: getItems(event)
  900. });
  901. },
  902. items(event) {
  903. const {
  904. items
  905. } = event.detail;
  906. while (selectElement.length !== 0) {
  907. selectElement.remove(0);
  908. }
  909. for (const item of items) {
  910. const {
  911. displayValue,
  912. exportValue
  913. } = item;
  914. const optionElement = document.createElement("option");
  915. optionElement.textContent = displayValue;
  916. optionElement.value = exportValue;
  917. selectElement.appendChild(optionElement);
  918. }
  919. if (selectElement.options.length > 0) {
  920. selectElement.options[0].selected = true;
  921. }
  922. storage.setValue(id, {
  923. value: getValue(event, true),
  924. items: getItems(event)
  925. });
  926. },
  927. indices(event) {
  928. const indices = new Set(event.detail.indices);
  929. const options = event.target.options;
  930. Array.prototype.forEach.call(options, (option, i) => {
  931. option.selected = indices.has(i);
  932. });
  933. storage.setValue(id, {
  934. value: getValue(event, true)
  935. });
  936. },
  937. editable(event) {
  938. event.target.disabled = !event.detail.editable;
  939. }
  940. };
  941. this._dispatchEventFromSandbox(actions, jsEvent);
  942. });
  943. selectElement.addEventListener("input", event => {
  944. const exportValue = getValue(event, true);
  945. const value = getValue(event, false);
  946. storage.setValue(id, {
  947. value: exportValue
  948. });
  949. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  950. source: this,
  951. detail: {
  952. id,
  953. name: "Keystroke",
  954. value,
  955. changeEx: exportValue,
  956. willCommit: true,
  957. commitKey: 1,
  958. keyDown: false
  959. }
  960. });
  961. });
  962. this._setEventListeners(selectElement, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"], ["input", "Action"]], event => event.target.checked);
  963. } else {
  964. selectElement.addEventListener("input", function (event) {
  965. storage.setValue(id, {
  966. value: getValue(event)
  967. });
  968. });
  969. }
  970. this.container.appendChild(selectElement);
  971. return this.container;
  972. }
  973. }
  974. class PopupAnnotationElement extends AnnotationElement {
  975. constructor(parameters) {
  976. const isRenderable = !!(parameters.data.title || parameters.data.contents);
  977. super(parameters, {
  978. isRenderable
  979. });
  980. }
  981. render() {
  982. const IGNORE_TYPES = ["Line", "Square", "Circle", "PolyLine", "Polygon", "Ink"];
  983. this.container.className = "popupAnnotation";
  984. if (IGNORE_TYPES.includes(this.data.parentType)) {
  985. return this.container;
  986. }
  987. const selector = `[data-annotation-id="${this.data.parentId}"]`;
  988. const parentElements = this.layer.querySelectorAll(selector);
  989. if (parentElements.length === 0) {
  990. return this.container;
  991. }
  992. const popup = new PopupElement({
  993. container: this.container,
  994. trigger: Array.from(parentElements),
  995. color: this.data.color,
  996. title: this.data.title,
  997. modificationDate: this.data.modificationDate,
  998. contents: this.data.contents
  999. });
  1000. const page = this.page;
  1001. const rect = _util.Util.normalizeRect([this.data.parentRect[0], page.view[3] - this.data.parentRect[1] + page.view[1], this.data.parentRect[2], page.view[3] - this.data.parentRect[3] + page.view[1]]);
  1002. const popupLeft = rect[0] + this.data.parentRect[2] - this.data.parentRect[0];
  1003. const popupTop = rect[1];
  1004. this.container.style.transformOrigin = `${-popupLeft}px ${-popupTop}px`;
  1005. this.container.style.left = `${popupLeft}px`;
  1006. this.container.style.top = `${popupTop}px`;
  1007. this.container.appendChild(popup.render());
  1008. return this.container;
  1009. }
  1010. }
  1011. class PopupElement {
  1012. constructor(parameters) {
  1013. this.container = parameters.container;
  1014. this.trigger = parameters.trigger;
  1015. this.color = parameters.color;
  1016. this.title = parameters.title;
  1017. this.modificationDate = parameters.modificationDate;
  1018. this.contents = parameters.contents;
  1019. this.hideWrapper = parameters.hideWrapper || false;
  1020. this.pinned = false;
  1021. }
  1022. render() {
  1023. const BACKGROUND_ENLIGHT = 0.7;
  1024. const wrapper = document.createElement("div");
  1025. wrapper.className = "popupWrapper";
  1026. this.hideElement = this.hideWrapper ? wrapper : this.container;
  1027. this.hideElement.hidden = true;
  1028. const popup = document.createElement("div");
  1029. popup.className = "popup";
  1030. const color = this.color;
  1031. if (color) {
  1032. const r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0];
  1033. const g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1];
  1034. const b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2];
  1035. popup.style.backgroundColor = _util.Util.makeHexColor(r | 0, g | 0, b | 0);
  1036. }
  1037. const title = document.createElement("h1");
  1038. title.textContent = this.title;
  1039. popup.appendChild(title);
  1040. const dateObject = _display_utils.PDFDateString.toDateObject(this.modificationDate);
  1041. if (dateObject) {
  1042. const modificationDate = document.createElement("span");
  1043. modificationDate.textContent = "{{date}}, {{time}}";
  1044. modificationDate.dataset.l10nId = "annotation_date_string";
  1045. modificationDate.dataset.l10nArgs = JSON.stringify({
  1046. date: dateObject.toLocaleDateString(),
  1047. time: dateObject.toLocaleTimeString()
  1048. });
  1049. popup.appendChild(modificationDate);
  1050. }
  1051. const contents = this._formatContents(this.contents);
  1052. popup.appendChild(contents);
  1053. if (!Array.isArray(this.trigger)) {
  1054. this.trigger = [this.trigger];
  1055. }
  1056. for (const element of this.trigger) {
  1057. element.addEventListener("click", this._toggle.bind(this));
  1058. element.addEventListener("mouseover", this._show.bind(this, false));
  1059. element.addEventListener("mouseout", this._hide.bind(this, false));
  1060. }
  1061. popup.addEventListener("click", this._hide.bind(this, true));
  1062. wrapper.appendChild(popup);
  1063. return wrapper;
  1064. }
  1065. _formatContents(contents) {
  1066. const p = document.createElement("p");
  1067. const lines = contents.split(/(?:\r\n?|\n)/);
  1068. for (let i = 0, ii = lines.length; i < ii; ++i) {
  1069. const line = lines[i];
  1070. p.appendChild(document.createTextNode(line));
  1071. if (i < ii - 1) {
  1072. p.appendChild(document.createElement("br"));
  1073. }
  1074. }
  1075. return p;
  1076. }
  1077. _toggle() {
  1078. if (this.pinned) {
  1079. this._hide(true);
  1080. } else {
  1081. this._show(true);
  1082. }
  1083. }
  1084. _show(pin = false) {
  1085. if (pin) {
  1086. this.pinned = true;
  1087. }
  1088. if (this.hideElement.hidden) {
  1089. this.hideElement.hidden = false;
  1090. this.container.style.zIndex += 1;
  1091. }
  1092. }
  1093. _hide(unpin = true) {
  1094. if (unpin) {
  1095. this.pinned = false;
  1096. }
  1097. if (!this.hideElement.hidden && !this.pinned) {
  1098. this.hideElement.hidden = true;
  1099. this.container.style.zIndex -= 1;
  1100. }
  1101. }
  1102. }
  1103. class FreeTextAnnotationElement extends AnnotationElement {
  1104. constructor(parameters) {
  1105. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1106. super(parameters, {
  1107. isRenderable,
  1108. ignoreBorder: true
  1109. });
  1110. }
  1111. render() {
  1112. this.container.className = "freeTextAnnotation";
  1113. if (!this.data.hasPopup) {
  1114. this._createPopup(null, this.data);
  1115. }
  1116. return this.container;
  1117. }
  1118. }
  1119. class LineAnnotationElement extends AnnotationElement {
  1120. constructor(parameters) {
  1121. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1122. super(parameters, {
  1123. isRenderable,
  1124. ignoreBorder: true
  1125. });
  1126. }
  1127. render() {
  1128. this.container.className = "lineAnnotation";
  1129. const data = this.data;
  1130. const width = data.rect[2] - data.rect[0];
  1131. const height = data.rect[3] - data.rect[1];
  1132. const svg = this.svgFactory.create(width, height);
  1133. const line = this.svgFactory.createElement("svg:line");
  1134. line.setAttribute("x1", data.rect[2] - data.lineCoordinates[0]);
  1135. line.setAttribute("y1", data.rect[3] - data.lineCoordinates[1]);
  1136. line.setAttribute("x2", data.rect[2] - data.lineCoordinates[2]);
  1137. line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]);
  1138. line.setAttribute("stroke-width", data.borderStyle.width || 1);
  1139. line.setAttribute("stroke", "transparent");
  1140. svg.appendChild(line);
  1141. this.container.append(svg);
  1142. this._createPopup(line, data);
  1143. return this.container;
  1144. }
  1145. }
  1146. class SquareAnnotationElement extends AnnotationElement {
  1147. constructor(parameters) {
  1148. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1149. super(parameters, {
  1150. isRenderable,
  1151. ignoreBorder: true
  1152. });
  1153. }
  1154. render() {
  1155. this.container.className = "squareAnnotation";
  1156. const data = this.data;
  1157. const width = data.rect[2] - data.rect[0];
  1158. const height = data.rect[3] - data.rect[1];
  1159. const svg = this.svgFactory.create(width, height);
  1160. const borderWidth = data.borderStyle.width;
  1161. const square = this.svgFactory.createElement("svg:rect");
  1162. square.setAttribute("x", borderWidth / 2);
  1163. square.setAttribute("y", borderWidth / 2);
  1164. square.setAttribute("width", width - borderWidth);
  1165. square.setAttribute("height", height - borderWidth);
  1166. square.setAttribute("stroke-width", borderWidth || 1);
  1167. square.setAttribute("stroke", "transparent");
  1168. square.setAttribute("fill", "none");
  1169. svg.appendChild(square);
  1170. this.container.append(svg);
  1171. this._createPopup(square, data);
  1172. return this.container;
  1173. }
  1174. }
  1175. class CircleAnnotationElement extends AnnotationElement {
  1176. constructor(parameters) {
  1177. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1178. super(parameters, {
  1179. isRenderable,
  1180. ignoreBorder: true
  1181. });
  1182. }
  1183. render() {
  1184. this.container.className = "circleAnnotation";
  1185. const data = this.data;
  1186. const width = data.rect[2] - data.rect[0];
  1187. const height = data.rect[3] - data.rect[1];
  1188. const svg = this.svgFactory.create(width, height);
  1189. const borderWidth = data.borderStyle.width;
  1190. const circle = this.svgFactory.createElement("svg:ellipse");
  1191. circle.setAttribute("cx", width / 2);
  1192. circle.setAttribute("cy", height / 2);
  1193. circle.setAttribute("rx", width / 2 - borderWidth / 2);
  1194. circle.setAttribute("ry", height / 2 - borderWidth / 2);
  1195. circle.setAttribute("stroke-width", borderWidth || 1);
  1196. circle.setAttribute("stroke", "transparent");
  1197. circle.setAttribute("fill", "none");
  1198. svg.appendChild(circle);
  1199. this.container.append(svg);
  1200. this._createPopup(circle, data);
  1201. return this.container;
  1202. }
  1203. }
  1204. class PolylineAnnotationElement extends AnnotationElement {
  1205. constructor(parameters) {
  1206. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1207. super(parameters, {
  1208. isRenderable,
  1209. ignoreBorder: true
  1210. });
  1211. this.containerClassName = "polylineAnnotation";
  1212. this.svgElementName = "svg:polyline";
  1213. }
  1214. render() {
  1215. this.container.className = this.containerClassName;
  1216. const data = this.data;
  1217. const width = data.rect[2] - data.rect[0];
  1218. const height = data.rect[3] - data.rect[1];
  1219. const svg = this.svgFactory.create(width, height);
  1220. let points = [];
  1221. for (const coordinate of data.vertices) {
  1222. const x = coordinate.x - data.rect[0];
  1223. const y = data.rect[3] - coordinate.y;
  1224. points.push(x + "," + y);
  1225. }
  1226. points = points.join(" ");
  1227. const polyline = this.svgFactory.createElement(this.svgElementName);
  1228. polyline.setAttribute("points", points);
  1229. polyline.setAttribute("stroke-width", data.borderStyle.width || 1);
  1230. polyline.setAttribute("stroke", "transparent");
  1231. polyline.setAttribute("fill", "none");
  1232. svg.appendChild(polyline);
  1233. this.container.append(svg);
  1234. this._createPopup(polyline, data);
  1235. return this.container;
  1236. }
  1237. }
  1238. class PolygonAnnotationElement extends PolylineAnnotationElement {
  1239. constructor(parameters) {
  1240. super(parameters);
  1241. this.containerClassName = "polygonAnnotation";
  1242. this.svgElementName = "svg:polygon";
  1243. }
  1244. }
  1245. class CaretAnnotationElement extends AnnotationElement {
  1246. constructor(parameters) {
  1247. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1248. super(parameters, {
  1249. isRenderable,
  1250. ignoreBorder: true
  1251. });
  1252. }
  1253. render() {
  1254. this.container.className = "caretAnnotation";
  1255. if (!this.data.hasPopup) {
  1256. this._createPopup(null, this.data);
  1257. }
  1258. return this.container;
  1259. }
  1260. }
  1261. class InkAnnotationElement extends AnnotationElement {
  1262. constructor(parameters) {
  1263. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1264. super(parameters, {
  1265. isRenderable,
  1266. ignoreBorder: true
  1267. });
  1268. this.containerClassName = "inkAnnotation";
  1269. this.svgElementName = "svg:polyline";
  1270. }
  1271. render() {
  1272. this.container.className = this.containerClassName;
  1273. const data = this.data;
  1274. const width = data.rect[2] - data.rect[0];
  1275. const height = data.rect[3] - data.rect[1];
  1276. const svg = this.svgFactory.create(width, height);
  1277. for (const inkList of data.inkLists) {
  1278. let points = [];
  1279. for (const coordinate of inkList) {
  1280. const x = coordinate.x - data.rect[0];
  1281. const y = data.rect[3] - coordinate.y;
  1282. points.push(`${x},${y}`);
  1283. }
  1284. points = points.join(" ");
  1285. const polyline = this.svgFactory.createElement(this.svgElementName);
  1286. polyline.setAttribute("points", points);
  1287. polyline.setAttribute("stroke-width", data.borderStyle.width || 1);
  1288. polyline.setAttribute("stroke", "transparent");
  1289. polyline.setAttribute("fill", "none");
  1290. this._createPopup(polyline, data);
  1291. svg.appendChild(polyline);
  1292. }
  1293. this.container.append(svg);
  1294. return this.container;
  1295. }
  1296. }
  1297. class HighlightAnnotationElement extends AnnotationElement {
  1298. constructor(parameters) {
  1299. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1300. super(parameters, {
  1301. isRenderable,
  1302. ignoreBorder: true,
  1303. createQuadrilaterals: true
  1304. });
  1305. }
  1306. render() {
  1307. if (!this.data.hasPopup) {
  1308. this._createPopup(null, this.data);
  1309. }
  1310. if (this.quadrilaterals) {
  1311. return this._renderQuadrilaterals("highlightAnnotation");
  1312. }
  1313. this.container.className = "highlightAnnotation";
  1314. return this.container;
  1315. }
  1316. }
  1317. class UnderlineAnnotationElement extends AnnotationElement {
  1318. constructor(parameters) {
  1319. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1320. super(parameters, {
  1321. isRenderable,
  1322. ignoreBorder: true,
  1323. createQuadrilaterals: true
  1324. });
  1325. }
  1326. render() {
  1327. if (!this.data.hasPopup) {
  1328. this._createPopup(null, this.data);
  1329. }
  1330. if (this.quadrilaterals) {
  1331. return this._renderQuadrilaterals("underlineAnnotation");
  1332. }
  1333. this.container.className = "underlineAnnotation";
  1334. return this.container;
  1335. }
  1336. }
  1337. class SquigglyAnnotationElement extends AnnotationElement {
  1338. constructor(parameters) {
  1339. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1340. super(parameters, {
  1341. isRenderable,
  1342. ignoreBorder: true,
  1343. createQuadrilaterals: true
  1344. });
  1345. }
  1346. render() {
  1347. if (!this.data.hasPopup) {
  1348. this._createPopup(null, this.data);
  1349. }
  1350. if (this.quadrilaterals) {
  1351. return this._renderQuadrilaterals("squigglyAnnotation");
  1352. }
  1353. this.container.className = "squigglyAnnotation";
  1354. return this.container;
  1355. }
  1356. }
  1357. class StrikeOutAnnotationElement extends AnnotationElement {
  1358. constructor(parameters) {
  1359. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1360. super(parameters, {
  1361. isRenderable,
  1362. ignoreBorder: true,
  1363. createQuadrilaterals: true
  1364. });
  1365. }
  1366. render() {
  1367. if (!this.data.hasPopup) {
  1368. this._createPopup(null, this.data);
  1369. }
  1370. if (this.quadrilaterals) {
  1371. return this._renderQuadrilaterals("strikeoutAnnotation");
  1372. }
  1373. this.container.className = "strikeoutAnnotation";
  1374. return this.container;
  1375. }
  1376. }
  1377. class StampAnnotationElement extends AnnotationElement {
  1378. constructor(parameters) {
  1379. const isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
  1380. super(parameters, {
  1381. isRenderable,
  1382. ignoreBorder: true
  1383. });
  1384. }
  1385. render() {
  1386. this.container.className = "stampAnnotation";
  1387. if (!this.data.hasPopup) {
  1388. this._createPopup(null, this.data);
  1389. }
  1390. return this.container;
  1391. }
  1392. }
  1393. class FileAttachmentAnnotationElement extends AnnotationElement {
  1394. constructor(parameters) {
  1395. super(parameters, {
  1396. isRenderable: true
  1397. });
  1398. const {
  1399. filename,
  1400. content
  1401. } = this.data.file;
  1402. this.filename = (0, _display_utils.getFilenameFromUrl)(filename);
  1403. this.content = content;
  1404. this.linkService.eventBus?.dispatch("fileattachmentannotation", {
  1405. source: this,
  1406. id: (0, _util.stringToPDFString)(filename),
  1407. filename,
  1408. content
  1409. });
  1410. }
  1411. render() {
  1412. this.container.className = "fileAttachmentAnnotation";
  1413. const trigger = document.createElement("div");
  1414. trigger.style.height = this.container.style.height;
  1415. trigger.style.width = this.container.style.width;
  1416. trigger.addEventListener("dblclick", this._download.bind(this));
  1417. if (!this.data.hasPopup && (this.data.title || this.data.contents)) {
  1418. this._createPopup(trigger, this.data);
  1419. }
  1420. this.container.appendChild(trigger);
  1421. return this.container;
  1422. }
  1423. _download() {
  1424. this.downloadManager?.openOrDownloadData(this.container, this.content, this.filename);
  1425. }
  1426. }
  1427. class AnnotationLayer {
  1428. static render(parameters) {
  1429. const sortedAnnotations = [],
  1430. popupAnnotations = [];
  1431. for (const data of parameters.annotations) {
  1432. if (!data) {
  1433. continue;
  1434. }
  1435. if (data.annotationType === _util.AnnotationType.POPUP) {
  1436. popupAnnotations.push(data);
  1437. continue;
  1438. }
  1439. sortedAnnotations.push(data);
  1440. }
  1441. if (popupAnnotations.length) {
  1442. sortedAnnotations.push(...popupAnnotations);
  1443. }
  1444. for (const data of sortedAnnotations) {
  1445. const element = AnnotationElementFactory.create({
  1446. data,
  1447. layer: parameters.div,
  1448. page: parameters.page,
  1449. viewport: parameters.viewport,
  1450. linkService: parameters.linkService,
  1451. downloadManager: parameters.downloadManager,
  1452. imageResourcesPath: parameters.imageResourcesPath || "",
  1453. renderInteractiveForms: parameters.renderInteractiveForms !== false,
  1454. svgFactory: new _display_utils.DOMSVGFactory(),
  1455. annotationStorage: parameters.annotationStorage || new _annotation_storage.AnnotationStorage(),
  1456. enableScripting: parameters.enableScripting,
  1457. hasJSActions: parameters.hasJSActions,
  1458. mouseState: parameters.mouseState || {
  1459. isDown: false
  1460. }
  1461. });
  1462. if (element.isRenderable) {
  1463. const rendered = element.render();
  1464. if (data.hidden) {
  1465. rendered.style.visibility = "hidden";
  1466. }
  1467. if (Array.isArray(rendered)) {
  1468. for (const renderedElement of rendered) {
  1469. parameters.div.appendChild(renderedElement);
  1470. }
  1471. } else {
  1472. if (element instanceof PopupAnnotationElement) {
  1473. parameters.div.prepend(rendered);
  1474. } else {
  1475. parameters.div.appendChild(rendered);
  1476. }
  1477. }
  1478. }
  1479. }
  1480. }
  1481. static update(parameters) {
  1482. const transform = `matrix(${parameters.viewport.transform.join(",")})`;
  1483. for (const data of parameters.annotations) {
  1484. const elements = parameters.div.querySelectorAll(`[data-annotation-id="${data.id}"]`);
  1485. if (elements) {
  1486. for (const element of elements) {
  1487. element.style.transform = transform;
  1488. }
  1489. }
  1490. }
  1491. parameters.div.hidden = false;
  1492. }
  1493. }
  1494. exports.AnnotationLayer = AnnotationLayer;