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