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