/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 |