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