Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: DXF Translator
4 : * Purpose: Implements OGRDXFWriterLayer - the OGRLayer class used for
5 : * writing a DXF file.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
10 : * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "ogr_dxf.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_string.h"
34 : #include "ogr_featurestyle.h"
35 :
36 : #include <cstdlib>
37 :
38 : /************************************************************************/
39 : /* OGRDXFWriterLayer() */
40 : /************************************************************************/
41 :
42 44 : OGRDXFWriterLayer::OGRDXFWriterLayer(OGRDXFWriterDS *poDSIn, VSILFILE *fpIn)
43 : : fp(fpIn),
44 : poFeatureDefn(nullptr), // TODO(schwehr): Can I move the new here?
45 44 : poDS(poDSIn)
46 : {
47 44 : nNextAutoID = 1;
48 44 : bWriteHatch = CPLTestBool(CPLGetConfigOption("DXF_WRITE_HATCH", "YES"));
49 :
50 44 : poFeatureDefn = new OGRFeatureDefn("entities");
51 44 : poFeatureDefn->Reference();
52 :
53 44 : OGRDXFDataSource::AddStandardFields(poFeatureDefn, ODFM_IncludeBlockFields);
54 44 : }
55 :
56 : /************************************************************************/
57 : /* ~OGRDXFWriterLayer() */
58 : /************************************************************************/
59 :
60 88 : OGRDXFWriterLayer::~OGRDXFWriterLayer()
61 :
62 : {
63 44 : if (poFeatureDefn)
64 44 : poFeatureDefn->Release();
65 88 : }
66 :
67 : /************************************************************************/
68 : /* ResetFP() */
69 : /* */
70 : /* Redirect output. Mostly used for writing block definitions. */
71 : /************************************************************************/
72 :
73 2 : void OGRDXFWriterLayer::ResetFP(VSILFILE *fpNew)
74 :
75 : {
76 2 : fp = fpNew;
77 2 : }
78 :
79 : /************************************************************************/
80 : /* TestCapability() */
81 : /************************************************************************/
82 :
83 88 : int OGRDXFWriterLayer::TestCapability(const char *pszCap)
84 :
85 : {
86 88 : if (EQUAL(pszCap, OLCStringsAsUTF8))
87 0 : return TRUE;
88 88 : else if (EQUAL(pszCap, OLCSequentialWrite))
89 16 : return TRUE;
90 : else
91 72 : return FALSE;
92 : }
93 :
94 : /************************************************************************/
95 : /* CreateField() */
96 : /* */
97 : /* This is really a dummy as our fields are precreated. */
98 : /************************************************************************/
99 :
100 80 : OGRErr OGRDXFWriterLayer::CreateField(const OGRFieldDefn *poField,
101 : int bApproxOK)
102 :
103 : {
104 80 : if (poFeatureDefn->GetFieldIndex(poField->GetNameRef()) >= 0 && bApproxOK)
105 0 : return OGRERR_NONE;
106 80 : if (EQUAL(poField->GetNameRef(), "OGR_STYLE"))
107 : {
108 0 : poFeatureDefn->AddFieldDefn(poField);
109 0 : return OGRERR_NONE;
110 : }
111 :
112 80 : CPLError(CE_Failure, CPLE_AppDefined,
113 : "DXF layer does not support arbitrary field creation, field '%s' "
114 : "not created.",
115 : poField->GetNameRef());
116 :
117 80 : return OGRERR_FAILURE;
118 : }
119 :
120 : /************************************************************************/
121 : /* WriteValue() */
122 : /************************************************************************/
123 :
124 703 : int OGRDXFWriterLayer::WriteValue(int nCode, const char *pszValue)
125 :
126 : {
127 703 : CPLString osLinePair;
128 :
129 703 : osLinePair.Printf("%3d\n", nCode);
130 :
131 703 : if (strlen(pszValue) < 255)
132 703 : osLinePair += pszValue;
133 : else
134 0 : osLinePair.append(pszValue, 255);
135 :
136 703 : osLinePair += "\n";
137 :
138 703 : return VSIFWriteL(osLinePair.c_str(), 1, osLinePair.size(), fp) ==
139 1406 : osLinePair.size();
140 : }
141 :
142 : /************************************************************************/
143 : /* WriteValue() */
144 : /************************************************************************/
145 :
146 355 : int OGRDXFWriterLayer::WriteValue(int nCode, int nValue)
147 :
148 : {
149 355 : CPLString osLinePair;
150 :
151 355 : osLinePair.Printf("%3d\n%d\n", nCode, nValue);
152 :
153 355 : return VSIFWriteL(osLinePair.c_str(), 1, osLinePair.size(), fp) ==
154 710 : osLinePair.size();
155 : }
156 :
157 : /************************************************************************/
158 : /* WriteValue() */
159 : /************************************************************************/
160 :
161 597 : int OGRDXFWriterLayer::WriteValue(int nCode, double dfValue)
162 :
163 : {
164 : char szLinePair[64];
165 :
166 597 : CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue);
167 597 : size_t nLen = strlen(szLinePair);
168 :
169 597 : return VSIFWriteL(szLinePair, 1, nLen, fp) == nLen;
170 : }
171 :
172 : /************************************************************************/
173 : /* WriteCore() */
174 : /* */
175 : /* Write core fields common to all sorts of elements. */
176 : /************************************************************************/
177 :
178 167 : OGRErr OGRDXFWriterLayer::WriteCore(OGRFeature *poFeature)
179 :
180 : {
181 : /* -------------------------------------------------------------------- */
182 : /* Write out an entity id. I'm not sure why this is critical, */
183 : /* but it seems that VoloView will just quietly fail to open */
184 : /* dxf files without entity ids set on most/all entities. */
185 : /* Also, for reasons I don't understand these ids seem to have */
186 : /* to start somewhere around 0x50 hex (80 decimal). */
187 : /* -------------------------------------------------------------------- */
188 167 : long nGotFID = -1;
189 167 : poDS->WriteEntityID(fp, nGotFID, (int)poFeature->GetFID());
190 167 : poFeature->SetFID(nGotFID);
191 :
192 167 : WriteValue(100, "AcDbEntity");
193 :
194 : /* -------------------------------------------------------------------- */
195 : /* For now we assign everything to the default layer - layer */
196 : /* "0" - if there is no layer property on the source features. */
197 : /* -------------------------------------------------------------------- */
198 167 : const char *pszLayer = poFeature->GetFieldAsString("Layer");
199 167 : if (pszLayer == nullptr || strlen(pszLayer) == 0)
200 : {
201 160 : WriteValue(8, "0");
202 : }
203 : else
204 : {
205 14 : CPLString osSanitizedLayer(pszLayer);
206 : // Replaced restricted characters with underscore
207 : // See
208 : // http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
209 7 : const char achForbiddenChars[] = {'<', '>', '/', '\\', '"', ':',
210 : ';', '?', '*', '|', '=', '\''};
211 91 : for (size_t i = 0; i < CPL_ARRAYSIZE(achForbiddenChars); ++i)
212 : {
213 84 : osSanitizedLayer.replaceAll(achForbiddenChars[i], '_');
214 : }
215 :
216 : // also remove newline characters (#15067)
217 7 : osSanitizedLayer.replaceAll("\r\n", "_");
218 7 : osSanitizedLayer.replaceAll('\r', '_');
219 7 : osSanitizedLayer.replaceAll('\n', '_');
220 :
221 : const char *pszExists =
222 7 : poDS->oHeaderDS.LookupLayerProperty(osSanitizedLayer, "Exists");
223 14 : if ((pszExists == nullptr || strlen(pszExists) == 0) &&
224 7 : CSLFindString(poDS->papszLayersToCreate, osSanitizedLayer) == -1)
225 : {
226 6 : poDS->papszLayersToCreate =
227 3 : CSLAddString(poDS->papszLayersToCreate, osSanitizedLayer);
228 : }
229 :
230 7 : WriteValue(8, osSanitizedLayer);
231 : }
232 :
233 167 : return OGRERR_NONE;
234 : }
235 :
236 : /************************************************************************/
237 : /* WriteINSERT() */
238 : /************************************************************************/
239 :
240 7 : OGRErr OGRDXFWriterLayer::WriteINSERT(OGRFeature *poFeature)
241 :
242 : {
243 7 : WriteValue(0, "INSERT");
244 7 : WriteCore(poFeature);
245 7 : WriteValue(100, "AcDbBlockReference");
246 7 : WriteValue(2, poFeature->GetFieldAsString("BlockName"));
247 :
248 : // Write style symbol color
249 7 : OGRStyleTool *poTool = nullptr;
250 14 : OGRStyleMgr oSM;
251 7 : if (poFeature->GetStyleString() != nullptr)
252 : {
253 0 : oSM.InitFromFeature(poFeature);
254 :
255 0 : if (oSM.GetPartCount() > 0)
256 0 : poTool = oSM.GetPart(0);
257 : }
258 7 : if (poTool && poTool->GetType() == OGRSTCSymbol)
259 : {
260 0 : OGRStyleSymbol *poSymbol = (OGRStyleSymbol *)poTool;
261 : GBool bDefault;
262 :
263 0 : if (poSymbol->Color(bDefault) != nullptr && !bDefault)
264 0 : WriteValue(62, ColorStringToDXFColor(poSymbol->Color(bDefault)));
265 : }
266 7 : delete poTool;
267 :
268 : /* -------------------------------------------------------------------- */
269 : /* Write location in OCS. */
270 : /* -------------------------------------------------------------------- */
271 7 : int nCoordCount = 0;
272 : const double *padfCoords =
273 7 : poFeature->GetFieldAsDoubleList("BlockOCSCoords", &nCoordCount);
274 :
275 7 : if (nCoordCount == 3)
276 : {
277 0 : WriteValue(10, padfCoords[0]);
278 0 : WriteValue(20, padfCoords[1]);
279 0 : if (!WriteValue(30, padfCoords[2]))
280 0 : return OGRERR_FAILURE;
281 : }
282 : else
283 : {
284 : // We don't have an OCS; we will just assume that the location of
285 : // the geometry (in WCS) is the correct insertion point.
286 7 : OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
287 :
288 7 : WriteValue(10, poPoint->getX());
289 7 : if (!WriteValue(20, poPoint->getY()))
290 0 : return OGRERR_FAILURE;
291 :
292 7 : if (poPoint->getGeometryType() == wkbPoint25D)
293 : {
294 0 : if (!WriteValue(30, poPoint->getZ()))
295 0 : return OGRERR_FAILURE;
296 : }
297 : }
298 :
299 : /* -------------------------------------------------------------------- */
300 : /* Write scaling. */
301 : /* -------------------------------------------------------------------- */
302 7 : int nScaleCount = 0;
303 : const double *padfScale =
304 7 : poFeature->GetFieldAsDoubleList("BlockScale", &nScaleCount);
305 :
306 7 : if (nScaleCount == 3)
307 : {
308 1 : WriteValue(41, padfScale[0]);
309 1 : WriteValue(42, padfScale[1]);
310 1 : WriteValue(43, padfScale[2]);
311 : }
312 :
313 : /* -------------------------------------------------------------------- */
314 : /* Write rotation. */
315 : /* -------------------------------------------------------------------- */
316 7 : const double dfAngle = poFeature->GetFieldAsDouble("BlockAngle");
317 :
318 7 : if (dfAngle != 0.0)
319 : {
320 1 : WriteValue(50, dfAngle); // degrees
321 : }
322 :
323 : /* -------------------------------------------------------------------- */
324 : /* Write OCS normal vector. */
325 : /* -------------------------------------------------------------------- */
326 7 : int nOCSCount = 0;
327 : const double *padfOCS =
328 7 : poFeature->GetFieldAsDoubleList("BlockOCSNormal", &nOCSCount);
329 :
330 7 : if (nOCSCount == 3)
331 : {
332 0 : WriteValue(210, padfOCS[0]);
333 0 : WriteValue(220, padfOCS[1]);
334 0 : WriteValue(230, padfOCS[2]);
335 : }
336 :
337 7 : return OGRERR_NONE;
338 : }
339 :
340 : /************************************************************************/
341 : /* WritePOINT() */
342 : /************************************************************************/
343 :
344 111 : OGRErr OGRDXFWriterLayer::WritePOINT(OGRFeature *poFeature)
345 :
346 : {
347 111 : WriteValue(0, "POINT");
348 111 : WriteCore(poFeature);
349 111 : WriteValue(100, "AcDbPoint");
350 :
351 : // Write style pen color
352 111 : OGRStyleTool *poTool = nullptr;
353 222 : OGRStyleMgr oSM;
354 111 : if (poFeature->GetStyleString() != nullptr)
355 : {
356 0 : oSM.InitFromFeature(poFeature);
357 :
358 0 : if (oSM.GetPartCount() > 0)
359 0 : poTool = oSM.GetPart(0);
360 : }
361 111 : if (poTool && poTool->GetType() == OGRSTCPen)
362 : {
363 0 : OGRStylePen *poPen = (OGRStylePen *)poTool;
364 : GBool bDefault;
365 :
366 0 : if (poPen->Color(bDefault) != nullptr && !bDefault)
367 0 : WriteValue(62, ColorStringToDXFColor(poPen->Color(bDefault)));
368 : }
369 111 : delete poTool;
370 :
371 111 : OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
372 :
373 111 : WriteValue(10, poPoint->getX());
374 111 : if (!WriteValue(20, poPoint->getY()))
375 0 : return OGRERR_FAILURE;
376 :
377 111 : if (poPoint->getGeometryType() == wkbPoint25D)
378 : {
379 4 : if (!WriteValue(30, poPoint->getZ()))
380 0 : return OGRERR_FAILURE;
381 : }
382 :
383 111 : return OGRERR_NONE;
384 : }
385 :
386 : /************************************************************************/
387 : /* TextEscape() */
388 : /* */
389 : /* Translate UTF8 to Win1252 and escape special characters like */
390 : /* newline and space with DXF style escapes. Note that */
391 : /* non-win1252 unicode characters are translated using the */
392 : /* unicode escape sequence. */
393 : /************************************************************************/
394 :
395 1 : CPLString OGRDXFWriterLayer::TextEscape(const char *pszInput)
396 :
397 : {
398 1 : CPLString osResult;
399 1 : wchar_t *panInput = CPLRecodeToWChar(pszInput, CPL_ENC_UTF8, CPL_ENC_UCS2);
400 31 : for (int i = 0; panInput[i] != 0; i++)
401 : {
402 30 : if (panInput[i] == '\n')
403 : {
404 0 : osResult += "\\P";
405 : }
406 30 : else if (panInput[i] == ' ')
407 : {
408 2 : osResult += "\\~";
409 : }
410 28 : else if (panInput[i] == '\\')
411 : {
412 0 : osResult += "\\\\";
413 : }
414 28 : else if (panInput[i] == '^')
415 : {
416 1 : osResult += "^ ";
417 : }
418 27 : else if (panInput[i] < ' ')
419 : {
420 1 : osResult += '^';
421 1 : osResult += static_cast<char>(panInput[i] + '@');
422 : }
423 26 : else if (panInput[i] > 255)
424 : {
425 0 : CPLString osUnicode;
426 0 : osUnicode.Printf("\\U+%04x", (int)panInput[i]);
427 0 : osResult += osUnicode;
428 : }
429 : else
430 : {
431 26 : osResult += (char)panInput[i];
432 : }
433 : }
434 :
435 1 : CPLFree(panInput);
436 :
437 1 : return osResult;
438 : }
439 :
440 : /************************************************************************/
441 : /* PrepareTextStyleDefinition() */
442 : /************************************************************************/
443 : std::map<CPLString, CPLString>
444 1 : OGRDXFWriterLayer::PrepareTextStyleDefinition(OGRStyleLabel *poLabelTool)
445 : {
446 : GBool bDefault;
447 :
448 1 : std::map<CPLString, CPLString> oTextStyleDef;
449 :
450 : /* -------------------------------------------------------------------- */
451 : /* Fetch the data for this text style. */
452 : /* -------------------------------------------------------------------- */
453 1 : const char *pszFontName = poLabelTool->FontName(bDefault);
454 1 : if (!bDefault)
455 1 : oTextStyleDef["Font"] = pszFontName;
456 :
457 1 : const GBool bBold = poLabelTool->Bold(bDefault);
458 1 : if (!bDefault)
459 1 : oTextStyleDef["Bold"] = bBold ? "1" : "0";
460 :
461 1 : const GBool bItalic = poLabelTool->Italic(bDefault);
462 1 : if (!bDefault)
463 0 : oTextStyleDef["Italic"] = bItalic ? "1" : "0";
464 :
465 1 : const double dfStretch = poLabelTool->Stretch(bDefault);
466 1 : if (!bDefault)
467 : {
468 1 : oTextStyleDef["Width"] = CPLString().Printf("%f", dfStretch / 100.0);
469 : }
470 :
471 2 : return oTextStyleDef;
472 : }
473 :
474 : /************************************************************************/
475 : /* WriteTEXT() */
476 : /************************************************************************/
477 :
478 1 : OGRErr OGRDXFWriterLayer::WriteTEXT(OGRFeature *poFeature)
479 :
480 : {
481 1 : WriteValue(0, "MTEXT");
482 1 : WriteCore(poFeature);
483 1 : WriteValue(100, "AcDbMText");
484 :
485 : /* -------------------------------------------------------------------- */
486 : /* Do we have styling information? */
487 : /* -------------------------------------------------------------------- */
488 1 : OGRStyleTool *poTool = nullptr;
489 2 : OGRStyleMgr oSM;
490 :
491 1 : if (poFeature->GetStyleString() != nullptr)
492 : {
493 1 : oSM.InitFromFeature(poFeature);
494 :
495 1 : if (oSM.GetPartCount() > 0)
496 1 : poTool = oSM.GetPart(0);
497 : }
498 :
499 : /* ==================================================================== */
500 : /* Process the LABEL tool. */
501 : /* ==================================================================== */
502 1 : double dfDx = 0.0;
503 1 : double dfDy = 0.0;
504 :
505 1 : if (poTool && poTool->GetType() == OGRSTCLabel)
506 : {
507 1 : OGRStyleLabel *poLabel = (OGRStyleLabel *)poTool;
508 : GBool bDefault;
509 :
510 : /* --------------------------------------------------------------------
511 : */
512 : /* Color */
513 : /* --------------------------------------------------------------------
514 : */
515 1 : if (poLabel->ForeColor(bDefault) != nullptr && !bDefault)
516 1 : WriteValue(62, ColorStringToDXFColor(poLabel->ForeColor(bDefault)));
517 :
518 : /* --------------------------------------------------------------------
519 : */
520 : /* Angle */
521 : /* --------------------------------------------------------------------
522 : */
523 1 : const double dfAngle = poLabel->Angle(bDefault);
524 :
525 1 : if (!bDefault)
526 1 : WriteValue(50, dfAngle);
527 :
528 : /* --------------------------------------------------------------------
529 : */
530 : /* Height - We need to fetch this in georeferenced units - I'm */
531 : /* doubt the default translation mechanism will be much good. */
532 : /* --------------------------------------------------------------------
533 : */
534 1 : poTool->SetUnit(OGRSTUGround);
535 1 : const double dfHeight = poLabel->Size(bDefault);
536 :
537 1 : if (!bDefault)
538 1 : WriteValue(40, dfHeight);
539 :
540 : /* --------------------------------------------------------------------
541 : */
542 : /* Anchor / Attachment Point */
543 : /* --------------------------------------------------------------------
544 : */
545 1 : const int nAnchor = poLabel->Anchor(bDefault);
546 :
547 1 : if (!bDefault)
548 : {
549 : const static int anAnchorMap[] = {-1, 7, 8, 9, 4, 5, 6,
550 : 1, 2, 3, 7, 8, 9};
551 :
552 0 : if (nAnchor > 0 && nAnchor < 13)
553 0 : WriteValue(71, anAnchorMap[nAnchor]);
554 : }
555 :
556 : /* --------------------------------------------------------------------
557 : */
558 : /* Offset */
559 : /* --------------------------------------------------------------------
560 : */
561 1 : dfDx = poLabel->SpacingX(bDefault);
562 1 : dfDy = poLabel->SpacingY(bDefault);
563 :
564 : /* --------------------------------------------------------------------
565 : */
566 : /* Escape the text, and convert to ISO8859. */
567 : /* --------------------------------------------------------------------
568 : */
569 1 : const char *pszText = poLabel->TextString(bDefault);
570 :
571 1 : if (pszText != nullptr && !bDefault)
572 : {
573 2 : CPLString osEscaped = TextEscape(pszText);
574 1 : while (osEscaped.size() > 250)
575 : {
576 0 : WriteValue(3, osEscaped.substr(0, 250).c_str());
577 0 : osEscaped.erase(0, 250);
578 : }
579 1 : WriteValue(1, osEscaped);
580 : }
581 :
582 : /* --------------------------------------------------------------------
583 : */
584 : /* Store the text style in the map. */
585 : /* --------------------------------------------------------------------
586 : */
587 : std::map<CPLString, CPLString> oTextStyleDef =
588 2 : PrepareTextStyleDefinition(poLabel);
589 2 : CPLString osStyleName;
590 :
591 1 : for (const auto &oPair : oNewTextStyles)
592 : {
593 0 : if (oPair.second == oTextStyleDef)
594 : {
595 0 : osStyleName = oPair.first;
596 0 : break;
597 : }
598 : }
599 :
600 1 : if (osStyleName == "")
601 : {
602 :
603 0 : do
604 : {
605 1 : osStyleName.Printf("AutoTextStyle-%d", nNextAutoID++);
606 1 : } while (poDS->oHeaderDS.TextStyleExists(osStyleName));
607 :
608 1 : oNewTextStyles[osStyleName] = std::move(oTextStyleDef);
609 : }
610 :
611 1 : WriteValue(7, osStyleName);
612 : }
613 :
614 1 : delete poTool;
615 :
616 : /* -------------------------------------------------------------------- */
617 : /* Write the location. */
618 : /* -------------------------------------------------------------------- */
619 1 : OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
620 :
621 1 : WriteValue(10, poPoint->getX() + dfDx);
622 1 : if (!WriteValue(20, poPoint->getY() + dfDy))
623 0 : return OGRERR_FAILURE;
624 :
625 1 : if (poPoint->getGeometryType() == wkbPoint25D)
626 : {
627 1 : if (!WriteValue(30, poPoint->getZ()))
628 0 : return OGRERR_FAILURE;
629 : }
630 :
631 1 : return OGRERR_NONE;
632 : }
633 :
634 : /************************************************************************/
635 : /* PrepareLineTypeDefinition() */
636 : /************************************************************************/
637 : std::vector<double>
638 5 : OGRDXFWriterLayer::PrepareLineTypeDefinition(OGRStylePen *poPen)
639 : {
640 :
641 : /* -------------------------------------------------------------------- */
642 : /* Fetch pattern. */
643 : /* -------------------------------------------------------------------- */
644 : GBool bDefault;
645 5 : const char *pszPattern = poPen->Pattern(bDefault);
646 :
647 5 : if (bDefault || strlen(pszPattern) == 0)
648 0 : return std::vector<double>();
649 :
650 : /* -------------------------------------------------------------------- */
651 : /* Split into pen up / pen down bits. */
652 : /* -------------------------------------------------------------------- */
653 5 : char **papszTokens = CSLTokenizeString(pszPattern);
654 10 : std::vector<double> adfWeightTokens;
655 :
656 15 : for (int i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; i++)
657 : {
658 10 : const char *pszToken = papszTokens[i];
659 20 : CPLString osAmount;
660 20 : CPLString osDXFEntry;
661 :
662 : // Split amount and unit.
663 10 : const char *pszUnit = pszToken; // Used after for.
664 48 : for (; strchr("0123456789.", *pszUnit) != nullptr; pszUnit++)
665 : {
666 : }
667 :
668 10 : osAmount.assign(pszToken, (int)(pszUnit - pszToken));
669 :
670 : // If the unit is other than 'g' we really should be trying to
671 : // do some type of transformation - but what to do? Pretty hard.
672 :
673 : // Even entries are "pen down" represented as positive in DXF.
674 : // "Pen up" entries (gaps) are represented as negative.
675 10 : if (i % 2 == 0)
676 5 : adfWeightTokens.push_back(CPLAtof(osAmount));
677 : else
678 5 : adfWeightTokens.push_back(-CPLAtof(osAmount));
679 : }
680 :
681 5 : CSLDestroy(papszTokens);
682 :
683 5 : return adfWeightTokens;
684 : }
685 :
686 : /************************************************************************/
687 : /* IsLineTypeProportional() */
688 : /************************************************************************/
689 :
690 7 : static double IsLineTypeProportional(const std::vector<double> &adfA,
691 : const std::vector<double> &adfB)
692 : {
693 : // If they are not the same length, they are not the same linetype
694 7 : if (adfA.size() != adfB.size())
695 0 : return 0.0;
696 :
697 : // Determine the proportion of the first elements
698 7 : const double dfRatio = (adfA[0] != 0.0) ? (adfB[0] / adfA[0]) : 0.0;
699 :
700 : // Check if all elements follow this proportionality
701 9 : for (size_t iIndex = 1; iIndex < adfA.size(); iIndex++)
702 7 : if (fabs(adfB[iIndex] - (adfA[iIndex] * dfRatio)) > 1e-6)
703 5 : return 0.0;
704 :
705 2 : return dfRatio;
706 : }
707 :
708 : /************************************************************************/
709 : /* WritePOLYLINE() */
710 : /************************************************************************/
711 :
712 30 : OGRErr OGRDXFWriterLayer::WritePOLYLINE(OGRFeature *poFeature,
713 : const OGRGeometry *poGeom)
714 :
715 : {
716 : /* -------------------------------------------------------------------- */
717 : /* For now we handle multilinestrings by writing a series of */
718 : /* entities. */
719 : /* -------------------------------------------------------------------- */
720 30 : if (poGeom == nullptr)
721 26 : poGeom = poFeature->GetGeometryRef();
722 :
723 30 : if (poGeom->IsEmpty())
724 : {
725 0 : return OGRERR_NONE;
726 : }
727 :
728 60 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ||
729 30 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
730 : {
731 4 : const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
732 4 : OGRErr eErr = OGRERR_NONE;
733 8 : for (auto &&poMember : *poGC)
734 : {
735 4 : eErr = WritePOLYLINE(poFeature, poMember);
736 4 : if (eErr != OGRERR_NONE)
737 0 : break;
738 : }
739 :
740 4 : return eErr;
741 : }
742 :
743 : /* -------------------------------------------------------------------- */
744 : /* Polygons are written with on entity per ring. */
745 : /* -------------------------------------------------------------------- */
746 52 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ||
747 26 : wkbFlatten(poGeom->getGeometryType()) == wkbTriangle)
748 : {
749 0 : const OGRPolygon *poPoly = poGeom->toPolygon();
750 0 : OGRErr eErr = OGRERR_NONE;
751 0 : for (auto &&poRing : *poPoly)
752 : {
753 0 : eErr = WritePOLYLINE(poFeature, poRing);
754 0 : if (eErr != OGRERR_NONE)
755 0 : break;
756 : }
757 :
758 0 : return eErr;
759 : }
760 :
761 : /* -------------------------------------------------------------------- */
762 : /* Do we now have a geometry we can work with? */
763 : /* -------------------------------------------------------------------- */
764 26 : if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
765 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
766 :
767 26 : const OGRLineString *poLS = poGeom->toLineString();
768 :
769 : /* -------------------------------------------------------------------- */
770 : /* Write as a lightweight polygon, */
771 : /* or as POLYLINE if the line contains different heights */
772 : /* -------------------------------------------------------------------- */
773 26 : int bHasDifferentZ = FALSE;
774 26 : if (poLS->getGeometryType() == wkbLineString25D)
775 : {
776 8 : double z0 = poLS->getZ(0);
777 23 : for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
778 : {
779 16 : if (z0 != poLS->getZ(iVert))
780 : {
781 1 : bHasDifferentZ = TRUE;
782 1 : break;
783 : }
784 : }
785 : }
786 :
787 26 : WriteValue(0, bHasDifferentZ ? "POLYLINE" : "LWPOLYLINE");
788 26 : WriteCore(poFeature);
789 26 : if (bHasDifferentZ)
790 : {
791 1 : WriteValue(100, "AcDb3dPolyline");
792 1 : WriteValue(10, 0.0);
793 1 : WriteValue(20, 0.0);
794 1 : WriteValue(30, 0.0);
795 : }
796 : else
797 25 : WriteValue(100, "AcDbPolyline");
798 26 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
799 0 : WriteValue(70, 1 + (bHasDifferentZ ? 8 : 0));
800 : else
801 26 : WriteValue(70, 0 + (bHasDifferentZ ? 8 : 0));
802 26 : if (!bHasDifferentZ)
803 25 : WriteValue(90, poLS->getNumPoints());
804 : else
805 1 : WriteValue(66, "1"); // Vertex Flag
806 :
807 : /* -------------------------------------------------------------------- */
808 : /* Do we have styling information? */
809 : /* -------------------------------------------------------------------- */
810 26 : OGRStyleTool *poTool = nullptr;
811 52 : OGRStyleMgr oSM;
812 :
813 26 : if (poFeature->GetStyleString() != nullptr)
814 : {
815 5 : oSM.InitFromFeature(poFeature);
816 :
817 5 : if (oSM.GetPartCount() > 0)
818 5 : poTool = oSM.GetPart(0);
819 : }
820 :
821 : /* -------------------------------------------------------------------- */
822 : /* Handle a PEN tool to control drawing color and width. */
823 : /* Perhaps one day also dottedness, etc. */
824 : /* -------------------------------------------------------------------- */
825 26 : if (poTool && poTool->GetType() == OGRSTCPen)
826 : {
827 5 : OGRStylePen *poPen = (OGRStylePen *)poTool;
828 : GBool bDefault;
829 :
830 5 : if (poPen->Color(bDefault) != nullptr && !bDefault)
831 5 : WriteValue(62, ColorStringToDXFColor(poPen->Color(bDefault)));
832 :
833 : // we want to fetch the width in ground units.
834 5 : poPen->SetUnit(OGRSTUGround, 1.0);
835 5 : const double dfWidth = poPen->Width(bDefault);
836 :
837 5 : if (!bDefault)
838 5 : WriteValue(370, (int)floor(dfWidth * 100 + 0.5));
839 : }
840 :
841 : /* -------------------------------------------------------------------- */
842 : /* Do we have a Linetype for the feature? */
843 : /* -------------------------------------------------------------------- */
844 52 : CPLString osLineType = poFeature->GetFieldAsString("Linetype");
845 26 : double dfLineTypeScale = 0.0;
846 :
847 26 : bool bGotLinetype = false;
848 :
849 26 : if (!osLineType.empty())
850 : {
851 : std::vector<double> adfLineType =
852 4 : poDS->oHeaderDS.LookupLineType(osLineType);
853 :
854 2 : if (adfLineType.empty() && oNewLineTypes.count(osLineType) > 0)
855 0 : adfLineType = oNewLineTypes[osLineType];
856 :
857 2 : if (!adfLineType.empty())
858 : {
859 1 : bGotLinetype = true;
860 1 : WriteValue(6, osLineType);
861 :
862 : // If the given linetype is proportional to the linetype data
863 : // in the style string, then apply a linetype scale
864 1 : if (poTool != nullptr && poTool->GetType() == OGRSTCPen)
865 : {
866 : std::vector<double> adfDefinition = PrepareLineTypeDefinition(
867 2 : static_cast<OGRStylePen *>(poTool));
868 :
869 1 : if (!adfDefinition.empty())
870 : {
871 : dfLineTypeScale =
872 1 : IsLineTypeProportional(adfLineType, adfDefinition);
873 :
874 1 : if (dfLineTypeScale != 0.0 &&
875 0 : fabs(dfLineTypeScale - 1.0) > 1e-4)
876 : {
877 0 : WriteValue(48, dfLineTypeScale);
878 : }
879 : }
880 : }
881 : }
882 : }
883 :
884 26 : if (!bGotLinetype && poTool != nullptr && poTool->GetType() == OGRSTCPen)
885 : {
886 : std::vector<double> adfDefinition =
887 8 : PrepareLineTypeDefinition(static_cast<OGRStylePen *>(poTool));
888 :
889 4 : if (!adfDefinition.empty())
890 : {
891 : // Is this definition already created and named?
892 7 : for (const auto &oPair : poDS->oHeaderDS.GetLineTypeTable())
893 : {
894 : dfLineTypeScale =
895 4 : IsLineTypeProportional(oPair.second, adfDefinition);
896 4 : if (dfLineTypeScale != 0.0)
897 : {
898 1 : osLineType = oPair.first;
899 1 : break;
900 : }
901 : }
902 :
903 4 : if (dfLineTypeScale == 0.0)
904 : {
905 4 : for (const auto &oPair : oNewLineTypes)
906 : {
907 : dfLineTypeScale =
908 2 : IsLineTypeProportional(oPair.second, adfDefinition);
909 2 : if (dfLineTypeScale != 0.0)
910 : {
911 1 : osLineType = oPair.first;
912 1 : break;
913 : }
914 : }
915 : }
916 :
917 : // If not, create an automatic name for it.
918 4 : if (osLineType == "")
919 : {
920 1 : dfLineTypeScale = 1.0;
921 0 : do
922 : {
923 1 : osLineType.Printf("AutoLineType-%d", nNextAutoID++);
924 1 : } while (poDS->oHeaderDS.LookupLineType(osLineType).size() > 0);
925 : }
926 :
927 : // If it isn't already defined, add it now.
928 7 : if (poDS->oHeaderDS.LookupLineType(osLineType).empty() &&
929 3 : oNewLineTypes.count(osLineType) == 0)
930 : {
931 2 : oNewLineTypes[osLineType] = std::move(adfDefinition);
932 : }
933 :
934 4 : WriteValue(6, osLineType);
935 :
936 4 : if (dfLineTypeScale != 0.0 && fabs(dfLineTypeScale - 1.0) > 1e-4)
937 : {
938 2 : WriteValue(48, dfLineTypeScale);
939 : }
940 : }
941 : }
942 :
943 : /* -------------------------------------------------------------------- */
944 : /* Write the vertices */
945 : /* -------------------------------------------------------------------- */
946 :
947 26 : if (!bHasDifferentZ && poLS->getGeometryType() == wkbLineString25D)
948 : {
949 : // if LWPOLYLINE with Z write it only once
950 7 : if (!WriteValue(38, poLS->getZ(0)))
951 0 : return OGRERR_FAILURE;
952 : }
953 :
954 78 : for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
955 : {
956 52 : if (bHasDifferentZ)
957 : {
958 2 : WriteValue(0, "VERTEX");
959 2 : WriteCore(poFeature);
960 2 : WriteValue(100, "AcDbVertex");
961 2 : WriteValue(100, "AcDb3dPolylineVertex");
962 : }
963 52 : WriteValue(10, poLS->getX(iVert));
964 52 : if (!WriteValue(20, poLS->getY(iVert)))
965 0 : return OGRERR_FAILURE;
966 :
967 52 : if (bHasDifferentZ)
968 : {
969 2 : if (!WriteValue(30, poLS->getZ(iVert)))
970 0 : return OGRERR_FAILURE;
971 2 : WriteValue(70, 32);
972 : }
973 : }
974 :
975 26 : if (bHasDifferentZ)
976 : {
977 1 : WriteValue(0, "SEQEND");
978 1 : WriteCore(poFeature);
979 : }
980 :
981 26 : delete poTool;
982 :
983 26 : return OGRERR_NONE;
984 :
985 : #ifdef notdef
986 : /* -------------------------------------------------------------------- */
987 : /* Alternate unmaintained implementation as a polyline entity. */
988 : /* -------------------------------------------------------------------- */
989 : WriteValue(0, "POLYLINE");
990 : WriteCore(poFeature);
991 : WriteValue(100, "AcDbPolyline");
992 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
993 : WriteValue(70, 1);
994 : else
995 : WriteValue(70, 0);
996 : WriteValue(66, "1");
997 :
998 : for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
999 : {
1000 : WriteValue(0, "VERTEX");
1001 : WriteValue(8, "0");
1002 : WriteValue(10, poLS->getX(iVert));
1003 : if (!WriteValue(20, poLS->getY(iVert)))
1004 : return OGRERR_FAILURE;
1005 :
1006 : if (poLS->getGeometryType() == wkbLineString25D)
1007 : {
1008 : if (!WriteValue(30, poLS->getZ(iVert)))
1009 : return OGRERR_FAILURE;
1010 : }
1011 : }
1012 :
1013 : WriteValue(0, "SEQEND");
1014 : WriteValue(8, "0");
1015 :
1016 : return OGRERR_NONE;
1017 : #endif
1018 : }
1019 :
1020 : /************************************************************************/
1021 : /* WriteHATCH() */
1022 : /************************************************************************/
1023 :
1024 23 : OGRErr OGRDXFWriterLayer::WriteHATCH(OGRFeature *poFeature, OGRGeometry *poGeom)
1025 :
1026 : {
1027 : /* -------------------------------------------------------------------- */
1028 : /* For now we handle multipolygons by writing a series of */
1029 : /* entities. */
1030 : /* -------------------------------------------------------------------- */
1031 23 : if (poGeom == nullptr)
1032 19 : poGeom = poFeature->GetGeometryRef();
1033 :
1034 23 : if (poGeom->IsEmpty())
1035 : {
1036 0 : return OGRERR_NONE;
1037 : }
1038 :
1039 23 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)
1040 : {
1041 4 : OGRErr eErr = OGRERR_NONE;
1042 8 : for (auto &&poMember : poGeom->toMultiPolygon())
1043 : {
1044 4 : eErr = WriteHATCH(poFeature, poMember);
1045 4 : if (eErr != OGRERR_NONE)
1046 0 : break;
1047 : }
1048 :
1049 4 : return eErr;
1050 : }
1051 :
1052 : /* -------------------------------------------------------------------- */
1053 : /* Do we now have a geometry we can work with? */
1054 : /* -------------------------------------------------------------------- */
1055 20 : if (wkbFlatten(poGeom->getGeometryType()) != wkbPolygon &&
1056 1 : wkbFlatten(poGeom->getGeometryType()) != wkbTriangle)
1057 : {
1058 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
1059 : }
1060 :
1061 : /* -------------------------------------------------------------------- */
1062 : /* Write as a hatch. */
1063 : /* -------------------------------------------------------------------- */
1064 19 : WriteValue(0, "HATCH");
1065 19 : WriteCore(poFeature);
1066 19 : WriteValue(100, "AcDbHatch");
1067 :
1068 : // Figure out "average" elevation
1069 19 : OGREnvelope3D oEnv;
1070 19 : poGeom->getEnvelope(&oEnv);
1071 19 : WriteValue(10, 0); // elevation point X = 0
1072 19 : WriteValue(20, 0); // elevation point Y = 0
1073 : // elevation point Z = constant elevation
1074 19 : WriteValue(30, oEnv.MinZ + (oEnv.MaxZ - oEnv.MinZ) / 2);
1075 :
1076 19 : WriteValue(210, 0); // extrusion direction X
1077 19 : WriteValue(220, 0); // extrusion direction Y
1078 19 : WriteValue(230, 1.0); // extrusion direction Z
1079 :
1080 19 : WriteValue(2, "SOLID"); // fill pattern
1081 19 : WriteValue(70, 1); // solid fill
1082 19 : WriteValue(71, 0); // associativity
1083 :
1084 : /* -------------------------------------------------------------------- */
1085 : /* Do we have styling information? */
1086 : /* -------------------------------------------------------------------- */
1087 19 : OGRStyleTool *poTool = nullptr;
1088 19 : OGRStyleMgr oSM;
1089 :
1090 19 : if (poFeature->GetStyleString() != nullptr)
1091 : {
1092 1 : oSM.InitFromFeature(poFeature);
1093 :
1094 1 : if (oSM.GetPartCount() > 0)
1095 1 : poTool = oSM.GetPart(0);
1096 : }
1097 : // Write style brush fore color
1098 19 : if (poTool && poTool->GetType() == OGRSTCBrush)
1099 : {
1100 1 : OGRStyleBrush *poBrush = (OGRStyleBrush *)poTool;
1101 : GBool bDefault;
1102 :
1103 1 : if (poBrush->ForeColor(bDefault) != nullptr && !bDefault)
1104 1 : WriteValue(62, ColorStringToDXFColor(poBrush->ForeColor(bDefault)));
1105 : }
1106 19 : delete poTool;
1107 :
1108 : /* -------------------------------------------------------------------- */
1109 : /* Handle a PEN tool to control drawing color and width. */
1110 : /* Perhaps one day also dottedness, etc. */
1111 : /* -------------------------------------------------------------------- */
1112 : #ifdef notdef
1113 : if (poTool && poTool->GetType() == OGRSTCPen)
1114 : {
1115 : OGRStylePen *poPen = (OGRStylePen *)poTool;
1116 : GBool bDefault;
1117 :
1118 : if (poPen->Color(bDefault) != NULL && !bDefault)
1119 : WriteValue(62, ColorStringToDXFColor(poPen->Color(bDefault)));
1120 :
1121 : double dfWidthInMM = poPen->Width(bDefault);
1122 :
1123 : if (!bDefault)
1124 : WriteValue(370, (int)floor(dfWidthInMM * 100 + 0.5));
1125 : }
1126 :
1127 : /* -------------------------------------------------------------------- */
1128 : /* Do we have a Linetype for the feature? */
1129 : /* -------------------------------------------------------------------- */
1130 : CPLString osLineType = poFeature->GetFieldAsString("Linetype");
1131 :
1132 : if (!osLineType.empty() &&
1133 : (poDS->oHeaderDS.LookupLineType(osLineType) != NULL ||
1134 : oNewLineTypes.count(osLineType) > 0))
1135 : {
1136 : // Already define -> just reference it.
1137 : WriteValue(6, osLineType);
1138 : }
1139 : else if (poTool != NULL && poTool->GetType() == OGRSTCPen)
1140 : {
1141 : CPLString osDefinition = PrepareLineTypeDefinition(poFeature, poTool);
1142 :
1143 : if (osDefinition != "" && osLineType == "")
1144 : {
1145 : // Is this definition already created and named?
1146 : std::map<CPLString, CPLString>::iterator it;
1147 :
1148 : for (it = oNewLineTypes.begin(); it != oNewLineTypes.end(); it++)
1149 : {
1150 : if ((*it).second == osDefinition)
1151 : {
1152 : osLineType = (*it).first;
1153 : break;
1154 : }
1155 : }
1156 :
1157 : // create an automatic name for it.
1158 : if (osLineType == "")
1159 : {
1160 : do
1161 : {
1162 : osLineType.Printf("AutoLineType-%d", nNextAutoID++);
1163 : } while (poDS->oHeaderDS.LookupLineType(osLineType) != NULL);
1164 : }
1165 : }
1166 :
1167 : // If it isn't already defined, add it now.
1168 : if (osDefinition != "" && oNewLineTypes.count(osLineType) == 0)
1169 : {
1170 : oNewLineTypes[osLineType] = osDefinition;
1171 : WriteValue(6, osLineType);
1172 : }
1173 : }
1174 : delete poTool;
1175 : #endif
1176 :
1177 : /* -------------------------------------------------------------------- */
1178 : /* Process the loops (rings). */
1179 : /* -------------------------------------------------------------------- */
1180 19 : OGRPolygon *poPoly = poGeom->toPolygon();
1181 :
1182 19 : WriteValue(91, poPoly->getNumInteriorRings() + 1);
1183 :
1184 39 : for (auto &&poLR : *poPoly)
1185 : {
1186 20 : WriteValue(92, 2); // Polyline
1187 20 : WriteValue(72, 0); // has bulge
1188 20 : WriteValue(73, 1); // is closed
1189 20 : WriteValue(93, poLR->getNumPoints());
1190 :
1191 116 : for (int iVert = 0; iVert < poLR->getNumPoints(); iVert++)
1192 : {
1193 96 : WriteValue(10, poLR->getX(iVert));
1194 96 : WriteValue(20, poLR->getY(iVert));
1195 : }
1196 :
1197 20 : WriteValue(97, 0); // 0 source boundary objects
1198 : }
1199 :
1200 19 : WriteValue(75, 0); // hatch style = Hatch "odd parity" area (Normal style)
1201 19 : WriteValue(76, 1); // hatch pattern type = predefined
1202 19 : WriteValue(98, 0); // 0 seed points
1203 :
1204 19 : return OGRERR_NONE;
1205 :
1206 : #ifdef notdef
1207 : /* -------------------------------------------------------------------- */
1208 : /* Alternate unmaintained implementation as a polyline entity. */
1209 : /* -------------------------------------------------------------------- */
1210 : WriteValue(0, "POLYLINE");
1211 : WriteCore(poFeature);
1212 : WriteValue(100, "AcDbPolyline");
1213 : if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
1214 : WriteValue(70, 1);
1215 : else
1216 : WriteValue(70, 0);
1217 : WriteValue(66, "1");
1218 :
1219 : for (int iVert = 0; iVert < poLS->getNumPoints(); iVert++)
1220 : {
1221 : WriteValue(0, "VERTEX");
1222 : WriteValue(8, "0");
1223 : WriteValue(10, poLS->getX(iVert));
1224 : if (!WriteValue(20, poLS->getY(iVert)))
1225 : return OGRERR_FAILURE;
1226 :
1227 : if (poLS->getGeometryType() == wkbLineString25D)
1228 : {
1229 : if (!WriteValue(30, poLS->getZ(iVert)))
1230 : return OGRERR_FAILURE;
1231 : }
1232 : }
1233 :
1234 : WriteValue(0, "SEQEND");
1235 : WriteValue(8, "0");
1236 :
1237 : return OGRERR_NONE;
1238 : #endif
1239 : }
1240 :
1241 : /************************************************************************/
1242 : /* ICreateFeature() */
1243 : /************************************************************************/
1244 :
1245 210 : OGRErr OGRDXFWriterLayer::ICreateFeature(OGRFeature *poFeature)
1246 :
1247 : {
1248 210 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1249 210 : OGRwkbGeometryType eGType = wkbNone;
1250 :
1251 210 : if (poGeom != nullptr)
1252 : {
1253 177 : if (!poGeom->IsEmpty())
1254 : {
1255 175 : OGREnvelope sEnvelope;
1256 175 : poGeom->getEnvelope(&sEnvelope);
1257 175 : poDS->UpdateExtent(&sEnvelope);
1258 : }
1259 177 : eGType = wkbFlatten(poGeom->getGeometryType());
1260 : }
1261 :
1262 210 : if (eGType == wkbPoint)
1263 : {
1264 119 : const char *pszBlockName = poFeature->GetFieldAsString("BlockName");
1265 :
1266 : // We don't want to treat as a blocks ref if the block is not defined
1267 238 : if (pszBlockName &&
1268 119 : poDS->oHeaderDS.LookupBlock(pszBlockName) == nullptr)
1269 : {
1270 124 : if (poDS->poBlocksLayer == nullptr ||
1271 7 : poDS->poBlocksLayer->FindBlock(pszBlockName) == nullptr)
1272 112 : pszBlockName = nullptr;
1273 : }
1274 :
1275 119 : if (pszBlockName != nullptr)
1276 7 : return WriteINSERT(poFeature);
1277 :
1278 113 : else if (poFeature->GetStyleString() != nullptr &&
1279 1 : STARTS_WITH_CI(poFeature->GetStyleString(), "LABEL"))
1280 1 : return WriteTEXT(poFeature);
1281 : else
1282 111 : return WritePOINT(poFeature);
1283 : }
1284 91 : else if (eGType == wkbLineString || eGType == wkbMultiLineString)
1285 26 : return WritePOLYLINE(poFeature);
1286 :
1287 65 : else if (eGType == wkbPolygon || eGType == wkbTriangle ||
1288 : eGType == wkbMultiPolygon)
1289 : {
1290 19 : if (bWriteHatch)
1291 19 : return WriteHATCH(poFeature);
1292 : else
1293 0 : return WritePOLYLINE(poFeature);
1294 : }
1295 :
1296 : // Explode geometry collections into multiple entities.
1297 46 : else if (eGType == wkbGeometryCollection)
1298 : {
1299 : OGRGeometryCollection *poGC =
1300 8 : poFeature->StealGeometry()->toGeometryCollection();
1301 26 : for (auto &&poMember : poGC)
1302 : {
1303 19 : poFeature->SetGeometry(poMember);
1304 :
1305 19 : OGRErr eErr = CreateFeature(poFeature);
1306 :
1307 19 : if (eErr != OGRERR_NONE)
1308 : {
1309 1 : delete poGC;
1310 1 : return eErr;
1311 : }
1312 : }
1313 :
1314 7 : poFeature->SetGeometryDirectly(poGC);
1315 7 : return OGRERR_NONE;
1316 : }
1317 : else
1318 : {
1319 38 : CPLError(CE_Failure, CPLE_AppDefined,
1320 : "No known way to write feature with geometry '%s'.",
1321 : OGRGeometryTypeToName(eGType));
1322 38 : return OGRERR_FAILURE;
1323 : }
1324 : }
1325 :
1326 : /************************************************************************/
1327 : /* ColorStringToDXFColor() */
1328 : /************************************************************************/
1329 :
1330 7 : int OGRDXFWriterLayer::ColorStringToDXFColor(const char *pszRGB)
1331 :
1332 : {
1333 : /* -------------------------------------------------------------------- */
1334 : /* Parse the RGB string. */
1335 : /* -------------------------------------------------------------------- */
1336 7 : if (pszRGB == nullptr)
1337 0 : return -1;
1338 :
1339 7 : unsigned int nRed = 0;
1340 7 : unsigned int nGreen = 0;
1341 7 : unsigned int nBlue = 0;
1342 7 : unsigned int nTransparency = 255;
1343 :
1344 : const int nCount =
1345 7 : sscanf(pszRGB, "#%2x%2x%2x%2x", &nRed, &nGreen, &nBlue, &nTransparency);
1346 :
1347 7 : if (nCount < 3)
1348 0 : return -1;
1349 :
1350 : /* -------------------------------------------------------------------- */
1351 : /* Find near color in DXF palette. */
1352 : /* -------------------------------------------------------------------- */
1353 7 : const unsigned char *pabyDXFColors = ACGetColorTable();
1354 7 : int nMinDist = 768;
1355 7 : int nBestColor = -1;
1356 :
1357 1792 : for (int i = 1; i < 256; i++)
1358 : {
1359 1785 : const int nDist =
1360 1785 : std::abs(static_cast<int>(nRed) - pabyDXFColors[i * 3 + 0]) +
1361 1785 : std::abs(static_cast<int>(nGreen) - pabyDXFColors[i * 3 + 1]) +
1362 1785 : std::abs(static_cast<int>(nBlue) - pabyDXFColors[i * 3 + 2]);
1363 :
1364 1785 : if (nDist < nMinDist)
1365 : {
1366 12 : nBestColor = i;
1367 12 : nMinDist = nDist;
1368 : }
1369 : }
1370 :
1371 7 : return nBestColor;
1372 : }
1373 :
1374 : /************************************************************************/
1375 : /* GetDataset() */
1376 : /************************************************************************/
1377 :
1378 17 : GDALDataset *OGRDXFWriterLayer::GetDataset()
1379 : {
1380 17 : return poDS;
1381 : }
|