FSStream.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. const { Stream } = require('pdfjs-dist/lib/core/stream')
  2. const { MissingDataException } = require('pdfjs-dist/lib/core/core_utils')
  3. const BUF_SIZE = 1024 // read from the file in 1024 byte pages
  4. class FSStream extends Stream {
  5. constructor(fh, start, length, dict, cachedBytes, checkDeadline) {
  6. const nonEmptyDummyBuffer = Buffer.alloc(1, 0)
  7. super(nonEmptyDummyBuffer, start, length, dict)
  8. delete this.bytes
  9. this.fh = fh
  10. this.checkDeadline = checkDeadline
  11. this.cachedBytes = cachedBytes || []
  12. }
  13. get length() {
  14. return this.end - this.start
  15. }
  16. get isEmpty() {
  17. return this.length === 0
  18. }
  19. // Manage cached reads from the file
  20. requestRange(begin, end) {
  21. this.checkDeadline(`request range ${begin} - ${end}`)
  22. // expand small ranges to read a larger amount
  23. if (end - begin < BUF_SIZE) {
  24. end = begin + BUF_SIZE
  25. }
  26. end = Math.min(end, this.length)
  27. // keep a cache of previous reads with {begin,end,buffer} values
  28. const result = {
  29. begin: begin,
  30. end: end,
  31. buffer: Buffer.alloc(end - begin, 0),
  32. }
  33. this.cachedBytes.push(result)
  34. return this.fh.read(result.buffer, 0, end - begin, begin)
  35. }
  36. _ensureGetPos(pos) {
  37. const found = this.cachedBytes.find(x => {
  38. return x.begin <= pos && pos < x.end
  39. })
  40. if (!found) {
  41. throw new MissingDataException(pos, pos + 1)
  42. }
  43. return found
  44. }
  45. _ensureGetRange(begin, end) {
  46. end = Math.min(end, this.length) // BG: handle overflow case
  47. const found = this.cachedBytes.find(x => {
  48. return x.begin <= begin && end <= x.end
  49. })
  50. if (!found) {
  51. throw new MissingDataException(begin, end)
  52. }
  53. return found
  54. }
  55. _readByte(found, pos) {
  56. return found.buffer[pos - found.begin]
  57. }
  58. _readBytes(found, pos, end) {
  59. return found.buffer.subarray(pos - found.begin, end - found.begin)
  60. }
  61. // handle accesses to the bytes
  62. ensureByte(pos) {
  63. this._ensureGetPos(pos) // may throw a MissingDataException
  64. }
  65. getByte() {
  66. const pos = this.pos
  67. if (this.pos >= this.end) {
  68. return -1
  69. }
  70. const found = this._ensureGetPos(pos)
  71. return this._readByte(found, this.pos++)
  72. }
  73. // BG: for a range, end is not included (see Buffer.subarray for example)
  74. ensureBytes(length, forceClamped = false) {
  75. const pos = this.pos
  76. this._ensureGetRange(pos, pos + length)
  77. }
  78. getBytes(length, forceClamped = false) {
  79. const pos = this.pos
  80. const strEnd = this.end
  81. const found = this._ensureGetRange(pos, pos + length)
  82. if (!length) {
  83. const subarray = this._readBytes(found, pos, strEnd)
  84. // `this.bytes` is always a `Uint8Array` here.
  85. return forceClamped ? new Uint8ClampedArray(subarray) : subarray
  86. }
  87. let end = pos + length
  88. if (end > strEnd) {
  89. end = strEnd
  90. }
  91. this.pos = end
  92. const subarray = this._readBytes(found, pos, end)
  93. // `this.bytes` is always a `Uint8Array` here.
  94. return forceClamped ? new Uint8ClampedArray(subarray) : subarray
  95. }
  96. getByteRange() {
  97. // BG: this isn't needed as far as I can tell
  98. throw new Error('not implemented')
  99. }
  100. reset() {
  101. this.pos = this.start
  102. }
  103. moveStart() {
  104. this.start = this.pos
  105. }
  106. makeSubStream(start, length, dict = null) {
  107. this.checkDeadline(`make sub stream start=${start}/length=${length}`)
  108. // BG: had to add this check for null length, it is being called with only
  109. // the start value at one point in the xref decoding. The intent is clear
  110. // enough
  111. // - a null length means "to the end of the file" -- not sure how it is
  112. // working in the existing pdfjs code without this.
  113. if (!length) {
  114. length = this.end - start
  115. }
  116. return new FSStream(
  117. this.fh,
  118. start,
  119. length,
  120. dict,
  121. this.cachedBytes,
  122. this.checkDeadline
  123. )
  124. }
  125. }
  126. module.exports = { FSStream }