Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_imapinfo
4 : * Project: MapInfo mid/mif Tab Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the IMapInfoFile class, super class of
7 : * of MIFFile and TABFile
8 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
9 : *
10 : **********************************************************************
11 : * Copyright (c) 1999-2008, Daniel Morissette
12 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
13 : *
14 : * SPDX-License-Identifier: MIT
15 : **********************************************************************/
16 :
17 : #include "cpl_port.h"
18 : #include "mitab.h"
19 :
20 : #include <cassert>
21 : #include <cctype>
22 : #include <cstring>
23 : #include <algorithm>
24 :
25 : #include "cpl_conv.h"
26 : #include "cpl_error.h"
27 : #include "cpl_vsi.h"
28 : #include "mitab_priv.h"
29 : #include "mitab_utils.h"
30 : #include "ogr_core.h"
31 : #include "ogr_feature.h"
32 : #include "ogr_geometry.h"
33 :
34 : #ifdef __HP_aCC
35 : #include <wchar.h> /* iswspace() */
36 : #else
37 : #include <wctype.h> /* iswspace() */
38 : #endif
39 :
40 : /**********************************************************************
41 : * IMapInfoFile::IMapInfoFile()
42 : *
43 : * Constructor.
44 : **********************************************************************/
45 2397 : IMapInfoFile::IMapInfoFile(GDALDataset *poDS)
46 : : m_poDS(poDS), m_nCurFeatureId(0), m_poCurFeature(nullptr),
47 2397 : m_bBoundsSet(FALSE), m_pszCharset(nullptr)
48 : {
49 2397 : }
50 :
51 : /**********************************************************************
52 : * IMapInfoFile::~IMapInfoFile()
53 : *
54 : * Destructor.
55 : **********************************************************************/
56 2397 : IMapInfoFile::~IMapInfoFile()
57 : {
58 2397 : if (m_poCurFeature)
59 : {
60 0 : delete m_poCurFeature;
61 0 : m_poCurFeature = nullptr;
62 : }
63 :
64 2397 : CPLFree(m_pszCharset);
65 2397 : m_pszCharset = nullptr;
66 2397 : }
67 :
68 : /**********************************************************************
69 : * IMapInfoFile::Open()
70 : *
71 : * Compatibility layer with new interface.
72 : * Return 0 on success, -1 in case of failure.
73 : **********************************************************************/
74 :
75 0 : int IMapInfoFile::Open(const char *pszFname, const char *pszAccess,
76 : GBool bTestOpenNoError, const char *pszCharset)
77 : {
78 : // cppcheck-suppress nullPointer
79 0 : if (STARTS_WITH_CI(pszAccess, "r"))
80 0 : return Open(pszFname, TABRead, bTestOpenNoError, pszCharset);
81 0 : else if (STARTS_WITH_CI(pszAccess, "w"))
82 0 : return Open(pszFname, TABWrite, bTestOpenNoError, pszCharset);
83 : else
84 : {
85 0 : CPLError(CE_Failure, CPLE_FileIO,
86 : "Open() failed: access mode \"%s\" not supported", pszAccess);
87 0 : return -1;
88 : }
89 : }
90 :
91 : /**********************************************************************
92 : * IMapInfoFile::SmartOpen()
93 : *
94 : * Use this static method to automatically open any flavor of MapInfo
95 : * dataset. This method will detect the file type, create an object
96 : * of the right type, and open the file.
97 : *
98 : * Call GetFileClass() on the returned object if you need to find out
99 : * its exact type. (To access format-specific methods for instance)
100 : *
101 : * Returns the new object ptr. , or NULL if the open failed.
102 : **********************************************************************/
103 2177 : IMapInfoFile *IMapInfoFile::SmartOpen(GDALDataset *poDS, const char *pszFname,
104 : GBool bUpdate,
105 : GBool bTestOpenNoError /*=FALSE*/)
106 : {
107 2177 : IMapInfoFile *poFile = nullptr;
108 2177 : int nLen = 0;
109 :
110 2177 : if (pszFname)
111 2177 : nLen = static_cast<int>(strlen(pszFname));
112 :
113 2177 : if (nLen > 4 && (EQUAL(pszFname + nLen - 4, ".MIF") ||
114 1302 : EQUAL(pszFname + nLen - 4, ".MID")))
115 : {
116 : /*-------------------------------------------------------------
117 : * MIF/MID file
118 : *------------------------------------------------------------*/
119 875 : poFile = new MIFFile(poDS);
120 : }
121 1302 : else if (nLen > 4 && EQUAL(pszFname + nLen - 4, ".TAB"))
122 : {
123 : /*-------------------------------------------------------------
124 : * .TAB file ... is it a TABFileView or a TABFile?
125 : * We have to read the .tab header to find out.
126 : *------------------------------------------------------------*/
127 1302 : char *pszAdjFname = CPLStrdup(pszFname);
128 1302 : GBool bFoundFields = FALSE;
129 1302 : GBool bFoundView = FALSE;
130 1302 : GBool bFoundSeamless = FALSE;
131 :
132 1302 : TABAdjustFilenameExtension(pszAdjFname);
133 1302 : VSILFILE *fp = VSIFOpenL(pszAdjFname, "r");
134 1302 : const char *pszLine = nullptr;
135 12028 : while (fp && (pszLine = CPLReadLineL(fp)) != nullptr)
136 : {
137 22016 : while (isspace(static_cast<unsigned char>(*pszLine)))
138 11290 : pszLine++;
139 10726 : if (STARTS_WITH_CI(pszLine, "Fields"))
140 1299 : bFoundFields = TRUE;
141 9427 : else if (STARTS_WITH_CI(pszLine, "create view"))
142 2 : bFoundView = TRUE;
143 9425 : else if (STARTS_WITH_CI(pszLine, "\"\\IsSeamless\" = \"TRUE\""))
144 1 : bFoundSeamless = TRUE;
145 : }
146 :
147 1302 : if (bFoundView)
148 2 : poFile = new TABView(poDS);
149 1300 : else if (bFoundFields && bFoundSeamless)
150 1 : poFile = new TABSeamless(poDS);
151 1299 : else if (bFoundFields)
152 1298 : poFile = new TABFile(poDS);
153 :
154 1302 : if (fp)
155 1302 : VSIFCloseL(fp);
156 :
157 1302 : CPLFree(pszAdjFname);
158 : }
159 :
160 : /*-----------------------------------------------------------------
161 : * Perform the open() call
162 : *----------------------------------------------------------------*/
163 4353 : if (poFile && poFile->Open(pszFname, bUpdate ? TABReadWrite : TABRead,
164 2176 : bTestOpenNoError) != 0)
165 : {
166 75 : delete poFile;
167 75 : poFile = nullptr;
168 : }
169 :
170 2177 : if (!bTestOpenNoError && poFile == nullptr)
171 : {
172 0 : CPLError(CE_Failure, CPLE_FileIO,
173 : "%s could not be opened as a MapInfo dataset.", pszFname);
174 : }
175 :
176 2177 : return poFile;
177 : }
178 :
179 : /**********************************************************************
180 : * IMapInfoFile::GetNextFeature()
181 : *
182 : * Standard OGR GetNextFeature implementation. This method is used
183 : * to retrieve the next OGRFeature.
184 : **********************************************************************/
185 223071 : OGRFeature *IMapInfoFile::GetNextFeature()
186 : {
187 223071 : GIntBig nFeatureId = 0;
188 :
189 547509 : while ((nFeatureId = GetNextFeatureId(m_nCurFeatureId)) != -1)
190 : {
191 536698 : OGRGeometry *poGeom = nullptr;
192 536698 : OGRFeature *poFeatureRef = GetFeatureRef(nFeatureId);
193 536698 : if (poFeatureRef == nullptr)
194 328 : return nullptr;
195 1451540 : else if ((m_poFilterGeom == nullptr ||
196 378805 : ((poGeom = poFeatureRef->GetGeometryRef()) != nullptr &&
197 1129310 : FilterGeometry(poGeom))) &&
198 214136 : (m_poAttrQuery == nullptr ||
199 4894 : m_poAttrQuery->Evaluate(poFeatureRef)))
200 : {
201 : // Avoid cloning feature... return the copy owned by the class
202 211932 : CPLAssert(poFeatureRef == m_poCurFeature);
203 211932 : m_poCurFeature = nullptr;
204 211932 : if (poFeatureRef->GetGeometryRef() != nullptr)
205 211763 : poFeatureRef->GetGeometryRef()->assignSpatialReference(
206 211763 : GetSpatialRef());
207 211932 : return poFeatureRef;
208 : }
209 : }
210 10811 : return nullptr;
211 : }
212 :
213 : /**********************************************************************
214 : * IMapInfoFile::CreateTABFeature()
215 : *
216 : * Instantiate a TABFeature* from a OGRFeature* (or NULL on error)
217 : **********************************************************************/
218 :
219 15224 : TABFeature *IMapInfoFile::CreateTABFeature(OGRFeature *poFeature)
220 : {
221 15224 : TABFeature *poTABFeature = nullptr;
222 : OGRwkbGeometryType eGType;
223 15224 : TABPoint *poTABPointFeature = nullptr;
224 15224 : TABRegion *poTABRegionFeature = nullptr;
225 15224 : TABPolyline *poTABPolylineFeature = nullptr;
226 :
227 : /*-----------------------------------------------------------------
228 : * MITAB won't accept new features unless they are in a type derived
229 : * from TABFeature... so we have to do our best to map to the right
230 : * feature type based on the geometry type.
231 : *----------------------------------------------------------------*/
232 15224 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
233 15224 : if (poGeom != nullptr)
234 15146 : eGType = poGeom->getGeometryType();
235 : else
236 78 : eGType = wkbNone;
237 :
238 15224 : switch (wkbFlatten(eGType))
239 : {
240 : /*-------------------------------------------------------------
241 : * POINT
242 : *------------------------------------------------------------*/
243 14779 : case wkbPoint:
244 : {
245 14779 : const char *pszStyleString = poFeature->GetStyleString();
246 14779 : if (pszStyleString)
247 : {
248 113 : if (strstr(pszStyleString, "LABEL("))
249 : {
250 4 : auto poText = new TABText(poFeature->GetDefnRef());
251 4 : poText->SetLabelFromStyleString(pszStyleString);
252 4 : poTABFeature = poText;
253 :
254 4 : if (strstr(pszStyleString, "SYMBOL("))
255 : {
256 0 : CPLError(CE_Warning, CPLE_AppDefined,
257 : "OGR style string contains both "
258 : "Label and Symbol parts. "
259 : "Only Label taken into account due to "
260 : "MapInfo TAB/MIF format limitations.");
261 : }
262 : }
263 : else
264 : {
265 : TABFeatureClass featureClass =
266 109 : ITABFeatureSymbol::GetSymbolFeatureClass(
267 : pszStyleString);
268 109 : if (featureClass == TABFCFontPoint)
269 : {
270 2 : poTABFeature =
271 2 : new TABFontPoint(poFeature->GetDefnRef());
272 : }
273 107 : else if (featureClass == TABFCCustomPoint)
274 : {
275 2 : poTABFeature =
276 2 : new TABCustomPoint(poFeature->GetDefnRef());
277 : }
278 : else
279 : {
280 105 : poTABFeature = new TABPoint(poFeature->GetDefnRef());
281 : }
282 : poTABPointFeature =
283 109 : cpl::down_cast<TABPoint *>(poTABFeature);
284 109 : poTABPointFeature->SetSymbolFromStyleString(
285 109 : poFeature->GetStyleString());
286 : }
287 : }
288 : else
289 : {
290 14666 : poTABFeature = new TABPoint(poFeature->GetDefnRef());
291 : }
292 14779 : break;
293 : }
294 :
295 : /*-------------------------------------------------------------
296 : * REGION
297 : *------------------------------------------------------------*/
298 111 : case wkbPolygon:
299 : case wkbMultiPolygon:
300 111 : poTABFeature = new TABRegion(poFeature->GetDefnRef());
301 111 : if (poFeature->GetStyleString())
302 : {
303 19 : poTABRegionFeature = cpl::down_cast<TABRegion *>(poTABFeature);
304 19 : poTABRegionFeature->SetPenFromStyleString(
305 19 : poFeature->GetStyleString());
306 :
307 19 : poTABRegionFeature->SetBrushFromStyleString(
308 19 : poFeature->GetStyleString());
309 : }
310 111 : break;
311 : /*-------------------------------------------------------------
312 : * LINE/PLINE/MULTIPLINE
313 : *------------------------------------------------------------*/
314 238 : case wkbLineString:
315 : case wkbMultiLineString:
316 238 : poTABFeature = new TABPolyline(poFeature->GetDefnRef());
317 238 : if (poFeature->GetStyleString())
318 : {
319 : poTABPolylineFeature =
320 13 : cpl::down_cast<TABPolyline *>(poTABFeature);
321 13 : poTABPolylineFeature->SetPenFromStyleString(
322 13 : poFeature->GetStyleString());
323 : }
324 238 : break;
325 : /*-------------------------------------------------------------
326 : * Collection types that are not directly supported... convert
327 : * to multiple features in output file through recursive calls.
328 : *------------------------------------------------------------*/
329 18 : case wkbGeometryCollection:
330 : case wkbMultiPoint:
331 : {
332 18 : OGRErr eStatus = OGRERR_NONE;
333 18 : assert(poGeom); // for clang static analyzer
334 18 : OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
335 18 : OGRFeature *poTmpFeature = poFeature->Clone();
336 :
337 112 : for (int i = 0; eStatus == OGRERR_NONE && poColl != nullptr &&
338 56 : i < poColl->getNumGeometries();
339 : i++)
340 : {
341 38 : poTmpFeature->SetFID(OGRNullFID);
342 38 : poTmpFeature->SetGeometry(poColl->getGeometryRef(i));
343 38 : eStatus = ICreateFeature(poTmpFeature);
344 : }
345 18 : delete poTmpFeature;
346 18 : return nullptr;
347 : }
348 : break;
349 : /*-------------------------------------------------------------
350 : * Unsupported type.... convert to MapInfo geometry NONE
351 : *------------------------------------------------------------*/
352 78 : case wkbUnknown:
353 : default:
354 78 : poTABFeature = new TABFeature(poFeature->GetDefnRef());
355 78 : break;
356 : }
357 :
358 15206 : if (poGeom != nullptr)
359 15128 : poTABFeature->SetGeometryDirectly(poGeom->clone());
360 :
361 31169 : for (int i = 0; i < poFeature->GetDefnRef()->GetFieldCount(); i++)
362 : {
363 15963 : poTABFeature->SetField(i, poFeature->GetRawFieldRef(i));
364 : }
365 :
366 15206 : poTABFeature->SetFID(poFeature->GetFID());
367 :
368 15206 : return poTABFeature;
369 : }
370 :
371 : /**********************************************************************
372 : * IMapInfoFile::ICreateFeature()
373 : *
374 : * Standard OGR CreateFeature implementation. This method is used
375 : * to create a new feature in current dataset
376 : **********************************************************************/
377 15008 : OGRErr IMapInfoFile::ICreateFeature(OGRFeature *poFeature)
378 : {
379 15008 : TABFeature *poTABFeature = CreateTABFeature(poFeature);
380 15008 : if (poTABFeature == nullptr) /* MultiGeometry */
381 18 : return OGRERR_NONE;
382 :
383 14990 : OGRErr eErr = CreateFeature(poTABFeature);
384 14990 : if (eErr == OGRERR_NONE)
385 14989 : poFeature->SetFID(poTABFeature->GetFID());
386 :
387 14990 : delete poTABFeature;
388 :
389 14990 : return eErr;
390 : }
391 :
392 : /**********************************************************************
393 : * IMapInfoFile::GetFeature()
394 : *
395 : * Standard OGR GetFeature implementation. This method is used
396 : * to get the wanted (nFeatureId) feature, a NULL value will be
397 : * returned on error.
398 : **********************************************************************/
399 292 : OGRFeature *IMapInfoFile::GetFeature(GIntBig nFeatureId)
400 : {
401 : /*fprintf(stderr, "GetFeature(%ld)\n", nFeatureId);*/
402 :
403 292 : OGRFeature *poFeatureRef = GetFeatureRef(nFeatureId);
404 292 : if (poFeatureRef)
405 : {
406 : // Avoid cloning feature... return the copy owned by the class
407 274 : CPLAssert(poFeatureRef == m_poCurFeature);
408 274 : m_poCurFeature = nullptr;
409 :
410 274 : return poFeatureRef;
411 : }
412 : else
413 18 : return nullptr;
414 : }
415 :
416 : /************************************************************************/
417 : /* GetTABType() */
418 : /* */
419 : /* Create a native field based on a generic OGR definition. */
420 : /************************************************************************/
421 :
422 352 : int IMapInfoFile::GetTABType(const OGRFieldDefn *poField,
423 : TABFieldType *peTABType, int *pnWidth,
424 : int *pnPrecision)
425 : {
426 : TABFieldType eTABType;
427 352 : int nWidth = poField->GetWidth();
428 : int nPrecision =
429 352 : poField->GetType() == OFTReal ? poField->GetPrecision() : 0;
430 :
431 352 : if (poField->GetType() == OFTInteger)
432 : {
433 103 : if (poField->GetSubType() == OFSTBoolean)
434 : {
435 2 : eTABType = TABFLogical;
436 2 : nWidth = 1;
437 : }
438 : else
439 : {
440 101 : eTABType = TABFInteger;
441 101 : if (nWidth == 0)
442 86 : nWidth = 12;
443 : }
444 : }
445 249 : else if (poField->GetType() == OFTInteger64)
446 : {
447 4 : eTABType = TABFLargeInt;
448 4 : if (nWidth == 0)
449 4 : nWidth = 20;
450 : }
451 245 : else if (poField->GetType() == OFTReal)
452 : {
453 41 : if (nWidth == 0 && poField->GetPrecision() == 0)
454 : {
455 29 : eTABType = TABFFloat;
456 29 : nWidth = 32;
457 : }
458 : else
459 : {
460 12 : eTABType = TABFDecimal;
461 : // Enforce Mapinfo limits, otherwise MapInfo will crash (#6392)
462 12 : if (nWidth > 20 || nWidth - nPrecision < 2 || nPrecision > 16)
463 : {
464 4 : if (nWidth > 20)
465 4 : nWidth = 20;
466 4 : if (nWidth - nPrecision < 2)
467 1 : nPrecision = nWidth - 2;
468 4 : if (nPrecision > 16)
469 1 : nPrecision = 16;
470 4 : CPLDebug("MITAB",
471 : "Adjusting initial width,precision of %s from %d,%d "
472 : "to %d,%d",
473 : poField->GetNameRef(), poField->GetWidth(),
474 : poField->GetPrecision(), nWidth, nPrecision);
475 : }
476 : }
477 : }
478 204 : else if (poField->GetType() == OFTDate)
479 : {
480 19 : eTABType = TABFDate;
481 19 : if (nWidth == 0)
482 17 : nWidth = 10;
483 : }
484 185 : else if (poField->GetType() == OFTTime)
485 : {
486 3 : eTABType = TABFTime;
487 3 : if (nWidth == 0)
488 1 : nWidth = 9;
489 : }
490 182 : else if (poField->GetType() == OFTDateTime)
491 : {
492 19 : eTABType = TABFDateTime;
493 19 : if (nWidth == 0)
494 17 : nWidth = 19;
495 : }
496 163 : else if (poField->GetType() == OFTString)
497 : {
498 163 : eTABType = TABFChar;
499 163 : if (nWidth == 0)
500 118 : nWidth = 254;
501 : else
502 45 : nWidth = std::min(254, nWidth);
503 : }
504 : else
505 : {
506 0 : CPLError(CE_Failure, CPLE_AppDefined,
507 : "IMapInfoFile::CreateField() called with unsupported field"
508 : " type %d.\n"
509 : "Note that Mapinfo files don't support list field types.\n",
510 0 : poField->GetType());
511 :
512 0 : return -1;
513 : }
514 :
515 352 : if (peTABType)
516 333 : *peTABType = eTABType;
517 352 : if (pnWidth)
518 341 : *pnWidth = nWidth;
519 352 : if (pnPrecision)
520 341 : *pnPrecision = nPrecision;
521 :
522 352 : return 0;
523 : }
524 :
525 : /************************************************************************/
526 : /* CreateField() */
527 : /* */
528 : /* Create a native field based on a generic OGR definition. */
529 : /************************************************************************/
530 :
531 322 : OGRErr IMapInfoFile::CreateField(const OGRFieldDefn *poField, int bApproxOK)
532 :
533 : {
534 : TABFieldType eTABType;
535 322 : int nWidth = 0;
536 322 : int nPrecision = 0;
537 :
538 322 : if (GetTABType(poField, &eTABType, &nWidth, &nPrecision) < 0)
539 0 : return OGRERR_FAILURE;
540 :
541 322 : if (AddFieldNative(poField->GetNameRef(), eTABType, nWidth, nPrecision,
542 644 : FALSE, FALSE, bApproxOK) > -1)
543 322 : return OGRERR_NONE;
544 :
545 0 : return OGRERR_FAILURE;
546 : }
547 :
548 : /**********************************************************************
549 : * IMapInfoFile::SetCharset()
550 : *
551 : * Set the charset for the tab header.
552 : *
553 : *
554 : * Returns 0 on success, -1 on error.
555 : **********************************************************************/
556 2374 : int IMapInfoFile::SetCharset(const char *pszCharset)
557 : {
558 2374 : if (pszCharset && strlen(pszCharset) > 0)
559 : {
560 2374 : if (pszCharset == m_pszCharset)
561 : {
562 0 : return 0;
563 : }
564 2374 : CPLFree(m_pszCharset);
565 2374 : m_pszCharset = CPLStrdup(pszCharset);
566 2374 : return 0;
567 : }
568 0 : return -1;
569 : }
570 :
571 5327 : const char *IMapInfoFile::GetCharset() const
572 : {
573 5327 : return m_pszCharset;
574 : }
575 :
576 : // Table is adopted from
577 : // http://www.i-signum.com/Formation/download/MB_ReferenceGuide.pdf pp. 127-128
578 : // NOTE: if modifying this table, please keep doc/source/drivers/vector/mapinfo_encodings.csv in sync
579 : static const char *const apszCharsets[][2] = {
580 : {"Neutral", ""}, // No character conversions performed.
581 : {"ISO8859_1", "ISO-8859-1"}, // ISO 8859-1 (UNIX)
582 : {"ISO8859_2", "ISO-8859-2"}, // ISO 8859-2 (UNIX)
583 : {"ISO8859_3", "ISO-8859-3"}, // ISO 8859-3 (UNIX)
584 : {"ISO8859_4", "ISO-8859-4"}, // ISO 8859-4 (UNIX)
585 : {"ISO8859_5", "ISO-8859-5"}, // ISO 8859-5 (UNIX)
586 : {"ISO8859_6", "ISO-8859-6"}, // ISO 8859-6 (UNIX)
587 : {"ISO8859_7", "ISO-8859-7"}, // ISO 8859-7 (UNIX)
588 : {"ISO8859_8", "ISO-8859-8"}, // ISO 8859-8 (UNIX)
589 : {"ISO8859_9", "ISO-8859-9"}, // ISO 8859-9 (UNIX)
590 : {"PackedEUCJapaese", "EUC-JP"}, // UNIX, standard Japanese implementation.
591 : {"WindowsLatin1", "CP1252"},
592 : {"WindowsLatin2", "CP1250"},
593 : {"WindowsArabic", "CP1256"},
594 : {"WindowsCyrillic", "CP1251"},
595 : {"WindowsBalticRim", "CP1257"},
596 : {"WindowsGreek", "CP1253"},
597 : {"WindowsHebrew", "CP1255"},
598 : {"WindowsTurkish", "CP1254"}, // Windows Eastern Europe
599 : {"WindowsTradChinese", "CP950"}, // Windows Traditional Chinese
600 : {"WindowsSimpChinese", "CP936"}, // Windows Simplified Chinese
601 : {"WindowsJapanese", "CP932"},
602 : {"WindowsKorean", "CP949"},
603 : {"CodePage437", "CP437"}, // DOS Code Page 437 = IBM Extended ASCII
604 : {"CodePage850", "CP850"}, // DOS Code Page 850 = Multilingual
605 : {"CodePage852", "CP852"}, // DOS Code Page 852 = Eastern Europe
606 : {"CodePage855", "CP855"}, // DOS Code Page 855 = Cyrillic
607 : {"CodePage857", "CP857"},
608 : {"CodePage860", "CP860"}, // DOS Code Page 860 = Portuguese
609 : {"CodePage861", "CP861"}, // DOS Code Page 861 = Icelandic
610 : {"CodePage863", "CP863"}, // DOS Code Page 863 = French Canadian
611 : {"CodePage864", "CP864"}, // DOS Code Page 864 = Arabic
612 : {"CodePage865", "CP865"}, // DOS Code Page 865 = Nordic
613 : {"CodePage869", "CP869"}, // DOS Code Page 869 = Modern Greek
614 : {"LICS", ""}, // Lotus worksheet release 1,2 character set
615 : {"LMBCS", ""}, // Lotus worksheet release 3,4 character set
616 : {"UTF-8", "UTF-8"},
617 : {nullptr, nullptr}};
618 :
619 7530 : const char *IMapInfoFile::CharsetToEncoding(const char *pszCharset)
620 : {
621 7530 : if (pszCharset == nullptr)
622 : {
623 874 : return apszCharsets[0][1];
624 : }
625 :
626 18049 : for (size_t i = 0; apszCharsets[i][0] != nullptr; ++i)
627 : {
628 18043 : if (EQUAL(pszCharset, apszCharsets[i][0]))
629 : {
630 6650 : return apszCharsets[i][1];
631 : }
632 : }
633 :
634 6 : CPLError(CE_Warning, CPLE_NotSupported,
635 : "Cannot find iconv encoding corresponding to MapInfo %s charset",
636 : pszCharset);
637 6 : return apszCharsets[0][1];
638 : }
639 :
640 361 : const char *IMapInfoFile::EncodingToCharset(const char *pszEncoding)
641 : {
642 361 : if (pszEncoding == nullptr)
643 : {
644 352 : return apszCharsets[0][0];
645 : }
646 :
647 179 : for (size_t i = 0; apszCharsets[i][1] != nullptr; ++i)
648 : {
649 179 : if (EQUAL(pszEncoding, apszCharsets[i][1]))
650 : {
651 9 : return apszCharsets[i][0];
652 : }
653 : }
654 :
655 0 : CPLError(CE_Warning, CPLE_NotSupported,
656 : "Cannot find MapInfo charset corresponding to iconv %s encoding",
657 : pszEncoding);
658 0 : return apszCharsets[0][0];
659 : }
660 :
661 3920 : const char *IMapInfoFile::GetEncoding() const
662 : {
663 3920 : return CharsetToEncoding(GetCharset());
664 : }
665 :
666 0 : void IMapInfoFile::SetEncoding(const char *pszEncoding)
667 : {
668 0 : SetCharset(EncodingToCharset(pszEncoding));
669 0 : }
670 :
671 360 : void IMapInfoFile::SetStrictLaundering(bool bStrictLaundering)
672 : {
673 360 : m_bStrictLaundering = bStrictLaundering;
674 360 : }
675 :
676 69 : int IMapInfoFile::TestUtf8Capability() const
677 : {
678 69 : const char *pszEncoding(GetEncoding());
679 69 : if (strlen(pszEncoding) == 0)
680 : {
681 55 : return FALSE;
682 : }
683 :
684 14 : return CPLCanRecode("test", GetEncoding(), CPL_ENC_UTF8);
685 : }
686 :
687 493 : CPLString IMapInfoFile::NormalizeFieldName(const char *pszName) const
688 : {
689 986 : CPLString osName(pszName);
690 493 : if (strlen(GetEncoding()) > 0)
691 84 : osName.Recode(CPL_ENC_UTF8, GetEncoding());
692 :
693 : char szNewFieldName[31 + 1]; /* 31 is the max characters for a field name*/
694 493 : unsigned int nRenameNum = 1;
695 :
696 493 : strncpy(szNewFieldName, osName.c_str(), sizeof(szNewFieldName) - 1);
697 493 : szNewFieldName[sizeof(szNewFieldName) - 1] = '\0';
698 :
699 986 : while (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
700 1479 : m_oSetFields.end() &&
701 : nRenameNum < 10)
702 : {
703 0 : CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.29s_%.1u",
704 : osName.c_str(), nRenameNum);
705 0 : nRenameNum++;
706 : }
707 :
708 986 : while (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
709 1479 : m_oSetFields.end() &&
710 : nRenameNum < 100)
711 : {
712 0 : CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.29s%.2u",
713 : osName.c_str(), nRenameNum);
714 0 : nRenameNum++;
715 : }
716 :
717 493 : if (m_oSetFields.find(CPLString(szNewFieldName).toupper()) !=
718 986 : m_oSetFields.end())
719 : {
720 0 : CPLError(CE_Failure, CPLE_NotSupported,
721 : "Too many field names like '%s' when truncated to 31 letters "
722 : "for MapInfo format.",
723 : pszName);
724 : }
725 :
726 493 : CPLString osNewFieldName(szNewFieldName);
727 493 : if (strlen(GetEncoding()) > 0)
728 84 : osNewFieldName.Recode(GetEncoding(), CPL_ENC_UTF8);
729 :
730 493 : if (!EQUAL(pszName, osNewFieldName.c_str()))
731 : {
732 0 : CPLError(CE_Warning, CPLE_NotSupported,
733 : "Normalized/laundered field name: '%s' to '%s'", pszName,
734 : osNewFieldName.c_str());
735 : }
736 :
737 986 : return osNewFieldName;
738 : }
|