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