/src/gdal/frmts/pdf/pdfreadvectors.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: PDF driver |
4 | | * Purpose: GDALDataset driver for PDF dataset (read vector features) |
5 | | * Author: Even Rouault, <even dot rouault at spatialys.com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "gdal_pdf.h" |
14 | | |
15 | | #include <algorithm> |
16 | | #include <array> |
17 | | |
18 | 108k | #define SQUARE(x) ((x) * (x)) |
19 | 50.6k | #define EPSILON 1e-5 |
20 | | |
21 | | // #define DEBUG_VERBOSE |
22 | | |
23 | | #ifdef HAVE_PDF_READ_SUPPORT |
24 | | |
25 | | constexpr int BEZIER_STEPS = 10; |
26 | | |
27 | | /************************************************************************/ |
28 | | /* OpenVectorLayers() */ |
29 | | /************************************************************************/ |
30 | | |
31 | | bool PDFDataset::OpenVectorLayers(GDALPDFDictionary *poPageDict) |
32 | 270M | { |
33 | 270M | if (m_bHasLoadedLayers) |
34 | 270M | return true; |
35 | 3.82k | m_bHasLoadedLayers = true; |
36 | | |
37 | 3.82k | if (poPageDict == nullptr) |
38 | 974 | { |
39 | 974 | poPageDict = m_poPageObj->GetDictionary(); |
40 | 974 | if (poPageDict == nullptr) |
41 | 0 | return false; |
42 | 974 | } |
43 | | |
44 | 3.82k | GetCatalog(); |
45 | 3.82k | if (m_poCatalogObject == nullptr || |
46 | 3.82k | m_poCatalogObject->GetType() != PDFObjectType_Dictionary) |
47 | 0 | return false; |
48 | | |
49 | 3.82k | GDALPDFObject *poContents = poPageDict->Get("Contents"); |
50 | 3.82k | if (poContents == nullptr) |
51 | 1.01k | return false; |
52 | | |
53 | 2.81k | if (poContents->GetType() != PDFObjectType_Dictionary && |
54 | 2.81k | poContents->GetType() != PDFObjectType_Array) |
55 | 50 | return false; |
56 | | |
57 | 2.76k | GDALPDFObject *poResources = poPageDict->Get("Resources"); |
58 | 2.76k | if (poResources == nullptr || |
59 | 2.76k | poResources->GetType() != PDFObjectType_Dictionary) |
60 | 508 | return false; |
61 | | |
62 | 2.25k | GDALPDFObject *poStructTreeRoot = |
63 | 2.25k | m_poCatalogObject->GetDictionary()->Get("StructTreeRoot"); |
64 | 2.25k | if (CPLTestBool(CPLGetConfigOption("OGR_PDF_READ_NON_STRUCTURED", "NO")) || |
65 | 2.25k | poStructTreeRoot == nullptr || |
66 | 2.25k | poStructTreeRoot->GetType() != PDFObjectType_Dictionary) |
67 | 1.74k | { |
68 | 1.74k | ExploreContentsNonStructured(poContents, poResources); |
69 | 1.74k | } |
70 | 516 | else |
71 | 516 | { |
72 | 516 | bool bHasFeatures; |
73 | 516 | { |
74 | 516 | std::set<std::pair<int, int>> aoSetAlreadyVisited; |
75 | 516 | bHasFeatures = ExploreTree(poStructTreeRoot, aoSetAlreadyVisited, 0, |
76 | 516 | /* bDryRun = */ true); |
77 | 516 | } |
78 | 516 | if (bHasFeatures) |
79 | 419 | { |
80 | 419 | int nDepth = 0; |
81 | 419 | int nVisited = 0; |
82 | 419 | bool bStop = false; |
83 | 419 | ExploreContents(poContents, poResources, nDepth, nVisited, bStop); |
84 | 419 | std::set<std::pair<int, int>> aoSetAlreadyVisited; |
85 | 419 | ExploreTree(poStructTreeRoot, aoSetAlreadyVisited, 0, |
86 | 419 | /* bDryRun = */ false); |
87 | 419 | } |
88 | 97 | else |
89 | 97 | { |
90 | 97 | ExploreContentsNonStructured(poContents, poResources); |
91 | 97 | } |
92 | 516 | } |
93 | | |
94 | 2.25k | CleanupIntermediateResources(); |
95 | | |
96 | 2.25k | bool bEmptyDS = true; |
97 | 2.25k | for (auto &poLayer : m_apoLayers) |
98 | 578 | { |
99 | 578 | if (poLayer->GetFeatureCount(false) != 0) |
100 | 578 | { |
101 | 578 | bEmptyDS = false; |
102 | 578 | break; |
103 | 578 | } |
104 | 578 | } |
105 | 2.25k | return !bEmptyDS; |
106 | 2.76k | } |
107 | | |
108 | | /************************************************************************/ |
109 | | /* CleanupIntermediateResources() */ |
110 | | /************************************************************************/ |
111 | | |
112 | | void PDFDataset::CleanupIntermediateResources() |
113 | 21.5k | { |
114 | 21.5k | for (const auto &oIter : m_oMapMCID) |
115 | 459 | delete oIter.second; |
116 | 21.5k | m_oMapMCID.clear(); |
117 | 21.5k | } |
118 | | |
119 | | /************************************************************************/ |
120 | | /* InitMapOperators() */ |
121 | | /************************************************************************/ |
122 | | |
123 | | typedef struct |
124 | | { |
125 | | char szOpName[4]; |
126 | | int nArgs; |
127 | | } PDFOperator; |
128 | | |
129 | | static const PDFOperator asPDFOperators[] = { |
130 | | {"b", 0}, |
131 | | {"B", 0}, |
132 | | {"b*", 0}, |
133 | | {"B*", 0}, |
134 | | {"BDC", 2}, |
135 | | // BI |
136 | | {"BMC", 1}, |
137 | | // BT |
138 | | {"BX", 0}, |
139 | | {"c", 6}, |
140 | | {"cm", 6}, |
141 | | {"CS", 1}, |
142 | | {"cs", 1}, |
143 | | {"d", 1}, /* we have ignored the first arg which is an array */ |
144 | | // d0 |
145 | | // d1 |
146 | | {"Do", 1}, |
147 | | {"DP", 2}, |
148 | | // EI |
149 | | {"EMC", 0}, |
150 | | // ET |
151 | | {"EX", 0}, |
152 | | {"f", 0}, |
153 | | {"F", 0}, |
154 | | {"f*", 0}, |
155 | | {"G", 1}, |
156 | | {"g", 1}, |
157 | | {"gs", 1}, |
158 | | {"h", 0}, |
159 | | {"i", 1}, |
160 | | // ID |
161 | | {"j", 1}, |
162 | | {"J", 1}, |
163 | | {"K", 4}, |
164 | | {"k", 4}, |
165 | | {"l", 2}, |
166 | | {"m", 2}, |
167 | | {"M", 1}, |
168 | | {"MP", 1}, |
169 | | {"n", 0}, |
170 | | {"q", 0}, |
171 | | {"Q", 0}, |
172 | | {"re", 4}, |
173 | | {"RG", 3}, |
174 | | {"rg", 3}, |
175 | | {"ri", 1}, |
176 | | {"s", 0}, |
177 | | {"S", 0}, |
178 | | {"SC", -1}, |
179 | | {"sc", -1}, |
180 | | {"SCN", -1}, |
181 | | {"scn", -1}, |
182 | | {"sh", 1}, |
183 | | // T* |
184 | | {"Tc", 1}, |
185 | | {"Td", 2}, |
186 | | {"TD", 2}, |
187 | | {"Tf", 1}, |
188 | | {"Tj", 1}, |
189 | | {"TJ", 1}, |
190 | | {"TL", 1}, |
191 | | {"Tm", 6}, |
192 | | {"Tr", 1}, |
193 | | {"Ts", 1}, |
194 | | {"Tw", 1}, |
195 | | {"Tz", 1}, |
196 | | {"v", 4}, |
197 | | {"w", 1}, |
198 | | {"W", 0}, |
199 | | {"W*", 0}, |
200 | | {"y", 4}, |
201 | | // ' |
202 | | // " |
203 | | }; |
204 | | |
205 | | void PDFDataset::InitMapOperators() |
206 | 19.2k | { |
207 | 19.2k | for (const auto &sPDFOperator : asPDFOperators) |
208 | 1.21M | m_oMapOperators[sPDFOperator.szOpName] = sPDFOperator.nArgs; |
209 | 19.2k | } |
210 | | |
211 | | /************************************************************************/ |
212 | | /* TestCapability() */ |
213 | | /************************************************************************/ |
214 | | |
215 | | int PDFDataset::TestCapability(CPL_UNUSED const char *pszCap) |
216 | 0 | { |
217 | 0 | return FALSE; |
218 | 0 | } |
219 | | |
220 | | /************************************************************************/ |
221 | | /* GetLayer() */ |
222 | | /************************************************************************/ |
223 | | |
224 | | OGRLayer *PDFDataset::GetLayer(int iLayer) |
225 | | |
226 | 135M | { |
227 | 135M | OpenVectorLayers(nullptr); |
228 | 135M | if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size())) |
229 | 0 | return nullptr; |
230 | | |
231 | 135M | return m_apoLayers[iLayer].get(); |
232 | 135M | } |
233 | | |
234 | | /************************************************************************/ |
235 | | /* GetLayerCount() */ |
236 | | /************************************************************************/ |
237 | | |
238 | | int PDFDataset::GetLayerCount() |
239 | 135M | { |
240 | 135M | OpenVectorLayers(nullptr); |
241 | 135M | return static_cast<int>(m_apoLayers.size()); |
242 | 135M | } |
243 | | |
244 | | /************************************************************************/ |
245 | | /* ExploreTree() */ |
246 | | /************************************************************************/ |
247 | | |
248 | | bool PDFDataset::ExploreTree(GDALPDFObject *poObj, |
249 | | std::set<std::pair<int, int>> &aoSetAlreadyVisited, |
250 | | int nRecLevel, bool bDryRun) |
251 | 46.8k | { |
252 | 46.8k | if (nRecLevel == 16) |
253 | 0 | return false; |
254 | | |
255 | 46.8k | std::pair<int, int> oObjPair(poObj->GetRefNum().toInt(), |
256 | 46.8k | poObj->GetRefGen()); |
257 | 46.8k | if (aoSetAlreadyVisited.find(oObjPair) != aoSetAlreadyVisited.end()) |
258 | 39.9k | return false; |
259 | 6.91k | aoSetAlreadyVisited.insert(oObjPair); |
260 | | |
261 | 6.91k | if (poObj->GetType() != PDFObjectType_Dictionary) |
262 | 99 | return false; |
263 | | |
264 | 6.81k | GDALPDFDictionary *poDict = poObj->GetDictionary(); |
265 | | |
266 | 6.81k | GDALPDFObject *poS = poDict->Get("S"); |
267 | 6.81k | std::string osS; |
268 | 6.81k | if (poS != nullptr && poS->GetType() == PDFObjectType_Name) |
269 | 5.73k | { |
270 | 5.73k | osS = poS->GetName(); |
271 | 5.73k | } |
272 | | |
273 | 6.81k | GDALPDFObject *poT = poDict->Get("T"); |
274 | 6.81k | std::string osT; |
275 | 6.81k | if (poT != nullptr && poT->GetType() == PDFObjectType_String) |
276 | 978 | { |
277 | 978 | osT = poT->GetString(); |
278 | 978 | } |
279 | | |
280 | 6.81k | GDALPDFObject *poK = poDict->Get("K"); |
281 | 6.81k | if (poK == nullptr) |
282 | 309 | return false; |
283 | | |
284 | 6.50k | bool bRet = false; |
285 | 6.50k | if (poK->GetType() == PDFObjectType_Array) |
286 | 3.89k | { |
287 | 3.89k | GDALPDFArray *poArray = poK->GetArray(); |
288 | 3.89k | if (poArray->GetLength() > 0 && poArray->Get(0) && |
289 | 3.89k | poArray->Get(0)->GetType() == PDFObjectType_Dictionary && |
290 | 3.89k | poArray->Get(0)->GetDictionary()->Get("K") != nullptr && |
291 | 3.89k | poArray->Get(0)->GetDictionary()->Get("K")->GetType() == |
292 | 2.76k | PDFObjectType_Int) |
293 | 1.10k | { |
294 | 1.10k | if (bDryRun) |
295 | 688 | { |
296 | 1.37k | for (int i = 0; i < poArray->GetLength(); i++) |
297 | 1.10k | { |
298 | 1.10k | auto poFeatureObj = poArray->Get(i); |
299 | 1.10k | if (poFeatureObj && |
300 | 1.10k | poFeatureObj->GetType() == PDFObjectType_Dictionary) |
301 | 1.09k | { |
302 | 1.09k | auto poA = poFeatureObj->GetDictionary()->Get("A"); |
303 | 1.09k | if (poA && poA->GetType() == PDFObjectType_Dictionary) |
304 | 488 | { |
305 | 488 | auto poO = poA->GetDictionary()->Get("O"); |
306 | 488 | if (poO && poO->GetType() == PDFObjectType_Name && |
307 | 488 | poO->GetName() == "UserProperties") |
308 | 419 | { |
309 | 419 | return true; |
310 | 419 | } |
311 | 488 | } |
312 | 1.09k | } |
313 | 1.10k | } |
314 | 269 | return false; |
315 | 688 | } |
316 | | |
317 | 419 | std::string osLayerName; |
318 | 419 | if (!osT.empty()) |
319 | 401 | osLayerName = std::move(osT); |
320 | 18 | else |
321 | 18 | { |
322 | 18 | if (!osS.empty()) |
323 | 13 | osLayerName = std::move(osS); |
324 | 5 | else |
325 | 5 | osLayerName = CPLSPrintf( |
326 | 5 | "Layer%d", static_cast<int>(m_apoLayers.size()) + 1); |
327 | 18 | } |
328 | | |
329 | 419 | auto poSRSOri = GetSpatialRef(); |
330 | 419 | OGRSpatialReference *poSRS = poSRSOri ? poSRSOri->Clone() : nullptr; |
331 | 419 | auto poLayer = std::make_unique<OGRPDFLayer>( |
332 | 419 | this, osLayerName.c_str(), poSRS, wkbUnknown); |
333 | 419 | if (poSRS) |
334 | 69 | poSRS->Release(); |
335 | | |
336 | 419 | poLayer->Fill(poArray); |
337 | | |
338 | 419 | m_apoLayers.emplace_back(std::move(poLayer)); |
339 | 419 | bRet = true; |
340 | 419 | } |
341 | 2.79k | else |
342 | 2.79k | { |
343 | 47.3k | for (int i = 0; i < poArray->GetLength(); i++) |
344 | 44.9k | { |
345 | 44.9k | auto poSubObj = poArray->Get(i); |
346 | 44.9k | if (poSubObj) |
347 | 44.1k | { |
348 | 44.1k | if (ExploreTree(poSubObj, aoSetAlreadyVisited, |
349 | 44.1k | nRecLevel + 1, bDryRun) && |
350 | 44.1k | bDryRun) |
351 | 419 | return true; |
352 | 44.1k | } |
353 | 44.9k | } |
354 | 2.79k | } |
355 | 3.89k | } |
356 | 2.60k | else if (poK->GetType() == PDFObjectType_Dictionary) |
357 | 1.72k | { |
358 | 1.72k | if (ExploreTree(poK, aoSetAlreadyVisited, nRecLevel + 1, bDryRun) && |
359 | 1.72k | bDryRun) |
360 | 0 | return true; |
361 | 1.72k | } |
362 | | |
363 | 5.39k | return bRet; |
364 | 6.50k | } |
365 | | |
366 | | /************************************************************************/ |
367 | | /* GetGeometryFromMCID() */ |
368 | | /************************************************************************/ |
369 | | |
370 | | OGRGeometry *PDFDataset::GetGeometryFromMCID(int nMCID) |
371 | 1.17k | { |
372 | 1.17k | auto oMapIter = m_oMapMCID.find(nMCID); |
373 | 1.17k | if (oMapIter != m_oMapMCID.end()) |
374 | 429 | return oMapIter->second; |
375 | 741 | else |
376 | 741 | return nullptr; |
377 | 1.17k | } |
378 | | |
379 | | /************************************************************************/ |
380 | | /* GraphicState::PreMultiplyBy() */ |
381 | | /************************************************************************/ |
382 | | |
383 | | void PDFDataset::GraphicState::PreMultiplyBy(double adfMatrix[6]) |
384 | 28.8k | { |
385 | | /* |
386 | | [ a b 0 ] [ a' b' 0] [ aa' + bc' ab' + bd' 0 ] |
387 | | [ c d 0 ] * [ c' d' 0] = [ ca' + dc' cb' + dd' 0 ] |
388 | | [ e f 1 ] [ e' f' 1] [ ea' + fc' + e' eb' + fd' + f' 1 ] |
389 | | */ |
390 | | |
391 | | // Be careful about the multiplication order! |
392 | | // PDF reference version 1.7, page 209: |
393 | | // when a sequence of transformations is carried out, the matrix |
394 | | // representing the combined transformation (M′) is calculated |
395 | | // by premultiplying the matrix representing the additional transformation (MT) |
396 | | // with the one representing all previously existing transformations (M) |
397 | | |
398 | 28.8k | double a = adfMatrix[0]; |
399 | 28.8k | double b = adfMatrix[1]; |
400 | 28.8k | double c = adfMatrix[2]; |
401 | 28.8k | double d = adfMatrix[3]; |
402 | 28.8k | double e = adfMatrix[4]; |
403 | 28.8k | double f = adfMatrix[5]; |
404 | 28.8k | double ap = adfCM[0]; |
405 | 28.8k | double bp = adfCM[1]; |
406 | 28.8k | double cp = adfCM[2]; |
407 | 28.8k | double dp = adfCM[3]; |
408 | 28.8k | double ep = adfCM[4]; |
409 | 28.8k | double fp = adfCM[5]; |
410 | 28.8k | adfCM[0] = a * ap + b * cp; |
411 | 28.8k | adfCM[1] = a * bp + b * dp; |
412 | 28.8k | adfCM[2] = c * ap + d * cp; |
413 | 28.8k | adfCM[3] = c * bp + d * dp; |
414 | 28.8k | adfCM[4] = e * ap + f * cp + ep; |
415 | 28.8k | adfCM[5] = e * bp + f * dp + fp; |
416 | 28.8k | } |
417 | | |
418 | | /************************************************************************/ |
419 | | /* GraphicState::ApplyMatrix() */ |
420 | | /************************************************************************/ |
421 | | |
422 | | void PDFDataset::GraphicState::ApplyMatrix(double adfCoords[2]) const |
423 | 1.27M | { |
424 | 1.27M | double x = adfCoords[0]; |
425 | 1.27M | double y = adfCoords[1]; |
426 | | |
427 | 1.27M | adfCoords[0] = x * adfCM[0] + y * adfCM[2] + adfCM[4]; |
428 | 1.27M | adfCoords[1] = x * adfCM[1] + y * adfCM[3] + adfCM[5]; |
429 | 1.27M | } |
430 | | |
431 | | /************************************************************************/ |
432 | | /* PDFCoordsToSRSCoords() */ |
433 | | /************************************************************************/ |
434 | | |
435 | | void PDFDataset::PDFCoordsToSRSCoords(double x, double y, double &X, double &Y) |
436 | 1.12M | { |
437 | 1.12M | x = x / m_dfPageWidth * nRasterXSize; |
438 | 1.12M | if (m_bGeoTransformValid) |
439 | 344k | y = (1 - y / m_dfPageHeight) * nRasterYSize; |
440 | 781k | else |
441 | 781k | y = (y / m_dfPageHeight) * nRasterYSize; |
442 | | |
443 | 1.12M | X = m_adfGeoTransform[0] + x * m_adfGeoTransform[1] + |
444 | 1.12M | y * m_adfGeoTransform[2]; |
445 | 1.12M | Y = m_adfGeoTransform[3] + x * m_adfGeoTransform[4] + |
446 | 1.12M | y * m_adfGeoTransform[5]; |
447 | | |
448 | 1.12M | if (fabs(X - std::round(X)) < 1e-8) |
449 | 156k | X = std::round(X); |
450 | 1.12M | if (fabs(Y - std::round(Y)) < 1e-8) |
451 | 163k | Y = std::round(Y); |
452 | 1.12M | } |
453 | | |
454 | | /************************************************************************/ |
455 | | /* PDFGetCircleCenter() */ |
456 | | /************************************************************************/ |
457 | | |
458 | | /* Return the center of a circle, or NULL if it is not recognized */ |
459 | | |
460 | | static OGRPoint *PDFGetCircleCenter(OGRLineString *poLS) |
461 | 127 | { |
462 | 127 | if (poLS == nullptr || poLS->getNumPoints() != 1 + 4 * BEZIER_STEPS) |
463 | 0 | return nullptr; |
464 | | |
465 | 127 | if (poLS->getY(0 * BEZIER_STEPS) == poLS->getY(2 * BEZIER_STEPS) && |
466 | 127 | poLS->getX(1 * BEZIER_STEPS) == poLS->getX(3 * BEZIER_STEPS) && |
467 | 127 | fabs((poLS->getX(0 * BEZIER_STEPS) + poLS->getX(2 * BEZIER_STEPS)) / 2 - |
468 | 127 | poLS->getX(1 * BEZIER_STEPS)) < EPSILON && |
469 | 127 | fabs((poLS->getY(1 * BEZIER_STEPS) + poLS->getY(3 * BEZIER_STEPS)) / 2 - |
470 | 123 | poLS->getY(0 * BEZIER_STEPS)) < EPSILON) |
471 | 120 | { |
472 | 120 | return new OGRPoint( |
473 | 120 | (poLS->getX(0 * BEZIER_STEPS) + poLS->getX(2 * BEZIER_STEPS)) / 2, |
474 | 120 | (poLS->getY(1 * BEZIER_STEPS) + poLS->getY(3 * BEZIER_STEPS)) / 2); |
475 | 120 | } |
476 | 7 | return nullptr; |
477 | 127 | } |
478 | | |
479 | | /************************************************************************/ |
480 | | /* PDFGetSquareCenter() */ |
481 | | /************************************************************************/ |
482 | | |
483 | | /* Return the center of a square, or NULL if it is not recognized */ |
484 | | |
485 | | static OGRPoint *PDFGetSquareCenter(OGRLineString *poLS) |
486 | 11.6k | { |
487 | 11.6k | if (poLS == nullptr || poLS->getNumPoints() < 4 || poLS->getNumPoints() > 5) |
488 | 0 | return nullptr; |
489 | | |
490 | 11.6k | if (poLS->getX(0) == poLS->getX(3) && poLS->getY(0) == poLS->getY(1) && |
491 | 11.6k | poLS->getX(1) == poLS->getX(2) && poLS->getY(2) == poLS->getY(3) && |
492 | 11.6k | fabs(fabs(poLS->getX(0) - poLS->getX(1)) - |
493 | 11.5k | fabs(poLS->getY(0) - poLS->getY(3))) < EPSILON) |
494 | 0 | { |
495 | 0 | return new OGRPoint((poLS->getX(0) + poLS->getX(1)) / 2, |
496 | 0 | (poLS->getY(0) + poLS->getY(3)) / 2); |
497 | 0 | } |
498 | 11.6k | return nullptr; |
499 | 11.6k | } |
500 | | |
501 | | /************************************************************************/ |
502 | | /* PDFGetTriangleCenter() */ |
503 | | /************************************************************************/ |
504 | | |
505 | | /* Return the center of a equilateral triangle, or NULL if it is not recognized |
506 | | */ |
507 | | |
508 | | static OGRPoint *PDFGetTriangleCenter(OGRLineString *poLS) |
509 | 17.9k | { |
510 | 17.9k | if (poLS == nullptr || poLS->getNumPoints() < 3 || poLS->getNumPoints() > 4) |
511 | 0 | return nullptr; |
512 | | |
513 | 17.9k | double dfSqD1 = SQUARE(poLS->getX(0) - poLS->getX(1)) + |
514 | 17.9k | SQUARE(poLS->getY(0) - poLS->getY(1)); |
515 | 17.9k | double dfSqD2 = SQUARE(poLS->getX(1) - poLS->getX(2)) + |
516 | 17.9k | SQUARE(poLS->getY(1) - poLS->getY(2)); |
517 | 17.9k | double dfSqD3 = SQUARE(poLS->getX(0) - poLS->getX(2)) + |
518 | 17.9k | SQUARE(poLS->getY(0) - poLS->getY(2)); |
519 | 17.9k | if (fabs(dfSqD1 - dfSqD2) < EPSILON && fabs(dfSqD2 - dfSqD3) < EPSILON) |
520 | 3 | { |
521 | 3 | return new OGRPoint((poLS->getX(0) + poLS->getX(1) + poLS->getX(2)) / 3, |
522 | 3 | (poLS->getY(0) + poLS->getY(1) + poLS->getY(2)) / |
523 | 3 | 3); |
524 | 3 | } |
525 | 17.9k | return nullptr; |
526 | 17.9k | } |
527 | | |
528 | | /************************************************************************/ |
529 | | /* PDFGetStarCenter() */ |
530 | | /************************************************************************/ |
531 | | |
532 | | /* Return the center of a 5-point star, or NULL if it is not recognized */ |
533 | | |
534 | | static OGRPoint *PDFGetStarCenter(OGRLineString *poLS) |
535 | 1 | { |
536 | 1 | if (poLS == nullptr || poLS->getNumPoints() < 10 || |
537 | 1 | poLS->getNumPoints() > 11) |
538 | 0 | return nullptr; |
539 | | |
540 | 1 | double dfSqD01 = SQUARE(poLS->getX(0) - poLS->getX(1)) + |
541 | 1 | SQUARE(poLS->getY(0) - poLS->getY(1)); |
542 | 1 | double dfSqD02 = SQUARE(poLS->getX(0) - poLS->getX(2)) + |
543 | 1 | SQUARE(poLS->getY(0) - poLS->getY(2)); |
544 | 1 | double dfSqD13 = SQUARE(poLS->getX(1) - poLS->getX(3)) + |
545 | 1 | SQUARE(poLS->getY(1) - poLS->getY(3)); |
546 | 1 | const double dfSin18divSin126 = 0.38196601125; |
547 | 1 | if (dfSqD02 == 0) |
548 | 0 | return nullptr; |
549 | 1 | int bOK = fabs(dfSqD13 / dfSqD02 - SQUARE(dfSin18divSin126)) < EPSILON; |
550 | 1 | for (int i = 1; i < 10 && bOK; i++) |
551 | 0 | { |
552 | 0 | double dfSqDiip1 = SQUARE(poLS->getX(i) - poLS->getX((i + 1) % 10)) + |
553 | 0 | SQUARE(poLS->getY(i) - poLS->getY((i + 1) % 10)); |
554 | 0 | if (fabs(dfSqDiip1 - dfSqD01) > EPSILON) |
555 | 0 | { |
556 | 0 | bOK = FALSE; |
557 | 0 | } |
558 | 0 | double dfSqDiip2 = SQUARE(poLS->getX(i) - poLS->getX((i + 2) % 10)) + |
559 | 0 | SQUARE(poLS->getY(i) - poLS->getY((i + 2) % 10)); |
560 | 0 | if ((i % 2) == 1 && fabs(dfSqDiip2 - dfSqD13) > EPSILON) |
561 | 0 | { |
562 | 0 | bOK = FALSE; |
563 | 0 | } |
564 | 0 | if ((i % 2) == 0 && fabs(dfSqDiip2 - dfSqD02) > EPSILON) |
565 | 0 | { |
566 | 0 | bOK = FALSE; |
567 | 0 | } |
568 | 0 | } |
569 | 1 | if (bOK) |
570 | 0 | { |
571 | 0 | return new OGRPoint((poLS->getX(0) + poLS->getX(2) + poLS->getX(4) + |
572 | 0 | poLS->getX(6) + poLS->getX(8)) / |
573 | 0 | 5, |
574 | 0 | (poLS->getY(0) + poLS->getY(2) + poLS->getY(4) + |
575 | 0 | poLS->getY(6) + poLS->getY(8)) / |
576 | 0 | 5); |
577 | 0 | } |
578 | 1 | return nullptr; |
579 | 1 | } |
580 | | |
581 | | /************************************************************************/ |
582 | | /* UnstackTokens() */ |
583 | | /************************************************************************/ |
584 | | |
585 | | int PDFDataset::UnstackTokens( |
586 | | const char *pszToken, int nRequiredArgs, |
587 | | char aszTokenStack[TOKEN_STACK_SIZE][MAX_TOKEN_SIZE], int &nTokenStackSize, |
588 | | double *adfCoords) |
589 | 1.27M | { |
590 | 1.27M | if (nTokenStackSize < nRequiredArgs) |
591 | 17 | { |
592 | 17 | CPLDebug("PDF", "not enough arguments for %s", pszToken); |
593 | 17 | return FALSE; |
594 | 17 | } |
595 | 1.27M | nTokenStackSize -= nRequiredArgs; |
596 | 4.06M | for (int i = 0; i < nRequiredArgs; i++) |
597 | 2.79M | { |
598 | 2.79M | adfCoords[i] = CPLAtof(aszTokenStack[nTokenStackSize + i]); |
599 | 2.79M | } |
600 | 1.27M | return TRUE; |
601 | 1.27M | } |
602 | | |
603 | | /************************************************************************/ |
604 | | /* AddBezierCurve() */ |
605 | | /************************************************************************/ |
606 | | |
607 | | static void AddBezierCurve(std::vector<double> &oCoords, const double *x0_y0, |
608 | | const double *x1_y1, const double *x2_y2, |
609 | | const double *x3_y3) |
610 | 2.40k | { |
611 | 2.40k | double x0 = x0_y0[0]; |
612 | 2.40k | double y0 = x0_y0[1]; |
613 | 2.40k | double x1 = x1_y1[0]; |
614 | 2.40k | double y1 = x1_y1[1]; |
615 | 2.40k | double x2 = x2_y2[0]; |
616 | 2.40k | double y2 = x2_y2[1]; |
617 | 2.40k | double x3 = x3_y3[0]; |
618 | 2.40k | double y3 = x3_y3[1]; |
619 | 24.0k | for (int i = 1; i < BEZIER_STEPS; i++) |
620 | 21.6k | { |
621 | 21.6k | const double t = static_cast<double>(i) / BEZIER_STEPS; |
622 | 21.6k | const double t2 = t * t; |
623 | 21.6k | const double t3 = t2 * t; |
624 | 21.6k | const double oneMinust = 1 - t; |
625 | 21.6k | const double oneMinust2 = oneMinust * oneMinust; |
626 | 21.6k | const double oneMinust3 = oneMinust2 * oneMinust; |
627 | 21.6k | const double three_t_oneMinust = 3 * t * oneMinust; |
628 | 21.6k | const double x = oneMinust3 * x0 + |
629 | 21.6k | three_t_oneMinust * (oneMinust * x1 + t * x2) + |
630 | 21.6k | t3 * x3; |
631 | 21.6k | const double y = oneMinust3 * y0 + |
632 | 21.6k | three_t_oneMinust * (oneMinust * y1 + t * y2) + |
633 | 21.6k | t3 * y3; |
634 | 21.6k | oCoords.push_back(x); |
635 | 21.6k | oCoords.push_back(y); |
636 | 21.6k | } |
637 | 2.40k | oCoords.push_back(x3); |
638 | 2.40k | oCoords.push_back(y3); |
639 | 2.40k | } |
640 | | |
641 | | /************************************************************************/ |
642 | | /* ParseContent() */ |
643 | | /************************************************************************/ |
644 | | |
645 | 3.52M | #define NEW_SUBPATH -99 |
646 | 2.46M | #define CLOSE_SUBPATH -98 |
647 | 2.36M | #define FILL_SUBPATH -97 |
648 | | |
649 | | OGRGeometry *PDFDataset::ParseContent( |
650 | | const char *pszContent, GDALPDFObject *poResources, bool bCollectAllObjects, |
651 | | bool bInitBDCStack, bool bMatchQ, |
652 | | const std::map<CPLString, OGRPDFLayer *> &oMapPropertyToLayer, |
653 | | const std::map<std::pair<int, int>, OGRPDFLayer *> &oMapNumGenToLayer, |
654 | | const GraphicState &graphicStateIn, OGRPDFLayer *poCurLayer, int nRecLevel) |
655 | 15.6k | { |
656 | 15.6k | if (nRecLevel == 32) |
657 | 0 | { |
658 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
659 | 0 | "Too many recursion levels in ParseContent()"); |
660 | 0 | return nullptr; |
661 | 0 | } |
662 | 15.6k | if (CPLTestBool(CPLGetConfigOption("PDF_DUMP_CONTENT", "NO"))) |
663 | 0 | { |
664 | 0 | static int counter = 1; |
665 | 0 | FILE *f = fopen(CPLSPrintf("content%d.txt", counter), "wb"); |
666 | 0 | ++counter; |
667 | 0 | fwrite(pszContent, 1, strlen(pszContent), f); |
668 | 0 | fclose(f); |
669 | 0 | } |
670 | 15.6k | const char *pszContentIni = pszContent; |
671 | | #ifdef DEBUG_VERBOSE |
672 | | CPLDebug("PDF", "Initial layer: %s", |
673 | | poCurLayer ? poCurLayer->GetName() : "(null)"); |
674 | | #endif |
675 | | |
676 | 15.6k | #define PUSH(aszTokenStack, str, strlen) \ |
677 | 2.92M | do \ |
678 | 2.92M | { \ |
679 | 2.92M | if (nTokenStackSize < TOKEN_STACK_SIZE) \ |
680 | 2.92M | memcpy(aszTokenStack[nTokenStackSize++], str, strlen + 1); \ |
681 | 2.92M | else \ |
682 | 2.92M | { \ |
683 | 180 | CPLError(CE_Failure, CPLE_AppDefined, \ |
684 | 180 | "Max token stack size reached"); \ |
685 | 180 | return nullptr; \ |
686 | 2.92M | }; \ |
687 | 2.92M | } while (false) |
688 | | |
689 | 15.6k | #define ADD_CHAR(szToken, c) \ |
690 | 30.5M | do \ |
691 | 30.5M | { \ |
692 | 30.5M | if (nTokenSize < MAX_TOKEN_SIZE - 1) \ |
693 | 30.5M | { \ |
694 | 30.5M | szToken[nTokenSize++] = c; \ |
695 | 30.5M | szToken[nTokenSize] = '\0'; \ |
696 | 30.5M | } \ |
697 | 30.5M | else \ |
698 | 30.5M | { \ |
699 | 35 | CPLError(CE_Failure, CPLE_AppDefined, "Max token size reached"); \ |
700 | 35 | return nullptr; \ |
701 | 30.5M | }; \ |
702 | 30.5M | } while (false) |
703 | | |
704 | 15.6k | char szToken[MAX_TOKEN_SIZE]; |
705 | 15.6k | int nTokenSize = 0; |
706 | 15.6k | char ch; |
707 | 15.6k | char aszTokenStack[TOKEN_STACK_SIZE][MAX_TOKEN_SIZE]; |
708 | 15.6k | int nTokenStackSize = 0; |
709 | 15.6k | int bInString = FALSE; |
710 | 15.6k | int nBDCOrBMCLevel = 0; |
711 | 15.6k | int nParenthesisLevel = 0; |
712 | 15.6k | int nArrayLevel = 0; |
713 | 15.6k | int nBTLevel = 0; |
714 | | |
715 | 15.6k | GraphicState oGS(graphicStateIn); |
716 | 15.6k | std::stack<GraphicState> oGSStack; |
717 | 15.6k | std::stack<OGRPDFLayer *> oLayerStack; |
718 | | |
719 | 15.6k | std::vector<double> oCoords; |
720 | 15.6k | int bHasFoundFill = FALSE; |
721 | 15.6k | int bHasMultiPart = FALSE; |
722 | | |
723 | 15.6k | szToken[0] = '\0'; |
724 | | |
725 | 15.6k | if (bInitBDCStack) |
726 | 595 | { |
727 | 595 | PUSH(aszTokenStack, "dummy", 5); |
728 | 595 | PUSH(aszTokenStack, "dummy", 5); |
729 | 595 | oLayerStack.push(nullptr); |
730 | 595 | } |
731 | | |
732 | 15.6k | int nLineNumber = 0; |
733 | | |
734 | 35.9M | while ((ch = *pszContent) != '\0') |
735 | 35.9M | { |
736 | 35.9M | int bPushToken = FALSE; |
737 | | |
738 | 35.9M | if (!bInString && ch == '%') |
739 | 175 | { |
740 | | /* Skip comments until end-of-line */ |
741 | 5.66k | while ((ch = *pszContent) != '\0') |
742 | 5.63k | { |
743 | 5.63k | if (ch == '\r' || ch == '\n') |
744 | 141 | break; |
745 | 5.49k | pszContent++; |
746 | 5.49k | } |
747 | 175 | if (ch == 0) |
748 | 34 | break; |
749 | 141 | ++nLineNumber; |
750 | 141 | if (ch == '\r' && pszContent[1] == '\n') |
751 | 2 | { |
752 | 2 | ++pszContent; |
753 | 2 | } |
754 | 141 | } |
755 | 35.9M | else if (!bInString && (ch == ' ' || ch == '\r' || ch == '\n')) |
756 | 5.31M | { |
757 | 5.31M | if (ch == '\r') |
758 | 150k | { |
759 | 150k | ++nLineNumber; |
760 | 150k | if (pszContent[1] == '\n') |
761 | 126k | { |
762 | 126k | ++pszContent; |
763 | 126k | } |
764 | 150k | } |
765 | 5.16M | else if (ch == '\n') |
766 | 1.40M | ++nLineNumber; |
767 | 5.31M | bPushToken = TRUE; |
768 | 5.31M | } |
769 | | |
770 | | /* Ignore arrays */ |
771 | 30.6M | else if (!bInString && nTokenSize == 0 && ch == '[') |
772 | 3.04k | { |
773 | 3.04k | nArrayLevel++; |
774 | 3.04k | } |
775 | 30.6M | else if (!bInString && nArrayLevel && ch == ']') |
776 | 2.92k | { |
777 | 2.92k | nArrayLevel--; |
778 | 2.92k | nTokenSize = 0; // completely ignore content in arrays |
779 | 2.92k | } |
780 | | |
781 | 30.6M | else if (!bInString && nTokenSize == 0 && ch == '(') |
782 | 38.5k | { |
783 | 38.5k | bInString = TRUE; |
784 | 38.5k | nParenthesisLevel++; |
785 | 38.5k | ADD_CHAR(szToken, ch); |
786 | 38.5k | } |
787 | 30.6M | else if (bInString && ch == '(') |
788 | 2.86k | { |
789 | 2.86k | nParenthesisLevel++; |
790 | 2.86k | ADD_CHAR(szToken, ch); |
791 | 2.86k | } |
792 | 30.6M | else if (bInString && ch == ')') |
793 | 41.3k | { |
794 | 41.3k | nParenthesisLevel--; |
795 | 41.3k | ADD_CHAR(szToken, ch); |
796 | 41.3k | if (nParenthesisLevel == 0) |
797 | 38.5k | { |
798 | 38.5k | bInString = FALSE; |
799 | 38.5k | bPushToken = TRUE; |
800 | 38.5k | } |
801 | 41.3k | } |
802 | 30.5M | else if (bInString && ch == '\\') |
803 | 18.1k | { |
804 | 18.1k | const auto nextCh = pszContent[1]; |
805 | 18.1k | if (nextCh == 'n') |
806 | 1 | { |
807 | 1 | ADD_CHAR(szToken, '\n'); |
808 | 1 | pszContent++; |
809 | 1 | } |
810 | 18.1k | else if (nextCh == 'r') |
811 | 3 | { |
812 | 3 | ADD_CHAR(szToken, '\r'); |
813 | 3 | pszContent++; |
814 | 3 | } |
815 | 18.1k | else if (nextCh == 't') |
816 | 0 | { |
817 | 0 | ADD_CHAR(szToken, '\t'); |
818 | 0 | pszContent++; |
819 | 0 | } |
820 | 18.1k | else if (nextCh == 'b') |
821 | 0 | { |
822 | 0 | ADD_CHAR(szToken, '\b'); |
823 | 0 | pszContent++; |
824 | 0 | } |
825 | 18.1k | else if (nextCh == '(' || nextCh == ')' || nextCh == '\\') |
826 | 582 | { |
827 | 582 | ADD_CHAR(szToken, nextCh); |
828 | 581 | pszContent++; |
829 | 581 | } |
830 | 17.5k | else if (nextCh >= '0' && nextCh <= '7' && pszContent[2] >= '0' && |
831 | 17.5k | pszContent[2] <= '7' && pszContent[3] >= '0' && |
832 | 17.5k | pszContent[3] <= '7') |
833 | 13.2k | { |
834 | 13.2k | ADD_CHAR(szToken, |
835 | 13.2k | ((nextCh - '\0') * 64 + (pszContent[2] - '\0') * 8 + |
836 | 13.2k | pszContent[3] - '\0')); |
837 | 13.2k | pszContent += 3; |
838 | 13.2k | } |
839 | 4.26k | else if (nextCh == '\n') |
840 | 8 | { |
841 | 8 | if (pszContent[2] == '\r') |
842 | 0 | pszContent += 2; |
843 | 8 | else |
844 | 8 | pszContent++; |
845 | 8 | } |
846 | 4.25k | else if (nextCh == '\r') |
847 | 112 | { |
848 | 112 | pszContent++; |
849 | 112 | } |
850 | 18.1k | } |
851 | 30.5M | else if (ch == '<' && pszContent[1] == '<' && nTokenSize == 0) |
852 | 36 | { |
853 | 36 | int nDictDepth = 0; |
854 | | |
855 | 3.85k | while (*pszContent != '\0') |
856 | 3.85k | { |
857 | 3.85k | if (pszContent[0] == '<' && pszContent[1] == '<') |
858 | 128 | { |
859 | 128 | ADD_CHAR(szToken, '<'); |
860 | 125 | ADD_CHAR(szToken, '<'); |
861 | 124 | nDictDepth++; |
862 | 124 | pszContent += 2; |
863 | 124 | } |
864 | 3.72k | else if (pszContent[0] == '>' && pszContent[1] == '>') |
865 | 40 | { |
866 | 40 | ADD_CHAR(szToken, '>'); |
867 | 40 | ADD_CHAR(szToken, '>'); |
868 | 40 | nDictDepth--; |
869 | 40 | pszContent += 2; |
870 | 40 | if (nDictDepth == 0) |
871 | 20 | break; |
872 | 40 | } |
873 | 3.68k | else |
874 | 3.68k | { |
875 | 3.68k | ADD_CHAR(szToken, *pszContent); |
876 | 3.67k | pszContent++; |
877 | 3.67k | } |
878 | 3.85k | } |
879 | 25 | if (nDictDepth == 0) |
880 | 20 | { |
881 | 20 | bPushToken = TRUE; |
882 | 20 | pszContent--; |
883 | 20 | } |
884 | 5 | else |
885 | 5 | break; |
886 | 25 | } |
887 | 30.5M | else |
888 | 30.5M | { |
889 | | // Do not create too long tokens in arrays, that we will ignore |
890 | | // anyway |
891 | 30.5M | if (nArrayLevel == 0 || nTokenSize == 0) |
892 | 30.4M | { |
893 | 30.4M | ADD_CHAR(szToken, ch); |
894 | 30.4M | } |
895 | 30.5M | } |
896 | | |
897 | 35.9M | pszContent++; |
898 | 35.9M | if (pszContent[0] == '\0') |
899 | 13.8k | bPushToken = TRUE; |
900 | | |
901 | 35.9M | #define EQUAL1(szToken, s) (szToken[0] == s[0] && szToken[1] == '\0') |
902 | 35.9M | #define EQUAL2(szToken, s) \ |
903 | 35.9M | (szToken[0] == s[0] && szToken[1] == s[1] && szToken[2] == '\0') |
904 | 35.9M | #define EQUAL3(szToken, s) \ |
905 | 35.9M | (szToken[0] == s[0] && szToken[1] == s[1] && szToken[2] == s[2] && \ |
906 | 15.1M | szToken[3] == '\0') |
907 | | |
908 | 35.9M | if (bPushToken && nTokenSize) |
909 | 5.05M | { |
910 | 5.05M | if (EQUAL2(szToken, "BI")) |
911 | 14 | { |
912 | 365k | while (*pszContent != '\0') |
913 | 365k | { |
914 | 365k | if (pszContent[0] == 'E' && pszContent[1] == 'I' && |
915 | 365k | pszContent[2] == ' ') |
916 | 0 | { |
917 | 0 | break; |
918 | 0 | } |
919 | 365k | pszContent++; |
920 | 365k | } |
921 | 14 | if (pszContent[0] == 'E') |
922 | 0 | pszContent += 3; |
923 | 14 | else |
924 | 14 | { |
925 | 14 | CPLDebug("PDF", |
926 | 14 | "ParseContent(), line %d: return at line %d of " |
927 | 14 | "content stream", |
928 | 14 | __LINE__, nLineNumber); |
929 | 14 | return nullptr; |
930 | 14 | } |
931 | 14 | } |
932 | 5.05M | else if (EQUAL3(szToken, "BDC")) |
933 | 27.1k | { |
934 | 27.1k | if (nTokenStackSize < 2) |
935 | 5 | { |
936 | 5 | CPLDebug("PDF", "not enough arguments for %s", szToken); |
937 | 5 | CPLDebug("PDF", |
938 | 5 | "ParseContent(), line %d: return at line %d of " |
939 | 5 | "content stream", |
940 | 5 | __LINE__, nLineNumber); |
941 | 5 | return nullptr; |
942 | 5 | } |
943 | 27.1k | nTokenStackSize -= 2; |
944 | 27.1k | const char *pszOC = aszTokenStack[nTokenStackSize]; |
945 | 27.1k | const char *pszOCGName = aszTokenStack[nTokenStackSize + 1]; |
946 | | |
947 | 27.1k | nBDCOrBMCLevel++; |
948 | | |
949 | 27.1k | if (EQUAL3(pszOC, "/OC") && pszOCGName[0] == '/') |
950 | 23.1k | { |
951 | 23.1k | const auto oIter = oMapPropertyToLayer.find(pszOCGName + 1); |
952 | 23.1k | if (oIter != oMapPropertyToLayer.end()) |
953 | 10.5k | { |
954 | 10.5k | poCurLayer = oIter->second; |
955 | 10.5k | } |
956 | 23.1k | } |
957 | | #ifdef DEBUG_VERBOSE |
958 | | CPLDebug("PDF", "%s %s BDC -> Cur layer : %s", pszOC, |
959 | | pszOCGName, |
960 | | poCurLayer ? poCurLayer->GetName() : "(null)"); |
961 | | #endif |
962 | 27.1k | oLayerStack.push(poCurLayer); |
963 | 27.1k | } |
964 | 5.03M | else if (EQUAL3(szToken, "BMC")) |
965 | 0 | { |
966 | 0 | if (nTokenStackSize < 1) |
967 | 0 | { |
968 | 0 | CPLDebug("PDF", "not enough arguments for %s", szToken); |
969 | 0 | CPLDebug("PDF", |
970 | 0 | "ParseContent(), line %d: return at line %d of " |
971 | 0 | "content stream", |
972 | 0 | __LINE__, nLineNumber); |
973 | 0 | return nullptr; |
974 | 0 | } |
975 | 0 | nTokenStackSize -= 1; |
976 | |
|
977 | 0 | nBDCOrBMCLevel++; |
978 | 0 | oLayerStack.push(poCurLayer); |
979 | 0 | } |
980 | 5.03M | else if (EQUAL3(szToken, "EMC")) |
981 | 22.8k | { |
982 | | // CPLDebug("PDF", "EMC"); |
983 | 22.8k | if (!oLayerStack.empty()) |
984 | 22.6k | { |
985 | 22.6k | oLayerStack.pop(); |
986 | 22.6k | if (!oLayerStack.empty()) |
987 | 574 | poCurLayer = oLayerStack.top(); |
988 | 22.1k | else |
989 | 22.1k | poCurLayer = nullptr; |
990 | | |
991 | | #ifdef DEBUG_VERBOSE |
992 | | CPLDebug("PDF", "EMC -> Cur layer : %s", |
993 | | poCurLayer ? poCurLayer->GetName() : "(null)"); |
994 | | #endif |
995 | 22.6k | } |
996 | 162 | else |
997 | 162 | { |
998 | 162 | CPLDebug( |
999 | 162 | "PDF", |
1000 | 162 | "Should not happen at line %d: offset %d in stream", |
1001 | 162 | __LINE__, int(pszContent - pszContentIni)); |
1002 | 162 | poCurLayer = nullptr; |
1003 | | // return NULL; |
1004 | 162 | } |
1005 | | |
1006 | 22.8k | nBDCOrBMCLevel--; |
1007 | 22.8k | if (nBDCOrBMCLevel == 0 && bInitBDCStack) |
1008 | 147 | break; |
1009 | 22.8k | } |
1010 | | |
1011 | | /* Ignore any text stuff */ |
1012 | 5.00M | else if (EQUAL2(szToken, "BT")) |
1013 | 6.67k | nBTLevel++; |
1014 | 5.00M | else if (EQUAL2(szToken, "ET")) |
1015 | 3.68k | { |
1016 | 3.68k | nBTLevel--; |
1017 | 3.68k | if (nBTLevel < 0) |
1018 | 4 | { |
1019 | 4 | CPLDebug( |
1020 | 4 | "PDF", |
1021 | 4 | "Should not happen at line %d: offset %d in stream", |
1022 | 4 | __LINE__, int(pszContent - pszContentIni)); |
1023 | 4 | CPLDebug("PDF", |
1024 | 4 | "ParseContent(), line %d: return at line %d of " |
1025 | 4 | "content stream", |
1026 | 4 | __LINE__, nLineNumber); |
1027 | 4 | return nullptr; |
1028 | 4 | } |
1029 | 3.68k | } |
1030 | 4.99M | else if (!nArrayLevel && !nBTLevel) |
1031 | 4.47M | { |
1032 | 4.47M | int bEmitFeature = FALSE; |
1033 | | |
1034 | 4.47M | if (szToken[0] < 'A') |
1035 | 2.91M | { |
1036 | 2.91M | PUSH(aszTokenStack, szToken, nTokenSize); |
1037 | 2.91M | } |
1038 | 1.56M | else if (EQUAL1(szToken, "q")) |
1039 | 47.9k | { |
1040 | 47.9k | oGSStack.push(oGS); |
1041 | 47.9k | } |
1042 | 1.51M | else if (EQUAL1(szToken, "Q")) |
1043 | 41.9k | { |
1044 | 41.9k | if (oGSStack.empty()) |
1045 | 2 | { |
1046 | 2 | CPLDebug("PDF", "not enough arguments for %s", szToken); |
1047 | 2 | CPLDebug("PDF", |
1048 | 2 | "ParseContent(), line %d: return at line %d " |
1049 | 2 | "of content stream", |
1050 | 2 | __LINE__, nLineNumber); |
1051 | 2 | return nullptr; |
1052 | 2 | } |
1053 | | |
1054 | 41.9k | oGS = oGSStack.top(); |
1055 | 41.9k | oGSStack.pop(); |
1056 | | |
1057 | 41.9k | if (oGSStack.empty() && bMatchQ) |
1058 | 0 | break; |
1059 | 41.9k | } |
1060 | 1.47M | else if (EQUAL2(szToken, "cm")) |
1061 | 28.8k | { |
1062 | 28.8k | double adfMatrix[6]; |
1063 | 28.8k | if (!UnstackTokens(szToken, 6, aszTokenStack, |
1064 | 28.8k | nTokenStackSize, adfMatrix)) |
1065 | 0 | { |
1066 | 0 | CPLDebug( |
1067 | 0 | "PDF", |
1068 | 0 | "Should not happen at line %d: offset %d in stream", |
1069 | 0 | __LINE__, int(pszContent - pszContentIni)); |
1070 | 0 | CPLDebug("PDF", |
1071 | 0 | "ParseContent(), line %d: return at line %d " |
1072 | 0 | "of content stream", |
1073 | 0 | __LINE__, nLineNumber); |
1074 | 0 | return nullptr; |
1075 | 0 | } |
1076 | | |
1077 | 28.8k | oGS.PreMultiplyBy(adfMatrix); |
1078 | 28.8k | } |
1079 | 1.44M | else if (EQUAL1(szToken, "b") || /* closepath, fill, stroke */ |
1080 | 1.44M | EQUAL2(szToken, "b*") /* closepath, eofill, stroke */) |
1081 | 457 | { |
1082 | 457 | if (!(!oCoords.empty() && |
1083 | 457 | oCoords[oCoords.size() - 2] == CLOSE_SUBPATH && |
1084 | 457 | oCoords.back() == CLOSE_SUBPATH)) |
1085 | 107 | { |
1086 | 107 | oCoords.push_back(CLOSE_SUBPATH); |
1087 | 107 | oCoords.push_back(CLOSE_SUBPATH); |
1088 | 107 | } |
1089 | 457 | oCoords.push_back(FILL_SUBPATH); |
1090 | 457 | oCoords.push_back(FILL_SUBPATH); |
1091 | 457 | bHasFoundFill = TRUE; |
1092 | | |
1093 | 457 | bEmitFeature = TRUE; |
1094 | 457 | } |
1095 | 1.44M | else if (EQUAL1(szToken, "B") || /* fill, stroke */ |
1096 | 1.44M | EQUAL2(szToken, "B*") || /* eofill, stroke */ |
1097 | 1.44M | EQUAL1(szToken, "f") || /* fill */ |
1098 | 1.44M | EQUAL1(szToken, "F") || /* fill */ |
1099 | 1.44M | EQUAL2(szToken, "f*") /* eofill */) |
1100 | 23.1k | { |
1101 | 23.1k | oCoords.push_back(FILL_SUBPATH); |
1102 | 23.1k | oCoords.push_back(FILL_SUBPATH); |
1103 | 23.1k | bHasFoundFill = TRUE; |
1104 | | |
1105 | 23.1k | bEmitFeature = TRUE; |
1106 | 23.1k | } |
1107 | 1.41M | else if (EQUAL1(szToken, "h")) /* close subpath */ |
1108 | 3.23k | { |
1109 | 3.23k | if (!(!oCoords.empty() && |
1110 | 3.23k | oCoords[oCoords.size() - 2] == CLOSE_SUBPATH && |
1111 | 3.23k | oCoords.back() == CLOSE_SUBPATH)) |
1112 | 3.22k | { |
1113 | 3.22k | oCoords.push_back(CLOSE_SUBPATH); |
1114 | 3.22k | oCoords.push_back(CLOSE_SUBPATH); |
1115 | 3.22k | } |
1116 | 3.23k | } |
1117 | 1.41M | else if (EQUAL1( |
1118 | 1.41M | szToken, |
1119 | 1.41M | "n")) /* new subpath without stroking or filling */ |
1120 | 30.3k | { |
1121 | 30.3k | oCoords.resize(0); |
1122 | 30.3k | } |
1123 | 1.38M | else if (EQUAL1(szToken, "s")) /* close and stroke */ |
1124 | 2 | { |
1125 | 2 | if (!(!oCoords.empty() && |
1126 | 2 | oCoords[oCoords.size() - 2] == CLOSE_SUBPATH && |
1127 | 2 | oCoords.back() == CLOSE_SUBPATH)) |
1128 | 2 | { |
1129 | 2 | oCoords.push_back(CLOSE_SUBPATH); |
1130 | 2 | oCoords.push_back(CLOSE_SUBPATH); |
1131 | 2 | } |
1132 | | |
1133 | 2 | bEmitFeature = TRUE; |
1134 | 2 | } |
1135 | 1.38M | else if (EQUAL1(szToken, "S")) /* stroke */ |
1136 | 24.2k | { |
1137 | 24.2k | bEmitFeature = TRUE; |
1138 | 24.2k | } |
1139 | 1.36M | else if (EQUAL1(szToken, "m") || EQUAL1(szToken, "l")) |
1140 | 1.16M | { |
1141 | 1.16M | double adfCoords[2]; |
1142 | 1.16M | if (!UnstackTokens(szToken, 2, aszTokenStack, |
1143 | 1.16M | nTokenStackSize, adfCoords)) |
1144 | 10 | { |
1145 | 10 | CPLDebug( |
1146 | 10 | "PDF", |
1147 | 10 | "Should not happen at line %d: offset %d in stream", |
1148 | 10 | __LINE__, int(pszContent - pszContentIni)); |
1149 | 10 | CPLDebug("PDF", |
1150 | 10 | "ParseContent(), line %d: return at line %d " |
1151 | 10 | "of content stream", |
1152 | 10 | __LINE__, nLineNumber); |
1153 | 10 | return nullptr; |
1154 | 10 | } |
1155 | | |
1156 | 1.16M | if (EQUAL1(szToken, "m")) |
1157 | 225k | { |
1158 | 225k | if (!oCoords.empty()) |
1159 | 200k | bHasMultiPart = TRUE; |
1160 | 225k | oCoords.push_back(NEW_SUBPATH); |
1161 | 225k | oCoords.push_back(NEW_SUBPATH); |
1162 | 225k | } |
1163 | | |
1164 | 1.16M | oGS.ApplyMatrix(adfCoords); |
1165 | 1.16M | oCoords.push_back(adfCoords[0]); |
1166 | 1.16M | oCoords.push_back(adfCoords[1]); |
1167 | 1.16M | } |
1168 | 196k | else if (EQUAL1(szToken, "c")) /* Bezier curve */ |
1169 | 2.40k | { |
1170 | 2.40k | double adfCoords[6]; |
1171 | 2.40k | if (!UnstackTokens(szToken, 6, aszTokenStack, |
1172 | 2.40k | nTokenStackSize, adfCoords)) |
1173 | 1 | { |
1174 | 1 | CPLDebug( |
1175 | 1 | "PDF", |
1176 | 1 | "Should not happen at line %d: offset %d in stream", |
1177 | 1 | __LINE__, int(pszContent - pszContentIni)); |
1178 | 1 | CPLDebug("PDF", |
1179 | 1 | "ParseContent(), line %d: return at line %d " |
1180 | 1 | "of content stream", |
1181 | 1 | __LINE__, nLineNumber); |
1182 | 1 | return nullptr; |
1183 | 1 | } |
1184 | | |
1185 | 2.40k | oGS.ApplyMatrix(adfCoords + 0); |
1186 | 2.40k | oGS.ApplyMatrix(adfCoords + 2); |
1187 | 2.40k | oGS.ApplyMatrix(adfCoords + 4); |
1188 | 2.40k | AddBezierCurve(oCoords, |
1189 | 2.40k | oCoords.empty() |
1190 | 2.40k | ? &adfCoords[0] |
1191 | 2.40k | : &oCoords[oCoords.size() - 2], |
1192 | 2.40k | &adfCoords[0], &adfCoords[2], &adfCoords[4]); |
1193 | 2.40k | } |
1194 | 194k | else if (EQUAL1(szToken, "v")) /* Bezier curve */ |
1195 | 3 | { |
1196 | 3 | double adfCoords[4]; |
1197 | 3 | if (!UnstackTokens(szToken, 4, aszTokenStack, |
1198 | 3 | nTokenStackSize, adfCoords)) |
1199 | 1 | { |
1200 | 1 | CPLDebug( |
1201 | 1 | "PDF", |
1202 | 1 | "Should not happen at line %d: offset %d in stream", |
1203 | 1 | __LINE__, int(pszContent - pszContentIni)); |
1204 | 1 | CPLDebug("PDF", |
1205 | 1 | "ParseContent(), line %d: return at line %d " |
1206 | 1 | "of content stream", |
1207 | 1 | __LINE__, nLineNumber); |
1208 | 1 | return nullptr; |
1209 | 1 | } |
1210 | | |
1211 | 2 | oGS.ApplyMatrix(adfCoords + 0); |
1212 | 2 | oGS.ApplyMatrix(adfCoords + 2); |
1213 | 2 | AddBezierCurve( |
1214 | 2 | oCoords, |
1215 | 2 | oCoords.empty() ? &adfCoords[0] |
1216 | 2 | : &oCoords[oCoords.size() - 2], |
1217 | 2 | oCoords.empty() ? &adfCoords[0] |
1218 | 2 | : &oCoords[oCoords.size() - 2], |
1219 | 2 | &adfCoords[0], &adfCoords[2]); |
1220 | 2 | } |
1221 | 194k | else if (EQUAL1(szToken, "y")) /* Bezier curve */ |
1222 | 1 | { |
1223 | 1 | double adfCoords[4]; |
1224 | 1 | if (!UnstackTokens(szToken, 4, aszTokenStack, |
1225 | 1 | nTokenStackSize, adfCoords)) |
1226 | 0 | { |
1227 | 0 | CPLDebug( |
1228 | 0 | "PDF", |
1229 | 0 | "Should not happen at line %d: offset %d in stream", |
1230 | 0 | __LINE__, int(pszContent - pszContentIni)); |
1231 | 0 | CPLDebug("PDF", |
1232 | 0 | "ParseContent(), line %d: return at line %d " |
1233 | 0 | "of content stream", |
1234 | 0 | __LINE__, nLineNumber); |
1235 | 0 | return nullptr; |
1236 | 0 | } |
1237 | | |
1238 | 1 | oGS.ApplyMatrix(adfCoords + 0); |
1239 | 1 | oGS.ApplyMatrix(adfCoords + 2); |
1240 | 1 | AddBezierCurve(oCoords, |
1241 | 1 | oCoords.empty() |
1242 | 1 | ? &adfCoords[0] |
1243 | 1 | : &oCoords[oCoords.size() - 2], |
1244 | 1 | &adfCoords[0], &adfCoords[2], &adfCoords[2]); |
1245 | 1 | } |
1246 | 194k | else if (EQUAL2(szToken, "re")) /* Rectangle */ |
1247 | 50.6k | { |
1248 | 50.6k | double adfCoords[4]; |
1249 | 50.6k | if (!UnstackTokens(szToken, 4, aszTokenStack, |
1250 | 50.6k | nTokenStackSize, adfCoords)) |
1251 | 3 | { |
1252 | 3 | CPLDebug( |
1253 | 3 | "PDF", |
1254 | 3 | "Should not happen at line %d: offset %d in stream", |
1255 | 3 | __LINE__, int(pszContent - pszContentIni)); |
1256 | 3 | CPLDebug("PDF", |
1257 | 3 | "ParseContent(), line %d: return at line %d " |
1258 | 3 | "of content stream", |
1259 | 3 | __LINE__, nLineNumber); |
1260 | 3 | return nullptr; |
1261 | 3 | } |
1262 | | |
1263 | 50.6k | adfCoords[2] += adfCoords[0]; |
1264 | 50.6k | adfCoords[3] += adfCoords[1]; |
1265 | | |
1266 | 50.6k | oGS.ApplyMatrix(adfCoords); |
1267 | 50.6k | oGS.ApplyMatrix(adfCoords + 2); |
1268 | | |
1269 | 50.6k | if (!oCoords.empty()) |
1270 | 12.0k | bHasMultiPart = TRUE; |
1271 | 50.6k | oCoords.push_back(NEW_SUBPATH); |
1272 | 50.6k | oCoords.push_back(NEW_SUBPATH); |
1273 | 50.6k | oCoords.push_back(adfCoords[0]); |
1274 | 50.6k | oCoords.push_back(adfCoords[1]); |
1275 | 50.6k | oCoords.push_back(adfCoords[2]); |
1276 | 50.6k | oCoords.push_back(adfCoords[1]); |
1277 | 50.6k | oCoords.push_back(adfCoords[2]); |
1278 | 50.6k | oCoords.push_back(adfCoords[3]); |
1279 | 50.6k | oCoords.push_back(adfCoords[0]); |
1280 | 50.6k | oCoords.push_back(adfCoords[3]); |
1281 | 50.6k | oCoords.push_back(CLOSE_SUBPATH); |
1282 | 50.6k | oCoords.push_back(CLOSE_SUBPATH); |
1283 | 50.6k | } |
1284 | | |
1285 | 143k | else if (EQUAL2(szToken, "Do")) |
1286 | 29.1k | { |
1287 | 29.1k | if (nTokenStackSize == 0) |
1288 | 0 | { |
1289 | 0 | CPLDebug("PDF", "not enough arguments for %s", szToken); |
1290 | 0 | CPLDebug("PDF", |
1291 | 0 | "ParseContent(), line %d: return at line %d " |
1292 | 0 | "of content stream", |
1293 | 0 | __LINE__, nLineNumber); |
1294 | 0 | return nullptr; |
1295 | 0 | } |
1296 | | |
1297 | 29.1k | CPLString osObjectName = aszTokenStack[--nTokenStackSize]; |
1298 | | |
1299 | 29.1k | if (osObjectName[0] != '/') |
1300 | 18 | { |
1301 | 18 | CPLDebug( |
1302 | 18 | "PDF", |
1303 | 18 | "Should not happen at line %d: offset %d in stream", |
1304 | 18 | __LINE__, int(pszContent - pszContentIni)); |
1305 | 18 | CPLDebug("PDF", |
1306 | 18 | "ParseContent(), line %d: return at line %d " |
1307 | 18 | "of content stream", |
1308 | 18 | __LINE__, nLineNumber); |
1309 | 18 | return nullptr; |
1310 | 18 | } |
1311 | | |
1312 | 29.0k | if (osObjectName.find("/SymImage") == 0) |
1313 | 4 | { |
1314 | 4 | oCoords.push_back(oGS.adfCM[4] + oGS.adfCM[0] / 2); |
1315 | 4 | oCoords.push_back(oGS.adfCM[5] + oGS.adfCM[3] / 2); |
1316 | | |
1317 | 4 | szToken[0] = '\0'; |
1318 | 4 | nTokenSize = 0; |
1319 | | |
1320 | 4 | if (poCurLayer != nullptr) |
1321 | 0 | bEmitFeature = TRUE; |
1322 | 4 | else |
1323 | 4 | continue; |
1324 | 4 | } |
1325 | 29.0k | else if (poResources == nullptr) |
1326 | 65 | { |
1327 | 65 | szToken[0] = '\0'; |
1328 | 65 | nTokenSize = 0; |
1329 | | |
1330 | 65 | CPLDebug("PDF", "Skipping unknown object %s at line %d", |
1331 | 65 | osObjectName.c_str(), nLineNumber); |
1332 | 65 | continue; |
1333 | 65 | } |
1334 | | |
1335 | 29.0k | if (!bEmitFeature) |
1336 | 29.0k | { |
1337 | 29.0k | GDALPDFObject *poXObject = |
1338 | 29.0k | poResources->GetDictionary()->Get("XObject"); |
1339 | 29.0k | if (poXObject == nullptr || |
1340 | 29.0k | poXObject->GetType() != PDFObjectType_Dictionary) |
1341 | 42 | { |
1342 | 42 | CPLDebug("PDF", |
1343 | 42 | "Should not happen at line %d: offset %d " |
1344 | 42 | "in stream", |
1345 | 42 | __LINE__, int(pszContent - pszContentIni)); |
1346 | 42 | CPLDebug("PDF", |
1347 | 42 | "ParseContent(), line %d: return at line " |
1348 | 42 | "%d of content stream", |
1349 | 42 | __LINE__, nLineNumber); |
1350 | 42 | return nullptr; |
1351 | 42 | } |
1352 | | |
1353 | 28.9k | GDALPDFObject *poObject = |
1354 | 28.9k | poXObject->GetDictionary()->Get( |
1355 | 28.9k | osObjectName.c_str() + 1); |
1356 | 28.9k | if (poObject == nullptr) |
1357 | 846 | { |
1358 | 846 | CPLDebug("PDF", |
1359 | 846 | "Should not happen at line %d: offset %d " |
1360 | 846 | "in stream", |
1361 | 846 | __LINE__, int(pszContent - pszContentIni)); |
1362 | 846 | CPLDebug("PDF", |
1363 | 846 | "ParseContent(), line %d: return at line " |
1364 | 846 | "%d of content stream", |
1365 | 846 | __LINE__, nLineNumber); |
1366 | 846 | return nullptr; |
1367 | 846 | } |
1368 | | |
1369 | 28.1k | int bParseStream = TRUE; |
1370 | 28.1k | GDALPDFObject *poSubResources = nullptr; |
1371 | | /* Check if the object is an image. If so, no need to |
1372 | | * try to parse */ |
1373 | | /* it. */ |
1374 | 28.1k | if (poObject->GetType() == PDFObjectType_Dictionary) |
1375 | 28.1k | { |
1376 | 28.1k | GDALPDFObject *poSubtype = |
1377 | 28.1k | poObject->GetDictionary()->Get("Subtype"); |
1378 | 28.1k | if (poSubtype != nullptr && |
1379 | 28.1k | poSubtype->GetType() == PDFObjectType_Name && |
1380 | 28.1k | poSubtype->GetName() == "Image") |
1381 | 12.7k | { |
1382 | 12.7k | bParseStream = FALSE; |
1383 | 12.7k | } |
1384 | | |
1385 | 28.1k | poSubResources = |
1386 | 28.1k | poObject->GetDictionary()->Get("Resources"); |
1387 | 28.1k | if (poSubResources && poSubResources->GetType() != |
1388 | 15.2k | PDFObjectType_Dictionary) |
1389 | 65 | { |
1390 | 65 | poSubResources = nullptr; |
1391 | 65 | } |
1392 | 28.1k | } |
1393 | | |
1394 | 28.1k | if (bParseStream) |
1395 | 15.3k | { |
1396 | 15.3k | GDALPDFStream *poStream = poObject->GetStream(); |
1397 | 15.3k | if (!poStream) |
1398 | 110 | { |
1399 | 110 | CPLDebug("PDF", |
1400 | 110 | "Should not happen at line %d: offset " |
1401 | 110 | "%d in stream", |
1402 | 110 | __LINE__, |
1403 | 110 | int(pszContent - pszContentIni)); |
1404 | 110 | CPLDebug("PDF", |
1405 | 110 | "ParseContent(), line %d: return at " |
1406 | 110 | "line %d of content stream", |
1407 | 110 | __LINE__, nLineNumber); |
1408 | 110 | return nullptr; |
1409 | 110 | } |
1410 | | |
1411 | 15.2k | OGRPDFLayer *poCurLayerRec = poCurLayer; |
1412 | | |
1413 | 15.2k | if (poObject->GetType() == PDFObjectType_Dictionary) |
1414 | 15.2k | { |
1415 | 15.2k | auto poOC = |
1416 | 15.2k | poObject->GetDictionary()->Get("OC"); |
1417 | 15.2k | if (poOC && |
1418 | 15.2k | poOC->GetType() == |
1419 | 3 | PDFObjectType_Dictionary && |
1420 | 15.2k | poOC->GetRefNum().toBool()) |
1421 | 3 | { |
1422 | 3 | const auto oIterNumGenToLayer = |
1423 | 3 | oMapNumGenToLayer.find( |
1424 | 3 | std::pair(poOC->GetRefNum().toInt(), |
1425 | 3 | poOC->GetRefGen())); |
1426 | 3 | if (oIterNumGenToLayer != |
1427 | 3 | oMapNumGenToLayer.end()) |
1428 | 0 | { |
1429 | 0 | poCurLayerRec = |
1430 | 0 | oIterNumGenToLayer->second; |
1431 | 0 | } |
1432 | 3 | } |
1433 | 15.2k | } |
1434 | | |
1435 | 15.2k | char *pszStr = poStream->GetBytes(); |
1436 | 15.2k | if (pszStr) |
1437 | 14.6k | { |
1438 | 14.6k | CPLDebug("PDF", "Starting parsing %s", |
1439 | 14.6k | osObjectName.c_str()); |
1440 | 14.6k | OGRGeometry *poGeom = ParseContent( |
1441 | 14.6k | pszStr, poSubResources, bCollectAllObjects, |
1442 | 14.6k | false, false, oMapPropertyToLayer, |
1443 | 14.6k | oMapNumGenToLayer, oGS, poCurLayerRec, |
1444 | 14.6k | nRecLevel + 1); |
1445 | 14.6k | CPLDebug("PDF", "End of parsing of %s", |
1446 | 14.6k | osObjectName.c_str()); |
1447 | 14.6k | CPLFree(pszStr); |
1448 | 14.6k | if (poGeom && !bCollectAllObjects) |
1449 | 410 | return poGeom; |
1450 | 14.2k | delete poGeom; |
1451 | 14.2k | } |
1452 | 15.2k | } |
1453 | 28.1k | } |
1454 | 29.0k | } |
1455 | 114k | else if (EQUAL2(szToken, "RG") || EQUAL2(szToken, "rg")) |
1456 | 25.3k | { |
1457 | 25.3k | double *padf = (EQUAL2(szToken, "RG")) |
1458 | 25.3k | ? &oGS.adfStrokeColor[0] |
1459 | 25.3k | : &oGS.adfFillColor[0]; |
1460 | 25.3k | if (!UnstackTokens(szToken, 3, aszTokenStack, |
1461 | 25.3k | nTokenStackSize, padf)) |
1462 | 2 | { |
1463 | 2 | CPLDebug( |
1464 | 2 | "PDF", |
1465 | 2 | "Should not happen at line %d: offset %d in stream", |
1466 | 2 | __LINE__, int(pszContent - pszContentIni)); |
1467 | 2 | CPLDebug("PDF", |
1468 | 2 | "ParseContent(), line %d: return at line %d " |
1469 | 2 | "of content stream", |
1470 | 2 | __LINE__, nLineNumber); |
1471 | 2 | return nullptr; |
1472 | 2 | } |
1473 | 25.3k | } |
1474 | 88.9k | else if (m_oMapOperators.find(szToken) != m_oMapOperators.end()) |
1475 | 77.5k | { |
1476 | 77.5k | int nArgs = m_oMapOperators[szToken]; |
1477 | 77.5k | if (nArgs < 0) |
1478 | 2 | { |
1479 | 11 | while (nTokenStackSize != 0) |
1480 | 9 | { |
1481 | 9 | CPLString osTopToken = |
1482 | 9 | aszTokenStack[--nTokenStackSize]; |
1483 | 9 | if (m_oMapOperators.find(osTopToken) != |
1484 | 9 | m_oMapOperators.end()) |
1485 | 0 | break; |
1486 | 9 | } |
1487 | 2 | } |
1488 | 77.5k | else |
1489 | 77.5k | { |
1490 | 77.5k | if (nArgs > nTokenStackSize) |
1491 | 1 | { |
1492 | 1 | CPLDebug("PDF", "not enough arguments for %s", |
1493 | 1 | szToken); |
1494 | 1 | CPLDebug("PDF", |
1495 | 1 | "ParseContent(), line %d: return at line " |
1496 | 1 | "%d of content stream", |
1497 | 1 | __LINE__, nLineNumber); |
1498 | 1 | return nullptr; |
1499 | 1 | } |
1500 | 77.5k | nTokenStackSize -= nArgs; |
1501 | 77.5k | } |
1502 | 77.5k | } |
1503 | 11.4k | else |
1504 | 11.4k | { |
1505 | 11.4k | PUSH(aszTokenStack, szToken, nTokenSize); |
1506 | 11.4k | } |
1507 | | |
1508 | 4.47M | if (bEmitFeature && poCurLayer != nullptr) |
1509 | 34.8k | { |
1510 | 34.8k | OGRGeometry *poGeom = |
1511 | 34.8k | BuildGeometry(oCoords, bHasFoundFill, bHasMultiPart); |
1512 | 34.8k | bHasFoundFill = FALSE; |
1513 | 34.8k | bHasMultiPart = FALSE; |
1514 | 34.8k | if (poGeom) |
1515 | 34.4k | { |
1516 | 34.4k | OGRFeature *poFeature = |
1517 | 34.4k | new OGRFeature(poCurLayer->GetLayerDefn()); |
1518 | 34.4k | if (m_bSetStyle) |
1519 | 34.4k | { |
1520 | 34.4k | OGRwkbGeometryType eType = |
1521 | 34.4k | wkbFlatten(poGeom->getGeometryType()); |
1522 | 34.4k | if (eType == wkbLineString || |
1523 | 34.4k | eType == wkbMultiLineString) |
1524 | 22.7k | { |
1525 | 22.7k | poFeature->SetStyleString(CPLSPrintf( |
1526 | 22.7k | "PEN(c:#%02X%02X%02X)", |
1527 | 22.7k | static_cast<int>( |
1528 | 22.7k | oGS.adfStrokeColor[0] * 255 + 0.5), |
1529 | 22.7k | static_cast<int>( |
1530 | 22.7k | oGS.adfStrokeColor[1] * 255 + 0.5), |
1531 | 22.7k | static_cast<int>( |
1532 | 22.7k | oGS.adfStrokeColor[2] * 255 + 0.5))); |
1533 | 22.7k | } |
1534 | 11.7k | else if (eType == wkbPolygon || |
1535 | 11.7k | eType == wkbMultiPolygon) |
1536 | 11.0k | { |
1537 | 11.0k | poFeature->SetStyleString(CPLSPrintf( |
1538 | 11.0k | "PEN(c:#%02X%02X%02X);BRUSH(fc:#%02X%02X%" |
1539 | 11.0k | "02X)", |
1540 | 11.0k | static_cast<int>( |
1541 | 11.0k | oGS.adfStrokeColor[0] * 255 + 0.5), |
1542 | 11.0k | static_cast<int>( |
1543 | 11.0k | oGS.adfStrokeColor[1] * 255 + 0.5), |
1544 | 11.0k | static_cast<int>( |
1545 | 11.0k | oGS.adfStrokeColor[2] * 255 + 0.5), |
1546 | 11.0k | static_cast<int>(oGS.adfFillColor[0] * 255 + |
1547 | 11.0k | 0.5), |
1548 | 11.0k | static_cast<int>(oGS.adfFillColor[1] * 255 + |
1549 | 11.0k | 0.5), |
1550 | 11.0k | static_cast<int>(oGS.adfFillColor[2] * 255 + |
1551 | 11.0k | 0.5))); |
1552 | 11.0k | } |
1553 | 34.4k | } |
1554 | 34.4k | poGeom->assignSpatialReference( |
1555 | 34.4k | poCurLayer->GetSpatialRef()); |
1556 | 34.4k | poFeature->SetGeometryDirectly(poGeom); |
1557 | 34.4k | CPL_IGNORE_RET_VAL( |
1558 | 34.4k | poCurLayer->CreateFeature(poFeature)); |
1559 | 34.4k | delete poFeature; |
1560 | 34.4k | } |
1561 | | |
1562 | 34.8k | oCoords.resize(0); |
1563 | 34.8k | } |
1564 | 4.47M | } |
1565 | | |
1566 | 5.05M | szToken[0] = '\0'; |
1567 | 5.05M | nTokenSize = 0; |
1568 | 5.05M | } |
1569 | 35.9M | } |
1570 | | |
1571 | 13.9k | CPLDebug("PDF", "ParseContent(): reached line %d", nLineNumber); |
1572 | 13.9k | if (!oGSStack.empty()) |
1573 | 279 | CPLDebug("PDF", "GSStack not empty"); |
1574 | | |
1575 | 13.9k | if (nTokenStackSize != 0) |
1576 | 459 | { |
1577 | 1.78k | while (nTokenStackSize != 0) |
1578 | 1.33k | { |
1579 | 1.33k | nTokenStackSize--; |
1580 | 1.33k | CPLDebug("PDF", "Remaining values in stack : %s", |
1581 | 1.33k | aszTokenStack[nTokenStackSize]); |
1582 | 1.33k | } |
1583 | 459 | return nullptr; |
1584 | 459 | } |
1585 | | |
1586 | 13.5k | if (bCollectAllObjects) |
1587 | 12.9k | return nullptr; |
1588 | | |
1589 | 576 | return BuildGeometry(oCoords, bHasFoundFill, bHasMultiPart); |
1590 | 13.5k | } |
1591 | | |
1592 | | /************************************************************************/ |
1593 | | /* BuildGeometry() */ |
1594 | | /************************************************************************/ |
1595 | | |
1596 | | OGRGeometry *PDFDataset::BuildGeometry(std::vector<double> &oCoords, |
1597 | | int bHasFoundFill, int bHasMultiPart) |
1598 | 35.4k | { |
1599 | 35.4k | OGRGeometry *poGeom = nullptr; |
1600 | | |
1601 | 35.4k | if (!oCoords.size()) |
1602 | 171 | return nullptr; |
1603 | | |
1604 | 35.2k | if (oCoords.size() == 2) |
1605 | 598 | { |
1606 | 598 | double X, Y; |
1607 | 598 | PDFCoordsToSRSCoords(oCoords[0], oCoords[1], X, Y); |
1608 | 598 | poGeom = new OGRPoint(X, Y); |
1609 | 598 | } |
1610 | 34.6k | else if (!bHasFoundFill) |
1611 | 23.1k | { |
1612 | 23.1k | OGRLineString *poLS = nullptr; |
1613 | 23.1k | OGRMultiLineString *poMLS = nullptr; |
1614 | 23.1k | if (bHasMultiPart) |
1615 | 3.82k | { |
1616 | 3.82k | poMLS = new OGRMultiLineString(); |
1617 | 3.82k | poGeom = poMLS; |
1618 | 3.82k | } |
1619 | | |
1620 | 117k | for (size_t i = 0; i < oCoords.size(); i += 2) |
1621 | 94.1k | { |
1622 | 94.1k | if (oCoords[i] == NEW_SUBPATH && oCoords[i + 1] == NEW_SUBPATH) |
1623 | 24.5k | { |
1624 | 24.5k | if (poMLS) |
1625 | 5.64k | { |
1626 | 5.64k | poLS = new OGRLineString(); |
1627 | 5.64k | poMLS->addGeometryDirectly(poLS); |
1628 | 5.64k | } |
1629 | 18.9k | else |
1630 | 18.9k | { |
1631 | 18.9k | delete poLS; |
1632 | 18.9k | poLS = new OGRLineString(); |
1633 | 18.9k | poGeom = poLS; |
1634 | 18.9k | } |
1635 | 24.5k | } |
1636 | 69.5k | else if (oCoords[i] == CLOSE_SUBPATH && |
1637 | 69.5k | oCoords[i + 1] == CLOSE_SUBPATH) |
1638 | 142 | { |
1639 | 142 | if (poLS && poLS->getNumPoints() >= 2 && |
1640 | 142 | !(poLS->getX(0) == poLS->getX(poLS->getNumPoints() - 1) && |
1641 | 136 | poLS->getY(0) == poLS->getY(poLS->getNumPoints() - 1))) |
1642 | 17 | { |
1643 | 17 | poLS->addPoint(poLS->getX(0), poLS->getY(0)); |
1644 | 17 | } |
1645 | 142 | } |
1646 | 69.3k | else if (oCoords[i] == FILL_SUBPATH && |
1647 | 69.3k | oCoords[i + 1] == FILL_SUBPATH) |
1648 | 0 | { |
1649 | | /* Should not happen */ |
1650 | 0 | } |
1651 | 69.3k | else |
1652 | 69.3k | { |
1653 | 69.3k | if (poLS) |
1654 | 68.4k | { |
1655 | 68.4k | double X, Y; |
1656 | 68.4k | PDFCoordsToSRSCoords(oCoords[i], oCoords[i + 1], X, Y); |
1657 | | |
1658 | 68.4k | poLS->addPoint(X, Y); |
1659 | 68.4k | } |
1660 | 69.3k | } |
1661 | 94.1k | } |
1662 | | |
1663 | | // Recognize points as written by GDAL (ogr-sym-2 : circle (not filled)) |
1664 | 23.1k | OGRGeometry *poCenter = nullptr; |
1665 | 23.1k | if (poCenter == nullptr && poLS != nullptr && |
1666 | 23.1k | poLS->getNumPoints() == 1 + BEZIER_STEPS * 4) |
1667 | 0 | { |
1668 | 0 | poCenter = PDFGetCircleCenter(poLS); |
1669 | 0 | } |
1670 | | |
1671 | | // Recognize points as written by GDAL (ogr-sym-4: square (not filled)) |
1672 | 23.1k | if (poCenter == nullptr && poLS != nullptr && |
1673 | 23.1k | (poLS->getNumPoints() == 4 || poLS->getNumPoints() == 5)) |
1674 | 611 | { |
1675 | 611 | poCenter = PDFGetSquareCenter(poLS); |
1676 | 611 | } |
1677 | | |
1678 | | // Recognize points as written by GDAL (ogr-sym-6: triangle (not |
1679 | | // filled)) |
1680 | 23.1k | if (poCenter == nullptr && poLS != nullptr && |
1681 | 23.1k | (poLS->getNumPoints() == 3 || poLS->getNumPoints() == 4)) |
1682 | 17.9k | { |
1683 | 17.9k | poCenter = PDFGetTriangleCenter(poLS); |
1684 | 17.9k | } |
1685 | | |
1686 | | // Recognize points as written by GDAL (ogr-sym-8: star (not filled)) |
1687 | 23.1k | if (poCenter == nullptr && poLS != nullptr && |
1688 | 23.1k | (poLS->getNumPoints() == 10 || poLS->getNumPoints() == 11)) |
1689 | 0 | { |
1690 | 0 | poCenter = PDFGetStarCenter(poLS); |
1691 | 0 | } |
1692 | | |
1693 | 23.1k | if (poCenter == nullptr && poMLS != nullptr && |
1694 | 23.1k | poMLS->getNumGeometries() == 2) |
1695 | 1.34k | { |
1696 | 1.34k | const OGRLineString *poLS1 = poMLS->getGeometryRef(0); |
1697 | 1.34k | const OGRLineString *poLS2 = poMLS->getGeometryRef(1); |
1698 | | |
1699 | | // Recognize points as written by GDAL (ogr-sym-0: cross (+) ). |
1700 | 1.34k | if (poLS1->getNumPoints() == 2 && poLS2->getNumPoints() == 2 && |
1701 | 1.34k | poLS1->getY(0) == poLS1->getY(1) && |
1702 | 1.34k | poLS2->getX(0) == poLS2->getX(1) && |
1703 | 1.34k | fabs(fabs(poLS1->getX(0) - poLS1->getX(1)) - |
1704 | 0 | fabs(poLS2->getY(0) - poLS2->getY(1))) < EPSILON && |
1705 | 1.34k | fabs((poLS1->getX(0) + poLS1->getX(1)) / 2 - poLS2->getX(0)) < |
1706 | 0 | EPSILON && |
1707 | 1.34k | fabs((poLS2->getY(0) + poLS2->getY(1)) / 2 - poLS1->getY(0)) < |
1708 | 0 | EPSILON) |
1709 | 0 | { |
1710 | 0 | poCenter = new OGRPoint(poLS2->getX(0), poLS1->getY(0)); |
1711 | 0 | } |
1712 | | // Recognize points as written by GDAL (ogr-sym-1: diagcross (X) ). |
1713 | 1.34k | else if (poLS1->getNumPoints() == 2 && poLS2->getNumPoints() == 2 && |
1714 | 1.34k | poLS1->getX(0) == poLS2->getX(0) && |
1715 | 1.34k | poLS1->getY(0) == poLS2->getY(1) && |
1716 | 1.34k | poLS1->getX(1) == poLS2->getX(1) && |
1717 | 1.34k | poLS1->getY(1) == poLS2->getY(0) && |
1718 | 1.34k | fabs(fabs(poLS1->getX(0) - poLS1->getX(1)) - |
1719 | 13 | fabs(poLS1->getY(0) - poLS1->getY(1))) < EPSILON) |
1720 | 13 | { |
1721 | 13 | poCenter = new OGRPoint((poLS1->getX(0) + poLS1->getX(1)) / 2, |
1722 | 13 | (poLS1->getY(0) + poLS1->getY(1)) / 2); |
1723 | 13 | } |
1724 | 1.34k | } |
1725 | | |
1726 | 23.1k | if (poCenter) |
1727 | 15 | { |
1728 | 15 | delete poGeom; |
1729 | 15 | poGeom = poCenter; |
1730 | 15 | } |
1731 | 23.1k | } |
1732 | 11.5k | else |
1733 | 11.5k | { |
1734 | 11.5k | OGRLinearRing *poLS = nullptr; |
1735 | 11.5k | int nPolys = 0; |
1736 | 11.5k | OGRGeometry **papoPoly = nullptr; |
1737 | | |
1738 | 1.29M | for (size_t i = 0; i < oCoords.size(); i += 2) |
1739 | 1.28M | { |
1740 | 1.28M | if (oCoords[i] == NEW_SUBPATH && oCoords[i + 1] == NEW_SUBPATH) |
1741 | 188k | { |
1742 | 188k | if (poLS && poLS->getNumPoints() >= 3) |
1743 | 150k | { |
1744 | 150k | OGRPolygon *poPoly = new OGRPolygon(); |
1745 | 150k | poPoly->addRingDirectly(poLS); |
1746 | 150k | poLS = nullptr; |
1747 | | |
1748 | 150k | papoPoly = static_cast<OGRGeometry **>(CPLRealloc( |
1749 | 150k | papoPoly, (nPolys + 1) * sizeof(OGRGeometry *))); |
1750 | 150k | papoPoly[nPolys++] = poPoly; |
1751 | 150k | } |
1752 | 188k | delete poLS; |
1753 | 188k | poLS = new OGRLinearRing(); |
1754 | 188k | } |
1755 | 1.09M | else if ((oCoords[i] == CLOSE_SUBPATH && |
1756 | 1.09M | oCoords[i + 1] == CLOSE_SUBPATH) || |
1757 | 1.09M | (oCoords[i] == FILL_SUBPATH && |
1758 | 1.07M | oCoords[i + 1] == FILL_SUBPATH)) |
1759 | 36.6k | { |
1760 | 36.6k | if (poLS) |
1761 | 36.2k | { |
1762 | 36.2k | poLS->closeRings(); |
1763 | | |
1764 | 36.2k | std::unique_ptr<OGRPoint> poCenter; |
1765 | | |
1766 | 36.2k | if (nPolys == 0 && poLS && |
1767 | 36.2k | poLS->getNumPoints() == 1 + BEZIER_STEPS * 4) |
1768 | 127 | { |
1769 | | // Recognize points as written by GDAL (ogr-sym-3 : |
1770 | | // circle (filled)) |
1771 | 127 | poCenter.reset(PDFGetCircleCenter(poLS)); |
1772 | 127 | } |
1773 | | |
1774 | 36.2k | if (nPolys == 0 && poCenter == nullptr && poLS && |
1775 | 36.2k | poLS->getNumPoints() == 5) |
1776 | 11.0k | { |
1777 | | // Recognize points as written by GDAL (ogr-sym-5: |
1778 | | // square (filled)) |
1779 | 11.0k | poCenter.reset(PDFGetSquareCenter(poLS)); |
1780 | | |
1781 | | /* ESRI points */ |
1782 | 11.0k | if (poCenter == nullptr && oCoords.size() == 14 && |
1783 | 11.0k | poLS->getY(0) == poLS->getY(1) && |
1784 | 11.0k | poLS->getX(1) == poLS->getX(2) && |
1785 | 11.0k | poLS->getY(2) == poLS->getY(3) && |
1786 | 11.0k | poLS->getX(3) == poLS->getX(0)) |
1787 | 14 | { |
1788 | 14 | poCenter.reset(new OGRPoint( |
1789 | 14 | (poLS->getX(0) + poLS->getX(1)) / 2, |
1790 | 14 | (poLS->getY(0) + poLS->getY(2)) / 2)); |
1791 | 14 | } |
1792 | 11.0k | } |
1793 | | // Recognize points as written by GDAL (ogr-sym-7: triangle |
1794 | | // (filled)) |
1795 | 25.2k | else if (nPolys == 0 && poLS && poLS->getNumPoints() == 4) |
1796 | 3 | { |
1797 | 3 | poCenter.reset(PDFGetTriangleCenter(poLS)); |
1798 | 3 | } |
1799 | | // Recognize points as written by GDAL (ogr-sym-9: star |
1800 | | // (filled)) |
1801 | 25.2k | else if (nPolys == 0 && poLS && poLS->getNumPoints() == 11) |
1802 | 1 | { |
1803 | 1 | poCenter.reset(PDFGetStarCenter(poLS)); |
1804 | 1 | } |
1805 | | |
1806 | 36.2k | if (poCenter) |
1807 | 135 | { |
1808 | 135 | delete poGeom; |
1809 | 135 | poGeom = poCenter.release(); |
1810 | 135 | break; |
1811 | 135 | } |
1812 | | |
1813 | 36.1k | if (poLS->getNumPoints() >= 3) |
1814 | 18.2k | { |
1815 | 18.2k | OGRPolygon *poPoly = new OGRPolygon(); |
1816 | 18.2k | poPoly->addRingDirectly(poLS); |
1817 | 18.2k | poLS = nullptr; |
1818 | | |
1819 | 18.2k | papoPoly = static_cast<OGRGeometry **>(CPLRealloc( |
1820 | 18.2k | papoPoly, (nPolys + 1) * sizeof(OGRGeometry *))); |
1821 | 18.2k | papoPoly[nPolys++] = poPoly; |
1822 | 18.2k | } |
1823 | 17.8k | else |
1824 | 17.8k | { |
1825 | 17.8k | delete poLS; |
1826 | 17.8k | poLS = nullptr; |
1827 | 17.8k | } |
1828 | 36.1k | } |
1829 | 36.6k | } |
1830 | 1.06M | else |
1831 | 1.06M | { |
1832 | 1.06M | if (poLS) |
1833 | 1.05M | { |
1834 | 1.05M | double X, Y; |
1835 | 1.05M | PDFCoordsToSRSCoords(oCoords[i], oCoords[i + 1], X, Y); |
1836 | | |
1837 | 1.05M | poLS->addPoint(X, Y); |
1838 | 1.05M | } |
1839 | 1.06M | } |
1840 | 1.28M | } |
1841 | | |
1842 | 11.5k | delete poLS; |
1843 | | |
1844 | 11.5k | int bIsValidGeometry; |
1845 | 11.5k | if (nPolys == 2 && |
1846 | 11.5k | papoPoly[0]->toPolygon()->getNumInteriorRings() == 0 && |
1847 | 11.5k | papoPoly[1]->toPolygon()->getNumInteriorRings() == 0) |
1848 | 67 | { |
1849 | 67 | OGRLinearRing *poRing0 = |
1850 | 67 | papoPoly[0]->toPolygon()->getExteriorRing(); |
1851 | 67 | OGRLinearRing *poRing1 = |
1852 | 67 | papoPoly[1]->toPolygon()->getExteriorRing(); |
1853 | 67 | if (poRing0->getNumPoints() == poRing1->getNumPoints()) |
1854 | 46 | { |
1855 | 46 | int bSameRing = TRUE; |
1856 | 56 | for (int i = 0; i < poRing0->getNumPoints(); i++) |
1857 | 54 | { |
1858 | 54 | if (poRing0->getX(i) != poRing1->getX(i)) |
1859 | 18 | { |
1860 | 18 | bSameRing = FALSE; |
1861 | 18 | break; |
1862 | 18 | } |
1863 | 36 | if (poRing0->getY(i) != poRing1->getY(i)) |
1864 | 26 | { |
1865 | 26 | bSameRing = FALSE; |
1866 | 26 | break; |
1867 | 26 | } |
1868 | 36 | } |
1869 | | |
1870 | | /* Just keep on ring if they are identical */ |
1871 | 46 | if (bSameRing) |
1872 | 2 | { |
1873 | 2 | delete papoPoly[1]; |
1874 | 2 | nPolys = 1; |
1875 | 2 | } |
1876 | 46 | } |
1877 | 67 | } |
1878 | 11.5k | if (nPolys) |
1879 | 11.4k | { |
1880 | 11.4k | poGeom = OGRGeometryFactory::organizePolygons( |
1881 | 11.4k | papoPoly, nPolys, &bIsValidGeometry, nullptr); |
1882 | 11.4k | } |
1883 | 11.5k | CPLFree(papoPoly); |
1884 | 11.5k | } |
1885 | | |
1886 | 35.2k | return poGeom; |
1887 | 35.4k | } |
1888 | | |
1889 | | /************************************************************************/ |
1890 | | /* ExploreContents() */ |
1891 | | /************************************************************************/ |
1892 | | |
1893 | | void PDFDataset::ExploreContents(GDALPDFObject *poObj, |
1894 | | GDALPDFObject *poResources, int nDepth, |
1895 | | int &nVisited, bool &bStop) |
1896 | 427 | { |
1897 | 427 | std::map<CPLString, OGRPDFLayer *> oMapPropertyToLayer; |
1898 | 427 | if (nDepth == 10 || nVisited == 1000) |
1899 | 0 | { |
1900 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1901 | 0 | "ExploreContents(): too deep exploration or too many items"); |
1902 | 0 | bStop = true; |
1903 | 0 | return; |
1904 | 0 | } |
1905 | 427 | if (bStop) |
1906 | 0 | return; |
1907 | | |
1908 | 427 | if (poObj->GetType() == PDFObjectType_Array) |
1909 | 3 | { |
1910 | 3 | GDALPDFArray *poArray = poObj->GetArray(); |
1911 | 27 | for (int i = 0; i < poArray->GetLength(); i++) |
1912 | 24 | { |
1913 | 24 | GDALPDFObject *poSubObj = poArray->Get(i); |
1914 | 24 | if (poSubObj) |
1915 | 8 | { |
1916 | 8 | nVisited++; |
1917 | 8 | ExploreContents(poSubObj, poResources, nDepth + 1, nVisited, |
1918 | 8 | bStop); |
1919 | 8 | if (bStop) |
1920 | 0 | return; |
1921 | 8 | } |
1922 | 24 | } |
1923 | 3 | } |
1924 | | |
1925 | 427 | if (poObj->GetType() != PDFObjectType_Dictionary) |
1926 | 3 | return; |
1927 | | |
1928 | 424 | GDALPDFStream *poStream = poObj->GetStream(); |
1929 | 424 | if (!poStream) |
1930 | 2 | return; |
1931 | | |
1932 | 422 | char *pszStr = poStream->GetBytes(); |
1933 | 422 | if (!pszStr) |
1934 | 1 | return; |
1935 | | |
1936 | 421 | const char *pszMCID = pszStr; |
1937 | 1.01k | while ((pszMCID = strstr(pszMCID, "/MCID")) != nullptr) |
1938 | 596 | { |
1939 | 596 | const char *pszBDC = strstr(pszMCID, "BDC"); |
1940 | 596 | if (pszBDC) |
1941 | 595 | { |
1942 | | /* Hack for |
1943 | | * http://www.avenza.com/sites/default/files/spatialpdf/US_County_Populations.pdf |
1944 | | */ |
1945 | | /* FIXME: that logic is too fragile. */ |
1946 | 595 | const char *pszStartParsing = pszBDC; |
1947 | 595 | const char *pszAfterBDC = pszBDC + 3; |
1948 | 595 | int bMatchQ = FALSE; |
1949 | 1.18k | while (pszAfterBDC[0] == ' ' || pszAfterBDC[0] == '\r' || |
1950 | 1.18k | pszAfterBDC[0] == '\n') |
1951 | 594 | pszAfterBDC++; |
1952 | 595 | if (STARTS_WITH(pszAfterBDC, "0 0 m")) |
1953 | 0 | { |
1954 | 0 | const char *pszLastq = pszBDC; |
1955 | 0 | while (pszLastq > pszStr && *pszLastq != 'q') |
1956 | 0 | pszLastq--; |
1957 | |
|
1958 | 0 | if (pszLastq > pszStr && *pszLastq == 'q' && |
1959 | 0 | (pszLastq[-1] == ' ' || pszLastq[-1] == '\r' || |
1960 | 0 | pszLastq[-1] == '\n') && |
1961 | 0 | (pszLastq[1] == ' ' || pszLastq[1] == '\r' || |
1962 | 0 | pszLastq[1] == '\n')) |
1963 | 0 | { |
1964 | 0 | pszStartParsing = pszLastq; |
1965 | 0 | bMatchQ = TRUE; |
1966 | 0 | } |
1967 | 0 | } |
1968 | | |
1969 | 595 | int nMCID = atoi(pszMCID + 6); |
1970 | 595 | if (GetGeometryFromMCID(nMCID) == nullptr) |
1971 | 595 | { |
1972 | 595 | OGRGeometry *poGeom = ParseContent( |
1973 | 595 | pszStartParsing, poResources, false, !bMatchQ, bMatchQ, |
1974 | 595 | oMapPropertyToLayer, {}, GraphicState(), nullptr, 0); |
1975 | 595 | if (poGeom != nullptr) |
1976 | 459 | { |
1977 | | /* Save geometry in map */ |
1978 | 459 | m_oMapMCID[nMCID] = poGeom; |
1979 | 459 | } |
1980 | 595 | } |
1981 | 595 | } |
1982 | 596 | pszMCID += 5; |
1983 | 596 | } |
1984 | 421 | CPLFree(pszStr); |
1985 | 421 | } |
1986 | | |
1987 | | /************************************************************************/ |
1988 | | /* ExploreContentsNonStructured() */ |
1989 | | /************************************************************************/ |
1990 | | |
1991 | | void PDFDataset::ExploreContentsNonStructuredInternal( |
1992 | | GDALPDFObject *poContents, GDALPDFObject *poResources, |
1993 | | const std::map<CPLString, OGRPDFLayer *> &oMapPropertyToLayer, |
1994 | | const std::map<std::pair<int, int>, OGRPDFLayer *> &oMapNumGenToLayer, |
1995 | | OGRPDFLayer *poSingleLayer) |
1996 | 563 | { |
1997 | 563 | if (poContents->GetType() == PDFObjectType_Array) |
1998 | 226 | { |
1999 | 226 | GDALPDFArray *poArray = poContents->GetArray(); |
2000 | 226 | char *pszConcatStr = nullptr; |
2001 | 226 | size_t nConcatLen = 0; |
2002 | 780 | for (int i = 0; i < poArray->GetLength(); i++) |
2003 | 722 | { |
2004 | 722 | GDALPDFObject *poObj = poArray->Get(i); |
2005 | 722 | if (poObj == nullptr || |
2006 | 722 | poObj->GetType() != PDFObjectType_Dictionary) |
2007 | 137 | break; |
2008 | 585 | GDALPDFStream *poStream = poObj->GetStream(); |
2009 | 585 | if (!poStream) |
2010 | 1 | break; |
2011 | 584 | char *pszStr = poStream->GetBytes(); |
2012 | 584 | if (!pszStr) |
2013 | 30 | break; |
2014 | 554 | const size_t nLen = strlen(pszStr); |
2015 | 554 | char *pszConcatStrNew = static_cast<char *>( |
2016 | 554 | CPLRealloc(pszConcatStr, nConcatLen + nLen + 1)); |
2017 | 554 | if (pszConcatStrNew == nullptr) |
2018 | 0 | { |
2019 | 0 | CPLFree(pszStr); |
2020 | 0 | break; |
2021 | 0 | } |
2022 | 554 | pszConcatStr = pszConcatStrNew; |
2023 | 554 | memcpy(pszConcatStr + nConcatLen, pszStr, nLen + 1); |
2024 | 554 | nConcatLen += nLen; |
2025 | 554 | CPLFree(pszStr); |
2026 | 554 | } |
2027 | 226 | if (pszConcatStr) |
2028 | 87 | ParseContent(pszConcatStr, poResources, poResources != nullptr, |
2029 | 87 | false, false, oMapPropertyToLayer, oMapNumGenToLayer, |
2030 | 87 | GraphicState(), poSingleLayer, 0); |
2031 | 226 | CPLFree(pszConcatStr); |
2032 | 226 | return; |
2033 | 226 | } |
2034 | | |
2035 | 337 | if (poContents->GetType() != PDFObjectType_Dictionary) |
2036 | 0 | return; |
2037 | | |
2038 | 337 | GDALPDFStream *poStream = poContents->GetStream(); |
2039 | 337 | if (!poStream) |
2040 | 22 | return; |
2041 | | |
2042 | 315 | char *pszStr = poStream->GetBytes(); |
2043 | 315 | if (!pszStr) |
2044 | 11 | return; |
2045 | 304 | ParseContent(pszStr, poResources, poResources != nullptr, false, false, |
2046 | 304 | oMapPropertyToLayer, oMapNumGenToLayer, GraphicState(), |
2047 | 304 | poSingleLayer, 0); |
2048 | 304 | CPLFree(pszStr); |
2049 | 304 | } |
2050 | | |
2051 | | /************************************************************************/ |
2052 | | /* ExploreResourceProperty() */ |
2053 | | /************************************************************************/ |
2054 | | |
2055 | | static void ExploreResourceProperty( |
2056 | | const char *pszKey, GDALPDFObject *poObj, const std::string &osType, |
2057 | | const std::map<std::pair<int, int>, OGRPDFLayer *> &oMapNumGenToLayer, |
2058 | | std::map<CPLString, OGRPDFLayer *> &oMapPropertyToLayer, int nRecLevel) |
2059 | 75.9k | { |
2060 | 75.9k | if (nRecLevel == 2) |
2061 | 0 | return; |
2062 | | |
2063 | 75.9k | if (osType == "OCG" && poObj->GetRefNum().toBool()) |
2064 | 742 | { |
2065 | 742 | const auto oIterNumGenToLayer = oMapNumGenToLayer.find( |
2066 | 742 | std::pair(poObj->GetRefNum().toInt(), poObj->GetRefGen())); |
2067 | 742 | if (oIterNumGenToLayer != oMapNumGenToLayer.end()) |
2068 | 355 | { |
2069 | 355 | auto poLayer = oIterNumGenToLayer->second; |
2070 | | #ifdef DEBUG_VERBOSE |
2071 | | CPLDebug("PDF", "Associating OCG %s to layer %s", pszKey, |
2072 | | poLayer->GetName()); |
2073 | | #endif |
2074 | 355 | oMapPropertyToLayer[pszKey] = poLayer; |
2075 | 355 | } |
2076 | 387 | else |
2077 | 387 | { |
2078 | 387 | CPLDebug("PDF", |
2079 | 387 | "Resource.Properties[%s] referencing " |
2080 | 387 | "OGC %d not tied with a layer", |
2081 | 387 | pszKey, poObj->GetRefNum().toInt()); |
2082 | 387 | } |
2083 | 742 | } |
2084 | 75.1k | else if (osType == "OCMD") |
2085 | 74.8k | { |
2086 | | // Optional Content Group Membership Dictionary |
2087 | | // Deal with constructs like |
2088 | | /* |
2089 | | Item[0] : MC0 |
2090 | | Type = dictionary, Num = 331, Gen = 0 |
2091 | | Item[0] : OCGs |
2092 | | Type = array |
2093 | | Item[0]: |
2094 | | Type = dictionary, Num = 251, Gen = 0 |
2095 | | Item[0] : Intent = View (name) |
2096 | | Item[1] : Name = Orthoimage (string) |
2097 | | Item[2] : Type = OCG (name) |
2098 | | Item[1]: |
2099 | | Type = dictionary, Num = 250, Gen = 0 |
2100 | | Item[0] : Intent = View (name) |
2101 | | Item[1] : Name = Images (string) |
2102 | | Item[2] : Type = OCG (name) |
2103 | | Item[1] : P = AllOn (name) |
2104 | | Item[2] : Type = OCMD (name) |
2105 | | */ |
2106 | | // where the OCG Orthoimage is actually a child |
2107 | | // of Images (which will be named Orthoimage.Images) |
2108 | | // In which case we only associate MC0 to |
2109 | | // Orthoimage.Images |
2110 | | // Cf https://github.com/OSGeo/gdal/issues/8372 |
2111 | | // and https://prd-tnm.s3.amazonaws.com/StagedProducts/Maps/USTopo/PDF/ID/ID_Big_Baldy_20200409_TM_geo.pdf |
2112 | 74.8k | auto poOCGs = poObj->GetDictionary()->Get("OCGs"); |
2113 | 74.8k | if (poOCGs && poOCGs->GetType() == PDFObjectType_Array) |
2114 | 74.6k | { |
2115 | 74.6k | auto poOCGsArray = poOCGs->GetArray(); |
2116 | 74.6k | const int nLength = poOCGsArray->GetLength(); |
2117 | 74.6k | size_t nMaxNameLength = 0; |
2118 | 74.6k | OGRPDFLayer *poCandidateLayer = nullptr; |
2119 | 74.6k | std::vector<std::string> aosLayerNames; |
2120 | 149k | for (int i = 0; i < nLength; ++i) |
2121 | 75.3k | { |
2122 | 75.3k | auto poOCG = poOCGsArray->Get(i); |
2123 | 75.3k | if (poOCG && poOCG->GetType() == PDFObjectType_Dictionary) |
2124 | 67.5k | { |
2125 | 67.5k | auto poP = poOCG->GetDictionary()->Get("P"); |
2126 | 67.5k | if (poP && poP->GetType() == PDFObjectType_Name) |
2127 | 0 | { |
2128 | | // Visibility Policy |
2129 | 0 | const auto &osP = poP->GetName(); |
2130 | 0 | if (osP != "AllOn" && osP != "AnyOn") |
2131 | 0 | { |
2132 | 0 | CPLDebug("PDF", |
2133 | 0 | "Resource.Properties[%s] " |
2134 | 0 | "has unhandled visibility policy %s", |
2135 | 0 | pszKey, osP.c_str()); |
2136 | 0 | } |
2137 | 0 | } |
2138 | 67.5k | auto poOCGType = poOCG->GetDictionary()->Get("Type"); |
2139 | 67.5k | if (poOCGType && poOCGType->GetType() == PDFObjectType_Name) |
2140 | 66.9k | { |
2141 | 66.9k | const std::string &osOCGType = poOCGType->GetName(); |
2142 | 66.9k | if (osOCGType == "OCG" && poOCG->GetRefNum().toBool()) |
2143 | 66.5k | { |
2144 | 66.5k | const auto oIterNumGenToLayer = |
2145 | 66.5k | oMapNumGenToLayer.find( |
2146 | 66.5k | std::pair(poOCG->GetRefNum().toInt(), |
2147 | 66.5k | poOCG->GetRefGen())); |
2148 | 66.5k | if (oIterNumGenToLayer != oMapNumGenToLayer.end()) |
2149 | 45.5k | { |
2150 | 45.5k | auto poLayer = oIterNumGenToLayer->second; |
2151 | 45.5k | aosLayerNames.emplace_back(poLayer->GetName()); |
2152 | 45.5k | if (strlen(poLayer->GetName()) > nMaxNameLength) |
2153 | 45.5k | { |
2154 | 45.5k | nMaxNameLength = strlen(poLayer->GetName()); |
2155 | 45.5k | poCandidateLayer = poLayer; |
2156 | 45.5k | } |
2157 | 45.5k | } |
2158 | 21.0k | else |
2159 | 21.0k | { |
2160 | 21.0k | CPLDebug("PDF", |
2161 | 21.0k | "Resource.Properties[%s][%d] " |
2162 | 21.0k | "referencing OGC %d not tied with " |
2163 | 21.0k | "a layer", |
2164 | 21.0k | pszKey, i, poOCG->GetRefNum().toInt()); |
2165 | 21.0k | } |
2166 | 66.5k | } |
2167 | 399 | else |
2168 | 399 | { |
2169 | 399 | CPLDebug( |
2170 | 399 | "PDF", |
2171 | 399 | "Resource.Properties[%s][%d] has unhandled " |
2172 | 399 | "Type member: %s", |
2173 | 399 | pszKey, i, osOCGType.c_str()); |
2174 | 399 | } |
2175 | 66.9k | } |
2176 | 67.5k | } |
2177 | 75.3k | } |
2178 | | |
2179 | 74.6k | if (!aosLayerNames.empty()) |
2180 | 45.5k | { |
2181 | | // Sort layer names and if each one starts |
2182 | | // with the previous ones, then the OCGs |
2183 | | // are part of a hierarchy, and we can |
2184 | | // associate the property name with the |
2185 | | // last one. |
2186 | 45.5k | std::sort(aosLayerNames.begin(), aosLayerNames.end()); |
2187 | 45.5k | bool bOK = true; |
2188 | 45.5k | for (size_t i = 1; i < aosLayerNames.size(); ++i) |
2189 | 0 | { |
2190 | 0 | if (aosLayerNames[i].find(aosLayerNames[i - 1]) != 0) |
2191 | 0 | { |
2192 | 0 | bOK = false; |
2193 | 0 | break; |
2194 | 0 | } |
2195 | 0 | } |
2196 | 45.5k | if (bOK) |
2197 | 45.5k | { |
2198 | 45.5k | CPLAssert(poCandidateLayer); |
2199 | | #ifdef DEBUG_VERBOSE |
2200 | | CPLDebug("PDF", "Associating OCG %s to layer %s", pszKey, |
2201 | | poCandidateLayer->GetName()); |
2202 | | #endif |
2203 | 45.5k | oMapPropertyToLayer[pszKey] = poCandidateLayer; |
2204 | 45.5k | } |
2205 | 0 | else |
2206 | 0 | { |
2207 | 0 | CPLDebug("PDF", |
2208 | 0 | "Resource.Properties[%s] " |
2209 | 0 | "contains a OCMD that cannot " |
2210 | 0 | "be mapped to a single layer", |
2211 | 0 | pszKey); |
2212 | 0 | } |
2213 | 45.5k | } |
2214 | 29.0k | else |
2215 | 29.0k | { |
2216 | 29.0k | CPLDebug("PDF", |
2217 | 29.0k | "Resource.Properties[%s] contains " |
2218 | 29.0k | "a OCMD without OCGs", |
2219 | 29.0k | pszKey); |
2220 | 29.0k | } |
2221 | 74.6k | } |
2222 | 234 | else if (poOCGs && poOCGs->GetType() == PDFObjectType_Dictionary) |
2223 | 2 | { |
2224 | 2 | auto poOGGsType = poOCGs->GetDictionary()->Get("Type"); |
2225 | 2 | if (poOGGsType && poOGGsType->GetType() == PDFObjectType_Name) |
2226 | 2 | { |
2227 | 2 | ExploreResourceProperty(pszKey, poOCGs, poOGGsType->GetName(), |
2228 | 2 | oMapNumGenToLayer, oMapPropertyToLayer, |
2229 | 2 | nRecLevel + 1); |
2230 | 2 | } |
2231 | 0 | else |
2232 | 0 | { |
2233 | 0 | CPLDebug("PDF", |
2234 | 0 | "Resource.Properties[%s] contains a OGCs member with " |
2235 | 0 | "no Type member", |
2236 | 0 | pszKey); |
2237 | 0 | } |
2238 | 2 | } |
2239 | 232 | else if (poOCGs) |
2240 | 29 | { |
2241 | 29 | CPLDebug("PDF", |
2242 | 29 | "Resource.Properties[%s] contains a OCMD " |
2243 | 29 | "with a OGCs member of unhandled type: %s", |
2244 | 29 | pszKey, poOCGs->GetTypeName()); |
2245 | 29 | } |
2246 | 203 | else |
2247 | 203 | { |
2248 | | // Could have a VE (visibility expression) |
2249 | | // expression instead, but we don't handle that |
2250 | 203 | CPLDebug("PDF", |
2251 | 203 | "Resource.Properties[%s] contains a " |
2252 | 203 | "OCMD with a missing OGC (perhaps has a VE?)", |
2253 | 203 | pszKey); |
2254 | 203 | } |
2255 | 74.8k | } |
2256 | 346 | else |
2257 | 346 | { |
2258 | 346 | CPLDebug("PDF", |
2259 | 346 | "Resource.Properties[%s] has unhandled " |
2260 | 346 | "Type member: %s", |
2261 | 346 | pszKey, osType.c_str()); |
2262 | 346 | } |
2263 | 75.9k | } |
2264 | | |
2265 | | /************************************************************************/ |
2266 | | /* ExploreContentsNonStructured() */ |
2267 | | /************************************************************************/ |
2268 | | |
2269 | | void PDFDataset::ExploreContentsNonStructured(GDALPDFObject *poContents, |
2270 | | GDALPDFObject *poResources) |
2271 | 1.83k | { |
2272 | 1.83k | std::map<CPLString, OGRPDFLayer *> oMapPropertyToLayer; |
2273 | 1.83k | std::map<std::pair<int, int>, OGRPDFLayer *> oMapNumGenToLayer; |
2274 | | |
2275 | 1.83k | const auto BuildMapNumGenToLayer = [this, &oMapNumGenToLayer]() |
2276 | 1.83k | { |
2277 | 673 | for (const auto &oLayerWithref : m_aoLayerWithRef) |
2278 | 443k | { |
2279 | 443k | CPLString osSanitizedName( |
2280 | 443k | PDFSanitizeLayerName(oLayerWithref.osName)); |
2281 | | |
2282 | 443k | OGRPDFLayer *poPDFLayer = dynamic_cast<OGRPDFLayer *>( |
2283 | 443k | GetLayerByName(osSanitizedName.c_str())); |
2284 | 443k | if (!poPDFLayer) |
2285 | 24.4k | { |
2286 | 24.4k | auto poSRSOri = GetSpatialRef(); |
2287 | 24.4k | OGRSpatialReference *poSRS = |
2288 | 24.4k | poSRSOri ? poSRSOri->Clone() : nullptr; |
2289 | 24.4k | auto poPDFLayerUniquePtr = std::make_unique<OGRPDFLayer>( |
2290 | 24.4k | this, osSanitizedName.c_str(), poSRS, wkbUnknown); |
2291 | 24.4k | if (poSRS) |
2292 | 134 | poSRS->Release(); |
2293 | | |
2294 | 24.4k | m_apoLayers.emplace_back(std::move(poPDFLayerUniquePtr)); |
2295 | 24.4k | poPDFLayer = m_apoLayers.back().get(); |
2296 | 24.4k | } |
2297 | | |
2298 | 443k | oMapNumGenToLayer[std::pair(oLayerWithref.nOCGNum.toInt(), |
2299 | 443k | oLayerWithref.nOCGGen)] = poPDFLayer; |
2300 | 443k | } |
2301 | 673 | }; |
2302 | | |
2303 | 1.83k | if (poResources != nullptr && |
2304 | 1.83k | poResources->GetType() == PDFObjectType_Dictionary) |
2305 | 1.83k | { |
2306 | 1.83k | auto poResourcesDict = poResources->GetDictionary(); |
2307 | 1.83k | GDALPDFObject *poTopProperties = poResourcesDict->Get("Properties"); |
2308 | 1.83k | if (poTopProperties != nullptr && |
2309 | 1.83k | poTopProperties->GetType() == PDFObjectType_Dictionary) |
2310 | 663 | { |
2311 | 663 | BuildMapNumGenToLayer(); |
2312 | | |
2313 | 663 | for (const auto &[osKey, poObj] : |
2314 | 663 | poTopProperties->GetDictionary()->GetValues()) |
2315 | 78.4k | { |
2316 | 78.4k | const char *pszKey = osKey.c_str(); |
2317 | 78.4k | if (poObj->GetType() == PDFObjectType_Dictionary) |
2318 | 76.6k | { |
2319 | 76.6k | auto poType = poObj->GetDictionary()->Get("Type"); |
2320 | 76.6k | if (poType && poType->GetType() == PDFObjectType_Name) |
2321 | 75.9k | { |
2322 | 75.9k | const auto &osType = poType->GetName(); |
2323 | 75.9k | ExploreResourceProperty(pszKey, poObj, osType, |
2324 | 75.9k | oMapNumGenToLayer, |
2325 | 75.9k | oMapPropertyToLayer, 0); |
2326 | 75.9k | } |
2327 | 769 | else |
2328 | 769 | { |
2329 | 769 | CPLDebug("PDF", |
2330 | 769 | "Resource.Properties[%s] has no Type member", |
2331 | 769 | pszKey); |
2332 | 769 | } |
2333 | 76.6k | } |
2334 | 78.4k | } |
2335 | 663 | } |
2336 | 1.17k | else |
2337 | 1.17k | { |
2338 | | // Code path taken for datasets mentioned at https://github.com/OSGeo/gdal/issues/9870 |
2339 | | // generated by ArcGIS 12.9 |
2340 | 1.17k | const auto poXObject = poResourcesDict->Get("XObject"); |
2341 | 1.17k | if (poXObject && poXObject->GetType() == PDFObjectType_Dictionary) |
2342 | 810 | { |
2343 | 810 | for (const auto &oNameObjectPair : |
2344 | 810 | poXObject->GetDictionary()->GetValues()) |
2345 | 1.56k | { |
2346 | 1.56k | const auto poProperties = |
2347 | 1.56k | oNameObjectPair.second->LookupObject( |
2348 | 1.56k | "Resources.Properties"); |
2349 | 1.56k | if (poProperties && |
2350 | 1.56k | poProperties->GetType() == PDFObjectType_Dictionary) |
2351 | 10 | { |
2352 | 10 | BuildMapNumGenToLayer(); |
2353 | | |
2354 | 10 | const auto &oMap = |
2355 | 10 | poProperties->GetDictionary()->GetValues(); |
2356 | 10 | for (const auto &[osKey, poObj] : oMap) |
2357 | 12 | { |
2358 | 12 | const char *pszKey = osKey.c_str(); |
2359 | 12 | if (poObj->GetType() == PDFObjectType_Dictionary) |
2360 | 10 | { |
2361 | 10 | GDALPDFObject *poType = |
2362 | 10 | poObj->GetDictionary()->Get("Type"); |
2363 | 10 | if (poType && |
2364 | 10 | poType->GetType() == PDFObjectType_Name) |
2365 | 5 | { |
2366 | 5 | const auto &osType = poType->GetName(); |
2367 | 5 | ExploreResourceProperty( |
2368 | 5 | pszKey, poObj, osType, |
2369 | 5 | oMapNumGenToLayer, oMapPropertyToLayer, |
2370 | 5 | 0); |
2371 | 5 | } |
2372 | 10 | } |
2373 | 12 | } |
2374 | | |
2375 | 10 | break; |
2376 | 10 | } |
2377 | 1.56k | } |
2378 | 810 | } |
2379 | 1.17k | } |
2380 | 1.83k | } |
2381 | | |
2382 | 1.83k | OGRPDFLayer *poSingleLayer = nullptr; |
2383 | 1.83k | if (m_apoLayers.empty()) |
2384 | 1.27k | { |
2385 | 1.27k | const char *pszReadNonStructured = |
2386 | 1.27k | CPLGetConfigOption("OGR_PDF_READ_NON_STRUCTURED", nullptr); |
2387 | 1.27k | if (pszReadNonStructured && CPLTestBool(pszReadNonStructured)) |
2388 | 0 | { |
2389 | 0 | auto poLayer = std::make_unique<OGRPDFLayer>(this, "content", |
2390 | 0 | nullptr, wkbUnknown); |
2391 | 0 | m_apoLayers.emplace_back(std::move(poLayer)); |
2392 | 0 | poSingleLayer = m_apoLayers.back().get(); |
2393 | 0 | } |
2394 | 1.27k | else |
2395 | 1.27k | { |
2396 | 1.27k | if (!pszReadNonStructured) |
2397 | 1.27k | { |
2398 | 1.27k | CPLDebug( |
2399 | 1.27k | "PDF", |
2400 | 1.27k | "No structured content nor PDF layers detected, hence " |
2401 | 1.27k | "vector content detection is disabled. You may force " |
2402 | 1.27k | "vector content detection by setting the " |
2403 | 1.27k | "OGR_PDF_READ_NON_STRUCTURED configuration option to YES"); |
2404 | 1.27k | } |
2405 | 1.27k | return; |
2406 | 1.27k | } |
2407 | 1.27k | } |
2408 | | |
2409 | 563 | ExploreContentsNonStructuredInternal(poContents, poResources, |
2410 | 563 | oMapPropertyToLayer, oMapNumGenToLayer, |
2411 | 563 | poSingleLayer); |
2412 | | |
2413 | | /* Remove empty layers */ |
2414 | 24.9k | for (auto oIter = m_apoLayers.begin(); oIter != m_apoLayers.end(); |
2415 | 563 | /* do nothing */) |
2416 | 24.4k | { |
2417 | 24.4k | if ((*oIter)->GetFeatureCount(false) == 0) |
2418 | 23.8k | { |
2419 | 23.8k | oIter = m_apoLayers.erase(oIter); |
2420 | 23.8k | } |
2421 | 531 | else |
2422 | 531 | { |
2423 | 531 | ++oIter; |
2424 | 531 | } |
2425 | 24.4k | } |
2426 | 563 | } |
2427 | | |
2428 | | #endif /* HAVE_PDF_READ_SUPPORT */ |