document_spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /* Copyright 2017 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. import { createIdFactory, XRefMock } from "./test_utils.js";
  16. import { Dict, Name, Ref } from "../../src/core/primitives.js";
  17. import { PDFDocument } from "../../src/core/document.js";
  18. import { StringStream } from "../../src/core/stream.js";
  19. describe("document", function () {
  20. describe("Page", function () {
  21. it("should create correct objId/fontId using the idFactory", function () {
  22. const idFactory1 = createIdFactory(/* pageIndex = */ 0);
  23. const idFactory2 = createIdFactory(/* pageIndex = */ 1);
  24. expect(idFactory1.createObjId()).toEqual("p0_1");
  25. expect(idFactory1.createObjId()).toEqual("p0_2");
  26. expect(idFactory1.createFontId()).toEqual("f1");
  27. expect(idFactory1.createFontId()).toEqual("f2");
  28. expect(idFactory1.getDocId()).toEqual("g_d0");
  29. expect(idFactory2.createObjId()).toEqual("p1_1");
  30. expect(idFactory2.createObjId()).toEqual("p1_2");
  31. expect(idFactory2.createFontId()).toEqual("f1");
  32. expect(idFactory2.createFontId()).toEqual("f2");
  33. expect(idFactory2.getDocId()).toEqual("g_d0");
  34. expect(idFactory1.createObjId()).toEqual("p0_3");
  35. expect(idFactory1.createObjId()).toEqual("p0_4");
  36. expect(idFactory1.createFontId()).toEqual("f3");
  37. expect(idFactory1.createFontId()).toEqual("f4");
  38. expect(idFactory1.getDocId()).toEqual("g_d0");
  39. });
  40. });
  41. describe("PDFDocument", function () {
  42. const stream = new StringStream("Dummy_PDF_data");
  43. function getDocument(acroForm, xref = new XRefMock()) {
  44. const catalog = { acroForm };
  45. const pdfManager = {
  46. get docId() {
  47. return "d0";
  48. },
  49. ensureDoc(prop, args) {
  50. return pdfManager.ensure(pdfDocument, prop, args);
  51. },
  52. ensureCatalog(prop, args) {
  53. return pdfManager.ensure(catalog, prop, args);
  54. },
  55. async ensure(obj, prop, args) {
  56. const value = obj[prop];
  57. if (typeof value === "function") {
  58. return value.apply(obj, args);
  59. }
  60. return value;
  61. },
  62. get evaluatorOptions() {
  63. return { isOffscreenCanvasSupported: false };
  64. },
  65. };
  66. const pdfDocument = new PDFDocument(pdfManager, stream);
  67. pdfDocument.xref = xref;
  68. pdfDocument.catalog = catalog;
  69. return pdfDocument;
  70. }
  71. it("should get form info when no form data is present", function () {
  72. const pdfDocument = getDocument(null);
  73. expect(pdfDocument.formInfo).toEqual({
  74. hasAcroForm: false,
  75. hasSignatures: false,
  76. hasXfa: false,
  77. hasFields: false,
  78. });
  79. });
  80. it("should get form info when XFA is present", function () {
  81. const acroForm = new Dict();
  82. // The `XFA` entry can only be a non-empty array or stream.
  83. acroForm.set("XFA", []);
  84. let pdfDocument = getDocument(acroForm);
  85. expect(pdfDocument.formInfo).toEqual({
  86. hasAcroForm: false,
  87. hasSignatures: false,
  88. hasXfa: false,
  89. hasFields: false,
  90. });
  91. acroForm.set("XFA", ["foo", "bar"]);
  92. pdfDocument = getDocument(acroForm);
  93. expect(pdfDocument.formInfo).toEqual({
  94. hasAcroForm: false,
  95. hasSignatures: false,
  96. hasXfa: true,
  97. hasFields: false,
  98. });
  99. acroForm.set("XFA", new StringStream(""));
  100. pdfDocument = getDocument(acroForm);
  101. expect(pdfDocument.formInfo).toEqual({
  102. hasAcroForm: false,
  103. hasSignatures: false,
  104. hasXfa: false,
  105. hasFields: false,
  106. });
  107. acroForm.set("XFA", new StringStream("non-empty"));
  108. pdfDocument = getDocument(acroForm);
  109. expect(pdfDocument.formInfo).toEqual({
  110. hasAcroForm: false,
  111. hasSignatures: false,
  112. hasXfa: true,
  113. hasFields: false,
  114. });
  115. });
  116. it("should get form info when AcroForm is present", function () {
  117. const acroForm = new Dict();
  118. // The `Fields` entry can only be a non-empty array.
  119. acroForm.set("Fields", []);
  120. let pdfDocument = getDocument(acroForm);
  121. expect(pdfDocument.formInfo).toEqual({
  122. hasAcroForm: false,
  123. hasSignatures: false,
  124. hasXfa: false,
  125. hasFields: false,
  126. });
  127. acroForm.set("Fields", ["foo", "bar"]);
  128. pdfDocument = getDocument(acroForm);
  129. expect(pdfDocument.formInfo).toEqual({
  130. hasAcroForm: true,
  131. hasSignatures: false,
  132. hasXfa: false,
  133. hasFields: true,
  134. });
  135. // If the first bit of the `SigFlags` entry is set and the `Fields` array
  136. // only contains document signatures, then there is no AcroForm data.
  137. acroForm.set("Fields", ["foo", "bar"]);
  138. acroForm.set("SigFlags", 2);
  139. pdfDocument = getDocument(acroForm);
  140. expect(pdfDocument.formInfo).toEqual({
  141. hasAcroForm: true,
  142. hasSignatures: false,
  143. hasXfa: false,
  144. hasFields: true,
  145. });
  146. const annotationDict = new Dict();
  147. annotationDict.set("FT", Name.get("Sig"));
  148. annotationDict.set("Rect", [0, 0, 0, 0]);
  149. const annotationRef = Ref.get(11, 0);
  150. const kidsDict = new Dict();
  151. kidsDict.set("Kids", [annotationRef]);
  152. const kidsRef = Ref.get(10, 0);
  153. const xref = new XRefMock([
  154. { ref: annotationRef, data: annotationDict },
  155. { ref: kidsRef, data: kidsDict },
  156. ]);
  157. acroForm.set("Fields", [kidsRef]);
  158. acroForm.set("SigFlags", 3);
  159. pdfDocument = getDocument(acroForm, xref);
  160. expect(pdfDocument.formInfo).toEqual({
  161. hasAcroForm: false,
  162. hasSignatures: true,
  163. hasXfa: false,
  164. hasFields: true,
  165. });
  166. });
  167. it("should get calculation order array or null", function () {
  168. const acroForm = new Dict();
  169. let pdfDocument = getDocument(acroForm);
  170. expect(pdfDocument.calculationOrderIds).toEqual(null);
  171. acroForm.set("CO", [Ref.get(1, 0), Ref.get(2, 0), Ref.get(3, 0)]);
  172. pdfDocument = getDocument(acroForm);
  173. expect(pdfDocument.calculationOrderIds).toEqual(["1R", "2R", "3R"]);
  174. acroForm.set("CO", []);
  175. pdfDocument = getDocument(acroForm);
  176. expect(pdfDocument.calculationOrderIds).toEqual(null);
  177. acroForm.set("CO", ["1", "2"]);
  178. pdfDocument = getDocument(acroForm);
  179. expect(pdfDocument.calculationOrderIds).toEqual(null);
  180. acroForm.set("CO", ["1", Ref.get(1, 0), "2"]);
  181. pdfDocument = getDocument(acroForm);
  182. expect(pdfDocument.calculationOrderIds).toEqual(["1R"]);
  183. });
  184. it("should get field objects array or null", async function () {
  185. const acroForm = new Dict();
  186. let pdfDocument = getDocument(acroForm);
  187. let fields = await pdfDocument.fieldObjects;
  188. expect(fields).toEqual(null);
  189. acroForm.set("Fields", []);
  190. pdfDocument = getDocument(acroForm);
  191. fields = await pdfDocument.fieldObjects;
  192. expect(fields).toEqual(null);
  193. const kid1Ref = Ref.get(314, 0);
  194. const kid11Ref = Ref.get(159, 0);
  195. const kid2Ref = Ref.get(265, 0);
  196. const kid2BisRef = Ref.get(266, 0);
  197. const parentRef = Ref.get(358, 0);
  198. const allFields = Object.create(null);
  199. for (const name of ["parent", "kid1", "kid2", "kid11"]) {
  200. const buttonWidgetDict = new Dict();
  201. buttonWidgetDict.set("Type", Name.get("Annot"));
  202. buttonWidgetDict.set("Subtype", Name.get("Widget"));
  203. buttonWidgetDict.set("FT", Name.get("Btn"));
  204. buttonWidgetDict.set("T", name);
  205. allFields[name] = buttonWidgetDict;
  206. }
  207. allFields.kid1.set("Kids", [kid11Ref]);
  208. allFields.parent.set("Kids", [kid1Ref, kid2Ref, kid2BisRef]);
  209. const xref = new XRefMock([
  210. { ref: parentRef, data: allFields.parent },
  211. { ref: kid1Ref, data: allFields.kid1 },
  212. { ref: kid11Ref, data: allFields.kid11 },
  213. { ref: kid2Ref, data: allFields.kid2 },
  214. { ref: kid2BisRef, data: allFields.kid2 },
  215. ]);
  216. acroForm.set("Fields", [parentRef]);
  217. pdfDocument = getDocument(acroForm, xref);
  218. fields = await pdfDocument.fieldObjects;
  219. for (const [name, objs] of Object.entries(fields)) {
  220. fields[name] = objs.map(obj => obj.id);
  221. }
  222. expect(fields["parent.kid1"]).toEqual(["314R"]);
  223. expect(fields["parent.kid1.kid11"]).toEqual(["159R"]);
  224. expect(fields["parent.kid2"]).toEqual(["265R", "266R"]);
  225. expect(fields.parent).toEqual(["358R"]);
  226. });
  227. it("should check if fields have any actions", async function () {
  228. const acroForm = new Dict();
  229. let pdfDocument = getDocument(acroForm);
  230. let hasJSActions = await pdfDocument.hasJSActions;
  231. expect(hasJSActions).toEqual(false);
  232. acroForm.set("Fields", []);
  233. pdfDocument = getDocument(acroForm);
  234. hasJSActions = await pdfDocument.hasJSActions;
  235. expect(hasJSActions).toEqual(false);
  236. const kid1Ref = Ref.get(314, 0);
  237. const kid11Ref = Ref.get(159, 0);
  238. const kid2Ref = Ref.get(265, 0);
  239. const parentRef = Ref.get(358, 0);
  240. const allFields = Object.create(null);
  241. for (const name of ["parent", "kid1", "kid2", "kid11"]) {
  242. const buttonWidgetDict = new Dict();
  243. buttonWidgetDict.set("Type", Name.get("Annot"));
  244. buttonWidgetDict.set("Subtype", Name.get("Widget"));
  245. buttonWidgetDict.set("FT", Name.get("Btn"));
  246. buttonWidgetDict.set("T", name);
  247. allFields[name] = buttonWidgetDict;
  248. }
  249. allFields.kid1.set("Kids", [kid11Ref]);
  250. allFields.parent.set("Kids", [kid1Ref, kid2Ref]);
  251. const xref = new XRefMock([
  252. { ref: parentRef, data: allFields.parent },
  253. { ref: kid1Ref, data: allFields.kid1 },
  254. { ref: kid11Ref, data: allFields.kid11 },
  255. { ref: kid2Ref, data: allFields.kid2 },
  256. ]);
  257. acroForm.set("Fields", [parentRef]);
  258. pdfDocument = getDocument(acroForm, xref);
  259. hasJSActions = await pdfDocument.hasJSActions;
  260. expect(hasJSActions).toEqual(false);
  261. const JS = Name.get("JavaScript");
  262. const additionalActionsDict = new Dict();
  263. const eDict = new Dict();
  264. eDict.set("JS", "hello()");
  265. eDict.set("S", JS);
  266. additionalActionsDict.set("E", eDict);
  267. allFields.kid2.set("AA", additionalActionsDict);
  268. pdfDocument = getDocument(acroForm, xref);
  269. hasJSActions = await pdfDocument.hasJSActions;
  270. expect(hasJSActions).toEqual(true);
  271. });
  272. });
  273. });