Coverage Report

Created: 2025-06-09 07:02

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