Coverage Report

Created: 2026-08-14 09:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/pdf/pdfdrivercore.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  PDF driver
5
 * Author:   Even Rouault, <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2023, Even Rouault, <even.rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdal_frmts.h"
14
#include "gdalplugindriverproxy.h"
15
16
#include "pdfdrivercore.h"
17
18
static const char *const szOpenOptionList =
19
    "<OpenOptionList>"
20
#if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM)
21
    "  <Option name='RENDERING_OPTIONS' type='string-select' "
22
    "description='Which graphical elements to render' "
23
    "default='RASTER,VECTOR,TEXT' "
24
    "alt_config_option='GDAL_PDF_RENDERING_OPTIONS'>"
25
    "     <Value>RASTER,VECTOR,TEXT</Value>\n"
26
    "     <Value>RASTER,VECTOR</Value>\n"
27
    "     <Value>RASTER,TEXT</Value>\n"
28
    "     <Value>RASTER</Value>\n"
29
    "     <Value>VECTOR,TEXT</Value>\n"
30
    "     <Value>VECTOR</Value>\n"
31
    "     <Value>TEXT</Value>\n"
32
    "  </Option>"
33
#endif
34
    "  <Option name='DPI' type='float' description='Resolution in Dot Per "
35
    "Inch' default='150' alt_config_option='GDAL_PDF_DPI'/>"
36
    "  <Option name='SAVE_DPI_TO_PAM' type='boolean' description='"
37
    "Whether to save user provided DPI into PAM (.aux.xml) metadata as the "
38
    "new default DPI'/>"
39
    "  <Option name='USER_PWD' type='string' description='Password' "
40
    "alt_config_option='PDF_USER_PWD'/>"
41
#ifdef HAVE_MULTIPLE_PDF_BACKENDS
42
    "  <Option name='PDF_LIB' type='string-select' description='Which "
43
    "underlying PDF library to use' "
44
#if defined(HAVE_PDFIUM)
45
    "default='PDFIUM'"
46
#elif defined(HAVE_POPPLER)
47
    "default='POPPLER'"
48
#elif defined(HAVE_PODOFO)
49
    "default='PODOFO'"
50
#endif  // ~ default PDF_LIB
51
    " alt_config_option='GDAL_PDF_LIB'>"
52
#if defined(HAVE_POPPLER)
53
    "     <Value>POPPLER</Value>\n"
54
#endif  // HAVE_POPPLER
55
#if defined(HAVE_PODOFO)
56
    "     <Value>PODOFO</Value>\n"
57
#endif  // HAVE_PODOFO
58
#if defined(HAVE_PDFIUM)
59
    "     <Value>PDFIUM</Value>\n"
60
#endif  // HAVE_PDFIUM
61
    "  </Option>"
62
#endif  // HAVE_MULTIPLE_PDF_BACKENDS
63
    "  <Option name='LAYERS' type='string' description='List of layers (comma "
64
    "separated) to turn ON (or ALL to turn all layers ON)' "
65
    "alt_config_option='GDAL_PDF_LAYERS'/>"
66
    "  <Option name='LAYERS_OFF' type='string' description='List of layers "
67
    "(comma separated) to turn OFF' alt_config_option='GDAL_PDF_LAYERS_OFF'/>"
68
    "  <Option name='BANDS' type='string-select' description='Number of raster "
69
    "bands' default='3' alt_config_option='GDAL_PDF_BANDS'>"
70
    "     <Value>3</Value>\n"
71
    "     <Value>4</Value>\n"
72
    "  </Option>"
73
    "  <Option name='NEATLINE' type='string' description='The name of the "
74
    "neatline to select' alt_config_option='GDAL_PDF_NEATLINE'/>"
75
    "</OpenOptionList>";
76
77
const char *PDFGetOpenOptionList()
78
160k
{
79
160k
    return szOpenOptionList;
80
160k
}
81
82
/************************************************************************/
83
/*                         PDFDatasetIdentify()                         */
84
/************************************************************************/
85
86
int PDFDatasetIdentify(GDALOpenInfo *poOpenInfo)
87
778k
{
88
778k
    if (STARTS_WITH(poOpenInfo->pszFilename, "PDF:"))
89
0
        return TRUE;
90
778k
    if (STARTS_WITH(poOpenInfo->pszFilename, "PDF_IMAGE:"))
91
0
        return TRUE;
92
93
778k
    if (poOpenInfo->nHeaderBytes < 128)
94
512k
        return FALSE;
95
96
266k
    return memcmp(poOpenInfo->pabyHeader, "%PDF", 4) == 0;
97
778k
}
98
99
/************************************************************************/
100
/*                     PDFDriverSetCommonMetadata()                     */
101
/************************************************************************/
102
103
void PDFDriverSetCommonMetadata(GDALDriver *poDriver)
104
22
{
105
22
    poDriver->SetDescription(DRIVER_NAME);
106
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
107
22
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
108
22
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
109
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Geospatial PDF");
110
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/pdf.html");
111
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "pdf");
112
22
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
113
22
    poDriver->SetMetadataItem(
114
22
        GDAL_DMD_CREATIONFIELDDATATYPES,
115
22
        "Integer Integer64 Real String Date DateTime Time");
116
117
22
#if defined(HAVE_POPPLER) || defined(HAVE_PDFIUM)
118
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
119
22
#endif
120
121
22
    poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES, "YES");
122
22
    poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES_READ, "YES");
123
22
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
124
22
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
125
126
22
#ifdef HAVE_POPPLER
127
22
    poDriver->SetMetadataItem("HAVE_POPPLER", "YES");
128
22
#endif  // HAVE_POPPLER
129
#ifdef HAVE_PODOFO
130
    poDriver->SetMetadataItem("HAVE_PODOFO", "YES");
131
#endif  // HAVE_PODOFO
132
#ifdef HAVE_PDFIUM
133
    poDriver->SetMetadataItem("HAVE_PDFIUM", "YES");
134
#endif  // HAVE_PDFIUM
135
136
22
    poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST,
137
22
                              "<LayerCreationOptionList/>");
138
139
22
    poDriver->SetMetadataItem(
140
22
        GDAL_DMD_CREATIONOPTIONLIST,
141
22
        "<CreationOptionList>\n"
142
22
        "   <Option name='COMPRESS' type='string-select' "
143
22
        "description='Compression method for raster data' default='DEFLATE'>\n"
144
22
        "     <Value>NONE</Value>\n"
145
22
        "     <Value>DEFLATE</Value>\n"
146
22
        "     <Value>JPEG</Value>\n"
147
22
        "     <Value>JPEG2000</Value>\n"
148
22
        "   </Option>\n"
149
22
        "   <Option name='STREAM_COMPRESS' type='string-select' "
150
22
        "description='Compression method for stream objects' "
151
22
        "default='DEFLATE'>\n"
152
22
        "     <Value>NONE</Value>\n"
153
22
        "     <Value>DEFLATE</Value>\n"
154
22
        "   </Option>\n"
155
22
        "   <Option name='GEO_ENCODING' type='string-select' "
156
22
        "description='Format of geo-encoding' default='ISO32000'>\n"
157
22
        "     <Value>NONE</Value>\n"
158
22
        "     <Value>ISO32000</Value>\n"
159
22
        "   </Option>\n"
160
22
        "   <Option name='NEATLINE' type='string' description='Neatline'/>\n"
161
22
        "   <Option name='DPI' type='float' description='DPI' default='72'/>\n"
162
22
        "   <Option name='WRITE_USERUNIT' type='boolean' description='Whether "
163
22
        "the UserUnit parameter must be written'/>\n"
164
22
        "   <Option name='PREDICTOR' type='int' description='Predictor Type "
165
22
        "(for DEFLATE compression)'/>\n"
166
22
        "   <Option name='JPEG_QUALITY' type='int' description='JPEG quality "
167
22
        "1-100' default='75'/>\n"
168
22
        "   <Option name='JPEG2000_DRIVER' type='string'/>\n"
169
22
        "   <Option name='TILED' type='boolean' description='Switch to tiled "
170
22
        "format' default='NO'/>\n"
171
22
        "   <Option name='BLOCKXSIZE' type='int' description='Block Width'/>\n"
172
22
        "   <Option name='BLOCKYSIZE' type='int' description='Block Height'/>\n"
173
22
        "   <Option name='LAYER_NAME' type='string' description='Layer name "
174
22
        "for raster content'/>\n"
175
22
        "   <Option name='CLIPPING_EXTENT' type='string' description='Clipping "
176
22
        "extent for main and extra rasters. Format: xmin,ymin,xmax,ymax'/>\n"
177
22
        "   <Option name='EXTRA_RASTERS' type='string' description='List of "
178
22
        "extra (georeferenced) rasters.'/>\n"
179
22
        "   <Option name='EXTRA_RASTERS_LAYER_NAME' type='string' "
180
22
        "description='List of layer names for the extra (georeferenced) "
181
22
        "rasters.'/>\n"
182
22
        "   <Option name='EXTRA_STREAM' type='string' description='Extra data "
183
22
        "to insert into the page content stream'/>\n"
184
22
        "   <Option name='EXTRA_IMAGES' type='string' description='List of "
185
22
        "image_file_name,x,y,scale[,link=some_url] (possibly repeated)'/>\n"
186
22
        "   <Option name='EXTRA_LAYER_NAME' type='string' description='Layer "
187
22
        "name for extra content'/>\n"
188
22
        "   <Option name='MARGIN' type='int' description='Margin around image "
189
22
        "in user units'/>\n"
190
22
        "   <Option name='LEFT_MARGIN' type='int' description='Left margin in "
191
22
        "user units'/>\n"
192
22
        "   <Option name='RIGHT_MARGIN' type='int' description='Right margin "
193
22
        "in user units'/>\n"
194
22
        "   <Option name='TOP_MARGIN' type='int' description='Top margin in "
195
22
        "user units'/>\n"
196
22
        "   <Option name='BOTTOM_MARGIN' type='int' description='Bottom margin "
197
22
        "in user units'/>\n"
198
22
        "   <Option name='OGR_DATASOURCE' type='string' description='Name of "
199
22
        "OGR datasource to display on top of the raster layer'/>\n"
200
22
        "   <Option name='OGR_DISPLAY_FIELD' type='string' description='Name "
201
22
        "of field to use as the display field in the feature tree'/>\n"
202
22
        "   <Option name='OGR_DISPLAY_LAYER_NAMES' type='string' "
203
22
        "description='Comma separated list of OGR layer names to display in "
204
22
        "the feature tree'/>\n"
205
22
        "   <Option name='OGR_WRITE_ATTRIBUTES' type='boolean' "
206
22
        "description='Whether to write attributes of OGR features' "
207
22
        "default='YES'/>\n"
208
22
        "   <Option name='OGR_LINK_FIELD' type='string' description='Name of "
209
22
        "field to use as the URL field to make objects clickable.'/>\n"
210
22
        "   <Option name='XMP' type='string' description='xml:XMP metadata'/>\n"
211
22
        "   <Option name='WRITE_INFO' type='boolean' description='to control "
212
22
        "whether a Info block must be written' default='YES'/>\n"
213
22
        "   <Option name='AUTHOR' type='string'/>\n"
214
22
        "   <Option name='CREATOR' type='string'/>\n"
215
22
        "   <Option name='CREATION_DATE' type='string'/>\n"
216
22
        "   <Option name='KEYWORDS' type='string'/>\n"
217
22
        "   <Option name='PRODUCER' type='string'/>\n"
218
22
        "   <Option name='SUBJECT' type='string'/>\n"
219
22
        "   <Option name='TITLE' type='string'/>\n"
220
22
        "   <Option name='OFF_LAYERS' type='string' description='Comma "
221
22
        "separated list of layer names that should be initially hidden'/>\n"
222
22
        "   <Option name='EXCLUSIVE_LAYERS' type='string' description='Comma "
223
22
        "separated list of layer names, such that only one of those layers can "
224
22
        "be ON at a time.'/>\n"
225
22
        "   <Option name='JAVASCRIPT' type='string' description='Javascript "
226
22
        "script to embed and run at file opening'/>\n"
227
22
        "   <Option name='JAVASCRIPT_FILE' type='string' description='Filename "
228
22
        "of the Javascript script to embed and run at file opening'/>\n"
229
22
        "   <Option name='COMPOSITION_FILE' type='string' description='XML "
230
22
        "file describing how the PDF should be composed'/>\n"
231
22
        "</CreationOptionList>\n");
232
233
22
#ifdef HAVE_PDF_READ_SUPPORT
234
22
    poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, szOpenOptionList);
235
22
    poDriver->pfnIdentify = PDFDatasetIdentify;
236
22
    poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
237
22
    poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
238
22
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
239
240
22
    poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
241
22
    poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
242
22
                              "GeoTransform SRS GCPs DatasetMetadata");
243
244
22
    poDriver->DeclareAlgorithm({"list-layers"});
245
22
#endif
246
247
22
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
248
22
    poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
249
22
}
250
251
/************************************************************************/
252
/*                      DeclareDeferredPDFPlugin()                      */
253
/************************************************************************/
254
255
#ifdef PLUGIN_FILENAME
256
void DeclareDeferredPDFPlugin()
257
{
258
    if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
259
    {
260
        return;
261
    }
262
    auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
263
#ifdef PLUGIN_INSTALLATION_MESSAGE
264
    poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
265
                              PLUGIN_INSTALLATION_MESSAGE);
266
#endif
267
    PDFDriverSetCommonMetadata(poDriver);
268
    GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
269
}
270
#endif