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