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