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