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