OLD | NEW |
| (Empty) |
1 // Copyright 2016 PDFium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
6 | |
7 #include "core/fpdfapi/fpdf_parser/cpdf_hint_tables.h" | |
8 | |
9 #include <limits> | |
10 | |
11 #include "core/fpdfapi/fpdf_parser/cpdf_array.h" | |
12 #include "core/fpdfapi/fpdf_parser/cpdf_data_avail.h" | |
13 #include "core/fpdfapi/fpdf_parser/cpdf_dictionary.h" | |
14 #include "core/fpdfapi/fpdf_parser/cpdf_document.h" | |
15 #include "core/fpdfapi/fpdf_parser/cpdf_stream.h" | |
16 #include "core/fpdfapi/fpdf_parser/cpdf_stream_acc.h" | |
17 #include "core/fxcrt/fx_safe_types.h" | |
18 #include "third_party/base/numerics/safe_conversions.h" | |
19 | |
20 namespace { | |
21 | |
22 bool CanReadFromBitStream(const CFX_BitStream* hStream, | |
23 const FX_SAFE_UINT32& bits) { | |
24 return bits.IsValid() && hStream->BitsRemaining() >= bits.ValueOrDie(); | |
25 } | |
26 | |
27 // Sanity check values from the page table header. The note in the PDF 1.7 | |
28 // reference for Table F.3 says the valid range is only 0 through 32. Though 0 | |
29 // is not useful either. | |
30 bool IsValidPageOffsetHintTableBitCount(uint32_t bits) { | |
31 return bits > 0 && bits <= 32; | |
32 } | |
33 | |
34 } // namespace | |
35 | |
36 CPDF_HintTables::CPDF_HintTables(CPDF_DataAvail* pDataAvail, | |
37 CPDF_Dictionary* pLinearized) | |
38 : m_pDataAvail(pDataAvail), | |
39 m_pLinearizedDict(pLinearized), | |
40 m_nFirstPageSharedObjs(0), | |
41 m_szFirstPageObjOffset(0) { | |
42 ASSERT(m_pLinearizedDict); | |
43 } | |
44 | |
45 CPDF_HintTables::~CPDF_HintTables() {} | |
46 | |
47 uint32_t CPDF_HintTables::GetItemLength( | |
48 uint32_t index, | |
49 const std::vector<FX_FILESIZE>& szArray) { | |
50 if (szArray.size() < 2 || index > szArray.size() - 2 || | |
51 szArray[index] > szArray[index + 1]) { | |
52 return 0; | |
53 } | |
54 return szArray[index + 1] - szArray[index]; | |
55 } | |
56 | |
57 bool CPDF_HintTables::ReadPageHintTable(CFX_BitStream* hStream) { | |
58 if (!hStream || hStream->IsEOF()) | |
59 return false; | |
60 | |
61 int nStreamOffset = ReadPrimaryHintStreamOffset(); | |
62 if (nStreamOffset < 0) | |
63 return false; | |
64 | |
65 int nStreamLen = ReadPrimaryHintStreamLength(); | |
66 if (nStreamLen < 1 || | |
67 !pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(nStreamLen)) { | |
68 return false; | |
69 } | |
70 | |
71 const uint32_t kHeaderSize = 288; | |
72 if (hStream->BitsRemaining() < kHeaderSize) | |
73 return false; | |
74 | |
75 // Item 1: The least number of objects in a page. | |
76 const uint32_t dwObjLeastNum = hStream->GetBits(32); | |
77 if (!dwObjLeastNum) | |
78 return false; | |
79 | |
80 // Item 2: The location of the first page's page object. | |
81 const uint32_t dwFirstObjLoc = hStream->GetBits(32); | |
82 if (dwFirstObjLoc > static_cast<uint32_t>(nStreamOffset)) { | |
83 FX_SAFE_FILESIZE safeLoc = nStreamLen; | |
84 safeLoc += dwFirstObjLoc; | |
85 if (!safeLoc.IsValid()) | |
86 return false; | |
87 m_szFirstPageObjOffset = safeLoc.ValueOrDie(); | |
88 } else { | |
89 if (!pdfium::base::IsValueInRangeForNumericType<FX_FILESIZE>(dwFirstObjLoc)) | |
90 return false; | |
91 m_szFirstPageObjOffset = dwFirstObjLoc; | |
92 } | |
93 | |
94 // Item 3: The number of bits needed to represent the difference | |
95 // between the greatest and least number of objects in a page. | |
96 const uint32_t dwDeltaObjectsBits = hStream->GetBits(16); | |
97 if (!IsValidPageOffsetHintTableBitCount(dwDeltaObjectsBits)) | |
98 return false; | |
99 | |
100 // Item 4: The least length of a page in bytes. | |
101 const uint32_t dwPageLeastLen = hStream->GetBits(32); | |
102 if (!dwPageLeastLen) | |
103 return false; | |
104 | |
105 // Item 5: The number of bits needed to represent the difference | |
106 // between the greatest and least length of a page, in bytes. | |
107 const uint32_t dwDeltaPageLenBits = hStream->GetBits(16); | |
108 if (!IsValidPageOffsetHintTableBitCount(dwDeltaPageLenBits)) | |
109 return false; | |
110 | |
111 // Skip Item 6, 7, 8, 9 total 96 bits. | |
112 hStream->SkipBits(96); | |
113 | |
114 // Item 10: The number of bits needed to represent the greatest | |
115 // number of shared object references. | |
116 const uint32_t dwSharedObjBits = hStream->GetBits(16); | |
117 if (!IsValidPageOffsetHintTableBitCount(dwSharedObjBits)) | |
118 return false; | |
119 | |
120 // Item 11: The number of bits needed to represent the numerically | |
121 // greatest shared object identifier used by the pages. | |
122 const uint32_t dwSharedIdBits = hStream->GetBits(16); | |
123 if (!IsValidPageOffsetHintTableBitCount(dwSharedIdBits)) | |
124 return false; | |
125 | |
126 // Item 12: The number of bits needed to represent the numerator of | |
127 // the fractional position for each shared object reference. For each | |
128 // shared object referenced from a page, there is an indication of | |
129 // where in the page's content stream the object is first referenced. | |
130 const uint32_t dwSharedNumeratorBits = hStream->GetBits(16); | |
131 if (!IsValidPageOffsetHintTableBitCount(dwSharedNumeratorBits)) | |
132 return false; | |
133 | |
134 // Item 13: Skip Item 13 which has 16 bits. | |
135 hStream->SkipBits(16); | |
136 | |
137 const int nPages = GetNumberOfPages(); | |
138 if (nPages < 1 || nPages >= FPDF_PAGE_MAX_NUM) | |
139 return false; | |
140 | |
141 const uint32_t dwPages = pdfium::base::checked_cast<uint32_t>(nPages); | |
142 FX_SAFE_UINT32 required_bits = dwDeltaObjectsBits; | |
143 required_bits *= dwPages; | |
144 if (!CanReadFromBitStream(hStream, required_bits)) | |
145 return false; | |
146 | |
147 for (int i = 0; i < nPages; ++i) { | |
148 FX_SAFE_UINT32 safeDeltaObj = hStream->GetBits(dwDeltaObjectsBits); | |
149 safeDeltaObj += dwObjLeastNum; | |
150 if (!safeDeltaObj.IsValid()) | |
151 return false; | |
152 m_dwDeltaNObjsArray.push_back(safeDeltaObj.ValueOrDie()); | |
153 } | |
154 hStream->ByteAlign(); | |
155 | |
156 required_bits = dwDeltaPageLenBits; | |
157 required_bits *= dwPages; | |
158 if (!CanReadFromBitStream(hStream, required_bits)) | |
159 return false; | |
160 | |
161 std::vector<uint32_t> dwPageLenArray; | |
162 for (int i = 0; i < nPages; ++i) { | |
163 FX_SAFE_UINT32 safePageLen = hStream->GetBits(dwDeltaPageLenBits); | |
164 safePageLen += dwPageLeastLen; | |
165 if (!safePageLen.IsValid()) | |
166 return false; | |
167 | |
168 dwPageLenArray.push_back(safePageLen.ValueOrDie()); | |
169 } | |
170 | |
171 int nOffsetE = GetEndOfFirstPageOffset(); | |
172 if (nOffsetE < 0) | |
173 return false; | |
174 | |
175 int nFirstPageNum = GetFirstPageNumber(); | |
176 if (nFirstPageNum < 0 || nFirstPageNum > std::numeric_limits<int>::max() - 1) | |
177 return false; | |
178 | |
179 for (int i = 0; i < nPages; ++i) { | |
180 if (i == nFirstPageNum) { | |
181 m_szPageOffsetArray.push_back(m_szFirstPageObjOffset); | |
182 } else if (i == nFirstPageNum + 1) { | |
183 if (i == 1) { | |
184 m_szPageOffsetArray.push_back(nOffsetE); | |
185 } else { | |
186 m_szPageOffsetArray.push_back(m_szPageOffsetArray[i - 2] + | |
187 dwPageLenArray[i - 2]); | |
188 } | |
189 } else { | |
190 if (i == 0) { | |
191 m_szPageOffsetArray.push_back(nOffsetE); | |
192 } else { | |
193 m_szPageOffsetArray.push_back(m_szPageOffsetArray[i - 1] + | |
194 dwPageLenArray[i - 1]); | |
195 } | |
196 } | |
197 } | |
198 | |
199 m_szPageOffsetArray.push_back(m_szPageOffsetArray[nPages - 1] + | |
200 dwPageLenArray[nPages - 1]); | |
201 hStream->ByteAlign(); | |
202 | |
203 // Number of shared objects. | |
204 required_bits = dwSharedObjBits; | |
205 required_bits *= dwPages; | |
206 if (!CanReadFromBitStream(hStream, required_bits)) | |
207 return false; | |
208 | |
209 for (int i = 0; i < nPages; i++) | |
210 m_dwNSharedObjsArray.push_back(hStream->GetBits(dwSharedObjBits)); | |
211 hStream->ByteAlign(); | |
212 | |
213 // Array of identifiers, size = nshared_objects. | |
214 for (int i = 0; i < nPages; i++) { | |
215 required_bits = dwSharedIdBits; | |
216 required_bits *= m_dwNSharedObjsArray[i]; | |
217 if (!CanReadFromBitStream(hStream, required_bits)) | |
218 return false; | |
219 | |
220 for (uint32_t j = 0; j < m_dwNSharedObjsArray[i]; j++) | |
221 m_dwIdentifierArray.push_back(hStream->GetBits(dwSharedIdBits)); | |
222 } | |
223 hStream->ByteAlign(); | |
224 | |
225 for (int i = 0; i < nPages; i++) { | |
226 FX_SAFE_UINT32 safeSize = m_dwNSharedObjsArray[i]; | |
227 safeSize *= dwSharedNumeratorBits; | |
228 if (!CanReadFromBitStream(hStream, safeSize)) | |
229 return false; | |
230 | |
231 hStream->SkipBits(safeSize.ValueOrDie()); | |
232 } | |
233 hStream->ByteAlign(); | |
234 | |
235 FX_SAFE_UINT32 safeTotalPageLen = dwPages; | |
236 safeTotalPageLen *= dwDeltaPageLenBits; | |
237 if (!CanReadFromBitStream(hStream, safeTotalPageLen)) | |
238 return false; | |
239 | |
240 hStream->SkipBits(safeTotalPageLen.ValueOrDie()); | |
241 hStream->ByteAlign(); | |
242 return true; | |
243 } | |
244 | |
245 bool CPDF_HintTables::ReadSharedObjHintTable(CFX_BitStream* hStream, | |
246 uint32_t offset) { | |
247 if (!hStream || hStream->IsEOF()) | |
248 return false; | |
249 | |
250 int nStreamOffset = ReadPrimaryHintStreamOffset(); | |
251 int nStreamLen = ReadPrimaryHintStreamLength(); | |
252 if (nStreamOffset < 0 || nStreamLen < 1) | |
253 return false; | |
254 | |
255 FX_SAFE_UINT32 bit_offset = offset; | |
256 bit_offset *= 8; | |
257 if (!bit_offset.IsValid() || hStream->GetPos() > bit_offset.ValueOrDie()) | |
258 return false; | |
259 hStream->SkipBits(bit_offset.ValueOrDie() - hStream->GetPos()); | |
260 | |
261 const uint32_t kHeaderSize = 192; | |
262 if (hStream->BitsRemaining() < kHeaderSize) | |
263 return false; | |
264 | |
265 // Item 1: The object number of the first object in the shared objects | |
266 // section. | |
267 uint32_t dwFirstSharedObjNum = hStream->GetBits(32); | |
268 | |
269 // Item 2: The location of the first object in the shared objects section. | |
270 uint32_t dwFirstSharedObjLoc = hStream->GetBits(32); | |
271 if (dwFirstSharedObjLoc > static_cast<uint32_t>(nStreamOffset)) | |
272 dwFirstSharedObjLoc += nStreamLen; | |
273 | |
274 // Item 3: The number of shared object entries for the first page. | |
275 m_nFirstPageSharedObjs = hStream->GetBits(32); | |
276 | |
277 // Item 4: The number of shared object entries for the shared objects | |
278 // section, including the number of shared object entries for the first page. | |
279 uint32_t dwSharedObjTotal = hStream->GetBits(32); | |
280 | |
281 // Item 5: The number of bits needed to represent the greatest number of | |
282 // objects in a shared object group. Skipped. | |
283 hStream->SkipBits(16); | |
284 | |
285 // Item 6: The least length of a shared object group in bytes. | |
286 uint32_t dwGroupLeastLen = hStream->GetBits(32); | |
287 | |
288 // Item 7: The number of bits needed to represent the difference between the | |
289 // greatest and least length of a shared object group, in bytes. | |
290 uint32_t dwDeltaGroupLen = hStream->GetBits(16); | |
291 | |
292 if (dwFirstSharedObjNum >= CPDF_Parser::kMaxObjectNumber || | |
293 m_nFirstPageSharedObjs >= CPDF_Parser::kMaxObjectNumber || | |
294 dwSharedObjTotal >= CPDF_Parser::kMaxObjectNumber) { | |
295 return false; | |
296 } | |
297 | |
298 int nFirstPageObjNum = GetFirstPageObjectNumber(); | |
299 if (nFirstPageObjNum < 0) | |
300 return false; | |
301 | |
302 uint32_t dwPrevObjLen = 0; | |
303 uint32_t dwCurObjLen = 0; | |
304 FX_SAFE_UINT32 required_bits = dwSharedObjTotal; | |
305 required_bits *= dwDeltaGroupLen; | |
306 if (!CanReadFromBitStream(hStream, required_bits)) | |
307 return false; | |
308 | |
309 for (uint32_t i = 0; i < dwSharedObjTotal; ++i) { | |
310 dwPrevObjLen = dwCurObjLen; | |
311 FX_SAFE_UINT32 safeObjLen = hStream->GetBits(dwDeltaGroupLen); | |
312 safeObjLen += dwGroupLeastLen; | |
313 if (!safeObjLen.IsValid()) | |
314 return false; | |
315 | |
316 dwCurObjLen = safeObjLen.ValueOrDie(); | |
317 if (i < m_nFirstPageSharedObjs) { | |
318 m_dwSharedObjNumArray.push_back(nFirstPageObjNum + i); | |
319 if (i == 0) | |
320 m_szSharedObjOffsetArray.push_back(m_szFirstPageObjOffset); | |
321 } else { | |
322 FX_SAFE_UINT32 safeObjNum = dwFirstSharedObjNum; | |
323 safeObjNum += i - m_nFirstPageSharedObjs; | |
324 if (!safeObjNum.IsValid()) | |
325 return false; | |
326 | |
327 m_dwSharedObjNumArray.push_back(safeObjNum.ValueOrDie()); | |
328 if (i == m_nFirstPageSharedObjs) { | |
329 FX_SAFE_FILESIZE safeLoc = dwFirstSharedObjLoc; | |
330 if (!safeLoc.IsValid()) | |
331 return false; | |
332 | |
333 m_szSharedObjOffsetArray.push_back(safeLoc.ValueOrDie()); | |
334 } | |
335 } | |
336 | |
337 if (i != 0 && i != m_nFirstPageSharedObjs) { | |
338 FX_SAFE_FILESIZE safeLoc = dwPrevObjLen; | |
339 safeLoc += m_szSharedObjOffsetArray[i - 1]; | |
340 if (!safeLoc.IsValid()) | |
341 return false; | |
342 | |
343 m_szSharedObjOffsetArray.push_back(safeLoc.ValueOrDie()); | |
344 } | |
345 } | |
346 | |
347 if (dwSharedObjTotal > 0) { | |
348 FX_SAFE_FILESIZE safeLoc = dwCurObjLen; | |
349 safeLoc += m_szSharedObjOffsetArray[dwSharedObjTotal - 1]; | |
350 if (!safeLoc.IsValid()) | |
351 return false; | |
352 | |
353 m_szSharedObjOffsetArray.push_back(safeLoc.ValueOrDie()); | |
354 } | |
355 | |
356 hStream->ByteAlign(); | |
357 if (hStream->BitsRemaining() < dwSharedObjTotal) | |
358 return false; | |
359 | |
360 hStream->SkipBits(dwSharedObjTotal); | |
361 hStream->ByteAlign(); | |
362 return true; | |
363 } | |
364 | |
365 bool CPDF_HintTables::GetPagePos(uint32_t index, | |
366 FX_FILESIZE* szPageStartPos, | |
367 FX_FILESIZE* szPageLength, | |
368 uint32_t* dwObjNum) { | |
369 *szPageStartPos = m_szPageOffsetArray[index]; | |
370 *szPageLength = GetItemLength(index, m_szPageOffsetArray); | |
371 | |
372 int nFirstPageObjNum = GetFirstPageObjectNumber(); | |
373 if (nFirstPageObjNum < 0) | |
374 return false; | |
375 | |
376 int nFirstPageNum = GetFirstPageNumber(); | |
377 if (!pdfium::base::IsValueInRangeForNumericType<uint32_t>(nFirstPageNum)) | |
378 return false; | |
379 | |
380 uint32_t dwFirstPageNum = static_cast<uint32_t>(nFirstPageNum); | |
381 if (index == dwFirstPageNum) { | |
382 *dwObjNum = nFirstPageObjNum; | |
383 return true; | |
384 } | |
385 | |
386 // The object number of remaining pages starts from 1. | |
387 *dwObjNum = 1; | |
388 for (uint32_t i = 0; i < index; ++i) { | |
389 if (i == dwFirstPageNum) | |
390 continue; | |
391 *dwObjNum += m_dwDeltaNObjsArray[i]; | |
392 } | |
393 return true; | |
394 } | |
395 | |
396 CPDF_DataAvail::DocAvailStatus CPDF_HintTables::CheckPage( | |
397 uint32_t index, | |
398 CPDF_DataAvail::DownloadHints* pHints) { | |
399 if (!pHints) | |
400 return CPDF_DataAvail::DataError; | |
401 | |
402 int nFirstPageNum = GetFirstPageNumber(); | |
403 if (!pdfium::base::IsValueInRangeForNumericType<uint32_t>(nFirstPageNum)) | |
404 return CPDF_DataAvail::DataError; | |
405 | |
406 if (index == static_cast<uint32_t>(nFirstPageNum)) | |
407 return CPDF_DataAvail::DataAvailable; | |
408 | |
409 uint32_t dwLength = GetItemLength(index, m_szPageOffsetArray); | |
410 // If two pages have the same offset, it should be treated as an error. | |
411 if (!dwLength) | |
412 return CPDF_DataAvail::DataError; | |
413 | |
414 if (!m_pDataAvail->IsDataAvail(m_szPageOffsetArray[index], dwLength, pHints)) | |
415 return CPDF_DataAvail::DataNotAvailable; | |
416 | |
417 // Download data of shared objects in the page. | |
418 uint32_t offset = 0; | |
419 for (uint32_t i = 0; i < index; ++i) | |
420 offset += m_dwNSharedObjsArray[i]; | |
421 | |
422 int nFirstPageObjNum = GetFirstPageObjectNumber(); | |
423 if (nFirstPageObjNum < 0) | |
424 return CPDF_DataAvail::DataError; | |
425 | |
426 uint32_t dwIndex = 0; | |
427 uint32_t dwObjNum = 0; | |
428 for (uint32_t j = 0; j < m_dwNSharedObjsArray[index]; ++j) { | |
429 dwIndex = m_dwIdentifierArray[offset + j]; | |
430 if (dwIndex >= m_dwSharedObjNumArray.size()) | |
431 return CPDF_DataAvail::DataNotAvailable; | |
432 | |
433 dwObjNum = m_dwSharedObjNumArray[dwIndex]; | |
434 if (dwObjNum >= static_cast<uint32_t>(nFirstPageObjNum) && | |
435 dwObjNum < | |
436 static_cast<uint32_t>(nFirstPageObjNum) + m_nFirstPageSharedObjs) { | |
437 continue; | |
438 } | |
439 | |
440 dwLength = GetItemLength(dwIndex, m_szSharedObjOffsetArray); | |
441 // If two objects have the same offset, it should be treated as an error. | |
442 if (!dwLength) | |
443 return CPDF_DataAvail::DataError; | |
444 | |
445 if (!m_pDataAvail->IsDataAvail(m_szSharedObjOffsetArray[dwIndex], dwLength, | |
446 pHints)) { | |
447 return CPDF_DataAvail::DataNotAvailable; | |
448 } | |
449 } | |
450 return CPDF_DataAvail::DataAvailable; | |
451 } | |
452 | |
453 bool CPDF_HintTables::LoadHintStream(CPDF_Stream* pHintStream) { | |
454 if (!pHintStream) | |
455 return false; | |
456 | |
457 CPDF_Dictionary* pDict = pHintStream->GetDict(); | |
458 CPDF_Object* pOffset = pDict ? pDict->GetObjectFor("S") : nullptr; | |
459 if (!pOffset || !pOffset->IsNumber()) | |
460 return false; | |
461 | |
462 int shared_hint_table_offset = pOffset->GetInteger(); | |
463 if (shared_hint_table_offset <= 0) | |
464 return false; | |
465 | |
466 CPDF_StreamAcc acc; | |
467 acc.LoadAllData(pHintStream); | |
468 | |
469 uint32_t size = acc.GetSize(); | |
470 // The header section of page offset hint table is 36 bytes. | |
471 // The header section of shared object hint table is 24 bytes. | |
472 // Hint table has at least 60 bytes. | |
473 const uint32_t kMinStreamLength = 60; | |
474 if (size < kMinStreamLength) | |
475 return false; | |
476 | |
477 FX_SAFE_UINT32 safe_shared_hint_table_offset = shared_hint_table_offset; | |
478 if (!safe_shared_hint_table_offset.IsValid() || | |
479 size < safe_shared_hint_table_offset.ValueOrDie()) { | |
480 return false; | |
481 } | |
482 | |
483 CFX_BitStream bs; | |
484 bs.Init(acc.GetData(), size); | |
485 return ReadPageHintTable(&bs) && | |
486 ReadSharedObjHintTable(&bs, shared_hint_table_offset); | |
487 } | |
488 | |
489 int CPDF_HintTables::GetEndOfFirstPageOffset() const { | |
490 CPDF_Object* pOffsetE = m_pLinearizedDict->GetDirectObjectFor("E"); | |
491 return pOffsetE ? pOffsetE->GetInteger() : -1; | |
492 } | |
493 | |
494 int CPDF_HintTables::GetNumberOfPages() const { | |
495 CPDF_Object* pPageNum = m_pLinearizedDict->GetDirectObjectFor("N"); | |
496 return pPageNum ? pPageNum->GetInteger() : 0; | |
497 } | |
498 | |
499 int CPDF_HintTables::GetFirstPageObjectNumber() const { | |
500 CPDF_Object* pFirstPageObj = m_pLinearizedDict->GetDirectObjectFor("O"); | |
501 return pFirstPageObj ? pFirstPageObj->GetInteger() : -1; | |
502 } | |
503 | |
504 int CPDF_HintTables::GetFirstPageNumber() const { | |
505 CPDF_Object* pFirstPageNum = m_pLinearizedDict->GetDirectObjectFor("P"); | |
506 return pFirstPageNum ? pFirstPageNum->GetInteger() : 0; | |
507 } | |
508 | |
509 int CPDF_HintTables::ReadPrimaryHintStreamOffset() const { | |
510 return ReadPrimaryHintStream(0); | |
511 } | |
512 | |
513 int CPDF_HintTables::ReadPrimaryHintStreamLength() const { | |
514 return ReadPrimaryHintStream(1); | |
515 } | |
516 | |
517 int CPDF_HintTables::ReadPrimaryHintStream(int index) const { | |
518 CPDF_Array* pRange = m_pLinearizedDict->GetArrayFor("H"); | |
519 if (!pRange) | |
520 return -1; | |
521 | |
522 CPDF_Object* pStreamLen = pRange->GetDirectObjectAt(index); | |
523 return pStreamLen ? pStreamLen->GetInteger() : -1; | |
524 } | |
OLD | NEW |