* Read stream data after the dict has been parsed. * The "stream" keyword has been detected by ObjectParser's lookahead, * and the scanner is positioned right after "stream".
(dict: PdfDict)
| 146 | * and the scanner is positioned right after "stream". |
| 147 | */ |
| 148 | private readStream(dict: PdfDict): PdfStream { |
| 149 | // The scanner is positioned right after "stream" keyword |
| 150 | // (ObjectParser consumed it during lookahead but didn't use it) |
| 151 | |
| 152 | // Skip EOL after "stream" (required: LF or CRLF) |
| 153 | this.skipStreamEOL(); |
| 154 | |
| 155 | const startPos = this.scanner.position; |
| 156 | |
| 157 | // Try to resolve /Length from the dict. If that fails (e.g. indirect |
| 158 | // ref during brute-force recovery with no resolver), fall back to |
| 159 | // scanning for the "endstream" keyword to determine the length. |
| 160 | let length = -1; |
| 161 | |
| 162 | try { |
| 163 | length = this.resolveLength(dict); |
| 164 | } catch { |
| 165 | length = -1; |
| 166 | } |
| 167 | |
| 168 | // Validate /Length: the "endstream" keyword must follow the data. |
| 169 | // Malformed PDFs often carry a wrong /Length — recover by scanning |
| 170 | // for endstream instead of trusting the declared value. |
| 171 | if (length >= 0 && !this.isEndstreamAt(startPos + length)) { |
| 172 | this.warn( |
| 173 | `Stream /Length ${length} does not point at endstream, scanning for actual end`, |
| 174 | startPos, |
| 175 | ); |
| 176 | |
| 177 | length = -1; |
| 178 | } |
| 179 | |
| 180 | length = length < 0 ? this.findEndStream(startPos) : length; |
| 181 | |
| 182 | if (length < 0) { |
| 183 | throw new ObjectParseError("Stream has no valid /Length and no endstream found"); |
| 184 | } |
| 185 | |
| 186 | // Read exactly `length` bytes. |
| 187 | // Use subarray (zero-copy view) since the underlying PDF bytes |
| 188 | // are kept alive by the PDF object for the document's lifetime. |
| 189 | const data = this.scanner.bytes.subarray(startPos, startPos + length); |
| 190 | |
| 191 | this.scanner.moveTo(startPos + length); |
| 192 | |
| 193 | // Skip optional EOL before "endstream" |
| 194 | this.skipOptionalEOL(); |
| 195 | |
| 196 | // Consume "endstream" keyword directly from scanner |
| 197 | this.expectKeyword("endstream"); |
| 198 | |
| 199 | return new PdfStream(dict, data); |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Check whether the "endstream" keyword appears at the given position, |
no test coverage detected