2
0

writer.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2020 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.writeDict = writeDict;
  27. exports.incrementalUpdate = incrementalUpdate;
  28. var _util = require("../shared/util.js");
  29. var _primitives = require("./primitives.js");
  30. var _crypto = require("./crypto.js");
  31. function writeDict(dict, buffer, transform) {
  32. buffer.push("<<");
  33. for (const key of dict.getKeys()) {
  34. buffer.push(` /${key} `);
  35. writeValue(dict.getRaw(key), buffer, transform);
  36. }
  37. buffer.push(">>");
  38. }
  39. function writeStream(stream, buffer, transform) {
  40. writeDict(stream.dict, buffer, transform);
  41. buffer.push(" stream\n");
  42. let string = (0, _util.bytesToString)(stream.getBytes());
  43. if (transform !== null) {
  44. string = transform.encryptString(string);
  45. }
  46. buffer.push(string);
  47. buffer.push("\nendstream\n");
  48. }
  49. function writeArray(array, buffer, transform) {
  50. buffer.push("[");
  51. let first = true;
  52. for (const val of array) {
  53. if (!first) {
  54. buffer.push(" ");
  55. } else {
  56. first = false;
  57. }
  58. writeValue(val, buffer, transform);
  59. }
  60. buffer.push("]");
  61. }
  62. function numberToString(value) {
  63. if (Number.isInteger(value)) {
  64. return value.toString();
  65. }
  66. const roundedValue = Math.round(value * 100);
  67. if (roundedValue % 100 === 0) {
  68. return (roundedValue / 100).toString();
  69. }
  70. if (roundedValue % 10 === 0) {
  71. return value.toFixed(1);
  72. }
  73. return value.toFixed(2);
  74. }
  75. function writeValue(value, buffer, transform) {
  76. if ((0, _primitives.isName)(value)) {
  77. buffer.push(`/${value.name}`);
  78. } else if ((0, _primitives.isRef)(value)) {
  79. buffer.push(`${value.num} ${value.gen} R`);
  80. } else if (Array.isArray(value)) {
  81. writeArray(value, buffer, transform);
  82. } else if (typeof value === "string") {
  83. if (transform !== null) {
  84. value = transform.encryptString(value);
  85. }
  86. buffer.push(`(${(0, _util.escapeString)(value)})`);
  87. } else if (typeof value === "number") {
  88. buffer.push(numberToString(value));
  89. } else if ((0, _primitives.isDict)(value)) {
  90. writeDict(value, buffer, transform);
  91. } else if ((0, _primitives.isStream)(value)) {
  92. writeStream(value, buffer, transform);
  93. }
  94. }
  95. function writeInt(number, size, offset, buffer) {
  96. for (let i = size + offset - 1; i > offset - 1; i--) {
  97. buffer[i] = number & 0xff;
  98. number >>= 8;
  99. }
  100. return offset + size;
  101. }
  102. function writeString(string, offset, buffer) {
  103. for (let i = 0, len = string.length; i < len; i++) {
  104. buffer[offset + i] = string.charCodeAt(i) & 0xff;
  105. }
  106. }
  107. function computeMD5(filesize, xrefInfo) {
  108. const time = Math.floor(Date.now() / 1000);
  109. const filename = xrefInfo.filename || "";
  110. const md5Buffer = [time.toString(), filename, filesize.toString()];
  111. let md5BufferLen = md5Buffer.reduce((a, str) => a + str.length, 0);
  112. for (const value of Object.values(xrefInfo.info)) {
  113. md5Buffer.push(value);
  114. md5BufferLen += value.length;
  115. }
  116. const array = new Uint8Array(md5BufferLen);
  117. let offset = 0;
  118. for (const str of md5Buffer) {
  119. writeString(str, offset, array);
  120. offset += str.length;
  121. }
  122. return (0, _util.bytesToString)((0, _crypto.calculateMD5)(array));
  123. }
  124. function incrementalUpdate(originalData, xrefInfo, newRefs) {
  125. const newXref = new _primitives.Dict(null);
  126. const refForXrefTable = xrefInfo.newRef;
  127. let buffer, baseOffset;
  128. const lastByte = originalData[originalData.length - 1];
  129. if (lastByte === 0x0a || lastByte === 0x0d) {
  130. buffer = [];
  131. baseOffset = originalData.length;
  132. } else {
  133. buffer = ["\n"];
  134. baseOffset = originalData.length + 1;
  135. }
  136. newXref.set("Size", refForXrefTable.num + 1);
  137. newXref.set("Prev", xrefInfo.startXRef);
  138. newXref.set("Type", _primitives.Name.get("XRef"));
  139. if (xrefInfo.rootRef !== null) {
  140. newXref.set("Root", xrefInfo.rootRef);
  141. }
  142. if (xrefInfo.infoRef !== null) {
  143. newXref.set("Info", xrefInfo.infoRef);
  144. }
  145. if (xrefInfo.encrypt !== null) {
  146. newXref.set("Encrypt", xrefInfo.encrypt);
  147. }
  148. newRefs.push({
  149. ref: refForXrefTable,
  150. data: ""
  151. });
  152. newRefs = newRefs.sort((a, b) => {
  153. return a.ref.num - b.ref.num;
  154. });
  155. const xrefTableData = [[0, 1, 0xffff]];
  156. const indexes = [0, 1];
  157. let maxOffset = 0;
  158. for (const {
  159. ref,
  160. data
  161. } of newRefs) {
  162. maxOffset = Math.max(maxOffset, baseOffset);
  163. xrefTableData.push([1, baseOffset, Math.min(ref.gen, 0xffff)]);
  164. baseOffset += data.length;
  165. indexes.push(ref.num);
  166. indexes.push(1);
  167. buffer.push(data);
  168. }
  169. newXref.set("Index", indexes);
  170. if (xrefInfo.fileIds.length !== 0) {
  171. const md5 = computeMD5(baseOffset, xrefInfo);
  172. newXref.set("ID", [xrefInfo.fileIds[0], md5]);
  173. }
  174. const offsetSize = Math.ceil(Math.log2(maxOffset) / 8);
  175. const sizes = [1, offsetSize, 2];
  176. const structSize = sizes[0] + sizes[1] + sizes[2];
  177. const tableLength = structSize * xrefTableData.length;
  178. newXref.set("W", sizes);
  179. newXref.set("Length", tableLength);
  180. buffer.push(`${refForXrefTable.num} ${refForXrefTable.gen} obj\n`);
  181. writeDict(newXref, buffer, null);
  182. buffer.push(" stream\n");
  183. const bufferLen = buffer.reduce((a, str) => a + str.length, 0);
  184. const footer = `\nendstream\nendobj\nstartxref\n${baseOffset}\n%%EOF\n`;
  185. const array = new Uint8Array(originalData.length + bufferLen + tableLength + footer.length);
  186. array.set(originalData);
  187. let offset = originalData.length;
  188. for (const str of buffer) {
  189. writeString(str, offset, array);
  190. offset += str.length;
  191. }
  192. for (const [type, objOffset, gen] of xrefTableData) {
  193. offset = writeInt(type, sizes[0], offset, array);
  194. offset = writeInt(objOffset, sizes[1], offset, array);
  195. offset = writeInt(gen, sizes[2], offset, array);
  196. }
  197. writeString(footer, offset, array);
  198. return array;
  199. }