1 /******************************************************************************
2  *
3  * Project:  CALS driver
4  * Purpose:  CALS driver
5  * Author:   Even Rouault, <even dot rouault at spatialys dot com>
6  *
7  ******************************************************************************
8  * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot 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_frmts.h"
30 #include "gdal_pam.h"
31 #include "gdal_priv.h"
32 
33 #include "tiff.h"
34 
35 CPL_CVSID("$Id: calsdataset.cpp b55a33407a80673ec314b165c82f47dd02e9dc9c 2020-04-27 20:37:55 +0200 Even Rouault $")
36 
37 /************************************************************************/
38 /* ==================================================================== */
39 /*                            CALSDataset                               */
40 /* ==================================================================== */
41 /************************************************************************/
42 
43 class CALSDataset final: public GDALPamDataset
44 {
45     friend class CALSRasterBand;
46 
47     CPLString    osTIFFHeaderFilename;
48     CPLString    osSparseFilename;
49     GDALDataset* poUnderlyingDS;
50 
51     static void WriteLEInt16( VSILFILE* fp, GInt16 nVal );
52     static void WriteLEInt32( VSILFILE* fp, GInt32 nVal );
53     static void WriteTIFFTAG( VSILFILE* fp, GInt16 nTagName, GInt16 nTagType,
54                               GInt32 nTagValue );
55 
56   public:
CALSDataset()57                  CALSDataset() : poUnderlyingDS(nullptr) {}
58                 ~CALSDataset();
59 
60     static int          Identify( GDALOpenInfo * poOpenInfo );
61     static GDALDataset *Open( GDALOpenInfo * );
62     static GDALDataset *CreateCopy( const char *pszFilename,
63                                     GDALDataset *poSrcDS,
64                                     int bStrict,
65                                     char **papszOptions,
66                                     GDALProgressFunc pfnProgress,
67                                     void *pProgressData );
68 };
69 
70 /************************************************************************/
71 /* ==================================================================== */
72 /*                          CALSRasterBand                              */
73 /* ==================================================================== */
74 /************************************************************************/
75 
76 class CALSRasterBand final: public GDALPamRasterBand
77 {
78     GDALRasterBand* poUnderlyingBand;
79 
80   public:
CALSRasterBand(CALSDataset * poDSIn)81     explicit CALSRasterBand( CALSDataset* poDSIn )
82     {
83         poDS = poDSIn;
84         poUnderlyingBand = poDSIn->poUnderlyingDS->GetRasterBand(1);
85         poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
86         nBand = 1;
87         eDataType = GDT_Byte;
88     }
89 
IReadBlock(int nBlockXOff,int nBlockYOff,void * pData)90     CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void * pData ) override
91     {
92         return poUnderlyingBand->ReadBlock(nBlockXOff, nBlockYOff, pData);
93     }
94 
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpace,GSpacing nLineSpace,GDALRasterIOExtraArg * psExtraArg)95     CPLErr IRasterIO( GDALRWFlag eRWFlag,
96                       int nXOff, int nYOff, int nXSize, int nYSize,
97                       void * pData, int nBufXSize, int nBufYSize,
98                       GDALDataType eBufType,
99                       GSpacing nPixelSpace,
100                       GSpacing nLineSpace,
101                       GDALRasterIOExtraArg* psExtraArg ) override
102     {
103         return poUnderlyingBand->RasterIO(
104             eRWFlag, nXOff, nYOff, nXSize, nYSize,
105             pData, nBufXSize, nBufYSize, eBufType,
106             nPixelSpace, nLineSpace, psExtraArg ) ;
107     }
108 
GetColorTable()109     GDALColorTable* GetColorTable() override
110     {
111         return poUnderlyingBand->GetColorTable();
112     }
113 
GetColorInterpretation()114     GDALColorInterp GetColorInterpretation() override
115     {
116         return GCI_PaletteIndex;
117     }
118 
GetMetadata(const char * pszDomain)119     char** GetMetadata(const char* pszDomain) override
120     {
121         return poUnderlyingBand->GetMetadata(pszDomain);
122     }
123 
GetMetadataItem(const char * pszKey,const char * pszDomain)124     const char* GetMetadataItem( const char* pszKey,
125                                  const char* pszDomain ) override
126     {
127         return poUnderlyingBand->GetMetadataItem(pszKey, pszDomain);
128     }
129 };
130 
131 /************************************************************************/
132 /* ==================================================================== */
133 /*                          CALSWrapperSrcBand                          */
134 /* ==================================================================== */
135 /************************************************************************/
136 
137 class CALSWrapperSrcBand final: public GDALPamRasterBand
138 {
139         GDALDataset* poSrcDS;
140         bool bInvertValues;
141 
142     public:
CALSWrapperSrcBand(GDALDataset * poSrcDSIn)143         explicit CALSWrapperSrcBand( GDALDataset* poSrcDSIn )
144         {
145             poSrcDS = poSrcDSIn;
146             SetMetadataItem("NBITS", "1", "IMAGE_STRUCTURE");
147             poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
148             eDataType = GDT_Byte;
149             bInvertValues = true;
150             GDALColorTable* poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
151             if( poCT != nullptr && poCT->GetColorEntryCount() >= 2 )
152             {
153                 const GDALColorEntry* psEntry1 = poCT->GetColorEntry(0);
154                 const GDALColorEntry* psEntry2 = poCT->GetColorEntry(1);
155                 if( psEntry1->c1 == 255 &&
156                     psEntry1->c2 == 255 &&
157                     psEntry1->c3 == 255 &&
158                     psEntry2->c1 == 0 &&
159                     psEntry2->c2 == 0 &&
160                     psEntry2->c3 == 0 )
161                 {
162                     bInvertValues = false;
163                 }
164             }
165         }
166 
IReadBlock(int,int,void *)167     CPLErr IReadBlock( int /* nBlockXOff */,
168                        int /* nBlockYOff */,
169                        void * /* pData */ ) override
170     {
171         // Should not be called.
172         return CE_Failure;
173     }
174 
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpace,GSpacing nLineSpace,GDALRasterIOExtraArg * psExtraArg)175     CPLErr IRasterIO( GDALRWFlag eRWFlag,
176                       int nXOff, int nYOff, int nXSize, int nYSize,
177                       void * pData, int nBufXSize, int nBufYSize,
178                       GDALDataType eBufType,
179                       GSpacing nPixelSpace,
180                       GSpacing nLineSpace,
181                       GDALRasterIOExtraArg* psExtraArg ) override
182     {
183         const CPLErr eErr =
184             poSrcDS->GetRasterBand(1)->RasterIO(
185                 eRWFlag, nXOff, nYOff, nXSize, nYSize,
186                 pData, nBufXSize, nBufYSize, eBufType,
187                 nPixelSpace, nLineSpace, psExtraArg ) ;
188         if( bInvertValues )
189         {
190             for( int j = 0; j < nBufYSize; j++ )
191             {
192                 for( int i = 0; i < nBufXSize; i++ )
193                     ((GByte*)pData)[j * nLineSpace + i * nPixelSpace] =
194                         1 - ((GByte*)pData)[j * nLineSpace +
195                                             i * nPixelSpace];
196             }
197         }
198         return eErr;
199     }
200 };
201 
202 /************************************************************************/
203 /* ==================================================================== */
204 /*                          CALSWrapperSrcDataset                       */
205 /* ==================================================================== */
206 /************************************************************************/
207 
208 class CALSWrapperSrcDataset final: public GDALPamDataset
209 {
210     public:
CALSWrapperSrcDataset(GDALDataset * poSrcDS,const char * pszPadding)211         CALSWrapperSrcDataset( GDALDataset* poSrcDS, const char* pszPadding )
212         {
213             nRasterXSize = poSrcDS->GetRasterXSize();
214             nRasterYSize = poSrcDS->GetRasterYSize();
215             SetBand(1, new CALSWrapperSrcBand(poSrcDS));
216             SetMetadataItem("TIFFTAG_DOCUMENTNAME", pszPadding);
217         }
218 };
219 
220 /************************************************************************/
221 /* ==================================================================== */
222 /*                            CALSDataset                               */
223 /* ==================================================================== */
224 /************************************************************************/
225 
226 /************************************************************************/
227 /*                            ~CALSDataset()                            */
228 /************************************************************************/
229 
~CALSDataset()230 CALSDataset::~CALSDataset()
231 
232 {
233     delete poUnderlyingDS;
234     if( !osTIFFHeaderFilename.empty() )
235         VSIUnlink(osTIFFHeaderFilename);
236     if( !osSparseFilename.empty() )
237         VSIUnlink(osSparseFilename);
238 }
239 
240 /************************************************************************/
241 /*                            Identify()                                */
242 /************************************************************************/
243 
Identify(GDALOpenInfo * poOpenInfo)244 int CALSDataset::Identify( GDALOpenInfo * poOpenInfo )
245 
246 {
247     // If in the ingested bytes we found neither srcdocid: or rtype: 1, give up
248     if( poOpenInfo->nHeaderBytes == 0 ||
249         (strstr( (const char*) poOpenInfo->pabyHeader, "srcdocid:") == nullptr &&
250          strstr( (const char*) poOpenInfo->pabyHeader, "rtype: 1") == nullptr) )
251         return FALSE;
252 
253     // If we found srcdocid: try to ingest up to 2048 bytes
254     if( strstr( (const char*) poOpenInfo->pabyHeader, "srcdocid:") &&
255         !poOpenInfo->TryToIngest(2048) )
256         return FALSE;
257 
258     return strstr((const char*) poOpenInfo->pabyHeader, "rtype: 1") != nullptr &&
259            strstr((const char*) poOpenInfo->pabyHeader, "rorient:") != nullptr &&
260            strstr((const char*) poOpenInfo->pabyHeader, "rpelcnt:") != nullptr;
261 }
262 
263 /************************************************************************/
264 /*                           WriteLEInt16()                             */
265 /************************************************************************/
266 
WriteLEInt16(VSILFILE * fp,GInt16 nVal)267 void CALSDataset::WriteLEInt16( VSILFILE* fp, GInt16 nVal )
268 {
269     CPL_LSBPTR16(&nVal);
270     VSIFWriteL(&nVal, 1, 2, fp);
271 }
272 
273 /************************************************************************/
274 /*                            WriteLEInt32()                            */
275 /************************************************************************/
276 
WriteLEInt32(VSILFILE * fp,GInt32 nVal)277 void CALSDataset::WriteLEInt32( VSILFILE* fp, GInt32 nVal )
278 {
279     CPL_LSBPTR32(&nVal);
280     VSIFWriteL(&nVal, 1, 4, fp);
281 }
282 
283 /************************************************************************/
284 /*                            WriteTIFFTAG()                            */
285 /************************************************************************/
286 
WriteTIFFTAG(VSILFILE * fp,GInt16 nTagName,GInt16 nTagType,GInt32 nTagValue)287 void CALSDataset::WriteTIFFTAG( VSILFILE* fp, GInt16 nTagName, GInt16 nTagType,
288                                 GInt32 nTagValue )
289 {
290     WriteLEInt16(fp, nTagName);
291     WriteLEInt16(fp, nTagType);
292     WriteLEInt32(fp, 1);
293     WriteLEInt32(fp, nTagValue);
294 }
295 
296 /************************************************************************/
297 /*                                Open()                                */
298 /************************************************************************/
299 
Open(GDALOpenInfo * poOpenInfo)300 GDALDataset *CALSDataset::Open( GDALOpenInfo * poOpenInfo )
301 
302 {
303     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr )
304         return nullptr;
305 
306     const char* pszRPelCnt =
307         strstr((const char*) poOpenInfo->pabyHeader, "rpelcnt:");
308     int nXSize = 0;
309     int nYSize = 0;
310     if( sscanf(pszRPelCnt+strlen("rpelcnt:"),"%d,%d",&nXSize,&nYSize) != 2 ||
311         nXSize <= 0 || nYSize <= 0 )
312         return nullptr;
313 
314     const char* pszOrient =
315         strstr((const char*) poOpenInfo->pabyHeader, "rorient:");
316     int nAngle1, nAngle2;
317     if( sscanf(pszOrient+strlen("rorient:"),"%d,%d",&nAngle1,&nAngle2) != 2 )
318         return nullptr;
319 
320     const char* pszDensity =
321         strstr((const char*) poOpenInfo->pabyHeader, "rdensty:");
322     int nDensity = 0;
323     if( pszDensity )
324         sscanf(pszDensity+strlen("rdensty:"), "%d", &nDensity);
325 
326     VSIFSeekL(poOpenInfo->fpL, 0, SEEK_END);
327     int nFAX4BlobSize = static_cast<int>(VSIFTellL(poOpenInfo->fpL)) - 2048;
328     if( nFAX4BlobSize < 0 )
329         return nullptr;
330 
331     CALSDataset* poDS = new CALSDataset();
332     poDS->nRasterXSize = nXSize;
333     poDS->nRasterYSize = nYSize;
334 
335     // Create a TIFF header for a single-strip CCITTFAX4 file.
336     poDS->osTIFFHeaderFilename = CPLSPrintf("/vsimem/cals/header_%p.tiff", poDS);
337     VSILFILE* fp = VSIFOpenL(poDS->osTIFFHeaderFilename, "wb");
338     const int nTagCount = 10;
339     const int nHeaderSize = 4 + 4 + 2 + nTagCount * 12 + 4;
340     WriteLEInt16(fp, TIFF_LITTLEENDIAN);  // TIFF little-endian signature.
341     WriteLEInt16(fp, 42);  // TIFF classic.
342 
343     WriteLEInt32(fp, 8);  // Offset of IFD0.
344 
345     WriteLEInt16(fp, nTagCount);  // Number of entries.
346 
347     WriteTIFFTAG(fp, TIFFTAG_IMAGEWIDTH, TIFF_LONG, nXSize);
348     WriteTIFFTAG(fp, TIFFTAG_IMAGELENGTH, TIFF_LONG, nYSize);
349     WriteTIFFTAG(fp, TIFFTAG_BITSPERSAMPLE, TIFF_SHORT, 1);
350     WriteTIFFTAG(fp, TIFFTAG_COMPRESSION, TIFF_SHORT, COMPRESSION_CCITTFAX4);
351     WriteTIFFTAG(fp, TIFFTAG_PHOTOMETRIC, TIFF_SHORT, PHOTOMETRIC_MINISWHITE);
352     WriteTIFFTAG(fp, TIFFTAG_STRIPOFFSETS, TIFF_LONG, nHeaderSize);
353     WriteTIFFTAG(fp, TIFFTAG_SAMPLESPERPIXEL, TIFF_SHORT, 1);
354     WriteTIFFTAG(fp, TIFFTAG_ROWSPERSTRIP, TIFF_LONG, nYSize);
355     WriteTIFFTAG(fp, TIFFTAG_STRIPBYTECOUNTS, TIFF_LONG, nFAX4BlobSize);
356     WriteTIFFTAG(fp, TIFFTAG_PLANARCONFIG, TIFF_SHORT, PLANARCONFIG_CONTIG);
357 
358     WriteLEInt32(fp, 0);  // Offset of next IFD.
359 
360     VSIFCloseL(fp);
361 
362     // Create a /vsisparse/ description file assembling the TIFF header
363     // with the FAX4 codestream that starts at offset 2048 of the CALS file.
364     poDS->osSparseFilename = CPLSPrintf("/vsimem/cals/sparse_%p.xml", poDS);
365     fp = VSIFOpenL(poDS->osSparseFilename, "wb");
366     CPLAssert(fp);
367     VSIFPrintfL(fp, "<VSISparseFile>"
368                     "<Length>%d</Length>"
369                     "<SubfileRegion>"
370                       "<Filename relative='0'>%s</Filename>"
371                       "<DestinationOffset>0</DestinationOffset>"
372                       "<SourceOffset>0</SourceOffset>"
373                       "<RegionLength>%d</RegionLength>"
374                     "</SubfileRegion>"
375                     "<SubfileRegion>"
376                       "<Filename relative='0'>%s</Filename>"
377                       "<DestinationOffset>%d</DestinationOffset>"
378                       "<SourceOffset>%d</SourceOffset>"
379                       "<RegionLength>%d</RegionLength>"
380                     "</SubfileRegion>"
381                     "</VSISparseFile>",
382                 nHeaderSize + nFAX4BlobSize,
383                 poDS->osTIFFHeaderFilename.c_str(),
384                 nHeaderSize,
385                 poOpenInfo->pszFilename,
386                 nHeaderSize,
387                 2048,
388                 nFAX4BlobSize);
389     VSIFCloseL(fp);
390 
391     poDS->poUnderlyingDS = (GDALDataset*) GDALOpenEx(
392         CPLSPrintf("/vsisparse/%s", poDS->osSparseFilename.c_str()),
393         GDAL_OF_RASTER | GDAL_OF_INTERNAL, nullptr, nullptr, nullptr);
394     if( poDS->poUnderlyingDS == nullptr )
395     {
396         delete poDS;
397         return nullptr;
398     }
399 
400     if( nAngle1 != 0 || nAngle2 != 270 )
401     {
402         poDS->SetMetadataItem("PIXEL_PATH", CPLSPrintf("%d", nAngle1));
403         poDS->SetMetadataItem("LINE_PROGRESSION", CPLSPrintf("%d", nAngle2));
404     }
405 
406     if( nDensity != 0 )
407     {
408         poDS->SetMetadataItem("TIFFTAG_XRESOLUTION", CPLSPrintf("%d", nDensity));
409         poDS->SetMetadataItem("TIFFTAG_YRESOLUTION", CPLSPrintf("%d", nDensity));
410         poDS->SetMetadataItem("TIFFTAG_RESOLUTIONUNIT", "2 (pixels/inch)");
411     }
412 
413     poDS->SetBand(1, new CALSRasterBand(poDS));
414 
415 /* -------------------------------------------------------------------- */
416 /*      Initialize any PAM information.                                 */
417 /* -------------------------------------------------------------------- */
418     poDS->SetDescription( poOpenInfo->pszFilename );
419     poDS->TryLoadXML( poOpenInfo->GetSiblingFiles() );
420 
421 /* -------------------------------------------------------------------- */
422 /*      Open overviews.                                                 */
423 /* -------------------------------------------------------------------- */
424     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename,
425                                  poOpenInfo->GetSiblingFiles() );
426 
427     return poDS;
428 }
429 
430 /************************************************************************/
431 /*                             CreateCopy()                             */
432 /************************************************************************/
433 
CreateCopy(const char * pszFilename,GDALDataset * poSrcDS,int bStrict,char **,GDALProgressFunc pfnProgress,void * pProgressData)434 GDALDataset *CALSDataset::CreateCopy( const char *pszFilename,
435                                       GDALDataset *poSrcDS,
436                                       int bStrict,
437                                       char ** /* papszOptionsUnused */,
438                                       GDALProgressFunc pfnProgress,
439                                       void *pProgressData )
440 {
441     if( poSrcDS->GetRasterCount() == 0 ||
442         (bStrict && poSrcDS->GetRasterCount() != 1) )
443     {
444         CPLError( CE_Failure, CPLE_NotSupported,
445                   "CALS driver only supports single band raster.");
446         return nullptr;
447     }
448     if( poSrcDS->GetRasterBand(1)->
449             GetMetadataItem("NBITS", "IMAGE_STRUCTURE") == nullptr ||
450         !EQUAL(poSrcDS->GetRasterBand(1)->
451                    GetMetadataItem("NBITS", "IMAGE_STRUCTURE"), "1") )
452     {
453         CPLError( bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
454                   "CALS driver only supports 1-bit.");
455         if( bStrict )
456             return nullptr;
457     }
458 
459     if( poSrcDS->GetRasterXSize() > 999999 ||
460         poSrcDS->GetRasterYSize() > 999999 )
461     {
462         CPLError(
463             CE_Failure, CPLE_NotSupported,
464             "CALS driver only supports datasets with dimension <= 999999.");
465         return nullptr;
466     }
467 
468     GDALDriver* poGTiffDrv =
469         static_cast<GDALDriver *>(GDALGetDriverByName("GTiff"));
470     if( poGTiffDrv == nullptr )
471     {
472         CPLError( CE_Failure, CPLE_NotSupported,
473                   "CALS driver needs GTiff driver." );
474         return nullptr;
475     }
476 
477     // Write a in-memory TIFF with just the TIFF header to figure out
478     // how large it will be.
479     CPLString osTmpFilename(CPLSPrintf("/vsimem/cals/tmp_%p", poSrcDS));
480     char** papszOptions = nullptr;
481     papszOptions = CSLSetNameValue(papszOptions, "COMPRESS", "CCITTFAX4");
482     papszOptions = CSLSetNameValue(papszOptions, "NBITS", "1");
483     papszOptions = CSLSetNameValue(papszOptions, "BLOCKYSIZE",
484                                    CPLSPrintf("%d", poSrcDS->GetRasterYSize()));
485     papszOptions = CSLSetNameValue(papszOptions, "SPARSE_OK", "YES");
486     GDALDataset* poDS = poGTiffDrv->Create(osTmpFilename,
487                                            poSrcDS->GetRasterXSize(),
488                                            poSrcDS->GetRasterYSize(),
489                                            1, GDT_Byte,
490                                            papszOptions);
491     if( poDS == nullptr )
492     {
493         // Should not happen normally (except if CCITTFAX4 not available).
494         CSLDestroy(papszOptions);
495         return nullptr;
496     }
497     const char INITIAL_PADDING[] = "12345";
498      // To adjust padding.
499     poDS->SetMetadataItem("TIFFTAG_DOCUMENTNAME", INITIAL_PADDING);
500     GDALClose(poDS);
501     VSIStatBufL sStat;
502     if( VSIStatL(osTmpFilename, &sStat) != 0 )
503     {
504         // Shouldn't happen really. Just to make Coverity happy.
505         CSLDestroy(papszOptions);
506         return nullptr;
507     }
508     int nTIFFHeaderSize = static_cast<int>(sStat.st_size);
509     VSIUnlink(osTmpFilename);
510 
511     // Redo the same thing, but this time write it to the output file
512     // and use a variable TIFF tag (TIFFTAG_DOCUMENTNAME) to enlarge the
513     // header + the variable TIFF tag so that they are 2048 bytes large.
514     char szBuffer[2048+1] = {};
515     memset(szBuffer, 'X', 2048 - nTIFFHeaderSize + strlen(INITIAL_PADDING));
516     szBuffer[2048 - nTIFFHeaderSize + strlen(INITIAL_PADDING)] = 0;
517     GDALDataset* poTmpDS = new CALSWrapperSrcDataset(poSrcDS, szBuffer);
518     poDS = poGTiffDrv->CreateCopy(pszFilename, poTmpDS, FALSE, papszOptions,
519                                   pfnProgress, pProgressData );
520     delete poTmpDS;
521     CSLDestroy(papszOptions);
522     if( poDS == nullptr )
523         return nullptr;
524     delete poDS;
525 
526     // Now replace the TIFF header by the CALS header.
527     VSILFILE* fp = VSIFOpenL(pszFilename, "rb+");
528     if( fp == nullptr )
529         return nullptr; // Shouldn't happen normally.
530     memset(szBuffer, ' ', 2048);
531     CPLString osField;
532     osField = "srcdocid: NONE";
533     // cppcheck-suppress redundantCopy
534     memcpy(szBuffer, osField, osField.size());
535 
536     osField = "dstdocid: NONE";
537     memcpy(szBuffer + 128, osField, osField.size());
538 
539     osField = "txtfilid: NONE";
540     memcpy(szBuffer + 128*2, osField, osField.size());
541 
542     osField = "figid: NONE";
543     memcpy(szBuffer + 128*3, osField, osField.size());
544 
545     osField = "srcgph: NONE";
546     memcpy(szBuffer + 128*4, osField, osField.size());
547 
548     osField = "doccls: NONE";
549     memcpy(szBuffer + 128*5, osField, osField.size());
550 
551     osField = "rtype: 1";
552     memcpy(szBuffer + 128*6, osField, osField.size());
553 
554     int nAngle1 = 0;
555     int nAngle2 = 270;
556     const char* pszPixelPath = poSrcDS->GetMetadataItem("PIXEL_PATH");
557     const char* pszLineProgression = poSrcDS->GetMetadataItem("LINE_PROGRESSION");
558     if( pszPixelPath && pszLineProgression )
559     {
560         nAngle1 = atoi(pszPixelPath);
561         nAngle2 = atoi(pszLineProgression);
562     }
563     osField = CPLSPrintf("rorient: %03d,%03d", nAngle1, nAngle2);
564     memcpy(szBuffer + 128*7, osField, osField.size());
565 
566     osField = CPLSPrintf("rpelcnt: %06d,%06d",
567                          poSrcDS->GetRasterXSize(),
568                          poSrcDS->GetRasterYSize());
569     memcpy(szBuffer + 128*8, osField, osField.size());
570 
571     int nDensity = 200;
572     const char* pszXRes = poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION");
573     const char* pszYRes = poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION");
574     const char* pszResUnit = poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT");
575     if( pszXRes && pszYRes && pszResUnit && EQUAL(pszXRes, pszYRes) &&
576         atoi(pszResUnit) == 2 )
577     {
578         nDensity = atoi(pszXRes);
579         if( nDensity < 1 || nDensity > 9999 )
580             nDensity = 200;
581     }
582     osField = CPLSPrintf("rdensty: %04d", nDensity);
583     memcpy(szBuffer + 128*9, osField, osField.size());
584 
585     osField = "notes: NONE";
586     memcpy(szBuffer + 128*10, osField, osField.size());
587     VSIFWriteL(szBuffer, 1, 2048, fp);
588     VSIFCloseL(fp);
589 
590     GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly, nullptr);
591     return Open(&oOpenInfo);
592 }
593 
594 /************************************************************************/
595 /*                        GDALRegister_CALS()                           */
596 /************************************************************************/
597 
GDALRegister_CALS()598 void GDALRegister_CALS()
599 
600 {
601     if( GDALGetDriverByName( "CALS" ) != nullptr )
602         return;
603 
604     GDALDriver *poDriver = new GDALDriver();
605 
606     poDriver->SetDescription( "CALS" );
607     poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
608     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
609                                "CALS (Type 1)" );
610     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
611                                "drivers/raster/cals.html" );
612     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
613     poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "cal ct1");
614 
615     poDriver->pfnIdentify = CALSDataset::Identify;
616     poDriver->pfnOpen = CALSDataset::Open;
617     poDriver->pfnCreateCopy = CALSDataset::CreateCopy;
618 
619     GetGDALDriverManager()->RegisterDriver( poDriver );
620 }
621