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