Bug Summary

File:libraries/libdxfrw/src/drw_entities.cpp
Warning:line 2598, column 16
Value stored to 'textBuf' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name drw_entities.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/libraries/libdxfrw -fcoverage-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/libraries/libdxfrw -resource-dir /usr/lib/llvm-18/lib/clang/18 -D _REENTRANT -D MUPARSER_STATIC -D QT_NO_DEBUG -I . -I ../../../Qt/6.9.0/gcc_64/mkspecs/linux-g++ -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=gnu++1z -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/runner/work/LibreCAD/LibreCAD/out/2026-09-15-234850-5000-1 -x c++ src/drw_entities.cpp
1/******************************************************************************
2** libDXFrw - Library to read/write DXF files (ascii & binary) **
3** **
4** Copyright (C) 2016-2022 A. Stebich (librecad@mail.lordofbikes.de) **
5** Copyright (C) 2011-2015 José F. Soriano, rallazz@gmail.com **
6** Copyright (C) 2026 LibreCAD (librecad.org) **
7** **
8** This library is free software, licensed under the terms of the GNU **
9** General Public License as published by the Free Software Foundation, **
10** either version 2 of the License, or (at your option) any later version. **
11** You should have received a copy of the GNU General Public License **
12** along with this program. If not, see <http://www.gnu.org/licenses/>. **
13******************************************************************************/
14
15#include <algorithm>
16#include <cctype>
17#include <cstdint>
18#include <cmath>
19#include <cstdio>
20#include <cstdlib>
21#include <cstring>
22#include <limits>
23#include <vector>
24#include "drw_entities.h"
25#include "intern/dxfreader.h"
26#include "intern/dxfparserlimits.h"
27#include "intern/dwgbuffer.h"
28#include "intern/dwgbufferw.h"
29#include "intern/drw_textcodec.h"
30#include "intern/drw_dbg.h"
31#include "intern/drw_reserve.h"
32#include "intern/dwgreader.h"
33#include "intern/dwgsafety.h"
34
35namespace {
36
37struct EmbeddedMTextHandleInfo {
38 bool m_ownerHandle = false;
39 int m_numReactors = 0;
40 // AC1015 has no xdict bit; AC1018+ supplies it and uses 1 to suppress
41 // the embedded xdictionary handle.
42 std::uint8_t m_xDictFlag = 0;
43 bool m_hasAcDbColorHandle = false;
44 int m_ltFlags = 0;
45 int m_plotFlags = 0;
46 int m_materialFlag = 0;
47 int m_shadowFlag = 0;
48 bool m_hasFullVisualStyle = false;
49 bool m_hasFaceVisualStyle = false;
50 bool m_hasEdgeVisualStyle = false;
51 bool m_hasStyleHandle = true;
52 bool m_hasR2018AppIdHandle = false;
53 bool m_hasAnnotativeAppHandle = false;
54};
55static bool parseEmbeddedMTextDwg(DRW::Version version, dwgBuffer *buf,
56 dwgBuffer *sBuf, DRW_MText& mtext,
57 EmbeddedMTextHandleInfo& info,
58 std::uint64_t bodyEndBit =
59 std::numeric_limits<std::uint64_t>::max(),
60 std::uint64_t stringEndBit =
61 std::numeric_limits<std::uint64_t>::max());
62static bool consumeEmbeddedMTextHandles(
63 DRW::Version version, dwgBuffer *buf,
64 const EmbeddedMTextHandleInfo& info, DRW_MText *mtext,
65 std::uint64_t handleEndBit =
66 std::numeric_limits<std::uint64_t>::max());
67
68constexpr std::uint32_t kMaxTableRows = 10000;
69constexpr std::uint32_t kMaxTableColumns = 1000;
70constexpr std::uint32_t kMaxTableCells = 200000;
71constexpr std::uint32_t kMaxTableItems = 100000;
72constexpr std::uint32_t kMaxTableStringBytes = 16 * 1024 * 1024;
73constexpr std::uint32_t kMaxTableCellAttributes = 10000;
74constexpr std::int32_t kMaxLWPolylineVertices = 1000000;
75constexpr std::int32_t kMaxSplineItems = 1000000;
76constexpr std::int32_t kMaxSplineDegree = 1024;
77constexpr std::int32_t kMaxHatchItems = DRW_Hatch::kMaxDxfItems;
78constexpr std::int32_t kMaxMInsertCount =
79 std::numeric_limits<std::int16_t>::max();
80constexpr std::int32_t kMaxViewportFrozenLayers = 512;
81constexpr std::int32_t kMaxMTextColumnHeights = 4096;
82constexpr std::int32_t kMaxMLeaderItems = 5000;
83constexpr std::int32_t kMaxLeaderVertices = 5000;
84constexpr std::uint16_t kMaxLoftedSurfaceModelerFormatVersion = 3;
85
86constexpr std::int32_t kSplineFlagMethodFitPoints = 1;
87constexpr std::int32_t kSplineFlagClosed = 4;
88constexpr std::int32_t kSplineFlagUseKnotParameter = 8;
89constexpr std::int32_t kSplineKnotParamCustom = 15;
90
91bool isValidCount(std::int32_t count, std::int32_t maxCount) {
92 return count >= 0 && count <= maxCount;
93}
94
95bool readDxfIntInRange(const std::unique_ptr<dxfReader>& reader,
96 int minimum, int maximum, int& value) {
97 value = reader->getInt32();
98 return value >= minimum && value <= maximum;
99}
100
101int hexNibble(char c) {
102 if (c >= '0' && c <= '9')
103 return c - '0';
104 if (c >= 'A' && c <= 'F')
105 return c - 'A' + 10;
106 if (c >= 'a' && c <= 'f')
107 return c - 'a' + 10;
108 return -1;
109}
110
111bool decodeHexBytes(const std::string& hex, std::vector<std::uint8_t>& out) {
112 if ((hex.size() % 2) != 0)
113 return false;
114
115 std::vector<std::uint8_t> decoded;
116 const std::size_t byteCount = hex.size() / 2;
117 if (byteCount > static_cast<std::size_t>(std::numeric_limits<int>::max())
118 || !DRW::reserve(decoded, static_cast<int>(byteCount)))
119 return false;
120 for (std::size_t i = 0; i < hex.size(); i += 2) {
121 const int hi = hexNibble(hex[i]);
122 const int lo = hexNibble(hex[i + 1]);
123 if (hi < 0 || lo < 0)
124 return false;
125 decoded.push_back(static_cast<std::uint8_t>((hi << 4) | lo));
126 }
127
128 out = std::move(decoded);
129 return true;
130}
131
132bool appendBytesChecked(std::vector<std::uint8_t>& destination,
133 const std::vector<std::uint8_t>& bytes,
134 std::size_t maxSize) {
135 if (destination.size() > maxSize
136 || bytes.size() > maxSize - destination.size())
137 return false;
138 const std::size_t newSize = destination.size() + bytes.size();
139 if (newSize > static_cast<std::size_t>(std::numeric_limits<int>::max())
140 || !DRW::reserve(destination, static_cast<int>(newSize)))
141 return false;
142 try {
143 destination.insert(destination.end(), bytes.begin(), bytes.end());
144 } catch (...) {
145 return false;
146 }
147 return true;
148}
149
150bool appendTextBytesChecked(std::vector<std::uint8_t>& destination,
151 const std::string& text,
152 std::size_t maxSize) {
153 if (destination.size() > maxSize
154 || text.size() > maxSize - destination.size())
155 return false;
156 const std::size_t newSize = destination.size() + text.size();
157 if (newSize > static_cast<std::size_t>(std::numeric_limits<int>::max())
158 || !DRW::reserve(destination, static_cast<int>(newSize)))
159 return false;
160 try {
161 destination.insert(destination.end(), text.begin(), text.end());
162 } catch (...) {
163 return false;
164 }
165 return true;
166}
167
168bool appendHexBytesChecked(std::string& destination, const std::string& hex,
169 std::size_t maxSize) {
170 if ((hex.size() & 1u) != 0u || destination.size() > maxSize)
171 return false;
172 const std::size_t byteCount = hex.size() / 2u;
173 if (byteCount > maxSize - destination.size()
174 || byteCount > static_cast<std::size_t>(std::numeric_limits<int>::max()))
175 return false;
176
177 std::string decoded;
178 if (!DRW::reserve(decoded, static_cast<int>(byteCount)))
179 return false;
180 for (std::size_t i = 0; i < hex.size(); i += 2) {
181 const int hi = hexNibble(hex[i]);
182 const int lo = hexNibble(hex[i + 1]);
183 if (hi < 0 || lo < 0)
184 return false;
185 decoded.push_back(static_cast<char>((hi << 4) | lo));
186 }
187 try {
188 destination.append(decoded);
189 } catch (...) {
190 return false;
191 }
192 return true;
193}
194
195std::uint64_t currentDwgBit(const dwgBuffer *buf) {
196 return buf->getPosition() * 8 + buf->getBitPos();
197}
198
199bool leaderHasEndpointProjection(DRW::Version version) {
200 return version >= DRW::AC1014;
201}
202
203bool leaderHasTextBox(DRW::Version version) {
204 // R2010+ files use the compact LEADER layout without these legacy fields.
205 return version <= DRW::AC1021;
206}
207
208std::uint64_t tableBodyEndBit(DRW::Version version,
209 const dwgBuffer *stringBuf,
210 std::uint32_t objectSize) {
211 // AC1021 objects may legally omit the string footer when they have no
212 // detached text. Keep their complete object body available; AC1024+
213 // frames have the unambiguous extended object-data boundary.
214 if (version > DRW::AC1021 && stringBuf != nullptr)
215 return currentDwgBit(stringBuf);
216 return objectSize;
217}
218
219bool readTableBodyCount(DRW::Version version, dwgBuffer *buf,
220 std::uint64_t objectSize, std::uint32_t maximum,
221 std::uint32_t minimumBitsPerItem,
222 std::uint32_t& value) {
223 if (buf == nullptr || !buf->isGood())
224 return false;
225
226 dwgBuffer probe = buf->forkIndependent();
227 const std::int32_t parsed = probe.getBitLong();
228 if (!probe.isGood() || parsed < 0
229 || static_cast<std::uint32_t>(parsed) > maximum)
230 return false;
231
232 if (version >= DRW::AC1015 && objectSize != 0) {
233 const std::uint64_t current = currentDwgBit(&probe);
234 if (current > objectSize || (minimumBitsPerItem != 0
235 && static_cast<std::uint64_t>(parsed)
236 > (objectSize - current)
237 / minimumBitsPerItem))
238 return false;
239 }
240
241 *buf = probe;
242 value = static_cast<std::uint32_t>(parsed);
243 return true;
244}
245
246std::uint64_t proxyEntityEndBit(const dwgBuffer& buffer,
247 std::uint32_t objectSize) {
248 if (objectSize != 0)
249 return objectSize;
250 return buffer.size() > std::numeric_limits<std::uint64_t>::max() / 8u
251 ? std::numeric_limits<std::uint64_t>::max()
252 : static_cast<std::uint64_t>(buffer.size()) * 8u;
253}
254
255bool entityBodyDataEndBit(const dwgBuffer& stringBuffer,
256 DRW::Version version, std::uint32_t objectSize,
257 std::uint64_t& endBit,
258 std::uint64_t* stringEndBit = nullptr) {
259 if (version <= DRW::AC1021 || objectSize == 0) {
260 endBit = proxyEntityEndBit(stringBuffer, objectSize);
261 if (stringEndBit != nullptr)
262 *stringEndBit = endBit;
263 return true;
264 }
265
266 std::uint64_t stringStartBit = 0;
267 std::uint64_t parsedStringEndBit = 0;
268 if (!stringBuffer.getR2007StringStreamBounds(
269 objectSize, stringStartBit, parsedStringEndBit)
270 || stringStartBit > parsedStringEndBit
271 || parsedStringEndBit > objectSize
272 || currentDwgBit(&stringBuffer) != stringStartBit) {
273 return false;
274 }
275 endBit = stringStartBit;
276 if (stringEndBit != nullptr)
277 *stringEndBit = parsedStringEndBit;
278 return true;
279}
280
281bool dwgHandleStreamEndBit(const dwgBuffer& buffer, DRW::Version version,
282 std::uint32_t objectSize, std::uint32_t handleBits,
283 std::uint64_t& endBit) {
284 if (version > DRW::AC1021) {
285 std::uint64_t totalBits = 0;
286 return dwgSafety::add(objectSize, handleBits, endBit)
287 && dwgSafety::multiply(buffer.size(), 8, totalBits)
288 && endBit <= totalBits;
289 }
290 return dwgSafety::multiply(buffer.size(), 8, endBit);
291}
292
293bool proxyEntityHasBits(const dwgBuffer& buffer, std::uint64_t endBit,
294 std::uint64_t count) {
295 const std::uint64_t current = currentDwgBit(&buffer);
296 return current <= endBit && count <= endBit - current;
297}
298
299bool readProxyEntityOptionalBitLong(dwgBuffer& buffer, std::uint64_t endBit,
300 bool& present, std::int32_t& value) {
301 present = false;
302 if (!proxyEntityHasBits(buffer, endBit, 2))
303 return true;
304
305 const std::uint8_t prefix = buffer.get2Bits();
306 if (!buffer.isGood())
307 return false;
308 if (prefix == 0) {
309 if (!proxyEntityHasBits(buffer, endBit, 32))
310 return false;
311 value = static_cast<std::int32_t>(buffer.getRawLong32());
312 } else if (prefix == 1) {
313 if (!proxyEntityHasBits(buffer, endBit, 8))
314 return false;
315 value = static_cast<std::int32_t>(buffer.getRawChar8());
316 } else {
317 value = 0;
318 }
319 present = buffer.isGood();
320 return present;
321}
322
323bool copyDwgBitRange(const dwgBuffer& source, std::uint64_t startBit,
324 std::uint64_t endBit,
325 std::vector<std::uint8_t>& data) {
326 data.clear();
327 if (endBit <= startBit)
328 return true;
329
330 const std::uint64_t sourceEnd = proxyEntityEndBit(source, 0);
331 if (endBit > sourceEnd)
332 return false;
333 const std::uint64_t startByte = startBit >> 3;
334 const std::uint64_t endByte = (endBit >> 3) + ((endBit & 7u) != 0);
335 const std::uint64_t byteCount = endByte - startByte;
336 if (endByte < startByte
337 || byteCount > std::numeric_limits<std::size_t>::max()
338 || byteCount > static_cast<std::uint64_t>(std::numeric_limits<int>::max())
339 || !DRW::resize(data, static_cast<int>(byteCount)))
340 return false;
341 dwgBuffer copy = source;
342 if (!copy.setPosition(startByte))
343 return false;
344 if ((startBit & 7u) != 0) {
345 copy.setBitPos(static_cast<std::uint8_t>(startBit & 7u));
346 if (!copy.isGood())
347 return false;
348 }
349 return data.empty()
350 || (copy.getBytes(data.data(), data.size()) && copy.isGood());
351}
352
353bool readBoundedBitShort(dwgBuffer& buffer, std::uint64_t endBit,
354 std::uint16_t& value) {
355 if (!proxyEntityHasBits(buffer, endBit, 2))
356 return false;
357
358 // BS has a two-bit selector followed by either no payload, one byte, or
359 // two bytes. Probe the selector on an independent cursor before touching
360 // the publishing cursor, so a frame boundary is checked exactly.
361 dwgBuffer probe = buffer.forkIndependent();
362 const std::uint8_t selector = probe.get2Bits();
363 if (!probe.isGood())
364 return false;
365 const std::uint64_t bits = selector == 0 ? 18
366 : selector == 1 ? 10 : 2;
367 if (!proxyEntityHasBits(buffer, endBit, bits))
368 return false;
369 value = buffer.getBitShort();
370 return buffer.isGood();
371}
372
373bool readBoundedRawChar8(dwgBuffer& buffer, std::uint64_t endBit,
374 std::uint8_t& value) {
375 if (!proxyEntityHasBits(buffer, endBit, 8))
376 return false;
377 value = buffer.getRawChar8();
378 return buffer.isGood();
379}
380
381bool readBounded2Bits(dwgBuffer& buffer, std::uint64_t endBit,
382 std::uint8_t& value) {
383 if (!proxyEntityHasBits(buffer, endBit, 2))
384 return false;
385 value = buffer.get2Bits();
386 return buffer.isGood();
387}
388
389bool readBoundedRawLong32(dwgBuffer& buffer, std::uint64_t endBit,
390 std::uint32_t& value) {
391 if (!proxyEntityHasBits(buffer, endBit, 32))
392 return false;
393 value = buffer.getRawLong32();
394 return buffer.isGood();
395}
396
397bool readBoundedBit(dwgBuffer& buffer, std::uint64_t endBit, bool& value) {
398 if (!proxyEntityHasBits(buffer, endBit, 1))
399 return false;
400 value = buffer.getBit() != 0;
401 return buffer.isGood();
402}
403
404bool readBoundedBitLong(dwgBuffer& buffer, std::uint64_t endBit,
405 std::int32_t& value) {
406 if (!proxyEntityHasBits(buffer, endBit, 2))
407 return false;
408 dwgBuffer probe = buffer.forkIndependent();
409 const std::uint8_t selector = probe.get2Bits();
410 if (!probe.isGood())
411 return false;
412 const std::uint64_t bits = selector == 0 ? 34
413 : selector == 1 ? 10 : 2;
414 if (!proxyEntityHasBits(buffer, endBit, bits))
415 return false;
416 value = buffer.getBitLong();
417 return buffer.isGood();
418}
419
420bool readBoundedBitLongLong(dwgBuffer& buffer, std::uint64_t endBit,
421 std::uint64_t& value) {
422 if (!proxyEntityHasBits(buffer, endBit, 3))
423 return false;
424 dwgBuffer probe = buffer.forkIndependent();
425 const std::uint8_t byteCount = probe.get3Bits();
426 const std::uint64_t bits = 3u + static_cast<std::uint64_t>(byteCount) * 8u;
427 if (!probe.isGood() || !proxyEntityHasBits(buffer, endBit, bits))
428 return false;
429 value = buffer.getBitLongLong();
430 return buffer.isGood();
431}
432
433bool readBoundedBitDouble(dwgBuffer& buffer, std::uint64_t endBit,
434 double& value) {
435 if (!proxyEntityHasBits(buffer, endBit, 2))
436 return false;
437 dwgBuffer probe = buffer.forkIndependent();
438 const std::uint8_t selector = probe.get2Bits();
439 if (!probe.isGood())
440 return false;
441 const std::uint64_t bits = selector == 0 ? 66 : 2;
442 if (!proxyEntityHasBits(buffer, endBit, bits))
443 return false;
444 dwgBuffer candidate = buffer.forkIndependent();
445 const double parsed = candidate.getBitDouble();
446 if (!candidate.isGood() || currentDwgBit(&candidate) > endBit
447 || !std::isfinite(parsed))
448 return false;
449 buffer = candidate;
450 value = parsed;
451 return true;
452}
453
454bool readBoundedBitCoord(dwgBuffer& buffer, std::uint64_t endBit,
455 DRW_Coord& value) {
456 return readBoundedBitDouble(buffer, endBit, value.x)
457 && readBoundedBitDouble(buffer, endBit, value.y)
458 && readBoundedBitDouble(buffer, endBit, value.z);
459}
460
461bool readBoundedEnColor(dwgBuffer& buffer, std::uint64_t endBit,
462 DRW::Version version, std::uint32_t& value) {
463 if (!buffer.isGood() || currentDwgBit(&buffer) > endBit)
464 return false;
465 dwgBuffer probe = buffer.forkIndependent();
466 const std::uint32_t parsed = probe.getEnColor(version);
467 if (!probe.isGood() || currentDwgBit(&probe) > endBit)
468 return false;
469 buffer = probe;
470 buffer.lastEnColorHadDbColorRef = probe.lastEnColorHadDbColorRef;
471 buffer.lastEnColorName = probe.lastEnColorName;
472 buffer.lastEnColorBookName = probe.lastEnColorBookName;
473 buffer.lastEnColorRgb = probe.lastEnColorRgb;
474 buffer.lastEnColorAlphaRaw = probe.lastEnColorAlphaRaw;
475 value = parsed;
476 return true;
477}
478
479struct TableDwgBounds {
480 std::uint64_t bodyEndBit = std::numeric_limits<std::uint64_t>::max();
481 std::uint64_t stringEndBit = std::numeric_limits<std::uint64_t>::max();
482 std::uint64_t handleEndBit = std::numeric_limits<std::uint64_t>::max();
483};
484
485std::uint64_t tableTextEndBit(const dwgBuffer *bodyBuf,
486 const dwgBuffer *textBuf,
487 const TableDwgBounds& bounds) {
488 return textBuf == bodyBuf ? bounds.bodyEndBit : bounds.stringEndBit;
489}
490
491bool readBoundedRawShort16(dwgBuffer& buffer, std::uint64_t endBit,
492 std::uint16_t& value) {
493 if (!proxyEntityHasBits(buffer, endBit, 16))
494 return false;
495 value = buffer.getRawShort16();
496 return buffer.isGood();
497}
498
499bool skipTableBit(dwgBuffer& buffer, std::uint64_t endBit) {
500 bool value = false;
501 return readBoundedBit(buffer, endBit, value);
502}
503
504bool skipTableBitShort(dwgBuffer& buffer, std::uint64_t endBit) {
505 std::uint16_t value = 0;
506 return readBoundedBitShort(buffer, endBit, value);
507}
508
509bool skipTableBitLong(dwgBuffer& buffer, std::uint64_t endBit) {
510 std::int32_t value = 0;
511 return readBoundedBitLong(buffer, endBit, value);
512}
513
514bool skipTableBitDouble(dwgBuffer& buffer, std::uint64_t endBit) {
515 double value = 0.0;
516 return readBoundedBitDouble(buffer, endBit, value);
517}
518
519bool skipTableRawChar8(dwgBuffer& buffer, std::uint64_t endBit) {
520 std::uint8_t value = 0;
521 return readBoundedRawChar8(buffer, endBit, value);
522}
523
524bool skipTableRawShort16(dwgBuffer& buffer, std::uint64_t endBit) {
525 std::uint16_t value = 0;
526 return readBoundedRawShort16(buffer, endBit, value);
527}
528
529bool readBounded3BitDouble(dwgBuffer& buffer, std::uint64_t endBit,
530 DRW_Coord& value) {
531 dwgBuffer probe = buffer.forkIndependent();
532 DRW_Coord parsed;
533 if (!readBoundedBitDouble(probe, endBit, parsed.x)
534 || !readBoundedBitDouble(probe, endBit, parsed.y)
535 || !readBoundedBitDouble(probe, endBit, parsed.z))
536 return false;
537 buffer = probe;
538 value = parsed;
539 return true;
540}
541
542bool readBoundedBytes(dwgBuffer& buffer, std::uint64_t endBit,
543 std::uint8_t *data, std::size_t size) {
544 std::uint64_t bits = 0;
545 if (!dwgSafety::multiply(static_cast<std::uint64_t>(size), 8, bits)
546 || !proxyEntityHasBits(buffer, endBit, bits))
547 return false;
548 return size == 0 || (buffer.getBytes(data, size) && buffer.isGood());
549}
550
551bool readBoundedRawLong64(dwgBuffer& buffer, std::uint64_t endBit,
552 std::uint64_t& value) {
553 if (!proxyEntityHasBits(buffer, endBit, 64))
554 return false;
555 dwgBuffer probe = buffer.forkIndependent();
556 const std::uint64_t parsed = probe.getRawLong64();
557 if (!probe.isGood())
558 return false;
559 buffer = probe;
560 value = parsed;
561 return true;
562}
563
564bool readBounded2RawDouble(dwgBuffer& buffer, std::uint64_t endBit,
565 DRW_Coord& value) {
566 if (!proxyEntityHasBits(buffer, endBit, 128))
567 return false;
568 dwgBuffer probe = buffer.forkIndependent();
569 DRW_Coord parsed = probe.get2RawDouble();
570 if (!probe.isGood()
571 || !std::isfinite(parsed.x)
572 || !std::isfinite(parsed.y)
573 || !std::isfinite(parsed.z))
574 return false;
575 parsed.z = 0.0; // 2RD carries only X and Y; keep the unused component defined.
576 buffer = probe;
577 value = parsed;
578 return true;
579}
580
581bool readBoundedRawDouble(dwgBuffer& buffer, std::uint64_t endBit,
582 double& value) {
583 if (!proxyEntityHasBits(buffer, endBit, 64))
584 return false;
585 dwgBuffer probe = buffer.forkIndependent();
586 const double parsed = probe.getRawDouble();
587 if (!probe.isGood() || currentDwgBit(&probe) > endBit
588 || !std::isfinite(parsed))
589 return false;
590 buffer = probe;
591 value = parsed;
592 return true;
593}
594
595bool readBoundedDwgHandle(dwgBuffer& buffer, std::uint64_t endBit,
596 std::uint32_t baseHandle, bool offset,
597 dwgHandle& value) {
598 if (!buffer.isGood() || currentDwgBit(&buffer) > endBit)
599 return false;
600 dwgBuffer probe = buffer.forkIndependent();
601 dwgHandle parsed;
602 if (!readDwgHandleChecked(probe, baseHandle, offset, parsed)
603 || currentDwgBit(&probe) > endBit)
604 return false;
605 buffer = probe;
606 value = parsed;
607 return true;
608}
609
610bool readBoundedDwgObjectId(dwgBuffer& buffer, std::uint64_t endBit,
611 std::uint32_t baseHandle, dwgHandle& raw,
612 dwgHandle& resolved) {
613 if (!buffer.isGood() || currentDwgBit(&buffer) > endBit)
614 return false;
615
616 dwgBuffer rawProbe = buffer.forkIndependent();
617 raw = rawProbe.getHandle();
618 if (!rawProbe.isGood() || currentDwgBit(&rawProbe) > endBit)
619 return false;
620
621 dwgBuffer resolvedProbe = buffer.forkIndependent();
622 resolved = resolvedProbe.getOffsetHandle(baseHandle);
623 if (!resolvedProbe.isGood() || currentDwgBit(&resolvedProbe) > endBit)
624 return false;
625
626 buffer = resolvedProbe;
627 return true;
628}
629
630bool readBoundedVariableText(dwgBuffer& buffer, std::uint64_t endBit,
631 DRW::Version version, UTF8STRINGstd::string& value) {
632 if (!proxyEntityHasBits(buffer, endBit, 2))
633 return false;
634
635 dwgBuffer probe = buffer.forkIndependent();
636 const std::uint16_t units = probe.getBitShort();
637 if (!probe.isGood())
638 return false;
639
640 const std::uint64_t bitsPerUnit = version > DRW::AC1018 ? 16u : 8u;
641 std::uint64_t payloadBits = 0;
642 if (!dwgSafety::multiply(static_cast<std::uint64_t>(units), bitsPerUnit,
643 payloadBits)
644 || !proxyEntityHasBits(probe, endBit, payloadBits))
645 return false;
646
647 value = buffer.getVariableText(version, false);
648 return buffer.isGood() && currentDwgBit(&buffer) <= endBit;
649}
650
651bool readBoundedDefaultDouble(dwgBuffer& buffer, std::uint64_t endBit,
652 double defaultValue, double& value) {
653 if (!proxyEntityHasBits(buffer, endBit, 2))
654 return false;
655 dwgBuffer probe = buffer.forkIndependent();
656 const std::uint8_t selector = probe.get2Bits();
657 if (!probe.isGood())
658 return false;
659 const std::uint64_t bits = selector == 0 ? 2
660 : selector == 1 ? 34
661 : selector == 2 ? 50 : 66;
662 if (!proxyEntityHasBits(buffer, endBit, bits))
663 return false;
664 value = buffer.getDefaultDouble(defaultValue);
665 return buffer.isGood();
666}
667
668bool readBoundedThickness(dwgBuffer& buffer, std::uint64_t endBit,
669 bool modern, double& value) {
670 if (!modern)
671 return readBoundedBitDouble(buffer, endBit, value);
672 if (!proxyEntityHasBits(buffer, endBit, 1))
673 return false;
674 dwgBuffer probe = buffer.forkIndependent();
675 const bool isZero = probe.getBit() != 0;
676 if (!probe.isGood())
677 return false;
678 buffer.getBit();
679 if (!buffer.isGood())
680 return false;
681 if (isZero) {
682 value = 0.0;
683 return true;
684 }
685 return readBoundedBitDouble(buffer, endBit, value);
686}
687
688bool readBoundedExtrusion(dwgBuffer& buffer, std::uint64_t endBit,
689 bool modern, DRW_Coord& value) {
690 if (modern) {
691 if (!proxyEntityHasBits(buffer, endBit, 1))
692 return false;
693 dwgBuffer probe = buffer.forkIndependent();
694 const bool isDefault = probe.getBit() != 0;
695 if (!probe.isGood())
696 return false;
697 buffer.getBit();
698 if (!buffer.isGood())
699 return false;
700 if (isDefault) {
701 value = DRW_Coord{0.0, 0.0, 1.0};
702 return true;
703 }
704 }
705
706 return readBoundedBitDouble(buffer, endBit, value.x)
707 && readBoundedBitDouble(buffer, endBit, value.y)
708 && readBoundedBitDouble(buffer, endBit, value.z);
709}
710
711bool readBoundedCmColor(dwgBuffer& buffer, dwgBuffer *stringBuffer,
712 std::uint64_t endBit, DRW::Version version,
713 std::uint32_t& value, std::int32_t *rgb24 = nullptr,
714 bool *hasRgbColor = nullptr,
715 UTF8STRINGstd::string *outName = nullptr,
716 UTF8STRINGstd::string *outBookName = nullptr,
717 std::uint64_t stringEndBit =
718 std::numeric_limits<std::uint64_t>::max()) {
719 const bool separateStringBuffer = stringBuffer != nullptr
720 && stringBuffer != &buffer;
721 dwgBuffer bodyProbe = buffer.forkIndependent();
722 dwgBuffer stringProbe = separateStringBuffer
723 ? stringBuffer->forkIndependent() : bodyProbe.forkIndependent();
724 dwgBuffer *colorStringBuffer = separateStringBuffer
725 ? &stringProbe : &bodyProbe;
726 UTF8STRINGstd::string parsedName;
727 UTF8STRINGstd::string parsedBookName;
728 std::int32_t parsedRgb = -1;
729 bool parsedHasRgb = false;
730 const std::uint32_t parsed = bodyProbe.getCmColor(
731 version, &parsedRgb, colorStringBuffer, &parsedName, &parsedBookName,
732 &parsedHasRgb);
733 if (!bodyProbe.isGood() || !colorStringBuffer->isGood()
734 || currentDwgBit(&bodyProbe) > endBit
735 || (separateStringBuffer
736 && currentDwgBit(&stringProbe) > stringEndBit))
737 return false;
738 buffer = bodyProbe;
739 if (separateStringBuffer)
740 *stringBuffer = stringProbe;
741 value = parsed;
742 if (outName != nullptr)
743 *outName = std::move(parsedName);
744 if (outBookName != nullptr)
745 *outBookName = std::move(parsedBookName);
746 if (rgb24 != nullptr)
747 *rgb24 = parsedRgb;
748 if (hasRgbColor != nullptr)
749 *hasRgbColor = parsedHasRgb;
750 return true;
751}
752
753struct DwgTextBodyData {
754 DRW_Coord basePoint{0.0, 0.0, 0.0};
755 DRW_Coord secPoint{0.0, 0.0, 0.0};
756 DRW_Coord extPoint{0.0, 0.0, 1.0};
757 double thickness = 0.0;
758 double oblique = 0.0;
759 double angle = 0.0;
760 double height = 0.0;
761 double widthscale = 1.0;
762 UTF8STRINGstd::string text;
763 int textgen = 0;
764 DRW_Text::HAlign alignH = DRW_Text::HLeft;
765 DRW_Text::VAlign alignV = DRW_Text::VBaseLine;
766};
767
768bool readBoundedDwgTextBody(
769 DRW::Version version, dwgBuffer& body, dwgBuffer& stringBuffer,
770 std::uint64_t bodyEndBit, std::uint64_t stringStartBit,
771 std::uint64_t stringEndBit, DwgTextBodyData& data) {
772 std::uint8_t dataFlags = 0;
773 if (version > DRW::AC1014) {
774 if (!readBoundedRawChar8(body, bodyEndBit, dataFlags))
775 return false;
776 if (!(dataFlags & 0x01)
777 && !readBoundedRawDouble(body, bodyEndBit, data.basePoint.z))
778 return false;
779 } else if (!readBoundedBitDouble(body, bodyEndBit, data.basePoint.z)) {
780 return false;
781 }
782
783 if (!readBoundedRawDouble(body, bodyEndBit, data.basePoint.x)
784 || !readBoundedRawDouble(body, bodyEndBit, data.basePoint.y))
785 return false;
786
787 if (version > DRW::AC1014) {
788 if (!(dataFlags & 0x02)) {
789 if (!readBoundedDefaultDouble(body, bodyEndBit,
790 data.basePoint.x, data.secPoint.x)
791 || !readBoundedDefaultDouble(body, bodyEndBit,
792 data.basePoint.y,
793 data.secPoint.y))
794 return false;
795 } else {
796 data.secPoint = data.basePoint;
797 }
798 } else if (!readBoundedRawDouble(body, bodyEndBit, data.secPoint.x)
799 || !readBoundedRawDouble(body, bodyEndBit, data.secPoint.y)) {
800 return false;
801 }
802 data.secPoint.z = data.basePoint.z;
803
804 if (!readBoundedExtrusion(body, bodyEndBit, version > DRW::AC1014,
805 data.extPoint)
806 || !readBoundedThickness(body, bodyEndBit, version > DRW::AC1014,
807 data.thickness))
808 return false;
809
810 if (version > DRW::AC1014) {
811 if (!(dataFlags & 0x04)
812 && !readBoundedRawDouble(body, bodyEndBit, data.oblique))
813 return false;
814 if (!(dataFlags & 0x08)
815 && !readBoundedRawDouble(body, bodyEndBit, data.angle))
816 return false;
817 if (!readBoundedRawDouble(body, bodyEndBit, data.height))
818 return false;
819 if (!(dataFlags & 0x10)
820 && !readBoundedRawDouble(body, bodyEndBit, data.widthscale))
821 return false;
822 } else if (!readBoundedBitDouble(body, bodyEndBit, data.oblique)
823 || !readBoundedBitDouble(body, bodyEndBit, data.angle)
824 || !readBoundedBitDouble(body, bodyEndBit, data.height)
825 || !readBoundedBitDouble(body, bodyEndBit, data.widthscale)) {
826 return false;
827 }
828 data.angle *= ARAD57.29577951308232;
829
830 if (version > DRW::AC1018 && stringStartBit >= stringEndBit) {
831 data.text.clear();
832 } else if (!readBoundedVariableText(stringBuffer, stringEndBit, version,
833 data.text)) {
834 return false;
835 }
836
837 std::uint16_t parsedTextgen = 0;
838 std::uint16_t parsedAlignH = 0;
839 std::uint16_t parsedAlignV = 0;
840 if (!(dataFlags & 0x20)
841 && !readBoundedBitShort(body, bodyEndBit, parsedTextgen))
842 return false;
843 if (!(dataFlags & 0x40)
844 && !readBoundedBitShort(body, bodyEndBit, parsedAlignH))
845 return false;
846 if (!(dataFlags & 0x80)
847 && !readBoundedBitShort(body, bodyEndBit, parsedAlignV))
848 return false;
849 data.textgen = parsedTextgen;
850 data.alignH = static_cast<DRW_Text::HAlign>(parsedAlignH);
851 data.alignV = static_cast<DRW_Text::VAlign>(parsedAlignV);
852
853 const auto finite = [](const DRW_Coord& point) {
854 return std::isfinite(point.x) && std::isfinite(point.y)
855 && std::isfinite(point.z);
856 };
857 return body.isGood() && stringBuffer.isGood() && finite(data.basePoint)
858 && finite(data.secPoint) && finite(data.extPoint)
859 && std::isfinite(data.thickness) && std::isfinite(data.oblique)
860 && std::isfinite(data.angle) && std::isfinite(data.height)
861 && std::isfinite(data.widthscale);
862}
863
864bool readBoundedHatchGradient(dwgBuffer& body, dwgBuffer *stringBuffer,
865 std::uint64_t endBit, DRW::Version version,
866 int& isGradient, int& reserved,
867 double& angle, double& shift, int& singleColor,
868 double& tint, UTF8STRINGstd::string& name,
869 std::vector<DRW_Hatch::GradientStop>& colors,
870 std::uint64_t stringEndBit =
871 std::numeric_limits<std::uint64_t>::max()) {
872 std::int32_t parsedIsGradient = 0;
873 std::int32_t parsedReserved = 0;
874 std::int32_t parsedSingleColor = 0;
875 double parsedAngle = 0.0;
876 double parsedShift = 0.0;
877 double parsedTint = 0.0;
878 if (!readBoundedBitLong(body, endBit, parsedIsGradient)
879 || !readBoundedBitLong(body, endBit, parsedReserved)
880 || !readBoundedBitDouble(body, endBit, parsedAngle)
881 || !readBoundedBitDouble(body, endBit, parsedShift)
882 || !readBoundedBitLong(body, endBit, parsedSingleColor)
883 || !readBoundedBitDouble(body, endBit, parsedTint))
884 return false;
885
886 std::uint32_t colorCount = 0;
887 if (!readTableBodyCount(version, &body, endBit, kMaxHatchItems, 14,
888 colorCount))
889 return false;
890 std::vector<DRW_Hatch::GradientStop> parsedColors;
891 if (!DRW::reserve(parsedColors, colorCount))
892 return false;
893 for (std::uint32_t i = 0; i < colorCount; ++i) {
894 DRW_Hatch::GradientStop stop;
895 std::uint16_t aciColor = 0;
896 std::int32_t rgb = 0;
897 std::uint8_t ignoredColor = 0;
898 if (!readBoundedBitDouble(body, endBit, stop.value)
899 || !readBoundedBitShort(body, endBit, aciColor)
900 || !readBoundedBitLong(body, endBit, rgb)
901 || !readBoundedRawChar8(body, endBit, ignoredColor)
902 || !std::isfinite(stop.value))
903 return false;
904 stop.aciColor = aciColor;
905 stop.rgb = rgb;
906 parsedColors.push_back(std::move(stop));
907 }
908
909 UTF8STRINGstd::string parsedName;
910 if (stringBuffer == nullptr
911 || !readBoundedVariableText(*stringBuffer, stringEndBit, version,
912 parsedName))
913 return false;
914
915 if (!std::isfinite(parsedAngle) || !std::isfinite(parsedShift)
916 || !std::isfinite(parsedTint))
917 return false;
918 isGradient = parsedIsGradient;
919 reserved = parsedReserved;
920 angle = parsedAngle;
921 shift = parsedShift;
922 singleColor = parsedSingleColor;
923 tint = parsedTint;
924 name = std::move(parsedName);
925 colors = std::move(parsedColors);
926 return true;
927}
928
929struct SurfaceDwgPayloadInfo {
930 bool acisEmpty = false;
931 int acisVersion = 0;
932 int modelerFormatVersion = 0;
933 int uIsolines = 0;
934 int vIsolines = 0;
935 bool revolvedFieldsValid = false;
936 std::uint32_t revolvedClassId = 0;
937 std::uint32_t revolvedId = 0;
938 DRW_Coord revolvedAxisPoint;
939 DRW_Coord revolvedAxisVector;
940 double revolvedAngle = 0.0;
941 double revolvedStartAngle = 0.0;
942 std::array<double, DRW_RevolvedSurface::kTransformSize> revolvedTransform{};
943 double revolvedDraftAngle = 0.0;
944 double revolvedDraftStartDistance = 0.0;
945 double revolvedDraftEndDistance = 0.0;
946 double revolvedTwistAngle = 0.0;
947 bool revolvedSolid = false;
948 bool revolvedCloseToAxis = false;
949 bool extrudedFieldsValid = false;
950 DRW_Coord extrudedSweepVector;
951 std::array<double, DRW_ExtrudedSurface::kTransformSize>
952 extrudedTransform{};
953 std::array<double, DRW_ExtrudedSurface::kTransformSize>
954 extrudedSweepEntityTransform{};
955 std::array<double, DRW_ExtrudedSurface::kTransformSize>
956 extrudedPathEntityTransform{};
957 double extrudedDraftAngle = 0.0;
958 double extrudedDraftStartDistance = 0.0;
959 double extrudedDraftEndDistance = 0.0;
960 double extrudedTwistAngle = 0.0;
961 double extrudedScaleFactor = 0.0;
962 double extrudedAlignAngle = 0.0;
963 bool extrudedSolid = false;
964 std::int32_t extrudedSweepAlignmentFlags = 0;
965 std::int32_t extrudedPathFlags = 0;
966 bool extrudedAlignStart = false;
967 bool extrudedBank = false;
968 bool extrudedBasePointSet = false;
969 bool extrudedSweepEntityTransformComputed = false;
970 bool extrudedPathEntityTransformComputed = false;
971 DRW_Coord extrudedReferenceVector;
972 bool sweptFieldsValid = false;
973 std::uint32_t sweptClassVersion = 0;
974 std::uint32_t sweptEntityId = 0;
975 std::vector<std::uint8_t> sweptData;
976 std::uint32_t sweptPathEntityId = 0;
977 std::vector<std::uint8_t> sweptPathData;
978 std::array<double, DRW_SweptSurface::kTransformSize>
979 sweptEntityTransform{};
980 std::array<double, DRW_SweptSurface::kTransformSize>
981 sweptPathEntityTransform{};
982 double sweptDraftAngle = 0.0;
983 double sweptDraftStartDistance = 0.0;
984 double sweptDraftEndDistance = 0.0;
985 double sweptTwistAngle = 0.0;
986 double sweptScaleFactor = 1.0;
987 double sweptAlignAngle = 0.0;
988 std::array<double, DRW_SweptSurface::kTransformSize>
989 sweptEntityTransformed{};
990 std::array<double, DRW_SweptSurface::kTransformSize>
991 sweptPathEntityTransformed{};
992 bool sweptSolid = false;
993 std::int32_t sweptAlignmentFlags = 0;
994 std::int32_t sweptPathFlags = 0;
995 bool sweptAlignStart = false;
996 bool sweptBank = false;
997 bool sweptBasePointSet = false;
998 bool sweptEntityTransformComputed = false;
999 bool sweptPathEntityTransformComputed = false;
1000 DRW_Coord sweptReferenceVector;
1001 bool loftedFieldsValid = false;
1002 std::array<double, DRW_LoftedSurface::kTransformSize>
1003 loftedEntityTransform{};
1004 std::int32_t loftedPlaneNormalLoftingType = 0;
1005 double loftedStartDraftAngle = 0.0;
1006 double loftedEndDraftAngle = 0.0;
1007 double loftedStartDraftMagnitude = 0.0;
1008 double loftedEndDraftMagnitude = 0.0;
1009 bool loftedArcLengthParameterization = false;
1010 bool loftedNoTwist = true;
1011 bool loftedAlignDirection = true;
1012 bool loftedSimpleSurfaces = true;
1013 bool loftedClosedSurfaces = false;
1014 bool loftedSolid = false;
1015 bool loftedRuledSurface = false;
1016 bool loftedVirtualGuide = false;
1017 std::int32_t loftedNumCrossSections = 0;
1018 std::int32_t loftedNumGuideCurves = 0;
1019 bool nurbsFieldsValid = false;
1020 std::uint16_t nurbsShort170 = 0;
1021 bool nurbsCvHullDisplay = false;
1022 DRW_Coord nurbsUvec1;
1023 DRW_Coord nurbsVvec1;
1024 DRW_Coord nurbsUvec2;
1025 DRW_Coord nurbsVvec2;
1026};
1027
1028std::uint64_t surfaceDwgBit(const dwgBuffer& buffer) {
1029 return buffer.getPosition() * 8u + buffer.getBitPos();
1030}
1031
1032bool surfaceDwgHasBits(const dwgBuffer& buffer, std::uint64_t endBit,
1033 std::uint64_t count) {
1034 const std::uint64_t current = surfaceDwgBit(buffer);
1035 return current <= endBit && count <= endBit - current;
1036}
1037
1038bool surfaceDwgReadBit(dwgBuffer& buffer, std::uint64_t endBit,
1039 std::uint8_t& value) {
1040 if (!surfaceDwgHasBits(buffer, endBit, 1))
1041 return false;
1042 // Probe variable-width fields on a fork so a short object frame cannot
1043 // invalidate the reader before the caller re-anchors at its boundary.
1044 dwgBuffer candidate(buffer);
1045 value = candidate.getBit();
1046 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit)
1047 return false;
1048 buffer = candidate;
1049 return true;
1050}
1051
1052bool surfaceDwgReadRawChar(dwgBuffer& buffer, std::uint64_t endBit,
1053 std::uint8_t& value) {
1054 if (!surfaceDwgHasBits(buffer, endBit, 8))
1055 return false;
1056 dwgBuffer candidate(buffer);
1057 value = candidate.getRawChar8();
1058 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit)
1059 return false;
1060 buffer = candidate;
1061 return true;
1062}
1063
1064bool surfaceDwgReadRawChar(dwgBuffer& buffer, std::uint64_t endBit) {
1065 std::uint8_t value = 0;
1066 return surfaceDwgReadRawChar(buffer, endBit, value);
1067}
1068
1069bool surfaceDwgReadBitShort(dwgBuffer& buffer, std::uint64_t endBit,
1070 std::uint16_t& value) {
1071 if (!surfaceDwgHasBits(buffer, endBit, 2))
1072 return false;
1073 dwgBuffer candidate(buffer);
1074 value = candidate.getBitShort();
1075 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit)
1076 return false;
1077 buffer = candidate;
1078 return true;
1079}
1080
1081bool surfaceDwgReadBitLong(dwgBuffer& buffer, std::uint64_t endBit,
1082 std::int32_t& value) {
1083 if (!surfaceDwgHasBits(buffer, endBit, 2))
1084 return false;
1085 dwgBuffer candidate(buffer);
1086 value = candidate.getBitLong();
1087 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit)
1088 return false;
1089 buffer = candidate;
1090 return true;
1091}
1092
1093bool surfaceDwgReadDouble(dwgBuffer& buffer, std::uint64_t endBit,
1094 double& value) {
1095 if (!surfaceDwgHasBits(buffer, endBit, 2))
1096 return false;
1097 dwgBuffer candidate(buffer);
1098 value = candidate.getBitDouble();
1099 if (!candidate.isGood() || surfaceDwgBit(candidate) > endBit
1100 || !std::isfinite(value))
1101 return false;
1102 buffer = candidate;
1103 return true;
1104}
1105
1106bool surfaceDwgReadDouble(dwgBuffer& buffer, std::uint64_t endBit) {
1107 double value = 0.0;
1108 return surfaceDwgReadDouble(buffer, endBit, value);
1109}
1110
1111bool surfaceDwgRead3BitDouble(dwgBuffer& buffer, std::uint64_t endBit) {
1112 return surfaceDwgReadDouble(buffer, endBit)
1113 && surfaceDwgReadDouble(buffer, endBit)
1114 && surfaceDwgReadDouble(buffer, endBit);
1115}
1116
1117bool surfaceDwgRead3BitDouble(dwgBuffer& buffer, std::uint64_t endBit,
1118 DRW_Coord& value) {
1119 return surfaceDwgReadDouble(buffer, endBit, value.x)
1120 && surfaceDwgReadDouble(buffer, endBit, value.y)
1121 && surfaceDwgReadDouble(buffer, endBit, value.z);
1122}
1123
1124bool surfaceDwgSkipBytes(dwgBuffer& buffer, std::uint64_t endBit,
1125 std::uint64_t count) {
1126 const std::uint64_t current = surfaceDwgBit(buffer);
1127 if (current > endBit
1128 || count > static_cast<std::uint64_t>(std::numeric_limits<std::int32_t>::max()) / 8u
1129 || count > (endBit - current) / 8u)
1130 return false;
1131 return buffer.moveBitPos(static_cast<std::int32_t>(count * 8u))
1132 && buffer.isGood() && surfaceDwgBit(buffer) <= endBit;
1133}
1134
1135bool surfaceDwgReadBytes(dwgBuffer& buffer, std::uint64_t endBit,
1136 std::int32_t count, std::size_t maxCount,
1137 std::vector<std::uint8_t>& data) {
1138 if (count < 0 || static_cast<std::size_t>(count) > maxCount
1139 || !surfaceDwgHasBits(buffer, endBit,
1140 static_cast<std::uint64_t>(count) * 8u))
1141 return false;
1142 dwgBuffer candidate(buffer);
1143 std::vector<std::uint8_t> decoded;
1144 if (!DRW::resize(decoded, count))
1145 return false;
1146 for (std::uint8_t& value : decoded) {
1147 if (!surfaceDwgReadRawChar(candidate, endBit, value))
1148 return false;
1149 }
1150 data = std::move(decoded);
1151 buffer = candidate;
1152 return true;
1153}
1154
1155std::uint8_t surfaceDwgPeekByte(const std::vector<std::uint8_t>& data,
1156 std::size_t index, std::uint8_t bit) {
1157 if (index >= data.size())
1158 return 0;
1159 if (bit == 0)
1160 return data[index];
1161 const std::uint8_t next = index + 1 < data.size() ? data[index + 1] : 0;
1162 return static_cast<std::uint8_t>((data[index] << bit) | (next >> (8 - bit)));
1163}
1164
1165std::size_t surfaceDwgFindMarker(const std::vector<std::uint8_t>& data,
1166 std::size_t start, std::uint8_t bit,
1167 const std::uint8_t* marker,
1168 std::size_t markerSize) {
1169 if (marker == nullptr || markerSize == 0 || data.size() < markerSize
1170 || start > data.size() - markerSize)
1171 return std::numeric_limits<std::size_t>::max();
1172 for (std::size_t index = start; index <= data.size() - markerSize; ++index) {
1173 bool matched = true;
1174 for (std::size_t offset = 0; offset < markerSize; ++offset) {
1175 if (surfaceDwgPeekByte(data, index + offset, bit) != marker[offset]) {
1176 matched = false;
1177 break;
1178 }
1179 }
1180 if (matched)
1181 return index;
1182 }
1183 return std::numeric_limits<std::size_t>::max();
1184}
1185
1186bool surfaceDwgSkipAcis(dwgBuffer& buffer, const std::vector<std::uint8_t>& data,
1187 std::uint64_t endBit, bool& acisEmpty, int& acisVersion) {
1188 std::uint8_t empty = 0;
1189 if (!surfaceDwgReadBit(buffer, endBit, empty))
1190 return false;
1191 acisEmpty = empty != 0;
1192 if (acisEmpty)
1193 return true;
1194
1195 std::uint8_t unused = 0;
1196 std::uint16_t version = 0;
1197 if (!surfaceDwgReadBit(buffer, endBit, unused)
1198 || !surfaceDwgReadBitShort(buffer, endBit, version))
1199 return false;
1200 acisVersion = static_cast<int>(version);
1201
1202 if (version == 2) {
1203 static constexpr std::uint8_t endAcis[] = {
1204 0x0E, 0x03, 0x45, 0x6E, 0x64, 0x0E, 0x02, 0x6F, 0x66, 0x0E,
1205 0x04, 0x41, 0x43, 0x49, 0x53, 0x0D, 0x04, 0x64, 0x61, 0x74,
1206 0x61};
1207 static constexpr std::uint8_t endAsm[] = {
1208 0x0E, 0x03, 0x45, 0x6E, 0x64, 0x0E, 0x02, 0x6F, 0x66, 0x0E,
1209 0x03, 0x41, 0x53, 0x4D, 0x0D, 0x04, 0x64, 0x61, 0x74, 0x61};
1210 const std::size_t start = static_cast<std::size_t>(buffer.getPosition());
1211 const std::uint8_t bit = buffer.getBitPos();
1212 std::size_t marker = surfaceDwgFindMarker(
1213 data, start, bit, endAcis, sizeof(endAcis));
1214 std::size_t markerSize = sizeof(endAcis);
1215 if (marker == std::numeric_limits<std::size_t>::max()) {
1216 marker = surfaceDwgFindMarker(
1217 data, start, bit, endAsm, sizeof(endAsm));
1218 markerSize = sizeof(endAsm);
1219 }
1220 if (marker == std::numeric_limits<std::size_t>::max()
1221 || marker < start)
1222 return false;
1223 return surfaceDwgSkipBytes(buffer, endBit,
1224 marker - start + markerSize);
1225 }
1226
1227 for (std::uint32_t block = 0; block < 65536u; ++block) {
1228 std::int32_t blockSize = 0;
1229 if (!surfaceDwgReadBitLong(buffer, endBit, blockSize))
1230 return false;
1231 if (blockSize == 0)
1232 return true;
1233 if (blockSize < 0
1234 || !surfaceDwgSkipBytes(buffer, endBit,
1235 static_cast<std::uint32_t>(blockSize)))
1236 return false;
1237 }
1238 return false;
1239}
1240
1241bool surfaceDwgSkipWire(dwgBuffer& buffer, std::uint64_t endBit,
1242 DRW::Version version) {
1243 if (!surfaceDwgReadRawChar(buffer, endBit))
1244 return false;
1245 std::int32_t ignored = 0;
1246 if (!surfaceDwgReadBitLong(buffer, endBit, ignored))
1247 return false;
1248 std::uint16_t color = 0;
1249 if (version < DRW::AC1018) {
1250 if (!surfaceDwgReadBitShort(buffer, endBit, color))
1251 return false;
1252 } else if (!surfaceDwgReadBitLong(buffer, endBit, ignored)) {
1253 return false;
1254 }
1255 if (!surfaceDwgReadBitLong(buffer, endBit, ignored))
1256 return false;
1257
1258 std::int32_t pointCount = 0;
1259 if (!surfaceDwgReadBitLong(buffer, endBit, pointCount)
1260 || pointCount < 0 || pointCount > 1000000)
1261 return false;
1262 for (std::int32_t point = 0; point < pointCount; ++point) {
1263 if (!surfaceDwgRead3BitDouble(buffer, endBit))
1264 return false;
1265 }
1266
1267 std::uint8_t transformPresent = 0;
1268 if (!surfaceDwgReadBit(buffer, endBit, transformPresent))
1269 return false;
1270 if (transformPresent != 0) {
1271 for (int field = 0; field < 5; ++field) {
1272 if (!surfaceDwgRead3BitDouble(buffer, endBit))
1273 return false;
1274 }
1275 for (int field = 0; field < 3; ++field) {
1276 if (!surfaceDwgReadBit(buffer, endBit, transformPresent))
1277 return false;
1278 }
1279 }
1280 return true;
1281}
1282
1283bool surfaceDwgSkipCommon3dSolid(dwgBuffer& buffer, std::uint64_t endBit,
1284 DRW::Version version, int acisVersion) {
1285 std::uint8_t present = 0;
1286 if (!surfaceDwgReadBit(buffer, endBit, present))
1287 return false;
1288 if (present != 0) {
1289 if (!surfaceDwgReadBit(buffer, endBit, present))
1290 return false;
1291 if (present != 0 && !surfaceDwgRead3BitDouble(buffer, endBit))
1292 return false;
1293
1294 std::int32_t ignored = 0;
1295 if (!surfaceDwgReadBitLong(buffer, endBit, ignored)
1296 || !surfaceDwgReadBit(buffer, endBit, present))
1297 return false;
1298 if (present != 0) {
1299 std::int32_t count = 0;
1300 if (!surfaceDwgReadBitLong(buffer, endBit, count)
1301 || count < 0 || count > 1000000)
1302 return false;
1303 for (std::int32_t i = 0; i < count; ++i) {
1304 if (!surfaceDwgSkipWire(buffer, endBit, version))
1305 return false;
1306 }
1307 if (!surfaceDwgReadBitLong(buffer, endBit, count)
1308 || count < 0 || count > 1000000)
1309 return false;
1310 for (std::int32_t i = 0; i < count; ++i) {
1311 if (!surfaceDwgReadBitLong(buffer, endBit, ignored)
1312 || !surfaceDwgRead3BitDouble(buffer, endBit)
1313 || !surfaceDwgRead3BitDouble(buffer, endBit)
1314 || !surfaceDwgRead3BitDouble(buffer, endBit)
1315 || !surfaceDwgReadBit(buffer, endBit, present)
1316 || !surfaceDwgReadBit(buffer, endBit, present))
1317 return false;
1318 if (present != 0) {
1319 std::int32_t wireCount = 0;
1320 if (!surfaceDwgReadBitLong(buffer, endBit, wireCount)
1321 || wireCount < 0 || wireCount > 1000000)
1322 return false;
1323 for (std::int32_t wire = 0; wire < wireCount; ++wire) {
1324 if (!surfaceDwgSkipWire(buffer, endBit, version))
1325 return false;
1326 }
1327 }
1328 }
1329 }
1330 }
1331
1332 if (!surfaceDwgReadBit(buffer, endBit, present))
1333 return false;
1334 if (acisVersion > 1 && version >= DRW::AC1021) {
1335 std::int32_t count = 0;
1336 if (!surfaceDwgReadBitLong(buffer, endBit, count)
1337 || count < 0 || count > 1000000)
1338 return false;
1339 for (std::int32_t i = 0; i < count; ++i) {
1340 std::int32_t ignored = 0;
1341 if (!surfaceDwgReadBitLong(buffer, endBit, ignored)
1342 || !surfaceDwgReadBitLong(buffer, endBit, ignored))
1343 return false;
1344 }
1345 }
1346 if (version >= DRW::AC1027) {
1347 if (!surfaceDwgReadBit(buffer, endBit, present))
1348 return false;
1349 std::int32_t ignored = 0;
1350 std::uint16_t ignoredShort = 0;
1351 if (!surfaceDwgReadBitLong(buffer, endBit, ignored)
1352 || !surfaceDwgReadBitShort(buffer, endBit, ignoredShort)
1353 || !surfaceDwgReadBitShort(buffer, endBit, ignoredShort)
1354 || !surfaceDwgSkipBytes(buffer, endBit, 8)
1355 || !surfaceDwgReadBitLong(buffer, endBit, ignored))
1356 return false;
1357 }
1358 return true;
1359}
1360
1361bool surfaceHasModelerFormatVersion(DRW::ETYPE type) {
1362 switch (type) {
1363 case DRW::PLANESURFACE:
1364 case DRW::LOFTEDSURFACE:
1365 case DRW::REVOLVEDSURFACE:
1366 case DRW::SWEPTSURFACE:
1367 return true;
1368 case DRW::EXTRUDEDSURFACE:
1369 case DRW::NURBSURFACE:
1370 default:
1371 return false;
1372 }
1373}
1374
1375std::uint16_t surfaceModelerFormatVersionLimit(DRW::ETYPE type) {
1376 return type == DRW::LOFTEDSURFACE ? kMaxLoftedSurfaceModelerFormatVersion
1377 : std::numeric_limits<std::uint16_t>::max();
1378}
1379
1380bool readSurfaceDwgPayload(const std::vector<std::uint8_t>& data,
1381 std::uint64_t bitSize, DRW::Version version,
1382 DRW::ETYPE type,
1383 bool hasModelerFormatVersion,
1384 std::uint16_t modelerFormatVersionLimit,
1385 SurfaceDwgPayloadInfo& info) {
1386 if (data.empty() || bitSize == 0
1387 || bitSize > static_cast<std::uint64_t>(data.size()) * 8u)
1388 return false;
1389
1390 std::vector<std::uint8_t> payload = data;
1391 dwgBuffer buffer(payload.data(), payload.size());
1392 const std::uint64_t endBit = bitSize;
1393 bool acisEmpty = false;
1394 int acisVersion = 0;
1395 if (!surfaceDwgSkipAcis(buffer, payload, endBit, acisEmpty, acisVersion))
1396 return false;
1397 if (!acisEmpty
1398 && !surfaceDwgSkipCommon3dSolid(buffer, endBit, version, acisVersion))
1399 return false;
1400
1401 std::uint16_t modelerFormatVersion = 0;
1402 std::uint16_t uIsolines = 0;
1403 std::uint16_t vIsolines = 0;
1404 if ((hasModelerFormatVersion
1405 && !surfaceDwgReadBitShort(buffer, endBit, modelerFormatVersion))
1406 || !surfaceDwgReadBitShort(buffer, endBit, uIsolines)
1407 || !surfaceDwgReadBitShort(buffer, endBit, vIsolines)) {
1408 return false;
1409 }
1410 if (hasModelerFormatVersion
1411 && modelerFormatVersion > modelerFormatVersionLimit)
1412 return false;
1413
1414 if (type == DRW::EXTRUDEDSURFACE) {
1415 // DWG stores SweepOptions before the sweep vector and its transform;
1416 // keep the candidate isolated because the same fields have a
1417 // different order in the DXF subclass.
1418 dwgBuffer candidate = buffer;
1419 std::array<double, DRW_ExtrudedSurface::kTransformSize>
1420 sweepEntityTransform{};
1421 std::array<double, DRW_ExtrudedSurface::kTransformSize>
1422 pathEntityTransform{};
1423 std::array<double, DRW_ExtrudedSurface::kTransformSize>
1424 extrudedTransform{};
1425 double draftAngle = 0.0;
1426 double draftStartDistance = 0.0;
1427 double draftEndDistance = 0.0;
1428 double twistAngle = 0.0;
1429 double scaleFactor = 0.0;
1430 double alignAngle = 0.0;
1431 std::uint8_t solid = 0;
1432 std::uint16_t sweepAlignmentFlags = 0;
1433 std::uint16_t pathFlags = 0;
1434 std::uint8_t alignStart = 0;
1435 std::uint8_t bank = 0;
1436 std::uint8_t basePointSet = 0;
1437 std::uint8_t sweepEntityTransformComputed = 0;
1438 std::uint8_t pathEntityTransformComputed = 0;
1439 DRW_Coord referenceVector;
1440 DRW_Coord sweepVector;
1441 auto readMatrix = [&](std::array<double,
1442 DRW_ExtrudedSurface::kTransformSize>& matrix) {
1443 for (double& value : matrix) {
1444 if (!surfaceDwgReadDouble(candidate, endBit, value))
1445 return false;
1446 }
1447 return true;
1448 };
1449 bool valid = surfaceDwgReadDouble(candidate, endBit, draftAngle)
1450 && surfaceDwgReadDouble(candidate, endBit, draftStartDistance)
1451 && surfaceDwgReadDouble(candidate, endBit, draftEndDistance)
1452 && surfaceDwgReadDouble(candidate, endBit, twistAngle)
1453 && surfaceDwgReadDouble(candidate, endBit, scaleFactor)
1454 && surfaceDwgReadDouble(candidate, endBit, alignAngle)
1455 && readMatrix(sweepEntityTransform)
1456 && readMatrix(pathEntityTransform)
1457 && surfaceDwgReadBit(candidate, endBit, solid)
1458 && surfaceDwgReadBitShort(candidate, endBit, sweepAlignmentFlags)
1459 && surfaceDwgReadBitShort(candidate, endBit, pathFlags)
1460 && surfaceDwgReadBit(candidate, endBit, alignStart)
1461 && surfaceDwgReadBit(candidate, endBit, bank)
1462 && surfaceDwgReadBit(candidate, endBit, basePointSet)
1463 && surfaceDwgReadBit(candidate, endBit, sweepEntityTransformComputed)
1464 && surfaceDwgReadBit(candidate, endBit, pathEntityTransformComputed)
1465 && surfaceDwgRead3BitDouble(candidate, endBit, referenceVector)
1466 && surfaceDwgRead3BitDouble(candidate, endBit, sweepVector)
1467 && readMatrix(extrudedTransform);
1468 if (valid) {
1469 info.extrudedFieldsValid = true;
1470 info.extrudedSweepVector = sweepVector;
1471 info.extrudedTransform = extrudedTransform;
1472 info.extrudedSweepEntityTransform = sweepEntityTransform;
1473 info.extrudedPathEntityTransform = pathEntityTransform;
1474 info.extrudedDraftAngle = draftAngle;
1475 info.extrudedDraftStartDistance = draftStartDistance;
1476 info.extrudedDraftEndDistance = draftEndDistance;
1477 info.extrudedTwistAngle = twistAngle;
1478 info.extrudedScaleFactor = scaleFactor;
1479 info.extrudedAlignAngle = alignAngle;
1480 info.extrudedSolid = solid != 0;
1481 info.extrudedSweepAlignmentFlags = sweepAlignmentFlags;
1482 info.extrudedPathFlags = pathFlags;
1483 info.extrudedAlignStart = alignStart != 0;
1484 info.extrudedBank = bank != 0;
1485 info.extrudedBasePointSet = basePointSet != 0;
1486 info.extrudedSweepEntityTransformComputed =
1487 sweepEntityTransformComputed != 0;
1488 info.extrudedPathEntityTransformComputed =
1489 pathEntityTransformComputed != 0;
1490 info.extrudedReferenceVector = referenceVector;
1491 }
1492 } else if (type == DRW::LOFTEDSURFACE) {
1493 dwgBuffer candidate = buffer;
1494 std::array<double, DRW_LoftedSurface::kTransformSize>
1495 loftEntityTransform{};
1496 std::int32_t planeNormalLoftingType = 0;
1497 double startDraftAngle = 0.0;
1498 double endDraftAngle = 0.0;
1499 double startDraftMagnitude = 0.0;
1500 double endDraftMagnitude = 0.0;
1501 std::uint8_t arcLengthParameterization = 0;
1502 std::uint8_t noTwist = 0;
1503 std::uint8_t alignDirection = 0;
1504 std::uint8_t simpleSurfaces = 0;
1505 std::uint8_t closedSurfaces = 0;
1506 std::uint8_t solid = 0;
1507 std::uint8_t ruledSurface = 0;
1508 std::uint8_t virtualGuide = 0;
1509 std::uint16_t numCrossSections = 0;
1510 std::uint16_t numGuideCurves = 0;
1511 auto readMatrix = [&](std::array<double,
1512 DRW_LoftedSurface::kTransformSize>& matrix) {
1513 for (double& value : matrix) {
1514 if (!surfaceDwgReadDouble(candidate, endBit, value))
1515 return false;
1516 }
1517 return true;
1518 };
1519 const bool valid = readMatrix(loftEntityTransform)
1520 && surfaceDwgReadBitLong(candidate, endBit,
1521 planeNormalLoftingType)
1522 && surfaceDwgReadDouble(candidate, endBit, startDraftAngle)
1523 && surfaceDwgReadDouble(candidate, endBit, endDraftAngle)
1524 && surfaceDwgReadDouble(candidate, endBit, startDraftMagnitude)
1525 && surfaceDwgReadDouble(candidate, endBit, endDraftMagnitude)
1526 && surfaceDwgReadBit(candidate, endBit, arcLengthParameterization)
1527 && surfaceDwgReadBit(candidate, endBit, noTwist)
1528 && surfaceDwgReadBit(candidate, endBit, alignDirection)
1529 && surfaceDwgReadBit(candidate, endBit, simpleSurfaces)
1530 && surfaceDwgReadBit(candidate, endBit, closedSurfaces)
1531 && surfaceDwgReadBit(candidate, endBit, solid)
1532 && surfaceDwgReadBit(candidate, endBit, ruledSurface)
1533 && surfaceDwgReadBit(candidate, endBit, virtualGuide)
1534 && surfaceDwgReadBitShort(candidate, endBit, numCrossSections)
1535 && surfaceDwgReadBitShort(candidate, endBit, numGuideCurves)
1536 && numCrossSections <= DRW_LoftedSurface::kMaxReferenceTokenCount
1537 && numGuideCurves <= DRW_LoftedSurface::kMaxReferenceTokenCount;
1538 if (valid) {
1539 info.loftedFieldsValid = true;
1540 info.loftedEntityTransform = loftEntityTransform;
1541 info.loftedPlaneNormalLoftingType = planeNormalLoftingType;
1542 info.loftedStartDraftAngle = startDraftAngle;
1543 info.loftedEndDraftAngle = endDraftAngle;
1544 info.loftedStartDraftMagnitude = startDraftMagnitude;
1545 info.loftedEndDraftMagnitude = endDraftMagnitude;
1546 info.loftedArcLengthParameterization = arcLengthParameterization != 0;
1547 info.loftedNoTwist = noTwist != 0;
1548 info.loftedAlignDirection = alignDirection != 0;
1549 info.loftedSimpleSurfaces = simpleSurfaces != 0;
1550 info.loftedClosedSurfaces = closedSurfaces != 0;
1551 info.loftedSolid = solid != 0;
1552 info.loftedRuledSurface = ruledSurface != 0;
1553 info.loftedVirtualGuide = virtualGuide != 0;
1554 info.loftedNumCrossSections = numCrossSections;
1555 info.loftedNumGuideCurves = numGuideCurves;
1556 }
1557 } else if (type == DRW::SWEPTSURFACE) {
1558 dwgBuffer candidate = buffer;
1559 std::int32_t classVersion = 0;
1560 std::int32_t sweepEntityId = 0;
1561 std::int32_t sweepDataSize = 0;
1562 std::int32_t pathEntityId = 0;
1563 std::int32_t pathDataSize = 0;
1564 std::vector<std::uint8_t> sweepData;
1565 std::vector<std::uint8_t> pathData;
1566 std::array<double, DRW_SweptSurface::kTransformSize>
1567 sweepEntityTransform{};
1568 std::array<double, DRW_SweptSurface::kTransformSize>
1569 pathEntityTransform{};
1570 double draftAngle = 0.0;
1571 double draftStartDistance = 0.0;
1572 double draftEndDistance = 0.0;
1573 double twistAngle = 0.0;
1574 double scaleFactor = 1.0;
1575 double alignAngle = 0.0;
1576 std::uint8_t solid = 0;
1577 std::uint16_t sweepAlignmentFlags = 0;
1578 std::uint16_t pathFlags = 0;
1579 std::uint8_t alignStart = 0;
1580 std::uint8_t bank = 0;
1581 std::uint8_t basePointSet = 0;
1582 std::uint8_t sweepEntityTransformComputed = 0;
1583 std::uint8_t pathEntityTransformComputed = 0;
1584 DRW_Coord referenceVector;
1585 auto readMatrix = [&](std::array<double,
1586 DRW_SweptSurface::kTransformSize>& matrix) {
1587 for (double& value : matrix) {
1588 if (!surfaceDwgReadDouble(candidate, endBit, value))
1589 return false;
1590 }
1591 return true;
1592 };
1593 bool valid = surfaceDwgReadBitLong(candidate, endBit, classVersion)
1594 && classVersion >= 0 && classVersion <= 10
1595 && surfaceDwgReadBitLong(candidate, endBit, sweepEntityId)
1596 && sweepEntityId >= 0
1597 && surfaceDwgReadBitLong(candidate, endBit, sweepDataSize)
1598 && surfaceDwgReadBytes(candidate, endBit, sweepDataSize,
1599 DRW_SweptSurface::kMaxSweepDataSize,
1600 sweepData)
1601 && surfaceDwgReadBitLong(candidate, endBit, pathEntityId)
1602 && pathEntityId >= 0
1603 && surfaceDwgReadBitLong(candidate, endBit, pathDataSize)
1604 && surfaceDwgReadBytes(candidate, endBit, pathDataSize,
1605 DRW_SweptSurface::kMaxSweepDataSize,
1606 pathData)
1607 && surfaceDwgReadDouble(candidate, endBit, draftAngle)
1608 && surfaceDwgReadDouble(candidate, endBit, draftStartDistance)
1609 && surfaceDwgReadDouble(candidate, endBit, draftEndDistance)
1610 && surfaceDwgReadDouble(candidate, endBit, twistAngle)
1611 && surfaceDwgReadDouble(candidate, endBit, scaleFactor)
1612 && surfaceDwgReadDouble(candidate, endBit, alignAngle)
1613 && readMatrix(sweepEntityTransform)
1614 && readMatrix(pathEntityTransform)
1615 && surfaceDwgReadBit(candidate, endBit, solid)
1616 && surfaceDwgReadBitShort(candidate, endBit, sweepAlignmentFlags)
1617 && surfaceDwgReadBitShort(candidate, endBit, pathFlags)
1618 && surfaceDwgReadBit(candidate, endBit, alignStart)
1619 && surfaceDwgReadBit(candidate, endBit, bank)
1620 && surfaceDwgReadBit(candidate, endBit, basePointSet)
1621 && surfaceDwgReadBit(candidate, endBit, sweepEntityTransformComputed)
1622 && surfaceDwgReadBit(candidate, endBit, pathEntityTransformComputed)
1623 && surfaceDwgRead3BitDouble(candidate, endBit, referenceVector);
1624 if (valid) {
1625 info.sweptFieldsValid = true;
1626 info.sweptClassVersion = static_cast<std::uint32_t>(classVersion);
1627 info.sweptEntityId = static_cast<std::uint32_t>(sweepEntityId);
1628 info.sweptData = std::move(sweepData);
1629 info.sweptPathEntityId = static_cast<std::uint32_t>(pathEntityId);
1630 info.sweptPathData = std::move(pathData);
1631 info.sweptEntityTransform = sweepEntityTransform;
1632 info.sweptPathEntityTransform = pathEntityTransform;
1633 info.sweptDraftAngle = draftAngle;
1634 info.sweptDraftStartDistance = draftStartDistance;
1635 info.sweptDraftEndDistance = draftEndDistance;
1636 info.sweptTwistAngle = twistAngle;
1637 info.sweptScaleFactor = scaleFactor;
1638 info.sweptAlignAngle = alignAngle;
1639 info.sweptSolid = solid != 0;
1640 info.sweptAlignmentFlags = sweepAlignmentFlags;
1641 info.sweptPathFlags = pathFlags;
1642 info.sweptAlignStart = alignStart != 0;
1643 info.sweptBank = bank != 0;
1644 info.sweptBasePointSet = basePointSet != 0;
1645 info.sweptEntityTransformComputed =
1646 sweepEntityTransformComputed != 0;
1647 info.sweptPathEntityTransformComputed =
1648 pathEntityTransformComputed != 0;
1649 info.sweptReferenceVector = referenceVector;
1650 }
1651 } else if (type == DRW::NURBSURFACE && version >= DRW::AC1027) {
1652 dwgBuffer candidate = buffer;
1653 std::uint16_t short170 = 0;
1654 std::uint8_t cvHullDisplay = 0;
1655 DRW_Coord uvec1;
1656 DRW_Coord vvec1;
1657 DRW_Coord uvec2;
1658 DRW_Coord vvec2;
1659 const bool valid = surfaceDwgReadBitShort(candidate, endBit, short170)
1660 && surfaceDwgReadBit(candidate, endBit, cvHullDisplay)
1661 && surfaceDwgRead3BitDouble(candidate, endBit, uvec1)
1662 && surfaceDwgRead3BitDouble(candidate, endBit, vvec1)
1663 && surfaceDwgRead3BitDouble(candidate, endBit, uvec2)
1664 && surfaceDwgRead3BitDouble(candidate, endBit, vvec2);
1665 if (valid) {
1666 info.nurbsFieldsValid = true;
1667 info.nurbsShort170 = short170;
1668 info.nurbsCvHullDisplay = cvHullDisplay != 0;
1669 info.nurbsUvec1 = uvec1;
1670 info.nurbsVvec1 = vvec1;
1671 info.nurbsUvec2 = uvec2;
1672 info.nurbsVvec2 = vvec2;
1673 }
1674 } else if (type == DRW::REVOLVEDSURFACE) {
1675 // The AcDbRevolvedSurface tail is optional from the carrier's point
1676 // of view: retain the complete bounded body when a producer omits or
1677 // truncates it, but publish the typed values only as one transaction.
1678 dwgBuffer candidate = buffer;
1679 std::int32_t classVersion = 0;
1680 std::int32_t id = 0;
1681 DRW_Coord axisPoint;
1682 DRW_Coord axisVector;
1683 std::array<double, DRW_RevolvedSurface::kTransformSize> transform{};
1684 double revolveAngle = 0.0;
1685 double startAngle = 0.0;
1686 double draftAngle = 0.0;
1687 double draftStartDistance = 0.0;
1688 double draftEndDistance = 0.0;
1689 double twistAngle = 0.0;
1690 std::uint8_t solid = 0;
1691 std::uint8_t closeToAxis = 0;
1692 bool valid = surfaceDwgReadBitLong(candidate, endBit, classVersion)
1693 && classVersion >= 0 && classVersion <= 10
1694 && surfaceDwgReadBitLong(candidate, endBit, id)
1695 && id >= 0
1696 && surfaceDwgRead3BitDouble(candidate, endBit, axisPoint)
1697 && surfaceDwgRead3BitDouble(candidate, endBit, axisVector)
1698 && surfaceDwgReadDouble(candidate, endBit, revolveAngle)
1699 && surfaceDwgReadDouble(candidate, endBit, startAngle);
1700 for (double& value : transform) {
1701 valid = valid && surfaceDwgReadDouble(candidate, endBit, value);
1702 }
1703 valid = valid
1704 && surfaceDwgReadDouble(candidate, endBit, draftAngle)
1705 && surfaceDwgReadDouble(candidate, endBit, draftStartDistance)
1706 && surfaceDwgReadDouble(candidate, endBit, draftEndDistance)
1707 && surfaceDwgReadDouble(candidate, endBit, twistAngle)
1708 && surfaceDwgReadBit(candidate, endBit, solid)
1709 && surfaceDwgReadBit(candidate, endBit, closeToAxis);
1710 if (valid) {
1711 info.revolvedFieldsValid = true;
1712 info.revolvedClassId = static_cast<std::uint32_t>(classVersion);
1713 info.revolvedId = static_cast<std::uint32_t>(id);
1714 info.revolvedAxisPoint = axisPoint;
1715 info.revolvedAxisVector = axisVector;
1716 info.revolvedAngle = revolveAngle;
1717 info.revolvedStartAngle = startAngle;
1718 info.revolvedTransform = transform;
1719 info.revolvedDraftAngle = draftAngle;
1720 info.revolvedDraftStartDistance = draftStartDistance;
1721 info.revolvedDraftEndDistance = draftEndDistance;
1722 info.revolvedTwistAngle = twistAngle;
1723 info.revolvedSolid = solid != 0;
1724 info.revolvedCloseToAxis = closeToAxis != 0;
1725 }
1726 }
1727
1728 info.acisEmpty = acisEmpty;
1729 info.acisVersion = acisVersion;
1730 info.modelerFormatVersion = static_cast<int>(modelerFormatVersion);
1731 info.uIsolines = static_cast<int>(uIsolines);
1732 info.vIsolines = static_cast<int>(vIsolines);
1733 return true;
1734}
1735
1736int proxyEntityDxfCode(std::uint8_t handleCode) {
1737 switch (handleCode) {
1738 case 3: return 340;
1739 case 4:
1740 case 6:
1741 case 8:
1742 case 10:
1743 case 12: return 350;
1744 case 5: return 360;
1745 default: return 330;
1746 }
1747}
1748
1749DRW_DwgSubrecordRange makeDwgSubrecordRange(const char *name, std::uint64_t startBit,
1750 std::uint64_t endBit, DRW::Version version,
1751 std::uint32_t count, bool parseComplete) {
1752 DRW_DwgSubrecordRange range;
1753 range.m_name = name;
1754 range.m_startBit = startBit;
1755 range.m_bitSize = endBit >= startBit ? endBit - startBit : 0;
1756 range.m_version = version;
1757 range.m_count = count;
1758 range.m_parseComplete = parseComplete;
1759 return range;
1760}
1761
1762bool isValidSplineDegree(int degree) {
1763 return degree >= 1 && degree <= kMaxSplineDegree;
1764}
1765
1766bool isValidControlSplineLayout(int degree, std::int32_t knotCount, std::int32_t controlCount) {
1767 if (!isValidSplineDegree(degree) || !isValidCount(knotCount, kMaxSplineItems) ||
1768 !isValidCount(controlCount, kMaxSplineItems)) {
1769 return false;
1770 }
1771
1772 if (controlCount < degree + 1) {
1773 return false;
1774 }
1775
1776 const std::int64_t expectedKnots = static_cast<std::int64_t>(controlCount) + degree + 1;
1777 return expectedKnots <= kMaxSplineItems && knotCount == expectedKnots;
1778}
1779
1780bool isValidFitSplineLayout(int degree, std::int32_t fitCount) {
1781 return isValidSplineDegree(degree) && isValidCount(fitCount, kMaxSplineItems) &&
1782 fitCount >= 2;
1783}
1784
1785bool differsFromUnitWeight(double weight) {
1786 return std::fabs(weight - 1.0) > 1e-12;
1787}
1788
1789//! \brief Compare two doubles by stored representation.
1790//! For values a compact DWG form restores verbatim, the match has to be exact:
1791//! dataFlags 3 makes the reader return exactly 1.0 and dataFlags 2 makes it
1792//! return xscale for y and z (see DRW_Insert::parseDwg), so picking those forms
1793//! on a tolerant match would silently round the scale that gets written. This
1794//! asks the question that is actually meant - "will the reader reconstruct this
1795//! value?" - without floating point comparison semantics, and as a side effect
1796//! keeps -0.0 distinct from 0.0, which `==` does not.
1797bool sameStoredDouble(double a, double b) {
1798 return std::memcmp(&a, &b, sizeof a) == 0;
1799}
1800
1801void putDwgReference(dwgBufferW *buf, std::uint8_t code,
1802 std::uint32_t ref) {
1803 dwgHandle h;
1804 h.code = code;
1805 h.ref = ref;
1806 h.size = 0;
1807 if (ref != 0) {
1808 std::uint32_t t = ref;
1809 while (t != 0) {
1810 t >>= 8;
1811 ++h.size;
1812 }
1813 }
1814 buf->putHandle(h);
1815}
1816
1817void putHardPointerHandle(dwgBufferW *buf, std::uint32_t ref) {
1818 putDwgReference(buf, DRW::DwgHardPointer, ref);
1819}
1820
1821void appendBitBuffer(dwgBufferW *destination, const dwgBufferW& source) {
1822 if (destination == nullptr)
1823 return;
1824 const auto& bytes = source.data();
1825 for (std::uint32_t bit = 0; bit < source.bitCount(); ++bit) {
1826 const std::uint8_t value =
1827 static_cast<std::uint8_t>((bytes[bit / 8] >> (7 - (bit % 8))) & 1);
1828 destination->putBit(value);
1829 }
1830}
1831
1832void putNullableHardPointerHandle(dwgBufferW *buf, std::uint32_t ref) {
1833 dwgHandle h;
1834 h.code = ref == 0 ? 0 : 5;
1835 h.ref = ref;
1836 h.size = 0;
1837 if (ref != 0) {
1838 std::uint32_t t = ref;
1839 while (t != 0) {
1840 t >>= 8;
1841 ++h.size;
1842 }
1843 }
1844 buf->putHandle(h);
1845}
1846
1847void putSurfaceHandle(dwgBufferW *buf, std::uint32_t ref) {
1848 dwgHandle h;
1849 h.code = 5;
1850 h.ref = ref;
1851 h.size = 0;
1852 for (std::uint32_t value = ref; value != 0; value >>= 8)
1853 ++h.size;
1854 buf->putHandle(h);
1855}
1856
1857std::size_t surfaceRawBitCapacity(const std::vector<std::uint8_t>& data) {
1858 constexpr std::size_t maxBitSize =
1859 std::numeric_limits<std::uint32_t>::max();
1860 constexpr std::size_t maxBytes = maxBitSize / 8u;
1861 return data.size() > maxBytes ? maxBitSize : data.size() * 8u;
1862}
1863
1864bool putSurfaceRawBits(dwgBufferW *buf, const std::vector<std::uint8_t>& data,
1865 std::uint32_t bitSize) {
1866 const std::size_t bitCapacity = surfaceRawBitCapacity(data);
1867 if (buf == nullptr || bitSize == 0 || bitSize > bitCapacity)
1868 return false;
1869 for (std::uint32_t bit = 0; bit < bitSize; ++bit) {
1870 const std::uint8_t byte = data[bit >> 3];
1871 buf->putBit((byte >> (7u - (bit & 7u))) & 1u);
1872 }
1873 return true;
1874}
1875
1876bool finiteSurfaceCoord(const DRW_Coord& value) {
1877 return std::isfinite(value.x) && std::isfinite(value.y)
1878 && std::isfinite(value.z);
1879}
1880
1881template <std::size_t N>
1882bool finiteSurfaceMatrix(const std::array<double, N>& values) {
1883 return std::all_of(values.begin(), values.end(),
1884 [](double value) { return std::isfinite(value); });
1885}
1886
1887std::uint16_t bitShortFromInt(int value) {
1888 if (value < 0)
1889 return 0;
1890 if (value > 0xffff)
1891 return 0xffff;
1892 return static_cast<std::uint16_t>(value);
1893}
1894
1895std::uint32_t readTableHandle(
1896 dwgBuffer *hdlBuf,
1897 std::uint64_t endBit = std::numeric_limits<std::uint64_t>::max()) {
1898 if (hdlBuf == nullptr || !hdlBuf->isGood())
1899 return 0;
1900 dwgBuffer probe = hdlBuf->forkIndependent();
1901 dwgHandle h;
1902 if (!readBoundedDwgHandle(probe, endBit, 0, false, h)) {
1903 hdlBuf->invalidate();
1904 return 0;
1905 }
1906 *hdlBuf = probe;
1907 return h.ref;
1908}
1909
1910void seekTableObjectHandleStream(DRW::Version version, dwgBuffer *buf, std::uint32_t objSize) {
1911 if (version > DRW::AC1018) {
1912 buf->setPosition(objSize >> 3);
1913 buf->setBitPos(objSize & 7);
1914 }
1915}
1916
1917bool readTableObjectCommonHandles(dwgBuffer *buf, std::uint32_t baseHandle,
1918 std::int32_t numReactors, std::uint8_t xDictFlag,
1919 std::uint32_t *parentHandle,
1920 std::vector<std::uint32_t> *reactorHandles,
1921 std::uint32_t *xDictHandle,
1922 std::uint64_t handleEndBit =
1923 std::numeric_limits<std::uint64_t>::max()) {
1924 if (buf == nullptr || !buf->isGood()
1925 || !dwgSafety::validReactorCount(numReactors)) {
1926 if (buf != nullptr)
1927 buf->invalidate();
1928 return false;
1929 }
1930 std::vector<std::uint32_t> parsedReactors;
1931 dwgHandle parentH;
1932 if (!readBoundedDwgHandle(*buf, handleEndBit, baseHandle, true, parentH))
1933 return false;
1934 if (!DRW::reserve(parsedReactors, numReactors))
1935 return false;
1936 for (std::int32_t i = 0; i < numReactors; ++i) {
1937 dwgHandle reactor;
1938 if (!readBoundedDwgHandle(*buf, handleEndBit, baseHandle, true,
1939 reactor))
1940 return false;
1941 parsedReactors.push_back(reactor.ref);
1942 }
1943
1944 std::uint32_t parsedXDictHandle = 0;
1945 if (xDictFlag != 1) {
1946 dwgHandle xDict;
1947 if (!readBoundedDwgHandle(*buf, handleEndBit, baseHandle, true,
1948 xDict))
1949 return false;
1950 parsedXDictHandle = xDict.ref;
1951 }
1952 if (parentHandle)
1953 *parentHandle = parentH.ref;
1954 if (reactorHandles)
1955 *reactorHandles = std::move(parsedReactors);
1956 if (xDictHandle)
1957 *xDictHandle = parsedXDictHandle;
1958 return true;
1959}
1960
1961bool readTableValueBytes(dwgBuffer *buf, std::uint64_t bodyEndBit,
1962 std::vector<std::uint8_t>& raw, const char *label) {
1963 std::int32_t parsedByteCount = 0;
1964 if (buf == nullptr
1965 || !readBoundedBitLong(*buf, bodyEndBit, parsedByteCount)
1966 || parsedByteCount < 0
1967 || static_cast<std::uint32_t>(parsedByteCount) > kMaxTableStringBytes) {
1968 if (buf != nullptr)
1969 buf->invalidate();
1970 return false;
1971 }
1972 const std::uint32_t byteCount = static_cast<std::uint32_t>(parsedByteCount);
1973 if (byteCount > kMaxTableStringBytes) {
1974 DRW_DBG(label)DRW_dbg::dbg(label); DRW_DBG(" too large: ")DRW_dbg::dbg(" too large: "); DRW_DBG(byteCount)DRW_dbg::dbg(byteCount); DRW_DBG("\n")DRW_dbg::dbg("\n");
1975 return false;
1976 }
1977 if (!DRW::resize(raw, static_cast<int>(byteCount)))
1978 return false;
1979 const bool good = readBoundedBytes(*buf, bodyEndBit, raw.data(), raw.size());
1980 if (!good) {
1981 DRW_DBG(label)DRW_dbg::dbg(label); DRW_DBG(" byte payload read failed, size: ")DRW_dbg::dbg(" byte payload read failed, size: "); DRW_DBG(byteCount)DRW_dbg::dbg(byteCount);
1982 DRW_DBG(" remaining: ")DRW_dbg::dbg(" remaining: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
1983 }
1984 return good;
1985}
1986
1987UTF8STRINGstd::string decodeTableValueText(DRW::Version version, dwgBuffer *buf, const std::vector<std::uint8_t>& raw) {
1988 if (raw.empty())
1989 return UTF8STRINGstd::string();
1990 std::string s(reinterpret_cast<const char*>(raw.data()), raw.size());
1991 if (version > DRW::AC1018 && s.size() >= 2 && s[s.size() - 1] == '\0'
1992 && s[s.size() - 2] == '\0') {
1993 s.resize(s.size() - 2);
1994 } else {
1995 while (!s.empty() && s.back() == '\0')
1996 s.pop_back();
1997 }
1998 if (buf->decoder)
1999 s = buf->decoder->toUtf8(s);
2000 return s;
2001}
2002
2003UTF8STRINGstd::string readTableText(DRW::Version version, dwgBuffer *buf,
2004 std::uint64_t endBit) {
2005 if (!buf)
2006 return UTF8STRINGstd::string();
2007 if (version <= DRW::AC1018) {
2008 UTF8STRINGstd::string value;
2009 if (!readBoundedVariableText(*buf, endBit, version, value))
2010 buf->invalidate();
2011 return value;
2012 }
2013
2014 std::uint16_t parsedByteLen = 0;
2015 if (!readBoundedBitShort(*buf, endBit, parsedByteLen)) {
2016 buf->invalidate();
2017 return UTF8STRINGstd::string();
2018 }
2019 const std::uint32_t byteLen = parsedByteLen;
2020 if (byteLen == 0)
2021 return UTF8STRINGstd::string();
2022 if (byteLen > kMaxTableStringBytes) {
2023 DRW_DBG("TABLE text byte length invalid: ")DRW_dbg::dbg("TABLE text byte length invalid: "); DRW_DBG(byteLen)DRW_dbg::dbg(byteLen); DRW_DBG("\n")DRW_dbg::dbg("\n");
2024 return UTF8STRINGstd::string();
2025 }
2026
2027 std::vector<std::uint8_t> raw;
2028 if (!DRW::resize(raw, static_cast<int>(byteLen + 2)))
2029 return UTF8STRINGstd::string();
2030 std::fill(raw.begin(), raw.end(), 0);
2031 if (!readBoundedBytes(*buf, endBit, raw.data(), byteLen)) {
2032 buf->invalidate();
2033 return UTF8STRINGstd::string();
2034 }
2035
2036 std::string s(reinterpret_cast<const char*>(raw.data()), byteLen);
2037 if (buf->decoder)
2038 s = buf->decoder->toUtf8(s);
2039 return s;
2040}
2041
2042bool readTableValuePoint(dwgBuffer *buf, std::uint64_t bodyEndBit,
2043 DRW_CadValue& value, int dimensions) {
2044 std::int32_t parsedDataSize = 0;
2045 if (buf == nullptr
2046 || !readBoundedBitLong(*buf, bodyEndBit, parsedDataSize)
2047 || parsedDataSize < 0) {
2048 if (buf != nullptr)
2049 buf->invalidate();
2050 return false;
2051 }
2052 value.m_dataSize = static_cast<std::uint32_t>(parsedDataSize);
2053 const std::uint32_t expectedSize = static_cast<std::uint32_t>(dimensions) * 8;
2054 if (value.m_dataSize > kMaxTableStringBytes)
2055 return false;
2056 if (value.m_dataSize < expectedSize) {
2057 if (!DRW::resize(value.m_rawData,
2058 static_cast<int>(value.m_dataSize)))
2059 return false;
2060 if (!readBoundedBytes(*buf, bodyEndBit, value.m_rawData.data(),
2061 value.m_rawData.size()))
2062 return false;
2063 value.m_value.addBinary(310, value.m_rawData);
2064 return true;
2065 }
2066
2067 DRW_Coord c;
2068 if (!readBoundedRawDouble(*buf, bodyEndBit, c.x)
2069 || !readBoundedRawDouble(*buf, bodyEndBit, c.y)
2070 || (dimensions == 3
2071 && !readBoundedRawDouble(*buf, bodyEndBit, c.z)))
2072 return false;
2073 if (dimensions != 3)
2074 c.z = 0.0;
2075 value.m_value.addCoord(11, c);
2076
2077 const std::uint32_t extraBytes = value.m_dataSize - expectedSize;
2078 if (!DRW::resize(value.m_rawData, static_cast<int>(extraBytes)))
2079 return false;
2080 return readBoundedBytes(*buf, bodyEndBit, value.m_rawData.data(),
2081 value.m_rawData.size());
2082}
2083
2084bool readTableCadValue(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
2085 dwgBuffer *hdlBuf, DRW_CadValue& value,
2086 const TableDwgBounds& bounds) {
2087 if (version > DRW::AC1018) {
2088 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, value.m_formatFlags))
2089 return false;
2090 }
2091
2092 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, value.m_dataType))
2093 return false;
2094 const bool emptyR2007Value = version > DRW::AC1018 && (value.m_formatFlags & 3);
2095 if (!emptyR2007Value) {
2096 switch (value.m_dataType) {
2097 case 0:
2098 case 1: {
2099 std::int32_t intValue = 0;
2100 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, intValue))
2101 return false;
2102 value.m_value.addInt(91, intValue);
2103 break;
2104 }
2105 case 2: {
2106 double doubleValue = 0.0;
2107 if (!readBoundedBitDouble(*buf, bounds.bodyEndBit, doubleValue))
2108 return false;
2109 value.m_value.addDouble(140, doubleValue);
2110 break;
2111 }
2112 case 4:
2113 case 512:
2114 if (!readTableValueBytes(buf, bounds.bodyEndBit, value.m_rawData,
2115 "TABLE value byte payload"))
2116 return false;
2117 value.m_dataSize = static_cast<std::uint32_t>(value.m_rawData.size());
2118 value.m_value.addString(1, decodeTableValueText(version, buf, value.m_rawData));
2119 break;
2120 case 8: {
2121 if (!readTableValueBytes(buf, bounds.bodyEndBit, value.m_rawData,
2122 "TABLE value date payload"))
2123 return false;
2124 value.m_dataSize = static_cast<std::uint32_t>(value.m_rawData.size());
2125 value.m_value.addBinary(310, value.m_rawData);
2126 break;
2127 }
2128 case 16:
2129 if (!readTableValuePoint(buf, bounds.bodyEndBit, value, 2))
2130 return false;
2131 break;
2132 case 32:
2133 if (!readTableValuePoint(buf, bounds.bodyEndBit, value, 3))
2134 return false;
2135 break;
2136 case 64:
2137 value.m_handle = readTableHandle(hdlBuf, bounds.handleEndBit);
2138 value.m_value.addInt(330, static_cast<std::uint32_t>(value.m_handle));
2139 break;
2140 case 128:
2141 case 256:
2142 DRW_DBG("unsupported TABLE CadValue buffer data type: ")DRW_dbg::dbg("unsupported TABLE CadValue buffer data type: "); DRW_DBG(value.m_dataType)DRW_dbg::dbg(value.m_dataType); DRW_DBG("\n")DRW_dbg::dbg("\n");
2143 return false;
2144 default:
2145 DRW_DBG("unsupported TABLE CadValue data type: ")DRW_dbg::dbg("unsupported TABLE CadValue data type: "); DRW_DBG(value.m_dataType)DRW_dbg::dbg(value.m_dataType); DRW_DBG("\n")DRW_dbg::dbg("\n");
2146 return false;
2147 }
2148 }
2149
2150 if (version > DRW::AC1018) {
2151 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2152 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, value.m_unitType))
2153 return false;
2154 const std::uint64_t textEndBit = tableTextEndBit(buf, textBuf, bounds);
2155 value.m_formatString = readTableText(version, textBuf, textEndBit);
2156 value.m_valueString = readTableText(version, textBuf, textEndBit);
2157 }
2158
2159 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2160 if (!good) {
2161 DRW_DBG("TABLE CadValue stream failed, flags: ")DRW_dbg::dbg("TABLE CadValue stream failed, flags: "); DRW_DBG(value.m_formatFlags)DRW_dbg::dbg(value.m_formatFlags);
2162 DRW_DBG(" type: ")DRW_dbg::dbg(" type: "); DRW_DBG(value.m_dataType)DRW_dbg::dbg(value.m_dataType);
2163 DRW_DBG(" unit: ")DRW_dbg::dbg(" unit: "); DRW_DBG(value.m_unitType)DRW_dbg::dbg(value.m_unitType);
2164 DRW_DBG(" bufGood: ")DRW_dbg::dbg(" bufGood: "); DRW_DBG(buf->isGood() ? 1 : 0)DRW_dbg::dbg(buf->isGood() ? 1 : 0);
2165 DRW_DBG(" strGood: ")DRW_dbg::dbg(" strGood: "); DRW_DBG((!strBuf || strBuf->isGood()) ? 1 : 0)DRW_dbg::dbg((!strBuf || strBuf->isGood()) ? 1 : 0);
2166 DRW_DBG(" hdlGood: ")DRW_dbg::dbg(" hdlGood: "); DRW_DBG((!hdlBuf || hdlBuf->isGood()) ? 1 : 0)DRW_dbg::dbg((!hdlBuf || hdlBuf->isGood()) ? 1 : 0);
2167 DRW_DBG(" bufPos: ")DRW_dbg::dbg(" bufPos: "); DRW_DBG(buf->getPosition())DRW_dbg::dbg(buf->getPosition());
2168 DRW_DBG(" strPos: ")DRW_dbg::dbg(" strPos: "); if (strBuf) DRW_DBG(strBuf->getPosition())DRW_dbg::dbg(strBuf->getPosition()); else DRW_DBG(-1)DRW_dbg::dbg(-1);
2169 DRW_DBG(" hdlPos: ")DRW_dbg::dbg(" hdlPos: "); if (hdlBuf) DRW_DBG(hdlBuf->getPosition())DRW_dbg::dbg(hdlBuf->getPosition()); else DRW_DBG(-1)DRW_dbg::dbg(-1);
2170 DRW_DBG("\n")DRW_dbg::dbg("\n");
2171 }
2172 return good;
2173}
2174
2175bool skipTableCustomData(DRW::Version version, dwgBuffer *buf,
2176 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2177 const TableDwgBounds& bounds) {
2178 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2179 UTF8STRINGstd::string key = readTableText(
2180 version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2181 if (!textBuf->isGood()) {
2182 DRW_DBG("TABLE custom data key string read failed\n")DRW_dbg::dbg("TABLE custom data key string read failed\n");
2183 return false;
2184 }
2185 DRW_CadValue value;
2186 const bool good = readTableCadValue(version, buf, strBuf, hdlBuf, value,
2187 bounds);
2188 if (!good) {
2189 DRW_DBG("TABLE custom data key failed: ")DRW_dbg::dbg("TABLE custom data key failed: "); DRW_DBG(key.c_str())DRW_dbg::dbg(key.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
2190 }
2191 return good;
2192}
2193
2194bool readTableCmColor(DRW::Version version, dwgBuffer *buf,
2195 dwgBuffer *strBuf, const TableDwgBounds& bounds) {
2196 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2197 if (version < DRW::AC1018) {
2198 std::uint16_t ignoredColor = 0;
2199 return readBoundedBitShort(*buf, bounds.bodyEndBit, ignoredColor);
2200 }
2201
2202 std::uint16_t ignoredColorType = 0;
2203 std::int32_t rgb = 0;
2204 std::uint8_t colorFlags = 0;
2205 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, ignoredColorType)
2206 || !readBoundedBitLong(*buf, bounds.bodyEndBit, rgb)
2207 || !readBoundedRawChar8(*buf, bounds.bodyEndBit, colorFlags))
2208 return false;
2209 DRW_DBG("\ntype COLOR: ")DRW_dbg::dbg("\ntype COLOR: "); DRW_DBGH(rgb >> 24)DRW_dbg::dbgH(rgb >> 24);
2210 DRW_DBG("\nRGB COLOR: ")DRW_dbg::dbg("\nRGB COLOR: "); DRW_DBGH(rgb)DRW_dbg::dbgH(rgb);
2211 DRW_DBG("\nbyte COLOR: ")DRW_dbg::dbg("\nbyte COLOR: "); DRW_DBGH(colorFlags)DRW_dbg::dbgH(colorFlags);
2212 const std::uint64_t textEndBit = tableTextEndBit(buf, textBuf, bounds);
2213 if (colorFlags & 1)
2214 readTableText(version, textBuf, textEndBit);
2215 if (colorFlags & 2)
2216 readTableText(version, textBuf, textEndBit);
2217 return textBuf->isGood();
2218}
2219
2220bool skipR2007TableCellOverrides(DRW::Version version, dwgBuffer *buf,
2221 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2222 DRW_TableCell& cell,
2223 std::vector<DRW_DwgSubrecordRange> *ranges,
2224 const TableDwgBounds& bounds) {
2225 const std::uint64_t startBit = currentDwgBit(buf);
2226 std::int32_t overrideFlags = 0;
2227 std::uint8_t virtualEdgeFlags = 0;
2228 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, overrideFlags)
2229 || !readBoundedRawChar8(*buf, bounds.bodyEndBit, virtualEdgeFlags))
2230 return false;
2231 cell.m_overrideFlags = static_cast<std::uint32_t>(overrideFlags);
2232 cell.m_virtualEdgeFlags = virtualEdgeFlags;
2233
2234 if (cell.m_overrideFlags & 0x00001
2235 && !skipTableRawShort16(*buf, bounds.bodyEndBit))
2236 return false;
2237 if (cell.m_overrideFlags & 0x00002
2238 && !skipTableBit(*buf, bounds.bodyEndBit))
2239 return false;
2240 if ((cell.m_overrideFlags & 0x00004)
2241 && !readTableCmColor(version, buf, strBuf, bounds))
2242 return false;
2243 if ((cell.m_overrideFlags & 0x00008)
2244 && !readTableCmColor(version, buf, strBuf, bounds))
2245 return false;
2246 if (cell.m_overrideFlags & 0x00010)
2247 cell.m_textStyleOverrideHandle = readTableHandle(hdlBuf, bounds.handleEndBit);
2248 if (cell.m_overrideFlags & 0x00020
2249 && !skipTableBitDouble(*buf, bounds.bodyEndBit))
2250 return false;
2251 if ((cell.m_overrideFlags & 0x00040)
2252 && !readTableCmColor(version, buf, strBuf, bounds))
2253 return false;
2254 if (cell.m_overrideFlags & 0x00400
2255 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2256 return false;
2257 if (cell.m_overrideFlags & 0x04000
2258 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2259 return false;
2260 if ((cell.m_overrideFlags & 0x00080)
2261 && !readTableCmColor(version, buf, strBuf, bounds))
2262 return false;
2263 if (cell.m_overrideFlags & 0x00800
2264 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2265 return false;
2266 if (cell.m_overrideFlags & 0x08000
2267 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2268 return false;
2269 if ((cell.m_overrideFlags & 0x00100)
2270 && !readTableCmColor(version, buf, strBuf, bounds))
2271 return false;
2272 if (cell.m_overrideFlags & 0x01000
2273 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2274 return false;
2275 if (cell.m_overrideFlags & 0x10000
2276 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2277 return false;
2278 if ((cell.m_overrideFlags & 0x00200)
2279 && !readTableCmColor(version, buf, strBuf, bounds))
2280 return false;
2281 if (cell.m_overrideFlags & 0x02000
2282 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2283 return false;
2284 if (cell.m_overrideFlags & 0x20000
2285 && !skipTableBitShort(*buf, bounds.bodyEndBit))
2286 return false;
2287
2288 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2289 if (ranges != nullptr) {
2290 ranges->push_back(makeDwgSubrecordRange(
2291 "r2007-table-cell-overrides", startBit, currentDwgBit(buf),
2292 version, cell.m_overrideFlags, good));
2293 }
2294 return good;
2295}
2296
2297bool parseR2007TableCell(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
2298 dwgBuffer *hdlBuf, DRW_TableCell& cell,
2299 std::vector<DRW_DwgSubrecordRange> *ranges,
2300 const TableDwgBounds& bounds) {
2301 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2302 std::uint16_t type = 0;
2303 std::uint8_t edgeFlags = 0;
2304 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, type)
2305 || !readBoundedRawChar8(*buf, bounds.bodyEndBit, edgeFlags)
2306 || !readBoundedBit(*buf, bounds.bodyEndBit, cell.m_isMerged)
2307 || !readBoundedBit(*buf, bounds.bodyEndBit, cell.m_autoFit)
2308 || !readBoundedBitLong(*buf, bounds.bodyEndBit, cell.m_mergedWidth)
2309 || !readBoundedBitLong(*buf, bounds.bodyEndBit, cell.m_mergedHeight)
2310 || !readBoundedBitDouble(*buf, bounds.bodyEndBit, cell.m_rotation))
2311 return false;
2312 cell.m_type = type;
2313 cell.m_edgeFlags = edgeFlags;
2314 cell.m_valueHandle = readTableHandle(hdlBuf, bounds.handleEndBit);
2315
2316 if (cell.m_type == 1) {
2317 cell.m_textStyleHandle = cell.m_valueHandle;
2318 if (cell.m_textStyleHandle == 0 && version < DRW::AC1021) {
2319 DRW_TableCellContent content;
2320 content.m_type = 1;
2321 content.m_text = readTableText(
2322 version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2323 content.m_value.m_dataType = 4;
2324 content.m_value.m_value.addString(1, content.m_text);
2325 cell.m_contents.push_back(content);
2326 }
2327 } else if (cell.m_type == 2) {
2328 cell.m_blockHandle = cell.m_valueHandle;
2329 if (!readBoundedBitDouble(*buf, bounds.bodyEndBit, cell.m_blockScale))
2330 return false;
2331 bool hasAttributes = false;
2332 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasAttributes))
2333 return false;
2334 if (hasAttributes) {
2335 std::uint16_t numAttributes = 0;
2336 if (!readBoundedBitShort(*buf, bounds.bodyEndBit,
2337 numAttributes)
2338 || numAttributes > kMaxTableCellAttributes)
2339 return false;
2340 if (!DRW::reserve(cell.m_attributes, numAttributes))
2341 return false;
2342 for (std::uint16_t i = 0; i < numAttributes; ++i) {
2343 DRW_TableCellAttribute attribute;
2344 attribute.m_attdefHandle = readTableHandle(
2345 hdlBuf, bounds.handleEndBit);
2346 std::uint16_t attributeIndex = 0;
2347 if (!readBoundedBitShort(*buf, bounds.bodyEndBit,
2348 attributeIndex))
2349 return false;
2350 attribute.m_index = attributeIndex;
2351 attribute.m_text = readTableText(
2352 version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2353 cell.m_attributes.push_back(attribute);
2354 }
2355 }
2356
2357 DRW_TableCellContent content;
2358 content.m_type = 4;
2359 content.m_handle = cell.m_blockHandle;
2360 cell.m_contents.push_back(content);
2361 }
2362
2363 bool hasOverrides = false;
2364 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasOverrides))
2365 return false;
2366 if (hasOverrides
2367 && !skipR2007TableCellOverrides(version, buf, strBuf, hdlBuf, cell,
2368 ranges, bounds))
2369 return false;
2370
2371 if (version > DRW::AC1018) {
2372 if (!skipTableBitLong(*buf, bounds.bodyEndBit))
2373 return false;
2374 DRW_TableCellContent content;
2375 content.m_type = 1;
2376 if (!readTableCadValue(version, buf, strBuf, hdlBuf, content.m_value,
2377 bounds))
2378 return false;
2379 if (content.m_value.m_value.type() == DRW_Variant::STRING)
2380 content.m_text = content.m_value.m_value.c_str();
2381 else if (!content.m_value.m_valueString.empty())
2382 content.m_text = content.m_value.m_valueString;
2383 cell.m_contents.push_back(content);
2384 }
2385
2386 return buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2387}
2388
2389bool skipR2007TableOverrides(DRW::Version version, dwgBuffer *buf,
2390 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2391 std::vector<DRW_DwgSubrecordRange> *ranges,
2392 const TableDwgBounds& bounds) {
2393 const std::uint64_t startBit = currentDwgBit(buf);
2394 std::uint32_t maskCount = 0;
2395 auto readFlags = [&](std::uint32_t& flags) {
2396 std::int32_t parsed = 0;
2397 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, parsed))
2398 return false;
2399 flags = static_cast<std::uint32_t>(parsed);
2400 return true;
2401 };
2402 bool hasMask = false;
2403 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasMask))
2404 return false;
2405 if (hasMask) {
2406 std::uint32_t flags = 0;
2407 if (!readFlags(flags))
2408 return false;
2409 ++maskCount;
2410 if (flags & 0x000001)
2411 if (!skipTableBit(*buf, bounds.bodyEndBit))
2412 return false;
2413 if (flags & 0x000004)
2414 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2415 return false;
2416 if (flags & 0x000008)
2417 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2418 return false;
2419 if (flags & 0x000010)
2420 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2421 return false;
2422 if ((flags & 0x000020)
2423 && !readTableCmColor(version, buf, strBuf, bounds))
2424 return false;
2425 if ((flags & 0x000040)
2426 && !readTableCmColor(version, buf, strBuf, bounds))
2427 return false;
2428 if ((flags & 0x000080)
2429 && !readTableCmColor(version, buf, strBuf, bounds))
2430 return false;
2431 if (flags & 0x000100)
2432 if (!skipTableBit(*buf, bounds.bodyEndBit))
2433 return false;
2434 if (flags & 0x000200)
2435 if (!skipTableBit(*buf, bounds.bodyEndBit))
2436 return false;
2437 if (flags & 0x000400)
2438 if (!skipTableBit(*buf, bounds.bodyEndBit))
2439 return false;
2440 if ((flags & 0x000800)
2441 && !readTableCmColor(version, buf, strBuf, bounds))
2442 return false;
2443 if ((flags & 0x001000)
2444 && !readTableCmColor(version, buf, strBuf, bounds))
2445 return false;
2446 if ((flags & 0x002000)
2447 && !readTableCmColor(version, buf, strBuf, bounds))
2448 return false;
2449 if (flags & 0x004000)
2450 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2451 return false;
2452 if (flags & 0x008000)
2453 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2454 return false;
2455 if (flags & 0x010000)
2456 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2457 return false;
2458 if (flags & 0x020000)
2459 if (readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2460 && hdlBuf != nullptr && !hdlBuf->isGood())
2461 return false;
2462 if (flags & 0x040000)
2463 if (readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2464 && hdlBuf != nullptr && !hdlBuf->isGood())
2465 return false;
2466 if (flags & 0x080000)
2467 if (readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2468 && hdlBuf != nullptr && !hdlBuf->isGood())
2469 return false;
2470 if (flags & 0x100000)
2471 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2472 return false;
2473 if (flags & 0x200000)
2474 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2475 return false;
2476 if (flags & 0x400000)
2477 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2478 return false;
2479 }
2480
2481 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasMask))
2482 return false;
2483 if (hasMask) {
2484 std::uint32_t flags = 0;
2485 if (!readFlags(flags))
2486 return false;
2487 ++maskCount;
2488 for (int i = 0; i < 18; ++i) {
2489 if (flags & (1u << i)
2490 && !readTableCmColor(version, buf, strBuf, bounds))
2491 return false;
2492 }
2493 }
2494
2495 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasMask))
2496 return false;
2497 if (hasMask) {
2498 std::uint32_t flags = 0;
2499 if (!readFlags(flags))
2500 return false;
2501 ++maskCount;
2502 for (int i = 0; i < 18; ++i) {
2503 if (flags & (1u << i))
2504 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2505 return false;
2506 }
2507 }
2508
2509 if (!readBoundedBit(*buf, bounds.bodyEndBit, hasMask))
2510 return false;
2511 if (hasMask) {
2512 std::uint32_t flags = 0;
2513 if (!readFlags(flags))
2514 return false;
2515 ++maskCount;
2516 for (int i = 0; i < 18; ++i) {
2517 if (flags & (1u << i))
2518 if (!skipTableBitShort(*buf, bounds.bodyEndBit))
2519 return false;
2520 }
2521 }
2522
2523 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2524 if (ranges != nullptr && (maskCount != 0 || currentDwgBit(buf) != startBit)) {
2525 ranges->push_back(makeDwgSubrecordRange(
2526 "r2007-table-overrides", startBit, currentDwgBit(buf),
2527 version, maskCount, good));
2528 }
2529 return good;
2530}
2531
2532bool skipTableContentFormat(DRW::Version version, dwgBuffer *buf,
2533 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2534 std::vector<DRW_DwgSubrecordRange> *ranges,
2535 const TableDwgBounds& bounds) {
2536 const std::uint64_t startBit = currentDwgBit(buf);
2537 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2538 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // property override flags
2539 || !skipTableBitLong(*buf, bounds.bodyEndBit) // property flags
2540 || !skipTableBitLong(*buf, bounds.bodyEndBit) // value data type
2541 || !skipTableBitLong(*buf, bounds.bodyEndBit)) { // value unit type
2542 return false;
2543 }
2544 readTableText(version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2545 if (!textBuf->isGood())
2546 return false;
2547 if (!skipTableBitDouble(*buf, bounds.bodyEndBit) // rotation
2548 || !skipTableBitDouble(*buf, bounds.bodyEndBit) // block scale
2549 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // alignment
2550 return false;
2551 std::int32_t rgb = -1;
2552 UTF8STRINGstd::string name;
2553 UTF8STRINGstd::string book;
2554 std::uint32_t color = 0;
2555 if (!readBoundedCmColor(*buf, strBuf, bounds.bodyEndBit, version,
2556 color, &rgb, nullptr, &name, &book,
2557 bounds.stringEndBit)
2558 || (hdlBuf != nullptr
2559 && readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2560 && !hdlBuf->isGood())
2561 || !skipTableBitDouble(*buf, bounds.bodyEndBit)) // text height
2562 return false;
2563 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2564 if (ranges != nullptr) {
2565 ranges->push_back(makeDwgSubrecordRange(
2566 "table-content-format", startBit, currentDwgBit(buf),
2567 version, 1, good));
2568 }
2569 return good;
2570}
2571
2572bool skipTableCellStyle(DRW::Version version, dwgBuffer *buf,
2573 dwgBuffer *strBuf, dwgBuffer *hdlBuf,
2574 std::vector<DRW_DwgSubrecordRange> *ranges,
2575 const TableDwgBounds& bounds) {
2576 const std::uint64_t startBit = currentDwgBit(buf);
2577 if (!skipTableBitLong(*buf, bounds.bodyEndBit)) // style type
2578 return false;
2579 std::uint16_t dataFlags = 0;
2580 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, dataFlags))
2581 return false;
2582 const bool hasData = dataFlags != 0;
2583 if (!hasData) {
2584 if (ranges != nullptr) {
2585 ranges->push_back(makeDwgSubrecordRange(
2586 "table-cell-style", startBit, currentDwgBit(buf),
2587 version, 0, buf->isGood()));
2588 }
2589 return buf->isGood();
2590 }
2591
2592 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // property override flags
2593 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // merge flags
2594 return false;
2595 std::int32_t rgb = -1;
2596 UTF8STRINGstd::string name;
2597 UTF8STRINGstd::string book;
2598 dwgBuffer *textBuf = strBuf ? strBuf : buf;
Value stored to 'textBuf' during its initialization is never read
2599 std::uint32_t color = 0;
2600 if (!readBoundedCmColor(*buf, strBuf, bounds.bodyEndBit, version,
2601 color, &rgb, nullptr, &name, &book,
2602 bounds.stringEndBit)
2603 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // content layout
2604 return false;
2605 if (!skipTableContentFormat(version, buf, strBuf, hdlBuf, ranges,
2606 bounds))
2607 return false;
2608
2609 std::uint16_t marginFlags = 0;
2610 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, marginFlags))
2611 return false;
2612 if (marginFlags != 0) {
2613 for (int i = 0; i < 6; ++i)
2614 if (!skipTableBitDouble(*buf, bounds.bodyEndBit))
2615 return false;
2616 }
2617
2618 std::uint32_t borders = 0;
2619 if (!readTableBodyCount(version, buf, bounds.bodyEndBit, 6, 2,
2620 borders)) {
2621 DRW_DBG("TABLE cell style border count out of range: ")DRW_dbg::dbg("TABLE cell style border count out of range: "); DRW_DBG(borders)DRW_dbg::dbg(borders); DRW_DBG("\n")DRW_dbg::dbg("\n");
2622 return false;
2623 }
2624 for (std::uint32_t i = 0; i < borders; ++i) {
2625 std::int32_t edgeFlags = 0;
2626 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, edgeFlags))
2627 return false;
2628 if (edgeFlags == 0)
2629 continue;
2630 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // border overrides
2631 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // border type
2632 return false;
2633 if (!readBoundedCmColor(*buf, strBuf, bounds.bodyEndBit, version,
2634 color, &rgb, nullptr, &name, &book,
2635 bounds.stringEndBit)
2636 || (hdlBuf != nullptr
2637 && readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2638 && !hdlBuf->isGood())
2639 || !skipTableBitLong(*buf, bounds.bodyEndBit) // visible/invisible
2640 || !skipTableBitDouble(*buf, bounds.bodyEndBit)) // double line spacing
2641 return false;
2642 }
2643
2644 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2645 if (ranges != nullptr) {
2646 ranges->push_back(makeDwgSubrecordRange(
2647 "table-cell-style", startBit, currentDwgBit(buf),
2648 version, borders, good));
2649 }
2650 return good;
2651}
2652
2653bool parseTableCell(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
2654 dwgBuffer *hdlBuf, DRW_TableCell& cell,
2655 std::vector<DRW_DwgSubrecordRange> *ranges,
2656 const TableDwgBounds& bounds) {
2657 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2658 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, cell.m_flags))
2659 return false;
2660 cell.m_toolTip = readTableText(
2661 version, textBuf, tableTextEndBit(buf, textBuf, bounds));
2662 if (!textBuf->isGood()) {
2663 DRW_DBG("TABLE cell tooltip string read failed\n")DRW_dbg::dbg("TABLE cell tooltip string read failed\n");
2664 return false;
2665 }
2666 if (!skipTableBitLong(*buf, bounds.bodyEndBit)) // custom data
2667 return false;
2668
2669 std::uint32_t customItems = 0;
2670 if (!readTableBodyCount(version, buf, bounds.bodyEndBit, kMaxTableItems, 2,
2671 customItems)) {
2672 DRW_DBG("TABLE cell custom item count out of range: ")DRW_dbg::dbg("TABLE cell custom item count out of range: "); DRW_DBG(customItems)DRW_dbg::dbg(customItems); DRW_DBG("\n")DRW_dbg::dbg("\n");
2673 return false;
2674 }
2675 for (std::uint32_t i = 0; i < customItems; ++i) {
2676 if (!skipTableCustomData(version, buf, strBuf, hdlBuf, bounds)) {
2677 DRW_DBG("TABLE cell custom data parse incomplete\n")DRW_dbg::dbg("TABLE cell custom data parse incomplete\n");
2678 return false;
2679 }
2680 }
2681
2682 std::int32_t hasLinkedData = 0;
2683 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, hasLinkedData))
2684 return false;
2685 if (hasLinkedData != 0) {
2686 if (hdlBuf != nullptr
2687 && readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2688 && !hdlBuf->isGood())
2689 return false;
2690 if (!skipTableBitLong(*buf, bounds.bodyEndBit)
2691 || !skipTableBitLong(*buf, bounds.bodyEndBit)
2692 || !skipTableBitLong(*buf, bounds.bodyEndBit))
2693 return false;
2694 }
2695
2696 std::uint32_t contentCount = 0;
2697 if (!readTableBodyCount(version, buf, bounds.bodyEndBit, kMaxTableItems, 2,
2698 contentCount)) {
2699 DRW_DBG("TABLE cell content count out of range: ")DRW_dbg::dbg("TABLE cell content count out of range: "); DRW_DBG(contentCount)DRW_dbg::dbg(contentCount); DRW_DBG("\n")DRW_dbg::dbg("\n");
2700 return false;
2701 }
2702 if (!DRW::reserve(cell.m_contents, static_cast<int>(contentCount)))
2703 return false;
2704 for (std::uint32_t i = 0; i < contentCount; ++i) {
2705 DRW_TableCellContent content;
2706 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, content.m_type))
2707 return false;
2708 if (content.m_type == 1) {
2709 if (!readTableCadValue(version, buf, strBuf, hdlBuf,
2710 content.m_value, bounds)) {
2711 DRW_DBG("TABLE cell value parse incomplete\n")DRW_dbg::dbg("TABLE cell value parse incomplete\n");
2712 return false;
2713 }
2714 if (content.m_value.m_value.type() == DRW_Variant::STRING)
2715 content.m_text = content.m_value.m_value.c_str();
2716 else if (!content.m_value.m_valueString.empty())
2717 content.m_text = content.m_value.m_valueString;
2718 } else if (content.m_type == 2 || content.m_type == 4) {
2719 content.m_handle = readTableHandle(hdlBuf, bounds.handleEndBit);
2720 }
2721
2722 std::uint32_t numAttrs = 0;
2723 if (!readTableBodyCount(version, buf, bounds.bodyEndBit, kMaxTableItems, 2,
2724 numAttrs)) {
2725 DRW_DBG("TABLE cell attribute count out of range: ")DRW_dbg::dbg("TABLE cell attribute count out of range: "); DRW_DBG(numAttrs)DRW_dbg::dbg(numAttrs); DRW_DBG("\n")DRW_dbg::dbg("\n");
2726 return false;
2727 }
2728 for (std::uint32_t attr = 0; attr < numAttrs; ++attr) {
2729 if (hdlBuf != nullptr
2730 && readTableHandle(hdlBuf, bounds.handleEndBit) == 0
2731 && !hdlBuf->isGood())
2732 return false;
2733 readTableText(version, textBuf,
2734 tableTextEndBit(buf, textBuf, bounds));
2735 if (!textBuf->isGood()
2736 || !skipTableBitLong(*buf, bounds.bodyEndBit))
2737 return false;
2738 }
2739
2740 std::uint16_t contentFormatFlag = 0;
2741 if (!readBoundedBitShort(*buf, bounds.bodyEndBit, contentFormatFlag))
2742 return false;
2743 const bool hasContentFormat = contentFormatFlag != 0;
2744 if (hasContentFormat
2745 && !skipTableContentFormat(version, buf, strBuf, hdlBuf, ranges,
2746 bounds)) {
2747 DRW_DBG("TABLE cell content format parse incomplete\n")DRW_dbg::dbg("TABLE cell content format parse incomplete\n");
2748 return false;
2749 }
2750 cell.m_contents.push_back(content);
2751 }
2752
2753 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf, ranges, bounds)) {
2754 DRW_DBG("TABLE cell style override parse incomplete\n")DRW_dbg::dbg("TABLE cell style override parse incomplete\n");
2755 return false;
2756 }
2757
2758 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, cell.m_styleId))
2759 return false;
2760 const std::uint64_t geometryStartBit = currentDwgBit(buf);
2761 std::int32_t hasGeometry = 0;
2762 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, hasGeometry))
2763 return false;
2764 if (hasGeometry != 0) {
2765 std::int32_t geometryFlags = 0;
2766 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // unknown AC1027+ geometry marker
2767 || !readBoundedBitDouble(*buf, bounds.bodyEndBit, cell.m_width)
2768 || !readBoundedBitDouble(*buf, bounds.bodyEndBit, cell.m_height)
2769 || !readBoundedBitLong(*buf, bounds.bodyEndBit, geometryFlags))
2770 return false;
2771 cell.m_geometryFlags = geometryFlags;
2772 cell.m_geometryHandle = readTableHandle(hdlBuf, bounds.handleEndBit);
2773 if (cell.m_geometryFlags != 0) {
2774 std::int32_t geometryRecordFlags = 0;
2775 if (!readBounded3BitDouble(*buf, bounds.bodyEndBit,
2776 cell.m_geometryTopLeft)
2777 || !readBounded3BitDouble(*buf, bounds.bodyEndBit,
2778 cell.m_geometryCenter)
2779 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
2780 cell.m_contentWidth)
2781 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
2782 cell.m_contentHeight)
2783 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
2784 cell.m_geometryWidth)
2785 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
2786 cell.m_geometryHeight)
2787 || !readBoundedBitLong(*buf, bounds.bodyEndBit,
2788 geometryRecordFlags))
2789 return false;
2790 cell.m_geometryRecordFlags = geometryRecordFlags;
2791 }
2792 if (ranges != nullptr) {
2793 const bool geometryGood = buf->isGood() && (!hdlBuf || hdlBuf->isGood());
2794 ranges->push_back(makeDwgSubrecordRange(
2795 "table-cell-geometry-tail", geometryStartBit, currentDwgBit(buf),
2796 version, cell.m_geometryFlags, geometryGood));
2797 }
2798 }
2799
2800 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2801 if (!good)
2802 DRW_DBG("TABLE cell stream ended unexpectedly\n")DRW_dbg::dbg("TABLE cell stream ended unexpectedly\n");
2803 return good;
2804}
2805
2806bool parseTableContent(DRW::Version version, dwgBuffer *buf, dwgBuffer *strBuf,
2807 dwgBuffer *hdlBuf, DRW_TableContent& content,
2808 const TableDwgBounds& bounds) {
2809 dwgBuffer *textBuf = strBuf ? strBuf : buf;
2810 const std::uint64_t textEndBit = tableTextEndBit(buf, textBuf, bounds);
2811 content.m_name = readTableText(version, textBuf, textEndBit);
2812 content.m_description = readTableText(version, textBuf, textEndBit);
2813 if (!textBuf->isGood())
2814 return false;
2815
2816 std::uint32_t columns = 0;
2817 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
2818 kMaxTableColumns, 2,
2819 columns)) {
2820 DRW_DBG("TABLECONTENT column count out of range: ")DRW_dbg::dbg("TABLECONTENT column count out of range: "); DRW_DBG(columns)DRW_dbg::dbg(columns); DRW_DBG("\n")DRW_dbg::dbg("\n");
2821 return false;
2822 }
2823 content.m_columns.clear();
2824 if (!DRW::reserve(content.m_columns, static_cast<int>(columns)))
2825 return false;
2826 for (std::uint32_t col = 0; col < columns; ++col) {
2827 DRW_TableColumn column;
2828 column.m_name = readTableText(version, textBuf, textEndBit);
2829 if (!textBuf->isGood()
2830 || !skipTableBitLong(*buf, bounds.bodyEndBit)) // custom data
2831 return false;
2832 std::uint32_t customItems = 0;
2833 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
2834 kMaxTableItems, 2,
2835 customItems)) {
2836 DRW_DBG("TABLECONTENT column custom item count out of range: ")DRW_dbg::dbg("TABLECONTENT column custom item count out of range: "
)
; DRW_DBG(customItems)DRW_dbg::dbg(customItems); DRW_DBG("\n")DRW_dbg::dbg("\n");
2837 return false;
2838 }
2839 for (std::uint32_t i = 0; i < customItems; ++i) {
2840 if (!skipTableCustomData(version, buf, strBuf, hdlBuf, bounds)) {
2841 DRW_DBG("TABLECONTENT column custom data parse incomplete\n")DRW_dbg::dbg("TABLECONTENT column custom data parse incomplete\n"
)
;
2842 return false;
2843 }
2844 }
2845 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf,
2846 &content.m_subrecordRanges, bounds)) {
2847 DRW_DBG("TABLECONTENT column cell style parse incomplete\n")DRW_dbg::dbg("TABLECONTENT column cell style parse incomplete\n"
)
;
2848 return false;
2849 }
2850 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // style id
2851 || !readBoundedBitDouble(*buf, bounds.bodyEndBit,
2852 column.m_width))
2853 return false;
2854 content.m_columns.push_back(column);
2855 }
2856
2857 std::uint32_t rows = 0;
2858 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
2859 kMaxTableRows, 2,
2860 rows)
2861 || (columns != 0 && rows > kMaxTableCells / columns)) {
2862 DRW_DBG("TABLECONTENT row count out of range: ")DRW_dbg::dbg("TABLECONTENT row count out of range: "); DRW_DBG(rows)DRW_dbg::dbg(rows); DRW_DBG("\n")DRW_dbg::dbg("\n");
2863 return false;
2864 }
2865 content.m_rows.clear();
2866 if (!DRW::reserve(content.m_rows, static_cast<int>(rows)))
2867 return false;
2868 for (std::uint32_t rowIndex = 0; rowIndex < rows; ++rowIndex) {
2869 DRW_TableRow row;
2870 std::uint32_t cells = 0;
2871 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
2872 kMaxTableColumns, 2,
2873 cells)
2874 || cells > kMaxTableItems) {
2875 DRW_DBG("TABLECONTENT row cell count out of range: ")DRW_dbg::dbg("TABLECONTENT row cell count out of range: "); DRW_DBG(cells)DRW_dbg::dbg(cells); DRW_DBG("\n")DRW_dbg::dbg("\n");
2876 return false;
2877 }
2878 if (!DRW::reserve(row.m_cells, static_cast<int>(cells)))
2879 return false;
2880 for (std::uint32_t cellIndex = 0; cellIndex < cells; ++cellIndex) {
2881 DRW_TableCell cell;
2882 if (!parseTableCell(version, buf, strBuf, hdlBuf, cell,
2883 &content.m_subrecordRanges, bounds)) {
2884 DRW_DBG("TABLECONTENT cell parse incomplete at row ")DRW_dbg::dbg("TABLECONTENT cell parse incomplete at row "); DRW_DBG(rowIndex)DRW_dbg::dbg(rowIndex);
2885 DRW_DBG(" cell ")DRW_dbg::dbg(" cell "); DRW_DBG(cellIndex)DRW_dbg::dbg(cellIndex); DRW_DBG("\n")DRW_dbg::dbg("\n");
2886 return false;
2887 }
2888 row.m_cells.push_back(cell);
2889 }
2890
2891 if (!skipTableBitLong(*buf, bounds.bodyEndBit)) // custom data
2892 return false;
2893 std::uint32_t customItems = 0;
2894 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
2895 kMaxTableItems, 2,
2896 customItems)) {
2897 DRW_DBG("TABLECONTENT row custom item count out of range: ")DRW_dbg::dbg("TABLECONTENT row custom item count out of range: "
)
; DRW_DBG(customItems)DRW_dbg::dbg(customItems); DRW_DBG("\n")DRW_dbg::dbg("\n");
2898 return false;
2899 }
2900 for (std::uint32_t i = 0; i < customItems; ++i) {
2901 if (!skipTableCustomData(version, buf, strBuf, hdlBuf, bounds)) {
2902 DRW_DBG("TABLECONTENT row custom data parse incomplete\n")DRW_dbg::dbg("TABLECONTENT row custom data parse incomplete\n"
)
;
2903 return false;
2904 }
2905 }
2906 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf,
2907 &content.m_subrecordRanges, bounds)) {
2908 DRW_DBG("TABLECONTENT row cell style parse incomplete\n")DRW_dbg::dbg("TABLECONTENT row cell style parse incomplete\n"
)
;
2909 return false;
2910 }
2911 if (!skipTableBitLong(*buf, bounds.bodyEndBit) // style id
2912 || !readBoundedBitDouble(*buf, bounds.bodyEndBit, row.m_height))
2913 return false;
2914 content.m_rows.push_back(row);
2915 }
2916
2917 std::uint32_t fieldRefs = 0;
2918 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
2919 kMaxTableItems, 0,
2920 fieldRefs)) {
2921 DRW_DBG("TABLECONTENT field reference count out of range: ")DRW_dbg::dbg("TABLECONTENT field reference count out of range: "
)
; DRW_DBG(fieldRefs)DRW_dbg::dbg(fieldRefs); DRW_DBG("\n")DRW_dbg::dbg("\n");
2922 return false;
2923 }
2924 content.m_fieldHandles.clear();
2925 if (!DRW::reserve(content.m_fieldHandles, static_cast<int>(fieldRefs)))
2926 return false;
2927 for (std::uint32_t i = 0; i < fieldRefs; ++i) {
2928 const std::uint32_t ref = readTableHandle(hdlBuf, bounds.handleEndBit);
2929 if (hdlBuf != nullptr && !hdlBuf->isGood())
2930 return false;
2931 if (ref != 0)
2932 content.m_fieldHandles.push_back(ref);
2933 }
2934
2935 if (!skipTableCellStyle(version, buf, strBuf, hdlBuf,
2936 &content.m_subrecordRanges, bounds)) {
2937 DRW_DBG("TABLECONTENT table cell style parse incomplete\n")DRW_dbg::dbg("TABLECONTENT table cell style parse incomplete\n"
)
;
2938 return false;
2939 }
2940
2941 std::uint32_t mergedRanges = 0;
2942 if (!readTableBodyCount(version, buf, bounds.bodyEndBit,
2943 kMaxTableItems, 8,
2944 mergedRanges)) {
2945 DRW_DBG("TABLECONTENT merged range count out of range: ")DRW_dbg::dbg("TABLECONTENT merged range count out of range: "
)
; DRW_DBG(mergedRanges)DRW_dbg::dbg(mergedRanges); DRW_DBG("\n")DRW_dbg::dbg("\n");
2946 return false;
2947 }
2948 content.m_mergedRanges.clear();
2949 if (!DRW::reserve(content.m_mergedRanges,
2950 static_cast<int>(mergedRanges)))
2951 return false;
2952 for (std::uint32_t i = 0; i < mergedRanges; ++i) {
2953 DRW_TableMergedRange range;
2954 if (!readBoundedBitLong(*buf, bounds.bodyEndBit, range.m_topRow)
2955 || !readBoundedBitLong(*buf, bounds.bodyEndBit,
2956 range.m_leftColumn)
2957 || !readBoundedBitLong(*buf, bounds.bodyEndBit,
2958 range.m_bottomRow)
2959 || !readBoundedBitLong(*buf, bounds.bodyEndBit,
2960 range.m_rightColumn))
2961 return false;
2962 content.m_mergedRanges.push_back(range);
2963 }
2964
2965 content.m_tableStyleHandle = readTableHandle(hdlBuf, bounds.handleEndBit);
2966 const bool good = buf->isGood() && (!strBuf || strBuf->isGood()) && (!hdlBuf || hdlBuf->isGood());
2967 if (!good)
2968 DRW_DBG("TABLECONTENT stream ended unexpectedly\n")DRW_dbg::dbg("TABLECONTENT stream ended unexpectedly\n");
2969 return good;
2970}
2971
2972} // namespace
2973
2974//! Calculate arbitrary axis
2975/*!
2976* Calculate arbitrary axis for apply extrusions
2977* @author Rallaz
2978*/
2979void DRW_Entity::calculateAxis(DRW_Coord extPoint){
2980 //Follow the arbitrary DXF definitions for extrusion axes.
2981 if (fabs(extPoint.x) < 0.015625 && fabs(extPoint.y) < 0.015625) {
2982 //If we get here, implement Ax = Wy x N where Wy is [0,1,0] per the DXF spec.
2983 //The cross product works out to Wy.y*N.z-Wy.z*N.y, Wy.z*N.x-Wy.x*N.z, Wy.x*N.y-Wy.y*N.x
2984 //Factoring in the fixed values for Wy gives N.z,0,-N.x
2985 extAxisX.x = extPoint.z;
2986 extAxisX.y = 0;
2987 extAxisX.z = -extPoint.x;
2988 } else {
2989 //Otherwise, implement Ax = Wz x N where Wz is [0,0,1] per the DXF spec.
2990 //The cross product works out to Wz.y*N.z-Wz.z*N.y, Wz.z*N.x-Wz.x*N.z, Wz.x*N.y-Wz.y*N.x
2991 //Factoring in the fixed values for Wz gives -N.y,N.x,0.
2992 extAxisX.x = -extPoint.y;
2993 extAxisX.y = extPoint.x;
2994 extAxisX.z = 0;
2995 }
2996
2997 extAxisX.unitize();
2998
2999 //Ay = N x Ax
3000 extAxisY.x = (extPoint.y * extAxisX.z) - (extAxisX.y * extPoint.z);
3001 extAxisY.y = (extPoint.z * extAxisX.x) - (extAxisX.z * extPoint.x);
3002 extAxisY.z = (extPoint.x * extAxisX.y) - (extAxisX.x * extPoint.y);
3003
3004 extAxisY.unitize();
3005}
3006
3007//! Extrude a point using arbitrary axis
3008/*!
3009* apply extrusion in a point using arbitrary axis (previous calculated)
3010* @author Rallaz
3011*/
3012void DRW_Entity::extrudePoint(DRW_Coord extPoint, DRW_Coord *point){
3013 double px, py, pz;
3014 px = (extAxisX.x*point->x)+(extAxisY.x*point->y)+(extPoint.x*point->z);
3015 py = (extAxisX.y*point->x)+(extAxisY.y*point->y)+(extPoint.y*point->z);
3016 pz = (extAxisX.z*point->x)+(extAxisY.z*point->y)+(extPoint.z*point->z);
3017
3018 point->x = px;
3019 point->y = py;
3020 point->z = pz;
3021}
3022
3023bool DRW_Entity::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
3024 try {
3025 switch (code) {
3026 case DRW::dxfCode::HANDLE:
3027 // A damaged file's empty or repeated handle on a typed entity is
3028 // dropped; the entity loads and gets a new handle when saved.
3029 if (reader->getString().empty())
3030 break;
3031 if (!reader->isValidHandleString())
3032 return false;
3033 if (reader->registerSelfHandle())
3034 handle = reader->getHandleString();
3035 break;
3036 case DRW::dxfCode::OWNER_HANDLE:
3037 parentHandle = reader->getHandleString();
3038 break;
3039 case DRW::dxfCode::LAYER:
3040 layer = reader->getUtf8String();
3041 break;
3042 case 6:
3043 lineType = reader->getUtf8String();
3044 break;
3045 case DRW::dxfCode::COLOR:
3046 color = reader->getInt32();
3047 break;
3048 case DRW::dxfCode::LINEWEIGHT:
3049 lWeight = DRW_LW_Conv::dxfInt2lineWidth(reader->getInt32());
3050 break;
3051 case 48:
3052 ltypeScale = reader->getDouble();
3053 break;
3054 case DRW::dxfCode::INVISIBLE:
3055 visible = (reader->getInt32() & 1) == 0;
3056 break;
3057 case 420:
3058 color24 = reader->getInt32();
3059 break;
3060 case 430:
3061 colorName = reader->getString();
3062 break;
3063 case 67:
3064 {
3065 const int value = reader->getInt32();
3066 if (value < DRW::ModelSpace || value > DRW::PaperSpace)
3067 return false;
3068 space = static_cast<DRW::Space>(value);
3069 }
3070 break;
3071 case 102:
3072 return parseDxfGroups(code, reader);
3073 case 284:
3074 {
3075 int value = 0;
3076 if (!readDxfIntInRange(reader, DRW::CastAndReceieveShadows,
3077 DRW::IgnoreShadows, value))
3078 return false;
3079 shadow = static_cast<DRW::ShadowMode>(value);
3080 }
3081 break;
3082 case 347:
3083 material = static_cast<std::uint32_t>(reader->getHandleString());
3084 break;
3085 case 348:
3086 fullVisualStyleHandle = reader->getHandleString();
3087 break;
3088 case 360:
3089 xDictHandle = reader->getHandleString();
3090 break;
3091 case DRW::dxfCode::PLOTSTYLE:
3092 plotStyle = reader->getHandleString();
3093 break;
3094 case 440:
3095 transparency = reader->getInt32();
3096 break;
3097 case 92: {
3098 // Proxy entity graphics byte count (ODA §20.4.95): 92 for R13–R2007.
3099 // Entities that repurpose 92 (for example MESH) handle it before this
3100 // base parser is reached.
3101 const std::int32_t value = reader->getInt32();
3102 if (value < 0
3103 || static_cast<std::uint32_t>(value)
3104 > DRW::kMaxDxfBinaryPayloadBytes)
3105 return false;
3106 numProxyGraph = value;
3107 proxyGraphics.clear();
3108 break;
3109 }
3110 case 160: {
3111 // R2010+ uses a 64-bit DXF count (group 160), not group 92's 32-bit
3112 // storage. Keep the public legacy field bounded and representable.
3113 const std::int64_t value = reader->getInt64();
3114 if (value < 0
3115 || static_cast<std::uint64_t>(value)
3116 > DRW::kMaxDxfBinaryPayloadBytes)
3117 return false;
3118 numProxyGraph = static_cast<int>(value);
3119 proxyGraphics.clear();
3120 break;
3121 }
3122 case 310:
3123 if (numProxyGraph != 0) {
3124 // Proxy graphics binary, hex-encoded across many ≤254-char chunks.
3125 const std::string& hex = reader->getString();
3126 if (!appendHexBytesChecked(
3127 proxyGraphics, hex,
3128 std::min<std::size_t>(
3129 DRW::kMaxDxfBinaryPayloadBytes,
3130 static_cast<std::size_t>(numProxyGraph))))
3131 return false;
3132 }
3133 break;
3134 case 1000:
3135 case 1001:
3136 case 1002:
3137 case 1003:
3138 case 1004:
3139 case 1005:
3140 if (extData.size() >= DRW_TableEntry::kMaxExtendedDataItems)
3141 return false;
3142 extData.push_back(std::make_shared<DRW_Variant>(code,
3143 reader->getString()));
3144 break;
3145 case 1010:
3146 case 1011:
3147 case 1012:
3148 case 1013:
3149 if (extData.size() >= DRW_TableEntry::kMaxExtendedDataItems)
3150 return false;
3151 curr = std::make_shared<DRW_Variant>(
3152 code, DRW_Coord(reader->getDouble(), 0.0, 0.0));
3153 extData.push_back(curr);
3154 break;
3155 // Files repeat Y and Z after a point's Z (R12 ACAD_MATERIAL_MAPPER and
3156 // ASC_SEEDPOINT); a component with no open point is dropped.
3157 case 1020:
3158 case 1021:
3159 case 1022:
3160 case 1023:
3161 if (curr)
3162 curr->setCoordY(reader->getDouble());
3163 break;
3164 case 1030:
3165 case 1031:
3166 case 1032:
3167 case 1033:
3168 if (curr)
3169 curr->setCoordZ(reader->getDouble());
3170 curr.reset();
3171 break;
3172 case 1040:
3173 case 1041:
3174 case 1042:
3175 if (extData.size() >= DRW_TableEntry::kMaxExtendedDataItems)
3176 return false;
3177 extData.push_back(std::make_shared<DRW_Variant>(code,
3178 reader->getDouble()));
3179 break;
3180 case 1070:
3181 case 1071:
3182 if (extData.size() >= DRW_TableEntry::kMaxExtendedDataItems)
3183 return false;
3184 extData.push_back(std::make_shared<DRW_Variant>(code,
3185 reader->getInt32()));
3186 break;
3187 default:
3188 break;
3189 }
3190 return true;
3191 } catch (...) {
3192 reset();
3193 return false;
3194 }
3195}
3196
3197//parses dxf 102 groups to read entity
3198bool DRW_Entity::parseDxfGroups(int code, const std::unique_ptr<dxfReader>& reader){
3199 if (appData.size() >= DRW::kMaxDxfApplicationGroups)
3200 return false;
3201 std::list<DRW_Variant> ls;
3202 DRW_Variant curr;
3203 std::string appName= reader->getString();
3204 if (appName.size() <= 1 || appName.front() != '{')
3205 return false;
3206
3207 const bool isReactors = appName == "{ACAD_REACTORS";
3208 const bool isXDictionary = appName == "{ACAD_XDICTIONARY";
3209 std::vector<std::uint32_t> parsedReactors;
3210 std::uint32_t parsedXDictionary = 0;
3211
3212 curr.addString(code, appName.substr(1));
3213 ls.push_back(curr);
3214 int depth = 1;
3215 int nextCode = 0;
3216 while (depth > 0 && reader->readRec(&nextCode)) {
3217 if (ls.size() >= DRW::kMaxDxfApplicationGroupPairs)
3218 return false;
3219
3220 // A section/entity boundary before the matching 102 close would be
3221 // consumed irreversibly and leave the caller out of sync.
3222 if (nextCode == 0)
3223 return false;
3224
3225 DRW_Variant value;
3226 if (nextCode == 102) {
3227 const std::string marker = reader->getString();
3228 if (marker.empty() || (marker.front() == '{' && marker.size() == 1))
3229 return false;
3230 if (marker.front() == '{') {
3231 if (++depth > DRW::kMaxDxfApplicationGroupNesting)
3232 return false;
3233 } else if (marker == "}") {
3234 --depth;
3235 } else {
3236 return false;
3237 }
3238 value.addString(nextCode, marker);
3239 } else if ((nextCode >= 320 && nextCode <= 369)
3240 || (nextCode >= 390 && nextCode <= 399)
3241 || nextCode == 480 || nextCode == 481
3242 || nextCode == 1005) {
3243 const std::string rawHandle = reader->getString();
3244 value.addString(nextCode, rawHandle);
3245 if (depth == 1 && isReactors && nextCode == 330) {
3246 const auto reactor = reader->getHandleString();
3247 if (reactor != 0) {
3248 if (parsedReactors.size() >= dwgSafety::MaxReactorCount)
3249 return false;
3250 parsedReactors.push_back(static_cast<std::uint32_t>(reactor));
3251 }
3252 } else if (depth == 1 && isXDictionary && nextCode == 360
3253 && parsedXDictionary == 0) {
3254 parsedXDictionary = static_cast<std::uint32_t>(
3255 reader->getHandleString());
3256 }
3257 } else {
3258 switch (reader->type) {
3259 case dxfReader::STRING:
3260 case dxfReader::BINARY:
3261 value.addString(nextCode, reader->getString());
3262 break;
3263 case dxfReader::INT32:
3264 case dxfReader::BOOL:
3265 value.addInt(nextCode, reader->getInt32());
3266 break;
3267 case dxfReader::INT64:
3268 value.addInt64(nextCode, static_cast<std::int64_t>(reader->getInt64()));
3269 break;
3270 case dxfReader::DOUBLE:
3271 value.addDouble(nextCode, reader->getDouble());
3272 break;
3273 default:
3274 return false;
3275 }
3276 }
3277 ls.push_back(value);
3278 }
3279
3280 if (depth != 0)
3281 return false;
3282 if (isReactors)
3283 reactorHandles = std::move(parsedReactors);
3284 if (isXDictionary && parsedXDictionary != 0)
3285 xDictHandle = parsedXDictionary;
3286 appData.push_back(std::move(ls));
3287 return true;
3288}
3289
3290bool DRW_Entity::parseDwg(DRW::Version version, dwgBuffer *buf, dwgBuffer* strBuf, std::uint32_t bs){
3291 // This overload is also used by the two-stage common/handle tests and
3292 // callers; concrete record parsers own carrier reset so handle parsing can
3293 // remain transactional.
3294 if (buf == nullptr)
3295 return false;
3296
3297 DRW_DBG("\n***************************** parsing entity *********************************************\n")DRW_dbg::dbg("\n***************************** parsing entity *********************************************\n"
)
;
3298 oType = buf->getObjType(version);
3299 if (!buf->isGood())
3300 return false;
3301 DRW_DBG("Object type: ")DRW_dbg::dbg("Object type: "); DRW_DBG(oType)DRW_dbg::dbg(oType); DRW_DBG(", ")DRW_dbg::dbg(", "); DRW_DBGH(oType)DRW_dbg::dbgH(oType);
3302
3303 if (version > DRW::AC1014 && version < DRW::AC1024) {//2000 & 2004
3304 const std::uint64_t totalBits = static_cast<std::uint64_t>(buf->size()) * 8u;
3305 std::uint32_t parsedObjectSize = 0;
3306 if (!readBoundedRawLong32(*buf, totalBits, parsedObjectSize))
3307 return false;
3308 objSize = parsedObjectSize; //RL 32bits object size in bits
3309 DRW_DBG(" Object size: ")DRW_dbg::dbg(" Object size: "); DRW_DBG(objSize)DRW_dbg::dbg(objSize); DRW_DBG("\n")DRW_dbg::dbg("\n");
3310 }
3311 if (version > DRW::AC1021) {//2010+
3312 const std::uint64_t totalBits = static_cast<std::uint64_t>(buf->size()) * 8u;
3313 // Clamp: a corrupt bs > ms*8 would underflow objSize (unsigned) to a
3314 // huge value and drive strBuf->moveBitPos(objSize-1) past the buffer.
3315 if (totalBits > std::numeric_limits<std::uint32_t>::max()
3316 || bs > totalBits)
3317 return false;
3318 objSize = static_cast<std::uint32_t>(totalBits - bs);
3319 DRW_DBG(" Object size: ")DRW_dbg::dbg(" Object size: "); DRW_DBG(objSize)DRW_dbg::dbg(objSize); DRW_DBG("\n")DRW_dbg::dbg("\n");
3320 }
3321
3322 if (strBuf != NULL__null && version > DRW::AC1018) {//2007+
3323 if (!strBuf->seekR2007StringStream(objSize))
3324 return false;
3325 DRW_DBG(" strBuf strbit pos 2007: ")DRW_dbg::dbg(" strBuf strbit pos 2007: "); DRW_DBG(strBuf->getPosition())DRW_dbg::dbg(strBuf->getPosition()); DRW_DBG(" strBuf bpos 2007: ")DRW_dbg::dbg(" strBuf bpos 2007: "); DRW_DBG(strBuf->getBitPos())DRW_dbg::dbg(strBuf->getBitPos()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3326 DRW_DBG("strBuf start pos 2007: ")DRW_dbg::dbg("strBuf start pos 2007: "); DRW_DBG(strBuf->getPosition())DRW_dbg::dbg(strBuf->getPosition()); DRW_DBG(" strBuf bpos 2007: ")DRW_dbg::dbg(" strBuf bpos 2007: "); DRW_DBG(strBuf->getBitPos())DRW_dbg::dbg(strBuf->getBitPos()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3327 }
3328
3329 // R2007+ objSize includes the detached string stream and its footer. A
3330 // common entity prefix must stay in the data portion just like the
3331 // entity-specific readers below. Only a caller that supplies the real
3332 // modern string cursor can require the footer here; the null-cursor form
3333 // is retained for isolated common-prefix tests without object framing.
3334 std::uint64_t bodyEndBit = proxyEntityEndBit(*buf, objSize);
3335 if (version > DRW::AC1021 && strBuf != nullptr) {
3336 dwgBuffer stringBoundary = strBuf->forkIndependent();
3337 if (!stringBoundary.seekR2007StringStream(objSize))
3338 return false;
3339 bodyEndBit = currentDwgBit(&stringBoundary);
3340 }
3341 dwgDataEndBit = bodyEndBit;
3342 const auto bodyWithinBounds = [buf, &bodyEndBit]() {
3343 return currentDwgBit(buf) <= bodyEndBit;
3344 };
3345
3346 dwgHandle ho;
3347 if (!readBoundedDwgHandle(*buf, bodyEndBit, 0, false, ho))
3348 return false;
3349 handle = ho.ref;
3350 DRW_DBG("Entity Handle: ")DRW_dbg::dbg("Entity Handle: "); DRW_DBGHL(ho.code, ho.size, ho.ref)DRW_dbg::dbgHL(ho.code, ho.size, ho.ref);
3351 // EED is a bounded, repeated chunk stream. Parse all chunks before
3352 // changing the entity so a malformed item cannot leak partial metadata.
3353 std::vector<DwgEedChunk> eedChunks;
3354 if (!readDwgEed(version, *buf, eedChunks, bodyEndBit))
3355 return false;
3356 if (!bodyWithinBounds())
3357 return false;
3358 std::vector<std::shared_ptr<DRW_Variant>> parsedExtData;
3359 std::vector<PendingHandleRef> parsedAppIdResolutions;
3360 std::vector<PendingHandleRef> parsedLayerRefResolutions;
3361 try {
3362 for (const DwgEedChunk& chunk : eedChunks) {
3363 const std::size_t appIndex =
3364 extData.size() + parsedExtData.size();
3365 parsedExtData.push_back(
3366 std::make_shared<DRW_Variant>(1001, std::string{}));
3367 parsedAppIdResolutions.push_back({appIndex, chunk.appHandle});
3368 for (const DRW_Variant& item : chunk.items)
3369 parsedExtData.push_back(std::make_shared<DRW_Variant>(item));
3370 for (const DwgEedHandleRef& ref : chunk.layerRefs) {
3371 parsedLayerRefResolutions.push_back(
3372 {appIndex + 1 + ref.itemIndex, ref.handleRef});
3373 }
3374 }
3375 } catch (...) {
3376 return false;
3377 }
3378 DRW_DBG(" [bidi-debug pre-graphFlag bufpos=")DRW_dbg::dbg(" [bidi-debug pre-graphFlag bufpos="); DRW_DBG(buf->getPosition())DRW_dbg::dbg(buf->getPosition()); DRW_DBG(" bitpos=")DRW_dbg::dbg(" bitpos="); DRW_DBG(buf->getBitPos())DRW_dbg::dbg(buf->getBitPos()); DRW_DBG("]\n")DRW_dbg::dbg("]\n");
3379 bool graphFlag = false;
3380 if (!readBoundedBit(*buf, bodyEndBit, graphFlag))
3381 return false;
3382 DRW_DBG(" graphFlag: ")DRW_dbg::dbg(" graphFlag: "); DRW_DBG(graphFlag)DRW_dbg::dbg(graphFlag); DRW_DBG("\n")DRW_dbg::dbg("\n");
3383 if (!bodyWithinBounds())
3384 return false;
3385 if (graphFlag) {
3386 DRW_DBG(" [bidi-debug pre-graphSize bufpos=")DRW_dbg::dbg(" [bidi-debug pre-graphSize bufpos="); DRW_DBG(buf->getPosition())DRW_dbg::dbg(buf->getPosition()); DRW_DBG(" bitpos=")DRW_dbg::dbg(" bitpos="); DRW_DBG(buf->getBitPos())DRW_dbg::dbg(buf->getBitPos()); DRW_DBG("]\n")DRW_dbg::dbg("]\n");
3387 std::uint64_t graphDataSize = 0;
3388 if (version >= DRW::AC1024) {
3389 if (!readBoundedBitLongLong(*buf, bodyEndBit, graphDataSize))
3390 return false;
3391 } else {
3392 std::uint32_t parsedGraphDataSize = 0;
3393 if (!readBoundedRawLong32(*buf, bodyEndBit,
3394 parsedGraphDataSize))
3395 return false;
3396 graphDataSize = parsedGraphDataSize;
3397 }
3398 DRW_DBG("graphData in bytes: ")DRW_dbg::dbg("graphData in bytes: "); DRW_DBG(static_cast<std::uint32_t>(graphDataSize))DRW_dbg::dbg(static_cast<std::uint32_t>(graphDataSize)); DRW_DBG("\n")DRW_dbg::dbg("\n");
3399 if (!bodyWithinBounds())
3400 return false;
3401 const std::uint64_t maxMoveBytes = static_cast<std::uint64_t>(std::numeric_limits<std::int32_t>::max() / 8);
3402 const std::uint64_t currentBit = currentDwgBit(buf);
3403 std::uint64_t graphBits = 0;
3404 if (!dwgSafety::multiply(graphDataSize, 8, graphBits)
3405 || currentBit > bodyEndBit
3406 || graphBits > bodyEndBit - currentBit
3407 || graphDataSize > static_cast<std::uint64_t>(buf->numRemainingBytes())
3408 || graphDataSize > maxMoveBytes) {
3409 DRW_DBG("graphData size outside object body\n")DRW_dbg::dbg("graphData size outside object body\n");
3410 return false;
3411 }
3412 // Capture the proxy-graphics byte stream instead of skipping it. These
3413 // are cached drawable primitives (lines/arcs/polylines/text) that any
3414 // reader can render for proxy/custom entities (STDPART2D, AEC_*, tables)
3415 // — previously discarded via moveBitPos, leaving proxyGraphics empty.
3416 // dwgBuffer::getBytes is bit-aware (reconstructs each byte at a non-zero
3417 // bitPos), so it lands at the exact same position moveBitPos(8N) did.
3418 // (write-review #32 / read-coverage gap #1)
3419 if (graphDataSize > 0) {
3420 if (!DRW::resize(proxyGraphics, static_cast<int>(graphDataSize)))
3421 return false;
3422 if (!readBoundedBytes(
3423 *buf, bodyEndBit,
3424 reinterpret_cast<std::uint8_t*>(&proxyGraphics[0]),
3425 static_cast<std::size_t>(graphDataSize)))
3426 return false;
3427 numProxyGraph = static_cast<int>(graphDataSize);
3428 }
3429 }
3430 if (version < DRW::AC1015) {//14-
3431 std::uint32_t parsedObjectSize = 0;
3432 if (!readBoundedRawLong32(*buf, proxyEntityEndBit(*buf, 0),
3433 parsedObjectSize))
3434 return false;
3435 objSize = parsedObjectSize; //RL 32bits object size in bits
3436 DRW_DBG(" Object size in bits: ")DRW_dbg::dbg(" Object size in bits: "); DRW_DBG(objSize)DRW_dbg::dbg(objSize); DRW_DBG("\n")DRW_dbg::dbg("\n");
3437 bodyEndBit = proxyEntityEndBit(*buf, objSize);
3438 }
3439
3440 std::uint8_t entmode = 0;
3441 if (!readBounded2Bits(*buf, bodyEndBit, entmode))
3442 return false;
3443 if (entmode == 0)
3444 ownerHandle= true;
3445 // entmode = 2;
3446 else if(entmode ==2)
3447 entmode = 0;
3448 space = (DRW::Space)entmode; //RLZ verify cast values
3449 DRW_DBG("entmode: ")DRW_dbg::dbg("entmode: "); DRW_DBG(entmode)DRW_dbg::dbg(entmode);
3450 if (!readBoundedBitLong(*buf, bodyEndBit, numReactors))
3451 return false; //BL per spec §20.4.1
3452 DRW_DBG(", numReactors: ")DRW_dbg::dbg(", numReactors: "); DRW_DBG(numReactors)DRW_dbg::dbg(numReactors);
3453 if (!buf->isGood() || !dwgSafety::validReactorCount(numReactors))
3454 return false;
3455
3456 if (version < DRW::AC1015) {//14-
3457 bool isByLayer = false;
3458 if (!readBoundedBit(*buf, bodyEndBit, isByLayer))
3459 return false;
3460 if (isByLayer) {//is bylayer line type
3461 lineType = "BYLAYER";
3462 ltFlags = 0;
3463 } else {
3464 lineType = "";
3465 ltFlags = 3;
3466 }
3467 DRW_DBG(" lineType: ")DRW_dbg::dbg(" lineType: "); DRW_DBG(lineType.c_str())DRW_dbg::dbg(lineType.c_str());
3468 DRW_DBG(" ltFlags: ")DRW_dbg::dbg(" ltFlags: "); DRW_DBG(ltFlags)DRW_dbg::dbg(ltFlags);
3469 }
3470 if (version > DRW::AC1015) {//2004+
3471 bool xDict = false;
3472 if (!readBoundedBit(*buf, bodyEndBit, xDict))
3473 return false;
3474 xDictFlag = xDict;
3475 DRW_DBG(" xDictFlag: ")DRW_dbg::dbg(" xDictFlag: "); DRW_DBG(xDictFlag)DRW_dbg::dbg(xDictFlag); DRW_DBG("\n")DRW_dbg::dbg("\n");
3476 }
3477
3478 // libreDWG common_entity_data.spec — the bit at this stream position has two
3479 // disjoint meanings by version:
3480 // * R13..R2002 (version < AC1018): `nolinks` (B). 1 = no prev/next handles
3481 // in the handle section; 0 = read prev+next at parseDwgEntHandle.
3482 // * R2004..R2010 (AC1018..AC1024): NO bit in the stream — reader forces
3483 // haveNextLinks=1 to skip the prev/next handle reads.
3484 // * R2013+ (version > AC1024): `has_ds_data` (B). 1 = inline ACIS SAB
3485 // datastore present. Stored separately because it gates SAB handling,
3486 // not prev/next links (which are already version<AC1018 gated).
3487 // Total bit consumption is unchanged for every version.
3488 if (version < DRW::AC1018) {
3489 bool parsedHaveNextLinks = false;
3490 if (!readBoundedBit(*buf, bodyEndBit, parsedHaveNextLinks))
3491 return false; //nolinks //B
3492 haveNextLinks = parsedHaveNextLinks ? 1 : 0;
3493 DRW_DBG(", haveNextLinks (0 yes, 1 prev next): ")DRW_dbg::dbg(", haveNextLinks (0 yes, 1 prev next): "); DRW_DBG(haveNextLinks)DRW_dbg::dbg(haveNextLinks); DRW_DBG("\n")DRW_dbg::dbg("\n");
3494 } else {
3495 haveNextLinks = 1; //AC1018+: not in stream, force 1 (no prev/next)
3496 DRW_DBG(", haveNextLinks (forced): ")DRW_dbg::dbg(", haveNextLinks (forced): "); DRW_DBG(haveNextLinks)DRW_dbg::dbg(haveNextLinks); DRW_DBG("\n")DRW_dbg::dbg("\n");
3497 }
3498 if (version > DRW::AC1024) {
3499 bool parsedHasDsData = false;
3500 if (!readBoundedBit(*buf, bodyEndBit, parsedHasDsData))
3501 return false; //has_ds_data //B (R2013+)
3502 hasDsData = parsedHasDsData ? 1 : 0;
3503 DRW_DBG(", hasDsData (R2013+): ")DRW_dbg::dbg(", hasDsData (R2013+): "); DRW_DBG(hasDsData)DRW_dbg::dbg(hasDsData); DRW_DBG("\n")DRW_dbg::dbg("\n");
3504 }
3505//ENC color
3506 std::uint32_t parsedColor = 0;
3507 if (!readBoundedEnColor(*buf, bodyEndBit, version, parsedColor))
3508 return false; //BS or CMC //ok for R14 or negate
3509 color = static_cast<int>(parsedColor);
3510 // Capture the AcDbColor side-channel before the common handle stream.
3511 // The handle follows owner/reactors/xdictionary and precedes layer in the
3512 // R2004+ common entity handle data.
3513 hasAcDbColorH = buf->lastEnColorHadDbColorRef;
3514 // libreDWG common_entity_data.spec:432-453 — ENC alpha_raw (DXF code
3515 // 440) is encoded as (alpha_type<<24) | alpha. Stored verbatim; the
3516 // filter (RS_FilterDXFRW::setEntityAttributes) decodes alpha_type==3
3517 // into a per-entity pen alpha, otherwise inherits from layer/block.
3518 if (buf->lastEnColorAlphaRaw != 0) {
3519 transparency = static_cast<int>(buf->lastEnColorAlphaRaw);
3520 }
3521 // libreDWG common_entity_data.spec:468-475 — inline TV name/book name
3522 // (flags 0x41/0x42) override any dbColorMap-resolved name. Captured
3523 // immediately; entryParse will skip the override only if colorName is
3524 // already populated here.
3525 if (!buf->lastEnColorName.empty()) {
3526 colorName = buf->lastEnColorBookName.empty()
3527 ? buf->lastEnColorName
3528 : (buf->lastEnColorBookName + "$" + buf->lastEnColorName);
3529 }
3530 color24 = buf->lastEnColorRgb;
3531 if (!readBoundedBitDouble(*buf, bodyEndBit, ltypeScale))
3532 return false; //BD
3533 DRW_DBG(" entity color: ")DRW_dbg::dbg(" entity color: "); DRW_DBG(color)DRW_dbg::dbg(color);
3534 DRW_DBG(" ltScale: ")DRW_dbg::dbg(" ltScale: "); DRW_DBG(ltypeScale)DRW_dbg::dbg(ltypeScale); DRW_DBG("\n")DRW_dbg::dbg("\n");
3535 if (version > DRW::AC1014) {//2000+ — §19.4.1: linetype-flags BB then plot-flags BB
3536 std::uint8_t parsedLtFlags = 0;
3537 if (!readBounded2Bits(*buf, bodyEndBit, parsedLtFlags))
3538 return false; //BB
3539 ltFlags = parsedLtFlags;
3540 if (ltFlags == 0) lineType = "BYLAYER";
3541 else if (ltFlags == 1) lineType = "BYBLOCK";
3542 else if (ltFlags == 2) lineType = "CONTINUOUS";
3543 else lineType = ""; //3 → handle at end
3544 DRW_DBG("ltFlags: ")DRW_dbg::dbg("ltFlags: "); DRW_DBG(ltFlags)DRW_dbg::dbg(ltFlags);
3545 DRW_DBG(" lineType: ")DRW_dbg::dbg(" lineType: "); DRW_DBG(lineType.c_str())DRW_dbg::dbg(lineType.c_str());
3546
3547 std::uint8_t parsedPlotFlags = 0;
3548 if (!readBounded2Bits(*buf, bodyEndBit, parsedPlotFlags))
3549 return false; //BB
3550 plotFlags = parsedPlotFlags;
3551 DRW_DBG(", plotFlags: ")DRW_dbg::dbg(", plotFlags: "); DRW_DBG(plotFlags)DRW_dbg::dbg(plotFlags);
3552 }
3553 if (version > DRW::AC1018) {//2007+
3554 std::uint8_t parsedMaterialFlag = 0;
3555 if (!readBounded2Bits(*buf, bodyEndBit, parsedMaterialFlag))
3556 return false; //BB
3557 materialFlag = parsedMaterialFlag;
3558 DRW_DBG("materialFlag: ")DRW_dbg::dbg("materialFlag: "); DRW_DBG(materialFlag)DRW_dbg::dbg(materialFlag);
3559 std::uint8_t parsedShadowFlag = 0;
3560 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedShadowFlag))
3561 return false; //RC, low 2 bits is shadow mode 0..3
3562 shadowFlag = parsedShadowFlag;
3563 DRW_DBG("shadowFlag: ")DRW_dbg::dbg("shadowFlag: "); DRW_DBG(shadowFlag)DRW_dbg::dbg(shadowFlag); DRW_DBG("\n")DRW_dbg::dbg("\n");
3564 shadow = static_cast<DRW::ShadowMode>(shadowFlag & 0x3);
3565 }
3566 if (version > DRW::AC1021) {//2010+ — §19.4.1: three single-bit flags
3567 // Ground-truth: libreDWG common_entity_data.spec lines 523-528
3568 // and ODA spec v5.4.1 §19.4.1 both define three FIELD_B (single bit)
3569 // flags here, one each for full/face/edge visual style. Total bit
3570 // consumption (3 bits) is identical to the historical BB+B shape;
3571 // only the semantics differ. The corresponding handles are read
3572 // conditionally in parseDwgEntHandle after the plotstyle handle.
3573 bool parsedHasFullVisualStyle = false;
3574 bool parsedHasFaceVisualStyle = false;
3575 bool parsedHasEdgeVisualStyle = false;
3576 if (!readBoundedBit(*buf, bodyEndBit, parsedHasFullVisualStyle)
3577 || !readBoundedBit(*buf, bodyEndBit, parsedHasFaceVisualStyle)
3578 || !readBoundedBit(*buf, bodyEndBit, parsedHasEdgeVisualStyle))
3579 return false; //B
3580 hasFullVisualStyle = parsedHasFullVisualStyle ? 1 : 0;
3581 hasFaceVisualStyle = parsedHasFaceVisualStyle ? 1 : 0;
3582 hasEdgeVisualStyle = parsedHasEdgeVisualStyle ? 1 : 0;
3583 DRW_DBG("hasFull/Face/Edge VisualStyle: ")DRW_dbg::dbg("hasFull/Face/Edge VisualStyle: ");
3584 DRW_DBG(hasFullVisualStyle)DRW_dbg::dbg(hasFullVisualStyle); DRW_DBG(" ")DRW_dbg::dbg(" ");
3585 DRW_DBG(hasFaceVisualStyle)DRW_dbg::dbg(hasFaceVisualStyle); DRW_DBG(" ")DRW_dbg::dbg(" ");
3586 DRW_DBG(hasEdgeVisualStyle)DRW_dbg::dbg(hasEdgeVisualStyle); DRW_DBG("\n")DRW_dbg::dbg("\n");
3587 }
3588 std::uint16_t parsedInvisibleFlag = 0;
3589 if (!readBoundedBitShort(*buf, bodyEndBit, parsedInvisibleFlag))
3590 return false; //BS
3591 const std::int16_t invisibleFlag =
3592 static_cast<std::int16_t>(parsedInvisibleFlag);
3593 DRW_DBG(" invisibleFlag: ")DRW_dbg::dbg(" invisibleFlag: "); DRW_DBG(invisibleFlag)DRW_dbg::dbg(invisibleFlag);
3594 // DXF group 60: bit 0 = invisible (1) / visible (0). libreDWG
3595 // common_entity_data.spec masks bit 0 only (`invisible & 1`) and ignores
3596 // the higher bits, so use the same mask rather than `== 0`. Paired with
3597 // the encode emit below.
3598 visible = ((invisibleFlag & 1) == 0);
3599 if (version > DRW::AC1014) {//2000+
3600 std::uint8_t parsedLineWeight = 0;
3601 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedLineWeight))
3602 return false; //RC
3603 lWeight = DRW_LW_Conv::dwgInt2lineWidth(parsedLineWeight);
3604 DRW_DBG(" lwFlag (lWeight): ")DRW_dbg::dbg(" lwFlag (lWeight): "); DRW_DBG(lWeight)DRW_dbg::dbg(lWeight); DRW_DBG("\n")DRW_dbg::dbg("\n");
3605 }
3606 //Only in blocks ????????
3607// if (version > DRW::AC1018) {//2007+
3608// std::uint8_t unk = buf->getBit();
3609// DRW_DBG("unknown bit: "); DRW_DBG(unk); DRW_DBG("\n");
3610// }
3611 if (!buf->isGood() || !bodyWithinBounds())
3612 return false;
3613
3614 const std::size_t oldExtDataSize = extData.size();
3615 const std::size_t oldAppResolutionSize =
3616 pendingAppIdResolutions.size();
3617 const std::size_t oldLayerResolutionSize =
3618 pendingLayerRefResolutions.size();
3619 const auto rollbackEed = [this, oldExtDataSize,
3620 oldAppResolutionSize, oldLayerResolutionSize]() {
3621 extData.resize(oldExtDataSize);
3622 pendingAppIdResolutions.resize(oldAppResolutionSize);
3623 pendingLayerRefResolutions.resize(oldLayerResolutionSize);
3624 };
3625 if (parsedExtData.size() > DRW_TableEntry::kMaxExtendedDataItems
3626 || extData.size() > DRW_TableEntry::kMaxExtendedDataItems
3627 || parsedExtData.size()
3628 > DRW_TableEntry::kMaxExtendedDataItems - extData.size())
3629 return false;
3630 try {
3631 extData.insert(extData.end(), parsedExtData.begin(),
3632 parsedExtData.end());
3633 pendingAppIdResolutions.insert(
3634 pendingAppIdResolutions.end(), parsedAppIdResolutions.begin(),
3635 parsedAppIdResolutions.end());
3636 pendingLayerRefResolutions.insert(
3637 pendingLayerRefResolutions.end(),
3638 parsedLayerRefResolutions.begin(), parsedLayerRefResolutions.end());
3639 } catch (...) {
3640 rollbackEed();
3641 return false;
3642 }
3643 return true;
3644}
3645
3646bool DRW_Entity::parseDwgEntHandle(
3647 DRW::Version version, dwgBuffer *buf, bool resetHandleStream,
3648 std::uint64_t handleEndBit){
3649 if (buf == nullptr || !buf->isGood()
3650 || !dwgSafety::validReactorCount(numReactors))
3651 return false;
3652 if (resetHandleStream && version > DRW::AC1018) {//2007+ skip string area
3653 if (!buf->setPosition(objSize >> 3))
3654 return false;
3655 buf->setBitPos(objSize & 7);
3656 if (!buf->isGood())
3657 return false;
3658 }
3659 if (handleEndBit != std::numeric_limits<std::uint64_t>::max()
3660 && currentDwgBit(buf) > handleEndBit)
3661 return false;
3662
3663 std::uint32_t newAcDbColorHandle = acDbColorHandle;
3664 std::uint32_t newParentHandle = ownerHandle ? parentHandle : DRW::NoHandle;
3665 std::vector<std::uint32_t> newReactorHandles;
3666 const bool hasXDictionary = version <= DRW::AC1015 || xDictFlag != 1;
3667 std::uint32_t newXDictHandle = hasXDictionary ? xDictHandle : 0;
3668 dwgHandle newLayerH = layerH;
3669 dwgHandle newLTypeH = lTypeH;
3670 std::uint32_t newNextEntLink = nextEntLink;
3671 std::uint32_t newPrevEntLink = prevEntLink;
3672 const DRW::ShadowMode newShadow = version > DRW::AC1018
3673 ? shadow : DRW::CastAndReceieveShadows;
3674 std::uint32_t newMaterial = materialFlag == 3 ? material
3675 : DRW::MaterialByLayer;
3676 int newPlotStyle = plotFlags == 3 ? plotStyle
3677 : DRW::DefaultPlotStyle;
3678 std::uint32_t newShadowHandle = shadowFlag == 3 ? shadowHandle : 0;
3679 std::uint32_t newFullVisualStyleHandle = hasFullVisualStyle
3680 ? fullVisualStyleHandle : 0;
3681 std::uint32_t newFaceVisualStyleHandle = hasFaceVisualStyle
3682 ? faceVisualStyleHandle : 0;
3683 std::uint32_t newEdgeVisualStyleHandle = hasEdgeVisualStyle
3684 ? edgeVisualStyleHandle : 0;
3685 if (version <= DRW::AC1018) {
3686 newMaterial = DRW::MaterialByLayer;
3687 newShadowHandle = 0;
3688 }
3689 if (version <= DRW::AC1021) {
3690 newFullVisualStyleHandle = 0;
3691 newFaceVisualStyleHandle = 0;
3692 newEdgeVisualStyleHandle = 0;
3693 }
3694 auto readOffsetHandle = [&](dwgHandle& out) {
3695 return handleEndBit == std::numeric_limits<std::uint64_t>::max()
3696 ? readDwgHandleChecked(*buf, handle, true, out)
3697 : readBoundedDwgHandle(*buf, handleEndBit, handle, true, out);
3698 };
3699 auto readRawHandle = [&](dwgHandle& out) {
3700 return handleEndBit == std::numeric_limits<std::uint64_t>::max()
3701 ? readDwgHandleChecked(*buf, handle, false, out)
3702 : readBoundedDwgHandle(*buf, handleEndBit, handle, false, out);
3703 };
3704 auto readHandleList = [&]() {
3705 if (handleEndBit == std::numeric_limits<std::uint64_t>::max())
3706 return readDwgHandleList(*buf, handle, numReactors, false,
3707 &newReactorHandles);
3708 if (!dwgSafety::validReactorCount(numReactors))
3709 return false;
3710 std::vector<std::uint32_t> parsed;
3711 if (!DRW::reserve(parsed, numReactors))
3712 return false;
3713 for (std::int32_t i = 0; i < numReactors; ++i) {
3714 dwgHandle reactor;
3715 if (!readBoundedDwgHandle(*buf, handleEndBit, handle, false,
3716 reactor))
3717 return false;
3718 parsed.push_back(reactor.ref);
3719 }
3720 newReactorHandles = std::move(parsed);
3721 return true;
3722 };
3723
3724 if(ownerHandle){//entity are in block or in a polyline
3725 dwgHandle ownerH;
3726 if (!readOffsetHandle(ownerH))
3727 return false;
3728 DRW_DBG("owner (parent) Handle: ")DRW_dbg::dbg("owner (parent) Handle: "); DRW_DBGHL(ownerH.code, ownerH.size, ownerH.ref)DRW_dbg::dbgHL(ownerH.code, ownerH.size, ownerH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3729 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3730 newParentHandle = ownerH.ref;
3731 DRW_DBG("Block (parent) Handle: ")DRW_dbg::dbg("Block (parent) Handle: "); DRW_DBGHL(ownerH.code, ownerH.size, newParentHandle)DRW_dbg::dbgHL(ownerH.code, ownerH.size, newParentHandle); DRW_DBG("\n")DRW_dbg::dbg("\n");
3732 } else
3733 DRW_DBG("NO Block (parent) Handle\n")DRW_dbg::dbg("NO Block (parent) Handle\n");
3734
3735 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3736 if (!readHandleList())
3737 return false;
3738 if (xDictFlag !=1){//linetype in 2004 seems not have XDicObjH or NULL handle
3739 dwgHandle XDicObjH;
3740 if (!readRawHandle(XDicObjH))
3741 return false;
3742 newXDictHandle = XDicObjH.ref; // 2a.2: persist xdict
3743 DRW_DBG(" XDicObj control Handle: ")DRW_dbg::dbg(" XDicObj control Handle: "); DRW_DBGHL(XDicObjH.code, XDicObjH.size, XDicObjH.ref)DRW_dbg::dbgHL(XDicObjH.code, XDicObjH.size, XDicObjH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3744 }
3745 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3746
3747 // ENC's optional AcDbColor reference follows the common owner, reactor,
3748 // and xdictionary handles in R2004+ files. The dwgReader resolves this
3749 // handle against dbColorMap after parsing the complete entity.
3750 if (hasAcDbColorH && version > DRW::AC1015) {
3751 dwgHandle dbcH;
3752 if (!readOffsetHandle(dbcH))
3753 return false;
3754 newAcDbColorHandle = dbcH.ref;
3755 DRW_DBG(" AcDbColor Handle: ")DRW_dbg::dbg(" AcDbColor Handle: ");
3756 DRW_DBGHL(dbcH.code, dbcH.size, dbcH.ref)DRW_dbg::dbgHL(dbcH.code, dbcH.size, dbcH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3757 }
3758
3759 if (version < DRW::AC1015) {//R14-
3760 //layer handle
3761 if (!readOffsetHandle(newLayerH))
3762 return false;
3763 DRW_DBG(" layer Handle: ")DRW_dbg::dbg(" layer Handle: "); DRW_DBGHL(newLayerH.code, newLayerH.size, newLayerH.ref)DRW_dbg::dbgHL(newLayerH.code, newLayerH.size, newLayerH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3764 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3765 //lineType handle
3766 if(ltFlags == 3){
3767 if (!readOffsetHandle(newLTypeH))
3768 return false;
3769 DRW_DBG("linetype Handle: ")DRW_dbg::dbg("linetype Handle: "); DRW_DBGHL(newLTypeH.code, newLTypeH.size, newLTypeH.ref)DRW_dbg::dbgHL(newLTypeH.code, newLTypeH.size, newLTypeH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3770 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3771 }
3772 }
3773 if (version < DRW::AC1018) {//2000+
3774 if (haveNextLinks == 0) {
3775 dwgHandle nextLinkH;
3776 if (!readOffsetHandle(nextLinkH))
3777 return false;
3778 DRW_DBG(" prev nextLinkers Handle: ")DRW_dbg::dbg(" prev nextLinkers Handle: "); DRW_DBGHL(nextLinkH.code, nextLinkH.size, nextLinkH.ref)DRW_dbg::dbgHL(nextLinkH.code, nextLinkH.size, nextLinkH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3779 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3780 const std::uint32_t prevLink = nextLinkH.ref;
3781 if (!readOffsetHandle(nextLinkH))
3782 return false;
3783 DRW_DBG(" next nextLinkers Handle: ")DRW_dbg::dbg(" next nextLinkers Handle: "); DRW_DBGHL(nextLinkH.code, nextLinkH.size, nextLinkH.ref)DRW_dbg::dbgHL(nextLinkH.code, nextLinkH.size, nextLinkH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3784 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3785 newPrevEntLink = prevLink;
3786 newNextEntLink = nextLinkH.ref;
3787 } else {
3788 if (handle == 0 || handle == std::numeric_limits<std::uint32_t>::max())
3789 return false;
3790 newNextEntLink = handle + 1;
3791 newPrevEntLink = handle - 1;
3792 }
3793 }
3794 if (version > DRW::AC1015) {//2004+
3795 //Parses Bookcolor handle
3796 }
3797 if (version > DRW::AC1014) {//2000+
3798 //layer handle
3799 if (!readOffsetHandle(newLayerH))
3800 return false;
3801 DRW_DBG(" layer Handle: ")DRW_dbg::dbg(" layer Handle: "); DRW_DBGHL(newLayerH.code, newLayerH.size, newLayerH.ref)DRW_dbg::dbgHL(newLayerH.code, newLayerH.size, newLayerH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3802 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3803 //lineType handle
3804 if(ltFlags == 3){
3805 if (!readOffsetHandle(newLTypeH))
3806 return false;
3807 DRW_DBG("linetype Handle: ")DRW_dbg::dbg("linetype Handle: "); DRW_DBGHL(newLTypeH.code, newLTypeH.size, newLTypeH.ref)DRW_dbg::dbgHL(newLTypeH.code, newLTypeH.size, newLTypeH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3808 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3809 }
3810 }
3811 if (version > DRW::AC1014) {//2000+
3812 if (version > DRW::AC1018) {//2007+
3813 if (materialFlag == 3) {
3814 dwgHandle materialH;
3815 if (!readOffsetHandle(materialH))
3816 return false;
3817 newMaterial = materialH.ref;
3818 DRW_DBG(" material Handle: ")DRW_dbg::dbg(" material Handle: "); DRW_DBGHL(materialH.code, materialH.size, materialH.ref)DRW_dbg::dbgHL(materialH.code, materialH.size, materialH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3819 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3820 }
3821 if (shadowFlag == 3) {
3822 // AcDbShadow object handle is separate from the mode encoded
3823 // in shadowFlag. Keep it even though LibreCAD has no render
3824 // consumer for the shadow object itself.
3825 dwgHandle shadowH;
3826 if (!readOffsetHandle(shadowH))
3827 return false;
3828 newShadowHandle = shadowH.ref;
3829 DRW_DBG(" shadow Handle: ")DRW_dbg::dbg(" shadow Handle: "); DRW_DBGHL(shadowH.code, shadowH.size, shadowH.ref)DRW_dbg::dbgHL(shadowH.code, shadowH.size, shadowH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3830 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3831 }
3832 }
3833 if (plotFlags == 3) {
3834 dwgHandle plotStyleH;
3835 if (!readOffsetHandle(plotStyleH))
3836 return false;
3837 newPlotStyle = static_cast<int>(plotStyleH.ref);
3838 DRW_DBG(" plot style Handle: ")DRW_dbg::dbg(" plot style Handle: "); DRW_DBGHL(plotStyleH.code, plotStyleH.size, plotStyleH.ref)DRW_dbg::dbgHL(plotStyleH.code, plotStyleH.size, plotStyleH.ref
)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
3839 DRW_DBG("\n Remaining bytes: ")DRW_dbg::dbg("\n Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
3840 }
3841 if (version > DRW::AC1021) {//2010+ — §19.4.2: visual-style handles
3842 // Ground-truth: libreDWG common_entity_handle_data.spec lines
3843 // 141-150 and ODA spec v5.4.1 §19.4.2. Order matches: full,
3844 // face, edge — each conditional on its single-bit flag from
3845 // §19.4.1 (set in parseDwg above). All three are hard pointers
3846 // (libreDWG FIELD_HANDLE code 5), matching the existing
3847 // material/shadow/plotstyle handles in this block.
3848 if (hasFullVisualStyle) {
3849 dwgHandle h;
3850 if (!readOffsetHandle(h))
3851 return false;
3852 newFullVisualStyleHandle = h.ref;
3853 DRW_DBG(" full visual-style H: ")DRW_dbg::dbg(" full visual-style H: ");
3854 DRW_DBGHL(h.code, h.size, h.ref)DRW_dbg::dbgHL(h.code, h.size, h.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3855 }
3856 if (hasFaceVisualStyle) {
3857 dwgHandle h;
3858 if (!readOffsetHandle(h))
3859 return false;
3860 newFaceVisualStyleHandle = h.ref;
3861 DRW_DBG(" face visual-style H: ")DRW_dbg::dbg(" face visual-style H: ");
3862 DRW_DBGHL(h.code, h.size, h.ref)DRW_dbg::dbgHL(h.code, h.size, h.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3863 }
3864 if (hasEdgeVisualStyle) {
3865 dwgHandle h;
3866 if (!readOffsetHandle(h))
3867 return false;
3868 newEdgeVisualStyleHandle = h.ref;
3869 DRW_DBG(" edge visual-style H: ")DRW_dbg::dbg(" edge visual-style H: ");
3870 DRW_DBGHL(h.code, h.size, h.ref)DRW_dbg::dbgHL(h.code, h.size, h.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
3871 }
3872 }
3873 }
3874 const int rb = buf->numRemainingBytes();
3875 DRW_DBG("\n DRW_Entity::parseDwgEntHandle Remaining bytes: ")DRW_dbg::dbg("\n DRW_Entity::parseDwgEntHandle Remaining bytes: "
)
; DRW_DBG(rb)DRW_dbg::dbg(rb); DRW_DBG("\n")DRW_dbg::dbg("\n");
3876 if (rb > 4) { // 2-byte CRC + slack
3877 DRW_DBG("\n*** parseDwgEntHandle leftover ")DRW_dbg::dbg("\n*** parseDwgEntHandle leftover ");
3878 DRW_DBG(rb)DRW_dbg::dbg(rb);
3879 DRW_DBG(" bytes; entity handle ")DRW_dbg::dbg(" bytes; entity handle ");
3880 DRW_DBGH(handle)DRW_dbg::dbgH(handle);
3881 DRW_DBG(" oType ")DRW_dbg::dbg(" oType ");
3882 DRW_DBG(oType)DRW_dbg::dbg(oType);
3883 DRW_DBG(" — possible bit-stream misalignment ***\n")DRW_dbg::dbg(" — possible bit-stream misalignment ***\n");
3884 }
3885 if (!buf->isGood()
3886 || (handleEndBit != std::numeric_limits<std::uint64_t>::max()
3887 && currentDwgBit(buf) > handleEndBit))
3888 return false;
3889
3890 acDbColorHandle = newAcDbColorHandle;
3891 parentHandle = newParentHandle;
3892 reactorHandles = std::move(newReactorHandles);
3893 xDictHandle = newXDictHandle;
3894 layerH = newLayerH;
3895 lTypeH = newLTypeH;
3896 nextEntLink = newNextEntLink;
3897 prevEntLink = newPrevEntLink;
3898 material = newMaterial;
3899 plotStyle = newPlotStyle;
3900 shadow = newShadow;
3901 if (version <= DRW::AC1018) {
3902 materialFlag = 0;
3903 shadowFlag = 0;
3904 }
3905 shadowHandle = newShadowHandle;
3906 if (version <= DRW::AC1021) {
3907 hasFullVisualStyle = false;
3908 hasFaceVisualStyle = false;
3909 hasEdgeVisualStyle = false;
3910 }
3911 fullVisualStyleHandle = newFullVisualStyleHandle;
3912 faceVisualStyleHandle = newFaceVisualStyleHandle;
3913 edgeVisualStyleHandle = newEdgeVisualStyleHandle;
3914 // R2007+ separates this tail from the object data stream. Earlier typed
3915 // parsers may consume inline handles but cannot give raw metadata an
3916 // independently bounded common-link proof.
3917 commonLinkTailValidated = version > DRW::AC1018;
3918 return true;
3919}
3920
3921bool DRW_Entity::parseDwgCommon(DRW::Version version, dwgBuffer *buf,
3922 std::uint32_t bs) {
3923 if (buf == nullptr)
3924 return false;
3925 // R2007 (AC1021) omits the footer when an object has no string stream;
3926 // the concrete readers using this helper have no detached text fields.
3927 if (version <= DRW::AC1021)
3928 return parseDwg(version, buf, nullptr, bs);
3929
3930 // Keep the publishing cursor transactional: a missing string footer is
3931 // legal for body-only R2007-era inputs and for the low-level entity test
3932 // API, but must not leave the cursor half-consumed before that fallback.
3933 dwgBuffer bodyProbe = buf->forkIndependent();
3934 dwgBuffer stringProbe = bodyProbe.forkIndependent();
3935 if (parseDwg(version, &bodyProbe, &stringProbe, bs)) {
3936 *buf = bodyProbe;
3937 return true;
3938 }
3939 return parseDwg(version, buf, nullptr, bs);
3940}
3941
3942bool DRW_ProxyEntity::parseDwg(DRW::Version version, dwgBuffer *buf,
3943 std::uint32_t bs) {
3944 if (buf == nullptr || version < DRW::AC1012)
3945 return false;
3946
3947 DRW_Entity::reset();
3948 eType = DRW::PROXYENTITY;
3949 m_hasProxyClassId = false;
3950 m_proxyClassId = 0;
3951 m_hasProxyCarrierId = false;
3952 m_proxyCarrierId = 0;
3953 m_proxySubclass.clear();
3954 m_hasProxyDrawingFormat = false;
3955 m_proxyDrawingFormat = 0;
3956 m_proxyDwgVersion = 0;
3957 m_proxyMaintenanceVersion = 0;
3958 m_hasFromDxf = false;
3959 m_fromDxf = false;
3960 m_hasProxyGraphicsByteSize = false;
3961 m_proxyGraphicsByteSize = 0;
3962 m_hasEntityDataBitSize = false;
3963 m_entityData.clear();
3964 m_entityDataBitSize = 0;
3965 m_hasUnknownDataByteSize = false;
3966 m_unknownDataByteSize = 0;
3967 m_unknownData.clear();
3968 m_objectIdRefs.clear();
3969
3970 dwgBuffer stringBuffer = *buf;
3971 dwgBuffer *stringBuf = version > DRW::AC1018 ? &stringBuffer : buf;
3972 if (!DRW_Entity::parseDwg(version, buf, stringBuf, bs))
3973 return false;
3974
3975 std::uint64_t dataEndBit = 0;
3976 std::uint64_t stringEndBit = 0;
3977 if (!entityBodyDataEndBit(*stringBuf, version, objSize, dataEndBit,
3978 &stringEndBit)
3979 || dataEndBit > proxyEntityEndBit(*buf, 0))
3980 return false;
3981
3982 bool present = false;
3983 std::int32_t value = 0;
3984 if (!readProxyEntityOptionalBitLong(*buf, dataEndBit, present, value))
3985 return false;
3986 if (present) {
3987 m_hasProxyCarrierId = true;
3988 m_proxyCarrierId = value;
3989 }
3990
3991 if (version >= DRW::AC1018
3992 && proxyEntityHasBits(*stringBuf, stringEndBit, 2)) {
3993 if (!readBoundedVariableText(*stringBuf, stringEndBit, version,
3994 m_proxySubclass))
3995 return false;
3996 }
3997
3998 if (version >= DRW::AC1032) {
3999 bool hasDwgVersion = false;
4000 bool hasMaintenanceVersion = false;
4001 std::int32_t dwgVersion = 0;
4002 std::int32_t maintenanceVersion = 0;
4003 if (!readProxyEntityOptionalBitLong(*buf, dataEndBit,
4004 hasDwgVersion, dwgVersion)
4005 || !readProxyEntityOptionalBitLong(*buf, dataEndBit,
4006 hasMaintenanceVersion,
4007 maintenanceVersion)) {
4008 return false;
4009 }
4010 if (hasDwgVersion || hasMaintenanceVersion) {
4011 m_hasProxyDrawingFormat = true;
4012 m_proxyDwgVersion = static_cast<std::uint16_t>(dwgVersion);
4013 m_proxyMaintenanceVersion =
4014 static_cast<std::uint16_t>(maintenanceVersion);
4015 m_proxyDrawingFormat =
4016 (static_cast<std::uint32_t>(m_proxyMaintenanceVersion) << 16u)
4017 | m_proxyDwgVersion;
4018 }
4019 } else if (!readProxyEntityOptionalBitLong(*buf, dataEndBit,
4020 present, value)) {
4021 return false;
4022 } else if (present) {
4023 m_hasProxyDrawingFormat = true;
4024 m_proxyDrawingFormat = static_cast<std::uint32_t>(value);
4025 m_proxyDwgVersion = static_cast<std::uint16_t>(value);
4026 m_proxyMaintenanceVersion = static_cast<std::uint16_t>(
4027 static_cast<std::uint32_t>(value) >> 16u);
4028 }
4029
4030 if (version >= DRW::AC1015 && proxyEntityHasBits(*buf, dataEndBit, 1)) {
4031 if (!readBoundedBit(*buf, dataEndBit, m_fromDxf))
4032 return false;
4033 m_hasFromDxf = true;
4034 }
4035
4036 const std::uint64_t payloadStartBit = currentDwgBit(buf);
4037 if (payloadStartBit > dataEndBit
4038 || !copyDwgBitRange(*buf, payloadStartBit, dataEndBit, m_entityData)) {
4039 return false;
4040 }
4041 m_entityDataBitSize = static_cast<std::uint32_t>(
4042 dataEndBit - payloadStartBit);
4043
4044 dwgBuffer handleBuffer = *buf;
4045 if (!handleBuffer.setPosition(dataEndBit >> 3))
4046 return false;
4047 handleBuffer.setBitPos(static_cast<std::uint8_t>(dataEndBit & 7u));
4048 if (!handleBuffer.isGood())
4049 return false;
4050 std::uint64_t handleEndBit = 0;
4051 if (!dwgHandleStreamEndBit(*buf, version, objSize, bs, handleEndBit)
4052 || !DRW_Entity::parseDwgEntHandle(version, &handleBuffer, true,
4053 handleEndBit))
4054 return false;
4055 while (proxyEntityHasBits(handleBuffer, handleEndBit, 8)) {
4056 dwgHandle rawHandle;
4057 dwgHandle resolvedHandle;
4058 if (!readBoundedDwgObjectId(handleBuffer, handleEndBit, handle,
4059 rawHandle, resolvedHandle))
4060 return false;
4061 if (rawHandle.code == 0 && rawHandle.ref64 == 0)
4062 break;
4063
4064 DRW_ProxyObjectIdRef ref;
4065 ref.m_dxfCode = proxyEntityDxfCode(rawHandle.code);
4066 ref.m_handleCode = rawHandle.code;
4067 ref.m_handle = resolvedHandle.ref64;
4068 ref.m_rawHandle = rawHandle.ref64;
4069 m_objectIdRefs.push_back(ref);
4070 }
4071
4072 return buf->isGood() && stringBuf->isGood() && handleBuffer.isGood();
4073}
4074
4075bool DRW_Point::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
4076 switch (code) {
4077 case 10:
4078 basePoint.x = reader->getDouble();
4079 break;
4080 case 20:
4081 basePoint.y = reader->getDouble();
4082 break;
4083 case 30:
4084 basePoint.z = reader->getDouble();
4085 break;
4086 case 39:
4087 thickness = reader->getDouble();
4088 break;
4089 case 50:
4090 // DXF code 50 is in degrees; the field is radians (matching the DWG
4091 // path, drw_entities.cpp:1787). degrees -> radians is /ARAD (x pi/180);
4092 // the prior *ARAD only canceled with the writer's /ARAD on DXF->DXF and
4093 // corrupted the value for DXF->DWG. ARAD = 180/pi.
4094 xAxisAngle = reader->getDouble() / ARAD57.29577951308232; // DXF degrees -> radians
4095 break;
4096 case 210:
4097 haveExtrusion = true;
4098 extPoint.x = reader->getDouble();
4099 break;
4100 case 220:
4101 extPoint.y = reader->getDouble();
4102 break;
4103 case 230:
4104 extPoint.z = reader->getDouble();
4105 break;
4106 default:
4107 return DRW_Entity::parseCode(code, reader);
4108 }
4109
4110 return true;
4111}
4112
4113// ---------------------------------------------------------------------------
4114// Phase 4a (drafted 2026-05-15)
4115// ---------------------------------------------------------------------------
4116// `DRW_Entity::encodeDwgCommon` and `encodeDwgEntHandle` mirror the common
4117// entity-header fragments parsed above. Version gates intentionally follow
4118// the DWG reader so newer headers retain their reference flags and handles.
4119//
4120// Discarded fields (Risk 4i):
4121// - haveNextLinks B — we emit 1 (no prev/next chain).
4122// - none of the common color side channels are discarded for R2004+;
4123// ENC names remain the byte-oriented TV fields defined by the DWG
4124// common-entity specification, while ordinary entity text uses strBuf.
4125//
4126// We still emit these fixed defaults:
4127// - entmode = 2 (modelspace, no owner-handle in stream — caller can
4128// override before calling encodeDwgCommon if entity needs an owner).
4129// - numReactors = 0 (reactor handles are emitted by the handle phase when
4130// present).
4131// - ltFlags = 0 (BYLAYER); plotFlags follows plotStyle.
4132// - invisibleFlag = 0 (visible)
4133//
4134// Caller must:
4135// - Pre-populate `eType`, `handle`, `color`, `ltypeScale`, `lWeight`,
4136// `layerH.ref` (handle of the layer this entity belongs to).
4137// - The body emit between encodeDwgCommon and encodeDwgEntHandle is
4138// per-entity (3BD basePoint for Point, etc.).
4139
4140// Phase-2a kill switch for the full common-entity-header write contract
4141// (entity reactors/xdict/EED/visibility/entmode emission). Default ON. The
4142// emission is gated by DATA PRESENCE (empty reactorHandles/extData + visible
4143// == today's hardcoded zeros), so flipping this OFF restores the legacy
4144// byte-identical output as an emergency escape hatch. The per-field emission
4145// arms land in 2a.1..2a.5; this scaffolding commit changes no bytes.
4146#ifndef LIBDXFRW_FULL_COMMON_HEADER1
4147#define LIBDXFRW_FULL_COMMON_HEADER1 1
4148#endif
4149
4150namespace {
4151
4152const DRW_Entity::PendingHandleRef* findEedWriteRef(
4153 const std::vector<DRW_Entity::PendingHandleRef>& refs,
4154 std::size_t index) {
4155 const auto it = std::find_if(refs.cbegin(), refs.cend(),
4156 [index](const auto& ref) {
4157 return ref.indexInExtData == index;
4158 });
4159 if (it == refs.cend())
4160 return nullptr;
4161 return &*it;
4162}
4163
4164bool parseEedHexHandle(const std::string& text, std::uint64_t& handle) {
4165 if (text.empty())
4166 return false;
4167 std::size_t index = 0;
4168 if (text.size() > 2 && text[0] == '0'
4169 && (text[1] == 'x' || text[1] == 'X')) {
4170 index = 2;
4171 }
4172 if (index == text.size())
4173 return false;
4174
4175 std::uint64_t parsed = 0;
4176 for (; index < text.size(); ++index) {
4177 const unsigned char character =
4178 static_cast<unsigned char>(text[index]);
4179 std::uint8_t digit = 0;
4180 if (character >= '0' && character <= '9')
4181 digit = static_cast<std::uint8_t>(character - '0');
4182 else if (character >= 'a' && character <= 'f')
4183 digit = static_cast<std::uint8_t>(character - 'a' + 10);
4184 else if (character >= 'A' && character <= 'F')
4185 digit = static_cast<std::uint8_t>(character - 'A' + 10);
4186 else
4187 return false;
4188 if (parsed > (std::numeric_limits<std::uint64_t>::max() - digit) / 16U)
4189 return false;
4190 parsed = parsed * 16U + digit;
4191 }
4192 handle = parsed;
4193 return true;
4194}
4195
4196void putEedRawHandleBE(dwgBufferW& buffer, std::uint64_t handle) {
4197 for (int index = 7; index >= 0; --index) {
4198 buffer.putRawChar8(static_cast<std::uint8_t>(
4199 (handle >> (index * 8)) & 0xFFU));
4200 }
4201}
4202
4203void putEedRawHandleLE(dwgBufferW& buffer, std::uint64_t handle) {
4204 for (unsigned index = 0; index < 8; ++index) {
4205 buffer.putRawChar8(static_cast<std::uint8_t>(
4206 (handle >> (index * 8U)) & 0xFFU));
4207 }
4208}
4209
4210bool encodeEedItem(DRW::Version version, const DRW_Variant& value,
4211 dwgBufferW& textEncoder, dwgBufferW& output,
4212 std::uint64_t resolvedLayerHandle,
4213 std::uint16_t codePage) {
4214 const int groupCode = value.code();
4215 switch (groupCode) {
4216 case 1000: {
4217 if (value.type() != DRW_Variant::STRING || value.content.s == nullptr)
4218 return false;
4219 const std::string encoded = textEncoder.encodeTextAreaString(
4220 *value.content.s, version > DRW::AC1018);
4221 if (version > DRW::AC1018) {
4222 if ((encoded.size() & 1U) != 0
4223 || encoded.size() / 2U > std::numeric_limits<std::uint16_t>::max()) {
4224 return false;
4225 }
4226 output.putRawChar8(0);
4227 output.putRawShort16(static_cast<std::uint16_t>(encoded.size() / 2U));
4228 } else {
4229 if (encoded.size() > std::numeric_limits<std::uint8_t>::max())
4230 return false;
4231 output.putRawChar8(0);
4232 output.putRawChar8(static_cast<std::uint8_t>(encoded.size()));
4233 output.putBERawShort16(codePage);
4234 }
4235 output.putBytes(reinterpret_cast<const std::uint8_t*>(encoded.data()),
4236 encoded.size());
4237 return output.isGood();
4238 }
4239 case 1002: {
4240 if (value.type() != DRW_Variant::STRING || value.content.s == nullptr
4241 || (*value.content.s != "{" && *value.content.s != "}")) {
4242 return false;
4243 }
4244 output.putRawChar8(2);
4245 output.putRawChar8(*value.content.s == "{" ? 0 : 1);
4246 return output.isGood();
4247 }
4248 case 1003:
4249 if (value.type() != DRW_Variant::STRING || resolvedLayerHandle == 0)
4250 return false;
4251 output.putRawChar8(3);
4252 putEedRawHandleLE(output, resolvedLayerHandle);
4253 return output.isGood();
4254 case 1004: {
4255 if (value.type() != DRW_Variant::BINARY || value.binary() == nullptr
4256 || value.binary()->size() > std::numeric_limits<std::uint8_t>::max()) {
4257 return false;
4258 }
4259 output.putRawChar8(4);
4260 output.putRawChar8(static_cast<std::uint8_t>(value.binary()->size()));
4261 output.putBytes(value.binary()->data(), value.binary()->size());
4262 return output.isGood();
4263 }
4264 case 1005: {
4265 if (value.type() != DRW_Variant::STRING || value.content.s == nullptr)
4266 return false;
4267 std::uint64_t handle = 0;
4268 if (!parseEedHexHandle(*value.content.s, handle))
4269 return false;
4270 output.putRawChar8(5);
4271 putEedRawHandleBE(output, handle);
4272 return output.isGood();
4273 }
4274 case 1010:
4275 case 1011:
4276 case 1012:
4277 case 1013: {
4278 if (value.type() != DRW_Variant::COORD || value.coord() == nullptr)
4279 return false;
4280 const DRW_Coord& coordinate = *value.coord();
4281 if (!std::isfinite(coordinate.x) || !std::isfinite(coordinate.y)
4282 || !std::isfinite(coordinate.z)) {
4283 return false;
4284 }
4285 output.putRawChar8(static_cast<std::uint8_t>(groupCode - 1000));
4286 output.putRawDouble(coordinate.x);
4287 output.putRawDouble(coordinate.y);
4288 output.putRawDouble(coordinate.z);
4289 return output.isGood();
4290 }
4291 case 1040:
4292 case 1041:
4293 case 1042:
4294 if (value.type() != DRW_Variant::DOUBLE || !std::isfinite(value.d_val()))
4295 return false;
4296 output.putRawChar8(static_cast<std::uint8_t>(groupCode - 1000));
4297 output.putRawDouble(value.d_val());
4298 return output.isGood();
4299 case 1070:
4300 if (value.type() != DRW_Variant::INTEGER
4301 || value.i_val() < std::numeric_limits<std::int16_t>::min()
4302 || value.i_val() > std::numeric_limits<std::int16_t>::max()) {
4303 return false;
4304 }
4305 output.putRawChar8(70);
4306 output.putRawShort16(static_cast<std::uint16_t>(
4307 static_cast<std::int16_t>(value.i_val())));
4308 return output.isGood();
4309 case 1071: {
4310 std::int64_t integer = 0;
4311 if (value.type() == DRW_Variant::INTEGER)
4312 integer = value.i_val();
4313 else if (value.type() == DRW_Variant::INTEGER64)
4314 integer = value.i64_val();
4315 else
4316 return false;
4317 if (integer < std::numeric_limits<std::int32_t>::min()
4318 || integer > std::numeric_limits<std::int32_t>::max()) {
4319 return false;
4320 }
4321 output.putRawChar8(71);
4322 output.putRawLong32(static_cast<std::uint32_t>(
4323 static_cast<std::int32_t>(integer)));
4324 return output.isGood();
4325 }
4326 default:
4327 return false;
4328 }
4329}
4330
4331} // namespace
4332
4333bool DRW_Entity::encodeDwgEed(
4334 DRW::Version version, const std::vector<DRW_Variant*>& extData,
4335 const std::vector<PendingHandleRef>& appIdRefs,
4336 const std::vector<PendingHandleRef>& layerRefs,
4337 std::uint16_t codePage, dwgBufferW& output) {
4338 if (extData.empty()) {
4339 output.putBitShort(0);
4340 return output.isGood();
4341 }
4342 if (extData.size() > dwgSafety::MaxEedTotalItems)
4343 return false;
4344
4345 std::size_t currentAppStart = 0;
4346 std::uint32_t appHandle = 0;
4347 std::uint32_t groupDepth = 0;
4348 std::uint32_t chunkCount = 0;
4349 dwgBufferW payload;
4350 const auto flush = [&]() {
4351 if (payload.data().empty() || payload.data().size()
4352 > std::numeric_limits<std::uint16_t>::max()
4353 || appHandle == 0 || ++chunkCount > dwgSafety::MaxEedChunks) {
4354 return false;
4355 }
4356 output.putBitShort(static_cast<std::uint16_t>(payload.data().size()));
4357 dwgHandle appId;
4358 appId.code = 5;
4359 appId.ref = appHandle;
4360 output.putHandle(appId);
4361 output.putBytes(payload.data().data(), payload.data().size());
4362 payload.reset();
4363 return output.isGood();
4364 };
4365
4366 for (std::size_t index = 0; index < extData.size(); ++index) {
4367 const DRW_Variant* value = extData[index];
4368 if (value == nullptr)
4369 return false;
4370 if (value->code() == 1001) {
4371 if (index != currentAppStart) {
4372 if (groupDepth != 0 || !flush())
4373 return false;
4374 }
4375 const PendingHandleRef* appIdRef = findEedWriteRef(appIdRefs, index);
4376 if (appIdRef == nullptr || appIdRef->handleRef == 0) {
4377 return false;
4378 }
4379 appHandle = appIdRef->handleRef;
4380 currentAppStart = index + 1;
4381 groupDepth = 0;
4382 continue;
4383 }
4384 if (index < currentAppStart)
4385 return false;
4386 if (value->code() == 1002) {
4387 if (value->type() != DRW_Variant::STRING || value->content.s == nullptr)
4388 return false;
4389 if (*value->content.s == "{") {
4390 ++groupDepth;
4391 } else if (*value->content.s == "}") {
4392 if (groupDepth == 0)
4393 return false;
4394 --groupDepth;
4395 } else {
4396 return false;
4397 }
4398 }
4399
4400 std::uint64_t layerHandle = 0;
4401 if (value->code() == 1003) {
4402 const PendingHandleRef* layerRef = findEedWriteRef(layerRefs, index);
4403 if (layerRef == nullptr)
4404 return false;
4405 layerHandle = layerRef->useRawHandleRef
4406 ? layerRef->rawHandleRef : layerRef->handleRef;
4407 if (layerHandle == 0)
4408 return false;
4409 }
4410 dwgBufferW item;
4411 if (!encodeEedItem(version, *value, output, item, layerHandle,
4412 codePage))
4413 return false;
4414 if (item.data().size() > std::numeric_limits<std::uint16_t>::max())
4415 return false;
4416 if (!payload.data().empty()
4417 && payload.data().size() + item.data().size()
4418 > std::numeric_limits<std::uint16_t>::max()) {
4419 if (groupDepth != 0 || !flush())
4420 return false;
4421 }
4422 payload.putBytes(item.data().data(), item.data().size());
4423 }
4424 if (currentAppStart == 0 || groupDepth != 0 || !flush())
4425 return false;
4426 output.putBitShort(0);
4427 return output.isGood();
4428}
4429
4430bool DRW_Entity::encodeDwgCommon(DRW::Version version, dwgBufferW *buf,
4431 dwgBufferW *strBuf) {
4432 (void)strBuf; // ENC names are byte-oriented TV fields in the data stream
4433 if (buf == nullptr || reactorHandles.size() > dwgSafety::MaxReactorCount ||
4434 (version != DRW::AC1015 && version != DRW::AC1018 &&
4435 version != DRW::AC1021 &&
4436 version != DRW::AC1024 && version != DRW::AC1027 &&
4437 version != DRW::AC1032)) return false;
4438
4439 // Object type: BS for AC1015/AC1018, OT for AC1024+.
4440 buf->putObjType(version, static_cast<std::uint16_t>(oType));
4441
4442 // objSize stub — back-patched for AC1015/AC1018 only. AC1024 derives
4443 // objSize from the body buffer size, so no RL is emitted.
4444 if (version < DRW::AC1024) {
4445 buf->putRawLong32(0);
4446 }
4447
4448 // Own handle: code 0 per spec §20.4.1.
4449 dwgHandle ownH;
4450 ownH.code = 0;
4451 ownH.ref = handle;
4452 ownH.size = 0;
4453 if (handle != 0) {
4454 std::uint32_t t = handle;
4455 while (t != 0) { t >>= 8; ++ownH.size; }
4456 }
4457 buf->putHandle(ownH);
4458
4459 std::vector<DRW_Variant*> eedValues;
4460 try {
4461 eedValues.reserve(extData.size());
4462 for (const auto& value : extData)
4463 eedValues.push_back(value.get());
4464 } catch (...) {
4465 return false;
4466 }
4467 if (!encodeDwgEed(version, eedValues, dwgEedAppIdWriteRefs,
4468 dwgEedLayerWriteRefs, dwgEedCodePage, *buf))
4469 return false;
4470
4471 // Proxy graphics lives in the common data stream. Preserve it when the
4472 // entity has a bounded byte stream; it is the cached fallback geometry
4473 // used for unrecognised custom/proxy entities.
4474 if (proxyGraphics.size()
4475 > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()
4476 / 8)) {
4477 return false;
4478 }
4479 const bool hasProxyGraphics = !proxyGraphics.empty();
4480 buf->putBit(hasProxyGraphics);
4481 if (hasProxyGraphics) {
4482 const auto graphDataSize = static_cast<std::uint64_t>(
4483 proxyGraphics.size());
4484 if (version >= DRW::AC1024)
4485 buf->putBitLongLong(graphDataSize);
4486 else
4487 buf->putRawLong32(static_cast<std::uint32_t>(graphDataSize));
4488 buf->putBytes(reinterpret_cast<const std::uint8_t*>(
4489 proxyGraphics.data()),
4490 proxyGraphics.size());
4491 if (!buf->isGood())
4492 return false;
4493 }
4494
4495 const bool hasOwner = parentHandle != DRW::NoHandle;
4496 // entmode BB (ODA §20.4.1 / Open Design FE):
4497 // 0 = owner handle follows in the handle stream
4498 // 1 = paperspace entity without owner-relative handle
4499 // 2 = modelspace entity without owner-relative handle
4500 // Prefer owner when present; otherwise honor DRW_Entity::space.
4501 std::uint8_t entmode = 2;
4502 if (hasOwner)
4503 entmode = 0;
4504 else if (space == DRW::PaperSpace)
4505 entmode = 1;
4506 buf->put2Bits(entmode);
4507
4508 // numReactors (BL per spec §20.4.1). 2a.2: emit the real count; empty
4509 // reactorHandles → 0 → byte-identical to legacy.
4510#if LIBDXFRW_FULL_COMMON_HEADER1
4511 buf->putBitLong(static_cast<std::int32_t>(reactorHandles.size()));
4512#else
4513 buf->putBitLong(0);
4514#endif
4515
4516 // R2004/R2010 (AC1018, AC1024): reader reads xDictFlag bit (version > AC1015)
4517 // then forces haveNextLinks=1 (no bit in stream). We always emit
4518 // xDictFlag=0 (xdic-present) so the reader reads exactly one xdic handle
4519 // in the handle section — we emit the real handle when xDictHandle!=0 and
4520 // a null handle otherwise. This keeps the empty case byte-identical to the
4521 // legacy path (bit 0 + null handle) while round-tripping a real xdict.
4522 // R2000 (AC1015): no xDictFlag bit; reader's xDictFlag stays 0 so it ALWAYS
4523 // reads an xdic handle — same emit rule applies.
4524 // R2013+ (AC1027+): reader reads xDictFlag then reads haveNextLinks (bit restored).
4525 if (version == DRW::AC1015) {
4526 buf->putBit(1); // nolinks=1 (R2000: no prev/next chain)
4527 } else {
4528 buf->putBit(0); // xDictFlag=0 (xdic present; real-or-null handle follows)
4529 if (version > DRW::AC1024) {
4530 // libreDWG common_entity_data.spec — R2013+ has_ds_data (B). libdxfrw
4531 // never inlines an ACIS SAB datastore, so emit hasDsData (default 0).
4532 // The old code emitted literal 1 (mislabeled haveNextLinks), falsely
4533 // advertising an SAB blob and risking misparse in strict readers.
4534 buf->putBit(hasDsData);
4535 }
4536 }
4537
4538 // ENC color. R2004+ retains the true-color, transparency, DBCOLOR, and
4539 // byte-oriented color/book-name side channels; AC1015 falls back to BS.
4540 UTF8STRINGstd::string encColorName = colorName;
4541 UTF8STRINGstd::string encBookName;
4542 const std::string::size_type separator = encColorName.find('$');
4543 if (separator != std::string::npos) {
4544 encBookName = encColorName.substr(0, separator);
4545 encColorName.erase(0, separator + 1);
4546 }
4547 buf->putEnColor(version, static_cast<std::uint16_t>(color), color24,
4548 encColorName, encBookName,
4549 static_cast<std::uint32_t>(transparency),
4550 version >= DRW::AC1018 && hasAcDbColorH);
4551
4552 // ltypeScale BD.
4553 buf->putBitDouble(ltypeScale);
4554
4555 // ltFlags BB: 0=BYLAYER, 1=BYBLOCK, 2=CONTINUOUS, 3=lTypeH present.
4556 // Prefer an already-set ltFlags; otherwise derive from lineType / lTypeH.
4557 {
4558 auto upper = [](std::string s) {
4559 for (char& c : s)
4560 c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
4561 return s;
4562 };
4563 std::uint8_t flags = ltFlags;
4564 if (flags > 3)
4565 flags = 0;
4566 if (flags == 0 && lTypeH.ref != 0)
4567 flags = 3;
4568 if (flags == 0) {
4569 const std::string lt = upper(lineType);
4570 if (lt.empty() || lt == "BYLAYER")
4571 flags = 0;
4572 else if (lt == "BYBLOCK")
4573 flags = 1;
4574 else if (lt == "CONTINUOUS")
4575 flags = 2;
4576 else
4577 flags = 3; // named linetype — handle required
4578 }
4579 ltFlags = flags;
4580 }
4581 buf->put2Bits(ltFlags);
4582 // plotFlags BB: a nonzero plot-style handle requires the hard-pointer
4583 // handle in the common handle stream. Keep the default zero path
4584 // unchanged for entities without an override.
4585 plotFlags = plotStyle > 0 ? 3 : 0;
4586 buf->put2Bits(plotFlags);
4587
4588 // R2010 (AC1024): materialFlag BB + shadowFlag RC (version > AC1018).
4589 if (version > DRW::AC1018) {
4590 materialFlag = material == DRW::MaterialByLayer ? 0 : 3;
4591 shadowFlag = static_cast<std::uint8_t>(shadow) & 0x3;
4592 buf->put2Bits(materialFlag);
4593 buf->putRawChar8(shadowFlag);
4594 }
4595
4596 // R2010 (AC1024): three visual-style flag bits (version > AC1021).
4597 if (version > DRW::AC1021) {
4598 hasFullVisualStyle = fullVisualStyleHandle != 0;
4599 hasFaceVisualStyle = faceVisualStyleHandle != 0;
4600 hasEdgeVisualStyle = edgeVisualStyleHandle != 0;
4601 buf->putBit(hasFullVisualStyle);
4602 buf->putBit(hasFaceVisualStyle);
4603 buf->putBit(hasEdgeVisualStyle);
4604 }
4605
4606 // invisibleFlag BS (DXF 60). 2a.1: emit from `visible` (bit 0 = invisible)
4607 // instead of a hardcoded 0. visible==true → 0 → byte-identical to legacy.
4608#if LIBDXFRW_FULL_COMMON_HEADER1
4609 buf->putBitShort(visible ? 0 : 1);
4610#else
4611 buf->putBitShort(0);
4612#endif
4613
4614 // lWeight RC (0 = byLayer per DRW_LW_Conv).
4615 buf->putRawChar8(static_cast<std::uint8_t>(lWeight));
4616
4617 return buf->isGood();
4618}
4619
4620bool DRW_Entity::encodeDwgEntHandle(DRW::Version version, dwgBufferW *buf,
4621 dwgBufferW *handleBuf) {
4622 if (buf == nullptr || reactorHandles.size() > dwgSafety::MaxReactorCount ||
4623 (version != DRW::AC1015 && version != DRW::AC1018 &&
4624 version != DRW::AC1021 &&
4625 version != DRW::AC1024 && version != DRW::AC1027 &&
4626 version != DRW::AC1032)) return false;
4627
4628 // For AC1024, handles are directed to handleBuf (the separate handle section);
4629 // for AC1015/AC1018, handles go into buf alongside the data.
4630 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
4631
4632 // Owner handle is present only when encodeDwgCommon emitted entmode=0.
4633 if (parentHandle != DRW::NoHandle) {
4634 dwgHandle owner;
4635 owner.code = 4; // soft pointer owner, read via getOffsetHandle()
4636 owner.ref = parentHandle;
4637 owner.size = 0;
4638 std::uint32_t t = parentHandle;
4639 while (t != 0) { t >>= 8; ++owner.size; }
4640 hb->putHandle(owner);
4641 }
4642
4643 // Reactor handles (2a.2): emitted before xdic, one per numReactors written
4644 // in the DATA section, as ABSOLUTE handles (reader uses getHandle()). Empty
4645 // reactorHandles → nothing emitted → byte-identical to legacy.
4646#if LIBDXFRW_FULL_COMMON_HEADER1
4647 for (std::uint32_t ref : reactorHandles) {
4648 dwgHandle rh;
4649 rh.code = 4; // soft pointer
4650 rh.ref = ref;
4651 rh.size = 0;
4652 if (ref != 0) { std::uint32_t t = ref; while (t != 0) { t >>= 8; ++rh.size; } }
4653 hb->putHandle(rh);
4654 }
4655#endif
4656
4657 // XDic handle — xDictFlag=0 in the DATA section means the reader reads one
4658 // XDicObj handle here: emit the real handle when xDictHandle!=0, else the
4659 // null handle (matching the legacy byte-for-byte for the empty case).
4660 dwgHandle xDic;
4661 xDic.code = 3;
4662#if LIBDXFRW_FULL_COMMON_HEADER1
4663 xDic.ref = xDictHandle;
4664 xDic.size = 0;
4665 if (xDictHandle != 0) {
4666 std::uint32_t t = xDictHandle; while (t != 0) { t >>= 8; ++xDic.size; }
4667 }
4668#else
4669 xDic.ref = 0;
4670 xDic.size = 0;
4671#endif
4672 hb->putHandle(xDic);
4673
4674 // R2004+ stores the optional AcDbColor reference after the common owner,
4675 // reactor, and xdictionary handles. Keep this order aligned with the
4676 // reader and the cross-project handle-stream contract.
4677 if (version > DRW::AC1015 && hasAcDbColorH)
4678 putHardPointerHandle(hb, acDbColorHandle);
4679
4680 // Layer handle (R2000+ unconditional). Hard pointer.
4681 dwgHandle lH;
4682 lH.code = layerH.ref == 0 ? 0 : 5; // 5 = hard pointer for layer ref
4683 lH.ref = layerH.ref;
4684 lH.size = 0;
4685 if (lH.ref != 0) {
4686 std::uint32_t t = lH.ref;
4687 while (t != 0) { t >>= 8; ++lH.size; }
4688 }
4689 hb->putHandle(lH);
4690
4691 // ltFlags=3 → lTypeH (hard pointer, code 5) present; else omit.
4692 if (ltFlags == 3) {
4693 dwgHandle ltH;
4694 ltH.code = lTypeH.ref == 0 ? 0 : 5;
4695 ltH.ref = lTypeH.ref;
4696 ltH.size = 0;
4697 if (ltH.ref != 0) {
4698 std::uint32_t t = ltH.ref;
4699 while (t != 0) { t >>= 8; ++ltH.size; }
4700 }
4701 hb->putHandle(ltH);
4702 }
4703 auto putHardPointer = [hb](std::uint32_t ref) {
4704 dwgHandle h;
4705 h.code = ref == 0 ? 0 : 5;
4706 h.ref = ref;
4707 h.size = 0;
4708 if (ref != 0) {
4709 std::uint32_t t = ref;
4710 while (t != 0) {
4711 t >>= 8;
4712 ++h.size;
4713 }
4714 }
4715 hb->putHandle(h);
4716 };
4717
4718 if (version > DRW::AC1018) {
4719 if (materialFlag == 3)
4720 putHardPointer(material);
4721 if (shadowFlag == 3)
4722 putHardPointer(shadowHandle);
4723 }
4724 if (plotFlags == 3)
4725 putHardPointer(static_cast<std::uint32_t>(plotStyle));
4726 if (version > DRW::AC1021) {
4727 if (hasFullVisualStyle)
4728 putHardPointer(fullVisualStyleHandle);
4729 if (hasFaceVisualStyle)
4730 putHardPointer(faceVisualStyleHandle);
4731 if (hasEdgeVisualStyle)
4732 putHardPointer(edgeVisualStyleHandle);
4733 }
4734
4735 return buf->isGood() && hb->isGood();
4736}
4737
4738bool DRW_Point::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
4739 (void)bs; (void)strBuf;
4740 oType = 27; // POINT class id — see dwgreader.cpp:1111 dispatch
4741 if (!encodeDwgCommon(version, buf)) return false;
4742
4743 // Point body — mirror of DRW_Point::parseDwg below.
4744 buf->putBitDouble(basePoint.x);
4745 buf->putBitDouble(basePoint.y);
4746 buf->putBitDouble(basePoint.z);
4747 buf->putThickness(thickness, /*b_R2000_style=*/true);
4748 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
4749 buf->putBitDouble(xAxisAngle); // ODA §20.4.31 code 50
4750
4751 return encodeDwgEntHandle(version, buf, handleBuf);
4752}
4753
4754void DRW_Point::resetDwgState() {
4755 DRW_Entity::reset();
4756 basePoint = DRW_Coord{0.0, 0.0, 0.0};
4757 thickness = 0.0;
4758 extPoint = DRW_Coord{0.0, 0.0, 1.0};
4759 xAxisAngle = 0.0;
4760}
4761
4762bool DRW_Point::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
4763 resetDwgState();
4764 dwgBuffer *sourceBuf = buf;
4765 auto fail = [this, sourceBuf]() {
4766 if (sourceBuf != nullptr)
4767 sourceBuf->invalidate();
4768 resetDwgState();
4769 return false;
4770 };
4771 if (sourceBuf == nullptr)
4772 return fail();
4773
4774 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
4775 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
4776 || !bodyProbe.isGood())
4777 return fail();
4778 const std::uint64_t bodyEndBit = dwgDataEndBit;
4779 DRW_Coord parsedBasePoint;
4780 DRW_Coord parsedExtrusion;
4781 double parsedThickness = 0.0;
4782 double parsedXAxisAngle = 0.0;
4783 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint)
4784 || !readBoundedThickness(bodyProbe, bodyEndBit,
4785 version > DRW::AC1014, parsedThickness)
4786 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
4787 version > DRW::AC1014, parsedExtrusion)
4788 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedXAxisAngle)
4789 || !std::isfinite(parsedBasePoint.x)
4790 || !std::isfinite(parsedBasePoint.y)
4791 || !std::isfinite(parsedBasePoint.z)
4792 || !std::isfinite(parsedThickness)
4793 || !std::isfinite(parsedExtrusion.x)
4794 || !std::isfinite(parsedExtrusion.y)
4795 || !std::isfinite(parsedExtrusion.z)
4796 || !std::isfinite(parsedXAxisAngle))
4797 return fail();
4798
4799 std::uint64_t handleEndBit = 0;
4800 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
4801 handleEndBit))
4802 return fail();
4803 dwgBuffer handleProbe = bodyProbe.forkIndependent();
4804 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
4805 handleEndBit)
4806 || !handleProbe.isGood())
4807 return fail();
4808
4809 basePoint = parsedBasePoint;
4810 thickness = parsedThickness;
4811 extPoint = parsedExtrusion;
4812 xAxisAngle = parsedXAxisAngle;
4813 *sourceBuf = handleProbe;
4814 return true;
4815}
4816
4817void DRW_Line::resetDwgState() {
4818 DRW_Point::resetDwgState();
4819 secPoint = DRW_Coord{0.0, 0.0, 0.0};
4820}
4821
4822bool DRW_Line::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
4823 switch (code) {
4824 case 11:
4825 secPoint.x = reader->getDouble();
4826 break;
4827 case 21:
4828 secPoint.y = reader->getDouble();
4829 break;
4830 case 31:
4831 secPoint.z = reader->getDouble();
4832 break;
4833 default:
4834 return DRW_Point::parseCode(code, reader);
4835 }
4836
4837 return true;
4838}
4839
4840bool DRW_Line::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
4841 (void)bs; (void)strBuf;
4842 oType = 19; // LINE class id — see dwgreader.cpp:1105
4843 if (!encodeDwgCommon(version, buf)) return false;
4844
4845 // R2000+ Line body — zIsZero shortcut: if both z's are 0, omit the
4846 // z fields entirely. Reader reads `zIsZero` first, then RD x +
4847 // DD secX (default = x), RD y + DD secY (default = y), and
4848 // conditionally RD z + DD secZ. Our putDefaultDouble always emits
4849 // the full RD via code 0b11; reader's getDefaultDouble with code
4850 // 0b11 returns the raw double.
4851 bool zIsZero = (basePoint.z == 0.0 && secPoint.z == 0.0);
4852 buf->putBit(zIsZero ? 1 : 0);
4853 buf->putRawDouble(basePoint.x);
4854 buf->putDefaultDouble(basePoint.x, secPoint.x);
4855 buf->putRawDouble(basePoint.y);
4856 buf->putDefaultDouble(basePoint.y, secPoint.y);
4857 if (!zIsZero) {
4858 buf->putRawDouble(basePoint.z);
4859 buf->putDefaultDouble(basePoint.z, secPoint.z);
4860 }
4861 buf->putThickness(thickness, /*b_R2000_style=*/true);
4862 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
4863
4864 return encodeDwgEntHandle(version, buf, handleBuf);
4865}
4866
4867bool DRW_3DLine::parseDwg(DRW::Version version, dwgBuffer *buf,
4868 std::uint32_t bs) {
4869 resetDwgState();
4870 dwgBuffer *sourceBuf = buf;
4871 auto fail = [this, sourceBuf]() {
4872 if (sourceBuf != nullptr)
4873 sourceBuf->invalidate();
4874 resetDwgState();
4875 return false;
4876 };
4877 if (sourceBuf == nullptr)
4878 return fail();
4879
4880 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
4881 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
4882 || !bodyProbe.isGood())
4883 return fail();
4884
4885 const std::uint64_t bodyEndBit = dwgDataEndBit;
4886 double values[10]{};
4887 for (double& value : values) {
4888 if (!readBoundedRawDouble(bodyProbe, bodyEndBit, value)
4889 || !std::isfinite(value))
4890 return fail();
4891 }
4892
4893 std::uint64_t handleEndBit = 0;
4894 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
4895 handleEndBit))
4896 return fail();
4897 dwgBuffer handleProbe = bodyProbe.forkIndependent();
4898 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
4899 handleEndBit)
4900 || !handleProbe.isGood())
4901 return fail();
4902
4903 basePoint = DRW_Coord{values[0], values[1], values[2]};
4904 secPoint = DRW_Coord{values[3], values[4], values[5]};
4905 extPoint = DRW_Coord{values[6], values[7], values[8]};
4906 thickness = values[9];
4907 *sourceBuf = handleProbe;
4908 return true;
4909}
4910
4911bool DRW_3DLine::encodeDwg(DRW::Version version, dwgBufferW *buf,
4912 std::uint32_t bs, dwgBufferW *strBuf,
4913 dwgBufferW *handleBuf) {
4914 (void)bs;
4915 if (version < DRW::AC1015 || buf == nullptr)
4916 return false;
4917
4918 // 3DLINE is a modern custom class. Its class ordinal is writer-local,
4919 // while the body remains the fixed raw-double layout used by the reader.
4920 oType = kDwgClassNum;
4921 if (!encodeDwgCommon(version, buf, strBuf))
4922 return false;
4923
4924 for (const double coordinate : {basePoint.x, basePoint.y, basePoint.z,
4925 secPoint.x, secPoint.y, secPoint.z,
4926 extPoint.x, extPoint.y, extPoint.z,
4927 thickness}) {
4928 buf->putRawDouble(coordinate);
4929 }
4930
4931 return encodeDwgEntHandle(version, buf, handleBuf);
4932}
4933
4934bool DRW_Circle::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
4935 (void)bs; (void)strBuf;
4936 oType = 18; // CIRCLE class id — see dwgreader.cpp:1099
4937 if (!encodeDwgCommon(version, buf)) return false;
4938
4939 // Circle body — mirror of DRW_Circle::parseDwg.
4940 buf->putBitDouble(basePoint.x);
4941 buf->putBitDouble(basePoint.y);
4942 buf->putBitDouble(basePoint.z);
4943 buf->putBitDouble(radious);
4944 buf->putThickness(thickness, /*b_R2000_style=*/true);
4945 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
4946
4947 return encodeDwgEntHandle(version, buf, handleBuf);
4948}
4949
4950bool DRW_Ray::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
4951 (void)bs; (void)strBuf;
4952 // Ray = 40, Xline = 41 — derive from runtime type so DRW_Xline can
4953 // share this encoder (it inherits from DRW_Ray).
4954 oType = (eType == DRW::XLINE) ? 41 : 40;
4955 if (!encodeDwgCommon(version, buf)) return false;
4956
4957 // 3 BD basePoint + 3 BD vector — same layout as parseDwg.
4958 buf->putBitDouble(basePoint.x);
4959 buf->putBitDouble(basePoint.y);
4960 buf->putBitDouble(basePoint.z);
4961 buf->putBitDouble(secPoint.x);
4962 buf->putBitDouble(secPoint.y);
4963 buf->putBitDouble(secPoint.z);
4964
4965 return encodeDwgEntHandle(version, buf, handleBuf);
4966}
4967
4968bool DRW_Trace::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
4969 (void)bs; (void)strBuf;
4970 oType = 32; // TRACE = 32 — see dwgreader.cpp:1317
4971 if (!encodeDwgCommon(version, buf)) return false;
4972
4973 // Trace body — mirror of parseDwg. Note the unusual layout:
4974 // thickness FIRST, then elevation (basePoint.z) as BD, then 4
4975 // corners as 2RD (z values share basePoint.z).
4976 buf->putThickness(thickness, /*b_R2000_style=*/true);
4977 buf->putBitDouble(basePoint.z);
4978 buf->putRawDouble(basePoint.x);
4979 buf->putRawDouble(basePoint.y);
4980 buf->putRawDouble(secPoint.x);
4981 buf->putRawDouble(secPoint.y);
4982 buf->putRawDouble(thirdPoint.x);
4983 buf->putRawDouble(thirdPoint.y);
4984 buf->putRawDouble(fourPoint.x);
4985 buf->putRawDouble(fourPoint.y);
4986 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
4987
4988 return encodeDwgEntHandle(version, buf, handleBuf);
4989}
4990
4991bool DRW_Spline::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
4992 (void)bs; (void)strBuf;
4993 if (buf == nullptr || !validatePayloadFields())
4994 return false;
4995 oType = 36; // SPLINE class id — see dwgreader.cpp:1329
4996 if (!encodeDwgCommon(version, buf)) return false;
4997 encodeDwgSplineBody(version, buf);
4998 return encodeDwgEntHandle(version, buf, handleBuf);
4999}
5000
5001// Spline body encode: the scenario/degree/knots/ctrl/fit section, WITHOUT the
5002// leading encodeDwgCommon or the trailing encodeDwgEntHandle. Factored out so
5003// DRW_Helix::encodeDwg can reuse the identical payload (Phase 8a-1).
5004// Omits the DXF-only flag70/extrusion (210-230) which the DWG stream never
5005// carries here.
5006void DRW_Spline::encodeDwgSplineBody(DRW::Version version, dwgBufferW *buf) const {
5007 // Scenario:
5008 // 1 = control-point / rational / planar (uses knots + control + weights)
5009 // 2 = fit-point (uses fit points + tangents + tolerance)
5010 // When both lists are populated (e.g. DXF-sourced splines), prefer scenario 1
5011 // (ctrl + knots) and drop the fit list from the DWG stream — scenario 1 has no
5012 // fit-point section, so writing both would corrupt all subsequent entities.
5013 const bool hasFit = !fitlist.empty();
5014 const bool hasCtrl = !controllist.empty();
5015 std::int32_t scenario = (hasFit && !hasCtrl) ? 2 : 1;
5016 if (m_scenario == 1 && hasCtrl) {
5017 scenario = 1;
5018 } else if (m_scenario == 2 && hasFit) {
5019 scenario = 2;
5020 }
5021 buf->putBitLong(scenario);
5022 if (version > DRW::AC1024) {
5023 // splFlag1 bit 0: method = fit points; bit 2: closed;
5024 // bit 3: knotParam participates in R2013+ scenario selection.
5025 std::int32_t splFlag1 = m_splineFlags1;
5026 splFlag1 &= ~(kSplineFlagMethodFitPoints | kSplineFlagUseKnotParameter | kSplineFlagClosed);
5027 if (scenario == 2) {
5028 splFlag1 |= kSplineFlagMethodFitPoints | kSplineFlagUseKnotParameter;
5029 if (flags & 0x01) splFlag1 |= kSplineFlagClosed;
5030 } else {
5031 if (flags & 0x01) splFlag1 |= kSplineFlagClosed;
5032 }
5033 buf->putBitLong(splFlag1);
5034 std::int32_t knotParam = m_knotParam;
5035 if (scenario == 1) {
5036 knotParam = kSplineKnotParamCustom;
5037 } else if (knotParam == kSplineKnotParamCustom) {
5038 knotParam = 0;
5039 }
5040 buf->putBitLong(knotParam);
5041 }
5042 buf->putBitLong(static_cast<std::int32_t>(degree));
5043
5044 if (scenario == 2) {
5045 buf->putBitDouble(tolfit);
5046 buf->put3BitDouble(tgStart);
5047 buf->put3BitDouble(tgEnd);
5048 const std::int32_t nFit = static_cast<std::int32_t>(fitlist.size());
5049 buf->putBitLong(nFit);
5050 } else {
5051 // scenario == 1
5052 // Reader at parseDwg reads three flag bits in this order:
5053 // rational bit (flags bit 2 → 0x04)
5054 // closed bit (flags bit 0 → 0x01)
5055 // periodic bit (flags bit 1 → 0x02)
5056 const bool hasNonDefaultWeights = std::any_of(weightlist.begin(), weightlist.end(), differsFromUnitWeight);
5057 buf->putBit(((flags & 0x4) || hasNonDefaultWeights) ? 1 : 0); // rational
5058 buf->putBit((flags & 0x1) ? 1 : 0); // closed
5059 buf->putBit((flags & 0x2) ? 1 : 0); // periodic
5060 buf->putBitDouble(tolknot);
5061 buf->putBitDouble(tolcontrol);
5062 const std::int32_t nKnots = static_cast<std::int32_t>(knotslist.size());
5063 const std::int32_t nCtrl = static_cast<std::int32_t>(controllist.size());
5064 buf->putBitLong(nKnots);
5065 buf->putBitLong(nCtrl);
5066 // weight bit: caller populates weightlist when each control point
5067 // has a non-default weight (NURBS conics).
5068 bool hasWeights = !weightlist.empty();
5069 buf->putBit(hasWeights ? 1 : 0);
5070 }
5071
5072 // Data sections are scenario-gated to avoid stream corruption:
5073 // parseDwg reads knots+ctrl only for scenario 1, fit only for scenario 2.
5074 if (scenario == 1) {
5075 for (double k : knotslist) buf->putBitDouble(k);
5076 for (size_t i = 0; i < controllist.size(); ++i) {
5077 buf->put3BitDouble(*controllist[i]);
5078 if (!weightlist.empty()) {
5079 double w = (i < weightlist.size()) ? weightlist[i] : 1.0;
5080 buf->putBitDouble(w);
5081 }
5082 }
5083 } else {
5084 for (const auto& fp : fitlist) buf->put3BitDouble(*fp);
5085 }
5086}
5087
5088// DRW_Helix::encodeDwg — spline body (oType = HELIX class 503) + AcDbHelix
5089// trailer, then the common entity handle data. Trailer field order MUST match
5090// DRW_Helix::parseDwg (Phase 8a-1).
5091bool DRW_Helix::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5092 (void)bs; (void)strBuf;
5093 const auto finite = [](const DRW_Coord& point) {
5094 return std::isfinite(point.x) && std::isfinite(point.y)
5095 && std::isfinite(point.z);
5096 };
5097 if (buf == nullptr || !validatePayloadFields()
5098 || !finite(axisBasePt) || !finite(startPt) || !finite(axisVector)
5099 || !std::isfinite(radius) || !std::isfinite(turns)
5100 || !std::isfinite(turnHeight))
5101 return false;
5102 oType = kDwgClassNum; // HELIX custom class 503
5103 if (!encodeDwgCommon(version, buf)) return false;
5104 encodeDwgSplineBody(version, buf);
5105
5106 // AcDbHelix trailer (same order as parseDwg):
5107 buf->putBitLong(m_majorVersion);
5108 buf->putBitLong(m_maintVersion);
5109 buf->put3BitDouble(axisBasePt);
5110 buf->put3BitDouble(startPt);
5111 buf->put3BitDouble(axisVector);
5112 buf->putBitDouble(radius);
5113 buf->putBitDouble(turns);
5114 buf->putBitDouble(turnHeight);
5115 buf->putBit(handedness ? 1 : 0);
5116 buf->putRawChar8(static_cast<std::uint8_t>(constraintType));
5117
5118 return encodeDwgEntHandle(version, buf, handleBuf);
5119}
5120
5121bool DRW_MText::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5122 (void)bs;
5123 oType = 44; // MTEXT class id — see dwgreader.cpp:1215
5124 if (!encodeDwgCommon(version, buf)) return false;
5125
5126 // R2000/R2004/R2010 MTEXT body — mirror of DRW_MText::parseDwg.
5127 buf->put3BitDouble(basePoint); // insertion
5128 buf->put3BitDouble(extPoint); // extrusion
5129 buf->put3BitDouble(secPoint); // X-axis dir
5130 buf->putBitDouble(widthscale); // rect width
5131 if (version > DRW::AC1018) {
5132 buf->putBitDouble(0.0); // rect height, R2007+
5133 }
5134 buf->putBitDouble(height); // text height
5135 buf->putBitShort(static_cast<std::uint16_t>(textgen)); // attachment
5136 buf->putBitShort(static_cast<std::uint16_t>(alignH)); // drawing dir
5137 buf->putBitDouble(0.0); // ext_ht (extents height; undocumented)
5138 buf->putBitDouble(0.0); // ext_wid (extents width; undocumented)
5139 // For AC1024: text goes to string buffer; for AC1015/AC1018: inline.
5140 (strBuf ? strBuf : buf)->putVariableText(version, text);
5141 // R2000+ extras:
5142 buf->putBitShort(linespacingStyle); // linespacing style BS 73
5143 buf->putBitDouble(interlin); // linespacing factor BD
5144 buf->putBit(0); // unknown bit
5145 if (version > DRW::AC1015) { // R2004+: background flags BL
5146 buf->putBitLong(m_backgroundFlags);
5147 if ((m_backgroundFlags & 0x01) || (version >= DRW::AC1032 && (m_backgroundFlags & 0x10))) {
5148 buf->putBitDouble(m_backgroundScale); // BitDouble (matches the read fix)
5149 buf->putCmColor(version, static_cast<std::uint16_t>(m_backgroundColor));
5150 buf->putBitLong(m_backgroundTransparency);
5151 }
5152 }
5153 if (version >= DRW::AC1032) {
5154 buf->putBit(m_r2018IsNotAnnotative ? 1 : 0);
5155 if (m_r2018IsNotAnnotative) {
5156 buf->putBitShort(m_r2018Version);
5157 buf->putBit(m_r2018DefaultFlag ? 1 : 0);
5158 buf->putBitLong(m_r2018Attachment);
5159 buf->put3BitDouble(m_r2018XAxisDir);
5160 buf->put3BitDouble(m_r2018InsertionPoint);
5161 buf->putBitDouble(m_r2018RectWidth);
5162 buf->putBitDouble(m_r2018RectHeight);
5163 buf->putBitDouble(m_r2018ExtentsHeight);
5164 buf->putBitDouble(m_r2018ExtentsWidth);
5165 if (m_r2018ColumnType > 2
5166 || m_r2018ColumnCount < 0
5167 || m_r2018ColumnCount > kMaxMTextColumnHeights
5168 || m_r2018ColumnHeights.size()
5169 > static_cast<std::size_t>(kMaxMTextColumnHeights))
5170 return false;
5171 buf->putBitShort(m_r2018ColumnType);
5172 if (m_r2018ColumnType != 0) {
5173 std::int32_t columnCount = m_r2018ColumnCount;
5174 if (!m_r2018ColumnAutoHeight && m_r2018ColumnType == 2
5175 && !m_r2018ColumnHeights.empty()) {
5176 columnCount = static_cast<std::int32_t>(m_r2018ColumnHeights.size());
5177 }
5178 if (!m_r2018ColumnAutoHeight && m_r2018ColumnType == 2
5179 && static_cast<std::size_t>(columnCount)
5180 != m_r2018ColumnHeights.size())
5181 return false;
5182 buf->putBitLong(columnCount);
5183 buf->putBitDouble(m_r2018ColumnWidth);
5184 buf->putBitDouble(m_r2018ColumnGutter);
5185 buf->putBit(m_r2018ColumnAutoHeight ? 1 : 0);
5186 buf->putBit(m_r2018ColumnFlowReversed ? 1 : 0);
5187 if (!m_r2018ColumnAutoHeight && m_r2018ColumnType == 2) {
5188 for (std::int32_t i = 0; i < columnCount; ++i) {
5189 const double columnHeight = static_cast<size_t>(i) < m_r2018ColumnHeights.size()
5190 ? m_r2018ColumnHeights[static_cast<size_t>(i)]
5191 : 0.0;
5192 buf->putBitDouble(columnHeight);
5193 }
5194 }
5195 }
5196 }
5197 }
5198
5199 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
5200
5201 // styleH — hard pointer to STYLE table record (default STANDARD).
5202 dwgBufferW *hb = handleBuf ? handleBuf : buf;
5203 putHardPointerHandle(hb, (styleH.ref == 0) ? 0x13 : styleH.ref);
5204 if (version >= DRW::AC1032 && m_r2018IsNotAnnotative)
5205 putHardPointerHandle(hb, (m_r2018AppIdHandle == 0) ? 0x14 : m_r2018AppIdHandle);
5206 return buf->isGood() && hb->isGood();
5207}
5208
5209bool DRW_Insert::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5210 (void)bs; (void)strBuf;
5211 if (buf == nullptr)
5212 return false;
5213 const bool hasAttrib = !attribHandles.empty();
5214 if (!hasAttrib) {
5215 if (seqendH.ref != DRW::NoHandle)
5216 return false;
5217 } else {
5218 if (seqendH.ref == DRW::NoHandle)
5219 return false;
5220 if (version < DRW::AC1018) {
5221 if (attribHandles.size() != 2 ||
5222 (attribHandles.front().ref == DRW::NoHandle) !=
5223 (attribHandles.back().ref == DRW::NoHandle)) {
5224 return false;
5225 }
5226 } else if (std::any_of(attribHandles.cbegin(), attribHandles.cend(),
5227 [](const dwgHandle& handle) {
5228 return handle.ref == DRW::NoHandle;
5229 })) {
5230 return false;
5231 }
5232 }
5233 if (attribHandles.size() > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()))
5234 return false;
5235
5236 // 2b.6: emit MINSERT (oType 8) when a column/row grid is present;
5237 // otherwise a plain INSERT (oType 7). The reader keys the grid block off
5238 // oType==8 (parseDwg :3189).
5239 oType = isMInsert() ? 8 : 7;
5240 if (!encodeDwgCommon(version, buf)) return false;
5241
5242 // INSERT body — mirror of DRW_Insert::parseDwg for R2000.
5243 buf->putBitDouble(basePoint.x);
5244 buf->putBitDouble(basePoint.y);
5245 buf->putBitDouble(basePoint.z);
5246
5247 // dataFlags: pick the most compact form based on actual scales.
5248 // 3 → all scales default to 1.0 (no emit)
5249 // 2 → uniform scale (xscale RD only; yscale=zscale=xscale)
5250 // 1 → xscale defaults to 1, yscale/zscale as DD against xscale
5251 // 0 → xscale RD; yscale/zscale as DD against xscale
5252 if (sameStoredDouble(xscale, 1.0) && sameStoredDouble(yscale, 1.0)
5253 && sameStoredDouble(zscale, 1.0)) {
5254 buf->put2Bits(3);
5255 } else if (sameStoredDouble(xscale, yscale) && sameStoredDouble(yscale, zscale)) {
5256 buf->put2Bits(2);
5257 buf->putRawDouble(xscale);
5258 } else if (sameStoredDouble(xscale, 1.0)) {
5259 // xscale is exactly 1.0 (parseDwg leaves it at its 1.0 default and
5260 // never reads it in this branch), so it can be omitted; y and z are
5261 // independent and go through the same DD-against-1.0 path parseDwg
5262 // reads them with.
5263 buf->put2Bits(1);
5264 buf->putDefaultDouble(1.0, yscale);
5265 buf->putDefaultDouble(1.0, zscale);
5266 } else {
5267 // Use dataFlags=0 (general case): RD x + DD y + DD z.
5268 buf->put2Bits(0);
5269 buf->putRawDouble(xscale);
5270 buf->putDefaultDouble(xscale, yscale);
5271 buf->putDefaultDouble(xscale, zscale);
5272 }
5273
5274 buf->putBitDouble(angle); // radians
5275 buf->putExtrusion(extPoint, /*b_R2000_style=*/false);
5276 buf->putBit(hasAttrib ? 1 : 0);
5277 if (hasAttrib && version > DRW::AC1015)
5278 buf->putBitLong(static_cast<std::int32_t>(attribHandles.size()));
5279 // hasAttrib==0 ⇒ the SINCE-R2004 num_owned BL is absent (parse :3184), so
5280 // the MINSERT grid (oType==8) follows the hasAttrib bit directly. Field
5281 // order mirrors parseDwg :3190-3193 (colcount BS, rowcount BS, colspace BD,
5282 // rowspace BD) and libreDWG dwg.spec num_cols/num_rows/col_spacing/row_spacing.
5283 if (oType == 8) { // MINSERT grid
5284 buf->putBitShort(static_cast<std::uint16_t>(colcount));
5285 buf->putBitShort(static_cast<std::uint16_t>(rowcount));
5286 buf->putBitDouble(colspace);
5287 buf->putBitDouble(rowspace);
5288 }
5289
5290 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
5291
5292 // BLOCK_RECORD hard pointer.
5293 dwgHandle bhH;
5294 bhH.code = (blockRecH.ref == 0) ? 0 : 5;
5295 bhH.ref = blockRecH.ref;
5296 bhH.size = 0;
5297 if (bhH.ref != 0) {
5298 std::uint32_t t = bhH.ref;
5299 while (t != 0) { t >>= 8; ++bhH.size; }
5300 }
5301 (handleBuf ? handleBuf : buf)->putHandle(bhH);
5302
5303 if (hasAttrib) {
5304 dwgBufferW *hb = handleBuf ? handleBuf : buf;
5305 if (version < DRW::AC1018) {
5306 for (const dwgHandle& attribHandle : attribHandles)
5307 putDwgReference(hb, DRW::DwgSoftPointer, attribHandle.ref);
5308 } else {
5309 const std::uint8_t attributeCode =
5310 oType == 8 ? DRW::DwgSoftPointer : DRW::DwgHardOwnership;
5311 for (const dwgHandle& attribHandle : attribHandles)
5312 putDwgReference(hb, attributeCode, attribHandle.ref);
5313 }
5314 putDwgReference(hb, DRW::DwgHardOwnership, seqendH.ref);
5315 }
5316 return buf->isGood() && (handleBuf == nullptr || handleBuf->isGood());
5317}
5318
5319bool DRW_3Dface::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5320 (void)bs; (void)strBuf;
5321 oType = 28; // 3DFACE class id — see dwgreader.cpp:1237
5322 if (!encodeDwgCommon(version, buf)) return false;
5323
5324 // R2000+ 3DFACE body — mirror of parseDwg's z_is_zero / has_no_flag
5325 // optimization. Reader checks `invisibleflag != NoEdge`; if NoEdge,
5326 // emit has_no_flag=1 to suppress the BS read.
5327 bool hasNoFlag = (invisibleflag == /*NoEdge*/0);
5328 bool zIsZero = (basePoint.z == 0.0);
5329 buf->putBit(hasNoFlag ? 1 : 0);
5330 buf->putBit(zIsZero ? 1 : 0);
5331 buf->putRawDouble(basePoint.x);
5332 buf->putRawDouble(basePoint.y);
5333 if (!zIsZero) buf->putRawDouble(basePoint.z);
5334 buf->putDefaultDouble(basePoint.x, secPoint.x);
5335 buf->putDefaultDouble(basePoint.y, secPoint.y);
5336 buf->putDefaultDouble(basePoint.z, secPoint.z);
5337 buf->putDefaultDouble(secPoint.x, thirdPoint.x);
5338 buf->putDefaultDouble(secPoint.y, thirdPoint.y);
5339 buf->putDefaultDouble(secPoint.z, thirdPoint.z);
5340 buf->putDefaultDouble(thirdPoint.x, fourPoint.x);
5341 buf->putDefaultDouble(thirdPoint.y, fourPoint.y);
5342 buf->putDefaultDouble(thirdPoint.z, fourPoint.z);
5343 if (!hasNoFlag) buf->putBitShort(static_cast<std::uint16_t>(invisibleflag));
5344
5345 return encodeDwgEntHandle(version, buf, handleBuf);
5346}
5347
5348bool DRW_Solid::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5349 (void)bs; (void)strBuf;
5350 oType = 31; // SOLID class id — see dwgreader.cpp:1305
5351 if (!encodeDwgCommon(version, buf)) return false;
5352
5353 // Same body layout as TRACE (4 corners + extrusion). Duplicated
5354 // here rather than delegating to DRW_Trace::encodeDwg because that
5355 // hardcodes oType=32.
5356 buf->putThickness(thickness, /*b_R2000_style=*/true);
5357 buf->putBitDouble(basePoint.z);
5358 buf->putRawDouble(basePoint.x);
5359 buf->putRawDouble(basePoint.y);
5360 buf->putRawDouble(secPoint.x);
5361 buf->putRawDouble(secPoint.y);
5362 buf->putRawDouble(thirdPoint.x);
5363 buf->putRawDouble(thirdPoint.y);
5364 buf->putRawDouble(fourPoint.x);
5365 buf->putRawDouble(fourPoint.y);
5366 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5367
5368 return encodeDwgEntHandle(version, buf, handleBuf);
5369}
5370
5371bool DRW_LWPolyline::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5372 (void)bs; (void)strBuf;
5373 if (buf == nullptr || !validatePayloadFields())
5374 return false;
5375 oType = 77; // LWPOLYLINE class id — see dwgreader.cpp:1202
5376 if (!encodeDwgCommon(version, buf)) return false;
5377
5378 // DRW_LWPolyline::flags carries DXF-side bits (1=closed, 128=plinegen).
5379 // DWG-side flags are different: they signal which optional fields are
5380 // present. Per parseDwg, bit 9 (0x200) = closed, bit 8 (0x100) =
5381 // plinegen. Build the DWG flags from the DXF flags plus the data.
5382 std::uint16_t dwgFlags = 0;
5383 if (flags & 1) dwgFlags |= 0x200; // closed
5384 if (flags & 128) dwgFlags |= 0x100; // plinegen
5385 if (thickness != 0.0) dwgFlags |= 0x2;
5386 if (width != 0.0) dwgFlags |= 0x4;
5387 if (elevation != 0.0) dwgFlags |= 0x8;
5388 bool defaultExt = (extPoint.x == 0.0 && extPoint.y == 0.0 && extPoint.z == 1.0);
5389 if (!defaultExt) dwgFlags |= 0x1;
5390 // Detect per-vertex bulge / width data.
5391 bool anyBulge = false;
5392 bool anyWidth = false;
5393 bool anyVertexId = false;
5394 for (const auto& v : vertlist) {
5395 if (v && v->bulge != 0.0) anyBulge = true;
5396 if (v && (v->stawidth != 0.0 || v->endwidth != 0.0)) anyWidth = true;
5397 if (v && v->identifier != 0) anyVertexId = true;
5398 }
5399 if (anyBulge) dwgFlags |= 0x10;
5400 if (anyWidth) dwgFlags |= 0x20;
5401 if (version > DRW::AC1021 && anyVertexId) dwgFlags |= 0x400;
5402
5403 buf->putBitShort(dwgFlags);
5404 if (dwgFlags & 0x4) buf->putBitDouble(width);
5405 if (dwgFlags & 0x8) buf->putBitDouble(elevation);
5406 if (dwgFlags & 0x2) buf->putBitDouble(thickness);
5407 if (dwgFlags & 0x1) buf->putExtrusion(extPoint, /*b_R2000_style=*/false);
5408
5409 const std::int32_t numVerts = static_cast<std::int32_t>(vertlist.size());
5410 buf->putBitLong(numVerts);
5411 if (dwgFlags & 0x10) buf->putBitLong(numVerts); // bulgesnum
5412 if (version > DRW::AC1021 && (dwgFlags & 0x400)) {
5413 buf->putBitLong(numVerts); // vertexIdCount
5414 }
5415 if (dwgFlags & 0x20) buf->putBitLong(numVerts); // widthsnum
5416
5417 if (numVerts > 0) {
5418 // First vertex as 2RD. Subsequent vertices as 2DD relative to
5419 // the previous, with putDefaultDouble always emitting code 0b11
5420 // (full RD); the reader's getDefaultDouble returns the raw value.
5421 buf->putRawDouble(vertlist[0]->x);
5422 buf->putRawDouble(vertlist[0]->y);
5423 for (size_t i = 1; i < vertlist.size(); ++i) {
5424 buf->putDefaultDouble(vertlist[i-1]->x, vertlist[i]->x);
5425 buf->putDefaultDouble(vertlist[i-1]->y, vertlist[i]->y);
5426 }
5427 if (dwgFlags & 0x10) {
5428 for (const auto& v : vertlist)
5429 buf->putBitDouble(v->bulge);
5430 }
5431 if (version > DRW::AC1021 && (dwgFlags & 0x400)) {
5432 for (const auto& v : vertlist)
5433 buf->putBitLong(static_cast<std::int32_t>(v->identifier));
5434 }
5435 if (dwgFlags & 0x20) {
5436 for (const auto& v : vertlist) {
5437 buf->putBitDouble(v->stawidth);
5438 buf->putBitDouble(v->endwidth);
5439 }
5440 }
5441 }
5442
5443 return encodeDwgEntHandle(version, buf, handleBuf);
5444}
5445
5446bool DRW_Block::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5447 (void)bs;
5448 // BLOCK = 4, ENDBLK = 5 per DWG spec. isEnd controls which.
5449 oType = isEnd ? 5 : 4;
5450 if (!encodeDwgCommon(version, buf)) return false;
5451 if (!isEnd) {
5452 (strBuf ? strBuf : buf)->putVariableText(version, name);
5453 }
5454 if (version > DRW::AC1018) {
5455 buf->putBit(0); // unknown bit (R2007+: always 0 for our output)
5456 }
5457 return encodeDwgEntHandle(version, buf, handleBuf);
5458}
5459
5460bool DRW_Text::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5461 (void)bs;
5462 oType = 1; // TEXT class id — see dwgreader.cpp:1208
5463 if (!encodeDwgCommon(version, buf)) return false;
5464
5465 // R2000+ TEXT body — mirror of DRW_Text::parseDwg. We emit
5466 // data_flags=0 so the reader sees every optional field rather than
5467 // substituting defaults — keeps the encoder simple, costs ~30 bytes
5468 // per TEXT versus the most compressed form.
5469 buf->putRawChar8(0); // data_flags=0
5470 buf->putRawDouble(basePoint.z); // elevation RD
5471 buf->putRawDouble(basePoint.x); // insertion 2RD
5472 buf->putRawDouble(basePoint.y);
5473 buf->putDefaultDouble(basePoint.x, secPoint.x); // alignment 2DD
5474 buf->putDefaultDouble(basePoint.y, secPoint.y);
5475 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5476 buf->putThickness(thickness, /*b_R2000_style=*/true);
5477 buf->putRawDouble(oblique); // oblique angle
5478 // Angle: struct holds degrees; on-disk format is radians. Reader
5479 // does `angle *= ARAD` (180/π) after read. Inverse: divide here.
5480 buf->putRawDouble(angle / ARAD57.29577951308232);
5481 buf->putRawDouble(height); // text height
5482 buf->putRawDouble(widthscale); // width factor
5483 (strBuf ? strBuf : buf)->putVariableText(version, text); // text string
5484 buf->putBitShort(static_cast<std::uint16_t>(textgen));
5485 buf->putBitShort(static_cast<std::uint16_t>(alignH));
5486 buf->putBitShort(static_cast<std::uint16_t>(alignV));
5487
5488 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
5489
5490 // styleH — hard pointer to STYLE table record. Default points at
5491 // the STANDARD textstyle (handle 0x13) if caller hasn't set one.
5492 dwgHandle sH;
5493 std::uint32_t sref = (styleH.ref == 0) ? 0x13 : styleH.ref;
5494 sH.code = 5; // hard pointer
5495 sH.ref = sref;
5496 sH.size = 0;
5497 if (sref != 0) {
5498 std::uint32_t t = sref;
5499 while (t != 0) { t >>= 8; ++sH.size; }
5500 }
5501 (handleBuf ? handleBuf : buf)->putHandle(sH);
5502 return true;
5503}
5504
5505bool DRW_Ellipse::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5506 (void)bs; (void)strBuf;
5507 oType = 35; // ELLIPSE class id — see dwgreader.cpp:1117
5508 if (!encodeDwgCommon(version, buf)) return false;
5509
5510 // Ellipse body — mirror of DRW_Ellipse::parseDwg.
5511 buf->put3BitDouble(basePoint); // center
5512 buf->put3BitDouble(secPoint); // major axis vector
5513 buf->put3BitDouble(extPoint); // extrusion
5514 buf->putBitDouble(ratio); // minor/major ratio
5515 buf->putBitDouble(staparam); // start parameter
5516 buf->putBitDouble(endparam); // end parameter
5517
5518 return encodeDwgEntHandle(version, buf, handleBuf);
5519}
5520
5521bool DRW_Arc::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
5522 (void)bs; (void)strBuf;
5523 oType = 17; // ARC class id — see dwgreader.cpp:1093
5524 if (!encodeDwgCommon(version, buf)) return false;
5525
5526 // Arc body — Circle body + 2 BD angles.
5527 buf->putBitDouble(basePoint.x);
5528 buf->putBitDouble(basePoint.y);
5529 buf->putBitDouble(basePoint.z);
5530 buf->putBitDouble(radious);
5531 buf->putThickness(thickness, /*b_R2000_style=*/true);
5532 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
5533 buf->putBitDouble(staangle);
5534 buf->putBitDouble(endangle);
5535
5536 return encodeDwgEntHandle(version, buf, handleBuf);
5537}
5538
5539bool DRW_Line::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5540 resetDwgState();
5541 dwgBuffer *sourceBuf = buf;
5542 auto fail = [this, sourceBuf]() {
5543 if (sourceBuf != nullptr)
5544 sourceBuf->invalidate();
5545 resetDwgState();
5546 return false;
5547 };
5548 if (sourceBuf == nullptr)
5549 return fail();
5550
5551 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5552 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5553 || !bodyProbe.isGood())
5554 return fail();
5555 const std::uint64_t bodyEndBit = dwgDataEndBit;
5556 DRW_Coord parsedBasePoint;
5557 DRW_Coord parsedSecPoint;
5558 DRW_Coord parsedExtrusion;
5559 double parsedThickness = 0.0;
5560 if (version < DRW::AC1015) {
5561 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint)
5562 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedSecPoint))
5563 return fail();
5564 } else {
5565 if (!proxyEntityHasBits(bodyProbe, bodyEndBit, 1))
5566 return fail();
5567 const bool zIsZero = bodyProbe.getBit() != 0;
5568 if (!bodyProbe.isGood()
5569 || !readBoundedRawDouble(bodyProbe, bodyEndBit,
5570 parsedBasePoint.x)
5571 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit,
5572 parsedBasePoint.x,
5573 parsedSecPoint.x)
5574 || !readBoundedRawDouble(bodyProbe, bodyEndBit,
5575 parsedBasePoint.y)
5576 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit,
5577 parsedBasePoint.y,
5578 parsedSecPoint.y))
5579 return fail();
5580 if (zIsZero) {
5581 parsedBasePoint.z = 0.0;
5582 parsedSecPoint.z = 0.0;
5583 } else if (!readBoundedRawDouble(bodyProbe, bodyEndBit,
5584 parsedBasePoint.z)
5585 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit,
5586 parsedBasePoint.z,
5587 parsedSecPoint.z))
5588 return fail();
5589 }
5590 if (!readBoundedThickness(bodyProbe, bodyEndBit,
5591 version > DRW::AC1014, parsedThickness)
5592 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
5593 version > DRW::AC1014, parsedExtrusion)
5594 || !std::isfinite(parsedBasePoint.x)
5595 || !std::isfinite(parsedBasePoint.y)
5596 || !std::isfinite(parsedBasePoint.z)
5597 || !std::isfinite(parsedSecPoint.x)
5598 || !std::isfinite(parsedSecPoint.y)
5599 || !std::isfinite(parsedSecPoint.z)
5600 || !std::isfinite(parsedThickness)
5601 || !std::isfinite(parsedExtrusion.x)
5602 || !std::isfinite(parsedExtrusion.y)
5603 || !std::isfinite(parsedExtrusion.z))
5604 return fail();
5605
5606 std::uint64_t handleEndBit = 0;
5607 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5608 handleEndBit))
5609 return fail();
5610 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5611 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5612 handleEndBit)
5613 || !handleProbe.isGood())
5614 return fail();
5615
5616 basePoint = parsedBasePoint;
5617 secPoint = parsedSecPoint;
5618 thickness = parsedThickness;
5619 extPoint = parsedExtrusion;
5620 *sourceBuf = handleProbe;
5621 return true;
5622}
5623
5624bool DRW_Ray::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5625 resetDwgState();
5626 dwgBuffer *sourceBuf = buf;
5627 auto fail = [this, sourceBuf]() {
5628 if (sourceBuf != nullptr)
5629 sourceBuf->invalidate();
5630 resetDwgState();
5631 return false;
5632 };
5633 if (sourceBuf == nullptr)
5634 return fail();
5635
5636 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5637 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5638 || !bodyProbe.isGood())
5639 return fail();
5640 const std::uint64_t bodyEndBit = dwgDataEndBit;
5641 DRW_Coord parsedBasePoint;
5642 DRW_Coord parsedDirection;
5643 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint)
5644 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedDirection)
5645 || !std::isfinite(parsedBasePoint.x)
5646 || !std::isfinite(parsedBasePoint.y)
5647 || !std::isfinite(parsedBasePoint.z)
5648 || !std::isfinite(parsedDirection.x)
5649 || !std::isfinite(parsedDirection.y)
5650 || !std::isfinite(parsedDirection.z))
5651 return fail();
5652
5653 std::uint64_t handleEndBit = 0;
5654 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5655 handleEndBit))
5656 return fail();
5657 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5658 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5659 handleEndBit)
5660 || !handleProbe.isGood())
5661 return fail();
5662
5663 basePoint = parsedBasePoint;
5664 secPoint = parsedDirection;
5665 *sourceBuf = handleProbe;
5666 return true;
5667}
5668
5669void DRW_Circle::applyExtrusion(){
5670 if (haveExtrusion) {
5671 //NOTE: Commenting these out causes the the arcs being tested to be located
5672 //on the other side of the y axis (all x dimensions are negated).
5673 calculateAxis(extPoint);
5674 extrudePoint(extPoint, &basePoint);
5675 }
5676}
5677
5678bool DRW_Circle::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5679 switch (code) {
5680 case 40:
5681 radious = reader->getDouble();
5682 break;
5683 default:
5684 return DRW_Point::parseCode(code, reader);
5685 }
5686
5687 return true;
5688}
5689
5690void DRW_Circle::resetDwgState() {
5691 DRW_Point::resetDwgState();
5692 radious = 0.0;
5693}
5694
5695bool DRW_Circle::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5696 resetDwgState();
5697 radious = 0.0;
5698 dwgBuffer *sourceBuf = buf;
5699 auto fail = [this, sourceBuf]() {
5700 if (sourceBuf != nullptr)
5701 sourceBuf->invalidate();
5702 resetDwgState();
5703 return false;
5704 };
5705 if (sourceBuf == nullptr)
5706 return fail();
5707
5708 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5709 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5710 || !bodyProbe.isGood())
5711 return fail();
5712 const std::uint64_t bodyEndBit = dwgDataEndBit;
5713 DRW_Coord parsedCenter;
5714 DRW_Coord parsedExtrusion;
5715 double parsedRadius = 0.0;
5716 double parsedThickness = 0.0;
5717 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedCenter)
5718 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedRadius)
5719 || !readBoundedThickness(bodyProbe, bodyEndBit,
5720 version > DRW::AC1014, parsedThickness)
5721 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
5722 version > DRW::AC1014, parsedExtrusion)
5723 || !std::isfinite(parsedCenter.x)
5724 || !std::isfinite(parsedCenter.y)
5725 || !std::isfinite(parsedCenter.z)
5726 || !std::isfinite(parsedRadius)
5727 || !std::isfinite(parsedThickness)
5728 || !std::isfinite(parsedExtrusion.x)
5729 || !std::isfinite(parsedExtrusion.y)
5730 || !std::isfinite(parsedExtrusion.z))
5731 return fail();
5732
5733 std::uint64_t handleEndBit = 0;
5734 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5735 handleEndBit))
5736 return fail();
5737 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5738 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5739 handleEndBit)
5740 || !handleProbe.isGood())
5741 return fail();
5742
5743 basePoint = parsedCenter;
5744 radious = parsedRadius;
5745 thickness = parsedThickness;
5746 extPoint = parsedExtrusion;
5747 *sourceBuf = handleProbe;
5748 return true;
5749}
5750
5751void DRW_Arc::applyExtrusion(){
5752 DRW_Circle::applyExtrusion();
5753
5754 if(haveExtrusion){
5755 // If the extrusion vector has a z value less than 0, the angles for the arc
5756 // have to be mirrored since DXF files use the right hand rule.
5757 // Note that the following code only handles the special case where there is a 2D
5758 // drawing with the z axis heading into the paper (or rather screen). An arbitrary
5759 // extrusion axis (with x and y values greater than 1/64) may still have issues.
5760 if (fabs(extPoint.x) < 0.015625 && fabs(extPoint.y) < 0.015625 && extPoint.z < 0.0) {
5761 staangle=M_PI3.14159265358979323846-staangle;
5762 endangle=M_PI3.14159265358979323846-endangle;
5763
5764 double temp = staangle;
5765 staangle=endangle;
5766 endangle=temp;
5767 }
5768 }
5769}
5770
5771bool DRW_Arc::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5772 switch (code) {
5773 case 50:
5774 staangle = reader->getDouble()/ ARAD57.29577951308232;
5775 break;
5776 case 51:
5777 endangle = reader->getDouble()/ ARAD57.29577951308232;
5778 break;
5779 default:
5780 return DRW_Circle::parseCode(code, reader);
5781 }
5782
5783 return true;
5784}
5785
5786bool DRW_Arc::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5787 DRW_Circle::resetDwgState();
5788 radious = 0.0;
5789 staangle = 0.0;
5790 endangle = 0.0;
5791 isccw = 1;
5792 dwgBuffer *sourceBuf = buf;
5793 auto fail = [this, sourceBuf]() {
5794 if (sourceBuf != nullptr)
5795 sourceBuf->invalidate();
5796 DRW_Circle::resetDwgState();
5797 staangle = 0.0;
5798 endangle = 0.0;
5799 isccw = 1;
5800 return false;
5801 };
5802 if (sourceBuf == nullptr)
5803 return fail();
5804
5805 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5806 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5807 || !bodyProbe.isGood())
5808 return fail();
5809 const std::uint64_t bodyEndBit = dwgDataEndBit;
5810 DRW_Coord parsedCenter;
5811 DRW_Coord parsedExtrusion;
5812 double parsedRadius = 0.0;
5813 double parsedThickness = 0.0;
5814 double parsedStartAngle = 0.0;
5815 double parsedEndAngle = 0.0;
5816 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedCenter)
5817 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedRadius)
5818 || !readBoundedThickness(bodyProbe, bodyEndBit,
5819 version > DRW::AC1014, parsedThickness)
5820 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
5821 version > DRW::AC1014, parsedExtrusion)
5822 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedStartAngle)
5823 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedEndAngle)
5824 || !std::isfinite(parsedCenter.x)
5825 || !std::isfinite(parsedCenter.y)
5826 || !std::isfinite(parsedCenter.z)
5827 || !std::isfinite(parsedRadius)
5828 || !std::isfinite(parsedThickness)
5829 || !std::isfinite(parsedExtrusion.x)
5830 || !std::isfinite(parsedExtrusion.y)
5831 || !std::isfinite(parsedExtrusion.z)
5832 || !std::isfinite(parsedStartAngle)
5833 || !std::isfinite(parsedEndAngle))
5834 return fail();
5835
5836 std::uint64_t handleEndBit = 0;
5837 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5838 handleEndBit))
5839 return fail();
5840 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5841 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5842 handleEndBit)
5843 || !handleProbe.isGood())
5844 return fail();
5845
5846 basePoint = parsedCenter;
5847 radious = parsedRadius;
5848 thickness = parsedThickness;
5849 extPoint = parsedExtrusion;
5850 staangle = parsedStartAngle;
5851 endangle = parsedEndAngle;
5852 *sourceBuf = handleProbe;
5853 return true;
5854}
5855
5856bool DRW_Ellipse::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
5857 switch (code) {
5858 case 40:
5859 ratio = reader->getDouble();
5860 break;
5861 case 41:
5862 staparam = reader->getDouble();
5863 break;
5864 case 42:
5865 endparam = reader->getDouble();
5866 break;
5867 default:
5868 return DRW_Line::parseCode(code, reader);
5869 }
5870
5871 return true;
5872}
5873
5874void DRW_Ellipse::applyExtrusion(){
5875 if (haveExtrusion) {
5876 calculateAxis(extPoint);
5877 extrudePoint(extPoint, &basePoint);
5878 extrudePoint(extPoint, &secPoint);
5879 double intialparam = staparam;
5880 if (extPoint.z < 0.){
5881 staparam = M_PIx26.283185307179586 - endparam;
5882 endparam = M_PIx26.283185307179586 - intialparam;
5883 }
5884 }
5885}
5886
5887//if ratio > 1 minor axis are greather than major axis, correct it
5888void DRW_Ellipse::correctAxis(){
5889 bool complete = false;
5890 if (staparam == endparam) {
5891 staparam = 0.0;
5892 endparam = M_PIx26.283185307179586; //2*M_PI;
5893 complete = true;
5894 }
5895 if (ratio > 1){
5896 if ( fabs(endparam - staparam - M_PIx26.283185307179586) < 1.0e-10)
5897 complete = true;
5898 double incX = secPoint.x;
5899 secPoint.x = -(secPoint.y * ratio);
5900 secPoint.y = incX*ratio;
5901 ratio = 1/ratio;
5902 if (!complete){
5903 if (staparam < M_PI_21.57079632679489661923)
5904 staparam += M_PI3.14159265358979323846 *2;
5905 if (endparam < M_PI_21.57079632679489661923)
5906 endparam += M_PI3.14159265358979323846 *2;
5907 endparam -= M_PI_21.57079632679489661923;
5908 staparam -= M_PI_21.57079632679489661923;
5909 }
5910 }
5911}
5912
5913bool DRW_Ellipse::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
5914 resetDwgState();
5915 ratio = 1.0;
5916 staparam = 0.0;
5917 endparam = 0.0;
5918 isccw = 1;
5919 dwgBuffer *sourceBuf = buf;
5920 auto fail = [this, sourceBuf]() {
5921 if (sourceBuf != nullptr)
5922 sourceBuf->invalidate();
5923 resetDwgState();
5924 ratio = 1.0;
5925 staparam = 0.0;
5926 endparam = 0.0;
5927 isccw = 1;
5928 return false;
5929 };
5930 if (sourceBuf == nullptr)
5931 return fail();
5932
5933 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
5934 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
5935 || !bodyProbe.isGood())
5936 return fail();
5937 const std::uint64_t bodyEndBit = dwgDataEndBit;
5938 DRW_Coord parsedCenter;
5939 DRW_Coord parsedAxis;
5940 DRW_Coord parsedExtrusion;
5941 double parsedRatio = 1.0;
5942 double parsedStart = 0.0;
5943 double parsedEnd = 0.0;
5944 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedCenter)
5945 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedAxis)
5946 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedExtrusion)
5947 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedRatio)
5948 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedStart)
5949 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedEnd)
5950 || !std::isfinite(parsedCenter.x)
5951 || !std::isfinite(parsedCenter.y)
5952 || !std::isfinite(parsedCenter.z)
5953 || !std::isfinite(parsedAxis.x)
5954 || !std::isfinite(parsedAxis.y)
5955 || !std::isfinite(parsedAxis.z)
5956 || !std::isfinite(parsedExtrusion.x)
5957 || !std::isfinite(parsedExtrusion.y)
5958 || !std::isfinite(parsedExtrusion.z)
5959 || !std::isfinite(parsedRatio)
5960 || !std::isfinite(parsedStart)
5961 || !std::isfinite(parsedEnd))
5962 return fail();
5963
5964 std::uint64_t handleEndBit = 0;
5965 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
5966 handleEndBit))
5967 return fail();
5968 dwgBuffer handleProbe = bodyProbe.forkIndependent();
5969 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
5970 handleEndBit)
5971 || !handleProbe.isGood())
5972 return fail();
5973
5974 basePoint = parsedCenter;
5975 secPoint = parsedAxis;
5976 extPoint = parsedExtrusion;
5977 ratio = parsedRatio;
5978 staparam = parsedStart;
5979 endparam = parsedEnd;
5980 *sourceBuf = handleProbe;
5981 return true;
5982}
5983
5984//parts are the number of vertex to split polyline, default 128
5985void DRW_Ellipse::toPolyline(DRW_Polyline *pol, int parts){
5986 double radMajor, radMinor, cosRot, sinRot, incAngle, curAngle;
5987 double cosCurr, sinCurr;
5988 radMajor = hypot(secPoint.x, secPoint.y);
5989 radMinor = radMajor*ratio;
5990 //calculate sin & cos of included angle
5991 incAngle = atan2(secPoint.y, secPoint.x);
5992 cosRot = cos(incAngle);
5993 sinRot = sin(incAngle);
5994 incAngle = M_PIx26.283185307179586 / parts;
5995 curAngle = staparam;
5996 int i = static_cast<int>(curAngle / incAngle);
5997 do {
5998 if (curAngle > endparam) {
5999 curAngle = endparam;
6000 i = parts+2;
6001 }
6002 cosCurr = cos(curAngle);
6003 sinCurr = sin(curAngle);
6004 double x = basePoint.x + (cosCurr*cosRot*radMajor) - (sinCurr*sinRot*radMinor);
6005 double y = basePoint.y + (cosCurr*sinRot*radMajor) + (sinCurr*cosRot*radMinor);
6006 pol->addVertex( DRW_Vertex(x, y, 0.0, 0.0));
6007 curAngle = (++i)*incAngle;
6008 } while (i<parts);
6009 if ( fabs(endparam - staparam - M_PIx26.283185307179586) < 1.0e-10){
6010 pol->flags = 1;
6011 }
6012 pol->layer = this->layer;
6013 pol->lineType = this->lineType;
6014 pol->color = this->color;
6015 pol->lWeight = this->lWeight;
6016 pol->extPoint = this->extPoint;
6017}
6018
6019void DRW_Trace::applyExtrusion(){
6020 if (haveExtrusion) {
6021 calculateAxis(extPoint);
6022 extrudePoint(extPoint, &basePoint);
6023 extrudePoint(extPoint, &secPoint);
6024 extrudePoint(extPoint, &thirdPoint);
6025 extrudePoint(extPoint, &fourPoint);
6026 }
6027}
6028
6029bool DRW_Trace::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6030 switch (code) {
6031 case 12:
6032 thirdPoint.x = reader->getDouble();
6033 break;
6034 case 22:
6035 thirdPoint.y = reader->getDouble();
6036 break;
6037 case 32:
6038 thirdPoint.z = reader->getDouble();
6039 break;
6040 case 13:
6041 fourPoint.x = reader->getDouble();
6042 break;
6043 case 23:
6044 fourPoint.y = reader->getDouble();
6045 break;
6046 case 33:
6047 fourPoint.z = reader->getDouble();
6048 break;
6049 default:
6050 return DRW_Line::parseCode(code, reader);
6051 }
6052
6053 return true;
6054}
6055
6056void DRW_Trace::resetDwgState() {
6057 DRW_Line::resetDwgState();
6058 thirdPoint = DRW_Coord{0.0, 0.0, 0.0};
6059 fourPoint = DRW_Coord{0.0, 0.0, 0.0};
6060}
6061
6062bool DRW_Trace::parseDwg(DRW::Version version, dwgBuffer *buf,
6063 std::uint32_t bs) {
6064 resetDwgState();
6065 dwgBuffer *sourceBuf = buf;
6066 auto fail = [this, sourceBuf]() {
6067 if (sourceBuf != nullptr)
6068 sourceBuf->invalidate();
6069 resetDwgState();
6070 return false;
6071 };
6072 if (sourceBuf == nullptr)
6073 return fail();
6074
6075 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
6076 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
6077 || !bodyProbe.isGood())
6078 return fail();
6079
6080 const std::uint64_t bodyEndBit = dwgDataEndBit;
6081 double parsedThickness = 0.0;
6082 double parsedElevation = 0.0;
6083 std::array<double, 8> coordinates{};
6084 DRW_Coord parsedExtrusion{0.0, 0.0, 1.0};
6085 if (!readBoundedThickness(bodyProbe, bodyEndBit,
6086 version > DRW::AC1014, parsedThickness)
6087 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedElevation))
6088 return fail();
6089 for (double& coordinate : coordinates) {
6090 if (!readBoundedRawDouble(bodyProbe, bodyEndBit, coordinate)
6091 || !std::isfinite(coordinate))
6092 return fail();
6093 }
6094 if (!std::isfinite(parsedThickness)
6095 || !std::isfinite(parsedElevation)
6096 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
6097 version > DRW::AC1014, parsedExtrusion)
6098 || !std::isfinite(parsedExtrusion.x)
6099 || !std::isfinite(parsedExtrusion.y)
6100 || !std::isfinite(parsedExtrusion.z))
6101 return fail();
6102
6103 std::uint64_t handleEndBit = 0;
6104 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
6105 handleEndBit))
6106 return fail();
6107 dwgBuffer handleProbe = bodyProbe.forkIndependent();
6108 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
6109 handleEndBit)
6110 || !handleProbe.isGood())
6111 return fail();
6112
6113 thickness = parsedThickness;
6114 basePoint = DRW_Coord{coordinates[0], coordinates[1], parsedElevation};
6115 secPoint = DRW_Coord{coordinates[2], coordinates[3], parsedElevation};
6116 thirdPoint = DRW_Coord{coordinates[4], coordinates[5], parsedElevation};
6117 fourPoint = DRW_Coord{coordinates[6], coordinates[7], parsedElevation};
6118 extPoint = parsedExtrusion;
6119 *sourceBuf = handleProbe;
6120 return true;
6121}
6122
6123bool DRW_Solid::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
6124 DRW_DBG("\n***************************** parsing Solid *********************************************\n")DRW_dbg::dbg("\n***************************** parsing Solid *********************************************\n"
)
;
6125 return DRW_Trace::parseDwg(v, buf, bs);
6126}
6127
6128bool DRW_3Dface::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6129 switch (code) {
6130 case 70:
6131 invisibleflag = reader->getInt32();
6132 break;
6133 default:
6134 return DRW_Trace::parseCode(code, reader);
6135 }
6136
6137 return true;
6138}
6139
6140bool DRW_3Dface::parseDwg(DRW::Version v, dwgBuffer *buf,
6141 std::uint32_t bs) {
6142 DRW_Trace::resetDwgState();
6143 invisibleflag = 0;
6144 dwgBuffer *sourceBuf = buf;
6145 auto fail = [this, sourceBuf]() {
6146 if (sourceBuf != nullptr)
6147 sourceBuf->invalidate();
6148 DRW_Trace::resetDwgState();
6149 invisibleflag = 0;
6150 return false;
6151 };
6152 if (sourceBuf == nullptr)
6153 return fail();
6154
6155 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
6156 if (!DRW_Entity::parseDwgCommon(v, &bodyProbe, bs)
6157 || !bodyProbe.isGood())
6158 return fail();
6159
6160 const std::uint64_t bodyEndBit = dwgDataEndBit;
6161 std::array<DRW_Coord, 4> points{};
6162 std::uint16_t parsedInvisibleFlag = 0;
6163 if (v < DRW::AC1015) {
6164 for (DRW_Coord& point : points) {
6165 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, point.x)
6166 || !readBoundedBitDouble(bodyProbe, bodyEndBit, point.y)
6167 || !readBoundedBitDouble(bodyProbe, bodyEndBit, point.z))
6168 return fail();
6169 }
6170 if (!readBoundedBitShort(bodyProbe, bodyEndBit,
6171 parsedInvisibleFlag))
6172 return fail();
6173 } else {
6174 if (!proxyEntityHasBits(bodyProbe, bodyEndBit, 2))
6175 return fail();
6176 const bool hasNoFlag = bodyProbe.getBit() != 0;
6177 const bool zIsZero = bodyProbe.getBit() != 0;
6178 if (!bodyProbe.isGood()
6179 || !readBoundedRawDouble(bodyProbe, bodyEndBit, points[0].x)
6180 || !readBoundedRawDouble(bodyProbe, bodyEndBit, points[0].y))
6181 return fail();
6182 if (zIsZero) {
6183 points[0].z = 0.0;
6184 } else if (!readBoundedRawDouble(bodyProbe, bodyEndBit,
6185 points[0].z)) {
6186 return fail();
6187 }
6188
6189 if (!readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[0].x,
6190 points[1].x)
6191 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[0].y,
6192 points[1].y)
6193 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[0].z,
6194 points[1].z)
6195 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[1].x,
6196 points[2].x)
6197 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[1].y,
6198 points[2].y)
6199 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[1].z,
6200 points[2].z)
6201 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[2].x,
6202 points[3].x)
6203 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[2].y,
6204 points[3].y)
6205 || !readBoundedDefaultDouble(bodyProbe, bodyEndBit, points[2].z,
6206 points[3].z))
6207 return fail();
6208 if (hasNoFlag) {
6209 parsedInvisibleFlag = NoEdge;
6210 } else if (!readBoundedBitShort(bodyProbe, bodyEndBit,
6211 parsedInvisibleFlag)) {
6212 return fail();
6213 }
6214 }
6215
6216 for (const DRW_Coord& point : points) {
6217 if (!std::isfinite(point.x) || !std::isfinite(point.y)
6218 || !std::isfinite(point.z))
6219 return fail();
6220 }
6221 if (parsedInvisibleFlag > AllEdges)
6222 return fail();
6223
6224 std::uint64_t handleEndBit = 0;
6225 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
6226 return fail();
6227 dwgBuffer handleProbe = bodyProbe.forkIndependent();
6228 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
6229 || !handleProbe.isGood())
6230 return fail();
6231
6232 basePoint = points[0];
6233 secPoint = points[1];
6234 thirdPoint = points[2];
6235 fourPoint = points[3];
6236 invisibleflag = static_cast<int>(parsedInvisibleFlag);
6237 *sourceBuf = handleProbe;
6238 return true;
6239}
6240
6241void DRW_ModelerGeometry::resetDwgState() {
6242 DRW_Entity::reset();
6243 m_modelerVersion = 0;
6244 m_bodyBitSize = 0;
6245 m_objectSize = 0;
6246 m_isEmpty = false;
6247 m_hasModelerData = false;
6248 m_modelerDataUnknownBit = false;
6249 m_hasWireframe = false;
6250 m_historyHandle = 0;
6251 m_rawBytes.clear();
6252 m_payloadRanges.clear();
6253 m_wireframe = DRW_AcisBrep();
6254 m_wireframeDecoded = false;
6255}
6256
6257bool DRW_ModelerGeometry::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
6258 resetDwgState();
6259 dwgBuffer *sourceBuf = buf;
6260 auto fail = [this, sourceBuf]() {
6261 if (sourceBuf != nullptr)
6262 sourceBuf->invalidate();
6263 resetDwgState();
6264 return false;
6265 };
6266 if (sourceBuf == nullptr)
6267 return fail();
6268
6269 dwgBuffer probe = sourceBuf->forkIndependent();
6270 dwgBuffer stringProbe = sourceBuf->forkIndependent();
6271 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : nullptr;
6272 bool ret = DRW_Entity::parseDwg(v, &probe, stringStream, bs);
6273 if (!ret)
6274 return fail();
6275 DRW_DBG("\n***************************** parsing modeler geometry ******************\n")DRW_dbg::dbg("\n***************************** parsing modeler geometry ******************\n"
)
;
6276
6277 std::uint64_t bodyEndBit = 0;
6278 if (!entityBodyDataEndBit(
6279 stringStream != nullptr ? *stringStream : probe,
6280 v, objSize, bodyEndBit))
6281 return fail();
6282 const std::uint64_t sourceEndBit = proxyEntityEndBit(*sourceBuf, 0);
6283 if ((v >= DRW::AC1015 && objSize == 0) || bodyEndBit > sourceEndBit
6284 || currentDwgBit(&probe) > bodyEndBit)
6285 return fail();
6286 bool parsedIsEmpty = false;
6287 bool parsedUnknownBit = false;
6288 std::uint16_t parsedModelerVersion = 0;
6289 if (!readBoundedBit(probe, bodyEndBit, parsedIsEmpty)
6290 || !readBoundedBit(probe, bodyEndBit, parsedUnknownBit))
6291 return fail();
6292 const bool parsedHasModelerData = !parsedIsEmpty;
6293 if (parsedHasModelerData
6294 && !readBoundedBitShort(probe, bodyEndBit, parsedModelerVersion))
6295 return fail();
6296 if (!probe.isGood() || (stringStream != nullptr && !stringStream->isGood()))
6297 return fail();
6298
6299 std::uint64_t handleEndBit = 0;
6300 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
6301 return fail();
6302 // R13-R2004 modeler bodies contain opaque ACIS/SAT data between this
6303 // header and the common handles. Do not interpret that payload: jump to
6304 // the bounded object-data end exactly as the surface/proxy readers do.
6305 dwgBuffer handleProbe = sourceBuf->forkIndependent();
6306 if (v <= DRW::AC1018) {
6307 if (!handleProbe.setPosition(bodyEndBit >> 3))
6308 return fail();
6309 handleProbe.setBitPos(static_cast<std::uint8_t>(bodyEndBit & 7u));
6310 if (!handleProbe.isGood())
6311 return fail();
6312 }
6313 ret = DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit);
6314 if (!ret || !handleProbe.isGood())
6315 return fail();
6316 std::uint32_t parsedHistoryHandle = 0;
6317 if (eType == DRW::E3DSOLID && v > DRW::AC1018
6318 && proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
6319 dwgHandle historyH;
6320 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
6321 historyH))
6322 return fail();
6323 parsedHistoryHandle = historyH.ref;
6324 DRW_DBG(" 3DSOLID history Handle: ")DRW_dbg::dbg(" 3DSOLID history Handle: ");
6325 DRW_DBGHL(historyH.code, historyH.size, historyH.ref)DRW_dbg::dbgHL(historyH.code, historyH.size, historyH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
6326 }
6327
6328 if (handleProbe.size() > std::numeric_limits<std::uint32_t>::max())
6329 return fail();
6330 m_bodyBitSize = bs;
6331 m_objectSize = static_cast<std::uint32_t>(probe.size());
6332 m_isEmpty = parsedIsEmpty;
6333 m_hasModelerData = parsedHasModelerData;
6334 m_modelerDataUnknownBit = parsedUnknownBit;
6335 m_modelerVersion = parsedModelerVersion;
6336 m_historyHandle = parsedHistoryHandle;
6337 *sourceBuf = handleProbe;
6338 return true;
6339}
6340
6341bool DRW_ModelerGeometry::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
6342 switch (code) {
6343 case 1:
6344 case 3:
6345 if (!appendTextBytesChecked(m_rawBytes, reader->getString(),
6346 dwgSafety::MaxBufferSize))
6347 return false;
6348 break;
6349 case 70: {
6350 const int value = reader->getInt32();
6351 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
6352 return false;
6353 m_modelerVersion = static_cast<std::uint16_t>(value);
6354 break;
6355 }
6356 case 350:
6357 case 360:
6358 m_historyHandle = static_cast<std::uint32_t>(reader->getHandleString());
6359 break;
6360 case 310:
6361 {
6362 std::vector<std::uint8_t> decoded;
6363 if (!decodeHexBytes(reader->getString(), decoded))
6364 return false;
6365 if (!appendBytesChecked(m_rawBytes, decoded,
6366 dwgSafety::MaxBufferSize))
6367 return false;
6368 }
6369 break;
6370 default:
6371 return DRW_Entity::parseCode(code, reader);
6372 }
6373 return true;
6374}
6375
6376void DRW_Mesh::resetDwgState() {
6377 DRW_Entity::reset();
6378 version = 2;
6379 blendCrease = false;
6380 subdivisionLevel = 0;
6381 subdivVertices.clear();
6382 vertices.clear();
6383 faces.clear();
6384 edges.clear();
6385 creases.clear();
6386 unknown = 0;
6387 propertyOverrides.clear();
6388 m_dxfMeshSubclassSeen = false;
6389 m_dxfState = 0;
6390 m_dxfVertexCount = -1;
6391 m_dxfFaceItemsRemaining = -1;
6392 m_dxfEdgeValuesRemaining = -1;
6393 m_dxfCreaseValuesRemaining = -1;
6394 m_dxfPending = 0;
6395 m_dxfEdgeFrom = -1;
6396 m_dxfOverrideEntityCount = -1;
6397 m_dxfOverridePropertyCount = -1;
6398}
6399
6400// DRW_Mesh::parseDwg — AcDbSubDMesh, field order shared by ACadSharp and
6401// dwgTs. DWG group 91 is the subdivision level, not a refined-vertex count;
6402// the following BL is the level-0 vertex count.
6403bool DRW_Mesh::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
6404 resetDwgState();
6405 dwgBuffer *sourceBuf = buf;
6406 auto fail = [this, sourceBuf]() {
6407 if (sourceBuf != nullptr)
6408 sourceBuf->invalidate();
6409 resetDwgState();
6410 return false;
6411 };
6412 if (sourceBuf == nullptr)
6413 return fail();
6414
6415 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
6416 dwgBuffer stringProbe = sourceBuf->forkIndependent();
6417 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : nullptr;
6418 if (!DRW_Entity::parseDwg(v, &bodyProbe, stringStream, bs))
6419 return fail();
6420 buf = &bodyProbe;
6421 DRW_DBG("\n***************************** parsing MESH (AcDbSubDMesh) *****************\n")DRW_dbg::dbg("\n***************************** parsing MESH (AcDbSubDMesh) *****************\n"
)
;
6422 std::uint64_t bodyEndBit = 0;
6423 if (!entityBodyDataEndBit(
6424 stringStream != nullptr ? *stringStream : bodyProbe,
6425 v, objSize, bodyEndBit))
6426 return fail();
6427
6428 auto readCount = [&](std::uint32_t minimumBitsPerItem,
6429 std::uint32_t& count) {
6430 return readTableBodyCount(
6431 v, buf, static_cast<std::uint32_t>(bodyEndBit),
6432 DRW_Mesh::kMaxMeshItems, minimumBitsPerItem, count);
6433 };
6434
6435 std::uint16_t parsedVersion = 0;
6436 bool parsedBlendCrease = false;
6437 std::int32_t parsedSubdivisionLevel = 0;
6438 if (!readBoundedBitShort(*buf, bodyEndBit, parsedVersion)
6439 || !readBoundedBit(*buf, bodyEndBit, parsedBlendCrease)
6440 || !readBoundedBitLong(*buf, bodyEndBit, parsedSubdivisionLevel)
6441 || parsedSubdivisionLevel < 0) {
6442 return fail();
6443 }
6444
6445 std::uint32_t nVert = 0; // BL vertex count (92)
6446 if (!readCount(6, nVert)) return fail(); // minimum 3BD width
6447 std::vector<DRW_Coord> parsedVertices;
6448 if (!DRW::reserve(parsedVertices, static_cast<int>(nVert)))
6449 return fail();
6450 std::vector<std::vector<std::int32_t>> parsedFaces;
6451 std::vector<std::pair<std::int32_t, std::int32_t>> parsedEdges;
6452 std::vector<double> parsedCreases;
6453 for (std::uint32_t i = 0; i < nVert; ++i) {
6454 DRW_Coord point;
6455 if (!readBoundedBitCoord(*buf, bodyEndBit, point))
6456 return fail();
6457 parsedVertices.push_back(point);
6458 }
6459 const auto finite = [](const DRW_Coord& point) {
6460 return std::isfinite(point.x) && std::isfinite(point.y)
6461 && std::isfinite(point.z);
6462 };
6463 if (!buf->isGood()
6464 || (stringStream != nullptr && !stringStream->isGood())
6465 || !std::all_of(parsedVertices.begin(), parsedVertices.end(), finite))
6466 return fail();
6467
6468 // faces (93) is a FLAT BL stream of length num_faces; each face is
6469 // [count, idx0, idx1, ...]. num_faces is the stream length, not the polygon
6470 // count — group on the fly.
6471 std::uint32_t faceItems = 0; // BL face-list size (93)
6472 if (!readCount(2, faceItems)) return fail();
6473 std::int32_t remaining = static_cast<std::int32_t>(faceItems);
6474 while (remaining > 0 && buf->isGood()) {
6475 std::int32_t cnt = 0;
6476 if (!readBoundedBitLong(*buf, bodyEndBit, cnt))
6477 return fail();
6478 --remaining;
6479 if (cnt < 0 || cnt > remaining)
6480 return fail();
6481 std::vector<std::int32_t> face;
6482 if (!DRW::reserve(face, cnt))
6483 return fail();
6484 for (std::int32_t j = 0; j < cnt; ++j) {
6485 std::int32_t index = 0;
6486 if (!readBoundedBitLong(*buf, bodyEndBit, index))
6487 return fail();
6488 if (index < 0 || static_cast<std::uint32_t>(index) >= nVert)
6489 return fail();
6490 face.push_back(index);
6491 --remaining;
6492 }
6493 if (!buf->isGood()) return fail();
6494 parsedFaces.push_back(std::move(face));
6495 }
6496 if (remaining != 0 || !buf->isGood()) return fail();
6497
6498 std::uint32_t nEdges = 0; // BL edge count (94)
6499 if (!readCount(4, nEdges)) return fail(); // two minimum-width BL values
6500 if (!DRW::reserve(parsedEdges, static_cast<int>(nEdges)))
6501 return fail();
6502 for (std::uint32_t i = 0; i < nEdges; ++i) {
6503 std::int32_t from = 0;
6504 std::int32_t to = 0;
6505 if (!readBoundedBitLong(*buf, bodyEndBit, from)
6506 || !readBoundedBitLong(*buf, bodyEndBit, to))
6507 return fail();
6508 if (from < 0 || to < 0
6509 || static_cast<std::uint32_t>(from) >= nVert
6510 || static_cast<std::uint32_t>(to) >= nVert)
6511 return fail();
6512 parsedEdges.emplace_back(from, to);
6513 }
6514 if (!buf->isGood()) return fail();
6515
6516 std::uint32_t nCrease = 0; // BL crease count (95)
6517 if (!readCount(2, nCrease)) return fail(); // minimum-width BD value
6518 if (!DRW::reserve(parsedCreases, static_cast<int>(nCrease)))
6519 return fail();
6520 for (std::uint32_t i = 0; i < nCrease; ++i) {
6521 double crease = 0.0;
6522 if (!readBoundedBitDouble(*buf, bodyEndBit, crease))
6523 return fail();
6524 parsedCreases.push_back(crease);
6525 }
6526 if (!buf->isGood()
6527 || !std::all_of(parsedCreases.begin(), parsedCreases.end(),
6528 [](double value) { return std::isfinite(value); }))
6529 return fail();
6530
6531 std::int32_t parsedUnknown = 0;
6532 if (!readBoundedBitLong(*buf, bodyEndBit, parsedUnknown))
6533 return fail(); // trailing unknown (BL)
6534
6535 std::uint64_t handleEndBit = 0;
6536 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
6537 return fail();
6538 dwgBuffer handleProbe = bodyProbe.forkIndependent();
6539 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
6540 || !handleProbe.isGood())
6541 return fail();
6542 vertices = std::move(parsedVertices);
6543 faces = std::move(parsedFaces);
6544 edges = std::move(parsedEdges);
6545 creases = std::move(parsedCreases);
6546 version = parsedVersion;
6547 blendCrease = parsedBlendCrease;
6548 subdivisionLevel = parsedSubdivisionLevel;
6549 unknown = parsedUnknown;
6550 *sourceBuf = handleProbe;
6551 return true;
6552}
6553
6554// DRW_Mesh::parseCode — DXF read (codes 71/72/91/92/10·20·30/93/90/94/95/140).
6555// The 90 stream is shared by faces (after 93) and edges (after 94); m_dxfState
6556// sequences which one is being filled (mirrors DRW_Image::parseCode's stateful
6557// 91/14/24 WIPEOUT-vertex accumulation). After the crease stream, group 90
6558// starts the bounded property-override records described by AcDbSubDMesh.
6559bool DRW_Mesh::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
6560 try {
6561 switch (code) {
6562 case 71: {
6563 const std::int32_t value = reader->getInt32();
6564 if (value < 0 || value > std::numeric_limits<std::int16_t>::max())
6565 return false;
6566 version = static_cast<std::uint16_t>(value);
6567 return true;
6568 }
6569 case 72: {
6570 const std::int32_t value = reader->getInt32();
6571 if (value != 0 && value != 1)
6572 return false;
6573 blendCrease = value != 0;
6574 return true;
6575 }
6576 case 91: {
6577 const std::int32_t value = reader->getInt32();
6578 if (m_dxfOverrideEntityCount >= 0) {
6579 if (m_dxfState != 96
6580 || (!propertyOverrides.empty() && m_dxfOverridePropertyCount != 0)
6581 || propertyOverrides.size() >= static_cast<std::size_t>(m_dxfOverrideEntityCount))
6582 return false;
6583 propertyOverrides.push_back({value, {}});
6584 m_dxfOverridePropertyCount = -1;
6585 return true;
6586 }
6587 if (value < 0)
6588 return false;
6589 subdivisionLevel = value;
6590 return true;
6591 }
6592 case 100:
6593 if (reader->getString() == "AcDbSubDMesh")
6594 m_dxfMeshSubclassSeen = true;
6595 return true;
6596 case 92: { // base-vertex count; later 92 values belong to overrides
6597 if (!m_dxfMeshSubclassSeen)
6598 return DRW_Entity::parseCode(code, reader);
6599 if (m_dxfVertexCount >= 0) {
6600 if (m_dxfOverrideEntityCount < 0 || m_dxfState != 96
6601 || propertyOverrides.empty() || m_dxfOverridePropertyCount >= 0)
6602 return false;
6603 const std::int32_t count = reader->getInt32();
6604 if (!isValidCount(count, 4))
6605 return false;
6606 if (!DRW::reserve(propertyOverrides.back().propertyTypes, count))
6607 return false;
6608 m_dxfOverridePropertyCount = count;
6609 return true;
6610 }
6611 const std::int32_t count = reader->getInt32();
6612 if (!isValidCount(count, DRW_Mesh::kMaxMeshItems))
6613 return false;
6614 if (!DRW::reserve(vertices, count))
6615 return false;
6616 m_dxfVertexCount = count;
6617 m_dxfState = 92;
6618 return true;
6619 }
6620 case 10:
6621 if (m_dxfState != 92 || m_dxfVertexCount < 0
6622 || vertices.size() >= static_cast<std::size_t>(m_dxfVertexCount))
6623 return false;
6624 {
6625 const double value = reader->getDouble();
6626 if (!std::isfinite(value))
6627 return false;
6628 vertices.emplace_back();
6629 vertices.back().x = value;
6630 }
6631 return true;
6632 case 20:
6633 if (m_dxfState != 92 || vertices.empty())
6634 return false;
6635 {
6636 const double value = reader->getDouble();
6637 if (!std::isfinite(value))
6638 return false;
6639 vertices.back().y = value;
6640 }
6641 return true;
6642 case 30:
6643 if (m_dxfState != 92 || vertices.empty())
6644 return false;
6645 {
6646 const double value = reader->getDouble();
6647 if (!std::isfinite(value))
6648 return false;
6649 vertices.back().z = value;
6650 }
6651 return true;
6652 case 93: { // face-list item count
6653 if (m_dxfVertexCount < 0
6654 || vertices.size() != static_cast<std::size_t>(m_dxfVertexCount))
6655 return false;
6656 const std::int32_t count = reader->getInt32();
6657 if (!isValidCount(count, DRW_Mesh::kMaxMeshItems))
6658 return false;
6659 m_dxfState = 93;
6660 m_dxfFaceItemsRemaining = count;
6661 m_dxfPending = 0;
6662 return true;
6663 }
6664 case 94: { // edge count; each edge consumes two code-90 values
6665 if (m_dxfFaceItemsRemaining != 0 || m_dxfPending != 0)
6666 return false;
6667 const std::int32_t count = reader->getInt32();
6668 if (!isValidCount(count, DRW_Mesh::kMaxMeshItems))
6669 return false;
6670 m_dxfState = 94;
6671 m_dxfEdgeValuesRemaining = count * 2;
6672 m_dxfEdgeFrom = -1;
6673 if (!DRW::reserve(edges, count))
6674 return false;
6675 return true;
6676 }
6677 case 90: {
6678 const std::int32_t val = reader->getInt32();
6679 if (m_dxfState == 95) {
6680 if (m_dxfCreaseValuesRemaining != 0)
6681 return false;
6682 if (m_dxfOverrideEntityCount >= 0)
6683 return false;
6684 if (!isValidCount(val, DRW_Mesh::kMaxMeshItems))
6685 return false;
6686 m_dxfOverrideEntityCount = val;
6687 m_dxfOverridePropertyCount = val == 0 ? 0 : -1;
6688 m_dxfState = 96;
6689 } else if (m_dxfState == 96) {
6690 if (m_dxfOverrideEntityCount < 0 || propertyOverrides.empty()
6691 || m_dxfOverridePropertyCount <= 0 || val < 0 || val > 3)
6692 return false;
6693 propertyOverrides.back().propertyTypes.push_back(val);
6694 --m_dxfOverridePropertyCount;
6695 } else if (m_dxfState == 93) {
6696 if (m_dxfFaceItemsRemaining <= 0)
6697 return false;
6698 if (m_dxfPending == 0) {
6699 if (val < 3 || val > m_dxfFaceItemsRemaining - 1)
6700 return false;
6701 faces.emplace_back();
6702 m_dxfPending = val;
6703 --m_dxfFaceItemsRemaining;
6704 } else {
6705 if (val < 0 || val >= m_dxfVertexCount)
6706 return false;
6707 if (!faces.empty())
6708 faces.back().push_back(val);
6709 --m_dxfFaceItemsRemaining;
6710 --m_dxfPending;
6711 }
6712 } else if (m_dxfState == 94) {
6713 if (m_dxfEdgeValuesRemaining <= 0 || val < 0 || val >= m_dxfVertexCount)
6714 return false;
6715 --m_dxfEdgeValuesRemaining;
6716 if (m_dxfEdgeFrom < 0)
6717 m_dxfEdgeFrom = val;
6718 else {
6719 edges.emplace_back(m_dxfEdgeFrom, val);
6720 m_dxfEdgeFrom = -1;
6721 }
6722 } else {
6723 return false;
6724 }
6725 return true;
6726 }
6727 case 95: { // edge crease count
6728 if (m_dxfEdgeValuesRemaining != 0 || m_dxfEdgeFrom != -1)
6729 return false;
6730 const std::int32_t count = reader->getInt32();
6731 if (!isValidCount(count, DRW_Mesh::kMaxMeshItems))
6732 return false;
6733 m_dxfState = 95;
6734 m_dxfCreaseValuesRemaining = count;
6735 if (!DRW::reserve(creases, count))
6736 return false;
6737 return true;
6738 }
6739 case 140:
6740 if (m_dxfState != 95 || m_dxfCreaseValuesRemaining <= 0)
6741 return false;
6742 {
6743 const double value = reader->getDouble();
6744 if (!std::isfinite(value))
6745 return false;
6746 creases.push_back(value);
6747 }
6748 --m_dxfCreaseValuesRemaining;
6749 return true;
6750 default:
6751 return DRW_Entity::parseCode(code, reader);
6752 }
6753 } catch (...) {
6754 return false;
6755 }
6756}
6757
6758bool DRW_Mesh::validateGeometry() const {
6759 if (subdivisionLevel < 0
6760 || vertices.size() > static_cast<std::size_t>(kMaxMeshItems)
6761 || edges.size() > static_cast<std::size_t>(kMaxMeshItems)
6762 || faces.size() > static_cast<std::size_t>(kMaxMeshItems)
6763 || creases.size() > static_cast<std::size_t>(kMaxMeshItems))
6764 return false;
6765
6766 const auto finite = [](const DRW_Coord& point) {
6767 return std::isfinite(point.x) && std::isfinite(point.y)
6768 && std::isfinite(point.z);
6769 };
6770 if (!std::all_of(vertices.cbegin(), vertices.cend(), finite)
6771 || !std::all_of(creases.cbegin(), creases.cend(),
6772 [](double value) { return std::isfinite(value); }))
6773 return false;
6774 std::int32_t faceStreamCount = 0;
6775 for (const auto& face : faces) {
6776 if (face.size() < 3
6777 || face.size() > static_cast<std::size_t>(kMaxMeshItems - 1)
6778 || faceStreamCount > kMaxMeshItems
6779 - static_cast<std::int32_t>(face.size() + 1))
6780 return false;
6781 for (std::int32_t index : face) {
6782 if (index < 0
6783 || static_cast<std::size_t>(index) >= vertices.size())
6784 return false;
6785 }
6786 faceStreamCount += static_cast<std::int32_t>(face.size() + 1);
6787 }
6788 for (const auto& edge : edges) {
6789 if (edge.first < 0 || edge.second < 0
6790 || static_cast<std::size_t>(edge.first) >= vertices.size()
6791 || static_cast<std::size_t>(edge.second) >= vertices.size())
6792 return false;
6793 }
6794 return true;
6795}
6796
6797bool DRW_Mesh::validateDxf() const {
6798 if (!m_dxfMeshSubclassSeen
6799 || m_dxfVertexCount < 0
6800 || vertices.size() != static_cast<std::size_t>(m_dxfVertexCount)
6801 || m_dxfFaceItemsRemaining != 0
6802 || m_dxfPending != 0
6803 || m_dxfEdgeValuesRemaining != 0
6804 || m_dxfEdgeFrom != -1
6805 || m_dxfCreaseValuesRemaining != 0
6806 || (m_dxfOverrideEntityCount >= 0
6807 && (m_dxfOverridePropertyCount < 0
6808 || m_dxfOverridePropertyCount != 0
6809 || propertyOverrides.size() != static_cast<std::size_t>(m_dxfOverrideEntityCount))))
6810 return false;
6811 for (const auto& overrideData : propertyOverrides) {
6812 if (overrideData.subEntityMarker < 0
6813 || overrideData.propertyTypes.size() > 4u)
6814 return false;
6815 if (!std::all_of(overrideData.propertyTypes.cbegin(),
6816 overrideData.propertyTypes.cend(),
6817 [](std::int32_t type) { return type >= 0 && type <= 3; }))
6818 return false;
6819 }
6820 return validateGeometry();
6821}
6822
6823bool DRW_Mesh::validateDxfOutput() const {
6824 if (version > static_cast<std::uint16_t>(std::numeric_limits<std::int16_t>::max())
6825 || propertyOverrides.size() > static_cast<std::size_t>(kMaxMeshItems))
6826 return false;
6827 for (const auto& overrideData : propertyOverrides) {
6828 if (overrideData.subEntityMarker < 0
6829 || overrideData.propertyTypes.size() > 4u
6830 || !std::all_of(overrideData.propertyTypes.cbegin(),
6831 overrideData.propertyTypes.cend(),
6832 [](std::int32_t type) { return type >= 0 && type <= 3; }))
6833 return false;
6834 }
6835 return validateGeometry();
6836}
6837
6838bool DRW_Mesh::encodeDwg(DRW::Version dwgVersion, dwgBufferW *buf, std::uint32_t bs,
6839 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
6840 (void)bs; (void)strBuf;
6841 if (!validateGeometry())
6842 return false;
6843
6844 std::int32_t faceStreamCount = 0;
6845 for (const auto& face : faces)
6846 faceStreamCount += static_cast<std::int32_t>(face.size() + 1);
6847
6848 oType = kDwgClassNum;
6849 if (!encodeDwgCommon(dwgVersion, buf)) return false;
6850
6851 buf->putBitShort(version);
6852 buf->putBit(blendCrease ? 1 : 0);
6853
6854 buf->putBitLong(subdivisionLevel);
6855
6856 buf->putBitLong(static_cast<std::int32_t>(vertices.size()));
6857 for (const DRW_Coord& vertex : vertices)
6858 buf->put3BitDouble(vertex);
6859
6860 buf->putBitLong(faceStreamCount);
6861 for (const auto& face : faces) {
6862 buf->putBitLong(static_cast<std::int32_t>(face.size()));
6863 for (std::int32_t index : face)
6864 buf->putBitLong(index);
6865 }
6866
6867 buf->putBitLong(static_cast<std::int32_t>(edges.size()));
6868 for (const auto& edge : edges) {
6869 buf->putBitLong(edge.first);
6870 buf->putBitLong(edge.second);
6871 }
6872
6873 buf->putBitLong(static_cast<std::int32_t>(creases.size()));
6874 for (double crease : creases)
6875 buf->putBitDouble(crease);
6876
6877 buf->putBitLong(unknown);
6878
6879 return encodeDwgEntHandle(dwgVersion, buf, handleBuf);
6880}
6881
6882bool DRW_Shape::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
6883 switch (code) {
6884 case 2:
6885 m_styleName = reader->getUtf8String();
6886 break;
6887 case 10:
6888 m_insertionPoint.x = reader->getDouble();
6889 break;
6890 case 20:
6891 m_insertionPoint.y = reader->getDouble();
6892 break;
6893 case 30:
6894 m_insertionPoint.z = reader->getDouble();
6895 break;
6896 case 39:
6897 m_thickness = reader->getDouble();
6898 break;
6899 case 40:
6900 m_scale = reader->getDouble();
6901 break;
6902 case 41:
6903 m_widthFactor = reader->getDouble();
6904 break;
6905 case 50:
6906 m_rotation = reader->getDouble() / ARAD57.29577951308232;
6907 break;
6908 case 51:
6909 m_oblique = reader->getDouble() / ARAD57.29577951308232;
6910 break;
6911 case 210:
6912 m_extrusion.x = reader->getDouble();
6913 break;
6914 case 220:
6915 m_extrusion.y = reader->getDouble();
6916 break;
6917 case 230:
6918 m_extrusion.z = reader->getDouble();
6919 break;
6920 default:
6921 return DRW_Entity::parseCode(code, reader);
6922 }
6923 return true;
6924}
6925
6926void DRW_Shape::resetDwgState() {
6927 DRW_Entity::reset();
6928 m_insertionPoint = DRW_Coord{0.0, 0.0, 0.0};
6929 m_scale = 1.0;
6930 m_rotation = 0.0;
6931 m_widthFactor = 1.0;
6932 m_oblique = 0.0;
6933 m_thickness = 0.0;
6934 m_shapeIndex = 0;
6935 m_extrusion = DRW_Coord{0.0, 0.0, 1.0};
6936 m_shapeFileHandle = 0;
6937 m_objectSize = 0;
6938 m_bodyBitSize = 0;
6939 m_rawBytes.clear();
6940}
6941
6942bool DRW_Shape::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
6943 resetDwgState();
6944 dwgBuffer *sourceBuf = buf;
6945 auto fail = [this, sourceBuf]() {
6946 if (sourceBuf != nullptr)
6947 sourceBuf->invalidate();
6948 resetDwgState();
6949 return false;
6950 };
6951 if (sourceBuf == nullptr)
6952 return fail();
6953
6954 m_bodyBitSize = bs;
6955 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
6956 if (!DRW_Entity::parseDwgCommon(v, &bodyProbe, bs))
6957 return fail();
6958 DRW_DBG("\n***************************** parsing SHAPE *****************************\n")DRW_dbg::dbg("\n***************************** parsing SHAPE *****************************\n"
)
;
6959
6960 const std::uint64_t bodyEndBit = dwgDataEndBit;
6961 DRW_Coord insertionPoint;
6962 DRW_Coord extrusion;
6963 double scale = 0.0;
6964 double rotation = 0.0;
6965 double widthFactor = 0.0;
6966 double oblique = 0.0;
6967 double thickness = 0.0;
6968 std::uint16_t shapeIndex = 0;
6969 if (!readBounded3BitDouble(bodyProbe, bodyEndBit, insertionPoint)
6970 || !readBoundedBitDouble(bodyProbe, bodyEndBit, scale)
6971 || !readBoundedBitDouble(bodyProbe, bodyEndBit, rotation)
6972 || !readBoundedBitDouble(bodyProbe, bodyEndBit, widthFactor)
6973 || !readBoundedBitDouble(bodyProbe, bodyEndBit, oblique)
6974 || !readBoundedBitDouble(bodyProbe, bodyEndBit, thickness)
6975 || !readBoundedBitShort(bodyProbe, bodyEndBit, shapeIndex)
6976 || !readBounded3BitDouble(bodyProbe, bodyEndBit, extrusion))
6977 return fail();
6978 const auto finite = [](const DRW_Coord& point) {
6979 return std::isfinite(point.x) && std::isfinite(point.y)
6980 && std::isfinite(point.z);
6981 };
6982 if (!bodyProbe.isGood() || !finite(insertionPoint) || !finite(extrusion)
6983 || !std::isfinite(scale) || !std::isfinite(rotation)
6984 || !std::isfinite(widthFactor) || !std::isfinite(oblique)
6985 || !std::isfinite(thickness))
6986 return fail();
6987
6988 std::uint64_t handleEndBit = 0;
6989 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
6990 return fail();
6991 dwgBuffer handleProbe = bodyProbe.forkIndependent();
6992 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
6993 || !handleProbe.isGood())
6994 return fail();
6995
6996 // DwgObjectFrame removes the CRC trailer before exposing the body. The
6997 // SHAPEFILE hard pointer is therefore mandatory and must be parsed even
6998 // when only its exact handle bytes remain.
6999 dwgHandle shapeFileH;
7000 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
7001 shapeFileH)
7002 || shapeFileH.ref == 0)
7003 return fail();
7004
7005 m_insertionPoint = insertionPoint;
7006 m_scale = scale;
7007 m_rotation = rotation;
7008 m_widthFactor = widthFactor;
7009 m_oblique = oblique;
7010 m_thickness = thickness;
7011 m_shapeIndex = shapeIndex;
7012 m_extrusion = extrusion;
7013 m_shapeFileHandle = shapeFileH.ref;
7014 DRW_DBG(" SHAPEFILE Handle: ")DRW_dbg::dbg(" SHAPEFILE Handle: ");
7015 DRW_DBGHL(shapeFileH.code, shapeFileH.size, shapeFileH.ref)DRW_dbg::dbgHL(shapeFileH.code, shapeFileH.size, shapeFileH.ref
)
;
7016 DRW_DBG("\n")DRW_dbg::dbg("\n");
7017 *sourceBuf = handleProbe;
7018 return true;
7019}
7020
7021// Phase 6.1: SHAPE encoder (fixed oType 33). Exact inverse of parseDwg above.
7022// Without this override a SHAPE would encode as a LINE (default DRW_Entity).
7023bool DRW_Shape::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
7024 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7025 (void)bs; (void)strBuf;
7026 const auto finite = [](const DRW_Coord& point) {
7027 return std::isfinite(point.x) && std::isfinite(point.y)
7028 && std::isfinite(point.z);
7029 };
7030 if (!finite(m_insertionPoint) || !finite(m_extrusion)
7031 || !std::isfinite(m_scale) || !std::isfinite(m_rotation)
7032 || !std::isfinite(m_widthFactor) || !std::isfinite(m_oblique)
7033 || !std::isfinite(m_thickness) || m_shapeFileHandle == 0) {
7034 return false;
7035 }
7036 oType = 33; // SHAPE class id — see dwgreader.cpp case 33
7037 if (!encodeDwgCommon(version, buf)) return false;
7038
7039 buf->put3BitDouble(m_insertionPoint);
7040 buf->putBitDouble(m_scale);
7041 buf->putBitDouble(m_rotation);
7042 buf->putBitDouble(m_widthFactor);
7043 buf->putBitDouble(m_oblique);
7044 buf->putBitDouble(m_thickness);
7045 buf->putBitShort(m_shapeIndex);
7046 buf->put3BitDouble(m_extrusion);
7047
7048 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
7049
7050 // Trailing SHAPEFILE style hard pointer (code 5), byte-count-sized.
7051 dwgHandle sH;
7052 sH.code = 5;
7053 sH.ref = m_shapeFileHandle;
7054 sH.size = 0;
7055 if (m_shapeFileHandle != 0) {
7056 std::uint32_t t = m_shapeFileHandle;
7057 while (t != 0) { t >>= 8; ++sH.size; }
7058 } else {
7059 sH.code = 0; // null handle
7060 }
7061 (handleBuf ? handleBuf : buf)->putHandle(sH);
7062 return true;
7063}
7064
7065bool DRW_Ole2Frame::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
7066 const auto readUnsigned = [&](std::uint32_t maximum,
7067 std::uint32_t& target) {
7068 const int value = reader->getInt32();
7069 if (value < 0 || static_cast<std::uint32_t>(value) > maximum)
7070 return false;
7071 target = static_cast<std::uint32_t>(value);
7072 return true;
7073 };
7074
7075 switch (code) {
7076 case 3:
7077 m_oleClient = reader->getUtf8String();
7078 break;
7079 case 10:
7080 m_pt1.x = reader->getDouble();
7081 break;
7082 case 20:
7083 m_pt1.y = reader->getDouble();
7084 break;
7085 case 30:
7086 m_pt1.z = reader->getDouble();
7087 break;
7088 case 11:
7089 m_pt2.x = reader->getDouble();
7090 break;
7091 case 21:
7092 m_pt2.y = reader->getDouble();
7093 break;
7094 case 31:
7095 m_pt2.z = reader->getDouble();
7096 break;
7097 case 70: {
7098 std::uint32_t value = 0;
7099 if (!readUnsigned(std::numeric_limits<std::uint16_t>::max(), value))
7100 return false;
7101 m_oleVersion = static_cast<std::uint16_t>(value);
7102 break;
7103 }
7104 case 71: {
7105 std::uint32_t value = 0;
7106 if (!readUnsigned(std::numeric_limits<std::uint16_t>::max(), value))
7107 return false;
7108 m_flags = static_cast<std::uint16_t>(value);
7109 break;
7110 }
7111 case 72: {
7112 std::uint32_t value = 0;
7113 if (!readUnsigned(std::numeric_limits<std::uint16_t>::max(), value))
7114 return false;
7115 m_mode = static_cast<std::uint16_t>(value);
7116 break;
7117 }
7118 case 73: {
7119 std::uint32_t value = 0;
7120 if (!readUnsigned(std::numeric_limits<std::uint8_t>::max(), value))
7121 return false;
7122 m_lockAspect = static_cast<std::uint8_t>(value);
7123 break;
7124 }
7125 case 90: {
7126 std::uint32_t value = 0;
7127 if (!readUnsigned(kMaxOlePayloadBytes, value))
7128 return false;
7129 m_declaredPayloadLength = value;
7130 m_dxfPayloadLengthSpecified = true;
7131 m_payloadPresent = value != 0;
7132 break;
7133 }
7134 case 310: {
7135 std::vector<std::uint8_t> bytes;
7136 if (!decodeHexBytes(reader->getString(), bytes))
7137 return false;
7138 if (!appendBytesChecked(m_payloadBytes, bytes,
7139 kMaxOlePayloadBytes)) {
7140 m_payloadTooLarge = m_payloadBytes.size() > kMaxOlePayloadBytes
7141 || bytes.size() > kMaxOlePayloadBytes -
7142 std::min(m_payloadBytes.size(),
7143 static_cast<std::size_t>(
7144 kMaxOlePayloadBytes));
7145 return false;
7146 }
7147 m_payloadPresent = true;
7148 break;
7149 }
7150 default:
7151 return DRW_Entity::parseCode(code, reader);
7152 }
7153 return true;
7154}
7155
7156void DRW_Ole2Frame::resetDwgState() {
7157 DRW_Entity::reset();
7158 m_flags = 0;
7159 m_mode = 0;
7160 m_declaredPayloadLength = 0;
7161 m_payloadByteCount = 0;
7162 m_payloadStartBit = 0;
7163 m_payloadPresent = false;
7164 m_payloadTruncated = false;
7165 m_payloadTooLarge = false;
7166 m_hasR2000TrailingByte = false;
7167 m_r2000TrailingByte = 0;
7168 m_objectSize = 0;
7169 m_bodyBitSize = 0;
7170 m_payloadBytes.clear();
7171 m_pt1 = DRW_Coord{0.0, 0.0, 0.0};
7172 m_pt2 = DRW_Coord{0.0, 0.0, 0.0};
7173 m_lockAspect = 3;
7174 m_dxfPayloadLengthSpecified = false;
7175 m_oleVersion = 2;
7176 m_oleClient = "OLE";
7177 m_rawBytes.clear();
7178}
7179
7180bool DRW_Ole2Frame::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
7181 resetDwgState();
7182 dwgBuffer *sourceBuf = buf;
7183 auto fail = [this, sourceBuf]() {
7184 if (sourceBuf != nullptr)
7185 sourceBuf->invalidate();
7186 resetDwgState();
7187 return false;
7188 };
7189 if (sourceBuf == nullptr)
7190 return fail();
7191
7192 m_bodyBitSize = bs;
7193 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7194 if (!DRW_Entity::parseDwgCommon(v, &bodyProbe, bs))
7195 return fail();
7196 DRW_DBG("\n***************************** parsing OLE2FRAME ************************\n")DRW_dbg::dbg("\n***************************** parsing OLE2FRAME ************************\n"
)
;
7197
7198 const std::uint64_t bodyEndBit = dwgDataEndBit;
7199 std::uint16_t parsedFlags = 0;
7200 std::uint16_t parsedMode = 0;
7201 std::int32_t declaredLength = 0;
7202 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedFlags)
7203 || (v > DRW::AC1014
7204 && !readBoundedBitShort(bodyProbe, bodyEndBit, parsedMode))
7205 || !readBoundedBitLong(bodyProbe, bodyEndBit, declaredLength)
7206 || declaredLength < 0
7207 || static_cast<std::uint32_t>(declaredLength) > kMaxOlePayloadBytes)
7208 return fail();
7209
7210 const std::uint64_t payloadStartBit = currentDwgBit(&bodyProbe);
7211 const auto bodyRemainingBits = [&](const dwgBuffer& cursor,
7212 std::uint64_t& remaining) {
7213 const std::uint64_t currentBit = currentDwgBit(&cursor);
7214 if (v > DRW::AC1014 && objSize != 0) {
7215 if (currentBit > bodyEndBit)
7216 return false;
7217 remaining = bodyEndBit - currentBit;
7218 return true;
7219 }
7220 const int bytes = cursor.numRemainingBytes();
7221 if (bytes < 0)
7222 return false;
7223 remaining = static_cast<std::uint64_t>(bytes) * 8u;
7224 return true;
7225 };
7226
7227 std::uint64_t remainingBits = 0;
7228 if (!bodyRemainingBits(bodyProbe, remainingBits))
7229 return fail();
7230 const std::uint64_t trailingBits = v > DRW::AC1014 ? 8u : 0u;
7231 if (remainingBits < trailingBits
7232 || static_cast<std::uint64_t>(declaredLength)
7233 > (remainingBits - trailingBits) / 8u)
7234 return fail();
7235
7236 std::vector<std::uint8_t> payload;
7237 if (!DRW::resize(payload, declaredLength))
7238 return fail();
7239 if (!readBoundedBytes(bodyProbe, bodyEndBit, payload.data(),
7240 payload.size()))
7241 return fail();
7242
7243 std::uint8_t trailingByte = 0;
7244 bool hasTrailingByte = false;
7245 if (v > DRW::AC1014) {
7246 if (!bodyRemainingBits(bodyProbe, remainingBits)
7247 || (remainingBits != 0 && remainingBits < 8))
7248 return fail();
7249 if (remainingBits >= 8) {
7250 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, trailingByte))
7251 return fail();
7252 hasTrailingByte = true;
7253 }
7254 }
7255
7256 // Decode the frame rectangle (DXF 10/11) from the OLE header. AutoCAD/ODA do
7257 // NOT store pt1/pt2 as DWG fields; they live in the first ~0x80 bytes of the
7258 // payload as raw little-endian doubles. Guarded so a non-finite/short
7259 // payload simply leaves both points at the origin.
7260 DRW_Coord point1;
7261 DRW_Coord point2;
7262 if (payload.size() >= 0x4a && payload[0] == 0x80) {
7263 auto rd = [&](std::size_t off) {
7264 double value = 0.0;
7265 std::memcpy(&value, payload.data() + off, sizeof(double));
7266 return value;
7267 };
7268 const DRW_Coord upperLeft(rd(0x02), rd(0x0a), rd(0x12));
7269 const DRW_Coord lowerRight(rd(0x32), rd(0x3a), rd(0x42));
7270 if (std::isfinite(upperLeft.x) && std::isfinite(upperLeft.y)
7271 && std::isfinite(upperLeft.z) && std::isfinite(lowerRight.x)
7272 && std::isfinite(lowerRight.y) && std::isfinite(lowerRight.z)) {
7273 point1 = upperLeft;
7274 point2 = lowerRight;
7275 }
7276 }
7277
7278 std::uint64_t handleEndBit = 0;
7279 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
7280 return fail();
7281 dwgBuffer handleProbe = bodyProbe.forkIndependent();
7282 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
7283 || !handleProbe.isGood())
7284 return fail();
7285
7286 m_flags = parsedFlags;
7287 m_mode = parsedMode;
7288 m_declaredPayloadLength = static_cast<std::uint32_t>(declaredLength);
7289 m_payloadStartBit = payloadStartBit;
7290 m_payloadPresent = declaredLength > 0;
7291 m_payloadByteCount = static_cast<std::uint32_t>(declaredLength);
7292 m_payloadBytes = std::move(payload);
7293 m_hasR2000TrailingByte = hasTrailingByte;
7294 m_r2000TrailingByte = trailingByte;
7295 m_pt1 = point1;
7296 m_pt2 = point2;
7297 *sourceBuf = handleProbe;
7298 return true;
7299}
7300
7301// Phase 6.2: OLE2FRAME encoder (fixed oType 74). Inverse of parseDwg, emitting
7302// the captured opaque payload byte-for-byte. Without this override an OLE2FRAME
7303// would encode as a LINE.
7304bool DRW_Ole2Frame::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
7305 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
7306 (void)bs; (void)strBuf;
7307 if (m_payloadTooLarge || m_payloadTruncated
7308 || m_payloadBytes.size() > kMaxOlePayloadBytes
7309 || (m_payloadPresent
7310 && m_declaredPayloadLength != m_payloadBytes.size())) {
7311 return false;
7312 }
7313 oType = 74; // OLE2FRAME class id — see dwgreader.cpp case 74
7314 if (!encodeDwgCommon(version, buf)) return false;
7315
7316 buf->putBitShort(m_flags);
7317 if (version > DRW::AC1014)
7318 buf->putBitShort(m_mode);
7319 // Emit the actual captured length so the reader's data_size matches the
7320 // bytes that follow (avoids a declared-vs-actual mismatch on re-read).
7321 const std::uint32_t payloadLen = static_cast<std::uint32_t>(m_payloadBytes.size());
7322 buf->putBitLong(static_cast<std::int32_t>(payloadLen));
7323 if (payloadLen > 0)
7324 buf->putBytes(m_payloadBytes.data(), m_payloadBytes.size());
7325 // R2000+ Unknown RC (ODA §20.4.88): emitted UNCONDITIONALLY for version >
7326 // AC1014. parseDwg reads it whenever bytes remain before the handle stream
7327 // (which is always — handle data always follows), so gating the write on
7328 // m_hasR2000TrailingByte desynced a directly-constructed OLE2FRAME (the
7329 // default false): the parser consumed the first handle byte as this RC and
7330 // shifted the entity handle stream. Default m_r2000TrailingByte is 0, so
7331 // constructed entities align and round-tripped ones keep the captured byte.
7332 if (version > DRW::AC1014)
7333 buf->putRawChar8(m_r2000TrailingByte);
7334
7335 return encodeDwgEntHandle(version, buf, handleBuf);
7336}
7337
7338bool DRW_OleFrame::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
7339 switch (code) {
7340 case 70: {
7341 const int value = reader->getInt32();
7342 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
7343 return false;
7344 m_flags = static_cast<std::uint16_t>(value);
7345 break;
7346 }
7347 case 90: {
7348 const int value = reader->getInt32();
7349 if (value < 0
7350 || static_cast<std::uint32_t>(value) > kMaxOlePayloadBytes) {
7351 m_payloadTooLarge = value >= 0;
7352 return false;
7353 }
7354 m_declaredPayloadLength = static_cast<std::uint32_t>(value);
7355 m_dxfPayloadLengthSpecified = true;
7356 m_payloadPresent = value != 0;
7357 break;
7358 }
7359 case 310: {
7360 std::vector<std::uint8_t> bytes;
7361 if (!decodeHexBytes(reader->getString(), bytes))
7362 return false;
7363 if (!appendBytesChecked(m_payloadBytes, bytes,
7364 kMaxOlePayloadBytes)) {
7365 m_payloadTooLarge = m_payloadBytes.size() > kMaxOlePayloadBytes
7366 || bytes.size() > kMaxOlePayloadBytes -
7367 std::min(m_payloadBytes.size(),
7368 static_cast<std::size_t>(
7369 kMaxOlePayloadBytes));
7370 return false;
7371 }
7372 m_payloadPresent = true;
7373 break;
7374 }
7375 default:
7376 return DRW_Entity::parseCode(code, reader);
7377 }
7378 return true;
7379}
7380
7381void DRW_OleFrame::resetDwgState() {
7382 DRW_Entity::reset();
7383 m_flags = 0;
7384 m_mode = 0;
7385 m_declaredPayloadLength = 0;
7386 m_payloadByteCount = 0;
7387 m_payloadPresent = false;
7388 m_payloadTruncated = false;
7389 m_payloadTooLarge = false;
7390 m_objectSize = 0;
7391 m_bodyBitSize = 0;
7392 m_payloadBytes.clear();
7393 m_dxfPayloadLengthSpecified = false;
7394 m_rawBytes.clear();
7395}
7396
7397bool DRW_OleFrame::parseDwg(DRW::Version version, dwgBuffer *buf,
7398 std::uint32_t bs) {
7399 resetDwgState();
7400 dwgBuffer *sourceBuf = buf;
7401 auto fail = [this, sourceBuf]() {
7402 if (sourceBuf != nullptr)
7403 sourceBuf->invalidate();
7404 resetDwgState();
7405 return false;
7406 };
7407 if (sourceBuf == nullptr)
7408 return fail();
7409
7410 m_bodyBitSize = bs;
7411 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7412 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs))
7413 return fail();
7414
7415 const std::uint64_t bodyEndBit = dwgDataEndBit;
7416 std::uint16_t parsedFlags = 0;
7417 std::uint16_t parsedMode = 0;
7418 std::int32_t declaredLength = 0;
7419 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedFlags)
7420 || (version > DRW::AC1014
7421 && !readBoundedBitShort(bodyProbe, bodyEndBit, parsedMode))
7422 || !readBoundedBitLong(bodyProbe, bodyEndBit, declaredLength)
7423 || declaredLength < 0
7424 || static_cast<std::uint32_t>(declaredLength) > kMaxOlePayloadBytes)
7425 return fail();
7426
7427 std::uint64_t bodyRemainingBits = 0;
7428 const std::uint64_t currentBit = currentDwgBit(&bodyProbe);
7429 if (version > DRW::AC1014 && objSize != 0) {
7430 if (currentBit > bodyEndBit)
7431 return fail();
7432 bodyRemainingBits = bodyEndBit - currentBit;
7433 } else {
7434 const int remainingBytes = bodyProbe.numRemainingBytes();
7435 if (remainingBytes < 0)
7436 return fail();
7437 bodyRemainingBits = static_cast<std::uint64_t>(remainingBytes) * 8u;
7438 }
7439
7440 if (static_cast<std::uint64_t>(declaredLength) > bodyRemainingBits / 8u)
7441 return fail();
7442 std::vector<std::uint8_t> payload;
7443 if (!DRW::resize(payload, declaredLength))
7444 return fail();
7445 if (!readBoundedBytes(bodyProbe, bodyEndBit, payload.data(),
7446 payload.size()))
7447 return fail();
7448
7449 std::uint64_t handleEndBit = 0;
7450 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
7451 handleEndBit))
7452 return fail();
7453 dwgBuffer handleProbe = bodyProbe.forkIndependent();
7454 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
7455 handleEndBit)
7456 || !handleProbe.isGood())
7457 return fail();
7458
7459 m_flags = parsedFlags;
7460 m_mode = parsedMode;
7461 m_declaredPayloadLength = static_cast<std::uint32_t>(declaredLength);
7462 m_payloadPresent = declaredLength != 0;
7463 m_payloadByteCount = static_cast<std::uint32_t>(declaredLength);
7464 m_payloadBytes = std::move(payload);
7465 *sourceBuf = handleProbe;
7466 return true;
7467
7468}
7469
7470bool DRW_OleFrame::encodeDwg(DRW::Version version, dwgBufferW *buf,
7471 std::uint32_t bs, dwgBufferW *strBuf,
7472 dwgBufferW *handleBuf) {
7473 (void)bs;
7474 (void)strBuf;
7475 if (buf == nullptr || m_payloadTooLarge || m_payloadTruncated
7476 || m_payloadBytes.size() > kMaxOlePayloadBytes
7477 || (m_payloadPresent
7478 && m_declaredPayloadLength != m_payloadBytes.size())) {
7479 return false;
7480 }
7481
7482 oType = 43;
7483 if (!encodeDwgCommon(version, buf))
7484 return false;
7485 buf->putBitShort(m_flags);
7486 if (version > DRW::AC1014)
7487 buf->putBitShort(m_mode);
7488 const std::uint32_t payloadLength =
7489 static_cast<std::uint32_t>(m_payloadBytes.size());
7490 buf->putBitLong(static_cast<std::int32_t>(payloadLength));
7491 if (payloadLength != 0)
7492 buf->putBytes(m_payloadBytes.data(), m_payloadBytes.size());
7493 return encodeDwgEntHandle(version, buf, handleBuf);
7494}
7495
7496void DRW_Light::resetDwgState() {
7497 DRW_Entity::reset();
7498 m_classVersion = 0;
7499 m_name.clear();
7500 m_type = 0;
7501 m_status = false;
7502 m_color = 0;
7503 m_plotGlyph = false;
7504 m_intensity = 0.0;
7505 m_position = DRW_Coord{0.0, 0.0, 0.0};
7506 m_target = DRW_Coord{0.0, 0.0, 0.0};
7507 m_attenuationType = 0;
7508 m_useAttenuationLimits = false;
7509 m_attenuationStartLimit = 0.0;
7510 m_attenuationEndLimit = 0.0;
7511 m_hotspotAngle = 0.0;
7512 m_falloffAngle = 0.0;
7513 m_castShadows = false;
7514 m_shadowType = 0;
7515 m_shadowMapSize = 0;
7516 m_shadowMapSoftness = 0;
7517 m_hasPhotometricData = false;
7518 m_hasWebFile = false;
7519 m_webFile.clear();
7520 m_physicalIntensityMethod = 0;
7521 m_physicalIntensity = 0.0;
7522 m_illuminanceDistance = 0.0;
7523 m_lampColorType = 0;
7524 m_lampColorTemperature = 0.0;
7525 m_lampColorPreset = 0;
7526 m_webRotation = {1.0, 0.0, 0.0};
7527 m_extendedLightShape = 0;
7528 m_extendedLightLength = 0.0;
7529 m_extendedLightWidth = 0.0;
7530 m_extendedLightRadius = 0.0;
7531}
7532
7533bool DRW_Light::parseDwg(DRW::Version v, dwgBuffer *buf, std::uint32_t bs){
7534 resetDwgState();
7535 dwgBuffer *sourceBuf = buf;
7536 auto fail = [this, sourceBuf]() {
7537 if (sourceBuf != nullptr)
7538 sourceBuf->invalidate();
7539 resetDwgState();
7540 return false;
7541 };
7542 if (sourceBuf == nullptr)
7543 return fail();
7544
7545 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7546 dwgBuffer stringProbe = sourceBuf->forkIndependent();
7547 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : nullptr;
7548 if (!DRW_Entity::parseDwg(v, &bodyProbe, stringStream, bs)
7549 || !bodyProbe.isGood() || !stringProbe.isGood())
7550 return fail();
7551 DRW_DBG("\n***************************** parsing LIGHT *****************************\n")DRW_dbg::dbg("\n***************************** parsing LIGHT *****************************\n"
)
;
7552
7553 const std::uint64_t bodyDataEndBit = dwgDataEndBit;
7554 std::uint64_t stringEndBit = bodyDataEndBit;
7555 if (stringStream != nullptr && v > DRW::AC1021) {
7556 std::uint64_t ignoredBodyEndBit = 0;
7557 if (!entityBodyDataEndBit(*stringStream, v, objSize,
7558 ignoredBodyEndBit, &stringEndBit))
7559 return fail();
7560 }
7561
7562 std::int32_t parsedClassVersion = 0;
7563 UTF8STRINGstd::string parsedName;
7564 std::int32_t parsedType = 0;
7565 bool parsedStatus = false;
7566 std::uint32_t parsedColor = 0;
7567 bool parsedPlotGlyph = false;
7568 double parsedIntensity = 0.0;
7569 DRW_Coord parsedPosition;
7570 DRW_Coord parsedTarget;
7571 std::int32_t parsedAttenuationType = 0;
7572 bool parsedUseAttenuationLimits = false;
7573 double parsedAttenuationStartLimit = 0.0;
7574 double parsedAttenuationEndLimit = 0.0;
7575 double parsedHotspotAngle = 0.0;
7576 double parsedFalloffAngle = 0.0;
7577 bool parsedCastShadows = false;
7578 std::int32_t parsedShadowType = 0;
7579 std::uint16_t parsedShadowMapSize = 0;
7580 std::uint8_t parsedShadowMapSoftness = 0;
7581 if (!readBoundedBitLong(bodyProbe, bodyDataEndBit, parsedClassVersion)
7582 || (stringStream != nullptr
7583 ? !readBoundedVariableText(stringProbe, stringEndBit, v,
7584 parsedName)
7585 : !readBoundedVariableText(bodyProbe, bodyDataEndBit, v,
7586 parsedName))
7587 || !readBoundedBitLong(bodyProbe, bodyDataEndBit, parsedType)
7588 || !readBoundedBit(bodyProbe, bodyDataEndBit, parsedStatus)
7589 || !readBoundedCmColor(bodyProbe, stringStream, bodyDataEndBit, v,
7590 parsedColor, nullptr, nullptr, nullptr,
7591 nullptr, stringEndBit)
7592 || !readBoundedBit(bodyProbe, bodyDataEndBit, parsedPlotGlyph)
7593 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit, parsedIntensity)
7594 || !readBoundedBitCoord(bodyProbe, bodyDataEndBit, parsedPosition)
7595 || !readBoundedBitCoord(bodyProbe, bodyDataEndBit, parsedTarget)
7596 || !readBoundedBitLong(bodyProbe, bodyDataEndBit,
7597 parsedAttenuationType)
7598 || !readBoundedBit(bodyProbe, bodyDataEndBit,
7599 parsedUseAttenuationLimits)
7600 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7601 parsedAttenuationStartLimit)
7602 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7603 parsedAttenuationEndLimit)
7604 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7605 parsedHotspotAngle)
7606 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7607 parsedFalloffAngle)
7608 || !readBoundedBit(bodyProbe, bodyDataEndBit, parsedCastShadows)
7609 || !readBoundedBitLong(bodyProbe, bodyDataEndBit, parsedShadowType)
7610 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7611 parsedShadowMapSize)
7612 || !readBoundedRawChar8(bodyProbe, bodyDataEndBit,
7613 parsedShadowMapSoftness))
7614 return fail();
7615
7616 bool hasPhotometricData = false;
7617 bool hasWebFile = false;
7618 UTF8STRINGstd::string webFile;
7619 std::uint16_t physicalIntensityMethod = 0;
7620 double physicalIntensity = 0.0;
7621 double illuminanceDistance = 0.0;
7622 std::uint16_t lampColorType = 0;
7623 double lampColorTemperature = 0.0;
7624 std::uint16_t lampColorPreset = 0;
7625 DRW_Coord webRotation{1.0, 0.0, 0.0};
7626 std::uint16_t extendedLightShape = 0;
7627 double extendedLightLength = 0.0;
7628 double extendedLightWidth = 0.0;
7629 double extendedLightRadius = 0.0;
7630 if (v > DRW::AC1018
7631 && proxyEntityHasBits(bodyProbe, bodyDataEndBit, 1)) {
7632 if (!readBoundedBit(bodyProbe, bodyDataEndBit, hasPhotometricData))
7633 return fail();
7634 if (hasPhotometricData) {
7635 if (!readBoundedBit(bodyProbe, bodyDataEndBit, hasWebFile)
7636 || !readBoundedVariableText(stringProbe, stringEndBit, v,
7637 webFile)
7638 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7639 physicalIntensityMethod)
7640 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7641 physicalIntensity)
7642 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7643 illuminanceDistance)
7644 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7645 lampColorType)
7646 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7647 lampColorTemperature)
7648 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7649 lampColorPreset)
7650 || !readBoundedBitCoord(bodyProbe, bodyDataEndBit,
7651 webRotation)
7652 || !readBoundedBitShort(bodyProbe, bodyDataEndBit,
7653 extendedLightShape)
7654 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7655 extendedLightLength)
7656 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7657 extendedLightWidth)
7658 || !readBoundedBitDouble(bodyProbe, bodyDataEndBit,
7659 extendedLightRadius))
7660 return fail();
7661 }
7662 }
7663
7664 const auto finite = [](const DRW_Coord& point) {
7665 return std::isfinite(point.x) && std::isfinite(point.y)
7666 && std::isfinite(point.z);
7667 };
7668 if (!bodyProbe.isGood() || !stringProbe.isGood()
7669 || currentDwgBit(&bodyProbe) > bodyDataEndBit
7670 || (stringStream != nullptr && currentDwgBit(&stringProbe) > stringEndBit)
7671 || parsedClassVersion < 0 || parsedType < 0
7672 || parsedAttenuationType < 0 || parsedShadowType < 0
7673 || !finite(parsedPosition) || !finite(parsedTarget)
7674 || !finite(webRotation)
7675 || !std::isfinite(parsedIntensity)
7676 || !std::isfinite(parsedAttenuationStartLimit)
7677 || !std::isfinite(parsedAttenuationEndLimit)
7678 || !std::isfinite(parsedHotspotAngle)
7679 || !std::isfinite(parsedFalloffAngle)
7680 || !std::isfinite(physicalIntensity)
7681 || !std::isfinite(illuminanceDistance)
7682 || !std::isfinite(lampColorTemperature)
7683 || !std::isfinite(extendedLightLength)
7684 || !std::isfinite(extendedLightWidth)
7685 || !std::isfinite(extendedLightRadius))
7686 return fail();
7687
7688 std::uint64_t handleEndBit = 0;
7689 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
7690 return fail();
7691 dwgBuffer handleProbe = bodyProbe.forkIndependent();
7692 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
7693 || !handleProbe.isGood())
7694 return fail();
7695
7696 m_classVersion = static_cast<std::uint32_t>(parsedClassVersion);
7697 m_name = std::move(parsedName);
7698 m_type = static_cast<std::uint32_t>(parsedType);
7699 m_status = parsedStatus;
7700 m_color = parsedColor;
7701 m_plotGlyph = parsedPlotGlyph;
7702 m_intensity = parsedIntensity;
7703 m_position = parsedPosition;
7704 m_target = parsedTarget;
7705 m_attenuationType = static_cast<std::uint32_t>(parsedAttenuationType);
7706 m_useAttenuationLimits = parsedUseAttenuationLimits;
7707 m_attenuationStartLimit = parsedAttenuationStartLimit;
7708 m_attenuationEndLimit = parsedAttenuationEndLimit;
7709 m_hotspotAngle = parsedHotspotAngle;
7710 m_falloffAngle = parsedFalloffAngle;
7711 m_castShadows = parsedCastShadows;
7712 m_shadowType = static_cast<std::uint32_t>(parsedShadowType);
7713 m_shadowMapSize = parsedShadowMapSize;
7714 m_shadowMapSoftness = parsedShadowMapSoftness;
7715 m_hasPhotometricData = hasPhotometricData;
7716 m_hasWebFile = hasWebFile;
7717 m_webFile = std::move(webFile);
7718 m_physicalIntensityMethod = physicalIntensityMethod;
7719 m_physicalIntensity = physicalIntensity;
7720 m_illuminanceDistance = illuminanceDistance;
7721 m_lampColorType = lampColorType;
7722 m_lampColorTemperature = lampColorTemperature;
7723 m_lampColorPreset = lampColorPreset;
7724 m_webRotation = webRotation;
7725 m_extendedLightShape = extendedLightShape;
7726 m_extendedLightLength = extendedLightLength;
7727 m_extendedLightWidth = extendedLightWidth;
7728 m_extendedLightRadius = extendedLightRadius;
7729 DRW_DBG("LIGHT name: ")DRW_dbg::dbg("LIGHT name: "); DRW_DBG(m_name.c_str())DRW_dbg::dbg(m_name.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
7730 *sourceBuf = handleProbe;
7731 return true;
7732}
7733
7734bool DRW_Camera::parseCode(int code,
7735 const std::unique_ptr<dxfReader>& reader) {
7736 if (code == 340) {
7737 m_viewHandle = reader->getHandleString();
7738 return true;
7739 }
7740 return DRW_Entity::parseCode(code, reader);
7741}
7742
7743void DRW_Camera::resetDwgState() {
7744 DRW_Entity::reset();
7745 m_viewHandle = 0;
7746}
7747
7748bool DRW_Camera::parseDwg(DRW::Version v, dwgBuffer *buf,
7749 std::uint32_t bs) {
7750 resetDwgState();
7751 dwgBuffer *sourceBuf = buf;
7752 auto fail = [this, sourceBuf]() {
7753 if (sourceBuf != nullptr)
7754 sourceBuf->invalidate();
7755 resetDwgState();
7756 return false;
7757 };
7758 if (sourceBuf == nullptr || v < DRW::AC1015)
7759 return fail();
7760
7761 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7762 dwgBuffer stringProbe = sourceBuf->forkIndependent();
7763 if (!DRW_Entity::parseDwg(
7764 v, &bodyProbe, v > DRW::AC1018 ? &stringProbe : nullptr, bs)
7765 || !bodyProbe.isGood() || !stringProbe.isGood())
7766 return fail();
7767
7768 std::uint64_t handleEndBit = 0;
7769 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
7770 return fail();
7771 dwgBuffer handleProbe = bodyProbe.forkIndependent();
7772 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
7773 || !handleProbe.isGood())
7774 return fail();
7775
7776 std::uint32_t viewHandle = 0;
7777 if (proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
7778 dwgHandle viewH;
7779 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false, viewH))
7780 return fail();
7781 viewHandle = viewH.ref;
7782 } else if (currentDwgBit(&handleProbe) != handleEndBit) {
7783 return fail();
7784 }
7785
7786 m_viewHandle = viewHandle;
7787 *sourceBuf = handleProbe;
7788 return true;
7789}
7790
7791bool DRW_Camera::encodeDwg(DRW::Version v, dwgBufferW *buf,
7792 std::uint32_t bs, dwgBufferW *strBuf,
7793 dwgBufferW *handleBuf) {
7794 (void)bs;
7795 if (v < DRW::AC1015 || buf == nullptr)
7796 return false;
7797
7798 oType = kDwgClassNum;
7799 if (!encodeDwgCommon(v, buf, strBuf)
7800 || !encodeDwgEntHandle(v, buf, handleBuf)) {
7801 return false;
7802 }
7803
7804 putHardPointerHandle(handleBuf ? handleBuf : buf, m_viewHandle);
7805 return true;
7806}
7807
7808bool DRW_GeoPositionMarker::parseCode(
7809 int code, const std::unique_ptr<dxfReader>& reader) {
7810 switch (code) {
7811 case 90:
7812 {
7813 int value = 0;
7814 if (!readDxfIntInRange(reader, 0, std::numeric_limits<int>::max(), value))
7815 return false;
7816 m_classVersion = static_cast<std::uint32_t>(value);
7817 }
7818 return true;
7819 case 10:
7820 m_position.x = reader->getDouble();
7821 return true;
7822 case 20:
7823 m_position.y = reader->getDouble();
7824 return true;
7825 case 30:
7826 m_position.z = reader->getDouble();
7827 return true;
7828 case 40:
7829 if (m_dxfDouble40Count++ == 0)
7830 m_radius = reader->getDouble();
7831 else if (m_dxfDouble40Count == 2)
7832 m_landingGap = reader->getDouble();
7833 else
7834 (void)reader->getDouble();
7835 return true;
7836 case 1:
7837 m_notes = reader->getUtf8String();
7838 return true;
7839 case 280:
7840 {
7841 const int value = reader->getInt32();
7842 if (value < 0 || value > std::numeric_limits<std::uint8_t>::max())
7843 return false;
7844 m_textAlignment = static_cast<std::uint8_t>(value);
7845 }
7846 return true;
7847 case 290:
7848 {
7849 int value = 0;
7850 if (!readDxfIntInRange(reader, 0, 1, value))
7851 return false;
7852 if (m_dxfBool290Count++ == 0)
7853 m_mtextVisible = value != 0;
7854 else if (m_dxfBool290Count == 2)
7855 m_enableFrameText = value != 0;
7856 }
7857 return true;
7858 default:
7859 return DRW_Entity::parseCode(code, reader);
7860 }
7861}
7862
7863bool DRW_GeoPositionMarker::parseDxfVariant(const DRW_Variant& value) {
7864 switch (value.code()) {
7865 case 90:
7866 if (value.type() != DRW_Variant::INTEGER)
7867 return false;
7868 if (value.i_val() < 0)
7869 return false;
7870 m_classVersion = static_cast<std::uint32_t>(value.i_val());
7871 return true;
7872 case 10:
7873 if (value.type() != DRW_Variant::DOUBLE)
7874 return false;
7875 m_position.x = value.d_val();
7876 return true;
7877 case 20:
7878 if (value.type() != DRW_Variant::DOUBLE)
7879 return false;
7880 m_position.y = value.d_val();
7881 return true;
7882 case 30:
7883 if (value.type() != DRW_Variant::DOUBLE)
7884 return false;
7885 m_position.z = value.d_val();
7886 return true;
7887 case 40:
7888 if (value.type() != DRW_Variant::DOUBLE)
7889 return false;
7890 if (m_dxfDouble40Count++ == 0)
7891 m_radius = value.d_val();
7892 else if (m_dxfDouble40Count == 2)
7893 m_landingGap = value.d_val();
7894 return true;
7895 case 1:
7896 if (value.type() != DRW_Variant::STRING)
7897 return false;
7898 m_notes = value.c_str();
7899 return true;
7900 case 280:
7901 if (value.type() != DRW_Variant::INTEGER)
7902 return false;
7903 if (value.i_val() < 0
7904 || value.i_val() > std::numeric_limits<std::uint8_t>::max())
7905 return false;
7906 m_textAlignment = static_cast<std::uint8_t>(value.i_val());
7907 return true;
7908 case 290:
7909 if (value.type() != DRW_Variant::INTEGER)
7910 return false;
7911 if (value.i_val() < 0 || value.i_val() > 1)
7912 return false;
7913 if (m_dxfBool290Count++ == 0)
7914 m_mtextVisible = value.i_val() != 0;
7915 else if (m_dxfBool290Count == 2)
7916 m_enableFrameText = value.i_val() != 0;
7917 return true;
7918 default:
7919 return true; // Common and unknown groups remain raw-authoritative.
7920 }
7921}
7922
7923void DRW_GeoPositionMarker::resetDwgState() {
7924 DRW_Entity::reset();
7925 m_classVersion = 0;
7926 m_position = DRW_Coord{0.0, 0.0, 0.0};
7927 m_radius = 0.0;
7928 m_notes.clear();
7929 m_landingGap = 0.0;
7930 m_mtextVisible = false;
7931 m_textAlignment = 0;
7932 m_enableFrameText = false;
7933 mtext.reset();
7934 m_dxfDouble40Count = 0;
7935 m_dxfBool290Count = 0;
7936}
7937
7938bool DRW_GeoPositionMarker::parseDwg(DRW::Version v, dwgBuffer *buf,
7939 std::uint32_t bs) {
7940 resetDwgState();
7941 dwgBuffer *sourceBuf = buf;
7942 auto fail = [this, sourceBuf]() {
7943 if (sourceBuf != nullptr)
7944 sourceBuf->invalidate();
7945 resetDwgState();
7946 return false;
7947 };
7948 if (sourceBuf == nullptr || v < DRW::AC1027)
7949 return fail();
7950
7951 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
7952 dwgBuffer stringProbe = sourceBuf->forkIndependent();
7953 if (!DRW_Entity::parseDwg(v, &bodyProbe, &stringProbe, bs)
7954 || !bodyProbe.isGood() || !stringProbe.isGood())
7955 return fail();
7956
7957 const std::uint64_t bodyEndBit = dwgDataEndBit;
7958 std::uint64_t stringStartBit = bodyEndBit;
7959 std::uint64_t stringEndBit = bodyEndBit;
7960 if (v > DRW::AC1018
7961 && (!bodyProbe.getR2007StringStreamBounds(objSize, stringStartBit,
7962 stringEndBit)
7963 || currentDwgBit(&stringProbe) > stringEndBit))
7964 return fail();
7965
7966 std::int32_t parsedClassVersion = 0;
7967 DRW_Coord position;
7968 double radius = 0.0;
7969 UTF8STRINGstd::string notes;
7970 double landingGap = 0.0;
7971 bool mtextVisible = false;
7972 std::uint8_t textAlignment = 0;
7973 bool enableFrameText = false;
7974 if (!readBoundedBitLong(bodyProbe, bodyEndBit, parsedClassVersion)
7975 || !readBoundedBitCoord(bodyProbe, bodyEndBit, position)
7976 || !readBoundedBitDouble(bodyProbe, bodyEndBit, radius)
7977 || !readBoundedVariableText(stringProbe, stringEndBit, v, notes)
7978 || !readBoundedBitDouble(bodyProbe, bodyEndBit, landingGap)
7979 || !readBoundedBit(bodyProbe, bodyEndBit, mtextVisible)
7980 || !readBoundedRawChar8(bodyProbe, bodyEndBit, textAlignment)
7981 || !readBoundedBit(bodyProbe, bodyEndBit, enableFrameText))
7982 return fail();
7983
7984 const auto finite = [](const DRW_Coord& point) {
7985 return std::isfinite(point.x) && std::isfinite(point.y)
7986 && std::isfinite(point.z);
7987 };
7988 if (!bodyProbe.isGood() || !stringProbe.isGood()
7989 || !finite(position) || !std::isfinite(radius)
7990 || !std::isfinite(landingGap))
7991 return fail();
7992
7993 // AcDbMTextObjectEmbedded follows the marker body when framing is enabled.
7994 // Parse it on a bounded probe before the parent and embedded handle passes;
7995 // otherwise a truncated nested body can consume parent handle bytes.
7996 dwgBuffer bodyTailProbe = bodyProbe.forkIndependent();
7997 std::unique_ptr<DRW_MText> parsedMText;
7998 EmbeddedMTextHandleInfo embeddedHandles;
7999 if (enableFrameText) {
8000 if (objSize == 0
8001 || !(parsedMText = std::make_unique<DRW_MText>())
8002 || !parseEmbeddedMTextDwg(v, &bodyTailProbe, &stringProbe,
8003 *parsedMText, embeddedHandles,
8004 bodyEndBit, stringEndBit)
8005 || !bodyTailProbe.isGood() || !stringProbe.isGood())
8006 return fail();
8007 }
8008
8009 std::uint64_t handleEndBit = 0;
8010 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
8011 return fail();
8012
8013 dwgBuffer handleProbe = bodyTailProbe.forkIndependent();
8014 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
8015 || !handleProbe.isGood())
8016 return fail();
8017 if (enableFrameText) {
8018 if (!consumeEmbeddedMTextHandles(v, &handleProbe, embeddedHandles,
8019 parsedMText.get(), handleEndBit))
8020 return fail();
8021 }
8022
8023 m_classVersion = static_cast<std::uint32_t>(parsedClassVersion);
8024 m_position = position;
8025 m_radius = radius;
8026 m_notes = notes;
8027 m_landingGap = landingGap;
8028 m_mtextVisible = mtextVisible;
8029 m_textAlignment = textAlignment;
8030 m_enableFrameText = enableFrameText;
8031 mtext = std::move(parsedMText);
8032 *sourceBuf = handleProbe;
8033 return true;
8034}
8035
8036bool DRW_Light::encodeDwg(DRW::Version v, dwgBufferW *buf, std::uint32_t bs,
8037 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8038 (void)bs;
8039 if (v < DRW::AC1021)
8040 return false;
8041
8042 oType = kDwgClassNum;
8043 if (!encodeDwgCommon(v, buf, strBuf))
8044 return false;
8045
8046 dwgBufferW *sb = strBuf ? strBuf : buf;
8047 buf->putBitLong(m_classVersion);
8048 sb->putVariableText(v, m_name);
8049 buf->putBitLong(m_type);
8050 buf->putBit(m_status ? 1 : 0);
8051 buf->putCmColor(v, static_cast<std::uint16_t>(m_color));
8052 buf->putBit(m_plotGlyph ? 1 : 0);
8053 buf->putBitDouble(m_intensity);
8054 buf->put3BitDouble(m_position);
8055 buf->put3BitDouble(m_target);
8056 buf->putBitLong(m_attenuationType);
8057 buf->putBit(m_useAttenuationLimits ? 1 : 0);
8058 buf->putBitDouble(m_attenuationStartLimit);
8059 buf->putBitDouble(m_attenuationEndLimit);
8060 buf->putBitDouble(m_hotspotAngle);
8061 buf->putBitDouble(m_falloffAngle);
8062 buf->putBit(m_castShadows ? 1 : 0);
8063 buf->putBitLong(m_shadowType);
8064 buf->putBitShort(m_shadowMapSize);
8065 buf->putRawChar8(m_shadowMapSoftness);
8066
8067 buf->putBit(m_hasPhotometricData ? 1 : 0);
8068 if (m_hasPhotometricData) {
8069 buf->putBit(m_hasWebFile ? 1 : 0);
8070 sb->putVariableText(v, m_webFile);
8071 buf->putBitShort(m_physicalIntensityMethod);
8072 buf->putBitDouble(m_physicalIntensity);
8073 buf->putBitDouble(m_illuminanceDistance);
8074 buf->putBitShort(m_lampColorType);
8075 buf->putBitDouble(m_lampColorTemperature);
8076 buf->putBitShort(m_lampColorPreset);
8077 buf->put3BitDouble(m_webRotation);
8078 buf->putBitShort(m_extendedLightShape);
8079 buf->putBitDouble(m_extendedLightLength);
8080 buf->putBitDouble(m_extendedLightWidth);
8081 buf->putBitDouble(m_extendedLightRadius);
8082 }
8083
8084 return encodeDwgEntHandle(v, buf, handleBuf);
8085}
8086
8087bool DRW_SectionObject::parseCode(
8088 int code, const std::unique_ptr<dxfReader>& reader) {
8089 if (code != 100 && !m_dxfInBody)
8090 return DRW_Entity::parseCode(code, reader);
8091 switch (code) {
8092 case 100:
8093 m_dxfInBody = reader->getUtf8String() == "AcDbSection";
8094 return true;
8095 case 90:
8096 m_state = static_cast<std::uint32_t>(reader->getInt32());
8097 return true;
8098 case 91:
8099 m_flags = static_cast<std::uint32_t>(reader->getInt32());
8100 return true;
8101 case 1:
8102 m_name = reader->getUtf8String();
8103 return true;
8104 case 10:
8105 m_vertDir.x = reader->getDouble();
8106 return true;
8107 case 20:
8108 m_vertDir.y = reader->getDouble();
8109 return true;
8110 case 30:
8111 m_vertDir.z = reader->getDouble();
8112 return true;
8113 case 40:
8114 m_topHeight = reader->getDouble();
8115 return true;
8116 case 41:
8117 m_bottomHeight = reader->getDouble();
8118 return true;
8119 case 70:
8120 {
8121 const int value = reader->getInt32();
8122 if (value < 0 || value > 0xFFFF)
8123 return false;
8124 m_indicatorAlpha = static_cast<std::uint16_t>(value);
8125 }
8126 return true;
8127 case 62:
8128 m_indicatorColor = static_cast<std::uint32_t>(reader->getInt32());
8129 return true;
8130 case 92:
8131 {
8132 const int value = reader->getInt32();
8133 if (value < 0
8134 || static_cast<std::size_t>(value) > DRW_SectionObject::kMaxVertices)
8135 return false;
8136 m_verts.clear();
8137 if (!DRW::reserve(m_verts, value))
8138 return false;
8139 return true;
8140 }
8141 case 93:
8142 {
8143 const int value = reader->getInt32();
8144 if (value < 0
8145 || static_cast<std::size_t>(value) > DRW_SectionObject::kMaxVertices)
8146 return false;
8147 m_blVerts.clear();
8148 if (!DRW::reserve(m_blVerts, value))
8149 return false;
8150 return true;
8151 }
8152 case 11:
8153 if (m_verts.size() >= DRW_SectionObject::kMaxVertices)
8154 return false;
8155 m_verts.emplace_back();
8156 m_verts.back().x = reader->getDouble();
8157 return true;
8158 case 21:
8159 if (m_verts.empty())
8160 return false;
8161 m_verts.back().y = reader->getDouble();
8162 return true;
8163 case 31:
8164 if (m_verts.empty())
8165 return false;
8166 m_verts.back().z = reader->getDouble();
8167 return true;
8168 case 12:
8169 if (m_blVerts.size() >= DRW_SectionObject::kMaxVertices)
8170 return false;
8171 m_blVerts.emplace_back();
8172 m_blVerts.back().x = reader->getDouble();
8173 return true;
8174 case 22:
8175 if (m_blVerts.empty())
8176 return false;
8177 m_blVerts.back().y = reader->getDouble();
8178 return true;
8179 case 32:
8180 if (m_blVerts.empty())
8181 return false;
8182 m_blVerts.back().z = reader->getDouble();
8183 return true;
8184 case 360:
8185 m_sectionSettingsHandle = reader->getHandleString();
8186 return true;
8187 default:
8188 return DRW_Entity::parseCode(code, reader);
8189 }
8190}
8191
8192// DRW_SectionObject::parseDwg — SECTIONOBJECT / AcDbSection, field order per
8193// libreDWG dwg2.spec DWG_ENTITY(SECTIONOBJECT): BL state, BL flags, T name,
8194// 3BD vert_dir, BD top/bottom height, BS indicator_alpha, CMTC indicator_color,
8195// BL num_verts + verts, BL num_blverts + blverts; then the common entity handle
8196// data followed by the section_settings hard reference (H 5, 360).
8197void DRW_SectionObject::resetDwgState() {
8198 DRW_Entity::reset();
8199 m_state = 0;
8200 m_flags = 0;
8201 m_name.clear();
8202 m_vertDir = DRW_Coord{0.0, 0.0, 0.0};
8203 m_topHeight = 0.0;
8204 m_bottomHeight = 0.0;
8205 m_indicatorAlpha = 0;
8206 m_indicatorColor = 0;
8207 m_verts.clear();
8208 m_blVerts.clear();
8209 m_sectionSettingsHandle = 0;
8210 m_dxfInBody = false;
8211}
8212
8213bool DRW_SectionObject::parseDwg(DRW::Version v, dwgBuffer *buf,
8214 std::uint32_t bs) {
8215 resetDwgState();
8216 dwgBuffer *sourceBuf = buf;
8217 auto fail = [this, sourceBuf]() {
8218 if (sourceBuf != nullptr)
8219 sourceBuf->invalidate();
8220 resetDwgState();
8221 return false;
8222 };
8223 if (sourceBuf == nullptr)
8224 return fail();
8225
8226 // R2007+ stores text in a separate stream. Independent cursors keep a
8227 // malformed field from consuming the source or a later handle stream.
8228 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
8229 dwgBuffer stringProbe = sourceBuf->forkIndependent();
8230 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : &bodyProbe;
8231 if (!DRW_Entity::parseDwg(v, &bodyProbe, stringStream, bs)
8232 || !bodyProbe.isGood() || !stringStream->isGood())
8233 return fail();
8234 DRW_DBG("\n***************************** parsing SECTIONOBJECT *********************\n")DRW_dbg::dbg("\n***************************** parsing SECTIONOBJECT *********************\n"
)
;
8235
8236 // The common entity parser leaves bodyProbe at the first SECTIONOBJECT
8237 // field and stringStream at the detached string stream for R2007+.
8238 // Keep both cursors inside their declared data ranges while staging the
8239 // complete header before publishing it.
8240 const std::uint64_t bodyEndBit = tableBodyEndBit(v, stringStream, objSize);
8241 const std::uint64_t stringEndBit = v > DRW::AC1018
8242 ? objSize : bodyEndBit;
8243 std::int32_t stateValue = 0;
8244 std::int32_t flagsValue = 0;
8245 UTF8STRINGstd::string name;
8246 DRW_Coord vertDir;
8247 double topHeight = 0.0;
8248 double bottomHeight = 0.0;
8249 std::uint16_t indicatorAlpha = 0;
8250 std::uint32_t indicatorColor = 0;
8251 if (!readBoundedBitLong(bodyProbe, bodyEndBit, stateValue)
8252 || !readBoundedBitLong(bodyProbe, bodyEndBit, flagsValue)
8253 || !readBoundedVariableText(*stringStream, stringEndBit, v, name)
8254 || !readBoundedBitCoord(bodyProbe, bodyEndBit, vertDir)
8255 || !readBoundedBitDouble(bodyProbe, bodyEndBit, topHeight)
8256 || !readBoundedBitDouble(bodyProbe, bodyEndBit, bottomHeight)
8257 || !readBoundedBitShort(bodyProbe, bodyEndBit, indicatorAlpha)
8258 || !readBoundedCmColor(bodyProbe, stringStream, bodyEndBit, v,
8259 indicatorColor, nullptr, nullptr, nullptr,
8260 nullptr, stringEndBit))
8261 return fail();
8262
8263 std::uint32_t numVerts = 0;
8264 if (!readTableBodyCount(
8265 v, &bodyProbe, bodyEndBit,
8266 static_cast<std::uint32_t>(DRW_SectionObject::kMaxVertices), 6,
8267 numVerts)
8268 || !proxyEntityHasBits(
8269 bodyProbe, bodyEndBit, static_cast<std::uint64_t>(numVerts) * 6u))
8270 return fail();
8271 std::vector<DRW_Coord> verts;
8272 if (!DRW::reserve(verts, static_cast<int>(numVerts)))
8273 return fail();
8274 for (std::uint32_t i = 0; i < numVerts; ++i) {
8275 DRW_Coord vertex;
8276 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, vertex))
8277 return fail();
8278 verts.push_back(vertex);
8279 }
8280
8281 std::uint32_t numBl = 0;
8282 if (!readTableBodyCount(
8283 v, &bodyProbe, bodyEndBit,
8284 static_cast<std::uint32_t>(DRW_SectionObject::kMaxVertices), 6,
8285 numBl)
8286 || !proxyEntityHasBits(
8287 bodyProbe, bodyEndBit, static_cast<std::uint64_t>(numBl) * 6u))
8288 return fail();
8289 std::vector<DRW_Coord> blVerts;
8290 if (!DRW::reserve(blVerts, static_cast<int>(numBl)))
8291 return fail();
8292 for (std::uint32_t i = 0; i < numBl; ++i) {
8293 DRW_Coord vertex;
8294 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, vertex))
8295 return fail();
8296 blVerts.push_back(vertex);
8297 }
8298
8299 const auto finite = [](const DRW_Coord& point) {
8300 return std::isfinite(point.x) && std::isfinite(point.y)
8301 && std::isfinite(point.z);
8302 };
8303 if (!bodyProbe.isGood() || !stringStream->isGood()
8304 || currentDwgBit(&bodyProbe) > bodyEndBit
8305 || !finite(vertDir) || !std::isfinite(topHeight)
8306 || !std::isfinite(bottomHeight))
8307 return fail();
8308 if (!std::all_of(verts.begin(), verts.end(), finite)
8309 || !std::all_of(blVerts.begin(), blVerts.end(), finite))
8310 return fail();
8311
8312 // The common entity handle stream starts at the object-data boundary for
8313 // R2007+ and follows the body inline in older versions. The section
8314 // settings reference is a hard pointer (H 5), after those common handles.
8315 std::uint64_t handleEndBit = 0;
8316 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
8317 return fail();
8318 dwgBuffer handleProbe = bodyProbe.forkIndependent();
8319 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
8320 || !handleProbe.isGood())
8321 return fail();
8322 std::uint32_t sectionSettingsHandle = 0;
8323 if (proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
8324 dwgHandle settingsHandle;
8325 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
8326 settingsHandle))
8327 return fail();
8328 sectionSettingsHandle = settingsHandle.ref;
8329 DRW_DBG(" section_settings Handle: ")DRW_dbg::dbg(" section_settings Handle: ");
8330 DRW_DBGHL(settingsHandle.code, settingsHandle.size,DRW_dbg::dbgHL(settingsHandle.code, settingsHandle.size, settingsHandle
.ref)
8331 settingsHandle.ref)DRW_dbg::dbgHL(settingsHandle.code, settingsHandle.size, settingsHandle
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
8332 }
8333 if (!handleProbe.isGood())
8334 return fail();
8335
8336 m_state = static_cast<std::uint32_t>(stateValue);
8337 m_flags = static_cast<std::uint32_t>(flagsValue);
8338 m_name = name;
8339 m_vertDir = vertDir;
8340 m_topHeight = topHeight;
8341 m_bottomHeight = bottomHeight;
8342 m_indicatorAlpha = indicatorAlpha;
8343 m_indicatorColor = indicatorColor;
8344 m_verts = std::move(verts);
8345 m_blVerts = std::move(blVerts);
8346 m_sectionSettingsHandle = sectionSettingsHandle;
8347 *sourceBuf = handleProbe;
8348 DRW_DBG("SECTIONOBJECT name: ")DRW_dbg::dbg("SECTIONOBJECT name: "); DRW_DBG(m_name.c_str())DRW_dbg::dbg(m_name.c_str());
8349 DRW_DBG(" verts: ")DRW_dbg::dbg(" verts: "); DRW_DBG(static_cast<int>(m_verts.size()))DRW_dbg::dbg(static_cast<int>(m_verts.size())); DRW_DBG("\n")DRW_dbg::dbg("\n");
8350 return true;
8351}
8352
8353bool DRW_SectionObject::encodeDwg(DRW::Version v, dwgBufferW *buf,
8354 std::uint32_t bs, dwgBufferW *strBuf,
8355 dwgBufferW *handleBuf) {
8356 (void)bs;
8357 if (v < DRW::AC1021 || buf == nullptr
8358 || m_verts.size() > DRW_SectionObject::kMaxVertices
8359 || m_blVerts.size() > DRW_SectionObject::kMaxVertices)
8360 return false;
8361 oType = kDwgClassNum;
8362 if (!encodeDwgCommon(v, buf, strBuf))
8363 return false;
8364 dwgBufferW *stringBuffer = strBuf != nullptr ? strBuf : buf;
8365 buf->putBitLong(static_cast<std::int32_t>(m_state));
8366 buf->putBitLong(static_cast<std::int32_t>(m_flags));
8367 stringBuffer->putVariableText(v, m_name);
8368 buf->put3BitDouble(m_vertDir);
8369 buf->putBitDouble(m_topHeight);
8370 buf->putBitDouble(m_bottomHeight);
8371 buf->putBitShort(m_indicatorAlpha);
8372 buf->putCmColor(v, static_cast<std::uint16_t>(m_indicatorColor));
8373 buf->putBitLong(static_cast<std::int32_t>(m_verts.size()));
8374 for (const DRW_Coord& point : m_verts)
8375 buf->put3BitDouble(point);
8376 buf->putBitLong(static_cast<std::int32_t>(m_blVerts.size()));
8377 for (const DRW_Coord& point : m_blVerts)
8378 buf->put3BitDouble(point);
8379 if (!encodeDwgEntHandle(v, buf, handleBuf))
8380 return false;
8381 putHardPointerHandle(handleBuf != nullptr ? handleBuf : buf,
8382 m_sectionSettingsHandle);
8383 return true;
8384}
8385
8386bool DRW_Tolerance::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8387 switch (code) {
8388 case 1:
8389 text = reader->getUtf8String();
8390 break;
8391 case 3:
8392 dimStyleName = reader->getUtf8String();
8393 break;
8394 case 10:
8395 insertionPoint.x = reader->getDouble();
8396 break;
8397 case 20:
8398 insertionPoint.y = reader->getDouble();
8399 break;
8400 case 30:
8401 insertionPoint.z = reader->getDouble();
8402 break;
8403 case 11:
8404 xAxisDirectionVector.x = reader->getDouble();
8405 break;
8406 case 21:
8407 xAxisDirectionVector.y = reader->getDouble();
8408 break;
8409 case 31:
8410 xAxisDirectionVector.z = reader->getDouble();
8411 break;
8412 case 210:
8413 extPoint.x = reader->getDouble();
8414 break;
8415 case 220:
8416 extPoint.y = reader->getDouble();
8417 break;
8418 case 230:
8419 extPoint.z = reader->getDouble();
8420 break;
8421 default:
8422 return DRW_Entity::parseCode(code, reader);
8423 }
8424 return true;
8425}
8426
8427void DRW_Tolerance::resetDwgState() {
8428 DRW_Entity::reset();
8429 text.clear();
8430 dimStyleName = "STANDARD";
8431 dimStyleH = dwgHandle{};
8432 insertionPoint = DRW_Coord{0.0, 0.0, 0.0};
8433 xAxisDirectionVector = DRW_Coord{0.0, 0.0, 0.0};
8434 extPoint = DRW_Coord{0.0, 0.0, 1.0};
8435}
8436
8437bool DRW_Tolerance::parseDwg(DRW::Version v, dwgBuffer *buf,
8438 std::uint32_t bs) {
8439 resetDwgState();
8440 dwgBuffer *sourceBuf = buf;
8441 auto fail = [this, sourceBuf]() {
8442 if (sourceBuf != nullptr)
8443 sourceBuf->invalidate();
8444 resetDwgState();
8445 return false;
8446 };
8447 if (sourceBuf == nullptr)
8448 return fail();
8449
8450 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
8451 dwgBuffer stringProbe = sourceBuf->forkIndependent();
8452 dwgBuffer *stringStream = v > DRW::AC1018 ? &stringProbe : &bodyProbe;
8453 if (!DRW_Entity::parseDwg(v, &bodyProbe, stringStream, bs)
8454 || !bodyProbe.isGood() || !stringStream->isGood())
8455 return fail();
8456
8457 DRW_DBG("\n***************************** parsing tolerance *********************************************\n")DRW_dbg::dbg("\n***************************** parsing tolerance *********************************************\n"
)
;
8458 const std::uint64_t bodyEndBit = dwgDataEndBit;
8459 const std::uint64_t stringStartBit = currentDwgBit(stringStream);
8460 std::uint64_t stringEndBit = bodyEndBit;
8461 if (v > DRW::AC1021) {
8462 std::uint64_t ignoredBodyEndBit = 0;
8463 if (!entityBodyDataEndBit(*stringStream, v, objSize,
8464 ignoredBodyEndBit, &stringEndBit))
8465 return fail();
8466 }
8467 auto read3BitDoubleChecked = [&](DRW_Coord& point) {
8468 return readBoundedBitDouble(bodyProbe, bodyEndBit, point.x)
8469 && readBoundedBitDouble(bodyProbe, bodyEndBit, point.y)
8470 && readBoundedBitDouble(bodyProbe, bodyEndBit, point.z);
8471 };
8472
8473 if (v < DRW::AC1015) {
8474 std::uint16_t unknown = 0;
8475 double heightAtCreation = 0.0;
8476 double dimGapAtCreation = 0.0;
8477 if (!readBoundedBitShort(bodyProbe, bodyEndBit, unknown)
8478 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
8479 heightAtCreation)
8480 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
8481 dimGapAtCreation)
8482 || !std::isfinite(heightAtCreation)
8483 || !std::isfinite(dimGapAtCreation))
8484 return fail();
8485 }
8486
8487 DRW_Coord parsedInsertionPoint;
8488 DRW_Coord parsedXAxisDirectionVector;
8489 DRW_Coord parsedExtPoint;
8490 if (!read3BitDoubleChecked(parsedInsertionPoint)
8491 || !read3BitDoubleChecked(parsedXAxisDirectionVector)
8492 || !read3BitDoubleChecked(parsedExtPoint))
8493 return fail();
8494 UTF8STRINGstd::string parsedText;
8495 if (v > DRW::AC1018 && stringStartBit >= stringEndBit) {
8496 parsedText.clear();
8497 } else if (!readBoundedVariableText(*stringStream, stringEndBit, v,
8498 parsedText)) {
8499 return fail();
8500 }
8501
8502 const auto finite = [](const DRW_Coord& point) {
8503 return std::isfinite(point.x) && std::isfinite(point.y)
8504 && std::isfinite(point.z);
8505 };
8506 if (!bodyProbe.isGood() || !stringStream->isGood()
8507 || currentDwgBit(&bodyProbe) > bodyEndBit
8508 || currentDwgBit(stringStream) > stringEndBit
8509 || !finite(parsedInsertionPoint)
8510 || !finite(parsedXAxisDirectionVector)
8511 || !finite(parsedExtPoint))
8512 return fail();
8513
8514 std::uint64_t handleEndBit = 0;
8515 if (!dwgHandleStreamEndBit(*sourceBuf, v, objSize, bs, handleEndBit))
8516 return fail();
8517 dwgBuffer handleProbe = bodyProbe.forkIndependent();
8518 if (!DRW_Entity::parseDwgEntHandle(v, &handleProbe, true, handleEndBit)
8519 || !handleProbe.isGood())
8520 return fail();
8521 dwgHandle parsedDimStyleH;
8522 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
8523 parsedDimStyleH)
8524 || !handleProbe.isGood())
8525 return fail();
8526
8527 insertionPoint = parsedInsertionPoint;
8528 xAxisDirectionVector = parsedXAxisDirectionVector;
8529 extPoint = parsedExtPoint;
8530 text = parsedText;
8531 dimStyleH = parsedDimStyleH;
8532 *sourceBuf = handleProbe;
8533 DRW_DBG("insertionPoint: ")DRW_dbg::dbg("insertionPoint: ");
8534 DRW_DBGPT(insertionPoint.x, insertionPoint.y, insertionPoint.z)DRW_dbg::dbgPT(insertionPoint.x, insertionPoint.y, insertionPoint
.z)
;
8535 DRW_DBG("\ntolerance text: ")DRW_dbg::dbg("\ntolerance text: "); DRW_DBG(text.c_str())DRW_dbg::dbg(text.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8536 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: ");
8537 DRW_DBGHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref)DRW_dbg::dbgHL(dimStyleH.code, dimStyleH.size, dimStyleH.ref);
8538 DRW_DBG("\n")DRW_dbg::dbg("\n");
8539 return true;
8540}
8541
8542bool DRW_Tolerance::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
8543 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
8544 (void)bs;
8545 oType = 46;
8546 if (!encodeDwgCommon(version, buf, strBuf))
8547 return false;
8548
8549 if (version < DRW::AC1015) {
8550 buf->putBitShort(0);
8551 buf->putBitDouble(0.0);
8552 buf->putBitDouble(0.0);
8553 }
8554
8555 buf->put3BitDouble(insertionPoint);
8556 buf->put3BitDouble(xAxisDirectionVector);
8557 buf->put3BitDouble(extPoint);
8558 (strBuf ? strBuf : buf)->putVariableText(version, text);
8559
8560 if (!encodeDwgEntHandle(version, buf, handleBuf))
8561 return false;
8562
8563 dwgBufferW *hb = handleBuf ? handleBuf : buf;
8564 putHardPointerHandle(hb, (dimStyleH.ref == 0) ? 0x15 : dimStyleH.ref);
8565 return true;
8566}
8567
8568
8569bool DRW_Block::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8570 switch (code) {
8571 case 1:
8572 xrefPath = reader->getUtf8String();
8573 break;
8574 case 2:
8575 name = reader->getUtf8String();
8576 break;
8577 case 70:
8578 flags = reader->getInt32();
8579 break;
8580 default:
8581 return DRW_Point::parseCode(code, reader);
8582 }
8583
8584 return true;
8585}
8586
8587void DRW_Block::resetDwgState() {
8588 const bool endBlock = isEnd;
8589 DRW_Point::resetDwgState();
8590 name = endBlock ? UTF8STRINGstd::string() : UTF8STRINGstd::string("*U0");
8591 flags = 0;
8592 insUnits = 0;
8593 xrefPath.clear();
8594 previewData.clear();
8595 isEnd = endBlock;
8596}
8597
8598bool DRW_Block::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8599 resetDwgState();
8600 if (buf == nullptr)
8601 return false;
8602 auto fail = [this, buf]() {
8603 buf->invalidate();
8604 resetDwgState();
8605 return false;
8606 };
8607 dwgBuffer bodyProbe = buf->forkIndependent();
8608 dwgBuffer sBuff = bodyProbe.forkIndependent();
8609 dwgBuffer *sBuf = &bodyProbe;
8610 if (version > DRW::AC1018) {//2007+
8611 sBuf = &sBuff; //separate buffer for strings
8612 }
8613 bool ret = DRW_Entity::parseDwg(version, &bodyProbe, sBuf, bs);
8614 if (!ret)
8615 return fail();
8616
8617 // BLOCK/ENDBLK carry one entity-specific bit after the common data range;
8618 // the detached string stream begins at dwgDataEndBit and is independent.
8619 const std::uint64_t bodyEndBit = version > DRW::AC1018
8620 && dwgDataEndBit < std::numeric_limits<std::uint64_t>::max()
8621 ? dwgDataEndBit + 1 : dwgDataEndBit;
8622 std::uint64_t stringStartBit = bodyEndBit;
8623 std::uint64_t stringEndBit = bodyEndBit;
8624 if (version > DRW::AC1018
8625 && (!bodyProbe.getR2007StringStreamBounds(objSize, stringStartBit,
8626 stringEndBit)
8627 || currentDwgBit(sBuf) > stringEndBit))
8628 return fail();
8629
8630 UTF8STRINGstd::string parsedName;
8631 if (!isEnd){
8632 DRW_DBG("\n***************************** parsing block *********************************************\n")DRW_dbg::dbg("\n***************************** parsing block *********************************************\n"
)
;
8633 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
8634 parsedName))
8635 return fail();
8636 DRW_DBG("Block name: ")DRW_dbg::dbg("Block name: "); DRW_DBG(parsedName.c_str())DRW_dbg::dbg(parsedName.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
8637 } else {
8638 DRW_DBG("\n***************************** parsing end block *********************************************\n")DRW_dbg::dbg("\n***************************** parsing end block *********************************************\n"
)
;
8639 }
8640 if (version > DRW::AC1018) {//2007+
8641 bool ignoredUnknown = false;
8642 if (!readBoundedBit(bodyProbe, bodyEndBit, ignoredUnknown))
8643 return fail();
8644 DRW_DBG("unknown bit: ")DRW_dbg::dbg("unknown bit: "); DRW_DBG(ignoredUnknown)DRW_dbg::dbg(ignoredUnknown); DRW_DBG("\n")DRW_dbg::dbg("\n");
8645 }
8646// X handleAssoc; //X
8647 if (!bodyProbe.isGood() || currentDwgBit(&bodyProbe) > bodyEndBit)
8648 return fail();
8649 std::uint64_t handleEndBit = 0;
8650 if (!dwgHandleStreamEndBit(*buf, version, objSize, bs, handleEndBit))
8651 return fail();
8652 ret = DRW_Entity::parseDwgEntHandle(version, &bodyProbe, true,
8653 handleEndBit);
8654 if (!ret || !bodyProbe.isGood())
8655 return fail();
8656 // RS crc; //RS */
8657 // R13-R2004 keep the handle stream inline and may consume the final
8658 // byte-alignment padding beyond the declared object-data bit count.
8659 // Only R2007+ has a separate string stream range to validate here.
8660 if (!bodyProbe.isGood() || !sBuf->isGood()
8661 || (version > DRW::AC1018 && currentDwgBit(sBuf) > stringEndBit))
8662 return fail();
8663 *buf = bodyProbe;
8664 if (!isEnd)
8665 name = std::move(parsedName);
8666 return true;
8667}
8668
8669bool DRW_Insert::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8670 switch (code) {
8671 case 2:
8672 name = reader->getUtf8String();
8673 break;
8674 case 41:
8675 xscale = reader->getDouble();
8676 break;
8677 case 42:
8678 yscale = reader->getDouble();
8679 break;
8680 case 43:
8681 zscale = reader->getDouble();
8682 break;
8683 case 50:
8684 angle = reader->getDouble();
8685 angle = angle/ARAD57.29577951308232; //convert to radian
8686 break;
8687 case 70: {
8688 int value = 0;
8689 if (!readDxfIntInRange(reader, 0, kMaxMInsertCount, value))
8690 return false;
8691 colcount = value;
8692 break;
8693 }
8694 case 71: {
8695 int value = 0;
8696 if (!readDxfIntInRange(reader, 0, kMaxMInsertCount, value))
8697 return false;
8698 rowcount = value;
8699 break;
8700 }
8701 case 44:
8702 colspace = reader->getDouble();
8703 break;
8704 case 45:
8705 rowspace = reader->getDouble();
8706 break;
8707 default:
8708 return DRW_Point::parseCode(code, reader);
8709 }
8710
8711 return true;
8712}
8713
8714bool DRW_Table::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
8715 auto ensureGrid = [this]() {
8716 if (m_dxfRowsExpected < 0 || m_dxfColumnsExpected < 0)
8717 return true;
8718
8719 const std::uint32_t rows = static_cast<std::uint32_t>(m_dxfRowsExpected);
8720 const std::uint32_t columns = static_cast<std::uint32_t>(m_dxfColumnsExpected);
8721 if (rows > kMaxTableRows || columns > kMaxTableColumns
8722 || (columns != 0 && rows > kMaxTableCells / columns)) {
8723 return false;
8724 }
8725
8726 if (m_content.m_columns.size() != columns) {
8727 m_content.m_columns.clear();
8728 if (!DRW::reserve(m_content.m_columns, static_cast<int>(columns)))
8729 return false;
8730 if (!DRW::resize(m_content.m_columns, static_cast<int>(columns)))
8731 return false;
8732 m_dxfColumnWidthsRead = 0;
8733 }
8734 if (m_content.m_rows.size() != rows) {
8735 m_content.m_rows.clear();
8736 if (!DRW::reserve(m_content.m_rows, static_cast<int>(rows)))
8737 return false;
8738 if (!DRW::resize(m_content.m_rows, static_cast<int>(rows)))
8739 return false;
8740 m_dxfRowHeightsRead = 0;
8741 }
8742 for (auto& row : m_content.m_rows) {
8743 if (!DRW::reserve(row.m_cells, static_cast<int>(columns)))
8744 return false;
8745 if (!DRW::resize(row.m_cells, static_cast<int>(columns)))
8746 return false;
8747 }
8748
8749 m_hasSemanticContent = true;
8750 m_semanticContentComplete = true;
8751 return true;
8752 };
8753
8754 auto currentCell = [this]() -> DRW_TableCell* {
8755 if (m_dxfCurrentCell < 0 || m_content.m_columns.empty()
8756 || m_content.m_rows.empty()) {
8757 return nullptr;
8758 }
8759
8760 const std::size_t columns = m_content.m_columns.size();
8761 const std::size_t cell = static_cast<std::size_t>(m_dxfCurrentCell);
8762 const std::size_t row = cell / columns;
8763 const std::size_t column = cell % columns;
8764 if (row >= m_content.m_rows.size()
8765 || column >= m_content.m_rows[row].m_cells.size()) {
8766 return nullptr;
8767 }
8768 return &m_content.m_rows[row].m_cells[column];
8769 };
8770
8771 auto currentContent = [&currentCell]() -> DRW_TableCellContent* {
8772 DRW_TableCell *cell = currentCell();
8773 if (cell == nullptr)
8774 return nullptr;
8775 if (cell->m_contents.empty() || cell->m_contents.back().m_type != 1) {
8776 DRW_TableCellContent content;
8777 content.m_type = 1;
8778 cell->m_contents.push_back(content);
8779 }
8780 return &cell->m_contents.back();
8781 };
8782
8783 if (code == 100) {
8784 const std::string subclass = reader->getString();
8785 if (subclass == "AcDbBlockReference") {
8786 m_dxfSubclass = DxfSubclass::BlockReference;
8787 } else if (subclass == "AcDbTable") {
8788 m_dxfSubclass = DxfSubclass::Table;
8789 } else if (subclass == "AcDbEntity") {
8790 m_dxfSubclass = DxfSubclass::Entity;
8791 }
8792 return true;
8793 }
8794
8795 if (m_dxfSubclass != DxfSubclass::Table)
8796 return DRW_Insert::parseCode(code, reader);
8797
8798 // A cell value's point, double, date and object-id groups reuse codes the
8799 // table itself and its proxy graphics own; only the displayed text is kept.
8800 if (m_dxfInCellValue
8801 && (code == 11 || code == 21 || code == 31 || code == 140
8802 || code == 310 || code == 330)) {
8803 return true;
8804 }
8805
8806 switch (code) {
8807 case 342:
8808 m_tableStyleHandle = static_cast<std::uint32_t>(reader->getHandleString());
8809 m_content.m_tableStyleHandle = m_tableStyleHandle;
8810 break;
8811 case 343:
8812 reader->getHandleString();
8813 break;
8814 case 11:
8815 m_horizontalDirection.x = reader->getDouble();
8816 break;
8817 case 21:
8818 m_horizontalDirection.y = reader->getDouble();
8819 break;
8820 case 31:
8821 m_horizontalDirection.z = reader->getDouble();
8822 break;
8823 case 90:
8824 if (m_dxfInCellValue) {
8825 if (DRW_TableCellContent *content = currentContent())
8826 content->m_value.m_dataType = reader->getInt32();
8827 else
8828 reader->getInt32();
8829 } else {
8830 m_valueFlag = reader->getInt32();
8831 }
8832 break;
8833 case 91:
8834 if (m_dxfRowsExpected < 0 && !m_dxfInCellValue) {
8835 int value = 0;
8836 if (!readDxfIntInRange(reader, 0,
8837 static_cast<int>(kMaxTableRows), value))
8838 return false;
8839 m_dxfRowsExpected = value;
8840 if (!ensureGrid())
8841 return false;
8842 } else {
8843 reader->getInt32();
8844 }
8845 break;
8846 case 92:
8847 if (m_dxfColumnsExpected < 0 && !m_dxfInCellValue) {
8848 int value = 0;
8849 if (!readDxfIntInRange(reader, 0,
8850 static_cast<int>(kMaxTableColumns), value))
8851 return false;
8852 m_dxfColumnsExpected = value;
8853 if (!ensureGrid())
8854 return false;
8855 } else {
8856 reader->getInt32();
8857 }
8858 break;
8859 case 93:
8860 case 94:
8861 case 95:
8862 case 96:
8863 case 172:
8864 case 173:
8865 case 174:
8866 case 175:
8867 case 176:
8868 case 178:
8869 reader->getInt32();
8870 break;
8871 case 141:
8872 if (!ensureGrid())
8873 return false;
8874 if (m_dxfRowHeightsRead < m_content.m_rows.size())
8875 m_content.m_rows[m_dxfRowHeightsRead++].m_height = reader->getDouble();
8876 else
8877 reader->getDouble();
8878 break;
8879 case 142:
8880 if (!ensureGrid())
8881 return false;
8882 if (m_dxfColumnWidthsRead < m_content.m_columns.size())
8883 m_content.m_columns[m_dxfColumnWidthsRead++].m_width = reader->getDouble();
8884 else
8885 reader->getDouble();
8886 break;
8887 case 145:
8888 reader->getDouble();
8889 break;
8890 case 171:
8891 if (!ensureGrid())
8892 return false;
8893 if (!m_content.m_rows.empty() && !m_content.m_columns.empty()
8894 && m_dxfNextCell < m_content.m_rows.size() * m_content.m_columns.size()) {
8895 m_dxfCurrentCell = static_cast<int>(m_dxfNextCell++);
8896 if (DRW_TableCell *cell = currentCell())
8897 cell->m_flags = reader->getInt32();
8898 else
8899 reader->getInt32();
8900 } else {
8901 m_dxfCurrentCell = -1;
8902 reader->getInt32();
8903 }
8904 m_dxfInCellValue = false;
8905 break;
8906 case 301:
8907 m_dxfInCellValue = reader->getString() == "CELL_VALUE";
8908 if (m_dxfInCellValue)
8909 currentContent();
8910 break;
8911 case 1:
8912 case 302: {
8913 const UTF8STRINGstd::string text = reader->getUtf8String();
8914 if (m_dxfInCellValue) {
8915 if (DRW_TableCellContent *content = currentContent()) {
8916 content->m_text = text;
8917 content->m_value.m_dataType = 4;
8918 content->m_value.m_value.addString(1, text);
8919 }
8920 }
8921 break;
8922 }
8923 case 300:
8924 if (m_dxfInCellValue) {
8925 if (DRW_TableCellContent *content = currentContent())
8926 content->m_value.m_valueString = reader->getUtf8String();
8927 else
8928 reader->getUtf8String();
8929 } else {
8930 reader->getUtf8String();
8931 }
8932 break;
8933 case 304:
8934 reader->getString();
8935 m_dxfInCellValue = false;
8936 break;
8937 default:
8938 return DRW_Entity::parseCode(code, reader);
8939 }
8940
8941 return true;
8942}
8943
8944void DRW_Insert::resetDwgState() {
8945 DRW_Point::resetDwgState();
8946 name.clear();
8947 xscale = yscale = zscale = 1.0;
8948 angle = 0.0;
8949 colcount = rowcount = 1;
8950 colspace = rowspace = 0.0;
8951 attlist.clear();
8952 blockRecH = dwgHandle{};
8953 seqendH = dwgHandle{};
8954 attribHandles.clear();
8955}
8956
8957bool DRW_Insert::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
8958 resetDwgState();
8959 dwgBuffer *sourceBuf = buf;
8960 auto fail = [this, sourceBuf]() {
8961 if (sourceBuf != nullptr)
8962 sourceBuf->invalidate();
8963 resetDwgState();
8964 return false;
8965 };
8966 if (sourceBuf == nullptr)
8967 return fail();
8968
8969 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
8970 buf = &bodyProbe;
8971 if (!DRW_Entity::parseDwgCommon(version, buf, bs)
8972 || !bodyProbe.isGood())
8973 return fail();
8974
8975 std::int32_t objCount = 0;
8976 std::vector<dwgHandle> parsedAttribHandles;
8977 DRW_DBG("\n************************** parsing insert/minsert *****************************************\n")DRW_dbg::dbg("\n************************** parsing insert/minsert *****************************************\n"
)
;
8978 const std::uint64_t bodyEndBit = dwgDataEndBit;
8979 DRW_Coord parsedBasePoint;
8980 DRW_Coord parsedExtPoint;
8981 double parsedXScale = 1.0;
8982 double parsedYScale = 1.0;
8983 double parsedZScale = 1.0;
8984 double parsedAngle = 0.0;
8985 bool hasAttrib = false;
8986
8987 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint))
8988 return fail();
8989 DRW_DBG("insertion point: ")DRW_dbg::dbg("insertion point: ");
8990 DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
8991 DRW_DBG("\n")DRW_dbg::dbg("\n");
8992 if (version < DRW::AC1015) {//14-
8993 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedXScale)
8994 || !readBoundedBitDouble(*buf, bodyEndBit, parsedYScale)
8995 || !readBoundedBitDouble(*buf, bodyEndBit, parsedZScale))
8996 return fail();
8997 } else {
8998 if (!proxyEntityHasBits(*buf, bodyEndBit, 2))
8999 return fail();
9000 const std::uint8_t dataFlags = buf->get2Bits();
9001 if (!buf->isGood())
9002 return fail();
9003 if (dataFlags == 3) {
9004 //none default value 1,1,1
9005 } else if (dataFlags == 1) { //x default value 1, y & z use DD(1)
9006 if (!readBoundedDefaultDouble(*buf, bodyEndBit, 1.0,
9007 parsedYScale)
9008 || !readBoundedDefaultDouble(*buf, bodyEndBit, 1.0,
9009 parsedZScale))
9010 return fail();
9011 } else if (dataFlags == 2) {
9012 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedXScale))
9013 return fail();
9014 parsedYScale = parsedZScale = parsedXScale;
9015 } else { //dataFlags == 0
9016 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedXScale)
9017 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9018 parsedYScale)
9019 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9020 parsedZScale))
9021 return fail();
9022 }
9023 }
9024 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
9025 || !readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint)
9026 || !readBoundedBit(*buf, bodyEndBit, hasAttrib))
9027 return fail();
9028 DRW_DBG("scale : ")DRW_dbg::dbg("scale : ");
9029 DRW_DBGPT(parsedXScale, parsedYScale, parsedZScale)DRW_dbg::dbgPT(parsedXScale, parsedYScale, parsedZScale);
9030 DRW_DBG(", angle: ")DRW_dbg::dbg(", angle: "); DRW_DBG(parsedAngle)DRW_dbg::dbg(parsedAngle);
9031 DRW_DBG("\nextrusion: ")DRW_dbg::dbg("\nextrusion: ");
9032 DRW_DBGPT(parsedExtPoint.x, parsedExtPoint.y, parsedExtPoint.z)DRW_dbg::dbgPT(parsedExtPoint.x, parsedExtPoint.y, parsedExtPoint
.z)
;
9033
9034 DRW_DBG(" has Attrib: ")DRW_dbg::dbg(" has Attrib: "); DRW_DBG(hasAttrib)DRW_dbg::dbg(hasAttrib);
9035
9036 if (hasAttrib && version > DRW::AC1015) {//2004+
9037 if (!readBoundedBitLong(*buf, bodyEndBit, objCount)
9038 || !dwgSafety::validOwnedObjectCount(
9039 objCount, buf->numRemainingBytes())) {
9040 DRW_DBG("\nWARNING: Invalid INSERT owned-attribute count\n")DRW_dbg::dbg("\nWARNING: Invalid INSERT owned-attribute count\n"
)
;
9041 return fail();
9042 }
9043 DRW_DBG(" objCount: ")DRW_dbg::dbg(" objCount: "); DRW_DBG(objCount)DRW_dbg::dbg(objCount); DRW_DBG("\n")DRW_dbg::dbg("\n");
9044 }
9045 int parsedColCount = 1;
9046 int parsedRowCount = 1;
9047 double parsedColSpace = 0.0;
9048 double parsedRowSpace = 0.0;
9049 if (oType == 8) {//entity are minsert
9050 std::uint16_t colCount = 0;
9051 std::uint16_t rowCount = 0;
9052 if (!readBoundedBitShort(*buf, bodyEndBit, colCount)
9053 || !readBoundedBitShort(*buf, bodyEndBit, rowCount)
9054 || !readBoundedBitDouble(*buf, bodyEndBit, parsedColSpace)
9055 || !readBoundedBitDouble(*buf, bodyEndBit, parsedRowSpace))
9056 return fail();
9057 parsedColCount = static_cast<int>(colCount);
9058 parsedRowCount = static_cast<int>(rowCount);
9059 }
9060 const auto finite = [](const DRW_Coord& point) {
9061 return std::isfinite(point.x) && std::isfinite(point.y)
9062 && std::isfinite(point.z);
9063 };
9064 if (!finite(parsedBasePoint) || !finite(parsedExtPoint)
9065 || !std::isfinite(parsedXScale) || !std::isfinite(parsedYScale)
9066 || !std::isfinite(parsedZScale) || !std::isfinite(parsedAngle)
9067 || !std::isfinite(parsedColSpace) || !std::isfinite(parsedRowSpace))
9068 return fail();
9069 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9070 std::uint64_t handleEndBit = 0;
9071 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
9072 handleEndBit))
9073 return fail();
9074 dwgBuffer handleProbe = buf->forkIndependent();
9075 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
9076 handleEndBit))
9077 return fail();
9078 dwgHandle parsedBlockRecH;
9079 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
9080 parsedBlockRecH))
9081 return fail();
9082 dwgHandle parsedSeqendH;
9083 DRW_DBG("BLOCK HEADER Handle: ")DRW_dbg::dbg("BLOCK HEADER Handle: "); DRW_DBGHL(parsedBlockRecH.code, parsedBlockRecH.size, parsedBlockRecH.ref)DRW_dbg::dbgHL(parsedBlockRecH.code, parsedBlockRecH.size, parsedBlockRecH
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
9084 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9085
9086 /*attribs follows*/
9087 if (hasAttrib) {
9088 const std::int32_t expectedCount =
9089 version < DRW::AC1018 ? 2 : objCount;
9090 if (!DRW::reserve(parsedAttribHandles, expectedCount))
9091 return fail();
9092 if (version < DRW::AC1018) {//2000-
9093 dwgHandle attH;
9094 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
9095 attH))
9096 return fail();
9097 DRW_DBG("first attrib Handle: ")DRW_dbg::dbg("first attrib Handle: "); DRW_DBGHL(attH.code, attH.size, attH.ref)DRW_dbg::dbgHL(attH.code, attH.size, attH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9098 parsedAttribHandles.push_back(attH);
9099 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
9100 attH))
9101 return fail();
9102 DRW_DBG("second attrib Handle: ")DRW_dbg::dbg("second attrib Handle: "); DRW_DBGHL(attH.code, attH.size, attH.ref)DRW_dbg::dbgHL(attH.code, attH.size, attH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9103 parsedAttribHandles.push_back(attH);
9104 } else {
9105 for (std::int32_t i=0; i < objCount; ++i){
9106 dwgHandle attH;
9107 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0,
9108 false, attH))
9109 return fail();
9110 DRW_DBG("attrib Handle #")DRW_dbg::dbg("attrib Handle #"); DRW_DBG(i)DRW_dbg::dbg(i); DRW_DBG(": ")DRW_dbg::dbg(": "); DRW_DBGHL(attH.code, attH.size, attH.ref)DRW_dbg::dbgHL(attH.code, attH.size, attH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
9111 parsedAttribHandles.push_back(attH);
9112 }
9113 }
9114 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
9115 parsedSeqendH))
9116 return fail();
9117 DRW_DBG("seqendH Handle: ")DRW_dbg::dbg("seqendH Handle: "); DRW_DBGHL(parsedSeqendH.code, parsedSeqendH.size, parsedSeqendH.ref)DRW_dbg::dbgHL(parsedSeqendH.code, parsedSeqendH.size, parsedSeqendH
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
9118 }
9119 DRW_DBG(" Remaining bytes: ")DRW_dbg::dbg(" Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
9120
9121 if (!handleProbe.isGood())
9122 return fail();
9123 *sourceBuf = handleProbe;
9124 basePoint = parsedBasePoint;
9125 xscale = parsedXScale;
9126 yscale = parsedYScale;
9127 zscale = parsedZScale;
9128 angle = parsedAngle;
9129 extPoint = parsedExtPoint;
9130 colcount = parsedColCount;
9131 rowcount = parsedRowCount;
9132 colspace = parsedColSpace;
9133 rowspace = parsedRowSpace;
9134 blockRecH = parsedBlockRecH;
9135 seqendH = parsedSeqendH;
9136 attribHandles = std::move(parsedAttribHandles);
9137 // RS crc; //RS */
9138 return true;
9139}
9140
9141void DRW_Table::resetDwgState() {
9142 DRW_Insert::resetDwgState();
9143 name.clear();
9144 xscale = yscale = zscale = 1.0;
9145 angle = 0.0;
9146 colcount = rowcount = 1;
9147 colspace = rowspace = 0.0;
9148 m_valueFlag = 0;
9149 m_horizontalDirection = DRW_Coord{0.0, 0.0, 0.0};
9150 m_hasSemanticContent = false;
9151 m_semanticContentComplete = false;
9152 m_tableStyleHandle = 0;
9153 m_content = DRW_TableContent{};
9154 m_dxfSubclass = DxfSubclass::Entity;
9155 m_dxfRowsExpected = -1;
9156 m_dxfColumnsExpected = -1;
9157 m_dxfRowHeightsRead = 0;
9158 m_dxfColumnWidthsRead = 0;
9159 m_dxfNextCell = 0;
9160 m_dxfCurrentCell = -1;
9161 m_dxfInCellValue = false;
9162}
9163
9164bool DRW_Table::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
9165 resetDwgState();
9166 auto fail = [this, buf]() {
9167 if (buf != nullptr)
9168 buf->invalidate();
9169 resetDwgState();
9170 return false;
9171 };
9172 if (buf == nullptr || version < DRW::AC1015)
9173 return fail();
9174
9175 dwgBuffer *sourceBuf = buf;
9176 dwgBuffer bodyProbe = buf->forkIndependent();
9177 buf = &bodyProbe;
9178 auto commit = [&]() {
9179 *sourceBuf = bodyProbe;
9180 return true;
9181 };
9182
9183 dwgBuffer sBuff = bodyProbe.forkIndependent();
9184 sBuff.setVariableTextByteLength(true);
9185 dwgBuffer *sBuf = &sBuff;
9186 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
9187 if (!ret)
9188 return fail();
9189
9190 DRW_DBG("\n************************** parsing table *****************************************\n")DRW_dbg::dbg("\n************************** parsing table *****************************************\n"
)
;
9191 const std::uint64_t bodyEndBit = dwgDataEndBit;
9192 DRW_Coord parsedBasePoint;
9193 std::uint8_t dataFlags = 0;
9194 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
9195 || !proxyEntityHasBits(*buf, bodyEndBit, 2))
9196 return fail();
9197 dataFlags = buf->get2Bits();
9198 if (!buf->isGood())
9199 return fail();
9200
9201 double parsedXScale = 1.0;
9202 double parsedYScale = 1.0;
9203 double parsedZScale = 1.0;
9204 if (dataFlags == 3) {
9205 // default scale 1,1,1
9206 } else if (dataFlags == 1) {
9207 if (!readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9208 parsedYScale)
9209 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9210 parsedZScale))
9211 return fail();
9212 } else if (dataFlags == 2) {
9213 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedXScale))
9214 return fail();
9215 parsedYScale = parsedZScale = parsedXScale;
9216 } else {
9217 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedXScale)
9218 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9219 parsedYScale)
9220 || !readBoundedDefaultDouble(*buf, bodyEndBit, parsedXScale,
9221 parsedZScale))
9222 return fail();
9223 }
9224
9225 double parsedAngle = 0.0;
9226 DRW_Coord parsedExtPoint;
9227 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
9228 || !readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint))
9229 return fail();
9230
9231 if (!sBuf->isGood())
9232 return fail();
9233
9234 std::int32_t objCount = 0;
9235 bool hasAttrib = false;
9236 if (!readBoundedBit(*buf, bodyEndBit, hasAttrib))
9237 return fail();
9238 if (hasAttrib && version > DRW::AC1015) {
9239 if (!readBoundedBitLong(*buf, bodyEndBit, objCount)
9240 || !dwgSafety::validOwnedObjectCount(
9241 objCount, buf->numRemainingBytes())) {
9242 DRW_DBG("\nWARNING: Invalid TABLE owned-attribute count\n")DRW_dbg::dbg("\nWARNING: Invalid TABLE owned-attribute count\n"
)
;
9243 return fail();
9244 }
9245 }
9246
9247 std::uint64_t handleEndBit = 0;
9248 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
9249 handleEndBit))
9250 return fail();
9251 dwgBuffer hBuff = buf->forkIndependent();
9252 if (version <= DRW::AC1018) {
9253 // R2000/R2004: parseDwgEntHandle only re-seeks to the handle stream
9254 // for version > AC1018 (2007+ string area). For the legacy versions
9255 // seek the snapshot to the handle-stream start (objSize is the
9256 // bit offset of the handle stream, RL field read in
9257 // DRW_Entity::parseDwg) or every handle below reads mid-DATA garbage.
9258 if (!hBuff.setPosition(objSize >> 3))
9259 return fail();
9260 hBuff.setBitPos(objSize & 7);
9261 }
9262 ret = DRW_Entity::parseDwgEntHandle(version, &hBuff, true,
9263 handleEndBit);
9264 if (!ret)
9265 return fail();
9266 dwgHandle parsedBlockRecH;
9267 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9268 parsedBlockRecH))
9269 return fail();
9270
9271 std::vector<dwgHandle> parsedAttribHandles;
9272 dwgHandle parsedSeqendH;
9273
9274 if (hasAttrib) {
9275 if (!DRW::reserve(parsedAttribHandles, objCount))
9276 return fail();
9277 for (std::int32_t i = 0; i < objCount; ++i) {
9278 dwgHandle attribHandle;
9279 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9280 attribHandle))
9281 return fail();
9282 parsedAttribHandles.push_back(attribHandle);
9283 }
9284 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9285 parsedSeqendH))
9286 return fail();
9287 }
9288
9289 if (!hBuff.isGood())
9290 return fail();
9291
9292 blockRecH = parsedBlockRecH;
9293 seqendH = parsedSeqendH;
9294 attribHandles = std::move(parsedAttribHandles);
9295 basePoint = parsedBasePoint;
9296 xscale = parsedXScale;
9297 yscale = parsedYScale;
9298 zscale = parsedZScale;
9299 angle = parsedAngle;
9300 extPoint = parsedExtPoint;
9301
9302 if (version >= DRW::AC1024) {
9303 std::uint8_t ignoredTableVersion = 0;
9304 if (!readBoundedRawChar8(*buf, bodyEndBit, ignoredTableVersion))
9305 return fail();
9306 dwgHandle parsedTableStyleHandle;
9307 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9308 parsedTableStyleHandle))
9309 return fail();
9310 const std::uint32_t tableStyleHandle = parsedTableStyleHandle.ref;
9311 std::int32_t ignoredContentFlag = 0;
9312 if (!readBoundedBitLong(*buf, bodyEndBit, ignoredContentFlag))
9313 return fail();
9314 if (version >= DRW::AC1027) {
9315 std::int32_t ignoredContentVersion = 0;
9316 if (!readBoundedBitLong(*buf, bodyEndBit, ignoredContentVersion))
9317 return fail();
9318 } else {
9319 bool ignoredContentBit = false;
9320 if (!readBoundedBit(*buf, bodyEndBit, ignoredContentBit))
9321 return fail();
9322 }
9323
9324 if (!buf->isGood() || !hBuff.isGood())
9325 return fail();
9326
9327 m_hasSemanticContent = true;
9328 // TABLECONTENT is an optional semantic tail. Decode it on independent
9329 // cursors so a newer/partially supported cell variant cannot poison the
9330 // already validated entity prefix and common handle stream.
9331 dwgBuffer contentBuf = buf->forkIndependent();
9332 dwgBuffer contentStringBuf = sBuf->forkIndependent();
9333 dwgBuffer contentHandleBuf = hBuff.forkIndependent();
9334 TableDwgBounds tableBounds;
9335 tableBounds.bodyEndBit = tableBodyEndBit(version, sBuf, objSize);
9336 if (version > DRW::AC1021) {
9337 std::uint64_t stringStartBit = 0;
9338 if (!contentStringBuf.getR2007StringStreamBounds(
9339 objSize, stringStartBit, tableBounds.stringEndBit))
9340 return fail();
9341 } else {
9342 tableBounds.stringEndBit = proxyEntityEndBit(contentStringBuf, 0);
9343 }
9344 tableBounds.handleEndBit = handleEndBit;
9345 DRW_TableContent parsedContent;
9346 m_semanticContentComplete = parseTableContent(
9347 version, &contentBuf, &contentStringBuf, &contentHandleBuf,
9348 parsedContent, tableBounds);
9349 if (!m_semanticContentComplete || !contentBuf.isGood()
9350 || !contentStringBuf.isGood() || !contentHandleBuf.isGood()) {
9351 // The frame, entity prefix, and common handles remain valid. Keep
9352 // the typed table shell and allow the raw DWG frame to preserve
9353 // content that this reader does not yet model.
9354 m_semanticContentComplete = false;
9355 return commit();
9356 }
9357 *buf = contentBuf;
9358 *sBuf = contentStringBuf;
9359 hBuff = contentHandleBuf;
9360 m_content = std::move(parsedContent);
9361 if (m_content.m_tableStyleHandle != 0)
9362 m_tableStyleHandle = m_content.m_tableStyleHandle;
9363 else
9364 m_tableStyleHandle = tableStyleHandle;
9365 if (!buf->isGood() || !sBuf->isGood() || !hBuff.isGood())
9366 return fail();
9367 if (!m_semanticContentComplete) {
9368 DRW_DBG("TABLECONTENT parse incomplete; anonymous block insert kept\n")DRW_dbg::dbg("TABLECONTENT parse incomplete; anonymous block insert kept\n"
)
;
9369 return commit();
9370 }
9371
9372 std::uint16_t ignoredTableFlags = 0;
9373 DRW_Coord parsedHorizontalDirection;
9374 if (!readBoundedBitShort(*buf, bodyEndBit, ignoredTableFlags)
9375 || !readBoundedBitCoord(*buf, bodyEndBit,
9376 parsedHorizontalDirection))
9377 return fail();
9378 m_horizontalDirection = parsedHorizontalDirection;
9379
9380 const std::uint64_t breakStartBit = currentDwgBit(buf);
9381 std::int32_t breakDataFlag = 0;
9382 if (!readBoundedBitLong(*buf, bodyEndBit, breakDataFlag))
9383 return fail();
9384 const bool hasBreakData = breakDataFlag != 0;
9385 if (hasBreakData) {
9386 std::int32_t ignoredBreakType = 0;
9387 double ignoredBreakDistance = 0.0;
9388 std::int32_t manualPositionCount = 0;
9389 if (!readBoundedBitLong(*buf, bodyEndBit, ignoredBreakType)
9390 || !readBoundedBitLong(*buf, bodyEndBit, ignoredBreakType)
9391 || !readBoundedBitDouble(*buf, bodyEndBit,
9392 ignoredBreakDistance)
9393 || !readBoundedBitLong(*buf, bodyEndBit, ignoredBreakType)
9394 || !readBoundedBitLong(*buf, bodyEndBit, ignoredBreakType)
9395 || !readBoundedBitLong(*buf, bodyEndBit,
9396 manualPositionCount))
9397 return fail();
9398 if (manualPositionCount < 0
9399 || static_cast<std::uint32_t>(manualPositionCount)
9400 > kMaxTableItems) {
9401 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
9402 "table-break-data", breakStartBit, currentDwgBit(buf),
9403 version, manualPositionCount, false));
9404 return commit();
9405 }
9406 for (std::int32_t i = 0; i < manualPositionCount; ++i) {
9407 DRW_Coord ignoredPosition;
9408 double ignoredRotation = 0.0;
9409 std::int32_t ignoredPositionType = 0;
9410 if (!readBoundedBitCoord(*buf, bodyEndBit,
9411 ignoredPosition)
9412 || !readBoundedBitDouble(*buf, bodyEndBit,
9413 ignoredRotation)
9414 || !readBoundedBitLong(*buf, bodyEndBit,
9415 ignoredPositionType))
9416 return fail();
9417 }
9418 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
9419 "table-break-data", breakStartBit, currentDwgBit(buf),
9420 version, static_cast<std::uint32_t>(manualPositionCount),
9421 buf->isGood()));
9422 }
9423
9424 const std::uint64_t rowRangeStartBit = currentDwgBit(buf);
9425 std::int32_t rowRangeCount = 0;
9426 if (!readBoundedBitLong(*buf, bodyEndBit, rowRangeCount))
9427 return fail();
9428 if (rowRangeCount >= 0
9429 && static_cast<std::uint32_t>(rowRangeCount) <= kMaxTableItems) {
9430 for (std::int32_t i = 0; i < rowRangeCount; ++i) {
9431 DRW_Coord ignoredRangePoint;
9432 std::int32_t ignoredRangeValue = 0;
9433 if (!readBoundedBitCoord(*buf, bodyEndBit,
9434 ignoredRangePoint)
9435 || !readBoundedBitLong(*buf, bodyEndBit,
9436 ignoredRangeValue)
9437 || !readBoundedBitLong(*buf, bodyEndBit,
9438 ignoredRangeValue))
9439 return fail();
9440 }
9441 if (rowRangeCount != 0) {
9442 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
9443 "table-row-ranges", rowRangeStartBit, currentDwgBit(buf),
9444 version, static_cast<std::uint32_t>(rowRangeCount),
9445 buf->isGood()));
9446 }
9447 } else {
9448 m_content.m_subrecordRanges.push_back(makeDwgSubrecordRange(
9449 "table-row-ranges", rowRangeStartBit, currentDwgBit(buf),
9450 version, rowRangeCount, false));
9451 }
9452
9453 if (!buf->isGood() || !sBuf->isGood() || !hBuff.isGood())
9454 return fail();
9455 return commit();
9456 }
9457
9458 std::uint16_t parsedValueFlag = 0;
9459 DRW_Coord parsedHorizontalDirection;
9460 std::int32_t parsedColumnCount = 0;
9461 std::int32_t parsedRowCount = 0;
9462 if (!readBoundedBitShort(*buf, bodyEndBit, parsedValueFlag)
9463 || !readBoundedBitCoord(*buf, bodyEndBit,
9464 parsedHorizontalDirection)
9465 || !readBoundedBitLong(*buf, bodyEndBit, parsedColumnCount)
9466 || !readBoundedBitLong(*buf, bodyEndBit, parsedRowCount)
9467 || parsedColumnCount < 0 || parsedRowCount < 0)
9468 return fail();
9469 const std::uint32_t columns = static_cast<std::uint32_t>(parsedColumnCount);
9470 const std::uint32_t rows = static_cast<std::uint32_t>(parsedRowCount);
9471 if (columns > kMaxTableColumns || rows > kMaxTableRows
9472 || (columns != 0 && rows > kMaxTableCells / columns)) {
9473 return fail();
9474 }
9475
9476 m_hasSemanticContent = true;
9477 m_semanticContentComplete = false;
9478 m_content.m_columns.clear();
9479 m_content.m_rows.clear();
9480 if (!DRW::reserve(m_content.m_columns, static_cast<int>(columns))
9481 || !DRW::reserve(m_content.m_rows, static_cast<int>(rows)))
9482 return fail();
9483 for (std::uint32_t i = 0; i < columns; ++i) {
9484 DRW_TableColumn column;
9485 if (!readBoundedBitDouble(*buf, bodyEndBit, column.m_width))
9486 return fail();
9487 m_content.m_columns.push_back(column);
9488 }
9489 for (std::uint32_t i = 0; i < rows; ++i) {
9490 DRW_TableRow row;
9491 if (!readBoundedBitDouble(*buf, bodyEndBit, row.m_height))
9492 return fail();
9493 if (!DRW::reserve(row.m_cells, static_cast<int>(columns)))
9494 return fail();
9495 if (!DRW::resize(row.m_cells, static_cast<int>(columns)))
9496 return fail();
9497 m_content.m_rows.push_back(row);
9498 }
9499 dwgHandle parsedTableStyleHandle;
9500 if (!readBoundedDwgHandle(hBuff, handleEndBit, 0, false,
9501 parsedTableStyleHandle))
9502 return fail();
9503 m_tableStyleHandle = parsedTableStyleHandle.ref;
9504 if (!hBuff.isGood())
9505 return fail();
9506 m_content.m_tableStyleHandle = m_tableStyleHandle;
9507 m_semanticContentComplete = true;
9508 // For <=AC1018 (R2000/R2004) there is no separate R2007+ string stream:
9509 // DRW_Entity::parseDwg only seeks sBuf when version > AC1018 (see the
9510 // `strBuf != NULL && version > DRW::AC1018` guard there), so legacy cell
9511 // text is inline in `buf`. Passing the stale sBuf copy here would read
9512 // text from the wrong position and desync `buf`. Pass nullptr so the
9513 // cell readers' `textBuf = strBuf ? strBuf : buf` falls back to the
9514 // inline `buf`. R2007 (AC1021) keeps the separate sBuf stream.
9515 dwgBuffer *cellStrBuf = (version > DRW::AC1018) ? sBuf : nullptr;
9516 TableDwgBounds tableBounds;
9517 tableBounds.bodyEndBit = bodyEndBit;
9518 tableBounds.stringEndBit = cellStrBuf == nullptr
9519 ? bodyEndBit : proxyEntityEndBit(*cellStrBuf, 0);
9520 tableBounds.handleEndBit = handleEndBit;
9521 for (std::uint32_t row = 0; row < rows && m_semanticContentComplete; ++row) {
9522 for (std::uint32_t column = 0; column < columns; ++column) {
9523 if (!parseR2007TableCell(version, buf, cellStrBuf, &hBuff,
9524 m_content.m_rows[row].m_cells[column],
9525 &m_content.m_subrecordRanges,
9526 tableBounds)) {
9527 m_semanticContentComplete = false;
9528 break;
9529 }
9530 }
9531 }
9532
9533 if (m_semanticContentComplete)
9534 m_semanticContentComplete = skipR2007TableOverrides(
9535 version, buf, cellStrBuf, &hBuff, &m_content.m_subrecordRanges,
9536 tableBounds);
9537 if (!m_semanticContentComplete)
9538 DRW_DBG("R2007 TABLE cell parse incomplete; anonymous block insert kept\n")DRW_dbg::dbg("R2007 TABLE cell parse incomplete; anonymous block insert kept\n"
)
;
9539
9540 if (!buf->isGood() || !sBuf->isGood() || !hBuff.isGood())
9541 return fail();
9542 m_valueFlag = parsedValueFlag;
9543 m_horizontalDirection = parsedHorizontalDirection;
9544 return commit();
9545}
9546
9547bool DRW_TableContentObject::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
9548 reset();
9549 const auto fail = [this, buf]() {
9550 if (buf != nullptr)
9551 buf->invalidate();
9552 reset();
9553 return false;
9554 };
9555 if (buf == nullptr || version <= DRW::AC1018)
9556 return fail();
9557
9558 dwgBuffer sBuff = *buf;
9559 sBuff.setVariableTextByteLength(true);
9560 dwgBuffer *sBuf = &sBuff;
9561 bool ret = DRW_TableEntry::parseDwg(version, buf, sBuf, bs);
9562 DRW_DBG("\n************************** parsing table content object ************************\n")DRW_dbg::dbg("\n************************** parsing table content object ************************\n"
)
;
9563 if (!ret)
9564 return fail();
9565
9566 dwgBuffer bodyBuff = *buf;
9567 dwgBuffer *bodyBuf = &bodyBuff;
9568
9569 dwgBuffer hBuff = *buf;
9570 seekTableObjectHandleStream(version, &hBuff, objSize);
9571 std::uint64_t handleEndBit = 0;
9572 if (!dwgHandleStreamEndBit(*buf, version, objSize, bs, handleEndBit))
9573 return fail();
9574 std::uint32_t parsedParentHandle = 0;
9575 std::vector<std::uint32_t> parsedReactors;
9576 std::uint32_t parsedXDictHandle = 0;
9577 if (!readTableObjectCommonHandles(&hBuff, handle, numReactors, xDictFlag,
9578 &parsedParentHandle, &parsedReactors,
9579 &parsedXDictHandle, handleEndBit))
9580 return fail();
9581
9582 DRW_TableContent parsedContent;
9583 TableDwgBounds tableBounds;
9584 tableBounds.bodyEndBit = tableBodyEndBit(version, sBuf, objSize);
9585 tableBounds.stringEndBit = proxyEntityEndBit(*sBuf, 0);
9586 tableBounds.handleEndBit = handleEndBit;
9587 const bool parseComplete = parseTableContent(
9588 version, bodyBuf, sBuf, &hBuff, parsedContent, tableBounds);
9589 const std::uint64_t bodyBitOffset =
9590 bodyBuf->getPosition() * 8u + bodyBuf->getBitPos();
9591 if (!parseComplete || !bodyBuf->isGood() || !sBuf->isGood()
9592 || !hBuff.isGood() || bodyBitOffset > objSize)
9593 return fail();
9594
9595 m_content = std::move(parsedContent);
9596 m_parseComplete = true;
9597 parentHandle = parsedParentHandle;
9598 reactorHandles = std::move(parsedReactors);
9599 xDictHandle = parsedXDictHandle;
9600 return true;
9601}
9602
9603void DRW_LWPolyline::applyExtrusion(){
9604 if (haveExtrusion) {
9605 calculateAxis(extPoint);
9606 for (unsigned int i=0; i<vertlist.size(); i++) {
9607 auto& vert = vertlist.at(i);
9608 DRW_Coord v(vert->x, vert->y, elevation);
9609 extrudePoint(extPoint, &v);
9610 vert->x = v.x;
9611 vert->y = v.y;
9612 }
9613 }
9614}
9615
9616bool DRW_LWPolyline::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
9617 switch (code) {
9618 case 10: {
9619 if (vertlist.size() >= static_cast<std::size_t>(kMaxLWPolylineVertices)
9620 || (m_dxfVertexCountSeen
9621 && vertlist.size() >= static_cast<std::size_t>(vertexnum)))
9622 return false;
9623 vertex = std::make_shared<DRW_Vertex2D>();
9624 vertlist.push_back(vertex);
9625 vertex->x = reader->getDouble();
9626 break; }
9627 case 20:
9628 if(vertex)
9629 vertex->y = reader->getDouble();
9630 break;
9631 case 40:
9632 if(vertex)
9633 vertex->stawidth = reader->getDouble();
9634 break;
9635 case 41:
9636 if(vertex)
9637 vertex->endwidth = reader->getDouble();
9638 break;
9639 case 42:
9640 if(vertex)
9641 vertex->bulge = reader->getDouble();
9642 break;
9643 case 91:
9644 if (vertex)
9645 vertex->identifier = reader->getInt32();
9646 break;
9647 case 38:
9648 elevation = reader->getDouble();
9649 break;
9650 case 39:
9651 thickness = reader->getDouble();
9652 break;
9653 case 43:
9654 width = reader->getDouble();
9655 break;
9656 case 70:
9657 flags = reader->getInt32();
9658 break;
9659 case 90:
9660 if (m_dxfVertexCountSeen)
9661 return false;
9662 vertexnum = reader->getInt32();
9663 if (!isValidCount(vertexnum, kMaxLWPolylineVertices))
9664 return false;
9665 m_dxfVertexCountSeen = true;
9666 return DRW::reserve(vertlist, vertexnum);
9667 case 210:
9668 haveExtrusion = true;
9669 extPoint.x = reader->getDouble();
9670 break;
9671 case 220:
9672 extPoint.y = reader->getDouble();
9673 break;
9674 case 230:
9675 extPoint.z = reader->getDouble();
9676 break;
9677 default:
9678 return DRW_Entity::parseCode(code, reader);
9679 }
9680
9681 return true;
9682}
9683
9684bool DRW_LWPolyline::validateDxf() const {
9685 return (!m_dxfVertexCountSeen
9686 || vertlist.size() == static_cast<std::size_t>(vertexnum))
9687 && validatePayloadFields();
9688}
9689
9690bool DRW_LWPolyline::validatePayloadFields() const {
9691 const auto finite = [](double value) { return std::isfinite(value); };
9692 const auto finiteVertex = [&finite](const auto& vertex) {
9693 return vertex != nullptr && finite(vertex->x) && finite(vertex->y)
9694 && finite(vertex->stawidth) && finite(vertex->endwidth)
9695 && finite(vertex->bulge)
9696 && vertex->identifier >= std::numeric_limits<std::int32_t>::min()
9697 && vertex->identifier <= std::numeric_limits<std::int32_t>::max();
9698 };
9699
9700 // LWPOLYLINE group 70 has only the closed and linetype-generation bits.
9701 return flags >= 0 && (flags & ~0x81) == 0
9702 && finite(width) && finite(elevation) && finite(thickness)
9703 && finite(extPoint.x) && finite(extPoint.y) && finite(extPoint.z)
9704 && vertlist.size() <= static_cast<std::size_t>(kMaxLWPolylineVertices)
9705 && vertlist.size()
9706 <= static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())
9707 && std::all_of(vertlist.cbegin(), vertlist.cend(), finiteVertex);
9708}
9709
9710bool DRW_LWPolyline::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
9711 const auto resetState = [this]() {
9712 DRW_Entity::reset();
9713 vertexnum = 0;
9714 flags = 0;
9715 width = 0.0;
9716 elevation = 0.0;
9717 thickness = 0.0;
9718 extPoint = DRW_Coord{0.0, 0.0, 1.0};
9719 vertlist.clear();
9720 vertex.reset();
9721 m_dxfVertexCountSeen = false;
9722 };
9723 resetState();
9724 dwgBuffer *sourceBuf = buf;
9725 const auto fail = [sourceBuf, resetState]() {
9726 if (sourceBuf != nullptr)
9727 sourceBuf->invalidate();
9728 resetState();
9729 return false;
9730 };
9731 if (sourceBuf == nullptr)
9732 return fail();
9733
9734 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
9735 buf = &bodyProbe;
9736 bool ret = DRW_Entity::parseDwgCommon(version, buf, bs);
9737 if (!ret || !buf->isGood())
9738 return fail();
9739 DRW_DBG("\n***************************** parsing LWPolyline *******************************************\n")DRW_dbg::dbg("\n***************************** parsing LWPolyline *******************************************\n"
)
;
9740
9741 const std::uint64_t bodyEndBit = dwgDataEndBit;
9742 std::uint16_t rawFlagsValue = 0;
9743 if (!readBoundedBitShort(*buf, bodyEndBit, rawFlagsValue))
9744 return fail();
9745 const int rawFlags = static_cast<int>(rawFlagsValue);
9746 double parsedWidth = 0.0;
9747 double parsedElevation = 0.0;
9748 double parsedThickness = 0.0;
9749 DRW_Coord parsedExtPoint{0.0, 0.0, 1.0};
9750 if ((rawFlags & 4)
9751 && !readBoundedBitDouble(*buf, bodyEndBit, parsedWidth))
9752 return fail();
9753 if ((rawFlags & 8)
9754 && !readBoundedBitDouble(*buf, bodyEndBit, parsedElevation))
9755 return fail();
9756 if ((rawFlags & 2)
9757 && !readBoundedBitDouble(*buf, bodyEndBit, parsedThickness))
9758 return fail();
9759 if ((rawFlags & 1)
9760 && !readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint))
9761 return fail();
9762 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(rawFlags)DRW_dbg::dbg(rawFlags);
9763
9764 const std::uint32_t minimumVertexBits = version < DRW::AC1015 ? 128u : 4u;
9765 std::uint32_t parsedVertexCount = 0;
9766 if (!readTableBodyCount(version, buf, objSize,
9767 kMaxLWPolylineVertices, minimumVertexBits,
9768 parsedVertexCount)) {
9769 return fail();
9770 }
9771
9772 std::uint64_t vertexBits = 0;
9773 if (parsedVertexCount > 0) {
9774 if (version < DRW::AC1015) {
9775 if (!dwgSafety::multiply(parsedVertexCount, 128, vertexBits))
9776 return fail();
9777 } else {
9778 std::uint64_t deltaBits = 0;
9779 if (!dwgSafety::multiply(parsedVertexCount - 1, 4, deltaBits)
9780 || !dwgSafety::add(128, deltaBits, vertexBits))
9781 return fail();
9782 }
9783 }
9784 if (!proxyEntityHasBits(*buf, bodyEndBit, vertexBits))
9785 return fail();
9786
9787 std::vector<std::shared_ptr<DRW_Vertex2D>> parsedVertices;
9788 if (!DRW::reserve(parsedVertices, parsedVertexCount))
9789 return fail();
9790
9791 std::uint32_t bulgesnum = 0;
9792 if (rawFlags & 16) {
9793 if (!readTableBodyCount(version, buf, objSize,
9794 kMaxLWPolylineVertices, 2,
9795 bulgesnum))
9796 return fail();
9797 }
9798 std::uint32_t vertexIdCount = 0;
9799 if (version > DRW::AC1021) {//2010+
9800 if (rawFlags & 1024) {
9801 if (!readTableBodyCount(version, buf, objSize,
9802 kMaxLWPolylineVertices, 2,
9803 vertexIdCount))
9804 return fail();
9805 }
9806 }
9807 std::uint32_t widthsnum = 0;
9808 if (rawFlags & 32) {
9809 if (!readTableBodyCount(version, buf, objSize,
9810 kMaxLWPolylineVertices, 4,
9811 widthsnum))
9812 return fail();
9813 }
9814 std::uint64_t bulgeBits = 0;
9815 std::uint64_t vertexIdBits = 0;
9816 std::uint64_t widthBits = 0;
9817 std::uint64_t optionalBits = 0;
9818 if (!dwgSafety::multiply(bulgesnum, 2, bulgeBits)
9819 || !dwgSafety::multiply(vertexIdCount, 2, vertexIdBits)
9820 || !dwgSafety::multiply(widthsnum, 4, widthBits)
9821 || !dwgSafety::add(bulgeBits, vertexIdBits, optionalBits)
9822 || !dwgSafety::add(optionalBits, widthBits, optionalBits)) {
9823 return fail();
9824 }
9825 if (!proxyEntityHasBits(*buf, bodyEndBit, optionalBits))
9826 return fail();
9827 DRW_DBG("\nvertex num: ")DRW_dbg::dbg("\nvertex num: "); DRW_DBG(parsedVertexCount)DRW_dbg::dbg(parsedVertexCount); DRW_DBG(" bulges num: ")DRW_dbg::dbg(" bulges num: "); DRW_DBG(bulgesnum)DRW_dbg::dbg(bulgesnum);
9828 DRW_DBG(" vertexIdCount: ")DRW_dbg::dbg(" vertexIdCount: "); DRW_DBG(vertexIdCount)DRW_dbg::dbg(vertexIdCount); DRW_DBG(" widths num: ")DRW_dbg::dbg(" widths num: "); DRW_DBG(widthsnum)DRW_dbg::dbg(widthsnum);
9829
9830 if (parsedVertexCount > 0) { //verify if is lwpol without vertex (empty)
9831 // add vertexes
9832 auto parsedVertex = std::make_shared<DRW_Vertex2D>();
9833 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedVertex->x)
9834 || !readBoundedRawDouble(*buf, bodyEndBit, parsedVertex->y))
9835 return fail();
9836 parsedVertices.push_back(parsedVertex);
9837 auto pv = parsedVertex;
9838 for (std::uint32_t i = 1; i < parsedVertexCount; ++i){
9839 parsedVertex = std::make_shared<DRW_Vertex2D>();
9840 if (version < DRW::AC1015) {//14-
9841 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedVertex->x)
9842 || !readBoundedRawDouble(*buf, bodyEndBit, parsedVertex->y))
9843 return fail();
9844 } else {
9845 if (!readBoundedDefaultDouble(*buf, bodyEndBit, pv->x,
9846 parsedVertex->x)
9847 || !readBoundedDefaultDouble(*buf, bodyEndBit, pv->y,
9848 parsedVertex->y))
9849 return fail();
9850 }
9851 pv = parsedVertex;
9852 parsedVertices.push_back(parsedVertex);
9853 }
9854 }
9855 // The optional vectors are independent DWG fields, not per-point
9856 // conditionals. Consume them even when num_points is zero so the handle
9857 // stream remains aligned; values without a corresponding vertex are
9858 // intentionally discarded by the existing model.
9859 for (std::uint32_t i = 0; i < bulgesnum; i++){
9860 double bulge = 0.0;
9861 if (!readBoundedBitDouble(*buf, bodyEndBit, bulge))
9862 return fail();
9863 if (i < parsedVertices.size())
9864 parsedVertices.at(i)->bulge = bulge;
9865 }
9866 //add vertexId
9867 if (version > DRW::AC1021) {//2010+
9868 for (std::uint32_t i = 0; i < vertexIdCount; i++){
9869 std::int32_t vertexId = 0;
9870 if (!readBoundedBitLong(*buf, bodyEndBit, vertexId))
9871 return fail();
9872 if (i < parsedVertices.size())
9873 parsedVertices.at(i)->identifier = vertexId;
9874 }
9875 }
9876 //add widths
9877 for (std::uint32_t i = 0; i < widthsnum; i++){
9878 double staW = 0.0;
9879 double endW = 0.0;
9880 if (!readBoundedBitDouble(*buf, bodyEndBit, staW)
9881 || !readBoundedBitDouble(*buf, bodyEndBit, endW))
9882 return fail();
9883 if (i < parsedVertices.size()) {
9884 parsedVertices.at(i)->stawidth = staW;
9885 parsedVertices.at(i)->endwidth = endW;
9886 }
9887 }
9888 if (DRW_DBGGLDRW_dbg::getInstance()->getLevel() == DRW_dbg::Level::Debug){
9889 DRW_DBG("\nVertex list: ")DRW_dbg::dbg("\nVertex list: ");
9890 for (auto& pv: parsedVertices) {
9891 DRW_DBG("\n x: ")DRW_dbg::dbg("\n x: "); DRW_DBG(pv->x)DRW_dbg::dbg(pv->x); DRW_DBG(" y: ")DRW_dbg::dbg(" y: "); DRW_DBG(pv->y)DRW_dbg::dbg(pv->y); DRW_DBG(" bulge: ")DRW_dbg::dbg(" bulge: "); DRW_DBG(pv->bulge)DRW_dbg::dbg(pv->bulge);
9892 DRW_DBG(" stawidth: ")DRW_dbg::dbg(" stawidth: "); DRW_DBG(pv->stawidth)DRW_dbg::dbg(pv->stawidth); DRW_DBG(" endwidth: ")DRW_dbg::dbg(" endwidth: "); DRW_DBG(pv->endwidth)DRW_dbg::dbg(pv->endwidth);
9893 DRW_DBG(" identifier: ")DRW_dbg::dbg(" identifier: "); DRW_DBG(pv->identifier)DRW_dbg::dbg(pv->identifier);
9894 }
9895 }
9896
9897 const auto finite = [](const DRW_Coord& point) {
9898 return std::isfinite(point.x) && std::isfinite(point.y)
9899 && std::isfinite(point.z);
9900 };
9901 if (!finite(parsedExtPoint) || !std::isfinite(parsedWidth)
9902 || !std::isfinite(parsedElevation) || !std::isfinite(parsedThickness)
9903 || !std::all_of(parsedVertices.begin(), parsedVertices.end(),
9904 [](const std::shared_ptr<DRW_Vertex2D>& point) {
9905 return point != nullptr
9906 && std::isfinite(point->x)
9907 && std::isfinite(point->y)
9908 && std::isfinite(point->bulge)
9909 && std::isfinite(point->stawidth)
9910 && std::isfinite(point->endwidth);
9911 }))
9912 return fail();
9913
9914 DRW_DBG("\n")DRW_dbg::dbg("\n");
9915 /* Common Entity Handle Data */
9916 std::uint64_t handleEndBit = 0;
9917 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
9918 handleEndBit))
9919 return fail();
9920 dwgBuffer handleProbe = buf->forkIndependent();
9921 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
9922 handleEndBit);
9923 if (!ret || !handleProbe.isGood())
9924 return fail();
9925
9926 // Translate DWG LWPLINE flag bits to DXF group 70 bits.
9927 int dxfFlags = 0;
9928 if (rawFlags & 512)
9929 dxfFlags |= 1;
9930 if (rawFlags & 256)
9931 dxfFlags |= 128;
9932 vertexnum = static_cast<std::int32_t>(parsedVertexCount);
9933 flags = dxfFlags;
9934 width = parsedWidth;
9935 elevation = parsedElevation;
9936 thickness = parsedThickness;
9937 extPoint = parsedExtPoint;
9938 vertlist = std::move(parsedVertices);
9939 *sourceBuf = handleProbe;
9940 DRW_DBG("end flags value: ")DRW_dbg::dbg("end flags value: "); DRW_DBG(flags)DRW_dbg::dbg(flags);
9941 /* CRC X --- */
9942 return true;
9943}
9944
9945
9946// ----------------------------------------------------------------------------
9947// DRW_MLine — multiline entity (ODA §19.4.78, fixed type 0x2F = 47).
9948// ----------------------------------------------------------------------------
9949
9950bool DRW_MLine::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
9951 switch (code) {
9952 case 2:
9953 styleName = reader->getString();
9954 break;
9955 case 340:
9956 styleHandle = static_cast<std::uint32_t>(reader->getHandleString());
9957 break;
9958 case 40:
9959 scale = reader->getDouble();
9960 break;
9961 case 70:
9962 {
9963 int value = 0;
9964 if (!readDxfIntInRange(reader, 0, 2, value))
9965 return false;
9966 justification = static_cast<std::uint8_t>(value);
9967 }
9968 break;
9969 case 71:
9970 openClosed = reader->getInt32();
9971 break;
9972 case 72:
9973 if (m_dxfVertexCountSeen)
9974 return false;
9975 {
9976 const std::int32_t count = reader->getInt32();
9977 if (!isValidCount(count, kMaxDxfVertices))
9978 return false;
9979 numVerts = static_cast<std::uint16_t>(count);
9980 m_dxfVertexCountSeen = true;
9981 }
9982 break;
9983 case 73:
9984 if (m_dxfLineCountSeen)
9985 return false;
9986 {
9987 const std::int32_t count = reader->getInt32();
9988 if (!isValidCount(count, kMaxDxfLines))
9989 return false;
9990 numLines = static_cast<std::uint8_t>(count);
9991 m_dxfLineCountSeen = true;
9992 }
9993 break;
9994 case 10:
9995 basePoint.x = reader->getDouble();
9996 break;
9997 case 20:
9998 basePoint.y = reader->getDouble();
9999 break;
10000 case 30:
10001 basePoint.z = reader->getDouble();
10002 break;
10003 case 210:
10004 extPoint.x = reader->getDouble();
10005 break;
10006 case 220:
10007 extPoint.y = reader->getDouble();
10008 break;
10009 case 230:
10010 extPoint.z = reader->getDouble();
10011 break;
10012 // Per-vertex block: code 11 starts a new vertex; 12/13 follow.
10013 case 11:
10014 if (m_currentSegExpected >= 0 || m_currentAreaExpected >= 0)
10015 return false;
10016 ++m_currentVertexIdx;
10017 m_currentElementIdx = 0;
10018 if (m_currentVertexIdx >= kMaxDxfVertices
10019 || (m_dxfVertexCountSeen
10020 && m_currentVertexIdx >= static_cast<int>(numVerts)))
10021 return false;
10022 vertlist.emplace_back();
10023 vertlist[m_currentVertexIdx].position.x = reader->getDouble();
10024 break;
10025 case 21:
10026 if (m_currentVertexIdx >= 0)
10027 vertlist[m_currentVertexIdx].position.y = reader->getDouble();
10028 break;
10029 case 31:
10030 if (m_currentVertexIdx >= 0)
10031 vertlist[m_currentVertexIdx].position.z = reader->getDouble();
10032 break;
10033 case 12:
10034 if (m_currentVertexIdx >= 0)
10035 vertlist[m_currentVertexIdx].vertexDir.x = reader->getDouble();
10036 break;
10037 case 22:
10038 if (m_currentVertexIdx >= 0)
10039 vertlist[m_currentVertexIdx].vertexDir.y = reader->getDouble();
10040 break;
10041 case 32:
10042 if (m_currentVertexIdx >= 0)
10043 vertlist[m_currentVertexIdx].vertexDir.z = reader->getDouble();
10044 break;
10045 case 13:
10046 if (m_currentVertexIdx >= 0)
10047 vertlist[m_currentVertexIdx].miterDir.x = reader->getDouble();
10048 break;
10049 case 23:
10050 if (m_currentVertexIdx >= 0)
10051 vertlist[m_currentVertexIdx].miterDir.y = reader->getDouble();
10052 break;
10053 case 33:
10054 if (m_currentVertexIdx >= 0)
10055 vertlist[m_currentVertexIdx].miterDir.z = reader->getDouble();
10056 break;
10057 // 74 = segment-param count for current element. Sets up the inner
10058 // vector and resets the running param count. 41 reads each param.
10059 // 75 = fill-param count; 42 reads each. After fills are consumed,
10060 // advance to the next element. AutoCAD emits 74/41*/75/42* per element.
10061 case 74:
10062 if (m_currentVertexIdx < 0
10063 || m_currentSegExpected >= 0 || m_currentAreaExpected >= 0)
10064 return false;
10065 {
10066 auto& v = vertlist[m_currentVertexIdx];
10067 if (m_currentElementIdx != static_cast<int>(v.segParms.size())
10068 || m_currentElementIdx >= kMaxDxfLines
10069 || (m_dxfLineCountSeen
10070 && m_currentElementIdx >= static_cast<int>(numLines)))
10071 return false;
10072 const std::int32_t count = reader->getInt32();
10073 if (!isValidCount(count, kMaxDxfParameters))
10074 return false;
10075 v.segParms.emplace_back();
10076 v.areaFillParms.emplace_back();
10077 m_currentSegExpected = count;
10078 m_currentSegFillCount = 0;
10079 }
10080 break;
10081 case 41:
10082 if (m_currentVertexIdx < 0 || m_currentSegExpected < 0
10083 || m_currentElementIdx >= static_cast<int>(vertlist[m_currentVertexIdx].segParms.size()))
10084 return false;
10085 if (static_cast<std::int32_t>(
10086 vertlist[m_currentVertexIdx].segParms[m_currentElementIdx].size())
10087 >= m_currentSegExpected)
10088 return false;
10089 vertlist[m_currentVertexIdx].segParms[m_currentElementIdx]
10090 .push_back(reader->getDouble());
10091 break;
10092 case 75:
10093 if (m_currentVertexIdx < 0 || m_currentSegExpected < 0
10094 || m_currentAreaExpected >= 0
10095 || m_currentElementIdx >= static_cast<int>(vertlist[m_currentVertexIdx].segParms.size()))
10096 return false;
10097 if (static_cast<std::int32_t>(
10098 vertlist[m_currentVertexIdx].segParms[m_currentElementIdx].size())
10099 != m_currentSegExpected)
10100 return false;
10101 m_currentSegExpected = -1;
10102 m_currentSegFillCount = reader->getInt32();
10103 if (!isValidCount(m_currentSegFillCount, kMaxDxfParameters))
10104 return false;
10105 m_currentAreaExpected = m_currentSegFillCount;
10106 if (m_currentAreaExpected == 0) {
10107 m_currentAreaExpected = -1;
10108 ++m_currentElementIdx;
10109 }
10110 break;
10111 case 42:
10112 if (m_currentVertexIdx < 0 || m_currentAreaExpected < 0
10113 || m_currentElementIdx >= static_cast<int>(vertlist[m_currentVertexIdx].areaFillParms.size()))
10114 return false;
10115 if (static_cast<std::int32_t>(
10116 vertlist[m_currentVertexIdx].areaFillParms[m_currentElementIdx].size())
10117 >= m_currentAreaExpected)
10118 return false;
10119 vertlist[m_currentVertexIdx].areaFillParms[m_currentElementIdx]
10120 .push_back(reader->getDouble());
10121 if (static_cast<std::int32_t>(
10122 vertlist[m_currentVertexIdx].areaFillParms[m_currentElementIdx].size())
10123 == m_currentAreaExpected) {
10124 m_currentAreaExpected = -1;
10125 ++m_currentElementIdx;
10126 }
10127 break;
10128 default:
10129 return DRW_Entity::parseCode(code, reader);
10130 }
10131 return true;
10132}
10133
10134bool DRW_MLine::validateDxf() const {
10135 if (m_currentSegExpected >= 0 || m_currentAreaExpected >= 0)
10136 return false;
10137 if (m_dxfVertexCountSeen && vertlist.size() != numVerts)
10138 return false;
10139 if (!m_dxfLineCountSeen)
10140 return true;
10141 for (const DRW_MLineVertex& vertex : vertlist) {
10142 if (vertex.segParms.size() != numLines
10143 || vertex.areaFillParms.size() != numLines)
10144 return false;
10145 }
10146 return validatePayloadFields();
10147}
10148
10149bool DRW_MLine::validatePayloadFields() const {
10150 const auto finite = [](const DRW_Coord& point) {
10151 return std::isfinite(point.x) && std::isfinite(point.y)
10152 && std::isfinite(point.z);
10153 };
10154 if (!finite(basePoint) || !finite(extPoint) || !std::isfinite(scale)
10155 || openClosed < 0 || openClosed > 0xffff
10156 || numVerts > static_cast<std::uint16_t>(kMaxDxfVertices)
10157 || vertlist.size() > static_cast<std::size_t>(kMaxDxfVertices)
10158 || vertlist.size() != static_cast<std::size_t>(numVerts))
10159 return false;
10160
10161 for (const DRW_MLineVertex& vertex : vertlist) {
10162 if (!finite(vertex.position) || !finite(vertex.vertexDir)
10163 || !finite(vertex.miterDir)
10164 || vertex.segParms.size() != numLines
10165 || vertex.areaFillParms.size() != numLines)
10166 return false;
10167 const auto validParameters = [](const auto& parameterLists) {
10168 return std::all_of(
10169 parameterLists.cbegin(), parameterLists.cend(),
10170 [](const std::vector<double>& values) {
10171 return values.size() <= static_cast<std::size_t>(
10172 DRW_MLine::kMaxDxfParameters)
10173 && std::all_of(values.cbegin(), values.cend(),
10174 [](double value) {
10175 return std::isfinite(value);
10176 });
10177 });
10178 };
10179 if (!validParameters(vertex.segParms)
10180 || !validParameters(vertex.areaFillParms))
10181 return false;
10182 }
10183 return true;
10184}
10185
10186void DRW_MLine::resetDwgState() {
10187 DRW_Entity::reset();
10188 scale = 1.0;
10189 justification = 0;
10190 basePoint = DRW_Coord{0.0, 0.0, 0.0};
10191 extPoint = DRW_Coord{0.0, 0.0, 1.0};
10192 openClosed = 1;
10193 numLines = 0;
10194 numVerts = 0;
10195 styleName.clear();
10196 styleHandle = 0;
10197 vertlist.clear();
10198 m_currentVertexIdx = -1;
10199 m_currentElementIdx = 0;
10200 m_currentSegFillCount = 0;
10201 m_currentSegExpected = -1;
10202 m_currentAreaExpected = -1;
10203 m_dxfVertexCountSeen = false;
10204 m_dxfLineCountSeen = false;
10205}
10206
10207bool DRW_MLine::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
10208 resetDwgState();
10209 dwgBuffer *sourceBuf = buf;
10210 auto fail = [this, sourceBuf]() {
10211 if (sourceBuf != nullptr)
10212 sourceBuf->invalidate();
10213 resetDwgState();
10214 return false;
10215 };
10216 if (sourceBuf == nullptr)
10217 return fail();
10218
10219 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
10220 buf = &bodyProbe;
10221
10222 if (!DRW_Entity::parseDwgCommon(version, buf, bs)) return fail();
10223 DRW_DBG("\n***************************** parsing MLINE *********************\n")DRW_dbg::dbg("\n***************************** parsing MLINE *********************\n"
)
;
10224 // Per ODA §19.4.78 / libreDWG dwg_decode_MLINE:
10225 // BD scale, RC justification, 3BD basePoint, BE extrusion,
10226 // BS open/closed flag, RC num_lines, BS num_verts,
10227 // then per-vertex: 3BD pos, 3BD vdir, 3BD mdir,
10228 // per-line: BS num_segparms × BD parm, BS num_areafillparms × BD parm.
10229 const std::uint64_t bodyEndBit = dwgDataEndBit;
10230 double parsedScale = 0.0;
10231 std::uint8_t parsedJustification = 0;
10232 DRW_Coord parsedBasePoint;
10233 DRW_Coord parsedExtPoint;
10234 std::uint16_t parsedOpenClosed = 0;
10235 std::uint8_t parsedNumLines = 0;
10236 std::uint16_t parsedNumVerts = 0;
10237 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedScale)
10238 || !readBoundedRawChar8(*buf, bodyEndBit, parsedJustification)
10239 || !readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
10240 || !readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint)
10241 || !readBoundedBitShort(*buf, bodyEndBit, parsedOpenClosed)
10242 || !readBoundedRawChar8(*buf, bodyEndBit, parsedNumLines)
10243 || !readBoundedBitShort(*buf, bodyEndBit, parsedNumVerts))
10244 return fail();
10245 if (!std::isfinite(parsedScale)
10246 || !std::isfinite(parsedBasePoint.x)
10247 || !std::isfinite(parsedBasePoint.y)
10248 || !std::isfinite(parsedBasePoint.z)
10249 || !std::isfinite(parsedExtPoint.x)
10250 || !std::isfinite(parsedExtPoint.y)
10251 || !std::isfinite(parsedExtPoint.z))
10252 return fail();
10253 DRW_DBG(" mline scale: ")DRW_dbg::dbg(" mline scale: "); DRW_DBG(parsedScale)DRW_dbg::dbg(parsedScale);
10254 DRW_DBG(" just: ")DRW_dbg::dbg(" just: "); DRW_DBG(static_cast<int>(parsedJustification))DRW_dbg::dbg(static_cast<int>(parsedJustification));
10255 DRW_DBG(" openClosed: ")DRW_dbg::dbg(" openClosed: "); DRW_DBG(parsedOpenClosed)DRW_dbg::dbg(parsedOpenClosed);
10256 DRW_DBG(" lines: ")DRW_dbg::dbg(" lines: "); DRW_DBG(static_cast<int>(parsedNumLines))DRW_dbg::dbg(static_cast<int>(parsedNumLines));
10257 DRW_DBG(" verts: ")DRW_dbg::dbg(" verts: "); DRW_DBG(parsedNumVerts)DRW_dbg::dbg(parsedNumVerts); DRW_DBG("\n")DRW_dbg::dbg("\n");
10258 if (!buf->isGood()
10259 || !isValidCount(static_cast<std::int32_t>(parsedNumVerts), kMaxDxfVertices)
10260 || !isValidCount(static_cast<std::int32_t>(parsedNumLines), kMaxDxfLines))
10261 return fail();
10262
10263 // Each vertex always contains three minimum-width 3BD values and one
10264 // minimum-width BS for each segment/fill parameter count. Check this
10265 // before allocating the per-vertex/per-line vector grid.
10266 const std::uint64_t minimumBitsPerVertex =
10267 18u + static_cast<std::uint64_t>(parsedNumLines) * 4u;
10268 std::uint64_t minimumVertexBits = 0;
10269 if (!dwgSafety::multiply(static_cast<std::uint64_t>(parsedNumVerts),
10270 minimumBitsPerVertex, minimumVertexBits)
10271 || !proxyEntityHasBits(*buf, bodyEndBit, minimumVertexBits))
10272 return fail();
10273
10274 // The per-vertex parameter lists are also capped by the DWG spec. Keep a
10275 // combined budget so a valid per-list maximum cannot multiply into an
10276 // unbounded number of vector allocations on hostile input.
10277 constexpr std::uint64_t kMaxDwgParameters = 1000000;
10278 std::uint64_t totalParameters = 0;
10279 std::vector<DRW_MLineVertex> parsedVertices;
10280 if (!DRW::reserve(parsedVertices, parsedNumVerts))
10281 return fail();
10282 for (int vi = 0; vi < parsedNumVerts; ++vi) {
10283 DRW_MLineVertex vtx;
10284 if (!readBoundedBitCoord(*buf, bodyEndBit, vtx.position)
10285 || !readBoundedBitCoord(*buf, bodyEndBit, vtx.vertexDir)
10286 || !readBoundedBitCoord(*buf, bodyEndBit, vtx.miterDir))
10287 return fail();
10288 if (!DRW::reserve(vtx.segParms, parsedNumLines)
10289 || !DRW::reserve(vtx.areaFillParms, parsedNumLines))
10290 return fail();
10291 if (!DRW::resize(vtx.segParms, parsedNumLines)
10292 || !DRW::resize(vtx.areaFillParms, parsedNumLines))
10293 return fail();
10294 for (int li = 0; li < parsedNumLines; ++li) {
10295 std::uint16_t nSeg = 0;
10296 if (!readBoundedBitShort(*buf, bodyEndBit, nSeg)
10297 || !proxyEntityHasBits(*buf, bodyEndBit,
10298 static_cast<std::uint64_t>(nSeg) * 2u))
10299 return fail();
10300 if (!buf->isGood()
10301 || !isValidCount(static_cast<std::int32_t>(nSeg),
10302 kMaxDxfParameters)
10303 || totalParameters > kMaxDwgParameters - nSeg)
10304 return fail();
10305 totalParameters += nSeg;
10306 if (!DRW::reserve(vtx.segParms[li], nSeg))
10307 return fail();
10308 for (int s = 0; s < nSeg; ++s) {
10309 double value = 0.0;
10310 if (!readBoundedBitDouble(*buf, bodyEndBit, value)
10311 || !std::isfinite(value))
10312 return fail();
10313 vtx.segParms[li].push_back(value);
10314 }
10315 std::uint16_t nFill = 0;
10316 if (!readBoundedBitShort(*buf, bodyEndBit, nFill)
10317 || !proxyEntityHasBits(*buf, bodyEndBit,
10318 static_cast<std::uint64_t>(nFill) * 2u))
10319 return fail();
10320 if (!buf->isGood()
10321 || !isValidCount(static_cast<std::int32_t>(nFill),
10322 kMaxDxfParameters)
10323 || totalParameters > kMaxDwgParameters - nFill)
10324 return fail();
10325 totalParameters += nFill;
10326 if (!DRW::reserve(vtx.areaFillParms[li], nFill))
10327 return fail();
10328 for (int f = 0; f < nFill; ++f) {
10329 double value = 0.0;
10330 if (!readBoundedBitDouble(*buf, bodyEndBit, value)
10331 || !std::isfinite(value))
10332 return fail();
10333 vtx.areaFillParms[li].push_back(value);
10334 }
10335 }
10336 const auto finite = [](const DRW_Coord& point) {
10337 return std::isfinite(point.x) && std::isfinite(point.y)
10338 && std::isfinite(point.z);
10339 };
10340 if (!finite(vtx.position) || !finite(vtx.vertexDir)
10341 || !finite(vtx.miterDir))
10342 return fail();
10343 parsedVertices.push_back(std::move(vtx));
10344 }
10345 if (!buf->isGood())
10346 return fail();
10347
10348 std::uint64_t handleEndBit = 0;
10349 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
10350 handleEndBit))
10351 return fail();
10352 dwgBuffer handleProbe = buf->forkIndependent();
10353 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
10354 handleEndBit))
10355 return fail();
10356 // MLINE has one extra handle in the handle stream after the standard
10357 // entity handles: the MLINESTYLE reference. Read if available — some
10358 // older files (R14) store the style name inline instead.
10359 std::uint32_t parsedStyleHandle = 0;
10360 if (version > DRW::AC1014
10361 && proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
10362 dwgHandle styleH;
10363 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
10364 styleH))
10365 return fail();
10366 if (!handleProbe.isGood())
10367 return fail();
10368 parsedStyleHandle = styleH.ref;
10369 DRW_DBG(" MLINE style handle: ")DRW_dbg::dbg(" MLINE style handle: ");
10370 DRW_DBGHL(styleH.code, styleH.size, styleH.ref)DRW_dbg::dbgHL(styleH.code, styleH.size, styleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
10371 }
10372 if (!handleProbe.isGood())
10373 return fail();
10374 *sourceBuf = handleProbe;
10375 scale = parsedScale;
10376 justification = parsedJustification;
10377 basePoint = parsedBasePoint;
10378 extPoint = parsedExtPoint;
10379 openClosed = parsedOpenClosed;
10380 numLines = parsedNumLines;
10381 numVerts = parsedNumVerts;
10382 styleHandle = parsedStyleHandle;
10383 vertlist = std::move(parsedVertices);
10384 return true;
10385}
10386
10387
10388// ----------------------------------------------------------------------------
10389// DRW_Underlay — UNDERLAY entity (PDFUNDERLAY/DGNUNDERLAY/DWFUNDERLAY).
10390// libreDWG UNDERLAYREFERENCE.spec field order:
10391// extrusion (BE) -> position (3BD) -> angle (BD radians) -> scale (BD x 3)
10392// -> flags (RC) -> contrast (RC) -> fade (RC) -> num_clip (BL)
10393// -> clip_verts (2RD × num_clip).
10394// Handle stream after standard entity handles: definition_id (H).
10395// ----------------------------------------------------------------------------
10396
10397bool DRW_Underlay::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
10398 switch (code) {
10399 case 340:
10400 definitionHandle = static_cast<std::uint32_t>(reader->getHandleString());
10401 break;
10402 case 10: position.x = reader->getDouble(); break;
10403 case 20: position.y = reader->getDouble(); break;
10404 case 30: position.z = reader->getDouble(); break;
10405 case 41: scale.x = reader->getDouble(); break;
10406 case 42: scale.y = reader->getDouble(); break;
10407 case 43: scale.z = reader->getDouble(); break;
10408 case 50: rotation = reader->getDouble(); break; // degrees in DXF
10409 case 210: extPoint.x = reader->getDouble(); break;
10410 case 220: extPoint.y = reader->getDouble(); break;
10411 case 230: extPoint.z = reader->getDouble(); break;
10412 case 280: {
10413 int value = 0;
10414 if (!readDxfIntInRange(reader, 0, 0xFF, value))
10415 return false;
10416 flags = static_cast<std::uint8_t>(value);
10417 break;
10418 }
10419 case 281: {
10420 int value = 0;
10421 if (!readDxfIntInRange(reader, 0, 100, value))
10422 return false;
10423 contrast = static_cast<std::uint8_t>(value);
10424 break;
10425 }
10426 case 282: {
10427 int value = 0;
10428 if (!readDxfIntInRange(reader, 0, 100, value))
10429 return false;
10430 fade = static_cast<std::uint8_t>(value);
10431 break;
10432 }
10433 case 11: {
10434 if (clipBoundary.size() >= kMaxClipVertices)
10435 return false;
10436 ++m_currentClipVertexIdx;
10437 if (m_currentClipVertexIdx >= static_cast<int>(clipBoundary.size())) {
10438 if (!DRW::resize(clipBoundary, m_currentClipVertexIdx + 1))
10439 return false;
10440 }
10441 clipBoundary[m_currentClipVertexIdx].x = reader->getDouble();
10442 break;
10443 }
10444 case 21:
10445 if (m_currentClipVertexIdx >= 0
10446 && m_currentClipVertexIdx < static_cast<int>(clipBoundary.size())) {
10447 clipBoundary[m_currentClipVertexIdx].y = reader->getDouble();
10448 }
10449 break;
10450 case 170: {
10451 const int count = reader->getInt32();
10452 if (!isValidCount(count, static_cast<std::int32_t>(kMaxClipVertices)))
10453 return false;
10454 m_inverseClipVertexCount = static_cast<std::uint32_t>(count);
10455 m_inverseClipCountSeen = true;
10456 if (!DRW::reserve(inverseClipBoundary, count))
10457 return false;
10458 break;
10459 }
10460 case 12: {
10461 if (inverseClipBoundary.size() >= kMaxClipVertices
10462 || (m_inverseClipCountSeen
10463 && inverseClipBoundary.size() >= m_inverseClipVertexCount))
10464 return false;
10465 ++m_currentInverseClipVertexIdx;
10466 if (m_currentInverseClipVertexIdx
10467 >= static_cast<int>(inverseClipBoundary.size())) {
10468 if (!DRW::resize(inverseClipBoundary,
10469 m_currentInverseClipVertexIdx + 1))
10470 return false;
10471 }
10472 inverseClipBoundary[m_currentInverseClipVertexIdx].x = reader->getDouble();
10473 break;
10474 }
10475 case 22:
10476 if (m_currentInverseClipVertexIdx >= 0
10477 && m_currentInverseClipVertexIdx
10478 < static_cast<int>(inverseClipBoundary.size())) {
10479 inverseClipBoundary[m_currentInverseClipVertexIdx].y = reader->getDouble();
10480 }
10481 break;
10482 default:
10483 return DRW_Entity::parseCode(code, reader);
10484 }
10485 return true;
10486}
10487
10488void DRW_Underlay::resetDwgState() {
10489 DRW_Entity::reset();
10490 // Entity carriers can be reused by callers. Clip payloads and their
10491 // parser counters belong to this record, not to the carrier lifetime.
10492 position = DRW_Coord{0.0, 0.0, 0.0};
10493 scale = DRW_Coord{1.0, 1.0, 1.0};
10494 rotation = 0.0;
10495 extPoint = DRW_Coord{0.0, 0.0, 1.0};
10496 flags = 2;
10497 contrast = 100;
10498 fade = 0;
10499 clipBoundary.clear();
10500 inverseClipBoundary.clear();
10501 m_currentClipVertexIdx = -1;
10502 m_currentInverseClipVertexIdx = -1;
10503 m_inverseClipVertexCount = 0;
10504 m_inverseClipCountSeen = false;
10505 definitionHandle = 0;
10506}
10507
10508bool DRW_Underlay::validatePayloadFields() const {
10509 const auto finite = [](const DRW_Coord& point) {
10510 return std::isfinite(point.x) && std::isfinite(point.y)
10511 && std::isfinite(point.z);
10512 };
10513 const auto finiteClipPoint = [](const DRW_Coord& point) {
10514 return std::isfinite(point.x) && std::isfinite(point.y);
10515 };
10516 return finite(position) && finite(scale) && finite(extPoint)
10517 && std::isfinite(rotation)
10518 && std::all_of(clipBoundary.cbegin(), clipBoundary.cend(),
10519 finiteClipPoint)
10520 && std::all_of(inverseClipBoundary.cbegin(),
10521 inverseClipBoundary.cend(), finiteClipPoint);
10522}
10523
10524bool DRW_Underlay::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
10525 resetDwgState();
10526 dwgBuffer *sourceBuf = buf;
10527 auto fail = [this, sourceBuf]() {
10528 if (sourceBuf != nullptr)
10529 sourceBuf->invalidate();
10530 resetDwgState();
10531 return false;
10532 };
10533 if (sourceBuf == nullptr)
10534 return fail();
10535
10536 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
10537 buf = &bodyProbe;
10538
10539 if (!DRW_Entity::parseDwgCommon(version, buf, bs)) return fail();
10540 DRW_DBG("\n***************************** parsing UNDERLAY ***************\n")DRW_dbg::dbg("\n***************************** parsing UNDERLAY ***************\n"
)
;
10541 const std::uint64_t bodyEndBit = dwgDataEndBit;
10542 DRW_Coord parsedExtPoint;
10543 DRW_Coord parsedPosition;
10544 DRW_Coord parsedScale;
10545 double parsedRotation = 0.0;
10546 std::uint8_t parsedFlags = 0;
10547 std::uint8_t parsedContrast = 0;
10548 std::uint8_t parsedFade = 0;
10549 if (!readBoundedExtrusion(*buf, bodyEndBit, false, parsedExtPoint)
10550 || !readBoundedBitCoord(*buf, bodyEndBit, parsedPosition)
10551 || !readBoundedBitDouble(*buf, bodyEndBit, parsedRotation)
10552 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale.x)
10553 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale.y)
10554 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale.z)
10555 || !readBoundedRawChar8(*buf, bodyEndBit, parsedFlags)
10556 || !readBoundedRawChar8(*buf, bodyEndBit, parsedContrast)
10557 || !readBoundedRawChar8(*buf, bodyEndBit, parsedFade))
10558 return fail();
10559 const auto finite = [](const DRW_Coord& point) {
10560 return std::isfinite(point.x) && std::isfinite(point.y)
10561 && std::isfinite(point.z);
10562 };
10563 if (!finite(parsedExtPoint) || !finite(parsedPosition)
10564 || !finite(parsedScale) || !std::isfinite(parsedRotation))
10565 return fail();
10566 std::uint32_t nClip = 0;
10567 DRW_DBG(" UNDERLAY pos: ")DRW_dbg::dbg(" UNDERLAY pos: "); DRW_DBG(parsedPosition.x)DRW_dbg::dbg(parsedPosition.x); DRW_DBG(",")DRW_dbg::dbg(",");
10568 DRW_DBG(parsedPosition.y)DRW_dbg::dbg(parsedPosition.y); DRW_DBG(" rot: ")DRW_dbg::dbg(" rot: "); DRW_DBG(parsedRotation)DRW_dbg::dbg(parsedRotation);
10569 DRW_DBG(" flags: ")DRW_dbg::dbg(" flags: "); DRW_DBGH(parsedFlags)DRW_dbg::dbgH(parsedFlags); DRW_DBG("\n")DRW_dbg::dbg("\n");
10570 if (!readTableBodyCount(version, buf, objSize, kMaxClipVertices, 128,
10571 nClip)
10572 || !proxyEntityHasBits(*buf, bodyEndBit,
10573 static_cast<std::uint64_t>(nClip) * 128u))
10574 return fail();
10575 std::vector<DRW_Coord> parsedClipBoundary;
10576 if (!DRW::reserve(parsedClipBoundary, static_cast<std::size_t>(nClip)))
10577 return fail();
10578 for (std::uint32_t i = 0; i < nClip; ++i) {
10579 DRW_Coord p;
10580 if (!readBoundedRawDouble(*buf, bodyEndBit, p.x)
10581 || !readBoundedRawDouble(*buf, bodyEndBit, p.y)
10582 || !std::isfinite(p.x) || !std::isfinite(p.y))
10583 return fail();
10584 p.z = 0.0;
10585 parsedClipBoundary.push_back(p);
10586 }
10587 std::vector<DRW_Coord> parsedInverseClipBoundary;
10588 if (version > DRW::AC1021 && (parsedFlags & 0x10) != 0) {
10589 std::uint16_t nInverse = 0;
10590 if (!readBoundedBitShort(*buf, bodyEndBit, nInverse)
10591 || nInverse > kMaxClipVertices
10592 || !proxyEntityHasBits(
10593 *buf, bodyEndBit,
10594 static_cast<std::uint64_t>(nInverse) * 128u)
10595 || !DRW::reserve(parsedInverseClipBoundary,
10596 static_cast<std::size_t>(nInverse)))
10597 return fail();
10598 for (std::uint16_t i = 0; i < nInverse; ++i) {
10599 DRW_Coord p;
10600 if (!readBoundedRawDouble(*buf, bodyEndBit, p.x)
10601 || !readBoundedRawDouble(*buf, bodyEndBit, p.y)
10602 || !std::isfinite(p.x) || !std::isfinite(p.y))
10603 return fail();
10604 p.z = 0.0;
10605 parsedInverseClipBoundary.push_back(p);
10606 }
10607 }
10608 if (!buf->isGood())
10609 return fail();
10610
10611 std::uint64_t handleEndBit = 0;
10612 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
10613 handleEndBit))
10614 return fail();
10615 dwgBuffer handleProbe = buf->forkIndependent();
10616 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
10617 handleEndBit))
10618 return fail();
10619 std::uint32_t parsedDefinitionHandle = 0;
10620 if (version > DRW::AC1014
10621 && proxyEntityHasBits(handleProbe, handleEndBit, 16)) {
10622 dwgHandle defH;
10623 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
10624 defH))
10625 return fail();
10626 if (!handleProbe.isGood())
10627 return fail();
10628 parsedDefinitionHandle = defH.ref;
10629 DRW_DBG(" UNDERLAY definitionHandle: ")DRW_dbg::dbg(" UNDERLAY definitionHandle: ");
10630 DRW_DBGHL(defH.code, defH.size, defH.ref)DRW_dbg::dbgHL(defH.code, defH.size, defH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
10631 }
10632 if (!handleProbe.isGood())
10633 return fail();
10634 *sourceBuf = handleProbe;
10635 extPoint = parsedExtPoint;
10636 position = parsedPosition;
10637 rotation = parsedRotation;
10638 scale = parsedScale;
10639 flags = parsedFlags;
10640 contrast = parsedContrast;
10641 fade = parsedFade;
10642 clipBoundary = std::move(parsedClipBoundary);
10643 inverseClipBoundary = std::move(parsedInverseClipBoundary);
10644 definitionHandle = parsedDefinitionHandle;
10645 return true;
10646}
10647
10648bool DRW_Underlay::encodeDwg(DRW::Version version, dwgBufferW *buf,
10649 std::uint32_t bs, dwgBufferW *strBuf,
10650 dwgBufferW *handleBuf) {
10651 (void)bs; (void)strBuf;
10652 if (!validatePayloadFields()
10653 || clipBoundary.size() > kMaxClipVertices
10654 || inverseClipBoundary.size() > kMaxClipVertices
10655 || (version <= DRW::AC1021 && !inverseClipBoundary.empty()))
10656 return false;
10657
10658 switch (kind) {
10659 case DGN:
10660 oType = kDwgClassNumDgn;
10661 break;
10662 case DWF:
10663 oType = kDwgClassNumDwf;
10664 break;
10665 case PDF:
10666 default:
10667 oType = kDwgClassNumPdf;
10668 break;
10669 }
10670 if (!encodeDwgCommon(version, buf))
10671 return false;
10672
10673 const bool hasInverseClip = !inverseClipBoundary.empty() || (flags & 0x10) != 0;
10674 const std::uint8_t wireFlags =
10675 hasInverseClip ? static_cast<std::uint8_t>(flags | 0x10) : flags;
10676
10677 buf->putExtrusion(extPoint, false);
10678 buf->put3BitDouble(position);
10679 buf->putBitDouble(rotation);
10680 buf->putBitDouble(scale.x);
10681 buf->putBitDouble(scale.y);
10682 buf->putBitDouble(scale.z);
10683 buf->putRawChar8(wireFlags);
10684 buf->putRawChar8(contrast);
10685 buf->putRawChar8(fade);
10686 buf->putBitLong(static_cast<std::int32_t>(clipBoundary.size()));
10687 for (const auto& vertex : clipBoundary) {
10688 buf->putRawDouble(vertex.x);
10689 buf->putRawDouble(vertex.y);
10690 }
10691 if (version > DRW::AC1021 && hasInverseClip) {
10692 buf->putBitShort(static_cast<std::uint16_t>(inverseClipBoundary.size()));
10693 for (const auto& vertex : inverseClipBoundary) {
10694 buf->putRawDouble(vertex.x);
10695 buf->putRawDouble(vertex.y);
10696 }
10697 }
10698
10699 if (!encodeDwgEntHandle(version, buf, handleBuf))
10700 return false;
10701 putNullableHardPointerHandle(handleBuf ? handleBuf : buf, definitionHandle);
10702 return true;
10703}
10704
10705
10706bool DRW_Text::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
10707 switch (code) {
10708 case 40:
10709 height = reader->getDouble();
10710 break;
10711 case 41:
10712 widthscale = reader->getDouble();
10713 break;
10714 case 50:
10715 angle = reader->getDouble();
10716 break;
10717 case 51:
10718 oblique = reader->getDouble();
10719 break;
10720 case 71:
10721 textgen = reader->getInt32();
10722 break;
10723 case 72:
10724 alignH = (HAlign)reader->getInt32();
10725 break;
10726 case 73:
10727 alignV = (VAlign)reader->getInt32();
10728 break;
10729 case 1:
10730 text = reader->getUtf8String();
10731 break;
10732 case 7:
10733 style = reader->getUtf8String();
10734 break;
10735 default:
10736 return DRW_Line::parseCode(code, reader);
10737 }
10738
10739 return true;
10740}
10741
10742void DRW_Text::resetDwgState() {
10743 DRW_Entity::reset();
10744 basePoint = DRW_Coord{0.0, 0.0, 0.0};
10745 secPoint = DRW_Coord{0.0, 0.0, 0.0};
10746 thickness = 0.0;
10747 extPoint = DRW_Coord{0.0, 0.0, 1.0};
10748 xAxisAngle = 0.0;
10749 height = 0.0;
10750 text.clear();
10751 angle = 0.0;
10752 widthscale = 1.0;
10753 oblique = 0.0;
10754 style = "STANDARD";
10755 textgen = 0;
10756 alignH = HLeft;
10757 alignV = VBaseLine;
10758 styleH = dwgHandle{};
10759}
10760
10761bool DRW_Text::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
10762 resetDwgState();
10763 dwgBuffer *sourceBuf = buf;
10764 auto fail = [this, sourceBuf]() {
10765 if (sourceBuf != nullptr)
10766 sourceBuf->invalidate();
10767 resetDwgState();
10768 return false;
10769 };
10770 if (sourceBuf == nullptr)
10771 return fail();
10772
10773 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
10774 buf = &bodyProbe;
10775 dwgBuffer sBuff = buf->forkIndependent();
10776 dwgBuffer *sBuf = buf;
10777 if (version > DRW::AC1018) {//2007+
10778 sBuf = &sBuff; //separate buffer for strings
10779 }
10780 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
10781 if (!ret)
10782 return fail();
10783 DRW_DBG("\n***************************** parsing text *********************************************\n")DRW_dbg::dbg("\n***************************** parsing text *********************************************\n"
)
;
10784 const std::uint64_t bodyEndBit = dwgDataEndBit;
10785
10786 // DataFlags RC Used to determine presence of subsequent data, set to 0xFF for R14-
10787 std::uint8_t data_flags = 0x00;
10788 if (version > DRW::AC1014) {//2000+
10789 if (!readBoundedRawChar8(*buf, bodyEndBit, data_flags))
10790 return fail(); /* DataFlags RC Used to determine presence of subsequent data */
10791 DRW_DBG("data_flags: ")DRW_dbg::dbg("data_flags: "); DRW_DBG(data_flags)DRW_dbg::dbg(data_flags); DRW_DBG("\n")DRW_dbg::dbg("\n");
10792 if ( !(data_flags & 0x01) ) { /* Elevation RD --- present if !(DataFlags & 0x01) */
10793 if (!readBoundedRawDouble(*buf, bodyEndBit, basePoint.z))
10794 return fail();
10795 }
10796 } else {//14-
10797 if (!readBoundedBitDouble(*buf, bodyEndBit, basePoint.z))
10798 return fail(); /* Elevation BD --- */
10799 }
10800 if (!readBoundedRawDouble(*buf, bodyEndBit, basePoint.x)
10801 || !readBoundedRawDouble(*buf, bodyEndBit, basePoint.y))
10802 return fail(); /* Insertion pt 2RD 10 */
10803 DRW_DBG("Insert point: ")DRW_dbg::dbg("Insert point: "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
10804 if (version > DRW::AC1014) {//2000+
10805 if ( !(data_flags & 0x02) ) { /* Alignment pt 2DD 11 present if !(DataFlags & 0x02), use 10 & 20 values for 2 default values.*/
10806 if (!readBoundedDefaultDouble(*buf, bodyEndBit, basePoint.x,
10807 secPoint.x)
10808 || !readBoundedDefaultDouble(*buf, bodyEndBit, basePoint.y,
10809 secPoint.y))
10810 return fail();
10811 } else {
10812 secPoint = basePoint;
10813 }
10814 } else {//14-
10815 if (!readBoundedRawDouble(*buf, bodyEndBit, secPoint.x)
10816 || !readBoundedRawDouble(*buf, bodyEndBit, secPoint.y))
10817 return fail(); /* Alignment pt 2RD 11 */
10818 }
10819 secPoint.z = basePoint.z;
10820 DRW_DBG("Alignment: ")DRW_dbg::dbg("Alignment: "); DRW_DBGPT(secPoint.x, secPoint.y, basePoint.z)DRW_dbg::dbgPT(secPoint.x, secPoint.y, basePoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
10821 if (!readBoundedExtrusion(*buf, bodyEndBit, version > DRW::AC1014,
10822 extPoint))
10823 return fail();
10824 DRW_DBG("Extrusion: ")DRW_dbg::dbg("Extrusion: "); DRW_DBGPT(extPoint.x, extPoint.y, extPoint.z)DRW_dbg::dbgPT(extPoint.x, extPoint.y, extPoint.z); DRW_DBG("\n")DRW_dbg::dbg("\n");
10825 if (!readBoundedThickness(*buf, bodyEndBit, version > DRW::AC1014,
10826 thickness))
10827 return fail(); /* Thickness BD 39 */
10828
10829 if (version > DRW::AC1014) {//2000+
10830 if ( !(data_flags & 0x04) ) { /* Oblique ang RD 51 present if !(DataFlags & 0x04) */
10831 if (!readBoundedRawDouble(*buf, bodyEndBit, oblique))
10832 return fail();
10833 }
10834 if ( !(data_flags & 0x08) ) { /* Rotation ang RD 50 present if !(DataFlags & 0x08) */
10835 if (!readBoundedRawDouble(*buf, bodyEndBit, angle))
10836 return fail();
10837 }
10838 if (!readBoundedRawDouble(*buf, bodyEndBit, height))
10839 return fail(); /* Height RD 40 */
10840 if ( !(data_flags & 0x10) ) { /* Width factor RD 41 present if !(DataFlags & 0x10) */
10841 if (!readBoundedRawDouble(*buf, bodyEndBit, widthscale))
10842 return fail();
10843 }
10844 } else {//14-
10845 if (!readBoundedBitDouble(*buf, bodyEndBit, oblique)
10846 || !readBoundedBitDouble(*buf, bodyEndBit, angle)
10847 || !readBoundedBitDouble(*buf, bodyEndBit, height)
10848 || !readBoundedBitDouble(*buf, bodyEndBit, widthscale))
10849 return fail(); /* Oblique/rotation/height/width BD */
10850 }
10851 angle *= ARAD57.29577951308232;
10852 if (!std::isfinite(basePoint.x) || !std::isfinite(basePoint.y)
10853 || !std::isfinite(basePoint.z) || !std::isfinite(secPoint.x)
10854 || !std::isfinite(secPoint.y) || !std::isfinite(secPoint.z)
10855 || !std::isfinite(extPoint.x) || !std::isfinite(extPoint.y)
10856 || !std::isfinite(extPoint.z) || !std::isfinite(thickness)
10857 || !std::isfinite(oblique) || !std::isfinite(angle)
10858 || !std::isfinite(height) || !std::isfinite(widthscale))
10859 return fail();
10860 DRW_DBG("thickness: ")DRW_dbg::dbg("thickness: "); DRW_DBG(thickness)DRW_dbg::dbg(thickness); DRW_DBG(", Oblique ang: ")DRW_dbg::dbg(", Oblique ang: "); DRW_DBG(oblique)DRW_dbg::dbg(oblique); DRW_DBG(", Width: ")DRW_dbg::dbg(", Width: ");
10861 DRW_DBG(widthscale)DRW_dbg::dbg(widthscale); DRW_DBG(", Rotation: ")DRW_dbg::dbg(", Rotation: "); DRW_DBG(angle)DRW_dbg::dbg(angle); DRW_DBG(", height: ")DRW_dbg::dbg(", height: "); DRW_DBG(height)DRW_dbg::dbg(height); DRW_DBG("\n")DRW_dbg::dbg("\n");
10862 std::uint64_t stringEndBit = bodyEndBit;
10863 if (version > DRW::AC1021) {
10864 std::uint64_t ignoredBodyEndBit = 0;
10865 if (!entityBodyDataEndBit(*sBuf, version, objSize,
10866 ignoredBodyEndBit, &stringEndBit))
10867 return fail();
10868 }
10869 UTF8STRINGstd::string parsedText;
10870 if (version > DRW::AC1018
10871 && currentDwgBit(sBuf) >= stringEndBit) {
10872 parsedText.clear();
10873 } else if (!readBoundedVariableText(*sBuf, stringEndBit, version,
10874 parsedText)) {
10875 return fail();
10876 }
10877 DRW_DBG("text string: ")DRW_dbg::dbg("text string: "); DRW_DBG(parsedText.c_str())DRW_dbg::dbg(parsedText.c_str());DRW_DBG("\n")DRW_dbg::dbg("\n");
10878 //textgen, alignH, alignV always present in R14-, data_flags set in initialisation
10879 if ( !(data_flags & 0x20) ) { /* Generation BS 71 present if !(DataFlags & 0x20) */
10880 std::uint16_t parsedTextgen = 0;
10881 if (!readBoundedBitShort(*buf, bodyEndBit, parsedTextgen))
10882 return fail();
10883 textgen = parsedTextgen;
10884 DRW_DBG("textgen: ")DRW_dbg::dbg("textgen: "); DRW_DBG(textgen)DRW_dbg::dbg(textgen);
10885 }
10886 if ( !(data_flags & 0x40) ) { /* Horiz align. BS 72 present if !(DataFlags & 0x40) */
10887 std::uint16_t parsedAlignH = 0;
10888 if (!readBoundedBitShort(*buf, bodyEndBit, parsedAlignH))
10889 return fail();
10890 alignH = static_cast<HAlign>(parsedAlignH);
10891 DRW_DBG(", alignH: ")DRW_dbg::dbg(", alignH: "); DRW_DBG(alignH)DRW_dbg::dbg(alignH);
10892 }
10893 if ( !(data_flags & 0x80) ) { /* Vert align. BS 73 present if !(DataFlags & 0x80) */
10894 std::uint16_t parsedAlignV = 0;
10895 if (!readBoundedBitShort(*buf, bodyEndBit, parsedAlignV))
10896 return fail();
10897 alignV = static_cast<VAlign>(parsedAlignV);
10898 DRW_DBG(", alignV: ")DRW_dbg::dbg(", alignV: "); DRW_DBG(alignV)DRW_dbg::dbg(alignV);
10899 }
10900 DRW_DBG("\n")DRW_dbg::dbg("\n");
10901
10902 if (!buf->isGood() || !sBuf->isGood()
10903 || currentDwgBit(buf) > bodyEndBit
10904 || currentDwgBit(sBuf) > stringEndBit)
10905 return fail();
10906
10907 /* Common Entity Handle Data */
10908 std::uint64_t handleEndBit = 0;
10909 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
10910 handleEndBit))
10911 return fail();
10912 dwgBuffer handleProbe = buf->forkIndependent();
10913 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
10914 handleEndBit);
10915 if (!ret)
10916 return fail();
10917
10918 dwgHandle parsedStyleH;
10919 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
10920 parsedStyleH)
10921 || !handleProbe.isGood() || !sBuf->isGood())
10922 return fail();
10923
10924 *sourceBuf = handleProbe;
10925 text = std::move(parsedText);
10926 styleH = parsedStyleH;
10927 DRW_DBG("text style Handle: ")DRW_dbg::dbg("text style Handle: "); DRW_DBGHL(styleH.code, styleH.size, styleH.ref)DRW_dbg::dbgHL(styleH.code, styleH.size, styleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
10928 /* CRC X --- */
10929 return true;
10930}
10931
10932// ---------------------------------------------------------------------------
10933// RTEXT (RText, Express Tools) — read-only, mapped onto DRW_Text.
10934// ---------------------------------------------------------------------------
10935bool DRW_RText::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
10936 // RTEXT's DXF layout is a TEXT subset (1 text, 7 style, 10/20/30 insertion,
10937 // 40 height, 50 rotation deg, 210/220/230 extrusion) plus a flags long (70)
10938 // that plain TEXT does not carry.
10939 if (70 == code) {
10940 m_rTextFlags = reader->getInt32();
10941 return true;
10942 }
10943 return DRW_Text::parseCode(code, reader);
10944}
10945
10946bool DRW_RText::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
10947 resetDwgState();
10948 m_rTextFlags = 0;
10949 dwgBuffer *sourceBuf = buf;
10950 auto fail = [this, sourceBuf]() {
10951 if (sourceBuf != nullptr)
10952 sourceBuf->invalidate();
10953 resetDwgState();
10954 m_rTextFlags = 0;
10955 return false;
10956 };
10957 if (sourceBuf == nullptr)
10958 return fail();
10959
10960 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
10961 buf = &bodyProbe;
10962 dwgBuffer sBuff = buf->forkIndependent();
10963 dwgBuffer *sBuf = buf;
10964 if (version > DRW::AC1018) // 2007+ strings live in a separate stream
10965 sBuf = &sBuff;
10966 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
10967 if (!ret)
10968 return fail();
10969 DRW_DBG("\n***************************** parsing rtext ********************************************\n")DRW_dbg::dbg("\n***************************** parsing rtext ********************************************\n"
)
;
10970
10971 const std::uint64_t bodyEndBit = dwgDataEndBit;
10972 DRW_Coord parsedBasePoint;
10973 DRW_Coord parsedExtrusion;
10974 double parsedAngle = 0.0;
10975 double parsedHeight = 0.0;
10976 std::uint16_t parsedFlags = 0;
10977 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
10978 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtrusion)
10979 || !readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
10980 || !readBoundedBitDouble(*buf, bodyEndBit, parsedHeight)
10981 || !readBoundedBitShort(*buf, bodyEndBit, parsedFlags)
10982 || !std::isfinite(parsedBasePoint.x)
10983 || !std::isfinite(parsedBasePoint.y)
10984 || !std::isfinite(parsedBasePoint.z)
10985 || !std::isfinite(parsedExtrusion.x)
10986 || !std::isfinite(parsedExtrusion.y)
10987 || !std::isfinite(parsedExtrusion.z)
10988 || !std::isfinite(parsedAngle)
10989 || !std::isfinite(parsedHeight))
10990 return fail();
10991 basePoint = parsedBasePoint; // insertion 3BD
10992 secPoint = parsedBasePoint; // no separate alignment point
10993 extPoint = parsedExtrusion; // extrusion 3BD
10994 angle = parsedAngle * ARAD57.29577951308232; // rotation BD (radians) -> degrees
10995 height = parsedHeight; // height BD
10996 m_rTextFlags = parsedFlags; // flags BS
10997 if (!std::isfinite(angle))
10998 return fail();
10999 std::uint64_t stringEndBit = bodyEndBit;
11000 if (version > DRW::AC1021) {
11001 std::uint64_t ignoredBodyEndBit = 0;
11002 if (!entityBodyDataEndBit(*sBuf, version, objSize,
11003 ignoredBodyEndBit, &stringEndBit))
11004 return fail();
11005 }
11006 UTF8STRINGstd::string parsedText;
11007 if (version > DRW::AC1018
11008 && currentDwgBit(sBuf) >= stringEndBit) {
11009 parsedText.clear();
11010 } else if (!readBoundedVariableText(*sBuf, stringEndBit, version,
11011 parsedText)) {
11012 return fail();
11013 }
11014 DRW_DBG("rtext string: ")DRW_dbg::dbg("rtext string: "); DRW_DBG(parsedText.c_str())DRW_dbg::dbg(parsedText.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
11015
11016 if (!buf->isGood() || !sBuf->isGood()
11017 || currentDwgBit(buf) > bodyEndBit
11018 || currentDwgBit(sBuf) > stringEndBit)
11019 return fail();
11020
11021 std::uint64_t handleEndBit = 0;
11022 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
11023 handleEndBit))
11024 return fail();
11025 dwgBuffer handleProbe = buf->forkIndependent();
11026 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
11027 handleEndBit);
11028 if (!ret)
11029 return fail();
11030 dwgHandle parsedStyleH;
11031 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
11032 parsedStyleH)
11033 || !handleProbe.isGood() || !sBuf->isGood())
11034 return fail();
11035
11036 *sourceBuf = handleProbe;
11037 text = std::move(parsedText);
11038 styleH = parsedStyleH;
11039 return true;
11040}
11041
11042// ---------------------------------------------------------------------------
11043// ARCALIGNEDTEXT (AcDbArcAlignedText, Express Tools) — read-only, mapped onto
11044// DRW_Text as a 2D approximation (text at the arc mid-point, tangent baseline).
11045// ---------------------------------------------------------------------------
11046bool DRW_RText::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
11047 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
11048 (void)bs;
11049 oType = kDwgClassNum;
11050 if (!encodeDwgCommon(version, buf)) return false;
11051
11052 buf->put3BitDouble(basePoint);
11053 buf->put3BitDouble(extPoint);
11054 buf->putBitDouble(angle / ARAD57.29577951308232);
11055 buf->putBitDouble(height);
11056 buf->putBitShort(bitShortFromInt(m_rTextFlags));
11057 (strBuf ? strBuf : buf)->putVariableText(version, text);
11058
11059 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
11060 putHardPointerHandle(handleBuf ? handleBuf : buf,
11061 (styleH.ref == 0) ? 0x13 : styleH.ref);
11062 return true;
11063}
11064
11065void DRW_ArcAlignedText::applyArcApproximation(){
11066 const double mid = 0.5 * (m_startAngle + m_endAngle);
11067 basePoint.x = m_center.x + m_radius * std::cos(mid);
11068 basePoint.y = m_center.y + m_radius * std::sin(mid);
11069 basePoint.z = m_center.z;
11070 secPoint = basePoint;
11071 // Baseline tangent to the arc at the mid-point; angle stored in degrees to
11072 // match DRW_Text (which the DWG path fills via `angle *= ARAD`).
11073 angle = (mid + M_PI_21.57079632679489661923) * ARAD57.29577951308232;
11074 // Height from the text-size D2T string when parseable, else a fraction of
11075 // the radius so the approximation is at least visible.
11076 double h = 0.0;
11077 try { h = std::stod(m_textSize); } catch (...) { h = 0.0; }
11078 if (h > 0.0)
11079 height = h;
11080 else if (height <= 0.0)
11081 height = 0.1 * m_radius;
11082}
11083
11084// Format a D2T (double-to-text) field the way the ARCALIGNEDTEXT model stores
11085// it: the DWG body carries these as text ("2.5", "1", "0"), while the DXF path
11086// reads them as doubles (group codes 41-46 fall in the double range, so the
11087// reader populates doubleData and leaves strData stale — getString() is unsafe
11088// here). %g reproduces the same compact textual form.
11089static std::string arcAlignedD2T(double v){
11090 char buf[32];
11091 std::snprintf(buf, sizeof(buf), "%g", v);
11092 return std::string(buf);
11093}
11094
11095static std::string arcAlignedStringOrDefault(const UTF8STRINGstd::string& value,
11096 const std::string& fallback) {
11097 return value.empty() ? fallback : value;
11098}
11099
11100bool DRW_ArcAlignedText::encodeDwg(DRW::Version version, dwgBufferW *buf,
11101 std::uint32_t bs, dwgBufferW *strBuf,
11102 dwgBufferW *handleBuf) {
11103 (void)bs;
11104 oType = kDwgClassNum;
11105 if (!encodeDwgCommon(version, buf)) return false;
11106
11107 dwgBufferW *sb = strBuf ? strBuf : buf;
11108 sb->putVariableText(version, arcAlignedStringOrDefault(
11109 m_textSize, arcAlignedD2T(height > 0.0 ? height : 0.0)));
11110 sb->putVariableText(version, arcAlignedStringOrDefault(
11111 m_xScale, arcAlignedD2T(widthscale > 0.0 ? widthscale : 1.0)));
11112 sb->putVariableText(version, arcAlignedStringOrDefault(m_charSpacing, "1"));
11113 sb->putVariableText(version, style.empty() ? "Standard" : style);
11114 sb->putVariableText(version, m_fontName);
11115 sb->putVariableText(version, m_bigFontName);
11116 sb->putVariableText(version, text);
11117 sb->putVariableText(version, arcAlignedStringOrDefault(m_offsetFromArc, "0"));
11118 sb->putVariableText(version, arcAlignedStringOrDefault(m_rightOffset, "0"));
11119 sb->putVariableText(version, arcAlignedStringOrDefault(m_leftOffset, "0"));
11120
11121 buf->put3BitDouble(m_center);
11122 buf->putBitDouble(m_radius);
11123 buf->putBitDouble(m_startAngle);
11124 buf->putBitDouble(m_endAngle);
11125 buf->put3BitDouble(extPoint);
11126 buf->putBitLong(static_cast<std::int32_t>(m_rawColor));
11127 buf->putBitShort(bitShortFromInt(m_characterSet));
11128 buf->putBitShort(bitShortFromInt(m_pitchAndFamily));
11129 buf->putBitShort(bitShortFromInt(m_isShx));
11130 buf->putBitShort(bitShortFromInt(m_isBold));
11131 buf->putBitShort(bitShortFromInt(m_isItalic));
11132 buf->putBitShort(bitShortFromInt(m_isUnderlined));
11133 buf->putBitShort(bitShortFromInt(m_alignment));
11134 buf->putBitShort(bitShortFromInt(m_isReverse));
11135 buf->putBitShort(bitShortFromInt(m_wizardFlag));
11136 buf->putBitShort(bitShortFromInt(m_textPosition));
11137 buf->putBitShort(bitShortFromInt(m_textDirection));
11138
11139 if (version <= DRW::AC1018)
11140 putNullableHardPointerHandle(buf, m_arcHandle);
11141 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
11142 if (version > DRW::AC1018)
11143 putNullableHardPointerHandle(handleBuf ? handleBuf : buf, m_arcHandle);
11144 return true;
11145}
11146
11147bool DRW_ArcAlignedText::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
11148 // ARCALIGNEDTEXT repurposes several TEXT codes (2/10/40/41/50/51/70…), so it
11149 // must not delegate those to DRW_Text; unknown codes fall through to the
11150 // AcDbEntity common parser. Angles (50/51) are DXF degrees -> radians.
11151 switch (code) {
11152 case 1: text = reader->getUtf8String(); break;
11153 case 2: m_fontName = reader->getUtf8String(); break;
11154 case 3: m_bigFontName = reader->getUtf8String(); break;
11155 case 7: style = reader->getUtf8String(); break;
11156 case 10: m_center.x = reader->getDouble(); break;
11157 case 20: m_center.y = reader->getDouble(); break;
11158 case 30: m_center.z = reader->getDouble(); break;
11159 case 40: m_radius = reader->getDouble(); break;
11160 case 41: m_xScale = arcAlignedD2T(reader->getDouble()); break;
11161 case 42: m_textSize = arcAlignedD2T(reader->getDouble()); break;
11162 case 43: m_charSpacing = arcAlignedD2T(reader->getDouble()); break;
11163 case 44: m_offsetFromArc = arcAlignedD2T(reader->getDouble()); break;
11164 case 45: m_rightOffset = arcAlignedD2T(reader->getDouble()); break;
11165 case 46: m_leftOffset = arcAlignedD2T(reader->getDouble()); break;
11166 case 50: m_startAngle = reader->getDouble() / ARAD57.29577951308232; break;
11167 case 51: m_endAngle = reader->getDouble() / ARAD57.29577951308232; break;
11168 case 70: m_isReverse = reader->getInt32(); break;
11169 case 71: m_textDirection = reader->getInt32(); break;
11170 case 72: m_alignment = reader->getInt32(); break;
11171 case 73: m_textPosition = reader->getInt32(); break;
11172 case 74: m_isBold = reader->getInt32(); break;
11173 case 75: m_isItalic = reader->getInt32(); break;
11174 case 76: m_isUnderlined = reader->getInt32(); break;
11175 case 77: m_characterSet = reader->getInt32(); break;
11176 case 78: m_pitchAndFamily = reader->getInt32(); break;
11177 case 79: m_isShx = reader->getInt32(); break;
11178 case 90: m_rawColor = reader->getInt32(); break;
11179 case 210: extPoint.x = reader->getDouble(); break;
11180 case 220: extPoint.y = reader->getDouble(); break;
11181 case 230: extPoint.z = reader->getDouble(); break;
11182 case 280: m_wizardFlag = reader->getInt32(); break;
11183 default: return DRW_Entity::parseCode(code, reader);
11184 }
11185 return true;
11186}
11187
11188void DRW_ArcAlignedText::resetDwgState() {
11189 DRW_Text::resetDwgState();
11190 m_center = DRW_Coord{0.0, 0.0, 0.0};
11191 m_radius = 0.0;
11192 m_startAngle = 0.0;
11193 m_endAngle = 0.0;
11194 m_textSize.clear();
11195 m_xScale.clear();
11196 m_charSpacing.clear();
11197 m_offsetFromArc.clear();
11198 m_rightOffset.clear();
11199 m_leftOffset.clear();
11200 m_fontName.clear();
11201 m_bigFontName.clear();
11202 m_rawColor = 0;
11203 m_characterSet = 0;
11204 m_pitchAndFamily = 0;
11205 m_isShx = 0;
11206 m_isBold = 0;
11207 m_isItalic = 0;
11208 m_isUnderlined = 0;
11209 m_alignment = 0;
11210 m_isReverse = 0;
11211 m_wizardFlag = 0;
11212 m_textPosition = 0;
11213 m_textDirection = 0;
11214 m_arcHandle = 0;
11215}
11216
11217bool DRW_ArcAlignedText::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
11218 // The arc reference is an optional trailer whose position depends on the
11219 // DWG version. Never carry it across a reused or truncated record.
11220 resetDwgState();
11221 dwgBuffer *sourceBuf = buf;
11222 auto fail = [this, sourceBuf]() {
11223 if (sourceBuf != nullptr)
11224 sourceBuf->invalidate();
11225 resetDwgState();
11226 return false;
11227 };
11228 if (sourceBuf == nullptr)
11229 return fail();
11230
11231 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
11232 buf = &bodyProbe;
11233
11234 dwgBuffer sBuff = buf->forkIndependent();
11235 dwgBuffer *sBuf = buf;
11236 if (version > DRW::AC1018) // 2007+ strings live in a separate stream
11237 sBuf = &sBuff;
11238 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
11239 if (!ret)
11240 return fail();
11241 DRW_DBG("\n***************************** parsing arcalignedtext **********************************\n")DRW_dbg::dbg("\n***************************** parsing arcalignedtext **********************************\n"
)
;
11242
11243 const std::uint64_t bodyEndBit = dwgDataEndBit;
11244 std::uint64_t stringEndBit = bodyEndBit;
11245 if (version > DRW::AC1021) {
11246 std::uint64_t ignoredBodyEndBit = 0;
11247 if (!entityBodyDataEndBit(*sBuf, version, objSize,
11248 ignoredBodyEndBit, &stringEndBit))
11249 return fail();
11250 }
11251
11252 UTF8STRINGstd::string parsedTextSize;
11253 UTF8STRINGstd::string parsedXScale;
11254 UTF8STRINGstd::string parsedCharSpacing;
11255 UTF8STRINGstd::string parsedStyle;
11256 UTF8STRINGstd::string parsedFontName;
11257 UTF8STRINGstd::string parsedBigFontName;
11258 UTF8STRINGstd::string parsedText;
11259 UTF8STRINGstd::string parsedOffsetFromArc;
11260 UTF8STRINGstd::string parsedRightOffset;
11261 UTF8STRINGstd::string parsedLeftOffset;
11262 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
11263 parsedTextSize)
11264 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11265 parsedXScale)
11266 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11267 parsedCharSpacing)
11268 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11269 parsedStyle)
11270 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11271 parsedFontName)
11272 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11273 parsedBigFontName)
11274 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11275 parsedText)
11276 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11277 parsedOffsetFromArc)
11278 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11279 parsedRightOffset)
11280 || !readBoundedVariableText(*sBuf, stringEndBit, version,
11281 parsedLeftOffset))
11282 return fail();
11283
11284 DRW_Coord parsedCenter;
11285 double parsedRadius = 0.0;
11286 double parsedStartAngle = 0.0;
11287 double parsedEndAngle = 0.0;
11288 DRW_Coord parsedExtPoint;
11289 std::int32_t parsedRawColor = 0;
11290 std::uint16_t parsedCharacterSet = 0;
11291 std::uint16_t parsedPitchAndFamily = 0;
11292 std::uint16_t parsedIsShx = 0;
11293 std::uint16_t parsedIsBold = 0;
11294 std::uint16_t parsedIsItalic = 0;
11295 std::uint16_t parsedIsUnderlined = 0;
11296 std::uint16_t parsedAlignment = 0;
11297 std::uint16_t parsedIsReverse = 0;
11298 std::uint16_t parsedWizardFlag = 0;
11299 std::uint16_t parsedTextPosition = 0;
11300 std::uint16_t parsedTextDirection = 0;
11301 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
11302 || !readBoundedBitDouble(*buf, bodyEndBit, parsedRadius)
11303 || !readBoundedBitDouble(*buf, bodyEndBit, parsedStartAngle)
11304 || !readBoundedBitDouble(*buf, bodyEndBit, parsedEndAngle)
11305 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtPoint)
11306 || !readBoundedBitLong(*buf, bodyEndBit, parsedRawColor)
11307 || !readBoundedBitShort(*buf, bodyEndBit, parsedCharacterSet)
11308 || !readBoundedBitShort(*buf, bodyEndBit, parsedPitchAndFamily)
11309 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsShx)
11310 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsBold)
11311 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsItalic)
11312 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsUnderlined)
11313 || !readBoundedBitShort(*buf, bodyEndBit, parsedAlignment)
11314 || !readBoundedBitShort(*buf, bodyEndBit, parsedIsReverse)
11315 || !readBoundedBitShort(*buf, bodyEndBit, parsedWizardFlag)
11316 || !readBoundedBitShort(*buf, bodyEndBit, parsedTextPosition)
11317 || !readBoundedBitShort(*buf, bodyEndBit, parsedTextDirection))
11318 return fail();
11319 DRW_DBG("arcalignedtext string: ")DRW_dbg::dbg("arcalignedtext string: "); DRW_DBG(parsedText.c_str())DRW_dbg::dbg(parsedText.c_str());
11320 DRW_DBG("\n")DRW_dbg::dbg("\n");
11321
11322 if (!buf->isGood() || !sBuf->isGood()
11323 || currentDwgBit(buf) > bodyEndBit
11324 || currentDwgBit(sBuf) > stringEndBit
11325 || !std::isfinite(parsedCenter.x) || !std::isfinite(parsedCenter.y)
11326 || !std::isfinite(parsedCenter.z) || !std::isfinite(parsedRadius)
11327 || parsedRadius < 0.0 || !std::isfinite(parsedStartAngle)
11328 || !std::isfinite(parsedEndAngle) || !std::isfinite(parsedExtPoint.x)
11329 || !std::isfinite(parsedExtPoint.y)
11330 || !std::isfinite(parsedExtPoint.z))
11331 return fail();
11332
11333 std::uint64_t handleEndBit = 0;
11334 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
11335 handleEndBit))
11336 return fail();
11337 // R2004- keeps the arc handle before the common handle stream; R2007+ after.
11338 dwgBuffer handleProbe = buf->forkIndependent();
11339 std::uint32_t parsedArcHandle = 0;
11340 if (version <= DRW::AC1018
11341 && proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
11342 dwgHandle arcHandle;
11343 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
11344 arcHandle))
11345 return fail();
11346 parsedArcHandle = arcHandle.ref;
11347 }
11348
11349 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
11350 handleEndBit);
11351 if (!ret)
11352 return fail();
11353 if (version > DRW::AC1018
11354 && proxyEntityHasBits(handleProbe, handleEndBit, 8)) {
11355 dwgHandle arcHandle;
11356 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
11357 arcHandle))
11358 return fail();
11359 parsedArcHandle = arcHandle.ref;
11360 }
11361 if (!handleProbe.isGood())
11362 return fail();
11363
11364 *sourceBuf = handleProbe;
11365 m_textSize = std::move(parsedTextSize);
11366 m_xScale = std::move(parsedXScale);
11367 m_charSpacing = std::move(parsedCharSpacing);
11368 style = std::move(parsedStyle);
11369 m_fontName = std::move(parsedFontName);
11370 m_bigFontName = std::move(parsedBigFontName);
11371 text = std::move(parsedText);
11372 m_offsetFromArc = std::move(parsedOffsetFromArc);
11373 m_rightOffset = std::move(parsedRightOffset);
11374 m_leftOffset = std::move(parsedLeftOffset);
11375 m_center = parsedCenter;
11376 m_radius = parsedRadius;
11377 m_startAngle = parsedStartAngle;
11378 m_endAngle = parsedEndAngle;
11379 extPoint = parsedExtPoint;
11380 m_rawColor = parsedRawColor;
11381 m_characterSet = parsedCharacterSet;
11382 m_pitchAndFamily = parsedPitchAndFamily;
11383 m_isShx = parsedIsShx;
11384 m_isBold = parsedIsBold;
11385 m_isItalic = parsedIsItalic;
11386 m_isUnderlined = parsedIsUnderlined;
11387 m_alignment = parsedAlignment;
11388 m_isReverse = parsedIsReverse;
11389 m_wizardFlag = parsedWizardFlag;
11390 m_textPosition = parsedTextPosition;
11391 m_textDirection = parsedTextDirection;
11392 m_arcHandle = parsedArcHandle;
11393 applyArcApproximation();
11394 return true;
11395}
11396
11397// Out-of-line special members: required because mtext is a unique_ptr<DRW_MText>
11398// declared with a forward-declared element type in the header.
11399DRW_Attrib::~DRW_Attrib() = default;
11400DRW_Attrib::DRW_Attrib(const DRW_Attrib& o)
11401 : DRW_Text(o), tag(o.tag), attribFlags(o.attribFlags),
11402 m_fieldLength(o.m_fieldLength),
11403 lockPosition(o.lockPosition), attVersion(o.attVersion),
11404 m_attributeType(o.m_attributeType),
11405 keepDuplicateRecords(o.keepDuplicateRecords),
11406 mtext(o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr) {}
11407DRW_Attrib& DRW_Attrib::operator=(const DRW_Attrib& o) {
11408 if (this != &o) {
11409 DRW_Text::operator=(o);
11410 tag = o.tag;
11411 attribFlags = o.attribFlags;
11412 m_fieldLength = o.m_fieldLength;
11413 lockPosition = o.lockPosition;
11414 attVersion = o.attVersion;
11415 m_attributeType = o.m_attributeType;
11416 keepDuplicateRecords = o.keepDuplicateRecords;
11417 mtext = o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr;
11418 }
11419 return *this;
11420}
11421DRW_Attrib::DRW_Attrib(DRW_Attrib&&) noexcept = default;
11422DRW_Attrib& DRW_Attrib::operator=(DRW_Attrib&&) noexcept = default;
11423
11424DRW_GeoPositionMarker::~DRW_GeoPositionMarker() = default;
11425DRW_GeoPositionMarker::DRW_GeoPositionMarker(
11426 const DRW_GeoPositionMarker& o)
11427 : DRW_Entity(o), m_classVersion(o.m_classVersion),
11428 m_position(o.m_position), m_radius(o.m_radius), m_notes(o.m_notes),
11429 m_landingGap(o.m_landingGap), m_mtextVisible(o.m_mtextVisible),
11430 m_textAlignment(o.m_textAlignment),
11431 m_enableFrameText(o.m_enableFrameText),
11432 mtext(o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr),
11433 m_dxfDouble40Count(o.m_dxfDouble40Count),
11434 m_dxfBool290Count(o.m_dxfBool290Count) {}
11435DRW_GeoPositionMarker& DRW_GeoPositionMarker::operator=(
11436 const DRW_GeoPositionMarker& o) {
11437 if (this != &o) {
11438 DRW_Entity::operator=(o);
11439 m_classVersion = o.m_classVersion;
11440 m_position = o.m_position;
11441 m_radius = o.m_radius;
11442 m_notes = o.m_notes;
11443 m_landingGap = o.m_landingGap;
11444 m_mtextVisible = o.m_mtextVisible;
11445 m_textAlignment = o.m_textAlignment;
11446 m_enableFrameText = o.m_enableFrameText;
11447 mtext = o.mtext ? std::make_unique<DRW_MText>(*o.mtext) : nullptr;
11448 m_dxfDouble40Count = o.m_dxfDouble40Count;
11449 m_dxfBool290Count = o.m_dxfBool290Count;
11450 }
11451 return *this;
11452}
11453DRW_GeoPositionMarker::DRW_GeoPositionMarker(
11454 DRW_GeoPositionMarker&&) noexcept = default;
11455DRW_GeoPositionMarker& DRW_GeoPositionMarker::operator=(
11456 DRW_GeoPositionMarker&&) noexcept = default;
11457
11458namespace {
11459static bool parseEmbeddedMTextEntityMode(DRW::Version version, dwgBuffer *buf,
11460 std::uint64_t bodyEndBit,
11461 EmbeddedMTextHandleInfo& info,
11462 DRW_MText& mtext) {
11463 std::uint8_t entmode = 0;
11464 if (!readBounded2Bits(*buf, bodyEndBit, entmode))
11465 return false;
11466 info.m_ownerHandle = entmode == 0;
11467 if (!readBoundedBitLong(*buf, bodyEndBit, info.m_numReactors)
11468 || !dwgSafety::validReactorCount(info.m_numReactors))
11469 return false;
11470 if (version > DRW::AC1015) {
11471 bool xDictFlag = false;
11472 if (!readBoundedBit(*buf, bodyEndBit, xDictFlag))
11473 return false;
11474 info.m_xDictFlag = xDictFlag ? 1 : 0;
11475 }
11476 if (version > DRW::AC1024) {
11477 bool hasDataStorage = false;
11478 if (!readBoundedBit(*buf, bodyEndBit, hasDataStorage))
11479 return false;
11480 mtext.setHasDataStorageBinaryData(hasDataStorage);
11481 } else if (version < DRW::AC1018) {
11482 bool noLinks = false;
11483 if (!readBoundedBit(*buf, bodyEndBit, noLinks))
11484 return false; // nolinks / have-next-links
11485 }
11486 std::uint32_t color = 0;
11487 if (!readBoundedEnColor(*buf, bodyEndBit, version, color))
11488 return false;
11489 mtext.color = static_cast<int>(color);
11490 info.m_hasAcDbColorHandle = buf->lastEnColorHadDbColorRef;
11491 mtext.color24 = buf->lastEnColorRgb;
11492 if (!buf->lastEnColorName.empty()) {
11493 mtext.colorName = buf->lastEnColorBookName.empty()
11494 ? buf->lastEnColorName
11495 : (buf->lastEnColorBookName + "$" + buf->lastEnColorName);
11496 }
11497 if (buf->lastEnColorAlphaRaw != 0)
11498 mtext.transparency = static_cast<int>(buf->lastEnColorAlphaRaw);
11499 if (!readBoundedBitDouble(*buf, bodyEndBit, mtext.ltypeScale))
11500 return false;
11501 if (version > DRW::AC1014) {
11502 std::uint8_t ltFlags = 0;
11503 std::uint8_t plotFlags = 0;
11504 if (!readBounded2Bits(*buf, bodyEndBit, ltFlags)
11505 || !readBounded2Bits(*buf, bodyEndBit, plotFlags))
11506 return false;
11507 info.m_ltFlags = ltFlags;
11508 info.m_plotFlags = plotFlags;
11509 switch (info.m_ltFlags) {
11510 case 0:
11511 mtext.lineType = "BYLAYER";
11512 break;
11513 case 1:
11514 mtext.lineType = "BYBLOCK";
11515 break;
11516 case 2:
11517 mtext.lineType = "CONTINUOUS";
11518 break;
11519 default:
11520 mtext.lineType.clear();
11521 break;
11522 }
11523 }
11524 if (version > DRW::AC1018) {
11525 std::uint8_t materialFlag = 0;
11526 std::uint8_t shadowFlag = 0;
11527 if (!readBounded2Bits(*buf, bodyEndBit, materialFlag)
11528 || !readBoundedRawChar8(*buf, bodyEndBit, shadowFlag))
11529 return false;
11530 info.m_materialFlag = materialFlag;
11531 info.m_shadowFlag = shadowFlag;
11532 mtext.shadow = static_cast<DRW::ShadowMode>(info.m_shadowFlag & 0x3);
11533 }
11534 if (version > DRW::AC1021) {
11535 if (!readBoundedBit(*buf, bodyEndBit, info.m_hasFullVisualStyle)
11536 || !readBoundedBit(*buf, bodyEndBit, info.m_hasFaceVisualStyle)
11537 || !readBoundedBit(*buf, bodyEndBit, info.m_hasEdgeVisualStyle))
11538 return false;
11539 }
11540 std::uint16_t invisibleFlag = 0;
11541 if (!readBoundedBitShort(*buf, bodyEndBit, invisibleFlag))
11542 return false;
11543 mtext.visible = (invisibleFlag & 1) == 0;
11544 if (version > DRW::AC1014) {
11545 std::uint8_t lineWeight = 0;
11546 if (!readBoundedRawChar8(*buf, bodyEndBit, lineWeight))
11547 return false;
11548 mtext.lWeight = DRW_LW_Conv::dwgInt2lineWidth(lineWeight);
11549 }
11550 return buf->isGood() && currentDwgBit(buf) <= bodyEndBit;
11551}
11552
11553static bool parseEmbeddedMTextDwgFields(
11554 DRW::Version version, dwgBuffer *buf, dwgBuffer *sBuf,
11555 DRW_MText& mtext, EmbeddedMTextHandleInfo& info,
11556 std::uint64_t bodyEndBit, std::uint64_t stringEndBit) {
11557 if (!parseEmbeddedMTextEntityMode(version, buf, bodyEndBit, info, mtext))
11558 return false;
11559
11560 if (!readBoundedBitCoord(*buf, bodyEndBit, mtext.basePoint)
11561 || !readBoundedBitCoord(*buf, bodyEndBit, mtext.extPoint)
11562 || !readBoundedBitCoord(*buf, bodyEndBit, mtext.secPoint))
11563 return false;
11564 mtext.angle = atan2(mtext.secPoint.y, mtext.secPoint.x) * ARAD57.29577951308232;
11565 if (!readBoundedBitDouble(*buf, bodyEndBit, mtext.widthscale))
11566 return false;
11567 if (version > DRW::AC1018) {
11568 double ignoredRectangleHeight = 0.0;
11569 if (!readBoundedBitDouble(*buf, bodyEndBit,
11570 ignoredRectangleHeight))
11571 return false;
11572 }
11573 if (!readBoundedBitDouble(*buf, bodyEndBit, mtext.height))
11574 return false;
11575 std::uint16_t textgen = 0;
11576 std::uint16_t alignH = 0;
11577 double ignoredExtentsHeight = 0.0;
11578 double ignoredExtentsWidth = 0.0;
11579 if (!readBoundedBitShort(*buf, bodyEndBit, textgen)
11580 || !readBoundedBitShort(*buf, bodyEndBit, alignH)
11581 || !readBoundedBitDouble(*buf, bodyEndBit, ignoredExtentsHeight)
11582 || !readBoundedBitDouble(*buf, bodyEndBit, ignoredExtentsWidth))
11583 return false;
11584 mtext.textgen = textgen;
11585 mtext.alignH = static_cast<DRW_Text::HAlign>(alignH);
11586 if (!readBoundedVariableText(*sBuf, stringEndBit, version, mtext.text))
11587 return false;
11588
11589 if (version > DRW::AC1014) {
11590 std::uint16_t ignoredLineSpacingStyle = 0;
11591 bool ignoredLineSpacingUnknown = false;
11592 if (!readBoundedBitShort(*buf, bodyEndBit,
11593 ignoredLineSpacingStyle)
11594 || !readBoundedBitDouble(*buf, bodyEndBit, mtext.interlin)
11595 || !readBoundedBit(*buf, bodyEndBit,
11596 ignoredLineSpacingUnknown))
11597 return false;
11598 }
11599 if (version > DRW::AC1015) {
11600 if (!readBoundedBitLong(*buf, bodyEndBit, mtext.m_backgroundFlags))
11601 return false;
11602 if ((mtext.m_backgroundFlags & 0x01)
11603 || (version >= DRW::AC1032 && (mtext.m_backgroundFlags & 0x10))) {
11604 std::uint32_t backgroundColor = 0;
11605 if (!readBoundedBitDouble(*buf, bodyEndBit,
11606 mtext.m_backgroundScale)
11607 || !readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
11608 backgroundColor, nullptr, nullptr,
11609 nullptr, nullptr, stringEndBit)
11610 || !readBoundedBitLong(*buf, bodyEndBit,
11611 mtext.m_backgroundTransparency))
11612 return false;
11613 mtext.m_backgroundColor = static_cast<int>(backgroundColor);
11614 }
11615 }
11616
11617 if (version >= DRW::AC1032) {
11618 mtext.m_r2018ColumnHeights.clear();
11619 if (!readBoundedBit(*buf, bodyEndBit,
11620 mtext.m_r2018IsNotAnnotative)
11621 || !readBoundedBit(*buf, bodyEndBit,
11622 mtext.m_r2018ReallyLocked))
11623 return false;
11624 info.m_hasR2018AppIdHandle = mtext.m_r2018IsNotAnnotative;
11625 }
11626
11627 std::uint16_t annotativeSize = 0;
11628 if (!readBoundedBitShort(*buf, bodyEndBit, annotativeSize))
11629 return false;
11630 if (annotativeSize > 0) {
11631 std::vector<std::uint8_t> annotativeData;
11632 if (!DRW::reserve(annotativeData, annotativeSize))
11633 return false;
11634 for (std::uint16_t i = 0; i < annotativeSize; ++i) {
11635 std::uint8_t value = 0;
11636 if (!readBoundedRawChar8(*buf, bodyEndBit, value))
11637 return false;
11638 annotativeData.push_back(value);
11639 }
11640 mtext.m_r2018AnnotativeData = std::move(annotativeData);
11641 info.m_hasAnnotativeAppHandle = true;
11642 if (!readBoundedBitShort(*buf, bodyEndBit,
11643 mtext.m_r2018AnnotativeUnknown))
11644 return false;
11645 }
11646 return buf->isGood() && sBuf->isGood()
11647 && currentDwgBit(buf) <= bodyEndBit
11648 && currentDwgBit(sBuf) <= stringEndBit;
11649}
11650
11651static bool parseEmbeddedMTextDwg(
11652 DRW::Version version, dwgBuffer *buf, dwgBuffer *sBuf,
11653 DRW_MText& mtext, EmbeddedMTextHandleInfo& info,
11654 std::uint64_t bodyEndBit, std::uint64_t stringEndBit) {
11655 if (buf == nullptr || sBuf == nullptr || !buf->isGood()
11656 || !sBuf->isGood())
11657 return false;
11658
11659 const std::uint64_t bodyStartBit = currentDwgBit(buf);
11660 const std::uint64_t stringStartBit = currentDwgBit(sBuf);
11661 const auto totalBits = [](const dwgBuffer& buffer) {
11662 std::uint64_t bits = 0;
11663 return dwgSafety::multiply(buffer.size(), 8, bits) ? bits : 0;
11664 };
11665 if (bodyEndBit == std::numeric_limits<std::uint64_t>::max())
11666 bodyEndBit = totalBits(*buf);
11667 if (stringEndBit == std::numeric_limits<std::uint64_t>::max())
11668 stringEndBit = totalBits(*sBuf);
11669 if (bodyStartBit > bodyEndBit || stringStartBit > stringEndBit)
11670 return false;
11671
11672 dwgBuffer bodyProbe = buf->forkIndependent();
11673 std::unique_ptr<dwgBuffer> separateStringProbe;
11674 dwgBuffer *stringProbe = &bodyProbe;
11675 if (sBuf != buf) {
11676 separateStringProbe = std::make_unique<dwgBuffer>(
11677 sBuf->forkIndependent());
11678 stringProbe = separateStringProbe.get();
11679 }
11680 if (!parseEmbeddedMTextDwgFields(version, &bodyProbe, stringProbe,
11681 mtext, info, bodyEndBit,
11682 stringEndBit))
11683 return false;
11684
11685 const std::uint64_t bodyConsumed = currentDwgBit(&bodyProbe);
11686 const std::uint64_t stringConsumed = currentDwgBit(stringProbe);
11687 if (bodyConsumed < bodyStartBit || stringConsumed < stringStartBit
11688 || bodyConsumed > bodyEndBit || stringConsumed > stringEndBit)
11689 return false;
11690
11691 const auto seekBits = [](dwgBuffer& buffer, std::uint64_t bit) {
11692 if (!buffer.setPosition(bit >> 3))
11693 return false;
11694 buffer.setBitPos(static_cast<std::uint8_t>(bit & 7u));
11695 return buffer.isGood() && currentDwgBit(&buffer) == bit;
11696 };
11697 if (!seekBits(*buf, bodyConsumed))
11698 return false;
11699 if (sBuf != buf && !seekBits(*sBuf, stringConsumed))
11700 return false;
11701 return true;
11702}
11703
11704static bool consumeEmbeddedMTextHandles(
11705 DRW::Version version, dwgBuffer *buf,
11706 const EmbeddedMTextHandleInfo& info, DRW_MText *mtext,
11707 std::uint64_t handleEndBit) {
11708 if (buf == nullptr || !buf->isGood())
11709 return false;
11710
11711 if (handleEndBit == std::numeric_limits<std::uint64_t>::max()) {
11712 if (!dwgSafety::multiply(buf->size(), 8, handleEndBit))
11713 return false;
11714 }
11715 dwgBuffer probe = buf->forkIndependent();
11716 auto readHandle = [&](bool offset, dwgHandle& value) {
11717 return readBoundedDwgHandle(probe, handleEndBit, 0, offset, value);
11718 };
11719
11720 dwgHandle owner;
11721 if (info.m_ownerHandle && !readHandle(false, owner))
11722 return false;
11723
11724 std::vector<std::uint32_t> reactors;
11725 if (info.m_numReactors < 0
11726 || !dwgSafety::validReactorCount(info.m_numReactors))
11727 return false;
11728 if (!DRW::reserve(reactors, info.m_numReactors))
11729 return false;
11730 for (int i = 0; i < info.m_numReactors; ++i) {
11731 dwgHandle reactor;
11732 if (!readHandle(false, reactor))
11733 return false;
11734 if (reactor.ref != 0)
11735 reactors.push_back(reactor.ref);
11736 }
11737
11738 dwgHandle xDict;
11739 const bool hasXDict = version <= DRW::AC1015 || info.m_xDictFlag != 1;
11740 if (hasXDict && !readHandle(false, xDict))
11741 return false;
11742
11743 dwgHandle color;
11744 if (info.m_hasAcDbColorHandle && !readHandle(false, color))
11745 return false;
11746
11747 dwgHandle layer;
11748 const bool hasLayer = version > DRW::AC1014;
11749 if (hasLayer && !readHandle(false, layer))
11750 return false;
11751
11752 dwgHandle lineType;
11753 const bool hasLineType = version > DRW::AC1014 && info.m_ltFlags == 3;
11754 if (hasLineType && !readHandle(false, lineType))
11755 return false;
11756
11757 dwgHandle material;
11758 const bool hasMaterial = version > DRW::AC1018 && info.m_materialFlag == 3;
11759 if (hasMaterial && !readHandle(false, material))
11760 return false;
11761
11762 dwgHandle shadow;
11763 const bool hasShadow = version > DRW::AC1018 && info.m_shadowFlag == 3;
11764 if (hasShadow && !readHandle(false, shadow))
11765 return false;
11766
11767 dwgHandle plotStyle;
11768 const bool hasPlotStyle = info.m_plotFlags == 3;
11769 if (hasPlotStyle && !readHandle(false, plotStyle))
11770 return false;
11771
11772 dwgHandle fullVisualStyle;
11773 const bool hasFullVisualStyle = version > DRW::AC1021
11774 && info.m_hasFullVisualStyle;
11775 if (hasFullVisualStyle && !readHandle(false, fullVisualStyle))
11776 return false;
11777
11778 dwgHandle faceVisualStyle;
11779 const bool hasFaceVisualStyle = version > DRW::AC1021
11780 && info.m_hasFaceVisualStyle;
11781 if (hasFaceVisualStyle && !readHandle(false, faceVisualStyle))
11782 return false;
11783
11784 dwgHandle edgeVisualStyle;
11785 const bool hasEdgeVisualStyle = version > DRW::AC1021
11786 && info.m_hasEdgeVisualStyle;
11787 if (hasEdgeVisualStyle && !readHandle(false, edgeVisualStyle))
11788 return false;
11789
11790 dwgHandle style;
11791 if (info.m_hasStyleHandle && !readHandle(false, style))
11792 return false;
11793
11794 dwgHandle appId;
11795 if (info.m_hasR2018AppIdHandle && !readHandle(false, appId))
11796 return false;
11797
11798 dwgHandle annotativeApp;
11799 if (info.m_hasAnnotativeAppHandle && !readHandle(false, annotativeApp))
11800 return false;
11801
11802 if (mtext != nullptr) {
11803 if (info.m_ownerHandle)
11804 mtext->parentHandle = owner.ref;
11805 mtext->reactorHandles = std::move(reactors);
11806 if (hasXDict)
11807 mtext->xDictHandle = xDict.ref;
11808 if (info.m_hasAcDbColorHandle)
11809 mtext->setDwgAcDbColorHandle(true, color.ref);
11810 if (hasLayer)
11811 mtext->setDwgLayerHandle(layer.ref);
11812 if (hasLineType)
11813 mtext->setDwgLinetypeHandle(lineType.ref);
11814 if (hasMaterial)
11815 mtext->material = material.ref;
11816 if (hasShadow)
11817 mtext->shadowHandle = shadow.ref;
11818 if (hasPlotStyle)
11819 mtext->plotStyle = static_cast<int>(plotStyle.ref);
11820 if (hasFullVisualStyle)
11821 mtext->fullVisualStyleHandle = fullVisualStyle.ref;
11822 if (hasFaceVisualStyle)
11823 mtext->faceVisualStyleHandle = faceVisualStyle.ref;
11824 if (hasEdgeVisualStyle)
11825 mtext->edgeVisualStyleHandle = edgeVisualStyle.ref;
11826 if (info.m_hasStyleHandle)
11827 mtext->styleH = style;
11828 if (info.m_hasR2018AppIdHandle)
11829 mtext->m_r2018AppIdHandle = appId.ref;
11830 if (info.m_hasAnnotativeAppHandle)
11831 mtext->m_r2018AnnotativeAppHandle = annotativeApp.ref;
11832 }
11833 *buf = probe;
11834 return true;
11835}
11836
11837static bool encodeEmbeddedMTextEntityMode(DRW::Version version, dwgBufferW *buf,
11838 const DRW_MText& mtext) {
11839 if (version < DRW::AC1032)
11840 return false;
11841
11842 // Embedded MTEXT begins at AcDbEntity mode, not with an object type,
11843 // object size, own handle, EED, or graphics data.
11844 if (mtext.reactorHandles.size()
11845 > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()))
11846 return false;
11847 buf->put2Bits(mtext.parentHandle == DRW::NoHandle ? 2 : 0);
11848 buf->putBitLong(static_cast<std::int32_t>(mtext.reactorHandles.size()));
11849 buf->putBit(mtext.xDictHandle == 0 ? 1 : 0);
11850 buf->putBit(mtext.hasDataStorageBinaryData() ? 1 : 0);
11851 UTF8STRINGstd::string colorName = mtext.colorName;
11852 UTF8STRINGstd::string bookName;
11853 const std::string::size_type separator = colorName.find('$');
11854 if (separator != std::string::npos) {
11855 bookName = colorName.substr(0, separator);
11856 colorName.erase(0, separator + 1);
11857 }
11858 buf->putEnColor(version, static_cast<std::uint16_t>(mtext.color),
11859 mtext.color24, colorName, bookName,
11860 static_cast<std::uint32_t>(mtext.transparency),
11861 mtext.hasDwgAcDbColorHandle());
11862 buf->putBitDouble(mtext.ltypeScale);
11863 std::uint8_t ltFlags = 0;
11864 if (mtext.dwgLinetypeHandle() != 0 || mtext.lineType.empty())
11865 ltFlags = 3;
11866 else if (mtext.lineType == "BYBLOCK")
11867 ltFlags = 1;
11868 else if (mtext.lineType == "CONTINUOUS")
11869 ltFlags = 2;
11870 buf->put2Bits(ltFlags);
11871 buf->put2Bits(mtext.plotStyle == DRW::DefaultPlotStyle ? 0 : 3);
11872 buf->put2Bits(mtext.material == DRW::MaterialByLayer ? 0 : 3);
11873 buf->putRawChar8(static_cast<std::uint8_t>(mtext.shadow));
11874 buf->putBit(mtext.fullVisualStyleHandle != 0);
11875 buf->putBit(mtext.faceVisualStyleHandle != 0);
11876 buf->putBit(mtext.edgeVisualStyleHandle != 0);
11877 buf->putBitShort(mtext.visible ? 0 : 1);
11878 buf->putRawChar8(static_cast<std::uint8_t>(mtext.lWeight));
11879 return true;
11880}
11881
11882static bool encodeEmbeddedMTextDwg(DRW::Version version, dwgBufferW *buf,
11883 dwgBufferW *strBuf, dwgBufferW *handleBuf,
11884 const DRW_MText& mtext) {
11885 if (!encodeEmbeddedMTextEntityMode(version, buf, mtext))
11886 return false;
11887
11888 buf->put3BitDouble(mtext.basePoint);
11889 buf->put3BitDouble(mtext.extPoint);
11890 buf->put3BitDouble(mtext.secPoint);
11891 buf->putBitDouble(mtext.widthscale);
11892 buf->putBitDouble(mtext.m_r2018RectHeight);
11893 buf->putBitDouble(mtext.height);
11894 buf->putBitShort(static_cast<std::uint16_t>(mtext.textgen));
11895 buf->putBitShort(static_cast<std::uint16_t>(mtext.alignH));
11896 buf->putBitDouble(mtext.m_r2018ExtentsHeight);
11897 buf->putBitDouble(mtext.m_r2018ExtentsWidth);
11898 (strBuf ? strBuf : buf)->putVariableText(version, mtext.text);
11899
11900 buf->putBitShort(0); // linespacing style
11901 buf->putBitDouble(mtext.interlin);
11902 buf->putBit(0);
11903 buf->putBitLong(mtext.m_backgroundFlags);
11904 if ((mtext.m_backgroundFlags & 0x01)
11905 || (mtext.m_backgroundFlags & 0x10)) {
11906 buf->putBitDouble(mtext.m_backgroundScale); // BitDouble, not BitLong
11907 buf->putCmColor(version, static_cast<std::uint16_t>(mtext.m_backgroundColor));
11908 buf->putBitLong(mtext.m_backgroundTransparency);
11909 }
11910
11911 buf->putBit(mtext.m_r2018IsNotAnnotative ? 1 : 0);
11912 buf->putBit(mtext.m_r2018ReallyLocked ? 1 : 0);
11913
11914 if (mtext.m_r2018AnnotativeData.size() > std::numeric_limits<std::uint16_t>::max())
11915 return false;
11916 buf->putBitShort(static_cast<std::uint16_t>(mtext.m_r2018AnnotativeData.size()));
11917 for (std::uint8_t byte : mtext.m_r2018AnnotativeData)
11918 buf->putRawChar8(byte);
11919 if (!mtext.m_r2018AnnotativeData.empty()) {
11920 buf->putBitShort(mtext.m_r2018AnnotativeUnknown);
11921 }
11922
11923 dwgBufferW *hb = handleBuf ? handleBuf : buf;
11924 if (mtext.parentHandle != DRW::NoHandle)
11925 putHardPointerHandle(hb, mtext.parentHandle);
11926 for (const std::uint32_t reactor : mtext.reactorHandles)
11927 putHardPointerHandle(hb, reactor);
11928 if (mtext.xDictHandle != 0)
11929 putHardPointerHandle(hb, mtext.xDictHandle);
11930 if (mtext.hasDwgAcDbColorHandle())
11931 putHardPointerHandle(hb, mtext.dwgAcDbColorHandle());
11932 putHardPointerHandle(hb, mtext.dwgLayerHandle() == 0
11933 ? 0x12 : mtext.dwgLayerHandle());
11934 if (mtext.dwgLinetypeHandle() != 0 || mtext.lineType.empty())
11935 putHardPointerHandle(hb, mtext.dwgLinetypeHandle());
11936 if (mtext.material != DRW::MaterialByLayer)
11937 putHardPointerHandle(hb, mtext.material);
11938 if (mtext.shadow == DRW::IgnoreShadows)
11939 putHardPointerHandle(hb, mtext.shadowHandle);
11940 if (mtext.plotStyle != DRW::DefaultPlotStyle)
11941 putHardPointerHandle(hb, static_cast<std::uint32_t>(mtext.plotStyle));
11942 if (mtext.fullVisualStyleHandle != 0)
11943 putHardPointerHandle(hb, mtext.fullVisualStyleHandle);
11944 if (mtext.faceVisualStyleHandle != 0)
11945 putHardPointerHandle(hb, mtext.faceVisualStyleHandle);
11946 if (mtext.edgeVisualStyleHandle != 0)
11947 putHardPointerHandle(hb, mtext.edgeVisualStyleHandle);
11948 putHardPointerHandle(hb, (mtext.styleH.ref == 0) ? 0x13 : mtext.styleH.ref);
11949 if (mtext.m_r2018IsNotAnnotative)
11950 putHardPointerHandle(hb, (mtext.m_r2018AppIdHandle == 0) ? 0x14 : mtext.m_r2018AppIdHandle);
11951 if (!mtext.m_r2018AnnotativeData.empty())
11952 putHardPointerHandle(hb, (mtext.m_r2018AnnotativeAppHandle == 0)
11953 ? 0x14 : mtext.m_r2018AnnotativeAppHandle);
11954 return true;
11955}
11956}
11957
11958bool DRW_GeoPositionMarker::encodeDwg(DRW::Version v, dwgBufferW *buf,
11959 std::uint32_t bs, dwgBufferW *strBuf,
11960 dwgBufferW *handleBuf) {
11961 (void)bs;
11962 if (v < DRW::AC1027 || buf == nullptr)
11963 return false;
11964 // The embedded AcDbMTextObjectEmbedded wire form was introduced with
11965 // AC1032. Reject an AC1027 framed marker rather than emitting a body that
11966 // a reader will interpret as the parent handle stream.
11967 if (m_enableFrameText && (v < DRW::AC1032 || mtext == nullptr))
11968 return false;
11969
11970 oType = kDwgType;
11971 if (!encodeDwgCommon(v, buf, strBuf))
11972 return false;
11973
11974 dwgBufferW *stringBuffer = strBuf ? strBuf : buf;
11975 buf->putBitLong(m_classVersion);
11976 buf->put3BitDouble(m_position);
11977 buf->putBitDouble(m_radius);
11978 stringBuffer->putVariableText(v, m_notes);
11979 buf->putBitDouble(m_landingGap);
11980 buf->putBit(m_mtextVisible ? 1 : 0);
11981 buf->putRawChar8(m_textAlignment);
11982 buf->putBit(m_enableFrameText ? 1 : 0);
11983
11984 dwgBufferW embeddedHandles;
11985 if (m_enableFrameText
11986 && !encodeEmbeddedMTextDwg(v, buf, strBuf, &embeddedHandles, *mtext))
11987 return false;
11988 if (!encodeDwgEntHandle(v, buf, handleBuf))
11989 return false;
11990
11991 dwgBufferW *resolvedHandles = handleBuf ? handleBuf : buf;
11992 appendBitBuffer(resolvedHandles, embeddedHandles);
11993 return true;
11994}
11995
11996bool DRW_Attrib::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
11997 // Multi-line ATTRIB (R2018+, ODA spec §20.4.4): an embedded MTEXT object
11998 // is introduced by the DXF subclass marker `100 / Embedded Object` (NOT
11999 // `AcDbMText`). After the marker, the standard MTEXT group codes follow
12000 // (10/20/30 insertion, 11/21/31 X-axis, 40 height, 41 rect width, 71
12001 // attachment point, 72 drawing direction, 1 formatted text, etc.), then
12002 // the ATTRIB-specific tail (tag=2, prompt=3 for ATTDEF, flags=70,
12003 // lock-position=280) which we must NOT route into the embedded MText.
12004 if (code == 100) {
12005 const std::string sub = reader->getString();
12006 if (sub == "Embedded Object" && !mtext) {
12007 mtext = std::make_unique<DRW_MText>();
12008 if (attVersion == 0) attVersion = 1;
12009 }
12010 dxfInAttributeSubclass = sub == "AcDbAttribute"
12011 || sub == "AcDbAttributeDefinition";
12012 return true;
12013 }
12014 if (dxfInAttributeSubclass && !mtext) {
12015 if (code == 71) {
12016 const int value = reader->getInt32();
12017 if (value < 0 || value > std::numeric_limits<std::uint8_t>::max())
12018 return false;
12019 m_attributeType = static_cast<std::uint8_t>(value);
12020 return true;
12021 }
12022 if (code == 72) {
12023 (void)reader->getInt32();
12024 return true;
12025 }
12026 }
12027 // Inside the embedded MText scope, route MTEXT-owned codes to mtext but
12028 // keep ATTRIB-specific tail codes for ATTRIB / ATTDEF handling below.
12029 if (mtext) {
12030 switch (code) {
12031 case 2: // tag (ATTRIB-specific; group 1 in MText is text body)
12032 case 3: // prompt (ATTDEF-specific)
12033 case 70: // ATTRIB flags
12034 case 280: // ATTRIB lock-position
12035 break; // fall through to ATTRIB handling below
12036 default:
12037 return mtext->parseCode(code, reader);
12038 }
12039 }
12040 switch (code) {
12041 case 2:
12042 tag = reader->getUtf8String();
12043 break;
12044 case 70:
12045 attribFlags = reader->getInt32();
12046 break;
12047 case 73:
12048 // AcDbAttribute code 73 = field length (obsolete); NOT the vertical
12049 // alignment from AcDbText (which is code 73 in TEXT but 74 in ATTRIB).
12050 m_fieldLength = reader->getInt32();
12051 break;
12052 case 74:
12053 // AcDbAttribute vertical alignment (code 74); TEXT uses code 73 for
12054 // this but ATTRIB moves it here to free code 73 for field length.
12055 alignV = (VAlign)reader->getInt32();
12056 break;
12057 case 280:
12058 // Lock position flag (R2010+ DXF group code)
12059 lockPosition = reader->getInt32() != 0;
12060 break;
12061 default:
12062 return DRW_Text::parseCode(code, reader);
12063 }
12064 return true;
12065}
12066
12067void DRW_Attrib::resetAttribDwgState() {
12068 resetDwgState();
12069 tag.clear();
12070 attribFlags = 0;
12071 m_fieldLength = 0;
12072 lockPosition = false;
12073 attVersion = 0;
12074 m_attributeType = 1;
12075 keepDuplicateRecords = 0;
12076 mtext.reset();
12077 dxfInAttributeSubclass = false;
12078}
12079
12080bool DRW_Attrib::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
12081 resetAttribDwgState();
12082 dwgBuffer *sourceBuf = buf;
12083 auto fail = [this, sourceBuf]() {
12084 if (sourceBuf != nullptr)
12085 sourceBuf->invalidate();
12086 resetAttribDwgState();
12087 return false;
12088 };
12089 if (sourceBuf == nullptr)
12090 return fail();
12091
12092 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
12093 buf = &bodyProbe;
12094 dwgBuffer sBuff = bodyProbe.forkIndependent();
12095 dwgBuffer *sBuf = &bodyProbe;
12096 if (version > DRW::AC1018) {//2007+
12097 sBuf = &sBuff; //separate buffer for strings
12098 }
12099 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
12100 if (!ret)
12101 return fail();
12102 DRW_DBG("\n***************************** parsing attrib *********************************************\n")DRW_dbg::dbg("\n***************************** parsing attrib *********************************************\n"
)
;
12103
12104 const std::uint64_t bodyEndBit = dwgDataEndBit;
12105 std::uint64_t stringStartBit = bodyEndBit;
12106 std::uint64_t stringEndBit = bodyEndBit;
12107 if (version > DRW::AC1018
12108 && (!buf->getR2007StringStreamBounds(objSize, stringStartBit,
12109 stringEndBit)
12110 || currentDwgBit(sBuf) > stringEndBit))
12111 return fail();
12112
12113 DwgTextBodyData parsedText;
12114 if (!readBoundedDwgTextBody(version, *buf, *sBuf, bodyEndBit,
12115 stringStartBit, stringEndBit, parsedText))
12116 return fail();
12117
12118 // R2010+ ATTRIB version follows the common TEXT data. R2018 adds the
12119 // attribute type immediately after it.
12120 std::uint8_t parsedAttVersion = 0;
12121 std::uint8_t parsedAttributeType = 1;
12122 if (version >= DRW::AC1024) {
12123 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedAttVersion))
12124 return fail();
12125 }
12126 if (version >= DRW::AC1032) {
12127 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedAttributeType))
12128 return fail();
12129 }
12130
12131 bool hasEmbeddedMText = false;
12132 EmbeddedMTextHandleInfo embeddedMTextHandles;
12133 std::unique_ptr<DRW_MText> parsedMText;
12134 if (version >= DRW::AC1032
12135 && parsedAttributeType != 0 && parsedAttributeType != 1) {
12136 parsedMText = std::make_unique<DRW_MText>();
12137 if (!parseEmbeddedMTextDwg(version, buf, sBuf, *parsedMText,
12138 embeddedMTextHandles, bodyEndBit,
12139 stringEndBit)) {
12140 DRW_DBG("R2018 multi-line ATTRIB payload failed\n")DRW_dbg::dbg("R2018 multi-line ATTRIB payload failed\n");
12141 return fail();
12142 }
12143 hasEmbeddedMText = true;
12144 }
12145
12146 // ATTRIB-specific fields
12147 UTF8STRINGstd::string parsedTag;
12148 if (version <= DRW::AC1018 || stringStartBit < stringEndBit) {
12149 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
12150 parsedTag))
12151 return fail();
12152 }
12153
12154 std::uint16_t parsedFieldLength = 0;
12155 std::uint8_t parsedAttribFlags = 0;
12156 bool parsedLockPosition = false;
12157 if (!readBoundedBitShort(*buf, bodyEndBit, parsedFieldLength)
12158 || !readBoundedRawChar8(*buf, bodyEndBit, parsedAttribFlags))
12159 return fail();
12160
12161 // lockPosition (DXF 280) appears since R2007 (AC1021) per ODA §20.4.x /
12162 // ACadSharp. Read gate lowered AC1024->AC1021 so R2007/8/9 imports keep
12163 // it. The writer emits the matching bit for AC1021 and later;
12164 // parseDwgEntHandle repositions to objSize for version>AC1018 before
12165 // consuming the handle stream.
12166 if (version >= DRW::AC1021) {
12167 if (!readBoundedBit(*buf, bodyEndBit, parsedLockPosition))
12168 return fail();
12169 }
12170
12171 if (!buf->isGood() || !sBuf->isGood())
12172 return fail();
12173
12174 /* Common Entity Handle Data */
12175 // The parent ATTRIB handle stream starts at objSize. Embedded MTEXT
12176 // handles follow it, so parse the parent first and consume the embedded
12177 // stream from the resulting position.
12178 std::uint64_t handleEndBit = 0;
12179 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
12180 handleEndBit))
12181 return fail();
12182 dwgBuffer handleProbe = buf->forkIndependent();
12183 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
12184 handleEndBit);
12185 if (!ret)
12186 return fail();
12187
12188 if (hasEmbeddedMText) {
12189 if (!consumeEmbeddedMTextHandles(version, &handleProbe,
12190 embeddedMTextHandles,
12191 parsedMText.get(), handleEndBit))
12192 return fail();
12193 }
12194
12195 dwgHandle parsedStyleH;
12196 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false, parsedStyleH)
12197 || !handleProbe.isGood() || !sBuf->isGood()
12198 || currentDwgBit(buf) > bodyEndBit
12199 || currentDwgBit(sBuf) > stringEndBit)
12200 return fail();
12201
12202 *sourceBuf = handleProbe;
12203 basePoint = parsedText.basePoint;
12204 secPoint = parsedText.secPoint;
12205 extPoint = parsedText.extPoint;
12206 thickness = parsedText.thickness;
12207 oblique = parsedText.oblique;
12208 angle = parsedText.angle;
12209 height = parsedText.height;
12210 widthscale = parsedText.widthscale;
12211 text = std::move(parsedText.text);
12212 textgen = parsedText.textgen;
12213 alignH = parsedText.alignH;
12214 alignV = parsedText.alignV;
12215 attVersion = parsedAttVersion;
12216 m_attributeType = parsedAttributeType;
12217 mtext = std::move(parsedMText);
12218 tag = std::move(parsedTag);
12219 m_fieldLength = parsedFieldLength;
12220 attribFlags = parsedAttribFlags;
12221 lockPosition = parsedLockPosition;
12222 styleH = parsedStyleH;
12223 DRW_DBG("text style Handle: ")DRW_dbg::dbg("text style Handle: "); DRW_DBGHL(styleH.code, styleH.size, styleH.ref)DRW_dbg::dbgHL(styleH.code, styleH.size, styleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
12224 return true;
12225}
12226
12227bool DRW_Attrib::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
12228 (void)bs;
12229 if (version >= DRW::AC1024 && version < DRW::AC1032 && (attVersion != 0 || mtext))
12230 return false;
12231 const std::uint8_t attributeType = (m_attributeType == 0) ? 1 : m_attributeType;
12232 const bool hasEmbeddedMText = version >= DRW::AC1032 && attributeType != 1;
12233 if (hasEmbeddedMText && !mtext)
12234 return false;
12235
12236 oType = 2; // ATTRIB class id — see dwgreader.cpp:1148
12237 if (!encodeDwgCommon(version, buf)) return false;
12238
12239 // TEXT-body section — mirrors DRW_Attrib::parseDwg.
12240 // data_flags=0: emit every optional field unconditionally (same
12241 // strategy as DRW_Text::encodeDwg — simpler encoder, ~30 bytes larger).
12242 buf->putRawChar8(0); // data_flags=0
12243 buf->putRawDouble(basePoint.z); // elevation RD
12244 buf->putRawDouble(basePoint.x); // insertion 2RD
12245 buf->putRawDouble(basePoint.y);
12246 buf->putDefaultDouble(basePoint.x, secPoint.x); // alignment 2DD
12247 buf->putDefaultDouble(basePoint.y, secPoint.y);
12248 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
12249 buf->putThickness(thickness, /*b_R2000_style=*/true);
12250 buf->putRawDouble(oblique); // oblique angle RD
12251 buf->putRawDouble(angle / ARAD57.29577951308232); // angle in radians RD
12252 buf->putRawDouble(height); // text height RD
12253 buf->putRawDouble(widthscale); // width factor RD
12254 dwgBufferW *sb = strBuf ? strBuf : buf;
12255 sb->putVariableText(version, text); // text string TV
12256 buf->putBitShort(static_cast<std::uint16_t>(textgen)); // generation flags BS
12257 buf->putBitShort(static_cast<std::uint16_t>(alignH)); // horiz align BS
12258 buf->putBitShort(static_cast<std::uint16_t>(alignV)); // vert align BS
12259
12260 if (version >= DRW::AC1024) {
12261 buf->putRawChar8(hasEmbeddedMText && attVersion == 0 ? 1 : attVersion);
12262 }
12263 if (version >= DRW::AC1032) {
12264 buf->putRawChar8(attributeType);
12265 }
12266
12267 dwgBufferW embeddedHandles;
12268 if (hasEmbeddedMText) {
12269 if (!encodeEmbeddedMTextDwg(version, buf, strBuf, &embeddedHandles, *mtext))
12270 return false;
12271 }
12272
12273 // ATTRIB-specific tail
12274 sb->putVariableText(version, tag); // tag TV
12275 buf->putBitShort(static_cast<std::uint16_t>(m_fieldLength)); // fieldLen BS
12276 buf->putRawChar8(attribFlags); // flags RC
12277 if (version >= DRW::AC1021) {
12278 buf->putBit(lockPosition ? 1 : 0); // lock position B (R2007a+)
12279 }
12280
12281 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
12282
12283 dwgHandle sH;
12284 std::uint32_t sref = (styleH.ref == 0) ? 0x13 : styleH.ref;
12285 sH.code = 5;
12286 sH.ref = sref;
12287 sH.size = 0;
12288 if (sref != 0) { std::uint32_t t = sref; while (t != 0) { t >>= 8; ++sH.size; } }
12289 dwgBufferW *hb = handleBuf ? handleBuf : buf;
12290 appendBitBuffer(hb, embeddedHandles);
12291 hb->putHandle(sH);
12292 return buf->isGood() && sb->isGood() && hb->isGood();
12293}
12294
12295bool DRW_Attdef::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
12296 switch (code) {
12297 case 3:
12298 prompt = reader->getUtf8String();
12299 break;
12300 default:
12301 return DRW_Attrib::parseCode(code, reader);
12302 }
12303 return true;
12304}
12305
12306bool DRW_Attdef::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
12307 // ATTDEF mirrors ATTRIB layout but adds a prompt string after the tag.
12308 // Implementation duplicates ATTRIB::parseDwg in order to inject the
12309 // prompt read at the correct offset; refactor opportunity if a third
12310 // sibling appears.
12311 resetAttribDwgState();
12312 prompt.clear();
12313 dwgBuffer *sourceBuf = buf;
12314 auto fail = [this, sourceBuf]() {
12315 if (sourceBuf != nullptr)
12316 sourceBuf->invalidate();
12317 resetAttribDwgState();
12318 prompt.clear();
12319 return false;
12320 };
12321 if (sourceBuf == nullptr)
12322 return fail();
12323
12324 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
12325 buf = &bodyProbe;
12326 dwgBuffer sBuff = bodyProbe.forkIndependent();
12327 dwgBuffer *sBuf = &bodyProbe;
12328 if (version > DRW::AC1018) {
12329 sBuf = &sBuff;
12330 }
12331 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
12332 if (!ret)
12333 return fail();
12334 DRW_DBG("\n***************************** parsing attdef *********************************************\n")DRW_dbg::dbg("\n***************************** parsing attdef *********************************************\n"
)
;
12335 const std::uint64_t bodyEndBit = dwgDataEndBit;
12336 std::uint64_t stringStartBit = bodyEndBit;
12337 std::uint64_t stringEndBit = bodyEndBit;
12338 if (version > DRW::AC1018
12339 && (!buf->getR2007StringStreamBounds(objSize, stringStartBit,
12340 stringEndBit)
12341 || currentDwgBit(sBuf) > stringEndBit))
12342 return fail();
12343
12344 DwgTextBodyData parsedText;
12345 if (!readBoundedDwgTextBody(version, *buf, *sBuf, bodyEndBit,
12346 stringStartBit, stringEndBit, parsedText))
12347 return fail();
12348
12349 std::uint8_t parsedAttVersion = 0;
12350 std::uint8_t parsedAttributeType = 1;
12351 if (version >= DRW::AC1024) {
12352 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedAttVersion))
12353 return fail();
12354 }
12355 if (version >= DRW::AC1032) {
12356 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedAttributeType))
12357 return fail();
12358 }
12359
12360 bool hasEmbeddedMText = false;
12361 EmbeddedMTextHandleInfo embeddedMTextHandles;
12362 std::unique_ptr<DRW_MText> parsedMText;
12363 if (version >= DRW::AC1032
12364 && parsedAttributeType != 0 && parsedAttributeType != 1) {
12365 parsedMText = std::make_unique<DRW_MText>();
12366 if (!parseEmbeddedMTextDwg(version, buf, sBuf, *parsedMText,
12367 embeddedMTextHandles, bodyEndBit,
12368 stringEndBit)) {
12369 DRW_DBG("R2018 multi-line ATTDEF payload failed\n")DRW_dbg::dbg("R2018 multi-line ATTDEF payload failed\n");
12370 return fail();
12371 }
12372 hasEmbeddedMText = true;
12373 }
12374
12375 UTF8STRINGstd::string parsedTag;
12376 if (version <= DRW::AC1018 || stringStartBit < stringEndBit) {
12377 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
12378 parsedTag))
12379 return fail();
12380 }
12381
12382 std::uint16_t parsedFieldLength = 0;
12383 std::uint8_t parsedAttribFlags = 0;
12384 bool parsedLockPosition = false;
12385 if (!readBoundedBitShort(*buf, bodyEndBit, parsedFieldLength)
12386 || !readBoundedRawChar8(*buf, bodyEndBit, parsedAttribFlags))
12387 return fail();
12388
12389 // lockPosition (DXF 280) is a bit in R2007+ DWG data.
12390 if (version >= DRW::AC1021) {
12391 if (!readBoundedBit(*buf, bodyEndBit, parsedLockPosition))
12392 return fail();
12393 }
12394
12395 // ATTDEF prompt follows attrib body
12396 UTF8STRINGstd::string parsedPrompt;
12397 if (version <= DRW::AC1018 || stringStartBit < stringEndBit) {
12398 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
12399 parsedPrompt))
12400 return fail();
12401 }
12402
12403 if (!buf->isGood() || !sBuf->isGood())
12404 return fail();
12405
12406 // The parent ATTDEF handle stream precedes the embedded MTEXT handles.
12407 std::uint64_t handleEndBit = 0;
12408 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
12409 handleEndBit))
12410 return fail();
12411 dwgBuffer handleProbe = buf->forkIndependent();
12412 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
12413 handleEndBit);
12414 if (!ret)
12415 return fail();
12416
12417 if (hasEmbeddedMText) {
12418 if (!consumeEmbeddedMTextHandles(version, &handleProbe,
12419 embeddedMTextHandles,
12420 parsedMText.get(), handleEndBit))
12421 return fail();
12422 }
12423
12424 dwgHandle parsedStyleH;
12425 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false, parsedStyleH)
12426 || !handleProbe.isGood() || !sBuf->isGood()
12427 || currentDwgBit(buf) > bodyEndBit
12428 || currentDwgBit(sBuf) > stringEndBit)
12429 return fail();
12430
12431 *sourceBuf = handleProbe;
12432 basePoint = parsedText.basePoint;
12433 secPoint = parsedText.secPoint;
12434 extPoint = parsedText.extPoint;
12435 thickness = parsedText.thickness;
12436 oblique = parsedText.oblique;
12437 angle = parsedText.angle;
12438 height = parsedText.height;
12439 widthscale = parsedText.widthscale;
12440 text = std::move(parsedText.text);
12441 textgen = parsedText.textgen;
12442 alignH = parsedText.alignH;
12443 alignV = parsedText.alignV;
12444 attVersion = parsedAttVersion;
12445 m_attributeType = parsedAttributeType;
12446 mtext = std::move(parsedMText);
12447 tag = std::move(parsedTag);
12448 m_fieldLength = parsedFieldLength;
12449 attribFlags = parsedAttribFlags;
12450 lockPosition = parsedLockPosition;
12451 prompt = std::move(parsedPrompt);
12452 styleH = parsedStyleH;
12453 return true;
12454}
12455
12456bool DRW_Attdef::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
12457 (void)bs;
12458 if (version >= DRW::AC1024 && version < DRW::AC1032 && (attVersion != 0 || mtext))
12459 return false;
12460 const std::uint8_t attributeType = (m_attributeType == 0) ? 1 : m_attributeType;
12461 const bool hasEmbeddedMText = version >= DRW::AC1032 && attributeType != 1;
12462 if (hasEmbeddedMText && !mtext)
12463 return false;
12464
12465 oType = 3; // ATTDEF class id — see dwgreader.cpp:1185
12466 if (!encodeDwgCommon(version, buf)) return false;
12467
12468 // TEXT-body section — identical layout to DRW_Attrib::encodeDwg.
12469 buf->putRawChar8(0);
12470 buf->putRawDouble(basePoint.z);
12471 buf->putRawDouble(basePoint.x);
12472 buf->putRawDouble(basePoint.y);
12473 buf->putDefaultDouble(basePoint.x, secPoint.x);
12474 buf->putDefaultDouble(basePoint.y, secPoint.y);
12475 buf->putExtrusion(extPoint, /*b_R2000_style=*/true);
12476 buf->putThickness(thickness, /*b_R2000_style=*/true);
12477 buf->putRawDouble(oblique);
12478 buf->putRawDouble(angle / ARAD57.29577951308232);
12479 buf->putRawDouble(height);
12480 buf->putRawDouble(widthscale);
12481 dwgBufferW *sb = strBuf ? strBuf : buf;
12482 sb->putVariableText(version, text);
12483 buf->putBitShort(static_cast<std::uint16_t>(textgen));
12484 buf->putBitShort(static_cast<std::uint16_t>(alignH));
12485 buf->putBitShort(static_cast<std::uint16_t>(alignV));
12486
12487 if (version >= DRW::AC1024) {
12488 buf->putRawChar8(hasEmbeddedMText && attVersion == 0 ? 1 : attVersion);
12489 }
12490 if (version >= DRW::AC1032) {
12491 buf->putRawChar8(attributeType);
12492 }
12493
12494 dwgBufferW embeddedHandles;
12495 if (hasEmbeddedMText) {
12496 if (!encodeEmbeddedMTextDwg(version, buf, strBuf, &embeddedHandles, *mtext))
12497 return false;
12498 }
12499
12500 sb->putVariableText(version, tag);
12501 buf->putBitShort(static_cast<std::uint16_t>(m_fieldLength)); // fieldLen BS
12502 buf->putRawChar8(attribFlags);
12503 if (version >= DRW::AC1021) {
12504 buf->putBit(lockPosition ? 1 : 0);
12505 }
12506
12507 // ATTDEF adds prompt between flags and handle stream
12508 sb->putVariableText(version, prompt);
12509
12510 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
12511
12512 dwgHandle sH;
12513 std::uint32_t sref = (styleH.ref == 0) ? 0x13 : styleH.ref;
12514 sH.code = 5;
12515 sH.ref = sref;
12516 sH.size = 0;
12517 if (sref != 0) { std::uint32_t t = sref; while (t != 0) { t >>= 8; ++sH.size; } }
12518 dwgBufferW *hb = handleBuf ? handleBuf : buf;
12519 appendBitBuffer(hb, embeddedHandles);
12520 hb->putHandle(sH);
12521 return buf->isGood() && sb->isGood() && hb->isGood();
12522}
12523
12524bool DRW_MText::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
12525 switch (code) {
12526 case 1:
12527 text += reader->getString();
12528 text = reader->toUtf8String(text);
12529 break;
12530 case 11:
12531 hasXAxisVec = true;
12532 return DRW_Text::parseCode(code, reader);
12533 case 3:
12534 text += reader->getString();
12535 break;
12536 case 44:
12537 interlin = reader->getDouble();
12538 break;
12539 case 50: // djm: per dxf docs, last of code 11 or code 50 prevails
12540 hasXAxisVec = false;
12541 angle = reader->getDouble();
12542 break;
12543 case 73: {
12544 const int value = reader->getInt32();
12545 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
12546 return false;
12547 linespacingStyle = static_cast<std::uint16_t>(value);
12548 break;
12549 }
12550 case 42:
12551 m_r2018ExtentsWidth = reader->getDouble();
12552 break;
12553 case 43:
12554 m_r2018ExtentsHeight = reader->getDouble();
12555 break;
12556 case 45:
12557 m_backgroundScale = reader->getDouble();
12558 break;
12559 case 63:
12560 m_backgroundColor = reader->getInt32();
12561 break;
12562 case 90:
12563 m_backgroundFlags = reader->getInt32();
12564 break;
12565 case 421:
12566 m_backgroundColor = reader->getInt32();
12567 break;
12568 case 441:
12569 m_backgroundTransparency = reader->getInt32();
12570 break;
12571 default:
12572 return DRW_Text::parseCode(code, reader);
12573 }
12574
12575 return true;
12576}
12577
12578void DRW_MText::resetDwgState() {
12579 DRW_Text::resetDwgState();
12580 interlin = 1.0;
12581 linespacingStyle = 1;
12582 m_backgroundFlags = 0;
12583 m_backgroundScale = 0.0;
12584 m_backgroundColor = 0;
12585 m_backgroundTransparency = 0;
12586 hasXAxisVec = false;
12587 m_r2018IsNotAnnotative = false;
12588 m_r2018ReallyLocked = false;
12589 m_r2018Version = 0;
12590 m_r2018DefaultFlag = false;
12591 m_r2018AppIdHandle = 0;
12592 m_r2018Attachment = 0;
12593 m_r2018XAxisDir = {};
12594 m_r2018InsertionPoint = {};
12595 m_r2018RectWidth = 0.0;
12596 m_r2018RectHeight = 0.0;
12597 m_r2018ExtentsHeight = 0.0;
12598 m_r2018ExtentsWidth = 0.0;
12599 m_r2018ColumnType = 0;
12600 m_r2018ColumnCount = 0;
12601 m_r2018ColumnWidth = 0.0;
12602 m_r2018ColumnGutter = 0.0;
12603 m_r2018ColumnAutoHeight = false;
12604 m_r2018ColumnFlowReversed = false;
12605 m_r2018ColumnHeights.clear();
12606 m_r2018AnnotativeData.clear();
12607 m_r2018AnnotativeUnknown = 0;
12608 m_r2018AnnotativeAppHandle = 0;
12609 textgen = 1;
12610 alignV = static_cast<VAlign>(TopLeft);
12611}
12612
12613bool DRW_MText::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
12614 // A reader may reuse an entity carrier while walking a compound stream.
12615 // R2018 context data is version-gated, so clear it before parsing an
12616 // older MTEXT or stale column/annotative state can leak into the result.
12617 resetDwgState();
12618 dwgBuffer *sourceBuf = buf;
12619 auto fail = [this, sourceBuf]() {
12620 if (sourceBuf != nullptr)
12621 sourceBuf->invalidate();
12622 resetDwgState();
12623 return false;
12624 };
12625 if (sourceBuf == nullptr)
12626 return fail();
12627
12628 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
12629 buf = &bodyProbe;
12630
12631 dwgBuffer sBuff = buf->forkIndependent();
12632 dwgBuffer *sBuf = buf;
12633 if (version > DRW::AC1018) {//2007+
12634 sBuf = &sBuff; //separate buffer for strings
12635 }
12636 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
12637 if (!ret)
12638 return fail();
12639 DRW_DBG("\n***************************** parsing mtext *********************************************\n")DRW_dbg::dbg("\n***************************** parsing mtext *********************************************\n"
)
;
12640
12641 const std::uint64_t bodyEndBit = dwgDataEndBit;
12642 std::uint64_t stringStartBit = bodyEndBit;
12643 std::uint64_t stringEndBit = bodyEndBit;
12644 if (version > DRW::AC1018) {
12645 if (!buf->getR2007StringStreamBounds(objSize, stringStartBit,
12646 stringEndBit)
12647 || currentDwgBit(sBuf) > stringEndBit)
12648 return fail();
12649 }
12650
12651 const bool hasStringStream = stringStartBit < stringEndBit;
12652 const auto readText = [&](UTF8STRINGstd::string& value) {
12653 if (version > DRW::AC1018 && !hasStringStream) {
12654 value.clear();
12655 return true;
12656 }
12657 const std::uint64_t endBit = version > DRW::AC1018
12658 ? stringEndBit : bodyEndBit;
12659 return readBoundedVariableText(*sBuf, endBit, version, value);
12660 };
12661
12662 DRW_Coord parsedBasePoint;
12663 DRW_Coord parsedExtrusion;
12664 DRW_Coord parsedXAxisDirection;
12665 double parsedWidthScale = 0.0;
12666 double parsedRectangleHeight = 0.0;
12667 double parsedHeight = 0.0;
12668 std::uint16_t parsedAttachment = 1;
12669 std::uint16_t parsedDrawingDirection = 0;
12670 double ignoredExtentsHeight = 0.0;
12671 double ignoredExtentsWidth = 0.0;
12672 UTF8STRINGstd::string parsedText;
12673 std::uint16_t parsedLineSpacingStyle = 1;
12674 double parsedInterlin = 1.0;
12675 std::int32_t parsedBackgroundFlags = 0;
12676 double parsedBackgroundScale = 0.0;
12677 std::uint32_t parsedBackgroundColor = 0;
12678 std::int32_t parsedBackgroundTransparency = 0;
12679 bool parsedIsNotAnnotative = false;
12680 bool parsedReallyLocked = false;
12681 std::uint16_t parsedR2018Version = 0;
12682 bool parsedDefaultFlag = false;
12683 std::int32_t parsedR2018Attachment = 0;
12684 DRW_Coord parsedR2018XAxisDirection;
12685 DRW_Coord parsedR2018InsertionPoint;
12686 double parsedR2018RectangleWidth = 0.0;
12687 double parsedR2018RectangleHeight = 0.0;
12688 double parsedR2018ExtentsHeight = 0.0;
12689 double parsedR2018ExtentsWidth = 0.0;
12690 std::uint16_t parsedColumnType = 0;
12691 std::int32_t parsedColumnCount = 0;
12692 double parsedColumnWidth = 0.0;
12693 double parsedColumnGutter = 0.0;
12694 bool parsedColumnAutoHeight = false;
12695 bool parsedColumnFlowReversed = false;
12696 std::vector<double> parsedColumnHeights;
12697
12698 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
12699 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtrusion)
12700 || !readBoundedBitCoord(*buf, bodyEndBit, parsedXAxisDirection)
12701 || !readBoundedBitDouble(*buf, bodyEndBit, parsedWidthScale)
12702 || (version > DRW::AC1018
12703 && !readBoundedBitDouble(*buf, bodyEndBit,
12704 parsedRectangleHeight))
12705 || !readBoundedBitDouble(*buf, bodyEndBit, parsedHeight)
12706 || !readBoundedBitShort(*buf, bodyEndBit, parsedAttachment)
12707 || !readBoundedBitShort(*buf, bodyEndBit, parsedDrawingDirection)
12708 || !readBoundedBitDouble(*buf, bodyEndBit, ignoredExtentsHeight)
12709 || !readBoundedBitDouble(*buf, bodyEndBit, ignoredExtentsWidth)
12710 || !readText(parsedText))
12711 return fail();
12712
12713 if (version > DRW::AC1014
12714 && (!readBoundedBitShort(*buf, bodyEndBit, parsedLineSpacingStyle)
12715 || !readBoundedBitDouble(*buf, bodyEndBit, parsedInterlin)))
12716 return fail();
12717 bool ignoredBit = false;
12718 if (version > DRW::AC1014 && !readBoundedBit(*buf, bodyEndBit,
12719 ignoredBit))
12720 return fail();
12721
12722 if (version > DRW::AC1015) {
12723 if (!readBoundedBitLong(*buf, bodyEndBit, parsedBackgroundFlags))
12724 return fail();
12725 const bool hasBackgroundDetails = (parsedBackgroundFlags & 0x01) != 0
12726 || (version >= DRW::AC1032
12727 && (parsedBackgroundFlags & 0x10) != 0);
12728 if (hasBackgroundDetails
12729 && (!readBoundedBitDouble(*buf, bodyEndBit,
12730 parsedBackgroundScale)
12731 || !readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
12732 parsedBackgroundColor, nullptr,
12733 nullptr, nullptr, nullptr,
12734 stringEndBit)
12735 || !readBoundedBitLong(*buf, bodyEndBit,
12736 parsedBackgroundTransparency)))
12737 return fail();
12738 }
12739
12740 bool hasR2018AppId = false;
12741 if (version >= DRW::AC1032) {
12742 if (!readBoundedBit(*buf, bodyEndBit, parsedIsNotAnnotative))
12743 return fail();
12744 if (parsedIsNotAnnotative) {
12745 hasR2018AppId = true;
12746 if (!readBoundedBitShort(*buf, bodyEndBit, parsedR2018Version)
12747 || !readBoundedBit(*buf, bodyEndBit, parsedDefaultFlag)
12748 || !readBoundedBitLong(*buf, bodyEndBit,
12749 parsedR2018Attachment)
12750 || !readBoundedBitCoord(*buf, bodyEndBit,
12751 parsedR2018XAxisDirection)
12752 || !readBoundedBitCoord(*buf, bodyEndBit,
12753 parsedR2018InsertionPoint)
12754 || !readBoundedBitDouble(*buf, bodyEndBit,
12755 parsedR2018RectangleWidth)
12756 || !readBoundedBitDouble(*buf, bodyEndBit,
12757 parsedR2018RectangleHeight)
12758 || !readBoundedBitDouble(*buf, bodyEndBit,
12759 parsedR2018ExtentsHeight)
12760 || !readBoundedBitDouble(*buf, bodyEndBit,
12761 parsedR2018ExtentsWidth)
12762 || !readBoundedBitShort(*buf, bodyEndBit, parsedColumnType)
12763 || parsedColumnType > 2)
12764 return fail();
12765 if (parsedColumnType != 0) {
12766 if (!readBoundedBitLong(*buf, bodyEndBit, parsedColumnCount)
12767 || !isValidCount(parsedColumnCount,
12768 kMaxMTextColumnHeights)
12769 || !readBoundedBitDouble(*buf, bodyEndBit,
12770 parsedColumnWidth)
12771 || !readBoundedBitDouble(*buf, bodyEndBit,
12772 parsedColumnGutter)
12773 || !readBoundedBit(*buf, bodyEndBit,
12774 parsedColumnAutoHeight)
12775 || !readBoundedBit(*buf, bodyEndBit,
12776 parsedColumnFlowReversed))
12777 return fail();
12778 if (!parsedColumnAutoHeight && parsedColumnType == 2
12779 && parsedColumnCount > 0) {
12780 if (!DRW::reserve(parsedColumnHeights,
12781 parsedColumnCount))
12782 return fail();
12783 for (std::int32_t i = 0; i < parsedColumnCount; ++i) {
12784 double columnHeight = 0.0;
12785 if (!readBoundedBitDouble(*buf, bodyEndBit,
12786 columnHeight)
12787 || !std::isfinite(columnHeight))
12788 return fail();
12789 parsedColumnHeights.push_back(columnHeight);
12790 }
12791 }
12792 }
12793 }
12794 }
12795
12796 const auto finite = [](const DRW_Coord& point) {
12797 return std::isfinite(point.x) && std::isfinite(point.y)
12798 && std::isfinite(point.z);
12799 };
12800 if (!buf->isGood() || !sBuf->isGood()
12801 || !finite(parsedBasePoint) || !finite(parsedExtrusion)
12802 || !finite(parsedXAxisDirection)
12803 || !std::isfinite(parsedWidthScale)
12804 || !std::isfinite(parsedRectangleHeight)
12805 || !std::isfinite(parsedHeight)
12806 || !std::isfinite(ignoredExtentsHeight)
12807 || !std::isfinite(ignoredExtentsWidth)
12808 || !std::isfinite(parsedInterlin)
12809 || !finite(parsedR2018XAxisDirection)
12810 || !finite(parsedR2018InsertionPoint)
12811 || !std::isfinite(parsedR2018RectangleWidth)
12812 || !std::isfinite(parsedR2018RectangleHeight)
12813 || !std::isfinite(parsedR2018ExtentsHeight)
12814 || !std::isfinite(parsedR2018ExtentsWidth))
12815 return fail();
12816
12817 std::uint64_t handleEndBit = 0;
12818 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
12819 handleEndBit))
12820 return fail();
12821 dwgBuffer handleProbe = buf->forkIndependent();
12822 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
12823 handleEndBit))
12824 return fail();
12825
12826 dwgHandle parsedStyleH;
12827 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
12828 parsedStyleH))
12829 return fail();
12830 dwgHandle parsedAppIdH;
12831 if (hasR2018AppId
12832 && !readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
12833 parsedAppIdH))
12834 return fail();
12835 if (!handleProbe.isGood() || !sBuf->isGood())
12836 return fail();
12837
12838 *sourceBuf = handleProbe;
12839 basePoint = parsedBasePoint;
12840 extPoint = parsedExtrusion;
12841 secPoint = parsedXAxisDirection;
12842 hasXAxisVec = true;
12843 widthscale = parsedWidthScale;
12844 height = parsedHeight;
12845 angle = atan2(secPoint.y, secPoint.x) * ARAD57.29577951308232;
12846 textgen = parsedAttachment;
12847 alignH = static_cast<HAlign>(parsedDrawingDirection);
12848 text = std::move(parsedText);
12849 linespacingStyle = parsedLineSpacingStyle;
12850 interlin = parsedInterlin;
12851 m_backgroundFlags = parsedBackgroundFlags;
12852 m_backgroundScale = parsedBackgroundScale;
12853 m_backgroundColor = static_cast<int>(parsedBackgroundColor);
12854 m_backgroundTransparency = parsedBackgroundTransparency;
12855 m_r2018IsNotAnnotative = parsedIsNotAnnotative;
12856 m_r2018Version = parsedR2018Version;
12857 m_r2018DefaultFlag = parsedDefaultFlag;
12858 m_r2018AppIdHandle = hasR2018AppId ? parsedAppIdH.ref : 0;
12859 m_r2018Attachment = parsedR2018Attachment;
12860 m_r2018XAxisDir = parsedR2018XAxisDirection;
12861 m_r2018InsertionPoint = parsedR2018InsertionPoint;
12862 m_r2018RectWidth = parsedR2018RectangleWidth;
12863 m_r2018RectHeight = parsedR2018RectangleHeight;
12864 m_r2018ExtentsHeight = parsedR2018ExtentsHeight;
12865 m_r2018ExtentsWidth = parsedR2018ExtentsWidth;
12866 m_r2018ColumnType = parsedColumnType;
12867 m_r2018ColumnCount = parsedColumnCount;
12868 m_r2018ColumnWidth = parsedColumnWidth;
12869 m_r2018ColumnGutter = parsedColumnGutter;
12870 m_r2018ColumnAutoHeight = parsedColumnAutoHeight;
12871 m_r2018ColumnFlowReversed = parsedColumnFlowReversed;
12872 m_r2018ColumnHeights = std::move(parsedColumnHeights);
12873 styleH = parsedStyleH;
12874 DRW_DBG("text style Handle: ")DRW_dbg::dbg("text style Handle: "); DRW_DBG(styleH.code)DRW_dbg::dbg(styleH.code); DRW_DBG(".")DRW_dbg::dbg(".");
12875 DRW_DBG(styleH.size)DRW_dbg::dbg(styleH.size); DRW_DBG(".")DRW_dbg::dbg("."); DRW_DBG(styleH.ref)DRW_dbg::dbg(styleH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
12876 return true;
12877}
12878
12879void DRW_MText::updateAngle() {
12880 if (hasXAxisVec) {
12881 angle = atan2(secPoint.y, secPoint.x) * ARAD57.29577951308232;
12882 }
12883}
12884
12885bool DRW_Polyline::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
12886 switch (code) {
12887 case 70:
12888 flags = reader->getInt32();
12889 break;
12890 case 40:
12891 defstawidth = reader->getDouble();
12892 break;
12893 case 41:
12894 defendwidth = reader->getDouble();
12895 break;
12896 case 71:
12897 vertexcount = reader->getInt32();
12898 break;
12899 case 72:
12900 facecount = reader->getInt32();
12901 break;
12902 case 73:
12903 smoothM = reader->getInt32();
12904 break;
12905 case 74:
12906 smoothN = reader->getInt32();
12907 break;
12908 case 75:
12909 curvetype = reader->getInt32();
12910 break;
12911 default:
12912 return DRW_Point::parseCode(code, reader);
12913 }
12914
12915 return true;
12916}
12917
12918//0x0F polyline 2D bit 4(8) & 5(16) NOT set
12919//0x10 polyline 3D bit 4(8) set
12920//0x1D PFACE bit 5(16) set
12921void DRW_Polyline::resetDwgState() {
12922 DRW_Point::resetDwgState();
12923 flags = 0;
12924 defstawidth = 0.0;
12925 defendwidth = 0.0;
12926 vertexcount = 0;
12927 facecount = 0;
12928 smoothM = 0;
12929 smoothN = 0;
12930 curvetype = 0;
12931 vertlist.clear();
12932 hadlesList.clear();
12933 firstEH = 0;
12934 lastEH = 0;
12935 seqEndH = dwgHandle{};
12936}
12937
12938bool DRW_Polyline::validatePayloadFields() const {
12939 const auto finite = [](const DRW_Coord& point) {
12940 return std::isfinite(point.x) && std::isfinite(point.y)
12941 && std::isfinite(point.z);
12942 };
12943 const auto validBitShort = [](int value) {
12944 return value >= 0
12945 && value <= std::numeric_limits<std::uint16_t>::max();
12946 };
12947 return flags >= 0 && flags <= std::numeric_limits<std::uint8_t>::max()
12948 && validBitShort(curvetype) && validBitShort(vertexcount)
12949 && validBitShort(facecount) && validBitShort(smoothM)
12950 && validBitShort(smoothN) && finite(basePoint)
12951 && finite(extPoint) && std::isfinite(defstawidth)
12952 && std::isfinite(defendwidth) && std::isfinite(thickness)
12953 && vertlist.size() <= dwgSafety::MaxOwnedObjectCount
12954 && vertlist.size()
12955 <= static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())
12956 && std::all_of(vertlist.cbegin(), vertlist.cend(),
12957 [](const auto& vertex) { return vertex != nullptr; });
12958}
12959
12960bool DRW_Polyline::isDwgVertexCompatible(
12961 const DRW_Vertex& vertex) const noexcept {
12962 if ((flags & 64) != 0) {
12963 return vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::Polyface
12964 || vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::PolyfaceFace;
12965 }
12966 if ((flags & 16) != 0)
12967 return vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::Mesh;
12968 if ((flags & 8) != 0)
12969 return vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::Vertex3D;
12970 return vertex.dwgSubtype() == DRW_Vertex::DwgSubtype::Vertex2D;
12971}
12972
12973bool DRW_Polyline::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
12974 resetDwgState();
12975 dwgBuffer *sourceBuf = buf;
12976 auto fail = [this, sourceBuf]() {
12977 if (sourceBuf != nullptr)
12978 sourceBuf->invalidate();
12979 resetDwgState();
12980 return false;
12981 };
12982
12983 if (sourceBuf == nullptr)
12984 return fail();
12985
12986 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
12987 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
12988 || !bodyProbe.isGood())
12989 return fail();
12990 DRW_DBG("\n***************************** parsing polyline *********************************************\n")DRW_dbg::dbg("\n***************************** parsing polyline *********************************************\n"
)
;
12991
12992 const std::uint64_t bodyEndBit = dwgDataEndBit;
12993 int parsedFlags = 0;
12994 double parsedDefStartWidth = 0.0;
12995 double parsedDefEndWidth = 0.0;
12996 int parsedVertexCount = 0;
12997 int parsedFaceCount = 0;
12998 int parsedSmoothM = 0;
12999 int parsedSmoothN = 0;
13000 int parsedCurveType = 0;
13001 double parsedThickness = 0.0;
13002 DRW_Coord parsedBasePoint;
13003 DRW_Coord parsedExtrusion;
13004 std::int32_t ooCount = 0;
13005 if (oType == 0x0F) { //pline 2D
13006 std::uint16_t value = 0;
13007 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13008 return fail();
13009 parsedFlags = value;
13010 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13011 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13012 return fail();
13013 parsedCurveType = value;
13014 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, parsedDefStartWidth)
13015 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedDefEndWidth)
13016 || !readBoundedThickness(bodyProbe, bodyEndBit,
13017 version > DRW::AC1014, parsedThickness)
13018 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedBasePoint.z)
13019 || !readBoundedExtrusion(bodyProbe, bodyEndBit,
13020 version > DRW::AC1014, parsedExtrusion))
13021 return fail();
13022 } else if (oType == 0x10) { //pline 3D
13023 std::uint8_t tmpFlag = 0;
13024 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, tmpFlag))
13025 return fail();
13026 DRW_DBG("flags 1 value: ")DRW_dbg::dbg("flags 1 value: "); DRW_DBG(tmpFlag)DRW_dbg::dbg(tmpFlag);
13027 if (tmpFlag & 1)
13028 parsedCurveType = 5; // quadratic B-spline
13029 else if (tmpFlag & 2)
13030 parsedCurveType = 6; // cubic B-spline
13031 if (tmpFlag & 3)
13032 parsedFlags |= 4; // splined (bit 2); do NOT overwrite curvetype to 8
13033 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, tmpFlag))
13034 return fail();
13035 if (tmpFlag & 1)
13036 parsedFlags |= 1;
13037 parsedFlags |= 8; //indicate 3DPOL
13038 DRW_DBG("flags 2 value: ")DRW_dbg::dbg("flags 2 value: "); DRW_DBG(tmpFlag)DRW_dbg::dbg(tmpFlag);
13039 } else if (oType == 0x1D) { //PFACE
13040 std::uint16_t vertexValue = 0;
13041 std::uint16_t faceValue = 0;
13042 if (!readBoundedBitShort(bodyProbe, bodyEndBit, vertexValue)
13043 || !readBoundedBitShort(bodyProbe, bodyEndBit, faceValue))
13044 return fail();
13045 parsedFlags = 64;
13046 parsedVertexCount = vertexValue;
13047 parsedFaceCount = faceValue;
13048 DRW_DBG("vertex count: ")DRW_dbg::dbg("vertex count: "); DRW_DBG(parsedVertexCount)DRW_dbg::dbg(parsedVertexCount);
13049 DRW_DBG("face count: ")DRW_dbg::dbg("face count: "); DRW_DBG(parsedFaceCount)DRW_dbg::dbg(parsedFaceCount);
13050 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13051 } else if (oType == 0x1E) { //POLYLINE_MESH per ODA spec sec 19.4.31
13052 std::uint16_t value = 0;
13053 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13054 return fail();
13055 parsedFlags = value | 16; //bit 4 = 3D polygon mesh
13056 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13057 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13058 return fail();
13059 parsedCurveType = value;
13060 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13061 return fail();
13062 parsedVertexCount = value; //M-count
13063 DRW_DBG(" M count: ")DRW_dbg::dbg(" M count: "); DRW_DBG(parsedVertexCount)DRW_dbg::dbg(parsedVertexCount);
13064 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13065 return fail();
13066 parsedFaceCount = value; //N-count
13067 DRW_DBG(" N count: ")DRW_dbg::dbg(" N count: "); DRW_DBG(parsedFaceCount)DRW_dbg::dbg(parsedFaceCount);
13068 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13069 return fail();
13070 parsedSmoothM = value; //M smooth-surface density, DXF 73
13071 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13072 return fail();
13073 parsedSmoothN = value; //N smooth-surface density, DXF 74
13074 DRW_DBG(" M/N density: ")DRW_dbg::dbg(" M/N density: "); DRW_DBG(parsedSmoothM)DRW_dbg::dbg(parsedSmoothM); DRW_DBG("/")DRW_dbg::dbg("/"); DRW_DBG(parsedSmoothN)DRW_dbg::dbg(parsedSmoothN);
13075 } else {
13076 return fail();
13077 }
13078 if (version > DRW::AC1015){ //2004+
13079 if (!readBoundedBitLong(bodyProbe, bodyEndBit, ooCount)
13080 || !dwgSafety::validOwnedObjectCount(
13081 ooCount, bodyProbe.numRemainingBytes())) {
13082 DRW_DBG("\nWARNING: Invalid POLYLINE owned-object count\n")DRW_dbg::dbg("\nWARNING: Invalid POLYLINE owned-object count\n"
)
;
13083 return fail();
13084 }
13085 }
13086
13087 std::uint64_t handleEndBit = 0;
13088 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
13089 handleEndBit))
13090 return fail();
13091 dwgBuffer handleProbe = bodyProbe.forkIndependent();
13092 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
13093 handleEndBit)
13094 || !handleProbe.isGood())
13095 return fail();
13096
13097 std::list<std::uint32_t> parsedHandles;
13098 std::uint32_t parsedFirstEH = 0;
13099 std::uint32_t parsedLastEH = 0;
13100
13101 if (version < DRW::AC1018){ //2000-
13102 dwgHandle objectH;
13103 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
13104 objectH))
13105 return fail();
13106 parsedFirstEH = objectH.ref;
13107 DRW_DBG(" first Vertex Handle: ")DRW_dbg::dbg(" first Vertex Handle: "); DRW_DBGHL(objectH.code, objectH.size, objectH.ref)DRW_dbg::dbgHL(objectH.code, objectH.size, objectH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
13108 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
13109 objectH))
13110 return fail();
13111 parsedLastEH = objectH.ref;
13112 DRW_DBG(" last Vertex Handle: ")DRW_dbg::dbg(" last Vertex Handle: "); DRW_DBGHL(objectH.code, objectH.size, objectH.ref)DRW_dbg::dbgHL(objectH.code, objectH.size, objectH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
13113 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
13114 } else {
13115 for (std::int32_t i = 0; i < ooCount; ++i){
13116 dwgHandle objectH;
13117 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle,
13118 true, objectH))
13119 return fail();
13120 try {
13121 parsedHandles.push_back(objectH.ref);
13122 } catch (...) {
13123 return fail();
13124 }
13125 DRW_DBG(" Vertex Handle: ")DRW_dbg::dbg(" Vertex Handle: "); DRW_DBGHL(objectH.code, objectH.size, objectH.ref)DRW_dbg::dbgHL(objectH.code, objectH.size, objectH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
13126 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
13127 }
13128 }
13129 dwgHandle parsedSeqEndH;
13130 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
13131 parsedSeqEndH))
13132 return fail();
13133 if (!handleProbe.isGood())
13134 return fail();
13135 *sourceBuf = handleProbe;
13136 flags = parsedFlags;
13137 defstawidth = parsedDefStartWidth;
13138 defendwidth = parsedDefEndWidth;
13139 vertexcount = parsedVertexCount;
13140 facecount = parsedFaceCount;
13141 smoothM = parsedSmoothM;
13142 smoothN = parsedSmoothN;
13143 curvetype = parsedCurveType;
13144 thickness = parsedThickness;
13145 basePoint = parsedBasePoint;
13146 extPoint = parsedExtrusion;
13147 firstEH = parsedFirstEH;
13148 lastEH = parsedLastEH;
13149 seqEndH = parsedSeqEndH;
13150 hadlesList = std::move(parsedHandles);
13151 DRW_DBG(" SEQEND Handle: ")DRW_dbg::dbg(" SEQEND Handle: "); DRW_DBGHL(seqEndH.code, seqEndH.size, seqEndH.ref)DRW_dbg::dbgHL(seqEndH.code, seqEndH.size, seqEndH.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
13152 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
13153
13154// RS crc; //RS */
13155 return buf->isGood();
13156}
13157
13158bool DRW_Vertex::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
13159 switch (code) {
13160 case 70:
13161 flags = reader->getInt32();
13162 break;
13163 case 40:
13164 stawidth = reader->getDouble();
13165 break;
13166 case 41:
13167 endwidth = reader->getDouble();
13168 break;
13169 case 42:
13170 bulge = reader->getDouble();
13171 break;
13172 case 50:
13173 tgdir = reader->getDouble();
13174 break;
13175 case 71:
13176 vindex1 = reader->getInt32();
13177 break;
13178 case 72:
13179 vindex2 = reader->getInt32();
13180 break;
13181 case 73:
13182 vindex3 = reader->getInt32();
13183 break;
13184 case 74:
13185 vindex4 = reader->getInt32();
13186 break;
13187 case 91:
13188 identifier = reader->getInt32();
13189 break;
13190 default:
13191 return DRW_Point::parseCode(code, reader);
13192 }
13193
13194 return true;
13195}
13196
13197//0x0A vertex 2D
13198//0x0B vertex 3D
13199//0x0C MESH
13200//0x0D PFACE
13201//0x0E PFACE FACE
13202void DRW_Vertex::resetDwgState() {
13203 DRW_Point::resetDwgState();
13204 stawidth = 0.0;
13205 endwidth = 0.0;
13206 bulge = 0.0;
13207 flags = 0;
13208 tgdir = 0.0;
13209 vindex1 = vindex2 = vindex3 = vindex4 = 0;
13210 identifier = 0;
13211 m_dwgSubtype = DwgSubtype::Auto;
13212}
13213
13214bool DRW_Vertex::validatePayloadFields() const {
13215 const auto finite = [](const DRW_Coord& point) {
13216 return std::isfinite(point.x) && std::isfinite(point.y)
13217 && std::isfinite(point.z);
13218 };
13219 const auto validFaceIndex = [](int value) {
13220 return value >= std::numeric_limits<std::int16_t>::min()
13221 && value <= std::numeric_limits<std::int16_t>::max();
13222 };
13223 const auto validInt32 = [](int value) {
13224 return value >= std::numeric_limits<std::int32_t>::min()
13225 && value <= std::numeric_limits<std::int32_t>::max();
13226 };
13227 return flags >= 0 && flags <= std::numeric_limits<std::uint8_t>::max()
13228 && validFaceIndex(vindex1) && validFaceIndex(vindex2)
13229 && validFaceIndex(vindex3) && validFaceIndex(vindex4)
13230 && validInt32(identifier) && finite(basePoint)
13231 && std::isfinite(stawidth) && std::isfinite(endwidth)
13232 && std::isfinite(bulge) && std::isfinite(tgdir);
13233}
13234
13235bool DRW_Vertex::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs, double el){
13236 resetDwgState();
13237 dwgBuffer *sourceBuf = buf;
13238 auto fail = [this, sourceBuf]() {
13239 if (sourceBuf != nullptr)
13240 sourceBuf->invalidate();
13241 resetDwgState();
13242 return false;
13243 };
13244
13245 if (sourceBuf == nullptr)
13246 return fail();
13247
13248 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
13249 if (!DRW_Entity::parseDwgCommon(version, &bodyProbe, bs)
13250 || !bodyProbe.isGood())
13251 return fail();
13252 DRW_DBG("\n***************************** parsing pline Vertex *********************************************\n")DRW_dbg::dbg("\n***************************** parsing pline Vertex *********************************************\n"
)
;
13253
13254 const std::uint64_t bodyEndBit = dwgDataEndBit;
13255 DRW_Vertex::DwgSubtype parsedSubtype = DRW_Vertex::DwgSubtype::Auto;
13256 std::uint8_t parsedFlags = 0;
13257 DRW_Coord parsedBasePoint;
13258 double parsedStartWidth = 0.0;
13259 double parsedEndWidth = 0.0;
13260 double parsedBulge = 0.0;
13261 double parsedTangentDirection = 0.0;
13262 int parsedIndex1 = 0;
13263 int parsedIndex2 = 0;
13264 int parsedIndex3 = 0;
13265 int parsedIndex4 = 0;
13266 int parsedIdentifier = 0;
13267 if (oType == 0x0A) { //pline 2D, needed example
13268 parsedSubtype = DwgSubtype::Vertex2D;
13269 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, parsedFlags)
13270 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint)
13271 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedStartWidth))
13272 return fail();
13273 parsedBasePoint.z = el;
13274 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13275 DRW_DBG("basePoint: ")DRW_dbg::dbg("basePoint: "); DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
13276 if (parsedStartWidth < 0.0) {
13277 parsedEndWidth = parsedStartWidth = std::fabs(parsedStartWidth);
13278 } else if (!readBoundedBitDouble(bodyProbe, bodyEndBit,
13279 parsedEndWidth)) {
13280 return fail();
13281 }
13282 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, parsedBulge))
13283 return fail();
13284 if (version > DRW::AC1021) { //2010+
13285 if (!readBoundedBitLong(bodyProbe, bodyEndBit, parsedIdentifier))
13286 return fail();
13287 DRW_DBG("Vertex ID: ")DRW_dbg::dbg("Vertex ID: "); DRW_DBG(parsedIdentifier)DRW_dbg::dbg(parsedIdentifier);
13288 }
13289 if (!readBoundedBitDouble(bodyProbe, bodyEndBit,
13290 parsedTangentDirection))
13291 return fail();
13292 } else if (oType == 0x0B || oType == 0x0C || oType == 0x0D) { //PFACE
13293 if (oType == 0x0B)
13294 parsedSubtype = DwgSubtype::Vertex3D;
13295 else if (oType == 0x0C)
13296 parsedSubtype = DwgSubtype::Mesh;
13297 else
13298 parsedSubtype = DwgSubtype::Polyface;
13299 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, parsedFlags)
13300 || !readBoundedBitCoord(bodyProbe, bodyEndBit, parsedBasePoint))
13301 return fail();
13302 DRW_DBG("flags value: ")DRW_dbg::dbg("flags value: "); DRW_DBG(parsedFlags)DRW_dbg::dbg(parsedFlags);
13303 DRW_DBG("basePoint: ")DRW_dbg::dbg("basePoint: "); DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
13304 } else if (oType == 0x0E) { //PFACE FACE
13305 parsedSubtype = DwgSubtype::PolyfaceFace;
13306 auto signedIndex = [](std::uint16_t value) {
13307 return value > 32767 ? value - 65536 : value;
13308 };
13309 std::uint16_t value = 0;
13310 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13311 return fail();
13312 parsedIndex1 = signedIndex(value);
13313 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13314 return fail();
13315 parsedIndex2 = signedIndex(value);
13316 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13317 return fail();
13318 parsedIndex3 = signedIndex(value);
13319 if (!readBoundedBitShort(bodyProbe, bodyEndBit, value))
13320 return fail();
13321 parsedIndex4 = signedIndex(value);
13322 } else {
13323 return fail();
13324 }
13325
13326 const auto finite = [](const DRW_Coord& point) {
13327 return std::isfinite(point.x) && std::isfinite(point.y)
13328 && std::isfinite(point.z);
13329 };
13330 if (!finite(parsedBasePoint) || !std::isfinite(parsedStartWidth)
13331 || !std::isfinite(parsedEndWidth) || !std::isfinite(parsedBulge)
13332 || !std::isfinite(parsedTangentDirection)
13333 || !std::isfinite(el))
13334 return fail();
13335
13336 std::uint64_t handleEndBit = 0;
13337 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
13338 handleEndBit))
13339 return fail();
13340 dwgBuffer handleProbe = bodyProbe.forkIndependent();
13341 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
13342 handleEndBit)
13343 || !handleProbe.isGood())
13344 return fail();
13345 // RS crc; //RS */
13346 *sourceBuf = handleProbe;
13347 m_dwgSubtype = parsedSubtype;
13348 flags = parsedFlags;
13349 basePoint = parsedBasePoint;
13350 stawidth = parsedStartWidth;
13351 endwidth = parsedEndWidth;
13352 bulge = parsedBulge;
13353 tgdir = parsedTangentDirection;
13354 vindex1 = parsedIndex1;
13355 vindex2 = parsedIndex2;
13356 vindex3 = parsedIndex3;
13357 vindex4 = parsedIndex4;
13358 identifier = parsedIdentifier;
13359 return true;
13360}
13361
13362bool DRW_Hatch::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
13363 try {
13364 switch (code) {
13365 case 2:
13366 name = reader->getUtf8String();
13367 break;
13368 case 70:
13369 solid = reader->getInt32();
13370 break;
13371 case 71:
13372 associative = reader->getInt32();
13373 break;
13374 case 72: { /*edge type*/
13375 if (ispol){ // polyline path: 72 is the has-bulge flag. Do NOT fold it
13376 // into pline->flags — bit 0 there is the *closed* flag (set by code
13377 // 73), and the per-vertex bulges arrive via code 42 regardless. Some
13378 // writers (e.g. ezdxf MPOLYGON) emit 73 before 72; the old code let
13379 // 72 clear the closed bit 73 had just set, leaving the boundary open
13380 // so RS_Hatch::validate() rejected the area.
13381 break;
13382 }
13383 if (spline && !validateCurrentSplineDxf())
13384 return false;
13385 if (!loop
13386 || loop->objlist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13387 || (m_dxfLoopEdgeCountExpected >= 0
13388 && loop->objlist.size()
13389 >= static_cast<std::size_t>(m_dxfLoopEdgeCountExpected)))
13390 return false;
13391 const int edgeType = reader->getInt32();
13392 if (edgeType == 1){ //line
13393 addLine();
13394 } else if (edgeType == 2){ //arc
13395 addArc();
13396 } else if (edgeType == 3){ //elliptic arc
13397 addEllipse();
13398 } else if (edgeType == 4){ //spline
13399 addSpline();
13400 } else {
13401 return false;
13402 }
13403 break;
13404 }
13405 case 10:
13406 // Spline edge: 10 is a control-point x-coord.
13407 if (spline) {
13408 if (spline->controllist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13409 || (m_dxfSplineControlCountExpected >= 0
13410 && spline->controllist.size()
13411 >= static_cast<std::size_t>(m_dxfSplineControlCountExpected)))
13412 return false;
13413 spline->controllist.push_back(std::make_shared<DRW_Coord>(reader->getDouble(), 0.0, 0.0));
13414 break;
13415 }
13416 if (pt) pt->basePoint.x = reader->getDouble();
13417 else if (pline) {
13418 if (pline->vertlist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13419 || (m_dxfPolylineVertexCountExpected >= 0
13420 && pline->vertlist.size()
13421 >= static_cast<std::size_t>(m_dxfPolylineVertexCountExpected)))
13422 return false;
13423 plvert = pline->addVertex();
13424 plvert->x = reader->getDouble();
13425 } else if (m_dxfSeedPointsExpected < 0) {
13426 return DRW_Point::parseCode(code, reader);
13427 } else {
13428 // After group 98 the boundary path is closed; seed-point
13429 // coords arrive as group-10/20 pairs.
13430 if (seedPoints.size() >= static_cast<std::size_t>(m_dxfSeedPointsExpected))
13431 return false;
13432 DRW_Coord seed;
13433 seed.x = reader->getDouble();
13434 seedPoints.push_back(seed);
13435 }
13436 break;
13437 case 20:
13438 if (spline && !spline->controllist.empty()) {
13439 spline->controllist.back()->y = reader->getDouble();
13440 break;
13441 }
13442 if (pt) pt->basePoint.y = reader->getDouble();
13443 else if (plvert) plvert ->y = reader->getDouble();
13444 else if (m_dxfSeedPointsExpected >= 0) {
13445 if (seedPoints.empty())
13446 return false;
13447 seedPoints.back().y = reader->getDouble();
13448 } else {
13449 return DRW_Point::parseCode(code, reader);
13450 }
13451 break;
13452 case 11:
13453 // Spline edge: 11 is a fit-point x-coord.
13454 if (spline) {
13455 if (spline->fitlist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13456 || (m_dxfSplineFitCountExpected >= 0
13457 && spline->fitlist.size()
13458 >= static_cast<std::size_t>(m_dxfSplineFitCountExpected)))
13459 return false;
13460 spline->fitlist.push_back(std::make_shared<DRW_Coord>(reader->getDouble(), 0.0, 0.0));
13461 break;
13462 }
13463 if (line) line->secPoint.x = reader->getDouble();
13464 else if (ellipse) ellipse->secPoint.x = reader->getDouble();
13465 break;
13466 case 21:
13467 if (spline && !spline->fitlist.empty()) {
13468 spline->fitlist.back()->y = reader->getDouble();
13469 break;
13470 }
13471 if (line) line->secPoint.y = reader->getDouble();
13472 else if (ellipse) ellipse->secPoint.y = reader->getDouble();
13473 break;
13474 case 12:
13475 if (spline) { spline->tgStart.x = reader->getDouble(); break; }
13476 break;
13477 case 22:
13478 if (spline) { spline->tgStart.y = reader->getDouble(); break; }
13479 break;
13480 case 13:
13481 if (spline) { spline->tgEnd.x = reader->getDouble(); break; }
13482 break;
13483 case 23:
13484 if (spline) { spline->tgEnd.y = reader->getDouble(); break; }
13485 break;
13486 case 40:
13487 // Spline edge: 40 is a knot value (occurs nknots times).
13488 if (spline) {
13489 if (spline->knotslist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13490 || (m_dxfSplineKnotCountExpected >= 0
13491 && spline->knotslist.size()
13492 >= static_cast<std::size_t>(m_dxfSplineKnotCountExpected)))
13493 return false;
13494 spline->knotslist.push_back(reader->getDouble());
13495 break;
13496 }
13497 if (arc) arc->radious = reader->getDouble();
13498 else if (ellipse) ellipse->ratio = reader->getDouble();
13499 break;
13500 case 41:
13501 scale = reader->getDouble();
13502 break;
13503 case 42:
13504 // Spline edge: 42 is a per-control-point weight.
13505 if (spline) {
13506 if (spline->weightlist.size() >= static_cast<std::size_t>(kMaxHatchItems)
13507 || (m_dxfSplineControlCountExpected >= 0
13508 && spline->weightlist.size()
13509 >= static_cast<std::size_t>(m_dxfSplineControlCountExpected)))
13510 return false;
13511 spline->weightlist.push_back(reader->getDouble());
13512 break;
13513 }
13514 if (plvert) plvert ->bulge = reader->getDouble();
13515 break;
13516 case 50:
13517 if (arc) arc->staangle = reader->getDouble()/ARAD57.29577951308232;
13518 else if (ellipse) ellipse->staparam = reader->getDouble()/ARAD57.29577951308232;
13519 break;
13520 case 51:
13521 if (arc) arc->endangle = reader->getDouble()/ARAD57.29577951308232;
13522 else if (ellipse) ellipse->endparam = reader->getDouble()/ARAD57.29577951308232;
13523 break;
13524 case 47:
13525 pixelSize = reader->getDouble();
13526 break;
13527 case 52:
13528 angle = reader->getDouble();
13529 break;
13530 case 53: // pattern line angle — starts a new PatternLine record
13531 if (!validateCurrentPatternLineDxf())
13532 return false;
13533 if (m_dxfPatternLineCountExpected >= 0
13534 && patternLines.size() >= static_cast<std::size_t>(m_dxfPatternLineCountExpected))
13535 return false;
13536 if (patternLines.size() >= static_cast<std::size_t>(kMaxHatchItems))
13537 return false;
13538 patternLines.push_back(PatternLine());
13539 patternLines.back().angle = reader->getDouble();
13540 m_dxfPatternDashCountExpected = -1;
13541 m_dxfPatternDashCountSeen = false;
13542 break;
13543 case 43:
13544 if (!patternLines.empty()) patternLines.back().baseX = reader->getDouble();
13545 break;
13546 case 44:
13547 if (!patternLines.empty()) patternLines.back().baseY = reader->getDouble();
13548 break;
13549 case 45:
13550 if (!patternLines.empty()) patternLines.back().offsetX = reader->getDouble();
13551 break;
13552 case 46:
13553 if (!patternLines.empty()) patternLines.back().offsetY = reader->getDouble();
13554 break;
13555 case 79: // dash count — the 49s that follow will accumulate
13556 if (patternLines.empty() || m_dxfPatternDashCountSeen)
13557 return false;
13558 m_dxfPatternDashCountExpected = reader->getInt32();
13559 if (!isValidCount(m_dxfPatternDashCountExpected, kMaxHatchItems))
13560 return false;
13561 m_dxfPatternDashCountSeen = true;
13562 return DRW::reserve(patternLines.back().dashList, m_dxfPatternDashCountExpected);
13563 case 49:
13564 if (patternLines.empty()
13565 || patternLines.back().dashList.size() >= static_cast<std::size_t>(kMaxHatchItems)
13566 || (m_dxfPatternDashCountExpected >= 0
13567 && patternLines.back().dashList.size()
13568 >= static_cast<std::size_t>(m_dxfPatternDashCountExpected)))
13569 return false;
13570 patternLines.back().dashList.push_back(reader->getDouble());
13571 break;
13572 case 73:
13573 // Spline edge: 73 is the rational flag (1 = rational).
13574 if (spline) {
13575 if (reader->getInt32()) spline->flags |= 0x4;
13576 break;
13577 }
13578 if (arc) arc->isccw = reader->getInt32();
13579 // polyline path: 73 is the is-closed flag -> set bit 0 only, leaving the
13580 // rest of pline->flags untouched (order-independent vs code 72).
13581 else if (pline) pline->flags = (pline->flags & ~1) | (reader->getInt32() ? 1 : 0);
13582 break;
13583 case 74:
13584 // Spline edge: 74 is the periodic flag (1 = periodic/closed).
13585 if (spline) {
13586 if (reader->getInt32()) spline->flags |= 0x2;
13587 }
13588 break;
13589 case 94:
13590 // Spline edge degree.
13591 if (spline) {
13592 spline->degree = reader->getInt32();
13593 return isValidCount(spline->degree, kMaxSplineDegree)
13594 && spline->degree != 0;
13595 }
13596 break;
13597 case 95:
13598 // Spline edge number of knots.
13599 if (spline) {
13600 if (m_dxfSplineKnotCountExpected >= 0)
13601 return false;
13602 spline->nknots = reader->getInt32();
13603 if (!isValidCount(spline->nknots, kMaxHatchItems))
13604 return false;
13605 m_dxfSplineKnotCountExpected = spline->nknots;
13606 return DRW::reserve(spline->knotslist, spline->nknots);
13607 }
13608 break;
13609 case 96:
13610 // Spline edge number of control points.
13611 if (spline) {
13612 if (m_dxfSplineControlCountExpected >= 0)
13613 return false;
13614 spline->ncontrol = reader->getInt32();
13615 if (!isValidCount(spline->ncontrol, kMaxHatchItems))
13616 return false;
13617 m_dxfSplineControlCountExpected = spline->ncontrol;
13618 return DRW::reserve(spline->controllist, spline->ncontrol);
13619 }
13620 break;
13621 case 97:
13622 if (spline) {
13623 if (!m_splineNfitSet) {
13624 // First 97 in this spline edge = fit-point count (nfit).
13625 spline->nfit = reader->getInt32();
13626 if (!isValidCount(spline->nfit, kMaxHatchItems))
13627 return false;
13628 if (m_dxfSplineFitCountExpected >= 0)
13629 return false;
13630 m_dxfSplineFitCountExpected = spline->nfit;
13631 if (!DRW::reserve(spline->fitlist, spline->nfit))
13632 return false;
13633 if (spline->nfit == 0) {
13634 if (!validateCurrentSplineDxf())
13635 return false;
13636 // No fit points or tangents follow; safe to clear spline
13637 // so the next code-97 (loop boundary count) is not
13638 // misinterpreted as another nfit.
13639 spline.reset();
13640 } else {
13641 m_splineNfitSet = true;
13642 }
13643 } else {
13644 // Second 97 while spline is active = loop boundary handle count.
13645 if (!validateCurrentSplineDxf())
13646 return false;
13647 spline.reset();
13648 m_splineNfitSet = false;
13649 m_boundaryHandleCount = reader->getInt32();
13650 if (!isValidCount(m_boundaryHandleCount, kMaxHatchItems))
13651 return false;
13652 if (m_dxfBoundaryHandleCountSeen)
13653 return false;
13654 m_dxfBoundaryHandleCountSeen = true;
13655 if (m_boundaryHandleCount > 0 && loop
13656 && !DRW::reserve(loop->m_boundaryHandles, m_boundaryHandleCount))
13657 return false;
13658 }
13659 break;
13660 }
13661 // No active spline: this is the loop boundary handle count.
13662 m_splineNfitSet = false;
13663 if (m_dxfBoundaryHandleCountSeen)
13664 return false;
13665 m_boundaryHandleCount = reader->getInt32();
13666 if (!isValidCount(m_boundaryHandleCount, kMaxHatchItems))
13667 return false;
13668 m_dxfBoundaryHandleCountSeen = true;
13669 if (m_boundaryHandleCount > 0 && loop
13670 && !DRW::reserve(loop->m_boundaryHandles, m_boundaryHandleCount))
13671 return false;
13672 break;
13673 case 330:
13674 if (m_boundaryHandleCount > 0 && loop) {
13675 // getHandleString() converts the hex string to int for us.
13676 loop->m_boundaryHandles.push_back(
13677 static_cast<std::uint32_t>(reader->getHandleString()));
13678 --m_boundaryHandleCount;
13679 break;
13680 }
13681 if (m_dxfBoundaryHandleCountSeen)
13682 return false;
13683 return DRW_Point::parseCode(code, reader);
13684 case 75:
13685 hstyle = reader->getInt32();
13686 break;
13687 case 76:
13688 hpattern = reader->getInt32();
13689 break;
13690 case 77:
13691 doubleflag = reader->getInt32();
13692 break;
13693 case 78:
13694 if (m_dxfPatternLineCountSeen)
13695 return false;
13696 deflines = reader->getInt32();
13697 if (!isValidCount(deflines, kMaxHatchItems))
13698 return false;
13699 m_dxfPatternLineCountExpected = deflines;
13700 m_dxfPatternLineCountSeen = true;
13701 if (patternLines.size() > static_cast<std::size_t>(deflines))
13702 return false;
13703 break;
13704 case 91:
13705 if (m_dxfLoopCountSeen)
13706 return false;
13707 loopsnum = reader->getInt32();
13708 if (!isValidCount(loopsnum, kMaxHatchItems))
13709 return false;
13710 m_dxfLoopCountExpected = loopsnum;
13711 m_dxfLoopCountSeen = true;
13712 return DRW::reserve( looplist, loopsnum);
13713 case 92: {
13714 if (!validateCurrentBoundaryPathDxf())
13715 return false;
13716 if (m_dxfLoopCountExpected >= 0
13717 && looplist.size() >= static_cast<std::size_t>(m_dxfLoopCountExpected))
13718 return false;
13719 clearEntities();
13720 m_dxfLoopEdgeCountExpected = -1;
13721 m_dxfLoopEdgeCountSeen = false;
13722 m_dxfPolylineVertexCountExpected = -1;
13723 m_dxfPolylineVertexCountSeen = false;
13724 m_dxfBoundaryHandleCountSeen = false;
13725 m_boundaryHandleCount = 0;
13726 loop = std::make_shared<DRW_HatchLoop>(reader->getInt32());
13727 looplist.push_back(loop);
13728 if (reader->getInt32() & 2) {
13729 ispol = true;
13730 clearEntities();
13731 pline = std::make_shared<DRW_LWPolyline>();
13732 loop->objlist.push_back(pline);
13733 } else ispol = false;
13734 break;
13735 }
13736 case 93:
13737 if (pline) {
13738 if (m_dxfPolylineVertexCountSeen)
13739 return false;
13740 pline->vertexnum = reader->getInt32();
13741 if (!isValidCount(pline->vertexnum, kMaxHatchItems))
13742 return false;
13743 m_dxfPolylineVertexCountExpected = pline->vertexnum;
13744 m_dxfPolylineVertexCountSeen = true;
13745 } else if (loop) {
13746 if (m_dxfLoopEdgeCountSeen)
13747 return false;
13748 loop->numedges = reader->getInt32();//aqui reserve
13749 if (!isValidCount(loop->numedges, kMaxHatchItems))
13750 return false;
13751 if (loop->objlist.size() > static_cast<std::size_t>(loop->numedges))
13752 return false;
13753 m_dxfLoopEdgeCountExpected = loop->numedges;
13754 m_dxfLoopEdgeCountSeen = true;
13755 }
13756 break;
13757 case 98: { // seed-point count; coords follow as group-10/20 pairs
13758 if (!validateCurrentBoundaryPathDxf() || !validateCurrentPatternLineDxf())
13759 return false;
13760 if (m_dxfSeedPointCountSeen)
13761 return false;
13762 clearEntities();
13763 const int count = reader->getInt32();
13764 if (!isValidCount(count, kMaxHatchItems)
13765 || !DRW::reserve(seedPoints, count))
13766 return false;
13767 m_dxfSeedPointsExpected = count;
13768 m_dxfSeedPointCountSeen = true;
13769 break;
13770 }
13771 case 450:
13772 isGradient = reader->getInt32();
13773 break;
13774 case 451:
13775 gradReserved = reader->getInt32();
13776 break;
13777 case 452:
13778 singleColor = reader->getInt32();
13779 break;
13780 case 453: {
13781 if (m_dxfGradientColorCountSeen)
13782 return false;
13783 const int n = reader->getInt32();
13784 if (!isValidCount(n, kMaxHatchItems)
13785 || !DRW::reserve(gradColors, n))
13786 return false;
13787 m_dxfGradientColorCountExpected = n;
13788 m_dxfGradientColorCountSeen = true;
13789 break;
13790 }
13791 case 460:
13792 gradAngle = reader->getDouble();
13793 break;
13794 case 461:
13795 gradShift = reader->getDouble();
13796 break;
13797 case 462:
13798 gradTint = reader->getDouble();
13799 break;
13800 case 463: {
13801 if (m_dxfGradientColorCountExpected >= 0
13802 && gradColors.size() >= static_cast<std::size_t>(m_dxfGradientColorCountExpected))
13803 return false;
13804 if (gradColors.size() >= static_cast<std::size_t>(kMaxHatchItems))
13805 return false;
13806 DRW_Hatch::GradientStop stop;
13807 stop.value = reader->getDouble();
13808 gradColors.push_back(stop);
13809 break;
13810 }
13811 case 421:
13812 if (!gradColors.empty())
13813 gradColors.back().rgb = reader->getInt32();
13814 break;
13815 case 63:
13816 if (!gradColors.empty())
13817 gradColors.back().aciColor = reader->getInt32();
13818 else
13819 return DRW_Point::parseCode(code, reader);
13820 break;
13821 case 431:
13822 if (!gradColors.empty())
13823 gradColors.back().colorMethod = reader->getInt32();
13824 break;
13825 case 432:
13826 if (!gradColors.empty())
13827 gradColors.back().colorName = reader->getUtf8String();
13828 break;
13829 case 433:
13830 if (!gradColors.empty())
13831 gradColors.back().colorBookName = reader->getUtf8String();
13832 break;
13833 case 470:
13834 gradName = reader->getUtf8String();
13835 break;
13836 default:
13837 return DRW_Point::parseCode(code, reader);
13838 }
13839
13840 return true;
13841 }
13842 catch (...) {
13843 return false;
13844 }
13845}
13846
13847bool DRW_Hatch::validateCurrentSplineDxf() const {
13848 if (!spline)
13849 return true;
13850 if (m_dxfSplineKnotCountExpected >= 0
13851 && spline->knotslist.size()
13852 != static_cast<std::size_t>(m_dxfSplineKnotCountExpected))
13853 return false;
13854 if (m_dxfSplineControlCountExpected >= 0
13855 && spline->controllist.size()
13856 != static_cast<std::size_t>(m_dxfSplineControlCountExpected))
13857 return false;
13858 if (m_dxfSplineFitCountExpected >= 0
13859 && spline->fitlist.size()
13860 != static_cast<std::size_t>(m_dxfSplineFitCountExpected))
13861 return false;
13862 if (spline->flags & 0x4)
13863 return m_dxfSplineControlCountExpected < 0
13864 || spline->weightlist.size()
13865 == static_cast<std::size_t>(m_dxfSplineControlCountExpected);
13866 return spline->weightlist.empty();
13867}
13868
13869bool DRW_Hatch::validateCurrentPatternLineDxf() const {
13870 if (patternLines.empty() || m_dxfPatternDashCountExpected < 0)
13871 return true;
13872 return patternLines.back().dashList.size()
13873 == static_cast<std::size_t>(m_dxfPatternDashCountExpected);
13874}
13875
13876bool DRW_Hatch::validateCurrentBoundaryPathDxf() const {
13877 if (m_boundaryHandleCount != 0 || !validateCurrentSplineDxf())
13878 return false;
13879 if (!loop)
13880 return true;
13881 if (ispol) {
13882 std::shared_ptr<DRW_LWPolyline> polyline = pline;
13883 if (!polyline && loop->objlist.size() == 1)
13884 polyline = std::dynamic_pointer_cast<DRW_LWPolyline>(loop->objlist.front());
13885 if (!polyline)
13886 return false;
13887 return !m_dxfPolylineVertexCountSeen
13888 || polyline->vertlist.size()
13889 == static_cast<std::size_t>(m_dxfPolylineVertexCountExpected);
13890 }
13891 return !m_dxfLoopEdgeCountSeen
13892 || loop->objlist.size()
13893 == static_cast<std::size_t>(m_dxfLoopEdgeCountExpected);
13894}
13895
13896bool DRW_Hatch::validateDxf() const {
13897 if (!validateCurrentBoundaryPathDxf()
13898 || !validateCurrentPatternLineDxf())
13899 return false;
13900 if (m_dxfLoopCountExpected >= 0
13901 && looplist.size() != static_cast<std::size_t>(m_dxfLoopCountExpected))
13902 return false;
13903 if (m_dxfPatternLineCountExpected >= 0
13904 && patternLines.size()
13905 != static_cast<std::size_t>(m_dxfPatternLineCountExpected))
13906 return false;
13907 if (m_dxfGradientColorCountExpected >= 0
13908 && gradColors.size()
13909 != static_cast<std::size_t>(m_dxfGradientColorCountExpected))
13910 return false;
13911 return m_dxfSeedPointsExpected < 0
13912 || seedPoints.size() == static_cast<std::size_t>(m_dxfSeedPointsExpected);
13913}
13914
13915bool DRW_MPolygon::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
13916 // MPOLYGON shares HATCH's boundary/pattern/gradient codes, so delegate those
13917 // to DRW_Hatch::parseCode. It adds a trailer that plain HATCH never emits:
13918 // 63 / 421 / 430 fill color (ACI / RGB / book-name) — the filled area's
13919 // color, which may differ from the boundary outline color;
13920 // 11 / 21 boundary x-direction vector (no render impact; left to
13921 // the base, which ignores it outside an edge context);
13922 // 99 count of degenerate boundary paths.
13923 // 63/421 are also gradient sub-codes in HATCH, so only claim them here when no
13924 // gradient is being accumulated (gradColors empty) — otherwise defer to base.
13925 switch (code) {
13926 case 63:
13927 if (gradColors.empty()) { fillColorAci = reader->getInt32(); return true; }
13928 break;
13929 case 421:
13930 if (gradColors.empty()) { fillColorRgb = reader->getInt32(); return true; }
13931 break;
13932 case 430:
13933 fillColorName = reader->getUtf8String();
13934 return true;
13935 case 99:
13936 degenerateLoops = reader->getInt32();
13937 return true;
13938 default:
13939 break;
13940 }
13941 return DRW_Hatch::parseCode(code, reader);
13942}
13943
13944// DRW_MPolygon::parseDwg — AcDbMPolygon DWG body.
13945// Layout mirrors HATCH except (per ACadSharp MPolygon / libreDWG dwg.spec):
13946// * a leading BS `style` (DXF group 75) precedes the gradient block, and
13947// * the trailer is a fill CMC + boundary x-direction (2RD) + degenerate-path
13948// count (BL) instead of HATCH's pixel-size + seed points.
13949// The gradient/elevation/extrusion/name/solid/associative prologue and the whole
13950// boundary-loop body are identical, so they reuse DRW_Hatch::parseDwgBoundaryData.
13951// DWG runtime coverage uses testdata/mpolygon_solid.dwg, ODA-synthesized from
13952// the ezdxf-verified inline DXF in mpolygon_tests.cpp and confirmed with the
13953// dwg-parser oracle.
13954bool DRW_MPolygon::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
13955 resetDwgState();
13956 dwgBuffer *sourceBuf = buf;
13957 auto fail = [this, sourceBuf]() {
13958 if (sourceBuf != nullptr)
13959 sourceBuf->invalidate();
13960 resetDwgState();
13961 return false;
13962 };
13963 if (sourceBuf == nullptr)
13964 return fail();
13965
13966 fillColorAci = 0;
13967 fillColorRgb = -1;
13968 fillColorName.clear();
13969 xDirX = 0.0;
13970 xDirY = 0.0;
13971 degenerateLoops = 0;
13972 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
13973 dwgBuffer stringProbe = sourceBuf->forkIndependent();
13974 buf = &bodyProbe;
13975 dwgBuffer *sBuf = version > DRW::AC1018 ? &stringProbe : buf;
13976 std::uint32_t totalBoundItems = 0;
13977 bool havePixelSize = false;
13978 if (!DRW_Entity::parseDwg(
13979 version, buf, version > DRW::AC1018 ? &stringProbe : nullptr, bs)
13980 || !buf->isGood() || !sBuf->isGood())
13981 return fail();
13982 DRW_DBG("\n***************************** parsing mpolygon *********************************************\n")DRW_dbg::dbg("\n***************************** parsing mpolygon *********************************************\n"
)
;
13983 const std::uint64_t bodyEndBit = dwgDataEndBit;
13984 std::uint64_t stringStartBit = bodyEndBit;
13985 std::uint64_t stringEndBit = bodyEndBit;
13986 if (version > DRW::AC1018) {
13987 stringStartBit = currentDwgBit(sBuf);
13988 if (version > DRW::AC1021) {
13989 std::uint64_t ignoredBodyEndBit = 0;
13990 if (!entityBodyDataEndBit(*sBuf, version, objSize,
13991 ignoredBodyEndBit, &stringEndBit))
13992 return fail();
13993 }
13994 }
13995
13996 // Leading BS style (group 75) — read once here and again after the loops
13997 // below (HATCH has only the latter); matches the reference parser, which
13998 // discards this first read.
13999 std::uint16_t parsedLeadingStyle = 0;
14000 if (!readBoundedBitShort(*buf, bodyEndBit, parsedLeadingStyle))
14001 return fail();
14002 hstyle = parsedLeadingStyle;
14003
14004 if (version > DRW::AC1015) { //2004+ gradient (same layout as HATCH)
14005 if (!readBoundedHatchGradient(
14006 *buf, sBuf, bodyEndBit, version, isGradient, gradReserved,
14007 gradAngle, gradShift, singleColor, gradTint, gradName,
14008 gradColors, stringEndBit))
14009 return fail();
14010 }
14011 if (!readBoundedBitDouble(*buf, bodyEndBit, basePoint.z)
14012 || !readBoundedBitCoord(*buf, bodyEndBit, extPoint)
14013 || !std::isfinite(basePoint.z)
14014 || !std::isfinite(extPoint.x)
14015 || !std::isfinite(extPoint.y)
14016 || !std::isfinite(extPoint.z))
14017 return fail();
14018 UTF8STRINGstd::string parsedName;
14019 if (version > DRW::AC1018 && stringStartBit >= stringEndBit) {
14020 parsedName.clear();
14021 } else if (!readBoundedVariableText(*sBuf, stringEndBit, version,
14022 parsedName)) {
14023 return fail();
14024 }
14025 bool parsedSolid = false;
14026 bool parsedAssociative = false;
14027 if (!sBuf->isGood()
14028 || currentDwgBit(sBuf) > stringEndBit
14029 || !readBoundedBit(*buf, bodyEndBit, parsedSolid)
14030 || !readBoundedBit(*buf, bodyEndBit, parsedAssociative))
14031 return fail();
14032 solid = parsedSolid;
14033 associative = parsedAssociative;
14034 name = std::move(parsedName);
14035
14036 if (!parseDwgBoundaryData(version, buf, totalBoundItems, havePixelSize))
14037 return fail();
14038
14039 std::uint16_t parsedStyle = 0;
14040 std::uint16_t parsedPattern = 0;
14041 if (!readBoundedBitShort(*buf, bodyEndBit, parsedStyle)
14042 || !readBoundedBitShort(*buf, bodyEndBit, parsedPattern))
14043 return fail();
14044 hstyle = parsedStyle;
14045 hpattern = parsedPattern;
14046 if (!solid){
14047 double parsedAngle = 0.0;
14048 double parsedScale = 0.0;
14049 bool parsedDoubleFlag = false;
14050 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
14051 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale)
14052 || !readBoundedBit(*buf, bodyEndBit, parsedDoubleFlag)
14053 || !std::isfinite(parsedAngle)
14054 || !std::isfinite(parsedScale))
14055 return fail();
14056 angle = parsedAngle;
14057 scale = parsedScale;
14058 doubleflag = parsedDoubleFlag;
14059 std::uint32_t parsedDeflines = 0;
14060 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 12,
14061 parsedDeflines))
14062 return fail();
14063 if (!DRW::reserve(patternLines, parsedDeflines))
14064 return fail();
14065 deflines = static_cast<int>(parsedDeflines);
14066 for (std::uint32_t i = 0 ; i < parsedDeflines; ++i){
14067 PatternLine patternLine;
14068 if (!readBoundedBitDouble(*buf, bodyEndBit, patternLine.angle)
14069 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.baseX)
14070 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.baseY)
14071 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.offsetX)
14072 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.offsetY)
14073 || !std::isfinite(patternLine.angle)
14074 || !std::isfinite(patternLine.baseX)
14075 || !std::isfinite(patternLine.baseY)
14076 || !std::isfinite(patternLine.offsetX)
14077 || !std::isfinite(patternLine.offsetY))
14078 return fail();
14079 std::uint32_t numDashL = 0;
14080 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 2,
14081 numDashL)
14082 || !proxyEntityHasBits(
14083 *buf, bodyEndBit, static_cast<std::uint64_t>(numDashL) * 2u)
14084 || !DRW::reserve(patternLine.dashList, numDashL))
14085 return fail();
14086 for (std::uint32_t d = 0 ; d < numDashL; ++d) {
14087 double dash = 0.0;
14088 if (!readBoundedBitDouble(*buf, bodyEndBit, dash)
14089 || !std::isfinite(dash))
14090 return fail();
14091 patternLine.dashList.push_back(dash);
14092 }
14093 patternLines.push_back(std::move(patternLine));
14094 }
14095 }
14096
14097 // MPOLYGON trailer (differs from HATCH): fill CMC + x-direction + degenerate
14098 // path count. No pixel size / seed points here.
14099 std::int32_t rgb = -1;
14100 UTF8STRINGstd::string colName;
14101 std::uint32_t parsedFillColor = 0;
14102 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
14103 parsedFillColor, &rgb, nullptr, &colName))
14104 return fail();
14105 fillColorAci = static_cast<int>(parsedFillColor);
14106 fillColorRgb = rgb;
14107 fillColorName = colName;
14108 DRW_Coord xdir;
14109 if (!readBounded2RawDouble(*buf, bodyEndBit, xdir)
14110 || !std::isfinite(xdir.x) || !std::isfinite(xdir.y))
14111 return fail();
14112 xDirX = xdir.x;
14113 xDirY = xdir.y;
14114 std::uint32_t parsedDegenerateLoops = 0;
14115 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 0,
14116 parsedDegenerateLoops))
14117 return fail();
14118 degenerateLoops = static_cast<std::int32_t>(parsedDegenerateLoops);
14119
14120 std::uint64_t handleEndBit = 0;
14121 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
14122 handleEndBit))
14123 return fail();
14124 dwgBuffer handleProbe = buf->forkIndependent();
14125 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
14126 handleEndBit)
14127 || !handleProbe.isGood())
14128 return fail();
14129 for (std::uint32_t i = 0 ; i < totalBoundItems; ++i) {
14130 dwgHandle boundaryHandle;
14131 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
14132 boundaryHandle))
14133 return fail();
14134 }
14135 if (!handleProbe.isGood())
14136 return fail();
14137
14138 *sourceBuf = handleProbe;
14139 return true;
14140}
14141
14142// Shared DWG boundary-loop reader for HATCH and MPOLYGON (ODA §20.4.36).
14143// Reads the loop count and, per loop, the derived-boundary flag plus its edge
14144// list or polyline. Accumulates the running boundary-source-handle total and
14145// whether any loop is derived (needs a trailing pixel size). Extracted from
14146// DRW_Hatch::parseDwg so DRW_MPolygon::parseDwg reuses the identical body while
14147// supplying its own differing leading (BS style) and trailing (fill CMC +
14148// x-direction + degenerate count) field order.
14149bool validateDwgHatchData(const DRW_Hatch& hatch) {
14150 if (hatch.looplist.size() > static_cast<std::size_t>(kMaxHatchItems)
14151 || hatch.patternLines.size() > static_cast<std::size_t>(kMaxHatchItems)
14152 || hatch.gradColors.size() > static_cast<std::size_t>(kMaxHatchItems)
14153 || hatch.seedPoints.size() > static_cast<std::size_t>(kMaxHatchItems))
14154 return false;
14155
14156 for (const auto& loop : hatch.looplist) {
14157 if (!loop
14158 || loop->objlist.size() > static_cast<std::size_t>(kMaxHatchItems)
14159 || loop->m_boundaryHandles.size() > static_cast<std::size_t>(kMaxHatchItems))
14160 return false;
14161
14162 if ((loop->type & 2) != 0) {
14163 if (loop->objlist.size() != 1)
14164 return false;
14165 const auto* polyline =
14166 dynamic_cast<const DRW_LWPolyline*>(loop->objlist.front().get());
14167 if (!polyline || polyline->vertlist.size() > static_cast<std::size_t>(kMaxHatchItems))
14168 return false;
14169 for (const auto& vertex : polyline->vertlist) {
14170 if (!vertex)
14171 return false;
14172 }
14173 continue;
14174 }
14175
14176 for (const auto& entity : loop->objlist) {
14177 if (dynamic_cast<const DRW_Line*>(entity.get())
14178 || dynamic_cast<const DRW_Arc*>(entity.get())
14179 || dynamic_cast<const DRW_Ellipse*>(entity.get())) {
14180 continue;
14181 }
14182 const auto* spline = dynamic_cast<const DRW_Spline*>(entity.get());
14183 if (!spline
14184 || spline->knotslist.size() > static_cast<std::size_t>(kMaxHatchItems)
14185 || spline->controllist.size() > static_cast<std::size_t>(kMaxHatchItems)
14186 || spline->fitlist.size() > static_cast<std::size_t>(kMaxHatchItems))
14187 return false;
14188 for (const auto& point : spline->controllist) {
14189 if (!point)
14190 return false;
14191 }
14192 for (const auto& point : spline->fitlist) {
14193 if (!point)
14194 return false;
14195 }
14196 }
14197 }
14198
14199 for (const auto& line : hatch.patternLines) {
14200 if (line.dashList.size() > static_cast<std::size_t>(kMaxHatchItems))
14201 return false;
14202 }
14203 return true;
14204}
14205
14206bool DRW_MPolygon::encodeDwg(DRW::Version version, dwgBufferW *buf,
14207 std::uint32_t bs, dwgBufferW *strBuf,
14208 dwgBufferW *handleBuf) {
14209 (void)bs;
14210 if (!validateDwgHatchData(*this)
14211 || !isValidCount(degenerateLoops, kMaxHatchItems))
14212 return false;
14213 oType = kDwgClassNum;
14214 if (!encodeDwgCommon(version, buf, strBuf))
14215 return false;
14216
14217 dwgBufferW *sb = strBuf ? strBuf : buf;
14218
14219 // AcDbMPolygon has a leading style field before the HATCH-like gradient
14220 // prologue. The same style is emitted again after the boundary data.
14221 buf->putBitShort(static_cast<std::uint16_t>(hstyle));
14222 encodeDwgGradientData(version, buf, sb);
14223
14224 buf->putBitDouble(basePoint.z);
14225 buf->put3BitDouble(extPoint);
14226 sb->putVariableText(version, name);
14227 buf->putBit(static_cast<std::uint8_t>(solid));
14228 buf->putBit(static_cast<std::uint8_t>(associative));
14229 if (!encodeDwgBoundaryData(version, buf)) return false;
14230
14231 buf->putBitShort(static_cast<std::uint16_t>(hstyle));
14232 buf->putBitShort(static_cast<std::uint16_t>(hpattern));
14233
14234 if (!solid) {
14235 buf->putBitDouble(angle);
14236 buf->putBitDouble(scale);
14237 buf->putBit(static_cast<std::uint8_t>(doubleflag));
14238 buf->putBitShort(static_cast<std::uint16_t>(patternLines.size()));
14239 for (const PatternLine& pl : patternLines) {
14240 buf->putBitDouble(pl.angle);
14241 buf->putBitDouble(pl.baseX);
14242 buf->putBitDouble(pl.baseY);
14243 buf->putBitDouble(pl.offsetX);
14244 buf->putBitDouble(pl.offsetY);
14245 buf->putBitShort(static_cast<std::uint16_t>(pl.dashList.size()));
14246 for (double dash : pl.dashList)
14247 buf->putBitDouble(dash);
14248 }
14249 }
14250
14251 buf->putCmColor(version,
14252 static_cast<std::uint16_t>(fillColorAci),
14253 fillColorRgb,
14254 fillColorName,
14255 {},
14256 sb);
14257 buf->put2RawDouble(DRW_Coord{xDirX, xDirY, 0.0});
14258 buf->putBitLong(degenerateLoops);
14259
14260 return encodeDwgEntHandle(version, buf, handleBuf);
14261}
14262
14263bool DRW_Hatch::parseDwgBoundaryData(DRW::Version version, dwgBuffer *buf,
14264 std::uint32_t &totalBoundItems, bool &havePixelSize) {
14265 try {
14266 const std::uint64_t bodyEndBit = dwgDataEndBit;
14267 std::uint32_t parsedLoopCount = 0;
14268 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 4,
14269 parsedLoopCount))
14270 return false;
14271 if (!DRW::reserve(looplist, parsedLoopCount))
14272 return false;
14273 loopsnum = static_cast<std::int32_t>(parsedLoopCount);
14274 DRW_DBG("solid: ")DRW_dbg::dbg("solid: "); DRW_DBG(solid)DRW_dbg::dbg(solid); DRW_DBG(" associative: ")DRW_dbg::dbg(" associative: "); DRW_DBG(associative)DRW_dbg::dbg(associative);
14275 DRW_DBG(" loopsnum: ")DRW_dbg::dbg(" loopsnum: "); DRW_DBG(loopsnum)DRW_dbg::dbg(loopsnum); DRW_DBG("\n")DRW_dbg::dbg("\n");
14276
14277 //read loops
14278 for (std::uint32_t i = 0 ; i < parsedLoopCount; ++i){
14279 std::int32_t loopType = 0;
14280 if (!readBoundedBitLong(*buf, bodyEndBit, loopType)
14281 || loopType < 0)
14282 return false;
14283 loop = std::make_shared<DRW_HatchLoop>(loopType);
14284 havePixelSize = havePixelSize || ((loop->type & 4) != 0);
14285 DRW_DBG(" loop[")DRW_dbg::dbg(" loop["); DRW_DBG(i)DRW_dbg::dbg(i); DRW_DBG("] type: ")DRW_dbg::dbg("] type: "); DRW_DBG(loop->type)DRW_dbg::dbg(loop->type);
14286 if (!(loop->type & 2)){ //Not polyline
14287 std::uint32_t numPathSeg = 0;
14288 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 8,
14289 numPathSeg))
14290 return false;
14291 if (!DRW::reserve(loop->objlist, numPathSeg))
14292 return false;
14293 DRW_DBG(" numPathSeg: ")DRW_dbg::dbg(" numPathSeg: "); DRW_DBG(numPathSeg)DRW_dbg::dbg(numPathSeg); DRW_DBG("\n")DRW_dbg::dbg("\n");
14294 for (std::uint32_t j = 0; j<numPathSeg;++j){
14295 std::uint8_t typePath = 0;
14296 if (!readBoundedRawChar8(*buf, bodyEndBit, typePath)
14297 || typePath < 1 || typePath > 4)
14298 return false;
14299 DRW_DBG(" seg[")DRW_dbg::dbg(" seg["); DRW_DBG(j)DRW_dbg::dbg(j); DRW_DBG("] typePath: ")DRW_dbg::dbg("] typePath: "); DRW_DBG(typePath)DRW_dbg::dbg(typePath); DRW_DBG("\n")DRW_dbg::dbg("\n");
14300 if (typePath == 1){ //line
14301 DRW_Coord basePoint;
14302 DRW_Coord secPoint;
14303 if (!readBounded2RawDouble(*buf, bodyEndBit, basePoint)
14304 || !readBounded2RawDouble(*buf, bodyEndBit, secPoint))
14305 return false;
14306 addLine();
14307 line->basePoint = basePoint;
14308 line->secPoint = secPoint;
14309 } else if (typePath == 2){ //circle arc
14310 DRW_Coord basePoint;
14311 double radius = 0.0;
14312 double startAngle = 0.0;
14313 double endAngle = 0.0;
14314 bool isCcw = false;
14315 if (!readBounded2RawDouble(*buf, bodyEndBit, basePoint)
14316 || !readBoundedBitDouble(*buf, bodyEndBit, radius)
14317 || !readBoundedBitDouble(*buf, bodyEndBit, startAngle)
14318 || !readBoundedBitDouble(*buf, bodyEndBit, endAngle)
14319 || !readBoundedBit(*buf, bodyEndBit, isCcw)
14320 || !std::isfinite(basePoint.x)
14321 || !std::isfinite(basePoint.y)
14322 || !std::isfinite(radius)
14323 || !std::isfinite(startAngle)
14324 || !std::isfinite(endAngle))
14325 return false;
14326 addArc();
14327 arc->basePoint = basePoint;
14328 arc->radious = radius;
14329 arc->staangle = startAngle;
14330 arc->endangle = endAngle;
14331 arc->isccw = isCcw;
14332 } else if (typePath == 3){ //ellipse arc
14333 DRW_Coord basePoint;
14334 DRW_Coord secPoint;
14335 double ratio = 0.0;
14336 double startParam = 0.0;
14337 double endParam = 0.0;
14338 bool isCcw = false;
14339 if (!readBounded2RawDouble(*buf, bodyEndBit, basePoint)
14340 || !readBounded2RawDouble(*buf, bodyEndBit, secPoint)
14341 || !readBoundedBitDouble(*buf, bodyEndBit, ratio)
14342 || !readBoundedBitDouble(*buf, bodyEndBit, startParam)
14343 || !readBoundedBitDouble(*buf, bodyEndBit, endParam)
14344 || !readBoundedBit(*buf, bodyEndBit, isCcw)
14345 || !std::isfinite(basePoint.x)
14346 || !std::isfinite(basePoint.y)
14347 || !std::isfinite(secPoint.x)
14348 || !std::isfinite(secPoint.y)
14349 || !std::isfinite(ratio)
14350 || !std::isfinite(startParam)
14351 || !std::isfinite(endParam))
14352 return false;
14353 addEllipse();
14354 ellipse->basePoint = basePoint;
14355 ellipse->secPoint = secPoint;
14356 ellipse->ratio = ratio;
14357 ellipse->staparam = startParam;
14358 ellipse->endparam = endParam;
14359 ellipse->isccw = isCcw;
14360 } else if (typePath == 4){ //spline
14361 std::int32_t degree = 0;
14362 bool isRational = false;
14363 bool isPeriodic = false;
14364 if (!readBoundedBitLong(*buf, bodyEndBit, degree)
14365 || !isValidSplineDegree(degree)
14366 || !readBoundedBit(*buf, bodyEndBit, isRational)
14367 || !readBoundedBit(*buf, bodyEndBit, isPeriodic))
14368 return false;
14369 std::uint32_t knotCount = 0;
14370 if (!readTableBodyCount(version, buf, objSize,
14371 kMaxHatchItems, 2, knotCount)) {
14372 return false;
14373 }
14374 std::uint32_t controlCount = 0;
14375 if (!readTableBodyCount(version, buf, objSize,
14376 kMaxHatchItems, 128, controlCount)) {
14377 return false;
14378 }
14379 DRW_Spline parsedSpline;
14380 parsedSpline.degree = degree;
14381 parsedSpline.nknots = static_cast<std::int32_t>(knotCount);
14382 parsedSpline.ncontrol = static_cast<std::int32_t>(controlCount);
14383 parsedSpline.flags = (isRational ? 4 : 0)
14384 | (isPeriodic ? 2 : 0);
14385 if (!DRW::reserve(parsedSpline.knotslist, knotCount)
14386 || !DRW::reserve(parsedSpline.controllist, controlCount))
14387 return false;
14388 for (std::uint32_t j = 0; j < knotCount;++j){
14389 double knot = 0.0;
14390 if (!readBoundedBitDouble(*buf, bodyEndBit, knot)
14391 || !std::isfinite(knot))
14392 return false;
14393 parsedSpline.knotslist.push_back(knot);
14394 }
14395 for (std::uint32_t j = 0; j < controlCount;++j){
14396 DRW_Coord control;
14397 if (!readBounded2RawDouble(*buf, bodyEndBit, control)
14398 || !std::isfinite(control.x)
14399 || !std::isfinite(control.y))
14400 return false;
14401 if (isRational) {
14402 if (!readBoundedBitDouble(*buf, bodyEndBit, control.z)
14403 || !std::isfinite(control.z))
14404 return false;
14405 }
14406 parsedSpline.controllist.push_back(
14407 std::make_shared<DRW_Coord>(control));
14408 }
14409 if (version > DRW::AC1021) { //2010+
14410 std::uint32_t fitCount = 0;
14411 if (!readTableBodyCount(version, buf, objSize,
14412 kMaxHatchItems, 128, fitCount))
14413 return false;
14414 parsedSpline.nfit = static_cast<std::int32_t>(fitCount);
14415 // Fit points AND the start/end tangents are present only
14416 // when nfit > 0 (matches ACadSharp's `if (nfitPoints > 0)`).
14417 // Reading the two tangents unconditionally on an nfit==0
14418 // spline edge over-runs the entity body and fails the parse
14419 // (e.g. svg/export_sample.dwg: a degree-3 non-rational
14420 // spline boundary edge with 9 control points / 0 fit points).
14421 if (parsedSpline.nfit > 0) {
14422 if (!DRW::reserve(parsedSpline.fitlist, fitCount)) {
14423 return false;
14424 }
14425 for (std::uint32_t j = 0; j < fitCount;++j){
14426 DRW_Coord fit;
14427 if (!readBounded2RawDouble(*buf, bodyEndBit, fit)
14428 || !std::isfinite(fit.x)
14429 || !std::isfinite(fit.y))
14430 return false;
14431 parsedSpline.fitlist.push_back(
14432 std::make_shared<DRW_Coord>(fit));
14433 }
14434 if (!readBounded2RawDouble(*buf, bodyEndBit,
14435 parsedSpline.tgStart)
14436 || !readBounded2RawDouble(*buf, bodyEndBit,
14437 parsedSpline.tgEnd)
14438 || !std::isfinite(parsedSpline.tgStart.x)
14439 || !std::isfinite(parsedSpline.tgStart.y)
14440 || !std::isfinite(parsedSpline.tgEnd.x)
14441 || !std::isfinite(parsedSpline.tgEnd.y))
14442 return false;
14443 }
14444 }
14445 addSpline();
14446 *spline = std::move(parsedSpline);
14447 }
14448 }
14449 } else { //end not pline, start polyline
14450 if (!proxyEntityHasBits(*buf, bodyEndBit, 2))
14451 return false;
14452 bool asBulge = false;
14453 bool closed = false;
14454 if (!readBoundedBit(*buf, bodyEndBit, asBulge)
14455 || !readBoundedBit(*buf, bodyEndBit, closed))
14456 return false;
14457 std::uint32_t numVert = 0;
14458 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems,
14459 128, numVert))
14460 return false;
14461 if (!DRW::reserve(loop->objlist, 1))
14462 return false;
14463 DRW_DBG(" asBulge: ")DRW_dbg::dbg(" asBulge: "); DRW_DBG(asBulge)DRW_dbg::dbg(asBulge); DRW_DBG(" closed: ")DRW_dbg::dbg(" closed: "); DRW_DBG(closed)DRW_dbg::dbg(closed);
14464 DRW_DBG(" numVert: ")DRW_dbg::dbg(" numVert: "); DRW_DBG(numVert)DRW_dbg::dbg(numVert); DRW_DBG("\n")DRW_dbg::dbg("\n");
14465 std::vector<DRW_Vertex2D> vertices;
14466 if (!DRW::reserve(vertices, numVert))
14467 return false;
14468 for (std::uint32_t j = 0; j<numVert;++j){
14469 DRW_Vertex2D v;
14470 if (!readBoundedRawDouble(*buf, bodyEndBit, v.x)
14471 || !readBoundedRawDouble(*buf, bodyEndBit, v.y)
14472 || !std::isfinite(v.x) || !std::isfinite(v.y))
14473 return false;
14474 if (asBulge
14475 && (!readBoundedBitDouble(*buf, bodyEndBit, v.bulge)
14476 || !std::isfinite(v.bulge)))
14477 return false;
14478 vertices.push_back(v);
14479 }
14480 pline = std::make_shared<DRW_LWPolyline>();
14481 pline->flags = closed;
14482 for (const DRW_Vertex2D& vertex : vertices)
14483 pline->addVertex(vertex);
14484 loop->objlist.push_back(pline);
14485 }//end polyline
14486 loop->update();
14487 std::uint32_t boundaryHandleCount = 0;
14488 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 0,
14489 boundaryHandleCount)
14490 || totalBoundItems > kMaxHatchItems - boundaryHandleCount)
14491 return false;
14492 looplist.push_back(loop);
14493 totalBoundItems += boundaryHandleCount;
14494 DRW_DBG(" totalBoundItems: ")DRW_dbg::dbg(" totalBoundItems: "); DRW_DBG(totalBoundItems)DRW_dbg::dbg(totalBoundItems);
14495 } //end read loops
14496 return buf->isGood();
14497 } catch (...) {
14498 return false;
14499 }
14500}
14501
14502bool DRW_Hatch::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
14503 resetDwgState();
14504 dwgBuffer *sourceBuf = buf;
14505 auto fail = [this, sourceBuf]() {
14506 if (sourceBuf != nullptr)
14507 sourceBuf->invalidate();
14508 resetDwgState();
14509 return false;
14510 };
14511 if (sourceBuf == nullptr)
14512 return fail();
14513
14514 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
14515 dwgBuffer stringProbe = sourceBuf->forkIndependent();
14516 buf = &bodyProbe;
14517 dwgBuffer *sBuf = version > DRW::AC1018 ? &stringProbe : buf;
14518 std::uint32_t totalBoundItems = 0;
14519 bool havePixelSize = false;
14520
14521 bool ret = DRW_Entity::parseDwg(
14522 version, buf, version > DRW::AC1018 ? &stringProbe : nullptr, bs);
14523 if (!ret || !buf->isGood() || !sBuf->isGood())
14524 return fail();
14525 DRW_DBG("\n***************************** parsing hatch *********************************************\n")DRW_dbg::dbg("\n***************************** parsing hatch *********************************************\n"
)
;
14526 const std::uint64_t bodyEndBit = dwgDataEndBit;
14527 std::uint64_t stringStartBit = bodyEndBit;
14528 std::uint64_t stringEndBit = bodyEndBit;
14529 if (version > DRW::AC1018) {
14530 stringStartBit = currentDwgBit(sBuf);
14531 if (version > DRW::AC1021) {
14532 std::uint64_t ignoredBodyEndBit = 0;
14533 if (!entityBodyDataEndBit(*sBuf, version, objSize,
14534 ignoredBodyEndBit, &stringEndBit))
14535 return fail();
14536 }
14537 }
14538
14539 if (version > DRW::AC1015) { //2004+
14540 if (!readBoundedHatchGradient(
14541 *buf, sBuf, bodyEndBit, version, isGradient, gradReserved,
14542 gradAngle, gradShift, singleColor, gradTint, gradName,
14543 gradColors, stringEndBit))
14544 return fail();
14545 DRW_DBG("\ngradient name: ")DRW_dbg::dbg("\ngradient name: "); DRW_DBG(gradName.c_str())DRW_dbg::dbg(gradName.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
14546 }
14547 DRW_Coord parsedBasePoint = basePoint;
14548 DRW_Coord parsedExtPoint;
14549 UTF8STRINGstd::string parsedName;
14550 bool parsedSolid = false;
14551 bool parsedAssociative = false;
14552 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedBasePoint.z)
14553 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtPoint)
14554 || !std::isfinite(parsedBasePoint.z)
14555 || !std::isfinite(parsedExtPoint.x)
14556 || !std::isfinite(parsedExtPoint.y)
14557 || !std::isfinite(parsedExtPoint.z))
14558 return fail();
14559 DRW_DBG("base point: ")DRW_dbg::dbg("base point: "); DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y,DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
14560 parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
14561 DRW_DBG("\nextrusion: ")DRW_dbg::dbg("\nextrusion: "); DRW_DBGPT(parsedExtPoint.x, parsedExtPoint.y,DRW_dbg::dbgPT(parsedExtPoint.x, parsedExtPoint.y, parsedExtPoint
.z)
14562 parsedExtPoint.z)DRW_dbg::dbgPT(parsedExtPoint.x, parsedExtPoint.y, parsedExtPoint
.z)
;
14563 if (version > DRW::AC1018 && stringStartBit >= stringEndBit) {
14564 parsedName.clear();
14565 } else if (!readBoundedVariableText(*sBuf, stringEndBit, version,
14566 parsedName)) {
14567 return fail();
14568 }
14569 if (!sBuf->isGood()
14570 || currentDwgBit(sBuf) > stringEndBit
14571 || !readBoundedBit(*buf, bodyEndBit, parsedSolid)
14572 || !readBoundedBit(*buf, bodyEndBit, parsedAssociative))
14573 return fail();
14574 basePoint = parsedBasePoint;
14575 extPoint = parsedExtPoint;
14576 name = std::move(parsedName);
14577 solid = parsedSolid;
14578 associative = parsedAssociative;
14579 DRW_DBG("\nhatch pattern name: ")DRW_dbg::dbg("\nhatch pattern name: "); DRW_DBG(name.c_str())DRW_dbg::dbg(name.c_str()); DRW_DBG("\n")DRW_dbg::dbg("\n");
14580 if (!parseDwgBoundaryData(version, buf, totalBoundItems, havePixelSize))
14581 return fail();
14582
14583 std::uint16_t parsedStyle = 0;
14584 std::uint16_t parsedPattern = 0;
14585 if (!readBoundedBitShort(*buf, bodyEndBit, parsedStyle)
14586 || !readBoundedBitShort(*buf, bodyEndBit, parsedPattern))
14587 return fail();
14588 hstyle = parsedStyle;
14589 hpattern = parsedPattern;
14590 DRW_DBG("\nhatch style: ")DRW_dbg::dbg("\nhatch style: "); DRW_DBG(hstyle)DRW_dbg::dbg(hstyle); DRW_DBG(" pattern type")DRW_dbg::dbg(" pattern type"); DRW_DBG(hpattern)DRW_dbg::dbg(hpattern);
14591 if (!solid){
14592 double parsedAngle = 0.0;
14593 double parsedScale = 0.0;
14594 bool parsedDoubleFlag = false;
14595 if (!readBoundedBitDouble(*buf, bodyEndBit, parsedAngle)
14596 || !readBoundedBitDouble(*buf, bodyEndBit, parsedScale)
14597 || !readBoundedBit(*buf, bodyEndBit, parsedDoubleFlag)
14598 || !std::isfinite(parsedAngle)
14599 || !std::isfinite(parsedScale))
14600 return fail();
14601 angle = parsedAngle;
14602 scale = parsedScale;
14603 doubleflag = parsedDoubleFlag;
14604 std::uint32_t parsedDeflines = 0;
14605 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 12,
14606 parsedDeflines))
14607 return fail();
14608 deflines = static_cast<int>(parsedDeflines);
14609 for (std::uint32_t i = 0 ; i < parsedDeflines; ++i){
14610 PatternLine patternLine;
14611 if (!readBoundedBitDouble(*buf, bodyEndBit, patternLine.angle)
14612 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.baseX)
14613 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.baseY)
14614 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.offsetX)
14615 || !readBoundedBitDouble(*buf, bodyEndBit, patternLine.offsetY)
14616 || !std::isfinite(patternLine.angle)
14617 || !std::isfinite(patternLine.baseX)
14618 || !std::isfinite(patternLine.baseY)
14619 || !std::isfinite(patternLine.offsetX)
14620 || !std::isfinite(patternLine.offsetY))
14621 return fail();
14622 std::uint32_t numDashL = 0;
14623 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 2,
14624 numDashL)
14625 || !proxyEntityHasBits(
14626 *buf, bodyEndBit, static_cast<std::uint64_t>(numDashL) * 2u)
14627 || !DRW::reserve(patternLine.dashList, numDashL))
14628 return fail();
14629 DRW_DBG("\ndef line: ")DRW_dbg::dbg("\ndef line: "); DRW_DBG(patternLine.angle)DRW_dbg::dbg(patternLine.angle); DRW_DBG(",")DRW_dbg::dbg(",");
14630 DRW_DBG(patternLine.baseX)DRW_dbg::dbg(patternLine.baseX); DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(patternLine.baseY)DRW_dbg::dbg(patternLine.baseY);
14631 DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(patternLine.offsetX)DRW_dbg::dbg(patternLine.offsetX); DRW_DBG(",")DRW_dbg::dbg(",");
14632 DRW_DBG(patternLine.offsetY)DRW_dbg::dbg(patternLine.offsetY);
14633 for (std::uint32_t i = 0 ; i < numDashL; ++i){
14634 double lengthL = 0.0;
14635 if (!readBoundedBitDouble(*buf, bodyEndBit, lengthL)
14636 || !std::isfinite(lengthL))
14637 return fail();
14638 patternLine.dashList.push_back(lengthL);
14639 DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(lengthL)DRW_dbg::dbg(lengthL);
14640 }
14641 patternLines.push_back(std::move(patternLine));
14642 }//end deflines
14643 } //end not solid
14644
14645 if (havePixelSize){
14646 if (!readBoundedBitDouble(*buf, bodyEndBit, pixelSize)
14647 || !std::isfinite(pixelSize))
14648 return fail();
14649 DRW_DBG("\npixel size: ")DRW_dbg::dbg("\npixel size: "); DRW_DBG(pixelSize)DRW_dbg::dbg(pixelSize);
14650 }
14651 std::uint32_t numSeedPoints = 0;
14652 if (!readTableBodyCount(version, buf, objSize, kMaxHatchItems, 128,
14653 numSeedPoints)
14654 || !proxyEntityHasBits(
14655 *buf, bodyEndBit, static_cast<std::uint64_t>(numSeedPoints) * 128u)
14656 || !DRW::reserve(seedPoints, numSeedPoints))
14657 return fail();
14658 DRW_DBG("\nnum Seed Points ")DRW_dbg::dbg("\nnum Seed Points "); DRW_DBG(numSeedPoints)DRW_dbg::dbg(numSeedPoints);
14659 for (std::uint32_t i = 0 ; i < numSeedPoints; ++i){
14660 DRW_Coord seedPt;
14661 if (!readBounded2RawDouble(*buf, bodyEndBit, seedPt)
14662 || !std::isfinite(seedPt.x)
14663 || !std::isfinite(seedPt.y))
14664 return fail();
14665 DRW_DBG("\n ")DRW_dbg::dbg("\n "); DRW_DBG(seedPt.x)DRW_dbg::dbg(seedPt.x); DRW_DBG(",")DRW_dbg::dbg(","); DRW_DBG(seedPt.y)DRW_dbg::dbg(seedPt.y);
14666 seedPoints.push_back(seedPt);
14667 }
14668
14669 DRW_DBG("\n")DRW_dbg::dbg("\n");
14670 std::uint64_t handleEndBit = 0;
14671 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
14672 handleEndBit))
14673 return fail();
14674 dwgBuffer handleProbe = buf->forkIndependent();
14675 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
14676 handleEndBit);
14677 if (!ret || !handleProbe.isGood())
14678 return fail();
14679 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
14680
14681 for (std::uint32_t i = 0 ; i < totalBoundItems; ++i){
14682 dwgHandle biH;
14683 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
14684 biH))
14685 return fail();
14686 DRW_DBG("Boundary Items Handle: ")DRW_dbg::dbg("Boundary Items Handle: "); DRW_DBGHL(biH.code, biH.size, biH.ref)DRW_dbg::dbgHL(biH.code, biH.size, biH.ref);
14687 }
14688 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
14689// RS crc; //RS */
14690 if (!handleProbe.isGood())
14691 return fail();
14692 *sourceBuf = handleProbe;
14693 return true;
14694}
14695
14696void DRW_Hatch::encodeDwgGradientData(DRW::Version version, dwgBufferW *buf,
14697 dwgBufferW *strBuf) const {
14698 if (version <= DRW::AC1015)
14699 return;
14700
14701 dwgBufferW *sb = strBuf ? strBuf : buf;
14702 buf->putBitLong(isGradient);
14703 buf->putBitLong(gradReserved);
14704 buf->putBitDouble(gradAngle);
14705 buf->putBitDouble(gradShift);
14706 buf->putBitLong(singleColor);
14707 buf->putBitDouble(gradTint);
14708 buf->putBitLong(static_cast<std::int32_t>(gradColors.size()));
14709 for (const GradientStop& stop : gradColors) {
14710 buf->putBitDouble(stop.value);
14711 buf->putBitShort(static_cast<std::uint16_t>(stop.aciColor));
14712 buf->putBitLong(static_cast<std::uint32_t>(stop.rgb));
14713 buf->putRawChar8(0);
14714 }
14715 sb->putVariableText(version, gradName);
14716}
14717
14718bool DRW_Hatch::encodeDwgBoundaryData(DRW::Version version, dwgBufferW *buf,
14719 bool includePixelSize) const {
14720 buf->putBitLong(static_cast<std::int32_t>(looplist.size()));
14721
14722 for (const auto& lp : looplist) {
14723 buf->putBitLong(includePixelSize ? lp->type : lp->type & ~4);
14724
14725 if (!(lp->type & 2)) {
14726 buf->putBitLong(static_cast<std::int32_t>(lp->objlist.size()));
14727 for (const auto& seg : lp->objlist) {
14728 if (const auto* ln = dynamic_cast<const DRW_Line*>(seg.get())) {
14729 buf->putRawChar8(1); // line
14730 buf->put2RawDouble(ln->basePoint);
14731 buf->put2RawDouble(ln->secPoint);
14732 } else if (const auto* arc = dynamic_cast<const DRW_Arc*>(seg.get())) {
14733 buf->putRawChar8(2); // circular arc
14734 buf->put2RawDouble(arc->basePoint);
14735 buf->putBitDouble(arc->radious);
14736 buf->putBitDouble(arc->staangle);
14737 buf->putBitDouble(arc->endangle);
14738 buf->putBit(static_cast<std::uint8_t>(arc->isccw));
14739 } else if (const auto* el = dynamic_cast<const DRW_Ellipse*>(seg.get())) {
14740 buf->putRawChar8(3); // ellipse arc
14741 buf->put2RawDouble(el->basePoint);
14742 buf->put2RawDouble(el->secPoint);
14743 buf->putBitDouble(el->ratio);
14744 buf->putBitDouble(el->staparam);
14745 buf->putBitDouble(el->endparam);
14746 buf->putBit(static_cast<std::uint8_t>(el->isccw));
14747 } else if (const auto* sp = dynamic_cast<const DRW_Spline*>(seg.get())) {
14748 buf->putRawChar8(4); // spline
14749 buf->putBitLong(sp->degree);
14750 bool isRational = (sp->flags & 4) != 0;
14751 bool isPeriodic = (sp->flags & 2) != 0;
14752 buf->putBit(static_cast<std::uint8_t>(isRational));
14753 buf->putBit(static_cast<std::uint8_t>(isPeriodic));
14754 buf->putBitLong(static_cast<std::int32_t>(sp->knotslist.size()));
14755 buf->putBitLong(static_cast<std::int32_t>(sp->controllist.size()));
14756 for (double k : sp->knotslist)
14757 buf->putBitDouble(k);
14758 for (const auto& cp : sp->controllist) {
14759 DRW_Coord c2{cp->x, cp->y, 0.0};
14760 buf->put2RawDouble(c2);
14761 if (isRational)
14762 buf->putBitDouble(cp->z);
14763 }
14764 if (version > DRW::AC1021) {
14765 buf->putBitLong(static_cast<std::int32_t>(sp->fitlist.size()));
14766 for (const auto& fp : sp->fitlist) {
14767 DRW_Coord f2{fp->x, fp->y, 0.0};
14768 buf->put2RawDouble(f2);
14769 }
14770 buf->put2RawDouble(sp->tgStart);
14771 buf->put2RawDouble(sp->tgEnd);
14772 }
14773 } else {
14774 return false;
14775 }
14776 }
14777 } else {
14778 const DRW_LWPolyline* pl = nullptr;
14779 if (!lp->objlist.empty())
14780 pl = dynamic_cast<const DRW_LWPolyline*>(lp->objlist[0].get());
14781 if (!pl)
14782 return false;
14783
14784 bool asBulge = false;
14785 for (const auto& v : pl->vertlist)
14786 if (v->bulge != 0.0) { asBulge = true; break; }
14787
14788 buf->putBit(static_cast<std::uint8_t>(asBulge));
14789 buf->putBit(static_cast<std::uint8_t>(pl->flags & 1));
14790 buf->putBitLong(static_cast<std::int32_t>(pl->vertlist.size()));
14791 for (const auto& v : pl->vertlist) {
14792 buf->putRawDouble(v->x);
14793 buf->putRawDouble(v->y);
14794 if (asBulge)
14795 buf->putBitDouble(v->bulge);
14796 }
14797 }
14798
14799 buf->putBitLong(0); // numBoundHandles for this loop (0 = non-associative)
14800 }
14801
14802 return true;
14803}
14804
14805bool DRW_Hatch::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
14806 (void)bs;
14807 if (!validateDwgHatchData(*this))
14808 return false;
14809 oType = 78; // HATCH class id — see dwgreader.cpp:1380
14810 if (!encodeDwgCommon(version, buf)) return false;
14811
14812 dwgBufferW *sb = strBuf ? strBuf : buf;
14813 encodeDwgGradientData(version, buf, sb);
14814
14815 buf->putBitDouble(basePoint.z); // BD: elevation
14816 buf->put3BitDouble(extPoint); // 3BD: extrusion (NOT BE-style for HATCH)
14817 sb->putVariableText(version, name); // TV: hatch pattern name
14818 buf->putBit(static_cast<std::uint8_t>(solid));
14819 buf->putBit(static_cast<std::uint8_t>(associative));
14820 if (!encodeDwgBoundaryData(version, buf, true)) return false;
14821
14822 buf->putBitShort(static_cast<std::uint16_t>(hstyle));
14823 buf->putBitShort(static_cast<std::uint16_t>(hpattern));
14824
14825 if (!solid) {
14826 buf->putBitDouble(angle);
14827 buf->putBitDouble(scale);
14828 buf->putBit(static_cast<std::uint8_t>(doubleflag));
14829 buf->putBitShort(static_cast<std::uint16_t>(patternLines.size()));
14830 for (const PatternLine& patternLine : patternLines) {
14831 buf->putBitDouble(patternLine.angle);
14832 buf->putBitDouble(patternLine.baseX);
14833 buf->putBitDouble(patternLine.baseY);
14834 buf->putBitDouble(patternLine.offsetX);
14835 buf->putBitDouble(patternLine.offsetY);
14836 buf->putBitShort(static_cast<std::uint16_t>(patternLine.dashList.size()));
14837 for (double dash : patternLine.dashList)
14838 buf->putBitDouble(dash);
14839 }
14840 }
14841
14842 bool havePixelSize = false;
14843 for (const auto& lp : looplist)
14844 havePixelSize = havePixelSize || (lp && ((lp->type & 4) != 0));
14845 if (havePixelSize)
14846 buf->putBitDouble(pixelSize);
14847
14848 buf->putBitLong(static_cast<std::int32_t>(seedPoints.size()));
14849 for (const auto& sp : seedPoints) {
14850 buf->putRawDouble(sp.x);
14851 buf->putRawDouble(sp.y);
14852 }
14853
14854 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
14855 return true;
14856}
14857
14858bool DRW_Spline::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
14859 switch (code) {
14860 case 210:
14861 normalVec.x = reader->getDouble();
14862 break;
14863 case 220:
14864 normalVec.y = reader->getDouble();
14865 break;
14866 case 230:
14867 normalVec.z = reader->getDouble();
14868 break;
14869 case 12:
14870 tgStart.x = reader->getDouble();
14871 break;
14872 case 22:
14873 tgStart.y = reader->getDouble();
14874 break;
14875 case 32:
14876 tgStart.z = reader->getDouble();
14877 break;
14878 case 13:
14879 tgEnd.x = reader->getDouble();
14880 break;
14881 case 23:
14882 tgEnd.y = reader->getDouble();
14883 break;
14884 case 33:
14885 tgEnd.z = reader->getDouble();
14886 break;
14887 case 70:
14888 flags = reader->getInt32();
14889 break;
14890 case 71:
14891 degree = reader->getInt32();
14892 if (!isValidCount(degree, kMaxDxfDegree) || degree == 0)
14893 return false;
14894 m_dxfDegreeSeen = true;
14895 break;
14896 case 72:
14897 if (m_dxfKnotCountSeen)
14898 return false;
14899 nknots = reader->getInt32();
14900 if (!isValidCount(nknots, kMaxDxfItems))
14901 return false;
14902 m_dxfKnotCountSeen = true;
14903 break;
14904 case 73:
14905 if (m_dxfControlCountSeen)
14906 return false;
14907 ncontrol = reader->getInt32();
14908 if (!isValidCount(ncontrol, kMaxDxfItems))
14909 return false;
14910 m_dxfControlCountSeen = true;
14911 break;
14912 case 74:
14913 if (m_dxfFitCountSeen)
14914 return false;
14915 nfit = reader->getInt32();
14916 if (!isValidCount(nfit, kMaxDxfItems))
14917 return false;
14918 m_dxfFitCountSeen = true;
14919 break;
14920 case 42:
14921 tolknot = reader->getDouble();
14922 break;
14923 case 43:
14924 tolcontrol = reader->getDouble();
14925 break;
14926 case 44:
14927 tolfit = reader->getDouble();
14928 break;
14929 case 10: {
14930 if (controllist.size() >= static_cast<std::size_t>(kMaxDxfItems)
14931 || (m_dxfControlCountSeen
14932 && controllist.size() >= static_cast<std::size_t>(ncontrol)))
14933 return false;
14934 controlpoint = std::make_shared<DRW_Coord>();
14935 controllist.push_back(controlpoint);
14936 controlpoint->x = reader->getDouble();
14937 break; }
14938 case 20:
14939 if(controlpoint)
14940 controlpoint->y = reader->getDouble();
14941 break;
14942 case 30:
14943 if(controlpoint)
14944 controlpoint->z = reader->getDouble();
14945 break;
14946 case 11: {
14947 if (fitlist.size() >= static_cast<std::size_t>(kMaxDxfItems)
14948 || (m_dxfFitCountSeen
14949 && fitlist.size() >= static_cast<std::size_t>(nfit)))
14950 return false;
14951 fitpoint = std::make_shared<DRW_Coord>();
14952 fitlist.push_back(fitpoint);
14953 fitpoint->x = reader->getDouble();
14954 break; }
14955 case 21:
14956 if(fitpoint)
14957 fitpoint->y = reader->getDouble();
14958 break;
14959 case 31:
14960 if(fitpoint)
14961 fitpoint->z = reader->getDouble();
14962 break;
14963 case 40:
14964 if (knotslist.size() >= static_cast<std::size_t>(kMaxDxfItems)
14965 || (m_dxfKnotCountSeen
14966 && knotslist.size() >= static_cast<std::size_t>(nknots)))
14967 return false;
14968 knotslist.push_back(reader->getDouble());
14969 break;
14970 case 41:
14971 if (weightlist.size() >= static_cast<std::size_t>(kMaxDxfItems)
14972 || (m_dxfControlCountSeen
14973 && weightlist.size() >= static_cast<std::size_t>(ncontrol)))
14974 return false;
14975 weightlist.push_back(reader->getDouble());
14976 break;
14977 default:
14978 return DRW_Entity::parseCode(code, reader);
14979 }
14980
14981 return true;
14982}
14983
14984bool DRW_Spline::validateDxf() const {
14985 if (m_dxfKnotCountSeen
14986 && knotslist.size() != static_cast<std::size_t>(nknots))
14987 return false;
14988 if (m_dxfControlCountSeen
14989 && controllist.size() != static_cast<std::size_t>(ncontrol))
14990 return false;
14991 if (m_dxfFitCountSeen
14992 && fitlist.size() != static_cast<std::size_t>(nfit))
14993 return false;
14994 return validatePayloadFields(/*allowMixedLists=*/true);
14995}
14996
14997bool DRW_Spline::validatePayloadFields(bool allowMixedLists) const {
14998 const auto finite = [](double value) { return std::isfinite(value); };
14999 const auto finiteCoord = [&finite](const DRW_Coord& point) {
15000 return finite(point.x) && finite(point.y) && finite(point.z);
15001 };
15002 const auto validCount = [](std::size_t count) {
15003 return count <= static_cast<std::size_t>(kMaxSplineItems)
15004 && count <= static_cast<std::size_t>(
15005 std::numeric_limits<std::int32_t>::max());
15006 };
15007 const auto validPointList = [&finiteCoord, &validCount](const auto& points) {
15008 return validCount(points.size())
15009 && std::all_of(points.cbegin(), points.cend(),
15010 [&finiteCoord](const auto& point) {
15011 return point != nullptr && finiteCoord(*point);
15012 });
15013 };
15014
15015 if (!isValidSplineDegree(degree)
15016 // Bits above 16 (AutoCAD 2013+ sets 32 and 1024 on fit splines) are
15017 // not used, but a 16-bit value is accepted.
15018 || flags < 0 || flags > 0xFFFF
15019 || !finiteCoord(normalVec) || !finiteCoord(tgStart)
15020 || !finiteCoord(tgEnd) || !finite(tolknot)
15021 || !finite(tolcontrol) || !finite(tolfit)
15022 || !validCount(knotslist.size())
15023 || !std::all_of(knotslist.cbegin(), knotslist.cend(), finite)
15024 || !validCount(weightlist.size())
15025 || !std::all_of(weightlist.cbegin(), weightlist.cend(), finite)
15026 || !validPointList(controllist) || !validPointList(fitlist)
15027 || weightlist.size() > controllist.size())
15028 return false;
15029
15030 // The DWG body has disjoint control-point and fit-point scenarios. When
15031 // both lists are present, the encoder selects control points and would
15032 // silently discard the fit points, so reject that lossy representation.
15033 if (!allowMixedLists && !fitlist.empty() && !controllist.empty())
15034 return false;
15035 if (!fitlist.empty() && !controllist.empty())
15036 return isValidControlSplineLayout(
15037 degree, static_cast<std::int32_t>(knotslist.size()),
15038 static_cast<std::int32_t>(controllist.size()))
15039 && isValidFitSplineLayout(
15040 degree, static_cast<std::int32_t>(fitlist.size()));
15041 if (!fitlist.empty())
15042 return isValidFitSplineLayout(
15043 degree, static_cast<std::int32_t>(fitlist.size()));
15044 return isValidControlSplineLayout(
15045 degree, static_cast<std::int32_t>(knotslist.size()),
15046 static_cast<std::int32_t>(controllist.size()));
15047}
15048
15049bool DRW_Helix::validateDxf() const {
15050 const auto finite = [](const DRW_Coord& point) {
15051 return std::isfinite(point.x) && std::isfinite(point.y)
15052 && std::isfinite(point.z);
15053 };
15054 return DRW_Spline::validateDxf()
15055 && finite(axisBasePt) && finite(startPt) && finite(axisVector)
15056 && std::isfinite(radius) && std::isfinite(turns)
15057 && std::isfinite(turnHeight);
15058}
15059
15060bool DRW_Helix::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
15061 if (code == 100) {
15062 const std::string subclass = reader->getString();
15063 m_parsingHelixSubclass = (subclass == "AcDbHelix");
15064 return true;
15065 }
15066
15067 if (!m_parsingHelixSubclass)
15068 return DRW_Spline::parseCode(code, reader);
15069
15070 switch (code) {
15071 case 90:
15072 m_majorVersion = reader->getInt32();
15073 break;
15074 case 91:
15075 m_maintVersion = reader->getInt32();
15076 break;
15077 case 10:
15078 axisBasePt.x = reader->getDouble();
15079 break;
15080 case 20:
15081 axisBasePt.y = reader->getDouble();
15082 break;
15083 case 30:
15084 axisBasePt.z = reader->getDouble();
15085 break;
15086 case 11:
15087 startPt.x = reader->getDouble();
15088 break;
15089 case 21:
15090 startPt.y = reader->getDouble();
15091 break;
15092 case 31:
15093 startPt.z = reader->getDouble();
15094 break;
15095 case 12:
15096 axisVector.x = reader->getDouble();
15097 break;
15098 case 22:
15099 axisVector.y = reader->getDouble();
15100 break;
15101 case 32:
15102 axisVector.z = reader->getDouble();
15103 break;
15104 case 40:
15105 radius = reader->getDouble();
15106 break;
15107 case 41:
15108 turns = reader->getDouble();
15109 break;
15110 case 42:
15111 turnHeight = reader->getDouble();
15112 break;
15113 case 290:
15114 {
15115 int value = 0;
15116 if (!readDxfIntInRange(reader, 0, 1, value))
15117 return false;
15118 handedness = value != 0;
15119 }
15120 break;
15121 case 280:
15122 {
15123 int value = 0;
15124 if (!readDxfIntInRange(reader, 0, 0xFF, value))
15125 return false;
15126 constraintType = static_cast<std::uint8_t>(value);
15127 }
15128 break;
15129 default:
15130 return DRW_Entity::parseCode(code, reader);
15131 }
15132
15133 return true;
15134}
15135
15136void DRW_Spline::resetDwgState() {
15137 DRW_Entity::reset();
15138 normalVec = DRW_Coord{0.0, 0.0, 0.0};
15139 tgStart = DRW_Coord{0.0, 0.0, 0.0};
15140 tgEnd = DRW_Coord{0.0, 0.0, 0.0};
15141 flags = 0;
15142 degree = 0;
15143 m_scenario = 0;
15144 m_splineFlags1 = 0;
15145 m_knotParam = 15;
15146 nknots = 0;
15147 ncontrol = 0;
15148 nfit = 0;
15149 tolknot = 0.0000001;
15150 tolcontrol = 0.0000001;
15151 tolfit = 0.0000001;
15152 knotslist.clear();
15153 weightlist.clear();
15154 controllist.clear();
15155 fitlist.clear();
15156 controlpoint.reset();
15157 fitpoint.reset();
15158 m_dxfDegreeSeen = false;
15159 m_dxfKnotCountSeen = false;
15160 m_dxfControlCountSeen = false;
15161 m_dxfFitCountSeen = false;
15162}
15163
15164bool DRW_Spline::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
15165 resetDwgState();
15166 dwgBuffer *sourceBuf = buf;
15167 auto fail = [this, sourceBuf]() {
15168 if (sourceBuf != nullptr)
15169 sourceBuf->invalidate();
15170 resetDwgState();
15171 return false;
15172 };
15173 if (sourceBuf == nullptr)
15174 return fail();
15175
15176 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
15177 buf = &bodyProbe;
15178 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
15179 if (!ret)
15180 return fail();
15181 if (!parseDwgSplineBody(version, buf))
15182 return fail();
15183
15184 std::uint64_t handleEndBit = 0;
15185 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
15186 handleEndBit))
15187 return fail();
15188 /* Common Entity Handle Data */
15189 dwgBuffer handleProbe = buf->forkIndependent();
15190 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
15191 handleEndBit);
15192 if (!ret || !handleProbe.isGood())
15193 return fail();
15194// RS crc; //RS */
15195 *sourceBuf = handleProbe;
15196 return true;
15197}
15198
15199// Spline body decode: the scenario/degree/knots/ctrl/fit section, WITHOUT
15200// the leading DRW_Entity::parseDwg(common) or the trailing parseDwgEntHandle.
15201// Factored out so DRW_Helix can reuse the identical spline payload before its
15202// AcDbHelix trailer (Phase 8a-1).
15203bool DRW_Spline::parseDwgSplineBody(DRW::Version version, dwgBuffer *buf){
15204 try {
15205 DRW_DBG("\n***************************** parsing spline *********************************************\n")DRW_dbg::dbg("\n***************************** parsing spline *********************************************\n"
)
;
15206 if (buf == nullptr || !buf->isGood())
15207 return false;
15208 std::uint8_t weight = 0; // RLZ ??? flags, weight, code 70, bit 4 (16)
15209 const std::uint64_t bodyEndBit = dwgDataEndBit;
15210 std::uint64_t controlBits = 0;
15211
15212 std::int32_t scenario = 0;
15213 if (!readBoundedBitLong(*buf, bodyEndBit, scenario))
15214 return false;
15215 m_scenario = scenario;
15216 DRW_DBG("scenario: ")DRW_dbg::dbg("scenario: "); DRW_DBG(scenario)DRW_dbg::dbg(scenario);
15217 if (version > DRW::AC1024) {
15218 std::int32_t splFlag1 = 0;
15219 std::int32_t knotParam = 0;
15220 if (!readBoundedBitLong(*buf, bodyEndBit, splFlag1)
15221 || !readBoundedBitLong(*buf, bodyEndBit, knotParam))
15222 return false;
15223 m_splineFlags1 = splFlag1;
15224 m_knotParam = knotParam;
15225 if (knotParam == kSplineKnotParamCustom || !(splFlag1 & kSplineFlagUseKnotParameter)) {
15226 scenario = 1;
15227 } else if (splFlag1 & kSplineFlagMethodFitPoints) {
15228 scenario = 2;
15229 }
15230 m_scenario = scenario;
15231 DRW_DBG(" 2013 splFlag1: ")DRW_dbg::dbg(" 2013 splFlag1: "); DRW_DBG(splFlag1)DRW_dbg::dbg(splFlag1);
15232 DRW_DBG(" 2013 knotParam: ")DRW_dbg::dbg(" 2013 knotParam: "); DRW_DBG(knotParam)DRW_dbg::dbg(knotParam);
15233// DRW_DBG("unk bit: "); DRW_DBG(buf->getBit());
15234 }
15235 if (!readBoundedBitLong(*buf, bodyEndBit, degree))
15236 return false;
15237 DRW_DBG(" degree: ")DRW_dbg::dbg(" degree: "); DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("\n")DRW_dbg::dbg("\n");
15238 if (!isValidSplineDegree(degree)) {
15239 DRW_DBG("\ndwg Spline, invalid degree ")DRW_dbg::dbg("\ndwg Spline, invalid degree "); DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("\n")DRW_dbg::dbg("\n");
15240 return false;
15241 }
15242 if (scenario == 2) {
15243 flags = 8;//scenario 2 = not rational & planar
15244 if (m_splineFlags1 & kSplineFlagClosed)
15245 flags |= 1;
15246 if (!readBoundedBitDouble(*buf, bodyEndBit, tolfit)
15247 || !readBoundedBitCoord(*buf, bodyEndBit, tgStart)
15248 || !readBoundedBitCoord(*buf, bodyEndBit, tgEnd))
15249 return false;
15250 DRW_DBG("flags: ")DRW_dbg::dbg("flags: "); DRW_DBG(flags)DRW_dbg::dbg(flags); DRW_DBG(" tolfit: ")DRW_dbg::dbg(" tolfit: "); DRW_DBG(tolfit)DRW_dbg::dbg(tolfit);
15251 DRW_DBG(" Start Tangent: ")DRW_dbg::dbg(" Start Tangent: "); DRW_DBGPT(tgStart.x, tgStart.y, tgStart.z)DRW_dbg::dbgPT(tgStart.x, tgStart.y, tgStart.z);
15252 DRW_DBG("\nEnd Tangent: ")DRW_dbg::dbg("\nEnd Tangent: "); DRW_DBGPT(tgEnd.x, tgEnd.y, tgEnd.z)DRW_dbg::dbgPT(tgEnd.x, tgEnd.y, tgEnd.z);
15253 std::uint32_t parsedFitCount = 0;
15254 if (!readTableBodyCount(version, buf, objSize, kMaxSplineItems, 6,
15255 parsedFitCount)) {
15256 return false;
15257 }
15258 nfit = static_cast<std::int32_t>(parsedFitCount);
15259 if (!isValidFitSplineLayout(degree, nfit)) {
15260 DRW_DBG("\ndwg Spline, invalid fit layout degree/count: ")DRW_dbg::dbg("\ndwg Spline, invalid fit layout degree/count: "
)
;
15261 DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("/")DRW_dbg::dbg("/"); DRW_DBG(nfit)DRW_dbg::dbg(nfit); DRW_DBG("\n")DRW_dbg::dbg("\n");
15262 return false;
15263 }
15264 DRW_DBG("\nnumber of fit points: ")DRW_dbg::dbg("\nnumber of fit points: "); DRW_DBG(nfit)DRW_dbg::dbg(nfit);
15265 } else if (scenario == 1) {
15266 flags = 8;//scenario 1 = rational & planar
15267 bool rational = false;
15268 bool closed = false;
15269 bool periodic = false;
15270 if (!readBoundedBit(*buf, bodyEndBit, rational)
15271 || !readBoundedBit(*buf, bodyEndBit, closed)
15272 || !readBoundedBit(*buf, bodyEndBit, periodic)
15273 || !readBoundedBitDouble(*buf, bodyEndBit, tolknot)
15274 || !readBoundedBitDouble(*buf, bodyEndBit, tolcontrol))
15275 return false;
15276 flags |= rational ? 4 : 0;
15277 flags |= closed ? 1 : 0;
15278 flags |= periodic ? 2 : 0;
15279 DRW_DBG("flags: ")DRW_dbg::dbg("flags: "); DRW_DBG(flags)DRW_dbg::dbg(flags); DRW_DBG(" knot tolerance: ")DRW_dbg::dbg(" knot tolerance: "); DRW_DBG(tolknot)DRW_dbg::dbg(tolknot);
15280 DRW_DBG(" control point tolerance: ")DRW_dbg::dbg(" control point tolerance: "); DRW_DBG(tolcontrol)DRW_dbg::dbg(tolcontrol);
15281 std::uint32_t parsedKnotCount = 0;
15282 if (!readTableBodyCount(version, buf, objSize, kMaxSplineItems, 2,
15283 parsedKnotCount)) {
15284 return false;
15285 }
15286 nknots = static_cast<std::int32_t>(parsedKnotCount);
15287 std::uint32_t parsedControlCount = 0;
15288 if (!readTableBodyCount(version, buf, objSize, kMaxSplineItems, 6,
15289 parsedControlCount)) {
15290 return false;
15291 }
15292 ncontrol = static_cast<std::int32_t>(parsedControlCount);
15293 if (!isValidControlSplineLayout(degree, nknots, ncontrol)) {
15294 DRW_DBG("\ndwg Spline, invalid control layout degree/knots/control: ")DRW_dbg::dbg("\ndwg Spline, invalid control layout degree/knots/control: "
)
;
15295 DRW_DBG(degree)DRW_dbg::dbg(degree); DRW_DBG("/")DRW_dbg::dbg("/"); DRW_DBG(nknots)DRW_dbg::dbg(nknots); DRW_DBG("/")DRW_dbg::dbg("/");
15296 DRW_DBG(ncontrol)DRW_dbg::dbg(ncontrol); DRW_DBG("\n")DRW_dbg::dbg("\n");
15297 return false;
15298 }
15299 bool hasWeights = false;
15300 if (!readBoundedBit(*buf, bodyEndBit, hasWeights))
15301 return false;
15302 weight = hasWeights ? 1 : 0; // flags bit 4: weights present (code 70)
15303 if (weight) flags |= 0x10;
15304 if (!dwgSafety::multiply(static_cast<std::uint64_t>(ncontrol),
15305 weight ? 8u : 6u, controlBits))
15306 return false;
15307 if (!proxyEntityHasBits(*buf, bodyEndBit, controlBits)) {
15308 return false;
15309 }
15310 DRW_DBG("\nnum of knots: ")DRW_dbg::dbg("\nnum of knots: "); DRW_DBG(nknots)DRW_dbg::dbg(nknots); DRW_DBG(" num of control pt: ")DRW_dbg::dbg(" num of control pt: ");
15311 DRW_DBG(ncontrol)DRW_dbg::dbg(ncontrol); DRW_DBG(" weight bit: ")DRW_dbg::dbg(" weight bit: "); DRW_DBG(weight)DRW_dbg::dbg(weight);
15312 } else {
15313 DRW_DBG("\ndwg Spline, unknown scenario ")DRW_dbg::dbg("\ndwg Spline, unknown scenario "); DRW_DBG(scenario)DRW_dbg::dbg(scenario);
15314 DRW_DBG(" (expected 1 or 2)\n")DRW_dbg::dbg(" (expected 1 or 2)\n");
15315 return false; //RLZ: from doc only 1 or 2 are ok ?
15316 }
15317
15318 std::vector<double> parsedKnots;
15319 std::vector<double> parsedWeights;
15320 std::vector<std::shared_ptr<DRW_Coord>> parsedControls;
15321 std::vector<std::shared_ptr<DRW_Coord>> parsedFits;
15322 std::uint64_t knotBits = 0;
15323 std::uint64_t fitBits = 0;
15324 std::uint64_t requiredBits = 0;
15325 if (!dwgSafety::multiply(static_cast<std::uint64_t>(nknots), 2,
15326 knotBits)
15327 || !dwgSafety::multiply(static_cast<std::uint64_t>(nfit), 6,
15328 fitBits)
15329 || !dwgSafety::add(knotBits, controlBits, requiredBits)
15330 || !dwgSafety::add(requiredBits, fitBits, requiredBits)
15331 || !proxyEntityHasBits(*buf, bodyEndBit, requiredBits)) {
15332 return false;
15333 }
15334 if (!DRW::reserve(parsedKnots, nknots)) {
15335 return false;
15336 }
15337 for (std::int32_t i= 0; i<nknots; ++i){
15338 double knot = 0.0;
15339 if (!readBoundedBitDouble(*buf, bodyEndBit, knot)
15340 || !std::isfinite(knot))
15341 return false;
15342 parsedKnots.push_back(knot);
15343 }
15344 if (!DRW::reserve(parsedControls, ncontrol)) {
15345 return false;
15346 }
15347 if (weight && !DRW::reserve(parsedWeights, ncontrol)) {
15348 return false;
15349 }
15350 for (std::int32_t i= 0; i<ncontrol; ++i){
15351 DRW_Coord control;
15352 if (!readBoundedBitCoord(*buf, bodyEndBit, control)
15353 || !std::isfinite(control.x)
15354 || !std::isfinite(control.y)
15355 || !std::isfinite(control.z))
15356 return false;
15357 parsedControls.push_back(std::make_shared<DRW_Coord>(control));
15358 if (weight) {
15359 //per-control-point weight; required for hyperbola/parabola
15360 //conic detection in consumers (e.g. LibreCAD addSpline)
15361 double w = 0.0; //RLZ Warning: D (BD or RD)
15362 if (!readBoundedBitDouble(*buf, bodyEndBit, w)
15363 || !std::isfinite(w))
15364 return false;
15365 parsedWeights.push_back(w);
15366 DRW_DBG("\n w: ")DRW_dbg::dbg("\n w: "); DRW_DBG(w)DRW_dbg::dbg(w);
15367 }
15368 }
15369 if (!DRW::reserve(parsedFits, nfit)) {
15370 return false;
15371 }
15372 for (std::int32_t i= 0; i<nfit; ++i) {
15373 DRW_Coord fit;
15374 if (!readBoundedBitCoord(*buf, bodyEndBit, fit)
15375 || !std::isfinite(fit.x)
15376 || !std::isfinite(fit.y)
15377 || !std::isfinite(fit.z))
15378 return false;
15379 parsedFits.push_back(std::make_shared<DRW_Coord>(fit));
15380 }
15381
15382 if (DRW_DBGGLDRW_dbg::getInstance()->getLevel() == DRW_dbg::Level::Debug) {
15383 DRW_DBG("\nknots list: ")DRW_dbg::dbg("\nknots list: ");
15384 for (auto const& v: parsedKnots) {
15385 DRW_DBG("\n")DRW_dbg::dbg("\n"); DRW_DBG(v)DRW_dbg::dbg(v);
15386 }
15387 DRW_DBG("\ncontrol point list: ")DRW_dbg::dbg("\ncontrol point list: ");
15388 for (auto const& v: parsedControls) {
15389 DRW_DBG("\n")DRW_dbg::dbg("\n"); DRW_DBGPT(v->x, v->y, v->z)DRW_dbg::dbgPT(v->x, v->y, v->z);
15390 }
15391 DRW_DBG("\nfit point list: ")DRW_dbg::dbg("\nfit point list: ");
15392 for (auto const& v: parsedFits) {
15393 DRW_DBG("\n")DRW_dbg::dbg("\n"); DRW_DBGPT(v->x, v->y, v->z)DRW_dbg::dbgPT(v->x, v->y, v->z);
15394 }
15395 }
15396
15397 if (!buf->isGood())
15398 return false;
15399 knotslist = std::move(parsedKnots);
15400 weightlist = std::move(parsedWeights);
15401 controllist = std::move(parsedControls);
15402 fitlist = std::move(parsedFits);
15403 return true;
15404 } catch (...) {
15405 return false;
15406 }
15407}
15408
15409// AcDbHelix trailer order (libreDWG dwg2.spec:2493-2503):
15410// major_version BL, maint_version BL, axis_base_pt 3BD, start_pt 3BD,
15411// axis_vector 3BD, radius BD, turns BD, turn_height BD, handedness B,
15412// constraint_type RC.
15413void DRW_Helix::resetDwgState() {
15414 DRW_Spline::resetDwgState();
15415 m_majorVersion = 0;
15416 m_maintVersion = 0;
15417 axisBasePt = DRW_Coord{0.0, 0.0, 0.0};
15418 startPt = DRW_Coord{0.0, 0.0, 0.0};
15419 axisVector = DRW_Coord{0.0, 0.0, 0.0};
15420 radius = 0.0;
15421 turns = 0.0;
15422 turnHeight = 0.0;
15423 handedness = false;
15424 constraintType = 0;
15425 m_parsingHelixSubclass = false;
15426}
15427
15428bool DRW_Helix::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
15429 resetDwgState();
15430 dwgBuffer *sourceBuf = buf;
15431 auto fail = [this, sourceBuf]() {
15432 if (sourceBuf != nullptr)
15433 sourceBuf->invalidate();
15434 resetDwgState();
15435 return false;
15436 };
15437 if (sourceBuf == nullptr)
15438 return fail();
15439
15440 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
15441 buf = &bodyProbe;
15442 bool ret = DRW_Entity::parseDwg(version, buf, NULL__null, bs);
15443 if (!ret)
15444 return fail();
15445 DRW_DBG("\n***************************** parsing helix *********************************************\n")DRW_dbg::dbg("\n***************************** parsing helix *********************************************\n"
)
;
15446 if (!parseDwgSplineBody(version, buf))
15447 return fail();
15448
15449 // AcDbHelix trailer (see field order above).
15450 const std::uint64_t bodyEndBit = dwgDataEndBit;
15451 std::int32_t parsedMajorVersion = 0;
15452 std::int32_t parsedMaintVersion = 0;
15453 DRW_Coord parsedAxisBasePt;
15454 DRW_Coord parsedStartPt;
15455 DRW_Coord parsedAxisVector;
15456 double parsedRadius = 0.0;
15457 double parsedTurns = 0.0;
15458 double parsedTurnHeight = 0.0;
15459 bool parsedHandedness = false;
15460 std::uint8_t parsedConstraintType = 0;
15461 if (!readBoundedBitLong(*buf, bodyEndBit, parsedMajorVersion)
15462 || !readBoundedBitLong(*buf, bodyEndBit, parsedMaintVersion)
15463 || !readBoundedBitCoord(*buf, bodyEndBit, parsedAxisBasePt)
15464 || !readBoundedBitCoord(*buf, bodyEndBit, parsedStartPt)
15465 || !readBoundedBitCoord(*buf, bodyEndBit, parsedAxisVector)
15466 || !readBoundedBitDouble(*buf, bodyEndBit, parsedRadius)
15467 || !readBoundedBitDouble(*buf, bodyEndBit, parsedTurns)
15468 || !readBoundedBitDouble(*buf, bodyEndBit, parsedTurnHeight)
15469 || !readBoundedBit(*buf, bodyEndBit, parsedHandedness)
15470 || !readBoundedRawChar8(*buf, bodyEndBit, parsedConstraintType)
15471 || !std::isfinite(parsedAxisBasePt.x)
15472 || !std::isfinite(parsedAxisBasePt.y)
15473 || !std::isfinite(parsedAxisBasePt.z)
15474 || !std::isfinite(parsedStartPt.x)
15475 || !std::isfinite(parsedStartPt.y)
15476 || !std::isfinite(parsedStartPt.z)
15477 || !std::isfinite(parsedAxisVector.x)
15478 || !std::isfinite(parsedAxisVector.y)
15479 || !std::isfinite(parsedAxisVector.z)
15480 || !std::isfinite(parsedRadius)
15481 || !std::isfinite(parsedTurns)
15482 || !std::isfinite(parsedTurnHeight))
15483 return fail();
15484 DRW_DBG("\nhelix radius: ")DRW_dbg::dbg("\nhelix radius: "); DRW_DBG(parsedRadius)DRW_dbg::dbg(parsedRadius); DRW_DBG(" turns: ")DRW_dbg::dbg(" turns: "); DRW_DBG(parsedTurns)DRW_dbg::dbg(parsedTurns);
15485
15486 std::uint64_t handleEndBit = 0;
15487 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
15488 handleEndBit))
15489 return fail();
15490 /* Common Entity Handle Data */
15491 dwgBuffer handleProbe = buf->forkIndependent();
15492 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
15493 handleEndBit);
15494 if (!ret || !handleProbe.isGood())
15495 return fail();
15496 // RS crc; //RS */
15497 m_majorVersion = parsedMajorVersion;
15498 m_maintVersion = parsedMaintVersion;
15499 axisBasePt = parsedAxisBasePt;
15500 startPt = parsedStartPt;
15501 axisVector = parsedAxisVector;
15502 radius = parsedRadius;
15503 turns = parsedTurns;
15504 turnHeight = parsedTurnHeight;
15505 handedness = parsedHandedness;
15506 constraintType = parsedConstraintType;
15507 *sourceBuf = handleProbe;
15508 return true;
15509}
15510
15511bool DRW_Image::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
15512 switch (code) {
15513 case 12:
15514 vVector.x = reader->getDouble();
15515 break;
15516 case 22:
15517 vVector.y = reader->getDouble();
15518 break;
15519 case 32:
15520 vVector.z = reader->getDouble();
15521 break;
15522 case 13:
15523 sizeu = reader->getDouble();
15524 break;
15525 case 23:
15526 sizev = reader->getDouble();
15527 break;
15528 case 70:
15529 {
15530 int value = 0;
15531 if (!readDxfIntInRange(reader, 0, 0xFFFF, value))
15532 return false;
15533 m_displayProps = value;
15534 }
15535 break;
15536 case 90: {
15537 const std::int32_t value = reader->getInt32();
15538 if (value < 0 || value > kMaxClassVersion)
15539 return false;
15540 m_classVersion = value;
15541 break;
15542 }
15543 case 340:
15544 ref = reader->getHandleString();
15545 break;
15546 case 360:
15547 m_imageDefReactorHandle = reader->getHandleString();
15548 break;
15549 case 280:
15550 {
15551 int value = 0;
15552 if (!readDxfIntInRange(reader, 0, 1, value))
15553 return false;
15554 clip = value;
15555 }
15556 break;
15557 case 281:
15558 {
15559 int value = 0;
15560 if (!readDxfIntInRange(reader, 0, 100, value))
15561 return false;
15562 brightness = value;
15563 }
15564 break;
15565 case 282:
15566 {
15567 int value = 0;
15568 if (!readDxfIntInRange(reader, 0, 100, value))
15569 return false;
15570 contrast = value;
15571 }
15572 break;
15573 case 283:
15574 {
15575 int value = 0;
15576 if (!readDxfIntInRange(reader, 0, 100, value))
15577 return false;
15578 fade = value;
15579 }
15580 break;
15581 case 71:
15582 {
15583 int value = 0;
15584 if (!readDxfIntInRange(reader, 0, 2, value))
15585 return false;
15586 m_clipBoundaryType = value;
15587 }
15588 break;
15589 case 91:
15590 // The declared count is a structural invariant: reject negative or
15591 // implausibly large values before reserve() can allocate unboundedly.
15592 {
15593 const std::int32_t count = reader->getInt32();
15594 if (count < 0 || count > static_cast<std::int32_t>(DRW_Image::kMaxClipVertices))
15595 return false;
15596 clipPath.clear();
15597 if (!DRW::reserve(clipPath, count))
15598 return false;
15599 m_declaredClipVertexCount = count;
15600 m_clipPathHasOpenVertex = false;
15601 }
15602 break;
15603 case 14:
15604 // WIPEOUT polygon vertex x — start a new vertex. Group 24 (y) follows.
15605 if (m_clipPathHasOpenVertex)
15606 return false;
15607 clipPath.emplace_back(reader->getDouble(), 0.0);
15608 m_clipPathHasOpenVertex = true;
15609 break;
15610 case 24:
15611 // WIPEOUT polygon vertex y — complete the most recently started vertex.
15612 if (!m_clipPathHasOpenVertex || clipPath.empty())
15613 return false;
15614 clipPath.back().y = reader->getDouble();
15615 m_clipPathHasOpenVertex = false;
15616 break;
15617 case 290:
15618 // R2010+ Clip mode (IMAGE/WIPEOUT, ODA spec §20.4.80):
15619 // 0 = mask outside the polygon, 1 = mask inside.
15620 clipMode = reader->getBool();
15621 break;
15622 default:
15623 return DRW_Line::parseCode(code, reader);
15624 }
15625
15626 return true;
15627}
15628
15629bool DRW_Image::hasValidClipBoundary() const {
15630 if (m_clipPathHasOpenVertex
15631 || (m_declaredClipVertexCount >= 0
15632 && static_cast<std::size_t>(m_declaredClipVertexCount) != clipPath.size())) {
15633 return false;
15634 }
15635 switch (m_clipBoundaryType) {
15636 case 0:
15637 return clipPath.empty();
15638 case 1:
15639 return clipPath.size() == 2;
15640 case 2:
15641 return clipPath.size() >= 3;
15642 default:
15643 return false;
15644 }
15645}
15646
15647bool DRW_Image::validatePayloadFields() const {
15648 const auto finite = [](const DRW_Coord& point) {
15649 return std::isfinite(point.x) && std::isfinite(point.y)
15650 && std::isfinite(point.z);
15651 };
15652 return m_classVersion >= 0 && m_classVersion <= kMaxClassVersion
15653 && m_displayProps >= 0 && m_displayProps <= 0xffff
15654 && clip >= 0 && clip <= 1
15655 && brightness >= 0 && brightness <= 0xff
15656 && contrast >= 0 && contrast <= 0xff
15657 && fade >= 0 && fade <= 0xff
15658 && std::isfinite(sizeu) && std::isfinite(sizev)
15659 && finite(basePoint) && finite(secPoint) && finite(vVector)
15660 && clipPath.size() <= kMaxClipVertices
15661 && std::all_of(clipPath.cbegin(), clipPath.cend(),
15662 [](const DRW_Coord& point) {
15663 return std::isfinite(point.x)
15664 && std::isfinite(point.y);
15665 });
15666}
15667
15668void DRW_Image::resetDwgState() {
15669 DRW_Line::resetDwgState();
15670 m_classVersion = 0;
15671 ref = 0;
15672 m_imageDefReactorHandle = 0;
15673 m_displayProps = 0;
15674 vVector = DRW_Coord{0.0, 0.0, 0.0};
15675 sizeu = 0.0;
15676 sizev = 0.0;
15677 dz = 0.0;
15678 clip = 0;
15679 brightness = 50;
15680 contrast = 50;
15681 fade = 0;
15682 m_clipBoundaryType = 0;
15683 clipPath.clear();
15684 clipMode = false;
15685 m_declaredClipVertexCount = -1;
15686 m_clipPathHasOpenVertex = false;
15687}
15688
15689bool DRW_Image::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
15690 resetDwgState();
15691 dwgBuffer *sourceBuf = buf;
15692 auto fail = [this, sourceBuf]() {
15693 if (sourceBuf != nullptr)
15694 sourceBuf->invalidate();
15695 resetDwgState();
15696 return false;
15697 };
15698 if (sourceBuf == nullptr)
15699 return fail();
15700
15701 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
15702 buf = &bodyProbe;
15703 dwgBuffer sBuff = buf->forkIndependent();
15704 dwgBuffer *sBuf = buf;
15705 if (version > DRW::AC1018) {//2007+
15706 sBuf = &sBuff; //separate buffer for strings
15707 }
15708 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
15709 if (!ret)
15710 return fail();
15711 DRW_DBG("\n***************************** parsing image *********************************************\n")DRW_dbg::dbg("\n***************************** parsing image *********************************************\n"
)
;
15712
15713 const auto finite = [](const DRW_Coord& point) {
15714 return std::isfinite(point.x) && std::isfinite(point.y)
15715 && std::isfinite(point.z);
15716 };
15717 const std::uint64_t bodyEndBit = dwgDataEndBit;
15718 std::int32_t parsedClassVersion = 0;
15719 DRW_Coord parsedBasePoint;
15720 DRW_Coord parsedSecPoint;
15721 DRW_Coord parsedVVector;
15722 double parsedSizeU = 0.0;
15723 double parsedSizeV = 0.0;
15724 std::uint16_t parsedDisplayProps = 0;
15725 bool parsedClip = false;
15726 std::uint8_t parsedBrightness = 0;
15727 std::uint8_t parsedContrast = 0;
15728 std::uint8_t parsedFade = 0;
15729 bool parsedClipMode = false;
15730 if (!readBoundedBitLong(*buf, bodyEndBit, parsedClassVersion)
15731 || parsedClassVersion < 0 || parsedClassVersion > kMaxClassVersion
15732 || !readBoundedBitCoord(*buf, bodyEndBit, parsedBasePoint)
15733 || !readBoundedBitCoord(*buf, bodyEndBit, parsedSecPoint)
15734 || !readBoundedBitCoord(*buf, bodyEndBit, parsedVVector)
15735 || !readBoundedRawDouble(*buf, bodyEndBit, parsedSizeU)
15736 || !readBoundedRawDouble(*buf, bodyEndBit, parsedSizeV)
15737 || !readBoundedBitShort(*buf, bodyEndBit, parsedDisplayProps)
15738 || !readBoundedBit(*buf, bodyEndBit, parsedClip)
15739 || !readBoundedRawChar8(*buf, bodyEndBit, parsedBrightness)
15740 || !readBoundedRawChar8(*buf, bodyEndBit, parsedContrast)
15741 || !readBoundedRawChar8(*buf, bodyEndBit, parsedFade)
15742 || (version > DRW::AC1021
15743 && !readBoundedBit(*buf, bodyEndBit, parsedClipMode))
15744 || !finite(parsedBasePoint) || !finite(parsedSecPoint)
15745 || !finite(parsedVVector) || !std::isfinite(parsedSizeU)
15746 || !std::isfinite(parsedSizeV))
15747 return fail();
15748 DRW_DBG("class Version: ")DRW_dbg::dbg("class Version: "); DRW_DBG(parsedClassVersion)DRW_dbg::dbg(parsedClassVersion);
15749 DRW_DBG("\nbase point: ")DRW_dbg::dbg("\nbase point: "); DRW_DBGPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint.z)DRW_dbg::dbgPT(parsedBasePoint.x, parsedBasePoint.y, parsedBasePoint
.z)
;
15750 DRW_DBG("\nU vector: ")DRW_dbg::dbg("\nU vector: "); DRW_DBGPT(parsedSecPoint.x, parsedSecPoint.y, parsedSecPoint.z)DRW_dbg::dbgPT(parsedSecPoint.x, parsedSecPoint.y, parsedSecPoint
.z)
;
15751 DRW_DBG("\nV vector: ")DRW_dbg::dbg("\nV vector: "); DRW_DBGPT(parsedVVector.x, parsedVVector.y, parsedVVector.z)DRW_dbg::dbgPT(parsedVVector.x, parsedVVector.y, parsedVVector
.z)
;
15752 DRW_DBG("\nsize U: ")DRW_dbg::dbg("\nsize U: "); DRW_DBG(parsedSizeU)DRW_dbg::dbg(parsedSizeU); DRW_DBG("\nsize V: ")DRW_dbg::dbg("\nsize V: "); DRW_DBG(parsedSizeV)DRW_dbg::dbg(parsedSizeV);
15753 DRW_DBG("\ndisplay props: ")DRW_dbg::dbg("\ndisplay props: "); DRW_DBG(parsedDisplayProps)DRW_dbg::dbg(parsedDisplayProps);
15754
15755 std::uint16_t parsedClipBoundaryType = 0;
15756 if (!readBoundedBitShort(*buf, bodyEndBit, parsedClipBoundaryType))
15757 return fail();
15758 std::vector<DRW_Coord> parsedClipPath;
15759 std::int32_t parsedDeclaredClipVertexCount = -1;
15760 if (parsedClipBoundaryType == 0) {
15761 // No clip boundary payload.
15762 } else if (parsedClipBoundaryType == 1){
15763 // Rectangles are encoded as exactly two opposite corners. Keep that
15764 // canonical payload intact; rendering expands it independently.
15765 DRW_Coord ll;
15766 DRW_Coord ur;
15767 if (!readBoundedRawDouble(*buf, bodyEndBit, ll.x)
15768 || !readBoundedRawDouble(*buf, bodyEndBit, ll.y)
15769 || !readBoundedRawDouble(*buf, bodyEndBit, ur.x)
15770 || !readBoundedRawDouble(*buf, bodyEndBit, ur.y)
15771 || !finite(ll) || !finite(ur))
15772 return fail();
15773 parsedClipPath.push_back(ll);
15774 parsedClipPath.push_back(ur);
15775 parsedDeclaredClipVertexCount = 2;
15776 } else if (parsedClipBoundaryType == 2) {
15777 std::uint32_t numVerts = 0;
15778 if (!readTableBodyCount(version, buf, objSize,
15779 static_cast<std::uint32_t>(kMaxClipVertices),
15780 128, numVerts))
15781 return fail();
15782 std::uint64_t vertexBits = 0;
15783 if (!dwgSafety::multiply(static_cast<std::uint64_t>(numVerts), 128,
15784 vertexBits)
15785 || !proxyEntityHasBits(*buf, bodyEndBit, vertexBits)
15786 || !DRW::reserve(parsedClipPath, numVerts))
15787 return fail();
15788 for (std::uint32_t i = 0; i < numVerts; ++i) {
15789 DRW_Coord point;
15790 if (!readBoundedRawDouble(*buf, bodyEndBit, point.x)
15791 || !readBoundedRawDouble(*buf, bodyEndBit, point.y)
15792 || !finite(point))
15793 return fail();
15794 parsedClipPath.push_back(point);
15795 }
15796 parsedDeclaredClipVertexCount = static_cast<std::int32_t>(numVerts);
15797 } else {
15798 DRW_DBG("unsupported image clip type: ")DRW_dbg::dbg("unsupported image clip type: "); DRW_DBG(parsedClipBoundaryType)DRW_dbg::dbg(parsedClipBoundaryType); DRW_DBG("\n")DRW_dbg::dbg("\n");
15799 return fail();
15800 }
15801
15802 if ((parsedClipBoundaryType == 0 && !parsedClipPath.empty())
15803 || (parsedClipBoundaryType == 2 && parsedClipPath.size() < 3))
15804 return fail();
15805
15806 if (!buf->isGood())
15807 return fail();
15808 std::uint64_t handleEndBit = 0;
15809 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
15810 handleEndBit))
15811 return fail();
15812 dwgBuffer handleProbe = buf->forkIndependent();
15813 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
15814 handleEndBit);
15815 if (!ret)
15816 return fail();
15817 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
15818
15819 if (!proxyEntityHasBits(handleProbe, handleEndBit, 8))
15820 return fail();
15821 dwgHandle biH;
15822 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false, biH))
15823 return fail();
15824 DRW_DBG("ImageDef Handle: ")DRW_dbg::dbg("ImageDef Handle: "); DRW_DBGHL(biH.code, biH.size, biH.ref)DRW_dbg::dbgHL(biH.code, biH.size, biH.ref);
15825 if (!proxyEntityHasBits(handleProbe, handleEndBit, 8))
15826 return fail();
15827 dwgHandle reactorH;
15828 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
15829 reactorH))
15830 return fail();
15831 DRW_DBG("ImageDefReactor Handle: ")DRW_dbg::dbg("ImageDefReactor Handle: "); DRW_DBGHL(reactorH.code, reactorH.size, reactorH.ref)DRW_dbg::dbgHL(reactorH.code, reactorH.size, reactorH.ref);
15832 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
15833 if (!handleProbe.isGood() || !sBuf->isGood())
15834 return fail();
15835 m_classVersion = parsedClassVersion;
15836 basePoint = parsedBasePoint;
15837 secPoint = parsedSecPoint;
15838 vVector = parsedVVector;
15839 sizeu = parsedSizeU;
15840 sizev = parsedSizeV;
15841 m_displayProps = parsedDisplayProps;
15842 clip = parsedClip ? 1 : 0;
15843 brightness = parsedBrightness;
15844 contrast = parsedContrast;
15845 fade = parsedFade;
15846 clipMode = parsedClipMode;
15847 m_clipBoundaryType = parsedClipBoundaryType;
15848 clipPath = std::move(parsedClipPath);
15849 m_declaredClipVertexCount = parsedDeclaredClipVertexCount;
15850 m_clipPathHasOpenVertex = false;
15851 *sourceBuf = handleProbe;
15852 ref = biH.ref;
15853 m_imageDefReactorHandle = reactorH.ref;
15854 return true;
15855}
15856
15857// DRW_Image::encodeDwg — inverse of DRW_Image::parseDwg above (libreDWG
15858// dwg.spec:5533-5563). Body field order: BL class_version, 3 x 3BD
15859// (base/uvec/vvec), 2 x RD (sizeu/sizev), BS display_props, B clip,
15860// 3 x RC (brightness/contrast/fade), [R2010+ B clip_mode], BS
15861// clip_boundary_type + verts. Both handles (imagedef code 5 + reactor
15862// code 3) are emitted UNCONDITIONALLY at the END of the handle stream,
15863// matching parseDwg's order — NOT the spec's interleaved mid-stream slots.
15864bool DRW_Image::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
15865 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
15866 (void)bs; (void)strBuf;
15867 if (!validatePayloadFields()) {
15868 DRW_DBG("IMAGE has an invalid scalar or clip field\n")DRW_dbg::dbg("IMAGE has an invalid scalar or clip field\n");
15869 return false;
15870 }
15871 // Callers sometimes populate clipPath without setting m_clipBoundaryType
15872 // (DXF import historically stored only the vertices). Infer a coherent type
15873 // so encode does not reject a well-formed polygon/rectangle path.
15874 if (m_clipBoundaryType == 0 && !clipPath.empty()) {
15875 if (clipPath.size() == 2)
15876 m_clipBoundaryType = 1;
15877 else if (clipPath.size() >= 3)
15878 m_clipBoundaryType = 2;
15879 }
15880 if (!hasValidClipBoundary()) {
15881 DRW_DBG("IMAGE has invalid clip boundary\n")DRW_dbg::dbg("IMAGE has invalid clip boundary\n");
15882 return false;
15883 }
15884 oType = 101; // IMAGE class id — see dwgreader.cpp case 101
15885 if (!encodeDwgCommon(version, buf)) return false;
15886
15887 buf->putBitLong(m_classVersion);
15888 buf->putBitDouble(basePoint.x); buf->putBitDouble(basePoint.y); buf->putBitDouble(basePoint.z);
15889 buf->putBitDouble(secPoint.x); buf->putBitDouble(secPoint.y); buf->putBitDouble(secPoint.z); // uvec
15890 buf->putBitDouble(vVector.x); buf->putBitDouble(vVector.y); buf->putBitDouble(vVector.z);
15891 buf->putRawDouble(sizeu);
15892 buf->putRawDouble(sizev);
15893 buf->putBitShort(static_cast<std::uint16_t>(m_displayProps));
15894 buf->putBit(static_cast<std::uint8_t>(clip & 1));
15895 buf->putRawChar8(static_cast<std::uint8_t>(brightness));
15896 buf->putRawChar8(static_cast<std::uint8_t>(contrast));
15897 buf->putRawChar8(static_cast<std::uint8_t>(fade));
15898 if (version > DRW::AC1021) { // 2010+ clip mode
15899 buf->putBit(clipMode ? 1 : 0);
15900 }
15901 if (m_clipBoundaryType == 0) {
15902 buf->putBitShort(0); // clip_boundary_type 0 = none
15903 } else if (m_clipBoundaryType == 1) {
15904 buf->putBitShort(1);
15905 buf->put2RawDouble(clipPath[0]);
15906 buf->put2RawDouble(clipPath[1]);
15907 } else {
15908 buf->putBitShort(2);
15909 buf->putBitLong(static_cast<std::int32_t>(clipPath.size()));
15910 for (std::size_t i = 0; i < clipPath.size(); ++i)
15911 buf->put2RawDouble(clipPath[i]);
15912 }
15913
15914 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
15915
15916 // Emit both trailing handles UNCONDITIONALLY in parseDwg order:
15917 // imagedef (hard pointer, code 5) then imagedefreactor (hard owner, code 3).
15918 dwgBufferW *hb = handleBuf ? handleBuf : buf;
15919 auto makeHandle = [](std::uint8_t code, std::uint32_t r) {
15920 dwgHandle h;
15921 h.code = (r == 0) ? 0 : code;
15922 h.ref = r;
15923 h.size = 0;
15924 if (r != 0) { std::uint32_t t = r; while (t != 0) { t >>= 8; ++h.size; } }
15925 return h;
15926 };
15927 hb->putHandle(makeHandle(5, ref)); // imagedef (340)
15928 hb->putHandle(makeHandle(3, m_imageDefReactorHandle)); // imagedefreactor (360)
15929 return true;
15930}
15931
15932bool DRW_Wipeout::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
15933 return DRW_Image::parseCode(code, reader);
15934}
15935
15936bool DRW_Wipeout::hasValidBoundary() const {
15937 return m_clipBoundaryType != 0 && hasValidClipBoundary();
15938}
15939
15940bool DRW_Wipeout::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
15941 if (buf == nullptr)
15942 return false;
15943
15944 // Image parsing is transactional; keep it on a second cursor because a
15945 // valid image payload is still invalid for WIPEOUT when it has no clip
15946 // boundary. The wrapper must preserve the same failure contract.
15947 dwgBuffer probe = buf->forkIndependent();
15948 if (!DRW_Image::parseDwg(version, &probe, bs)) {
15949 buf->invalidate();
15950 resetDwgState();
15951 return false;
15952 }
15953 if (!hasValidBoundary()) {
15954 buf->invalidate();
15955 resetDwgState();
15956 return false;
15957 }
15958 *buf = probe;
15959 return true;
15960}
15961
15962bool DRW_Wipeout::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
15963 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
15964 (void)bs; (void)strBuf;
15965 if (!validatePayloadFields()) {
15966 DRW_DBG("WIPEOUT has an invalid scalar or clip field\n")DRW_dbg::dbg("WIPEOUT has an invalid scalar or clip field\n");
15967 return false;
15968 }
15969 if (clipPath.size() > DRW_Image::kMaxClipVertices) {
15970 DRW_DBG("WIPEOUT clip vertices exceed DWG limit\n")DRW_dbg::dbg("WIPEOUT clip vertices exceed DWG limit\n");
15971 return false;
15972 }
15973 if (!hasValidBoundary()) {
15974 DRW_DBG("WIPEOUT has invalid clip boundary\n")DRW_dbg::dbg("WIPEOUT has invalid clip boundary\n");
15975 return false;
15976 }
15977 // WIPEOUT is fixed DWG entity type 1109. It is not a custom class and
15978 // must not depend on a file-local CLASSES ordinal.
15979 oType = dwgType::WIPEOUT;
15980 if (!encodeDwgCommon(version, buf)) return false;
15981
15982 buf->putBitLong(0);
15983 buf->putBitDouble(basePoint.x); buf->putBitDouble(basePoint.y); buf->putBitDouble(basePoint.z);
15984 buf->putBitDouble(secPoint.x); buf->putBitDouble(secPoint.y); buf->putBitDouble(secPoint.z);
15985 buf->putBitDouble(vVector.x); buf->putBitDouble(vVector.y); buf->putBitDouble(vVector.z);
15986 buf->putRawDouble(sizeu);
15987 buf->putRawDouble(sizev);
15988 buf->putBitShort(static_cast<std::uint16_t>(m_displayProps));
15989 buf->putBit(static_cast<std::uint8_t>(clip & 1));
15990 buf->putRawChar8(static_cast<std::uint8_t>(brightness));
15991 buf->putRawChar8(static_cast<std::uint8_t>(contrast));
15992 buf->putRawChar8(static_cast<std::uint8_t>(fade));
15993 if (version > DRW::AC1021) {
15994 buf->putBit(clipMode ? 1 : 0);
15995 }
15996 if (m_clipBoundaryType == 1) {
15997 buf->putBitShort(1);
15998 buf->put2RawDouble(clipPath[0]);
15999 buf->put2RawDouble(clipPath[1]);
16000 } else {
16001 buf->putBitShort(2);
16002 buf->putBitLong(static_cast<std::int32_t>(clipPath.size()));
16003 for (std::size_t i = 0; i < clipPath.size(); ++i)
16004 buf->put2RawDouble(clipPath[i]);
16005 }
16006
16007 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
16008
16009 dwgBufferW *hb = handleBuf ? handleBuf : buf;
16010 auto makeHandle = [](std::uint8_t code, std::uint32_t r) {
16011 dwgHandle h;
16012 h.code = (r == 0) ? 0 : code;
16013 h.ref = r;
16014 h.size = 0;
16015 if (r != 0) { std::uint32_t t = r; while (t != 0) { t >>= 8; ++h.size; } }
16016 return h;
16017 };
16018 hb->putHandle(makeHandle(5, ref));
16019 hb->putHandle(makeHandle(3, m_imageDefReactorHandle));
16020 return true;
16021}
16022
16023bool DRW_NavisworksModel::parseCode(
16024 int code, const std::unique_ptr<dxfReader>& reader) {
16025 if (code == 100) {
16026 const std::string subclass = reader->getString();
16027 if (subclass == "AcDbNavisworksModel")
16028 m_dxfInBody = true;
16029 return true;
16030 }
16031 if (!m_dxfInBody)
16032 return DRW_Entity::parseCode(code, reader);
16033
16034 switch (code) {
16035 case 70: {
16036 const std::int32_t value = reader->getInt32();
16037 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
16038 return false;
16039 flags = static_cast<std::uint16_t>(value);
16040 m_dxfSawFlags = true;
16041 return true;
16042 }
16043 case 340:
16044 definitionHandle = reader->getHandleString();
16045 m_dxfSawDefinition = true;
16046 return true;
16047 case 40:
16048 if (m_dxfTransformCount < kTransformSize) {
16049 transform[m_dxfTransformCount++] = reader->getDouble();
16050 return true;
16051 }
16052 if (m_dxfSawUnitFactor)
16053 return false;
16054 unitFactor = reader->getDouble();
16055 m_dxfSawUnitFactor = true;
16056 return true;
16057 default:
16058 return DRW_Entity::parseCode(code, reader);
16059 }
16060}
16061
16062bool DRW_NavisworksModel::finalizeDxf() {
16063 return m_dxfTransformCount == kTransformSize && m_dxfSawUnitFactor
16064 && std::isfinite(unitFactor)
16065 && std::all_of(transform.begin(), transform.end(),
16066 [](double value) { return std::isfinite(value); });
16067}
16068
16069void DRW_NavisworksModel::resetDwgState() {
16070 DRW_Entity::reset();
16071 flags = 0;
16072 definitionHandle = 0;
16073 transform = {1.0, 0.0, 0.0, 0.0,
16074 0.0, 1.0, 0.0, 0.0,
16075 0.0, 0.0, 1.0, 0.0,
16076 0.0, 0.0, 0.0, 1.0};
16077 unitFactor = 1.0;
16078}
16079
16080bool DRW_NavisworksModel::parseDwg(DRW::Version version, dwgBuffer *buf,
16081 std::uint32_t bs) {
16082 resetDwgState();
16083 dwgBuffer *sourceBuf = buf;
16084 auto fail = [this, sourceBuf]() {
16085 if (sourceBuf != nullptr)
16086 sourceBuf->invalidate();
16087 resetDwgState();
16088 return false;
16089 };
16090 if (sourceBuf == nullptr)
16091 return fail();
16092 if (version < DRW::AC1015)
16093 return fail();
16094
16095 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
16096 dwgBuffer stringBuffer = bodyProbe.forkIndependent();
16097 dwgBuffer *stringBuf = version > DRW::AC1018 ? &stringBuffer : &bodyProbe;
16098 if (!DRW_Entity::parseDwg(version, &bodyProbe, stringBuf, bs))
16099 return fail();
16100
16101 const std::uint64_t bodyEndBit = dwgDataEndBit;
16102 std::uint64_t handleEndBit = 0;
16103 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
16104 handleEndBit))
16105 return fail();
16106 // BS is an unsigned bit-short at the codec boundary. The flags are a
16107 // mask, so values with bit 15 set must survive instead of being rejected
16108 // through an intermediate signed conversion.
16109 std::uint16_t parsedFlags = 0;
16110 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedFlags))
16111 return fail();
16112
16113 std::uint32_t parsedDefinitionHandle = 0;
16114 if (version <= DRW::AC1018) {
16115 dwgHandle definition;
16116 if (!readBoundedDwgHandle(bodyProbe, handleEndBit, 0, false,
16117 definition))
16118 return fail();
16119 parsedDefinitionHandle = definition.ref;
16120 }
16121
16122 std::array<double, 16> parsedTransform{};
16123 for (double& value : parsedTransform) {
16124 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, value))
16125 return fail();
16126 }
16127 double parsedUnitFactor = 0.0;
16128 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, parsedUnitFactor))
16129 return fail();
16130 if (!std::all_of(parsedTransform.begin(), parsedTransform.end(),
16131 [](double value) { return std::isfinite(value); })
16132 || !std::isfinite(parsedUnitFactor)) {
16133 return fail();
16134 }
16135
16136 if (!DRW_Entity::parseDwgEntHandle(version, &bodyProbe, true,
16137 handleEndBit))
16138 return fail();
16139 dwgBuffer handleProbe = bodyProbe.forkIndependent();
16140 if (version > DRW::AC1018) {
16141 dwgHandle definition;
16142 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
16143 definition))
16144 return fail();
16145 parsedDefinitionHandle = definition.ref;
16146 }
16147 if (!handleProbe.isGood() || !stringBuf->isGood())
16148 return fail();
16149 flags = parsedFlags;
16150 transform = parsedTransform;
16151 unitFactor = parsedUnitFactor;
16152 definitionHandle = parsedDefinitionHandle;
16153 *sourceBuf = handleProbe;
16154 return true;
16155}
16156
16157bool DRW_NavisworksModel::encodeDwg(DRW::Version version, dwgBufferW *buf,
16158 std::uint32_t bs, dwgBufferW *strBuf,
16159 dwgBufferW *handleBuf) {
16160 (void)bs;
16161 if (version < DRW::AC1015
16162 || !std::isfinite(unitFactor)
16163 || !std::all_of(transform.begin(), transform.end(),
16164 [](double value) { return std::isfinite(value); }))
16165 return false;
16166
16167 oType = kDwgClassNum;
16168 if (!encodeDwgCommon(version, buf, strBuf))
16169 return false;
16170
16171 buf->putBitShort(flags);
16172 dwgBufferW *hb = handleBuf ? handleBuf : buf;
16173 auto makeHandle = [](std::uint8_t code, std::uint32_t ref) {
16174 dwgHandle handle;
16175 handle.code = ref == 0 ? 0 : code;
16176 handle.ref = ref;
16177 handle.size = 0;
16178 for (std::uint32_t value = ref; value != 0; value >>= 8)
16179 ++handle.size;
16180 return handle;
16181 };
16182
16183 // AC1015/AC1018 place this entity-specific handle before the matrix in
16184 // the legacy data stream. AC1021+ places it after the common handles.
16185 if (version <= DRW::AC1018)
16186 buf->putHandle(makeHandle(2, definitionHandle));
16187 for (double value : transform)
16188 buf->putBitDouble(value);
16189 buf->putBitDouble(unitFactor);
16190
16191 if (!DRW_Entity::encodeDwgEntHandle(version, buf, handleBuf))
16192 return false;
16193 if (version > DRW::AC1018)
16194 hb->putHandle(makeHandle(2, definitionHandle));
16195 return true;
16196}
16197
16198bool DRW_PointCloud::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
16199 if (code == 100) {
16200 m_dxfInBody = reader->getString() == "AcDbPointCloud";
16201 return true;
16202 }
16203 if (!m_dxfInBody)
16204 return DRW_Entity::parseCode(code, reader);
16205 switch (code) {
16206 case 70:
16207 m_dxfSawClassVersion = true;
16208 classVersion = reader->getInt32();
16209 break;
16210 case 10: origin.x = reader->getDouble(); break;
16211 case 20: origin.y = reader->getDouble(); break;
16212 case 30: origin.z = reader->getDouble(); break;
16213 case 1: savedFilename = reader->getUtf8String(); break;
16214 case 90:
16215 if (m_dxfSawSourceCount)
16216 return false;
16217 m_dxfSawSourceCount = true;
16218 sourceFileCount = reader->getInt32();
16219 if (!isValidCount(sourceFileCount,
16220 static_cast<std::int32_t>(kMaxItems))) {
16221 return false;
16222 }
16223 sourceFiles.clear();
16224 if (!DRW::reserve(sourceFiles, sourceFileCount))
16225 return false;
16226 break;
16227 case 2:
16228 if (sourceFiles.size() >= static_cast<std::size_t>(sourceFileCount))
16229 return false;
16230 sourceFiles.push_back(reader->getUtf8String());
16231 break;
16232 case 3: ucsName = reader->getUtf8String(); break;
16233 case 11: extentsMin.x = reader->getDouble(); break;
16234 case 21: extentsMin.y = reader->getDouble(); break;
16235 case 31: extentsMin.z = reader->getDouble(); break;
16236 case 12: extentsMax.x = reader->getDouble(); break;
16237 case 22: extentsMax.y = reader->getDouble(); break;
16238 case 32: extentsMax.z = reader->getDouble(); break;
16239 case 92: {
16240 if (m_dxfSawPointCount)
16241 return false;
16242 m_dxfSawPointCount = true;
16243 const std::int32_t count = reader->getInt32();
16244 if (count < 0)
16245 return false;
16246 pointCount = static_cast<std::uint64_t>(count);
16247 break;
16248 }
16249 case 13: ucsOrigin.x = reader->getDouble(); break;
16250 case 23: ucsOrigin.y = reader->getDouble(); break;
16251 case 33: ucsOrigin.z = reader->getDouble(); break;
16252 case 210: ucsXDirection.x = reader->getDouble(); break;
16253 case 220: ucsXDirection.y = reader->getDouble(); break;
16254 case 230: ucsXDirection.z = reader->getDouble(); break;
16255 case 211: ucsYDirection.x = reader->getDouble(); break;
16256 case 221: ucsYDirection.y = reader->getDouble(); break;
16257 case 231: ucsYDirection.z = reader->getDouble(); break;
16258 case 212: ucsZDirection.x = reader->getDouble(); break;
16259 case 222: ucsZDirection.y = reader->getDouble(); break;
16260 case 232: ucsZDirection.z = reader->getDouble(); break;
16261 case 330:
16262 if (!m_dxfInBody)
16263 return DRW_Entity::parseCode(code, reader);
16264 definitionHandle = static_cast<std::uint32_t>(reader->getHandleString());
16265 break;
16266 case 360: reactorHandle = static_cast<std::uint32_t>(reader->getHandleString()); break;
16267 case 71: intensityScheme = reader->getInt32(); break;
16268 case 40: intensityStyle.minIntensity = reader->getDouble(); break;
16269 case 41: intensityStyle.maxIntensity = reader->getDouble(); break;
16270 case 42: intensityStyle.lowThreshold = reader->getDouble(); break;
16271 case 43: intensityStyle.highThreshold = reader->getDouble(); break;
16272 default:
16273 return DRW_Entity::parseCode(code, reader);
16274 }
16275 return true;
16276}
16277
16278bool DRW_PointCloud::finalizeDxf() {
16279 return m_dxfSawClassVersion && m_dxfSawSourceCount && m_dxfSawPointCount
16280 && isValidCount(sourceFileCount,
16281 static_cast<std::int32_t>(kMaxItems))
16282 && sourceFiles.size() == static_cast<std::size_t>(sourceFileCount);
16283}
16284
16285void DRW_PointCloud::resetDwgState() {
16286 DRW_Entity::reset();
16287 classVersion = 0;
16288 origin = DRW_Coord{0.0, 0.0, 0.0};
16289 savedFilename.clear();
16290 sourceFileCount = 0;
16291 sourceFiles.clear();
16292 extentsMin = DRW_Coord{0.0, 0.0, 0.0};
16293 extentsMax = DRW_Coord{0.0, 0.0, 0.0};
16294 pointCount = 0;
16295 ucsName.clear();
16296 ucsOrigin = DRW_Coord{0.0, 0.0, 0.0};
16297 ucsXDirection = DRW_Coord{1.0, 0.0, 0.0};
16298 ucsYDirection = DRW_Coord{0.0, 1.0, 0.0};
16299 ucsZDirection = DRW_Coord{0.0, 0.0, 1.0};
16300 definitionHandle = 0;
16301 reactorHandle = 0;
16302 showIntensity = false;
16303 intensityScheme = 0;
16304 intensityStyle = DRW_PointCloudIntensityStyle{};
16305 showClipping = false;
16306 clippingCount = 0;
16307 clippings.clear();
16308 m_dxfInBody = false;
16309 m_dxfSawClassVersion = false;
16310 m_dxfSawSourceCount = false;
16311 m_dxfSawPointCount = false;
16312}
16313
16314bool DRW_PointCloud::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
16315 resetDwgState();
16316 dwgBuffer *sourceBuf = buf;
16317 auto fail = [this, sourceBuf]() {
16318 if (sourceBuf != nullptr)
16319 sourceBuf->invalidate();
16320 resetDwgState();
16321 return false;
16322 };
16323 if (sourceBuf == nullptr || version <= DRW::AC1018)
16324 return fail();
16325
16326 const auto finite = [](const DRW_Coord& point) {
16327 return std::isfinite(point.x) && std::isfinite(point.y)
16328 && std::isfinite(point.z);
16329 };
16330
16331 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
16332 dwgBuffer stringProbe = sourceBuf->forkIndependent();
16333 if (!DRW_Entity::parseDwg(version, &bodyProbe, &stringProbe, bs))
16334 return fail();
16335
16336 const std::uint64_t bodyEndBit = dwgDataEndBit;
16337 std::uint64_t stringEndBit = bodyEndBit;
16338 if (version > DRW::AC1021) {
16339 std::uint64_t ignoredBodyEndBit = 0;
16340 if (!entityBodyDataEndBit(stringProbe, version, objSize,
16341 ignoredBodyEndBit, &stringEndBit))
16342 return fail();
16343 }
16344 std::uint16_t parsedClassVersion = 0;
16345 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedClassVersion)
16346 || !readBoundedBitCoord(bodyProbe, bodyEndBit, origin))
16347 return fail();
16348 classVersion = parsedClassVersion;
16349 if (!finite(origin))
16350 return fail();
16351 if (!readBoundedVariableText(stringProbe, stringEndBit, version,
16352 savedFilename))
16353 return fail();
16354 std::uint32_t declaredSourceFiles = 0;
16355 if (!readTableBodyCount(
16356 version, &bodyProbe, objSize,
16357 static_cast<std::uint32_t>(kMaxItems), 2,
16358 declaredSourceFiles)) {
16359 return fail();
16360 }
16361 sourceFileCount = static_cast<int>(declaredSourceFiles);
16362 sourceFiles.clear();
16363
16364 if (sourceFileCount == 0) {
16365 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, extentsMin)
16366 || !readBoundedBitCoord(bodyProbe, bodyEndBit, extentsMax)
16367 || !readBoundedRawLong64(bodyProbe, bodyEndBit, pointCount))
16368 return fail();
16369 if (!finite(extentsMin) || !finite(extentsMax))
16370 return fail();
16371 if (!readBoundedVariableText(stringProbe, stringEndBit, version,
16372 ucsName))
16373 return fail();
16374 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, ucsOrigin)
16375 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsXDirection)
16376 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsYDirection)
16377 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsZDirection))
16378 return fail();
16379 if (!finite(ucsOrigin) || !finite(ucsXDirection)
16380 || !finite(ucsYDirection) || !finite(ucsZDirection)) {
16381 return fail();
16382 }
16383
16384 if (version > DRW::AC1024) {
16385 std::uint16_t parsedIntensityScheme = 0;
16386 if (!readBoundedBit(bodyProbe, bodyEndBit, showIntensity)
16387 || !readBoundedBitShort(bodyProbe, bodyEndBit,
16388 parsedIntensityScheme)
16389 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16390 intensityStyle.minIntensity)
16391 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16392 intensityStyle.maxIntensity)
16393 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16394 intensityStyle.lowThreshold)
16395 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16396 intensityStyle.highThreshold))
16397 return fail();
16398 intensityScheme = static_cast<int>(parsedIntensityScheme);
16399 if (!std::isfinite(intensityStyle.minIntensity)
16400 || !std::isfinite(intensityStyle.maxIntensity)
16401 || !std::isfinite(intensityStyle.lowThreshold)
16402 || !std::isfinite(intensityStyle.highThreshold)) {
16403 return fail();
16404 }
16405 if (!readBoundedBit(bodyProbe, bodyEndBit, showClipping))
16406 return fail();
16407
16408 std::uint32_t declaredClippings = 0;
16409 if (!readTableBodyCount(
16410 version, &bodyProbe, objSize,
16411 static_cast<std::uint32_t>(kMaxItems), 2,
16412 declaredClippings)) {
16413 return fail();
16414 }
16415 clippingCount = static_cast<int>(declaredClippings);
16416 clippings.clear();
16417 if (!DRW::reserve(clippings, clippingCount))
16418 return fail();
16419 for (std::int32_t i = 0; i < clippingCount; ++i) {
16420 DRW_PointCloudClipping clipping;
16421 if (!readBoundedBit(bodyProbe, bodyEndBit,
16422 clipping.isInverted))
16423 return fail();
16424 std::uint16_t clippingType = 0;
16425 if (!readBoundedBitShort(bodyProbe, bodyEndBit, clippingType))
16426 return fail();
16427 clipping.type = clippingType;
16428 if (clipping.type < 1 || clipping.type > 3)
16429 return fail();
16430 std::uint32_t vertexCount = 2;
16431 if (clipping.type == 3
16432 && !readTableBodyCount(
16433 version, &bodyProbe, objSize,
16434 static_cast<std::uint32_t>(kMaxItems), 128,
16435 vertexCount)) {
16436 return fail();
16437 }
16438 clipping.vertexCount = static_cast<int>(vertexCount);
16439 if (!DRW::reserve(clipping.vertices, clipping.vertexCount))
16440 return fail();
16441 for (std::int32_t j = 0; j < clipping.vertexCount; ++j) {
16442 DRW_Coord vertex;
16443 if (!readBounded2RawDouble(bodyProbe, bodyEndBit, vertex))
16444 return fail();
16445 clipping.vertices.push_back(vertex);
16446 }
16447 if (clipping.type == 1) {
16448 if (!readBoundedBitDouble(bodyProbe, bodyEndBit,
16449 clipping.zMin)
16450 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
16451 clipping.zMax))
16452 return fail();
16453 }
16454 if (!std::all_of(
16455 clipping.vertices.begin(), clipping.vertices.end(),
16456 finite)
16457 || !std::isfinite(clipping.zMin)
16458 || !std::isfinite(clipping.zMax)) {
16459 return fail();
16460 }
16461 clippings.push_back(std::move(clipping));
16462 }
16463 } else {
16464 showIntensity = false;
16465 intensityScheme = 0;
16466 showClipping = false;
16467 clippingCount = 0;
16468 clippings.clear();
16469 }
16470 }
16471
16472 if (!DRW::reserve(sourceFiles, sourceFileCount))
16473 return fail();
16474 for (std::int32_t i = 0; i < sourceFileCount; ++i) {
16475 UTF8STRINGstd::string sourceFile;
16476 if (!readBoundedVariableText(stringProbe, stringEndBit, version,
16477 sourceFile))
16478 return fail();
16479 sourceFiles.push_back(std::move(sourceFile));
16480 }
16481
16482 if (!bodyProbe.isGood() || !stringProbe.isGood()
16483 || currentDwgBit(&bodyProbe) > bodyEndBit
16484 || currentDwgBit(&stringProbe) > stringEndBit)
16485 return fail();
16486
16487 std::uint64_t handleEndBit = 0;
16488 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
16489 handleEndBit))
16490 return fail();
16491 dwgBuffer handleProbe = bodyProbe.forkIndependent();
16492 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
16493 handleEndBit)
16494 || !handleProbe.isGood())
16495 return fail();
16496 std::uint32_t parsedDefinitionHandle = 0;
16497 std::uint32_t parsedReactorHandle = 0;
16498 if (version > DRW::AC1024 && sourceFileCount == 0) {
16499 dwgHandle definition;
16500 dwgHandle reactor;
16501 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
16502 definition)
16503 || !readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
16504 reactor))
16505 return fail();
16506 parsedDefinitionHandle = definition.ref;
16507 parsedReactorHandle = reactor.ref;
16508 }
16509 if (!handleProbe.isGood() || !stringProbe.isGood())
16510 return fail();
16511 definitionHandle = parsedDefinitionHandle;
16512 reactorHandle = parsedReactorHandle;
16513 *sourceBuf = handleProbe;
16514 return true;
16515}
16516
16517bool DRW_PointCloud::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
16518 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
16519 (void)bs;
16520 const auto finite = [](const DRW_Coord& point) {
16521 return std::isfinite(point.x) && std::isfinite(point.y)
16522 && std::isfinite(point.z);
16523 };
16524 if (version <= DRW::AC1018
16525 || classVersion > std::numeric_limits<std::uint16_t>::max()
16526 || sourceFileCount < 0
16527 || sourceFileCount > static_cast<int>(kMaxItems)
16528 || sourceFiles.size() != static_cast<std::size_t>(sourceFileCount)
16529 || clippingCount < 0
16530 || (sourceFileCount == 0 && clippings.size() > kMaxItems)
16531 || (sourceFileCount != 0
16532 && (clippingCount != 0 || !clippings.empty()
16533 || showIntensity || showClipping))
16534 || intensityScheme < 0
16535 || intensityScheme > std::numeric_limits<std::uint16_t>::max()
16536 || clippings.size() != static_cast<std::size_t>(clippingCount)) {
16537 return false;
16538 }
16539 if (!finite(origin) || !finite(extentsMin) || !finite(extentsMax)
16540 || !finite(ucsOrigin) || !finite(ucsXDirection)
16541 || !finite(ucsYDirection) || !finite(ucsZDirection)
16542 || !std::isfinite(intensityStyle.minIntensity)
16543 || !std::isfinite(intensityStyle.maxIntensity)
16544 || !std::isfinite(intensityStyle.lowThreshold)
16545 || !std::isfinite(intensityStyle.highThreshold)) {
16546 return false;
16547 }
16548 for (const DRW_PointCloudClipping& clipping : clippings) {
16549 if (clipping.type < 1 || clipping.type > 3
16550 || clipping.vertexCount < 0
16551 || clipping.vertexCount > static_cast<int>(kMaxItems)
16552 || clipping.vertices.size() != static_cast<std::size_t>(clipping.vertexCount)
16553 || (clipping.type != 3 && clipping.vertexCount != 2)
16554 || !std::isfinite(clipping.zMin)
16555 || !std::isfinite(clipping.zMax)) {
16556 return false;
16557 }
16558 for (const DRW_Coord& vertex : clipping.vertices) {
16559 if (!finite(vertex))
16560 return false;
16561 }
16562 }
16563
16564 oType = kDwgClassNum;
16565 if (!encodeDwgCommon(version, buf, strBuf))
16566 return false;
16567 dwgBufferW *sBuf = strBuf ? strBuf : buf;
16568 buf->putBitShort(static_cast<std::uint16_t>(classVersion));
16569 buf->put3BitDouble(origin);
16570 sBuf->putVariableText(version, savedFilename);
16571 buf->putBitLong(sourceFileCount);
16572 if (sourceFileCount == 0) {
16573 buf->put3BitDouble(extentsMin);
16574 buf->put3BitDouble(extentsMax);
16575 buf->putRawLong64(pointCount);
16576 sBuf->putVariableText(version, ucsName);
16577 buf->put3BitDouble(ucsOrigin);
16578 buf->put3BitDouble(ucsXDirection);
16579 buf->put3BitDouble(ucsYDirection);
16580 buf->put3BitDouble(ucsZDirection);
16581
16582 if (version > DRW::AC1024) {
16583 buf->putBit(showIntensity ? 1 : 0);
16584 buf->putBitShort(static_cast<std::uint16_t>(intensityScheme));
16585 buf->putBitDouble(intensityStyle.minIntensity);
16586 buf->putBitDouble(intensityStyle.maxIntensity);
16587 buf->putBitDouble(intensityStyle.lowThreshold);
16588 buf->putBitDouble(intensityStyle.highThreshold);
16589 buf->putBit(showClipping ? 1 : 0);
16590 buf->putBitLong(clippingCount);
16591 for (const DRW_PointCloudClipping& clipping : clippings) {
16592 buf->putBit(clipping.isInverted ? 1 : 0);
16593 buf->putBitShort(static_cast<std::uint16_t>(clipping.type));
16594 if (clipping.type == 3)
16595 buf->putBitLong(clipping.vertexCount);
16596 for (const DRW_Coord& vertex : clipping.vertices)
16597 buf->put2RawDouble(vertex);
16598 if (clipping.type == 1) {
16599 buf->putBitDouble(clipping.zMin);
16600 buf->putBitDouble(clipping.zMax);
16601 }
16602 }
16603 }
16604 }
16605 for (const UTF8STRINGstd::string& sourceFile : sourceFiles)
16606 sBuf->putVariableText(version, sourceFile);
16607
16608 if (!encodeDwgEntHandle(version, buf, handleBuf))
16609 return false;
16610 if (version > DRW::AC1024 && sourceFileCount == 0) {
16611 dwgBufferW *hBuf = handleBuf ? handleBuf : buf;
16612 auto putHandle = [hBuf](std::uint8_t code, std::uint32_t ref) {
16613 dwgHandle handle;
16614 handle.code = ref == 0 ? 0 : code;
16615 handle.ref = ref;
16616 handle.size = 0;
16617 for (std::uint32_t value = ref; value != 0; value >>= 8)
16618 ++handle.size;
16619 hBuf->putHandle(handle);
16620 };
16621 putHandle(5, definitionHandle);
16622 putHandle(3, reactorHandle);
16623 }
16624 return true;
16625}
16626
16627bool DRW_PointCloudEx::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
16628 if (code == 100) {
16629 m_dxfInBody = reader->getString() == "AcDbPointCloud";
16630 return true;
16631 }
16632 if (!m_dxfInBody)
16633 return DRW_Entity::parseCode(code, reader);
16634 switch (code) {
16635 case 70:
16636 if (m_dxfSeen70++ != 0) return false;
16637 classVersion = reader->getInt32();
16638 break;
16639 case 10: extentsMin.x = reader->getDouble(); break;
16640 case 20: extentsMin.y = reader->getDouble(); break;
16641 case 30: extentsMin.z = reader->getDouble(); break;
16642 case 11: extentsMax.x = reader->getDouble(); break;
16643 case 21: extentsMax.y = reader->getDouble(); break;
16644 case 31: extentsMax.z = reader->getDouble(); break;
16645 case 12: ucsOrigin.x = reader->getDouble(); break;
16646 case 22: ucsOrigin.y = reader->getDouble(); break;
16647 case 32: ucsOrigin.z = reader->getDouble(); break;
16648 case 210: ucsXDirection.x = reader->getDouble(); break;
16649 case 220: ucsXDirection.y = reader->getDouble(); break;
16650 case 230: ucsXDirection.z = reader->getDouble(); break;
16651 case 211: ucsYDirection.x = reader->getDouble(); break;
16652 case 221: ucsYDirection.y = reader->getDouble(); break;
16653 case 231: ucsYDirection.z = reader->getDouble(); break;
16654 case 212: ucsZDirection.x = reader->getDouble(); break;
16655 case 222: ucsZDirection.y = reader->getDouble(); break;
16656 case 232: ucsZDirection.z = reader->getDouble(); break;
16657 case 290:
16658 if (m_dxfHasCropping) {
16659 if (m_dxfSeen290 % 2 == 0)
16660 m_dxfCurrentCropping.isInside = reader->getBool();
16661 else
16662 m_dxfCurrentCropping.isInverted = reader->getBool();
16663 ++m_dxfSeen290;
16664 } else {
16665 if (m_dxfSeen290++ != 0) return false;
16666 isLocked = reader->getBool();
16667 }
16668 break;
16669 case 330:
16670 if (!m_dxfInBody)
16671 return DRW_Entity::parseCode(code, reader);
16672 definitionHandle = static_cast<std::uint32_t>(reader->getHandleString());
16673 break;
16674 case 360:
16675 reactorHandle = static_cast<std::uint32_t>(reader->getHandleString());
16676 break;
16677 case 1:
16678 switch (m_dxfTextCount++) {
16679 case 0: name = reader->getUtf8String(); break;
16680 case 1: intensityColorScheme = reader->getUtf8String(); break;
16681 case 2: currentColorScheme = reader->getUtf8String(); break;
16682 case 3: classificationColorScheme = reader->getUtf8String(); break;
16683 default: return false;
16684 }
16685 break;
16686 case 291: showIntensity = reader->getBool(); break;
16687 case 295: showCropping = reader->getBool(); break;
16688 case 71:
16689 if (m_dxfSeen71++ == 0)
16690 stylizationType = reader->getInt32();
16691 else if (m_dxfSeen71 == 2)
16692 intensityOutOfRangeBehavior = reader->getInt32();
16693 else
16694 return false;
16695 break;
16696 case 72: elevationOutOfRangeBehavior = reader->getInt32(); break;
16697 case 40:
16698 if (m_dxfSeen40++ == 0)
16699 elevationMin = reader->getDouble();
16700 else if (m_dxfSeen40 == 2)
16701 elevationMax = reader->getDouble();
16702 else
16703 return false;
16704 break;
16705 case 41: elevationMax = reader->getDouble(); break;
16706 case 90:
16707 if (m_dxfSeen90++ != 0) return false;
16708 intensityMin = reader->getInt32();
16709 break;
16710 case 91:
16711 if (m_dxfSeen91++ != 0) return false;
16712 intensityMax = reader->getInt32();
16713 break;
16714 case 292: elevationApplyToFixedRange = reader->getBool(); break;
16715 case 293: intensityAsGradient = reader->getBool(); break;
16716 case 294: elevationAsGradient = reader->getBool(); break;
16717 case 92: {
16718 if (m_dxfSawCroppingCount) return false;
16719 const std::int32_t count = reader->getInt32();
16720 if (!isValidCount(count, static_cast<std::int32_t>(kMaxItems)))
16721 return false;
16722 croppingCount = count;
16723 m_dxfSawCroppingCount = true;
16724 break;
16725 }
16726 case 93:
16727 if (m_dxfHasCropping) {
16728 if (m_dxfSeen93++ != 0) return false;
16729 m_dxfCurrentCropping.pointCount = reader->getInt32();
16730 if (!isValidCount(m_dxfCurrentCropping.pointCount,
16731 static_cast<std::int32_t>(kMaxItems))) {
16732 return false;
16733 }
16734 m_dxfCurrentCropping.points.clear();
16735 if (!DRW::reserve(m_dxfCurrentCropping.points,
16736 m_dxfCurrentCropping.pointCount))
16737 return false;
16738 } else if (m_dxfSawCroppingCount && croppingCount == 0) {
16739 if (m_dxfSeen93++ == 0)
16740 unknownInt0 = reader->getInt32();
16741 else if (m_dxfSeen93 == 2)
16742 unknownInt1 = reader->getInt32();
16743 else
16744 return false;
16745 } else {
16746 return false;
16747 }
16748 break;
16749 case 280:
16750 if (!m_dxfSawCroppingCount || croppingCount == 0
16751 || !finishDxfCropping()) {
16752 return false;
16753 }
16754 m_dxfCurrentCropping = DRW_PointCloudExCropping{};
16755 m_dxfCurrentCropping.type = reader->getInt32();
16756 if (m_dxfCurrentCropping.type < 0
16757 || m_dxfCurrentCropping.type > std::numeric_limits<std::uint16_t>::max()) {
16758 return false;
16759 }
16760 m_dxfHasCropping = true;
16761 m_dxfSeen290 = 0;
16762 m_dxfSeen93 = 0;
16763 m_dxfDirectionComponent = 0;
16764 m_dxfCropPlaneComponent = 0;
16765 m_dxfCropPointOpen = false;
16766 break;
16767 case 13:
16768 case 23:
16769 case 33:
16770 if (!m_dxfHasCropping) {
16771 if (code == 13) ucsOrigin.x = reader->getDouble();
16772 else if (code == 23) ucsOrigin.y = reader->getDouble();
16773 else ucsOrigin.z = reader->getDouble();
16774 } else if (m_dxfCropPlaneComponent < 3) {
16775 if (code == 13 && m_dxfCropPlaneComponent != 0) return false;
16776 if (code == 23 && m_dxfCropPlaneComponent != 1) return false;
16777 if (code == 33 && m_dxfCropPlaneComponent != 2) return false;
16778 const double value = reader->getDouble();
16779 if (code == 13) m_dxfCurrentCropping.cropPlane.x = value;
16780 else if (code == 23) m_dxfCurrentCropping.cropPlane.y = value;
16781 else m_dxfCurrentCropping.cropPlane.z = value;
16782 ++m_dxfCropPlaneComponent;
16783 } else {
16784 if (code == 13) {
16785 if (m_dxfCropPointOpen) return false;
16786 m_dxfCropPoint = DRW_Coord{};
16787 m_dxfCropPointOpen = true;
16788 } else if (!m_dxfCropPointOpen) {
16789 return false;
16790 }
16791 const double value = reader->getDouble();
16792 if (code == 13) m_dxfCropPoint.x = value;
16793 else if (code == 23) m_dxfCropPoint.y = value;
16794 else m_dxfCropPoint.z = value;
16795 if (code == 33) {
16796 m_dxfCurrentCropping.points.push_back(m_dxfCropPoint);
16797 m_dxfCropPointOpen = false;
16798 }
16799 }
16800 break;
16801 case 213:
16802 case 223:
16803 case 233: {
16804 if (!m_dxfHasCropping || m_dxfDirectionComponent >= 6)
16805 return false;
16806 const double value = reader->getDouble();
16807 DRW_Coord *direction = m_dxfDirectionComponent < 3
16808 ? &m_dxfCurrentCropping.cropXDirection
16809 : &m_dxfCurrentCropping.cropYDirection;
16810 const int component = static_cast<int>(m_dxfDirectionComponent % 3);
16811 if ((component == 0 && code != 213)
16812 || (component == 1 && code != 223)
16813 || (component == 2 && code != 233)) {
16814 return false;
16815 }
16816 if (component == 0) direction->x = value;
16817 else if (component == 1) direction->y = value;
16818 else direction->z = value;
16819 ++m_dxfDirectionComponent;
16820 break;
16821 }
16822 default:
16823 return DRW_Entity::parseCode(code, reader);
16824 }
16825 return true;
16826}
16827
16828bool DRW_PointCloudEx::finishDxfCropping() {
16829 if (!m_dxfHasCropping)
16830 return true;
16831 if (m_dxfSeen290 != 2 || m_dxfDirectionComponent != 6
16832 || m_dxfCropPlaneComponent != 3 || m_dxfCropPointOpen
16833 || m_dxfSeen93 != 1
16834 || m_dxfCurrentCropping.points.size()
16835 != static_cast<std::size_t>(m_dxfCurrentCropping.pointCount)) {
16836 return false;
16837 }
16838 croppings.push_back(m_dxfCurrentCropping);
16839 m_dxfHasCropping = false;
16840 return true;
16841}
16842
16843bool DRW_PointCloudEx::finalizeDxf() {
16844 if (!finishDxfCropping())
16845 return false;
16846 if (m_dxfSeen70 != 1 || !m_dxfSawCroppingCount)
16847 return false;
16848 if (croppingCount == 0)
16849 return m_dxfSeen93 == 2 && croppings.empty();
16850 return croppings.size() == static_cast<std::size_t>(croppingCount);
16851}
16852
16853void DRW_PointCloudEx::resetDwgState() {
16854 DRW_Entity::reset();
16855 classVersion = 0;
16856 extentsMin = DRW_Coord{0.0, 0.0, 0.0};
16857 extentsMax = DRW_Coord{0.0, 0.0, 0.0};
16858 ucsOrigin = DRW_Coord{0.0, 0.0, 0.0};
16859 ucsXDirection = DRW_Coord{1.0, 0.0, 0.0};
16860 ucsYDirection = DRW_Coord{0.0, 1.0, 0.0};
16861 ucsZDirection = DRW_Coord{0.0, 0.0, 1.0};
16862 isLocked = false;
16863 definitionHandle = 0;
16864 reactorHandle = 0;
16865 name.clear();
16866 showIntensity = false;
16867 showCropping = false;
16868 croppingCount = 0;
16869 unknownInt0 = 0;
16870 unknownInt1 = 0;
16871 stylizationType = 0;
16872 intensityColorScheme.clear();
16873 currentColorScheme.clear();
16874 classificationColorScheme.clear();
16875 elevationMin = 0.0;
16876 elevationMax = 0.0;
16877 intensityMin = 0.0;
16878 intensityMax = 0.0;
16879 intensityOutOfRangeBehavior = 0;
16880 elevationOutOfRangeBehavior = 0;
16881 elevationApplyToFixedRange = false;
16882 intensityAsGradient = false;
16883 elevationAsGradient = false;
16884 croppings.clear();
16885 m_dxfCurrentCropping = DRW_PointCloudExCropping{};
16886 m_dxfTextCount = 0;
16887 m_dxfSeen70 = 0;
16888 m_dxfSeen71 = 0;
16889 m_dxfSeen90 = 0;
16890 m_dxfSeen91 = 0;
16891 m_dxfSeen93 = 0;
16892 m_dxfSeen290 = 0;
16893 m_dxfSeen40 = 0;
16894 m_dxfDirectionComponent = 0;
16895 m_dxfCropPlaneComponent = 0;
16896 m_dxfSawCroppingCount = false;
16897 m_dxfInBody = false;
16898 m_dxfHasCropping = false;
16899 m_dxfCropPointOpen = false;
16900 m_dxfCropPoint = DRW_Coord{0.0, 0.0, 0.0};
16901}
16902
16903bool DRW_PointCloudEx::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
16904 resetDwgState();
16905 dwgBuffer *sourceBuf = buf;
16906 auto fail = [this, sourceBuf]() {
16907 if (sourceBuf != nullptr)
16908 sourceBuf->invalidate();
16909 resetDwgState();
16910 return false;
16911 };
16912 if (sourceBuf == nullptr || version <= DRW::AC1024)
16913 return fail();
16914
16915 const auto finite = [](const DRW_Coord& point) {
16916 return std::isfinite(point.x) && std::isfinite(point.y)
16917 && std::isfinite(point.z);
16918 };
16919 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
16920 dwgBuffer stringProbe = sourceBuf->forkIndependent();
16921 if (!DRW_Entity::parseDwg(version, &bodyProbe, &stringProbe, bs))
16922 return fail();
16923
16924 const std::uint64_t bodyEndBit = dwgDataEndBit;
16925 std::uint64_t stringEndBit = bodyEndBit;
16926 if (version > DRW::AC1021) {
16927 std::uint64_t ignoredBodyEndBit = 0;
16928 if (!entityBodyDataEndBit(stringProbe, version, objSize,
16929 ignoredBodyEndBit, &stringEndBit))
16930 return fail();
16931 }
16932 std::uint16_t parsedClassVersion = 0;
16933 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedClassVersion)
16934 || !readBoundedBitCoord(bodyProbe, bodyEndBit, extentsMin)
16935 || !readBoundedBitCoord(bodyProbe, bodyEndBit, extentsMax)
16936 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsOrigin)
16937 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsXDirection)
16938 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsYDirection)
16939 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ucsZDirection))
16940 return fail();
16941 classVersion = parsedClassVersion;
16942 if (!finite(extentsMin) || !finite(extentsMax) || !finite(ucsOrigin)
16943 || !finite(ucsXDirection) || !finite(ucsYDirection)
16944 || !finite(ucsZDirection)) {
16945 return fail();
16946 }
16947 if (!readBoundedBit(bodyProbe, bodyEndBit, isLocked))
16948 return fail();
16949 if (!readBoundedVariableText(stringProbe, stringEndBit, version, name))
16950 return fail();
16951 if (!readBoundedBit(bodyProbe, bodyEndBit, showIntensity)
16952 || !readBoundedBit(bodyProbe, bodyEndBit, showCropping))
16953 return fail();
16954 std::uint32_t declaredCroppings = 0;
16955 if (!readTableBodyCount(
16956 version, &bodyProbe, objSize,
16957 static_cast<std::uint32_t>(kMaxItems), 24,
16958 declaredCroppings)) {
16959 return fail();
16960 }
16961 croppingCount = static_cast<int>(declaredCroppings);
16962 croppings.clear();
16963 if (croppingCount == 0) {
16964 std::uint16_t parsedStylizationType = 0;
16965 if (!readBoundedBitLong(bodyProbe, bodyEndBit, unknownInt0)
16966 || !readBoundedBitLong(bodyProbe, bodyEndBit, unknownInt1)
16967 || !readBoundedBitShort(bodyProbe, bodyEndBit,
16968 parsedStylizationType))
16969 return fail();
16970 stylizationType = parsedStylizationType;
16971 if (!readBoundedVariableText(stringProbe, stringEndBit, version,
16972 intensityColorScheme)
16973 || !readBoundedVariableText(stringProbe, stringEndBit, version,
16974 currentColorScheme)
16975 || !readBoundedVariableText(stringProbe, stringEndBit, version,
16976 classificationColorScheme))
16977 return fail();
16978 std::int32_t parsedIntensityMin = 0;
16979 std::int32_t parsedIntensityMax = 0;
16980 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, elevationMin)
16981 || !readBoundedBitDouble(bodyProbe, bodyEndBit, elevationMax)
16982 || !readBoundedBitLong(bodyProbe, bodyEndBit, parsedIntensityMin)
16983 || !readBoundedBitLong(bodyProbe, bodyEndBit, parsedIntensityMax))
16984 return fail();
16985 intensityMin = static_cast<double>(parsedIntensityMin);
16986 intensityMax = static_cast<double>(parsedIntensityMax);
16987 if (!std::isfinite(elevationMin) || !std::isfinite(elevationMax))
16988 return fail();
16989 std::uint16_t parsedIntensityBehavior = 0;
16990 std::uint16_t parsedElevationBehavior = 0;
16991 if (!readBoundedBitShort(bodyProbe, bodyEndBit,
16992 parsedIntensityBehavior)
16993 || !readBoundedBitShort(bodyProbe, bodyEndBit,
16994 parsedElevationBehavior)
16995 || !readBoundedBit(bodyProbe, bodyEndBit,
16996 elevationApplyToFixedRange)
16997 || !readBoundedBit(bodyProbe, bodyEndBit, intensityAsGradient)
16998 || !readBoundedBit(bodyProbe, bodyEndBit, elevationAsGradient))
16999 return fail();
17000 intensityOutOfRangeBehavior = parsedIntensityBehavior;
17001 elevationOutOfRangeBehavior = parsedElevationBehavior;
17002 } else {
17003 unknownInt0 = 0;
17004 unknownInt1 = 0;
17005 }
17006 if (!bodyProbe.isGood() || !stringProbe.isGood()
17007 || currentDwgBit(&bodyProbe) > bodyEndBit
17008 || currentDwgBit(&stringProbe) > stringEndBit)
17009 return fail();
17010
17011 if (!DRW::reserve(croppings, croppingCount))
17012 return fail();
17013 for (std::uint32_t i = 0; i < declaredCroppings; ++i) {
17014 DRW_PointCloudExCropping cropping;
17015 std::uint16_t croppingType = 0;
17016 if (!readBoundedBitShort(bodyProbe, bodyEndBit, croppingType)
17017 || !readBoundedBit(bodyProbe, bodyEndBit, cropping.isInside)
17018 || !readBoundedBit(bodyProbe, bodyEndBit, cropping.isInverted)
17019 || !readBoundedBitCoord(bodyProbe, bodyEndBit,
17020 cropping.cropPlane)
17021 || !readBoundedBitCoord(bodyProbe, bodyEndBit,
17022 cropping.cropXDirection)
17023 || !readBoundedBitCoord(bodyProbe, bodyEndBit,
17024 cropping.cropYDirection)) {
17025 return fail();
17026 }
17027 cropping.type = static_cast<int>(croppingType);
17028 if (!finite(cropping.cropPlane) || !finite(cropping.cropXDirection)
17029 || !finite(cropping.cropYDirection)) {
17030 return fail();
17031 }
17032 std::uint32_t declaredPoints = 0;
17033 if (!readTableBodyCount(
17034 version, &bodyProbe, objSize,
17035 static_cast<std::uint32_t>(kMaxItems),
17036 6, declaredPoints)) {
17037 return fail();
17038 }
17039 std::uint64_t pointBits = 0;
17040 if (!dwgSafety::multiply(declaredPoints, 6, pointBits)
17041 || !proxyEntityHasBits(bodyProbe, bodyEndBit, pointBits)) {
17042 return fail();
17043 }
17044 cropping.pointCount = static_cast<int>(declaredPoints);
17045 if (!DRW::reserve(cropping.points, cropping.pointCount))
17046 return fail();
17047 for (std::uint32_t j = 0; j < declaredPoints; ++j) {
17048 DRW_Coord point;
17049 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, point))
17050 return fail();
17051 cropping.points.push_back(point);
17052 }
17053 if (!std::all_of(cropping.points.begin(), cropping.points.end(), finite))
17054 return fail();
17055 croppings.push_back(std::move(cropping));
17056 }
17057
17058 std::uint64_t handleEndBit = 0;
17059 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
17060 handleEndBit))
17061 return fail();
17062 dwgBuffer handleProbe = bodyProbe.forkIndependent();
17063 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
17064 handleEndBit)
17065 || !handleProbe.isGood())
17066 return fail();
17067 dwgHandle definition;
17068 dwgHandle reactor;
17069 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
17070 definition)
17071 || !readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
17072 reactor))
17073 return fail();
17074 const std::uint32_t parsedDefinitionHandle = definition.ref;
17075 const std::uint32_t parsedReactorHandle = reactor.ref;
17076 if (!handleProbe.isGood() || !stringProbe.isGood())
17077 return fail();
17078 definitionHandle = parsedDefinitionHandle;
17079 reactorHandle = parsedReactorHandle;
17080 *sourceBuf = handleProbe;
17081 return true;
17082}
17083
17084bool DRW_PointCloudEx::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
17085 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
17086 (void)bs;
17087 const auto finite = [](const DRW_Coord& point) {
17088 return std::isfinite(point.x) && std::isfinite(point.y)
17089 && std::isfinite(point.z);
17090 };
17091 const auto fitsUint16 = [](int value) {
17092 return value >= 0
17093 && value <= std::numeric_limits<std::uint16_t>::max();
17094 };
17095 if (version <= DRW::AC1024
17096 || classVersion > std::numeric_limits<std::uint16_t>::max()
17097 || croppingCount < 0
17098 || croppingCount > static_cast<int>(kMaxItems)
17099 || croppings.size() != static_cast<std::size_t>(croppingCount)) {
17100 return false;
17101 }
17102 for (const DRW_PointCloudExCropping& cropping : croppings) {
17103 if (cropping.pointCount < 0
17104 || cropping.pointCount > static_cast<int>(kMaxItems)
17105 || cropping.points.size() != static_cast<std::size_t>(cropping.pointCount)) {
17106 return false;
17107 }
17108 if (!fitsUint16(cropping.type) || !finite(cropping.cropPlane)
17109 || !finite(cropping.cropXDirection)
17110 || !finite(cropping.cropYDirection)) {
17111 return false;
17112 }
17113 for (const DRW_Coord& point : cropping.points) {
17114 if (!finite(point))
17115 return false;
17116 }
17117 }
17118 if (!finite(extentsMin) || !finite(extentsMax) || !finite(ucsOrigin)
17119 || !finite(ucsXDirection) || !finite(ucsYDirection)
17120 || !finite(ucsZDirection) || !std::isfinite(elevationMin)
17121 || !std::isfinite(elevationMax)
17122 || !fitsUint16(stylizationType)
17123 || !fitsUint16(intensityOutOfRangeBehavior)
17124 || !fitsUint16(elevationOutOfRangeBehavior)) {
17125 return false;
17126 }
17127 const auto integral32 = [](double value) {
17128 return std::isfinite(value)
17129 && value >= static_cast<double>(std::numeric_limits<std::int32_t>::min())
17130 && value <= static_cast<double>(std::numeric_limits<std::int32_t>::max())
17131 && std::floor(value) == value;
17132 };
17133 if (croppingCount == 0
17134 && (!integral32(intensityMin) || !integral32(intensityMax))) {
17135 return false;
17136 }
17137
17138 oType = kDwgClassNum;
17139 if (!encodeDwgCommon(version, buf, strBuf))
17140 return false;
17141 dwgBufferW *sBuf = strBuf ? strBuf : buf;
17142 buf->putBitShort(static_cast<std::uint16_t>(classVersion));
17143 buf->put3BitDouble(extentsMin);
17144 buf->put3BitDouble(extentsMax);
17145 buf->put3BitDouble(ucsOrigin);
17146 buf->put3BitDouble(ucsXDirection);
17147 buf->put3BitDouble(ucsYDirection);
17148 buf->put3BitDouble(ucsZDirection);
17149 buf->putBit(isLocked ? 1 : 0);
17150 sBuf->putVariableText(version, name);
17151 buf->putBit(showIntensity ? 1 : 0);
17152 buf->putBit(showCropping ? 1 : 0);
17153 buf->putBitLong(croppingCount);
17154 if (croppingCount == 0) {
17155 buf->putBitLong(unknownInt0);
17156 buf->putBitLong(unknownInt1);
17157 buf->putBitShort(static_cast<std::uint16_t>(stylizationType));
17158 sBuf->putVariableText(version, intensityColorScheme);
17159 sBuf->putVariableText(version, currentColorScheme);
17160 sBuf->putVariableText(version, classificationColorScheme);
17161 buf->putBitDouble(elevationMin);
17162 buf->putBitDouble(elevationMax);
17163 buf->putBitLong(static_cast<std::int32_t>(intensityMin));
17164 buf->putBitLong(static_cast<std::int32_t>(intensityMax));
17165 buf->putBitShort(static_cast<std::uint16_t>(intensityOutOfRangeBehavior));
17166 buf->putBitShort(static_cast<std::uint16_t>(elevationOutOfRangeBehavior));
17167 buf->putBit(elevationApplyToFixedRange ? 1 : 0);
17168 buf->putBit(intensityAsGradient ? 1 : 0);
17169 buf->putBit(elevationAsGradient ? 1 : 0);
17170 }
17171 for (const DRW_PointCloudExCropping& cropping : croppings) {
17172 buf->putBitShort(static_cast<std::uint16_t>(cropping.type));
17173 buf->putBit(cropping.isInside ? 1 : 0);
17174 buf->putBit(cropping.isInverted ? 1 : 0);
17175 buf->put3BitDouble(cropping.cropPlane);
17176 buf->put3BitDouble(cropping.cropXDirection);
17177 buf->put3BitDouble(cropping.cropYDirection);
17178 buf->putBitLong(cropping.pointCount);
17179 for (const DRW_Coord& point : cropping.points)
17180 buf->put3BitDouble(point);
17181 }
17182 if (!encodeDwgEntHandle(version, buf, handleBuf))
17183 return false;
17184
17185 dwgBufferW *hBuf = handleBuf ? handleBuf : buf;
17186 auto putHandle = [hBuf](std::uint8_t code, std::uint32_t ref) {
17187 dwgHandle handle;
17188 handle.code = ref == 0 ? 0 : code;
17189 handle.ref = ref;
17190 handle.size = 0;
17191 for (std::uint32_t value = ref; value != 0; value >>= 8)
17192 ++handle.size;
17193 hBuf->putHandle(handle);
17194 };
17195 putHandle(5, definitionHandle);
17196 putHandle(3, reactorHandle);
17197 return true;
17198}
17199
17200bool DRW_Surface::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
17201 switch (code) {
17202 case 1:
17203 case 3:
17204 // SURFACE ACIS payloads use text groups in the AcDbModelerGeometry
17205 // subclass. Keep the bytes verbatim; group 3 is a continuation of
17206 // the same payload and must not be interpreted as a surface field.
17207 if (!appendTextBytesChecked(rawAcisData, reader->getString(),
17208 dwgSafety::MaxBufferSize))
17209 return false;
17210 break;
17211 case 70:
17212 modelerFormatVersion = reader->getInt32();
17213 break;
17214 case 71:
17215 uIsolines = reader->getInt32();
17216 break;
17217 case 72:
17218 vIsolines = reader->getInt32();
17219 break;
17220 case 310:
17221 {
17222 std::vector<std::uint8_t> decoded;
17223 if (!decodeHexBytes(reader->getString(), decoded))
17224 return false;
17225 if (!appendBytesChecked(rawAcisData, decoded,
17226 dwgSafety::MaxBufferSize))
17227 return false;
17228 }
17229 break;
17230 default:
17231 return DRW_Entity::parseCode(code, reader);
17232 }
17233 return true;
17234}
17235
17236bool DRW_ExtrudedSurface::parseCode(
17237 int code, const std::unique_ptr<dxfReader>& reader) {
17238 switch (code) {
17239 case 100: {
17240 const std::string marker = reader->getString();
17241 if (marker == "AcDbExtrudedSurface")
17242 m_dxfInSubtype = true;
17243 else if (marker == "AcDbModelerGeometry"
17244 || marker == "AcDbSurface")
17245 m_dxfInSubtype = false;
17246 break;
17247 }
17248 case 90:
17249 {
17250 // The class id, then the size of the 310 data that follows.
17251 const std::int32_t value = reader->getInt32();
17252 if (value < 0 || m_dxfDataSizeSeen)
17253 return false;
17254 if (m_dxfClassIdSeen) {
17255 m_dxfDataSizeSeen = true;
17256 break;
17257 }
17258 classId = static_cast<std::uint32_t>(value);
17259 m_dxfClassIdSeen = true;
17260 }
17261 break;
17262 case 310:
17263 // Extrusion data is not kept; it must not join the ACIS body.
17264 if (!m_dxfInSubtype)
17265 return DRW_Surface::parseCode(code, reader);
17266 break;
17267 case 10:
17268 sweepVector.x = reader->getDouble();
17269 break;
17270 case 20:
17271 sweepVector.y = reader->getDouble();
17272 break;
17273 case 30:
17274 sweepVector.z = reader->getDouble();
17275 break;
17276 case 11:
17277 referenceVector.x = reader->getDouble();
17278 break;
17279 case 21:
17280 referenceVector.y = reader->getDouble();
17281 break;
17282 case 31:
17283 referenceVector.z = reader->getDouble();
17284 break;
17285 case 40:
17286 if (m_dxfExtrudedTransformCount >= extrudedTransform.size())
17287 return false;
17288 extrudedTransform[m_dxfExtrudedTransformCount++] = reader->getDouble();
17289 break;
17290 case 41:
17291 // Code 41 is not part of the AcDbExtrudedSurface mapping. Keep an
17292 // unexpected value out of the common entity state.
17293 return false;
17294 case 42:
17295 if (!m_dxfInSubtype)
17296 return DRW_Surface::parseCode(code, reader);
17297 draftAngle = reader->getDouble();
17298 break;
17299 case 43:
17300 draftStartDistance = reader->getDouble();
17301 break;
17302 case 44:
17303 draftEndDistance = reader->getDouble();
17304 break;
17305 case 45:
17306 twistAngle = reader->getDouble();
17307 break;
17308 case 46:
17309 if (m_dxfSweepTransformCount >= sweepEntityTransform.size())
17310 return false;
17311 sweepEntityTransform[m_dxfSweepTransformCount++] = reader->getDouble();
17312 break;
17313 case 47:
17314 if (m_dxfPathTransformCount >= pathEntityTransform.size())
17315 return false;
17316 pathEntityTransform[m_dxfPathTransformCount++] = reader->getDouble();
17317 break;
17318 case 48:
17319 scaleFactor = reader->getDouble();
17320 break;
17321 case 49:
17322 alignAngle = reader->getDouble();
17323 break;
17324 case 70:
17325 if (!m_dxfInSubtype)
17326 return DRW_Surface::parseCode(code, reader);
17327 {
17328 const std::int32_t value = reader->getInt32();
17329 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
17330 return false;
17331 sweepAlignmentFlags = value;
17332 }
17333 break;
17334 case 71:
17335 if (!m_dxfInSubtype)
17336 return DRW_Surface::parseCode(code, reader);
17337 {
17338 const std::int32_t value = reader->getInt32();
17339 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
17340 return false;
17341 pathFlags = value;
17342 }
17343 break;
17344 case 290:
17345 solid = reader->getInt32() != 0;
17346 break;
17347 case 292:
17348 alignStart = reader->getInt32() != 0;
17349 break;
17350 case 293:
17351 bank = reader->getInt32() != 0;
17352 break;
17353 case 294:
17354 basePointSet = reader->getInt32() != 0;
17355 break;
17356 case 295:
17357 sweepEntityTransformComputed = reader->getInt32() != 0;
17358 break;
17359 case 296:
17360 pathEntityTransformComputed = reader->getInt32() != 0;
17361 break;
17362 default:
17363 return DRW_Surface::parseCode(code, reader);
17364 }
17365 return true;
17366}
17367
17368bool DRW_ExtrudedSurface::finalizeDxf() const {
17369 if (!m_dxfClassIdSeen)
17370 return true;
17371 return m_dxfInSubtype
17372 && m_dxfExtrudedTransformCount == extrudedTransform.size()
17373 && m_dxfSweepTransformCount == sweepEntityTransform.size()
17374 && m_dxfPathTransformCount == pathEntityTransform.size();
17375}
17376
17377bool DRW_SweptSurface::parseCode(
17378 int code, const std::unique_ptr<dxfReader>& reader) {
17379 switch (code) {
17380 case 100: {
17381 const std::string marker = reader->getString();
17382 if (marker == "AcDbSweptSurface")
17383 m_dxfInSubtype = true;
17384 else if (marker == "AcDbModelerGeometry"
17385 || marker == "AcDbSurface")
17386 m_dxfInSubtype = false;
17387 break;
17388 }
17389 case 90:
17390 if (!m_dxfInSubtype)
17391 return DRW_Surface::parseCode(code, reader);
17392 m_dxfTypedFieldSeen = true;
17393 {
17394 // Each entity id may be followed by the size of its 310 data.
17395 const std::int32_t value = reader->getInt32();
17396 if (value < 0)
17397 return false;
17398 if (m_dxfSweepEntityIdSeen) {
17399 if (m_dxfDataSizeCount >= (m_dxfPathEntityIdSeen ? 2 : 1))
17400 return false;
17401 ++m_dxfDataSizeCount;
17402 break;
17403 }
17404 sweepEntityId = static_cast<std::uint32_t>(value);
17405 m_dxfSweepEntityIdSeen = true;
17406 }
17407 break;
17408 case 91:
17409 if (!m_dxfInSubtype)
17410 return DRW_Surface::parseCode(code, reader);
17411 if (m_dxfPathEntityIdSeen)
17412 return false;
17413 m_dxfTypedFieldSeen = true;
17414 {
17415 const std::int32_t value = reader->getInt32();
17416 if (value < 0)
17417 return false;
17418 pathEntityId = static_cast<std::uint32_t>(value);
17419 m_dxfPathEntityIdSeen = true;
17420 }
17421 break;
17422 case 310: {
17423 if (!m_dxfInSubtype)
17424 return DRW_Surface::parseCode(code, reader);
17425 m_dxfTypedFieldSeen = true;
17426 std::vector<std::uint8_t> decoded;
17427 if (!decodeHexBytes(reader->getString(), decoded))
17428 return false;
17429 auto& destination = m_dxfPathEntityIdSeen ? pathData : sweepData;
17430 if (!appendBytesChecked(destination, decoded, kMaxSweepDataSize))
17431 return false;
17432 break;
17433 }
17434 case 11:
17435 if (!m_dxfInSubtype)
17436 return DRW_Surface::parseCode(code, reader);
17437 m_dxfTypedFieldSeen = true;
17438 referenceVector.x = reader->getDouble();
17439 break;
17440 case 21:
17441 if (!m_dxfInSubtype)
17442 return DRW_Surface::parseCode(code, reader);
17443 m_dxfTypedFieldSeen = true;
17444 referenceVector.y = reader->getDouble();
17445 break;
17446 case 31:
17447 if (!m_dxfInSubtype)
17448 return DRW_Surface::parseCode(code, reader);
17449 m_dxfTypedFieldSeen = true;
17450 referenceVector.z = reader->getDouble();
17451 break;
17452 case 40:
17453 if (!m_dxfInSubtype)
17454 return DRW_Surface::parseCode(code, reader);
17455 m_dxfTypedFieldSeen = true;
17456 if (m_dxfSweepTransformCount >= sweepEntityTransformed.size())
17457 return false;
17458 sweepEntityTransformed[m_dxfSweepTransformCount++] = reader->getDouble();
17459 break;
17460 case 41:
17461 if (!m_dxfInSubtype)
17462 return DRW_Surface::parseCode(code, reader);
17463 m_dxfTypedFieldSeen = true;
17464 if (m_dxfPathTransformCount >= pathEntityTransformed.size())
17465 return false;
17466 pathEntityTransformed[m_dxfPathTransformCount++] = reader->getDouble();
17467 break;
17468 case 42:
17469 if (!m_dxfInSubtype)
17470 return DRW_Surface::parseCode(code, reader);
17471 m_dxfTypedFieldSeen = true;
17472 draftAngle = reader->getDouble();
17473 break;
17474 case 43:
17475 if (!m_dxfInSubtype)
17476 return DRW_Surface::parseCode(code, reader);
17477 m_dxfTypedFieldSeen = true;
17478 draftStartDistance = reader->getDouble();
17479 break;
17480 case 44:
17481 if (!m_dxfInSubtype)
17482 return DRW_Surface::parseCode(code, reader);
17483 m_dxfTypedFieldSeen = true;
17484 draftEndDistance = reader->getDouble();
17485 break;
17486 case 45:
17487 if (!m_dxfInSubtype)
17488 return DRW_Surface::parseCode(code, reader);
17489 m_dxfTypedFieldSeen = true;
17490 twistAngle = reader->getDouble();
17491 break;
17492 case 46:
17493 if (!m_dxfInSubtype)
17494 return DRW_Surface::parseCode(code, reader);
17495 m_dxfTypedFieldSeen = true;
17496 if (m_dxfSweepEntityTransformedCount >= sweepEntityTransform.size())
17497 return false;
17498 sweepEntityTransform[m_dxfSweepEntityTransformedCount++] =
17499 reader->getDouble();
17500 break;
17501 case 47:
17502 if (!m_dxfInSubtype)
17503 return DRW_Surface::parseCode(code, reader);
17504 m_dxfTypedFieldSeen = true;
17505 if (m_dxfPathEntityTransformedCount >= pathEntityTransform.size())
17506 return false;
17507 pathEntityTransform[m_dxfPathEntityTransformedCount++] =
17508 reader->getDouble();
17509 break;
17510 case 48:
17511 if (!m_dxfInSubtype)
17512 return DRW_Surface::parseCode(code, reader);
17513 m_dxfTypedFieldSeen = true;
17514 scaleFactor = reader->getDouble();
17515 break;
17516 case 49:
17517 if (!m_dxfInSubtype)
17518 return DRW_Surface::parseCode(code, reader);
17519 m_dxfTypedFieldSeen = true;
17520 alignAngle = reader->getDouble();
17521 break;
17522 case 70:
17523 if (!m_dxfInSubtype)
17524 return DRW_Surface::parseCode(code, reader);
17525 m_dxfTypedFieldSeen = true;
17526 {
17527 const std::int32_t value = reader->getInt32();
17528 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
17529 return false;
17530 sweepAlignmentFlags = value;
17531 }
17532 break;
17533 case 71:
17534 if (!m_dxfInSubtype)
17535 return DRW_Surface::parseCode(code, reader);
17536 m_dxfTypedFieldSeen = true;
17537 {
17538 const std::int32_t value = reader->getInt32();
17539 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
17540 return false;
17541 pathFlags = value;
17542 }
17543 break;
17544 case 290:
17545 if (!m_dxfInSubtype)
17546 return DRW_Surface::parseCode(code, reader);
17547 m_dxfTypedFieldSeen = true;
17548 solid = reader->getInt32() != 0;
17549 break;
17550 case 292:
17551 if (!m_dxfInSubtype)
17552 return DRW_Surface::parseCode(code, reader);
17553 m_dxfTypedFieldSeen = true;
17554 alignStart = reader->getInt32() != 0;
17555 break;
17556 case 293:
17557 if (!m_dxfInSubtype)
17558 return DRW_Surface::parseCode(code, reader);
17559 m_dxfTypedFieldSeen = true;
17560 bank = reader->getInt32() != 0;
17561 break;
17562 case 294:
17563 if (!m_dxfInSubtype)
17564 return DRW_Surface::parseCode(code, reader);
17565 m_dxfTypedFieldSeen = true;
17566 basePointSet = reader->getInt32() != 0;
17567 break;
17568 case 295:
17569 if (!m_dxfInSubtype)
17570 return DRW_Surface::parseCode(code, reader);
17571 m_dxfTypedFieldSeen = true;
17572 sweepEntityTransformComputed = reader->getInt32() != 0;
17573 break;
17574 case 296:
17575 if (!m_dxfInSubtype)
17576 return DRW_Surface::parseCode(code, reader);
17577 m_dxfTypedFieldSeen = true;
17578 pathEntityTransformComputed = reader->getInt32() != 0;
17579 break;
17580 default:
17581 return DRW_Surface::parseCode(code, reader);
17582 }
17583 return true;
17584}
17585
17586bool DRW_SweptSurface::finalizeDxf() const {
17587 if (!m_dxfTypedFieldSeen)
17588 return true;
17589 return m_dxfInSubtype && m_dxfSweepEntityIdSeen
17590 && m_dxfPathEntityIdSeen
17591 && m_dxfSweepTransformCount == sweepEntityTransformed.size()
17592 && m_dxfPathTransformCount == pathEntityTransformed.size()
17593 && m_dxfSweepEntityTransformedCount == sweepEntityTransform.size()
17594 && m_dxfPathEntityTransformedCount == pathEntityTransform.size();
17595}
17596
17597bool DRW_LoftedSurface::parseCode(
17598 int code, const std::unique_ptr<dxfReader>& reader) {
17599 switch (code) {
17600 case 100: {
17601 const std::string marker = reader->getString();
17602 if (marker == "AcDbLoftedSurface")
17603 m_dxfInSubtype = true;
17604 else if (marker == "AcDbModelerGeometry"
17605 || marker == "AcDbSurface")
17606 m_dxfInSubtype = false;
17607 break;
17608 }
17609 case 40:
17610 if (!m_dxfInSubtype)
17611 return DRW_Surface::parseCode(code, reader);
17612 m_dxfTypedFieldSeen = true;
17613 if (m_dxfTransformCount >= loftEntityTransform.size())
17614 return false;
17615 loftEntityTransform[m_dxfTransformCount++] = reader->getDouble();
17616 break;
17617 case 70:
17618 if (!m_dxfInSubtype)
17619 return DRW_Surface::parseCode(code, reader);
17620 m_dxfTypedFieldSeen = true;
17621 planeNormalLoftingType = reader->getInt32();
17622 break;
17623 case 41:
17624 if (!m_dxfInSubtype)
17625 return DRW_Surface::parseCode(code, reader);
17626 m_dxfTypedFieldSeen = true;
17627 startDraftAngle = reader->getDouble();
17628 break;
17629 case 42:
17630 if (!m_dxfInSubtype)
17631 return DRW_Surface::parseCode(code, reader);
17632 m_dxfTypedFieldSeen = true;
17633 endDraftAngle = reader->getDouble();
17634 break;
17635 case 43:
17636 if (!m_dxfInSubtype)
17637 return DRW_Surface::parseCode(code, reader);
17638 m_dxfTypedFieldSeen = true;
17639 startDraftMagnitude = reader->getDouble();
17640 break;
17641 case 44:
17642 if (!m_dxfInSubtype)
17643 return DRW_Surface::parseCode(code, reader);
17644 m_dxfTypedFieldSeen = true;
17645 endDraftMagnitude = reader->getDouble();
17646 break;
17647 case 90: {
17648 if (!m_dxfInSubtype)
17649 return DRW_Surface::parseCode(code, reader);
17650 if (m_dxfReferenceTokenCount >= kMaxReferenceTokenCount)
17651 return false;
17652 const std::int32_t value = reader->getInt32();
17653 if (value < 0 || static_cast<std::size_t>(value)
17654 > kMaxReferenceTokenCount)
17655 return false;
17656 m_dxfTypedFieldSeen = true;
17657 dxfReferenceData.emplace_back(90, value);
17658 ++m_dxfReferenceTokenCount;
17659 break;
17660 }
17661 case 310: {
17662 if (!m_dxfInSubtype)
17663 return DRW_Surface::parseCode(code, reader);
17664 std::vector<std::uint8_t> decoded;
17665 if (!decodeHexBytes(reader->getString(), decoded))
17666 return false;
17667 if (m_dxfReferenceTokenCount >= kMaxReferenceTokenCount
17668 || decoded.size() > kMaxReferenceDataSize
17669 || m_dxfReferenceDataSize
17670 > kMaxReferenceDataSize - decoded.size())
17671 return false;
17672 m_dxfTypedFieldSeen = true;
17673 dxfReferenceData.emplace_back(310, std::move(decoded));
17674 m_dxfReferenceDataSize += dxfReferenceData.back().binary()->size();
17675 ++m_dxfReferenceTokenCount;
17676 break;
17677 }
17678 case 290:
17679 if (!m_dxfInSubtype)
17680 return DRW_Surface::parseCode(code, reader);
17681 m_dxfTypedFieldSeen = true;
17682 arcLengthParameterization = reader->getInt32() != 0;
17683 break;
17684 case 291:
17685 if (!m_dxfInSubtype)
17686 return DRW_Surface::parseCode(code, reader);
17687 m_dxfTypedFieldSeen = true;
17688 noTwist = reader->getInt32() != 0;
17689 break;
17690 case 292:
17691 if (!m_dxfInSubtype)
17692 return DRW_Surface::parseCode(code, reader);
17693 m_dxfTypedFieldSeen = true;
17694 alignDirection = reader->getInt32() != 0;
17695 break;
17696 case 293:
17697 if (!m_dxfInSubtype)
17698 return DRW_Surface::parseCode(code, reader);
17699 m_dxfTypedFieldSeen = true;
17700 simpleSurfaces = reader->getInt32() != 0;
17701 break;
17702 case 294:
17703 if (!m_dxfInSubtype)
17704 return DRW_Surface::parseCode(code, reader);
17705 m_dxfTypedFieldSeen = true;
17706 closedSurfaces = reader->getInt32() != 0;
17707 break;
17708 case 295:
17709 if (!m_dxfInSubtype)
17710 return DRW_Surface::parseCode(code, reader);
17711 m_dxfTypedFieldSeen = true;
17712 solid = reader->getInt32() != 0;
17713 break;
17714 case 296:
17715 if (!m_dxfInSubtype)
17716 return DRW_Surface::parseCode(code, reader);
17717 m_dxfTypedFieldSeen = true;
17718 ruledSurface = reader->getInt32() != 0;
17719 break;
17720 case 297:
17721 if (!m_dxfInSubtype)
17722 return DRW_Surface::parseCode(code, reader);
17723 m_dxfTypedFieldSeen = true;
17724 virtualGuide = reader->getInt32() != 0;
17725 break;
17726 case 5:
17727 if (!m_dxfInSubtype)
17728 return DRW_Surface::parseCode(code, reader);
17729 if (!reader->isValidHandleString())
17730 return false;
17731 m_dxfTypedFieldSeen = true;
17732 pathCurveHandle = reader->getHandleString();
17733 break;
17734 default:
17735 return DRW_Surface::parseCode(code, reader);
17736 }
17737 return true;
17738}
17739
17740bool DRW_LoftedSurface::finalizeDxf() const {
17741 if (!m_dxfTypedFieldSeen)
17742 return true;
17743 return m_dxfInSubtype
17744 && m_dxfTransformCount == loftEntityTransform.size()
17745 && std::all_of(loftEntityTransform.begin(), loftEntityTransform.end(),
17746 [](double value) { return std::isfinite(value); })
17747 && std::isfinite(startDraftAngle)
17748 && std::isfinite(endDraftAngle)
17749 && std::isfinite(startDraftMagnitude)
17750 && std::isfinite(endDraftMagnitude);
17751}
17752
17753bool DRW_NurbsSurface::parseCode(
17754 int code, const std::unique_ptr<dxfReader>& reader) {
17755 switch (code) {
17756 case 100: {
17757 const std::string marker = reader->getString();
17758 if (marker == "AcDbNurbSurface")
17759 m_dxfInSubtype = true;
17760 else if (marker == "AcDbModelerGeometry"
17761 || marker == "AcDbSurface")
17762 m_dxfInSubtype = false;
17763 break;
17764 }
17765 case 170:
17766 if (!m_dxfInSubtype)
17767 return DRW_Surface::parseCode(code, reader);
17768 {
17769 const std::int32_t value = reader->getInt32();
17770 if (value < 0 || value > std::numeric_limits<std::uint16_t>::max())
17771 return false;
17772 short170 = static_cast<std::uint16_t>(value);
17773 m_dxfTypedFieldSeen = true;
17774 }
17775 break;
17776 case 290:
17777 if (!m_dxfInSubtype)
17778 return DRW_Surface::parseCode(code, reader);
17779 cvHullDisplay = reader->getInt32() != 0;
17780 m_dxfTypedFieldSeen = true;
17781 break;
17782 case 10:
17783 if (!m_dxfInSubtype)
17784 return DRW_Surface::parseCode(code, reader);
17785 uvec1.x = reader->getDouble();
17786 m_dxfCoordinateMask[0] |= 1;
17787 m_dxfTypedFieldSeen = true;
17788 break;
17789 case 20:
17790 if (!m_dxfInSubtype)
17791 return DRW_Surface::parseCode(code, reader);
17792 uvec1.y = reader->getDouble();
17793 m_dxfCoordinateMask[0] |= 2;
17794 m_dxfTypedFieldSeen = true;
17795 break;
17796 case 30:
17797 if (!m_dxfInSubtype)
17798 return DRW_Surface::parseCode(code, reader);
17799 uvec1.z = reader->getDouble();
17800 m_dxfCoordinateMask[0] |= 4;
17801 m_dxfTypedFieldSeen = true;
17802 break;
17803 case 11:
17804 if (!m_dxfInSubtype)
17805 return DRW_Surface::parseCode(code, reader);
17806 vvec1.x = reader->getDouble();
17807 m_dxfCoordinateMask[1] |= 1;
17808 m_dxfTypedFieldSeen = true;
17809 break;
17810 case 21:
17811 if (!m_dxfInSubtype)
17812 return DRW_Surface::parseCode(code, reader);
17813 vvec1.y = reader->getDouble();
17814 m_dxfCoordinateMask[1] |= 2;
17815 m_dxfTypedFieldSeen = true;
17816 break;
17817 case 31:
17818 if (!m_dxfInSubtype)
17819 return DRW_Surface::parseCode(code, reader);
17820 vvec1.z = reader->getDouble();
17821 m_dxfCoordinateMask[1] |= 4;
17822 m_dxfTypedFieldSeen = true;
17823 break;
17824 case 12:
17825 if (!m_dxfInSubtype)
17826 return DRW_Surface::parseCode(code, reader);
17827 uvec2.x = reader->getDouble();
17828 m_dxfCoordinateMask[2] |= 1;
17829 m_dxfTypedFieldSeen = true;
17830 break;
17831 case 22:
17832 if (!m_dxfInSubtype)
17833 return DRW_Surface::parseCode(code, reader);
17834 uvec2.y = reader->getDouble();
17835 m_dxfCoordinateMask[2] |= 2;
17836 m_dxfTypedFieldSeen = true;
17837 break;
17838 case 32:
17839 if (!m_dxfInSubtype)
17840 return DRW_Surface::parseCode(code, reader);
17841 uvec2.z = reader->getDouble();
17842 m_dxfCoordinateMask[2] |= 4;
17843 m_dxfTypedFieldSeen = true;
17844 break;
17845 case 13:
17846 if (!m_dxfInSubtype)
17847 return DRW_Surface::parseCode(code, reader);
17848 vvec2.x = reader->getDouble();
17849 m_dxfCoordinateMask[3] |= 1;
17850 m_dxfTypedFieldSeen = true;
17851 break;
17852 case 23:
17853 if (!m_dxfInSubtype)
17854 return DRW_Surface::parseCode(code, reader);
17855 vvec2.y = reader->getDouble();
17856 m_dxfCoordinateMask[3] |= 2;
17857 m_dxfTypedFieldSeen = true;
17858 break;
17859 case 33:
17860 if (!m_dxfInSubtype)
17861 return DRW_Surface::parseCode(code, reader);
17862 vvec2.z = reader->getDouble();
17863 m_dxfCoordinateMask[3] |= 4;
17864 m_dxfTypedFieldSeen = true;
17865 break;
17866 default:
17867 return DRW_Surface::parseCode(code, reader);
17868 }
17869 return true;
17870}
17871
17872bool DRW_NurbsSurface::finalizeDxf() const {
17873 if (!m_dxfTypedFieldSeen)
17874 return true;
17875 const bool coordinatesSeen = std::any_of(
17876 m_dxfCoordinateMask.begin(), m_dxfCoordinateMask.end(),
17877 [](std::uint8_t mask) { return mask != 0; });
17878 if (!m_dxfInSubtype
17879 || (coordinatesSeen && std::any_of(
17880 m_dxfCoordinateMask.begin(), m_dxfCoordinateMask.end(),
17881 [](std::uint8_t mask) { return mask != 7; }))) {
17882 return false;
17883 }
17884 return std::all_of(m_dxfCoordinateMask.begin(), m_dxfCoordinateMask.end(),
17885 [](std::uint8_t mask) { return mask == 0 || mask == 7; })
17886 && std::isfinite(uvec1.x) && std::isfinite(uvec1.y)
17887 && std::isfinite(uvec1.z) && std::isfinite(vvec1.x)
17888 && std::isfinite(vvec1.y) && std::isfinite(vvec1.z)
17889 && std::isfinite(uvec2.x) && std::isfinite(uvec2.y)
17890 && std::isfinite(uvec2.z) && std::isfinite(vvec2.x)
17891 && std::isfinite(vvec2.y) && std::isfinite(vvec2.z);
17892}
17893
17894bool DRW_RevolvedSurface::parseCode(
17895 int code, const std::unique_ptr<dxfReader>& reader) {
17896 switch (code) {
17897 case 90:
17898 if (!m_dxfClassIdSeen) {
17899 const std::int32_t value = reader->getInt32();
17900 if (value < 0)
17901 return false;
17902 classId = static_cast<std::uint32_t>(value);
17903 m_dxfClassIdSeen = true;
17904 } else {
17905 const std::int32_t value = reader->getInt32();
17906 if (value < 0)
17907 return false;
17908 id = static_cast<std::uint32_t>(value);
17909 }
17910 break;
17911 case 310:
17912 // Revolve data follows the subclass id; it must not join the ACIS body.
17913 if (!m_dxfClassIdSeen)
17914 return DRW_Surface::parseCode(code, reader);
17915 break;
17916 case 10:
17917 axisPoint.x = reader->getDouble();
17918 break;
17919 case 20:
17920 axisPoint.y = reader->getDouble();
17921 break;
17922 case 30:
17923 axisPoint.z = reader->getDouble();
17924 break;
17925 case 11:
17926 axisVector.x = reader->getDouble();
17927 break;
17928 case 21:
17929 axisVector.y = reader->getDouble();
17930 break;
17931 case 31:
17932 axisVector.z = reader->getDouble();
17933 break;
17934 case 40:
17935 revolveAngle = reader->getDouble();
17936 break;
17937 case 41:
17938 startAngle = reader->getDouble();
17939 break;
17940 case 42:
17941 if (m_dxfTransformCount >= transform.size())
17942 return false;
17943 transform[m_dxfTransformCount++] = reader->getDouble();
17944 break;
17945 case 43:
17946 draftAngle = reader->getDouble();
17947 break;
17948 case 44:
17949 draftStartDistance = reader->getDouble();
17950 break;
17951 case 45:
17952 draftEndDistance = reader->getDouble();
17953 break;
17954 case 46:
17955 twistAngle = reader->getDouble();
17956 break;
17957 case 290:
17958 solid = reader->getInt32() != 0;
17959 break;
17960 case 291:
17961 closeToAxis = reader->getInt32() != 0;
17962 break;
17963 default:
17964 return DRW_Surface::parseCode(code, reader);
17965 }
17966 return true;
17967}
17968
17969void DRW_Surface::resetDwgState() {
17970 DRW_Entity::reset();
17971 uIsolines = 0;
17972 vIsolines = 0;
17973 modelerFormatVersion = 0;
17974 acisEmpty = false;
17975 acisVersion = 0;
17976 rawAcisData.clear();
17977 hasRawDwgBody = false;
17978 rawDwgBodyBitSize = 0;
17979 rawDwgBodyVersion = DRW::UNKNOWNV;
17980 crossSectionHandles.clear();
17981 guideCurveHandles.clear();
17982 dwgClassNum = 0;
17983 dwgPayloadDecoded = false;
17984 m_wireframe = DRW_AcisBrep();
17985 m_wireframeDecoded = false;
17986
17987 const auto identity = [] {
17988 return std::array<double, DRW_ExtrudedSurface::kTransformSize>{
17989 1.0, 0.0, 0.0, 0.0,
17990 0.0, 1.0, 0.0, 0.0,
17991 0.0, 0.0, 1.0, 0.0,
17992 0.0, 0.0, 0.0, 1.0};
17993 };
17994 switch (eType) {
17995 case DRW::EXTRUDEDSURFACE: {
17996 auto *surface = static_cast<DRW_ExtrudedSurface *>(this);
17997 surface->classId = 0;
17998 surface->sweepVector = DRW_Coord{0.0, 0.0, 0.0};
17999 surface->extrudedTransform = identity();
18000 surface->sweepEntityTransform = identity();
18001 surface->pathEntityTransform = identity();
18002 surface->draftAngle = 0.0;
18003 surface->draftStartDistance = 0.0;
18004 surface->draftEndDistance = 0.0;
18005 surface->twistAngle = 0.0;
18006 surface->scaleFactor = 0.0;
18007 surface->alignAngle = 0.0;
18008 surface->solid = false;
18009 surface->sweepAlignmentFlags = 0;
18010 surface->pathFlags = 0;
18011 surface->alignStart = false;
18012 surface->bank = false;
18013 surface->basePointSet = false;
18014 surface->sweepEntityTransformComputed = false;
18015 surface->pathEntityTransformComputed = false;
18016 surface->referenceVector = DRW_Coord{0.0, 0.0, 0.0};
18017 break;
18018 }
18019 case DRW::REVOLVEDSURFACE: {
18020 auto *surface = static_cast<DRW_RevolvedSurface *>(this);
18021 surface->classId = 0;
18022 surface->id = 0;
18023 surface->axisPoint = DRW_Coord{0.0, 0.0, 0.0};
18024 surface->axisVector = DRW_Coord{0.0, 0.0, 0.0};
18025 surface->revolveAngle = 0.0;
18026 surface->startAngle = 0.0;
18027 surface->transform = identity();
18028 surface->draftAngle = 0.0;
18029 surface->draftStartDistance = 0.0;
18030 surface->draftEndDistance = 0.0;
18031 surface->twistAngle = 0.0;
18032 surface->solid = false;
18033 surface->closeToAxis = false;
18034 break;
18035 }
18036 case DRW::SWEPTSURFACE: {
18037 auto *surface = static_cast<DRW_SweptSurface *>(this);
18038 surface->classVersion = 0;
18039 surface->sweepEntityId = 0;
18040 surface->sweepData.clear();
18041 surface->pathEntityId = 0;
18042 surface->pathData.clear();
18043 surface->sweepEntityTransform = identity();
18044 surface->pathEntityTransform = identity();
18045 surface->draftAngle = 0.0;
18046 surface->draftStartDistance = 0.0;
18047 surface->draftEndDistance = 0.0;
18048 surface->twistAngle = 0.0;
18049 surface->scaleFactor = 1.0;
18050 surface->alignAngle = 0.0;
18051 surface->sweepEntityTransformed = identity();
18052 surface->pathEntityTransformed = identity();
18053 surface->solid = false;
18054 surface->sweepAlignmentFlags = 0;
18055 surface->pathFlags = 0;
18056 surface->alignStart = false;
18057 surface->bank = false;
18058 surface->basePointSet = false;
18059 surface->sweepEntityTransformComputed = false;
18060 surface->pathEntityTransformComputed = false;
18061 surface->referenceVector = DRW_Coord{0.0, 0.0, 0.0};
18062 break;
18063 }
18064 case DRW::LOFTEDSURFACE: {
18065 auto *surface = static_cast<DRW_LoftedSurface *>(this);
18066 surface->loftEntityTransform = identity();
18067 surface->planeNormalLoftingType = 0;
18068 surface->startDraftAngle = 0.0;
18069 surface->endDraftAngle = 0.0;
18070 surface->startDraftMagnitude = 0.0;
18071 surface->endDraftMagnitude = 0.0;
18072 surface->arcLengthParameterization = false;
18073 surface->noTwist = true;
18074 surface->alignDirection = true;
18075 surface->simpleSurfaces = true;
18076 surface->closedSurfaces = false;
18077 surface->solid = false;
18078 surface->ruledSurface = false;
18079 surface->virtualGuide = false;
18080 surface->numCrossSections = 0;
18081 surface->numGuideCurves = 0;
18082 surface->dxfReferenceData.clear();
18083 surface->pathCurveHandle = 0;
18084 break;
18085 }
18086 case DRW::NURBSURFACE: {
18087 auto *surface = static_cast<DRW_NurbsSurface *>(this);
18088 surface->short170 = 0;
18089 surface->cvHullDisplay = false;
18090 surface->uvec1 = DRW_Coord{0.0, 0.0, 0.0};
18091 surface->vvec1 = DRW_Coord{0.0, 0.0, 0.0};
18092 surface->uvec2 = DRW_Coord{0.0, 0.0, 0.0};
18093 surface->vvec2 = DRW_Coord{0.0, 0.0, 0.0};
18094 break;
18095 }
18096 default:
18097 break;
18098 }
18099}
18100
18101bool DRW_Surface::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
18102 resetDwgState();
18103 dwgBuffer *sourceBuf = buf;
18104 auto fail = [this, sourceBuf]() {
18105 if (sourceBuf != nullptr)
18106 sourceBuf->invalidate();
18107 resetDwgState();
18108 return false;
18109 };
18110 if (sourceBuf == nullptr)
18111 return fail();
18112
18113 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
18114 dwgBuffer stringProbe = sourceBuf->forkIndependent();
18115 dwgBuffer *stringStream = version > DRW::AC1018 ? &stringProbe : nullptr;
18116 if (!DRW_Entity::parseDwg(version, &bodyProbe, stringStream, bs))
18117 return fail();
18118 buf = &bodyProbe;
18119 const std::uint64_t bodyStartBit = currentDwgBit(buf);
18120
18121 // Surface bodies are opaque here, but their bounded bytes still carry the
18122 // ACIS payload and must be available for rendering and metadata replay.
18123 std::uint64_t bodyEndBit = bodyStartBit;
18124 if (!entityBodyDataEndBit(
18125 stringStream != nullptr ? *stringStream : bodyProbe,
18126 version, objSize, bodyEndBit))
18127 return fail();
18128 if (bodyEndBit < bodyStartBit)
18129 return fail();
18130 if (bodyEndBit > bodyStartBit
18131 && bodyEndBit - bodyStartBit > std::numeric_limits<std::uint32_t>::max())
18132 return fail();
18133 hasRawDwgBody = bodyEndBit > bodyStartBit;
18134 rawDwgBodyBitSize = static_cast<std::uint32_t>(
18135 bodyEndBit >= bodyStartBit ? bodyEndBit - bodyStartBit : 0);
18136 rawDwgBodyVersion = version;
18137 dwgClassNum = oType >= 0 ? static_cast<std::uint16_t>(oType) : 0;
18138 dwgPayloadDecoded = false;
18139 crossSectionHandles.clear();
18140 guideCurveHandles.clear();
18141 if (bodyEndBit > bodyStartBit
18142 && !copyDwgBitRange(*buf, bodyStartBit, bodyEndBit, rawAcisData)) {
18143 return fail();
18144 }
18145
18146 // When has_ds_data is set, the bounded body is an inline DataStorage/SAB
18147 // payload. Keep it opaque, matching the cross-project reader contract;
18148 // interpreting its bytes as subtype fields can fabricate counts and make
18149 // a valid surface fail while reading a nonexistent handle tail.
18150 if (bodyEndBit > bodyStartBit && !hasDsData) {
18151 SurfaceDwgPayloadInfo payload;
18152 if (readSurfaceDwgPayload(
18153 rawAcisData, bodyEndBit - bodyStartBit, version, eType,
18154 surfaceHasModelerFormatVersion(eType),
18155 surfaceModelerFormatVersionLimit(eType), payload)) {
18156 acisEmpty = payload.acisEmpty;
18157 acisVersion = payload.acisVersion;
18158 modelerFormatVersion = payload.modelerFormatVersion;
18159 uIsolines = payload.uIsolines;
18160 vIsolines = payload.vIsolines;
18161 if (payload.extrudedFieldsValid
18162 && eType == DRW::EXTRUDEDSURFACE) {
18163 auto *extruded = static_cast<DRW_ExtrudedSurface *>(this);
18164 extruded->sweepVector = payload.extrudedSweepVector;
18165 extruded->extrudedTransform = payload.extrudedTransform;
18166 extruded->sweepEntityTransform =
18167 payload.extrudedSweepEntityTransform;
18168 extruded->pathEntityTransform =
18169 payload.extrudedPathEntityTransform;
18170 extruded->draftAngle = payload.extrudedDraftAngle;
18171 extruded->draftStartDistance =
18172 payload.extrudedDraftStartDistance;
18173 extruded->draftEndDistance = payload.extrudedDraftEndDistance;
18174 extruded->twistAngle = payload.extrudedTwistAngle;
18175 extruded->scaleFactor = payload.extrudedScaleFactor;
18176 extruded->alignAngle = payload.extrudedAlignAngle;
18177 extruded->solid = payload.extrudedSolid;
18178 extruded->sweepAlignmentFlags =
18179 payload.extrudedSweepAlignmentFlags;
18180 extruded->pathFlags = payload.extrudedPathFlags;
18181 extruded->alignStart = payload.extrudedAlignStart;
18182 extruded->bank = payload.extrudedBank;
18183 extruded->basePointSet = payload.extrudedBasePointSet;
18184 extruded->sweepEntityTransformComputed =
18185 payload.extrudedSweepEntityTransformComputed;
18186 extruded->pathEntityTransformComputed =
18187 payload.extrudedPathEntityTransformComputed;
18188 extruded->referenceVector = payload.extrudedReferenceVector;
18189 }
18190 if (payload.sweptFieldsValid && eType == DRW::SWEPTSURFACE) {
18191 auto *swept = static_cast<DRW_SweptSurface *>(this);
18192 swept->classVersion = payload.sweptClassVersion;
18193 swept->sweepEntityId = payload.sweptEntityId;
18194 swept->sweepData = payload.sweptData;
18195 swept->pathEntityId = payload.sweptPathEntityId;
18196 swept->pathData = payload.sweptPathData;
18197 swept->sweepEntityTransform = payload.sweptEntityTransform;
18198 swept->pathEntityTransform = payload.sweptPathEntityTransform;
18199 swept->draftAngle = payload.sweptDraftAngle;
18200 swept->draftStartDistance = payload.sweptDraftStartDistance;
18201 swept->draftEndDistance = payload.sweptDraftEndDistance;
18202 swept->twistAngle = payload.sweptTwistAngle;
18203 swept->scaleFactor = payload.sweptScaleFactor;
18204 swept->alignAngle = payload.sweptAlignAngle;
18205 swept->solid = payload.sweptSolid;
18206 swept->sweepAlignmentFlags = payload.sweptAlignmentFlags;
18207 swept->pathFlags = payload.sweptPathFlags;
18208 swept->alignStart = payload.sweptAlignStart;
18209 swept->bank = payload.sweptBank;
18210 swept->basePointSet = payload.sweptBasePointSet;
18211 swept->sweepEntityTransformComputed =
18212 payload.sweptEntityTransformComputed;
18213 swept->pathEntityTransformComputed =
18214 payload.sweptPathEntityTransformComputed;
18215 swept->referenceVector = payload.sweptReferenceVector;
18216 }
18217 if (payload.loftedFieldsValid && eType == DRW::LOFTEDSURFACE) {
18218 auto *lofted = static_cast<DRW_LoftedSurface *>(this);
18219 lofted->loftEntityTransform = payload.loftedEntityTransform;
18220 lofted->planeNormalLoftingType =
18221 payload.loftedPlaneNormalLoftingType;
18222 lofted->startDraftAngle = payload.loftedStartDraftAngle;
18223 lofted->endDraftAngle = payload.loftedEndDraftAngle;
18224 lofted->startDraftMagnitude =
18225 payload.loftedStartDraftMagnitude;
18226 lofted->endDraftMagnitude = payload.loftedEndDraftMagnitude;
18227 lofted->arcLengthParameterization =
18228 payload.loftedArcLengthParameterization;
18229 lofted->noTwist = payload.loftedNoTwist;
18230 lofted->alignDirection = payload.loftedAlignDirection;
18231 lofted->simpleSurfaces = payload.loftedSimpleSurfaces;
18232 lofted->closedSurfaces = payload.loftedClosedSurfaces;
18233 lofted->solid = payload.loftedSolid;
18234 lofted->ruledSurface = payload.loftedRuledSurface;
18235 lofted->virtualGuide = payload.loftedVirtualGuide;
18236 lofted->numCrossSections = payload.loftedNumCrossSections;
18237 lofted->numGuideCurves = payload.loftedNumGuideCurves;
18238 }
18239 if (payload.nurbsFieldsValid && eType == DRW::NURBSURFACE) {
18240 auto *nurbs = static_cast<DRW_NurbsSurface *>(this);
18241 nurbs->short170 = payload.nurbsShort170;
18242 nurbs->cvHullDisplay = payload.nurbsCvHullDisplay;
18243 nurbs->uvec1 = payload.nurbsUvec1;
18244 nurbs->vvec1 = payload.nurbsVvec1;
18245 nurbs->uvec2 = payload.nurbsUvec2;
18246 nurbs->vvec2 = payload.nurbsVvec2;
18247 }
18248 dwgPayloadDecoded = true;
18249 if (payload.revolvedFieldsValid
18250 && eType == DRW::REVOLVEDSURFACE) {
18251 auto *revolved = static_cast<DRW_RevolvedSurface *>(this);
18252 revolved->classId = payload.revolvedClassId;
18253 revolved->id = payload.revolvedId;
18254 revolved->axisPoint = payload.revolvedAxisPoint;
18255 revolved->axisVector = payload.revolvedAxisVector;
18256 revolved->revolveAngle = payload.revolvedAngle;
18257 revolved->startAngle = payload.revolvedStartAngle;
18258 revolved->transform = payload.revolvedTransform;
18259 revolved->draftAngle = payload.revolvedDraftAngle;
18260 revolved->draftStartDistance = payload.revolvedDraftStartDistance;
18261 revolved->draftEndDistance = payload.revolvedDraftEndDistance;
18262 revolved->twistAngle = payload.revolvedTwistAngle;
18263 revolved->solid = payload.revolvedSolid;
18264 revolved->closeToAxis = payload.revolvedCloseToAxis;
18265 }
18266 }
18267 }
18268
18269 // R13-R2004 keep the handle stream inline; seek to the documented body
18270 // boundary before the common handle parser. R2007+ performs that seek in
18271 // parseDwgEntHandle() itself.
18272 dwgBuffer handleProbe = bodyProbe.forkIndependent();
18273 if (version <= DRW::AC1018 && bodyEndBit > bodyStartBit) {
18274 if (!handleProbe.setPosition(bodyEndBit >> 3))
18275 return fail();
18276 handleProbe.setBitPos(static_cast<std::uint8_t>(bodyEndBit & 7u));
18277 if (!handleProbe.isGood())
18278 return fail();
18279 }
18280 std::uint64_t handleEndBit = 0;
18281 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
18282 handleEndBit))
18283 return fail();
18284 if (!DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
18285 handleEndBit)
18286 || !handleProbe.isGood())
18287 return fail();
18288 std::vector<std::uint32_t> parsedCrossSectionHandles;
18289 std::vector<std::uint32_t> parsedGuideCurveHandles;
18290 std::uint32_t parsedPathCurveHandle = 0;
18291 const auto readHandleList = [&](std::int32_t count,
18292 std::vector<std::uint32_t>& values) {
18293 if (count < 0
18294 || count > static_cast<std::int32_t>(
18295 DRW_LoftedSurface::kMaxReferenceTokenCount)
18296 || !DRW::reserve(values, count))
18297 return false;
18298 for (std::int32_t i = 0; i < count; ++i) {
18299 dwgHandle value;
18300 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
18301 value))
18302 return false;
18303 values.push_back(value.ref);
18304 }
18305 return true;
18306 };
18307 if (eType == DRW::LOFTEDSURFACE && dwgPayloadDecoded) {
18308 auto *lofted = static_cast<DRW_LoftedSurface *>(this);
18309 if (!readHandleList(lofted->numCrossSections,
18310 parsedCrossSectionHandles)
18311 || !readHandleList(lofted->numGuideCurves,
18312 parsedGuideCurveHandles))
18313 return fail();
18314 dwgHandle pathCurve;
18315 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, true,
18316 pathCurve))
18317 return fail();
18318 parsedPathCurveHandle = pathCurve.ref;
18319 }
18320 if (!handleProbe.isGood())
18321 return fail();
18322 crossSectionHandles = std::move(parsedCrossSectionHandles);
18323 guideCurveHandles = std::move(parsedGuideCurveHandles);
18324 if (eType == DRW::LOFTEDSURFACE)
18325 static_cast<DRW_LoftedSurface *>(this)->pathCurveHandle =
18326 parsedPathCurveHandle;
18327 *sourceBuf = handleProbe;
18328 return true;
18329}
18330
18331bool DRW_Surface::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
18332 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
18333 (void)bs;
18334 if (buf == nullptr || version < DRW::AC1021 || dwgClassNum == 0)
18335 return false;
18336
18337 const auto validBitShort = [](int value) {
18338 return value >= 0
18339 && value <= std::numeric_limits<std::uint16_t>::max();
18340 };
18341 const auto validCount = [](std::int32_t value, std::size_t limit) {
18342 return value >= 0 && static_cast<std::size_t>(value) <= limit;
18343 };
18344 const auto validTypedPayload = [&]() {
18345 if (!acisEmpty && !rawAcisData.empty())
18346 return false;
18347 if (surfaceHasModelerFormatVersion(eType)
18348 && (!validBitShort(modelerFormatVersion)
18349 || (eType == DRW::LOFTEDSURFACE
18350 && modelerFormatVersion
18351 > surfaceModelerFormatVersionLimit(eType))))
18352 return false;
18353 if (!validBitShort(uIsolines) || !validBitShort(vIsolines))
18354 return false;
18355
18356 switch (eType) {
18357 case DRW::EXTRUDEDSURFACE: {
18358 const auto *surface = static_cast<const DRW_ExtrudedSurface *>(this);
18359 return std::isfinite(surface->draftAngle)
18360 && std::isfinite(surface->draftStartDistance)
18361 && std::isfinite(surface->draftEndDistance)
18362 && std::isfinite(surface->twistAngle)
18363 && std::isfinite(surface->scaleFactor)
18364 && std::isfinite(surface->alignAngle)
18365 && finiteSurfaceCoord(surface->referenceVector)
18366 && finiteSurfaceCoord(surface->sweepVector)
18367 && validBitShort(surface->sweepAlignmentFlags)
18368 && validBitShort(surface->pathFlags)
18369 && finiteSurfaceMatrix(surface->extrudedTransform)
18370 && finiteSurfaceMatrix(surface->sweepEntityTransform)
18371 && finiteSurfaceMatrix(surface->pathEntityTransform);
18372 }
18373 case DRW::LOFTEDSURFACE: {
18374 const auto *surface = static_cast<const DRW_LoftedSurface *>(this);
18375 return std::isfinite(surface->startDraftAngle)
18376 && std::isfinite(surface->endDraftAngle)
18377 && std::isfinite(surface->startDraftMagnitude)
18378 && std::isfinite(surface->endDraftMagnitude)
18379 && validCount(surface->numCrossSections,
18380 DRW_LoftedSurface::kMaxReferenceTokenCount)
18381 && validCount(surface->numGuideCurves,
18382 DRW_LoftedSurface::kMaxReferenceTokenCount)
18383 && surface->crossSectionHandles.size()
18384 == static_cast<std::size_t>(surface->numCrossSections)
18385 && surface->guideCurveHandles.size()
18386 == static_cast<std::size_t>(surface->numGuideCurves)
18387 && finiteSurfaceMatrix(surface->loftEntityTransform);
18388 }
18389 case DRW::REVOLVEDSURFACE: {
18390 const auto *surface = static_cast<const DRW_RevolvedSurface *>(this);
18391 return surface->classId <= std::numeric_limits<std::int32_t>::max()
18392 && surface->id <= std::numeric_limits<std::int32_t>::max()
18393 && finiteSurfaceCoord(surface->axisPoint)
18394 && finiteSurfaceCoord(surface->axisVector)
18395 && std::isfinite(surface->revolveAngle)
18396 && std::isfinite(surface->startAngle)
18397 && std::isfinite(surface->draftAngle)
18398 && std::isfinite(surface->draftStartDistance)
18399 && std::isfinite(surface->draftEndDistance)
18400 && std::isfinite(surface->twistAngle)
18401 && finiteSurfaceMatrix(surface->transform);
18402 }
18403 case DRW::SWEPTSURFACE: {
18404 const auto *surface = static_cast<const DRW_SweptSurface *>(this);
18405 return surface->classVersion <= 10
18406 && surface->sweepEntityId
18407 <= std::numeric_limits<std::int32_t>::max()
18408 && surface->pathEntityId
18409 <= std::numeric_limits<std::int32_t>::max()
18410 && surface->sweepData.size()
18411 <= DRW_SweptSurface::kMaxSweepDataSize
18412 && surface->pathData.size()
18413 <= DRW_SweptSurface::kMaxSweepDataSize
18414 && std::isfinite(surface->draftAngle)
18415 && std::isfinite(surface->draftStartDistance)
18416 && std::isfinite(surface->draftEndDistance)
18417 && std::isfinite(surface->twistAngle)
18418 && std::isfinite(surface->scaleFactor)
18419 && std::isfinite(surface->alignAngle)
18420 && finiteSurfaceCoord(surface->referenceVector)
18421 && validBitShort(surface->sweepAlignmentFlags)
18422 && validBitShort(surface->pathFlags)
18423 && finiteSurfaceMatrix(surface->sweepEntityTransform)
18424 && finiteSurfaceMatrix(surface->pathEntityTransform);
18425 }
18426 case DRW::NURBSURFACE:
18427 if (version < DRW::AC1027)
18428 return true;
18429 {
18430 const auto *surface = static_cast<const DRW_NurbsSurface *>(this);
18431 return finiteSurfaceCoord(surface->uvec1)
18432 && finiteSurfaceCoord(surface->vvec1)
18433 && finiteSurfaceCoord(surface->uvec2)
18434 && finiteSurfaceCoord(surface->vvec2);
18435 }
18436 case DRW::PLANESURFACE:
18437 return true;
18438 default:
18439 return false;
18440 }
18441 };
18442
18443 if (hasRawDwgBody && rawDwgBodyVersion == version) {
18444 if (rawDwgBodyBitSize == 0
18445 || rawDwgBodyBitSize > surfaceRawBitCapacity(rawAcisData))
18446 return false;
18447 } else if (!validTypedPayload()) {
18448 return false;
18449 }
18450 if (!encodeDwgCommon(version, buf, strBuf))
18451 return false;
18452
18453 if (hasRawDwgBody && rawDwgBodyVersion == version) {
18454 if (!putSurfaceRawBits(buf, rawAcisData, rawDwgBodyBitSize))
18455 return false;
18456 } else {
18457 // DXF ACIS/SAB bytes are not the bit-packed DWG ACTION_3DSOLID body.
18458 // Do not place them directly into a DWG object. Native ACIS emission
18459 // remains a separate conversion step; empty modeler bodies are valid
18460 // and allow metadata-only surfaces to round-trip safely.
18461 if (!acisEmpty && !rawAcisData.empty())
18462 return false;
18463 buf->putBit(1); // ACTION_3DSOLID: no ACIS body
18464
18465 const bool hasModelerFormat = surfaceHasModelerFormatVersion(eType);
18466 if (hasModelerFormat) {
18467 if (modelerFormatVersion < 0
18468 || modelerFormatVersion > 0xFFFF
18469 || (eType == DRW::LOFTEDSURFACE && modelerFormatVersion > 3))
18470 return false;
18471 buf->putBitShort(static_cast<std::uint16_t>(modelerFormatVersion));
18472 }
18473 if (uIsolines < 0 || uIsolines > 0xFFFF
18474 || vIsolines < 0 || vIsolines > 0xFFFF)
18475 return false;
18476 buf->putBitShort(static_cast<std::uint16_t>(uIsolines));
18477 buf->putBitShort(static_cast<std::uint16_t>(vIsolines));
18478
18479 auto putCoord = [&](const DRW_Coord& value) {
18480 if (!finiteSurfaceCoord(value))
18481 return false;
18482 buf->put3BitDouble(value);
18483 return true;
18484 };
18485 auto putBool = [&](bool value) { buf->putBit(value ? 1 : 0); };
18486 auto putCount = [&](std::int32_t value, std::size_t limit) {
18487 return value >= 0 && static_cast<std::size_t>(value) <= limit;
18488 };
18489
18490 switch (eType) {
18491 case DRW::EXTRUDEDSURFACE: {
18492 const auto *surface = static_cast<const DRW_ExtrudedSurface *>(this);
18493 if (!std::isfinite(surface->draftAngle)
18494 || !std::isfinite(surface->draftStartDistance)
18495 || !std::isfinite(surface->draftEndDistance)
18496 || !std::isfinite(surface->twistAngle)
18497 || !std::isfinite(surface->scaleFactor)
18498 || !std::isfinite(surface->alignAngle)
18499 || !finiteSurfaceCoord(surface->referenceVector)
18500 || !finiteSurfaceCoord(surface->sweepVector)
18501 || surface->sweepAlignmentFlags < 0
18502 || surface->sweepAlignmentFlags > 0xFFFF
18503 || surface->pathFlags < 0 || surface->pathFlags > 0xFFFF
18504 || !finiteSurfaceMatrix(surface->sweepEntityTransform)
18505 || !finiteSurfaceMatrix(surface->pathEntityTransform))
18506 return false;
18507 buf->putBitDouble(surface->draftAngle);
18508 buf->putBitDouble(surface->draftStartDistance);
18509 buf->putBitDouble(surface->draftEndDistance);
18510 buf->putBitDouble(surface->twistAngle);
18511 buf->putBitDouble(surface->scaleFactor);
18512 buf->putBitDouble(surface->alignAngle);
18513 // The matrices were validated above; emit them in their specified
18514 // position after SweepOptions' scalar fields.
18515 for (double value : surface->sweepEntityTransform)
18516 buf->putBitDouble(value);
18517 for (double value : surface->pathEntityTransform)
18518 buf->putBitDouble(value);
18519 putBool(surface->solid);
18520 buf->putBitShort(static_cast<std::uint16_t>(surface->sweepAlignmentFlags));
18521 buf->putBitShort(static_cast<std::uint16_t>(surface->pathFlags));
18522 putBool(surface->alignStart);
18523 putBool(surface->bank);
18524 putBool(surface->basePointSet);
18525 putBool(surface->sweepEntityTransformComputed);
18526 putBool(surface->pathEntityTransformComputed);
18527 if (!finiteSurfaceCoord(surface->referenceVector)
18528 || !finiteSurfaceCoord(surface->sweepVector)
18529 || !finiteSurfaceMatrix(surface->extrudedTransform))
18530 return false;
18531 putCoord(surface->referenceVector);
18532 putCoord(surface->sweepVector);
18533 for (double value : surface->extrudedTransform)
18534 buf->putBitDouble(value);
18535 break;
18536 }
18537 case DRW::LOFTEDSURFACE: {
18538 const auto *surface = static_cast<const DRW_LoftedSurface *>(this);
18539 if (!std::isfinite(surface->startDraftAngle)
18540 || !std::isfinite(surface->endDraftAngle)
18541 || !std::isfinite(surface->startDraftMagnitude)
18542 || !std::isfinite(surface->endDraftMagnitude)
18543 || !putCount(surface->numCrossSections,
18544 DRW_LoftedSurface::kMaxReferenceTokenCount)
18545 || !putCount(surface->numGuideCurves,
18546 DRW_LoftedSurface::kMaxReferenceTokenCount)
18547 || !finiteSurfaceMatrix(surface->loftEntityTransform))
18548 return false;
18549 for (double value : surface->loftEntityTransform)
18550 buf->putBitDouble(value);
18551 buf->putBitLong(surface->planeNormalLoftingType);
18552 buf->putBitDouble(surface->startDraftAngle);
18553 buf->putBitDouble(surface->endDraftAngle);
18554 buf->putBitDouble(surface->startDraftMagnitude);
18555 buf->putBitDouble(surface->endDraftMagnitude);
18556 putBool(surface->arcLengthParameterization);
18557 putBool(surface->noTwist);
18558 putBool(surface->alignDirection);
18559 putBool(surface->simpleSurfaces);
18560 putBool(surface->closedSurfaces);
18561 putBool(surface->solid);
18562 putBool(surface->ruledSurface);
18563 putBool(surface->virtualGuide);
18564 buf->putBitShort(static_cast<std::uint16_t>(surface->numCrossSections));
18565 buf->putBitShort(static_cast<std::uint16_t>(surface->numGuideCurves));
18566 break;
18567 }
18568 case DRW::REVOLVEDSURFACE: {
18569 const auto *surface = static_cast<const DRW_RevolvedSurface *>(this);
18570 if (surface->classId > std::numeric_limits<std::int32_t>::max()
18571 || surface->id > std::numeric_limits<std::int32_t>::max()
18572 || !finiteSurfaceCoord(surface->axisPoint)
18573 || !finiteSurfaceCoord(surface->axisVector)
18574 || !std::isfinite(surface->revolveAngle)
18575 || !std::isfinite(surface->startAngle)
18576 || !std::isfinite(surface->draftAngle)
18577 || !std::isfinite(surface->draftStartDistance)
18578 || !std::isfinite(surface->draftEndDistance)
18579 || !std::isfinite(surface->twistAngle)
18580 || !finiteSurfaceMatrix(surface->transform))
18581 return false;
18582 buf->putBitLong(static_cast<std::int32_t>(surface->classId));
18583 buf->putBitLong(static_cast<std::int32_t>(surface->id));
18584 buf->put3BitDouble(surface->axisPoint);
18585 buf->put3BitDouble(surface->axisVector);
18586 buf->putBitDouble(surface->revolveAngle);
18587 buf->putBitDouble(surface->startAngle);
18588 for (double value : surface->transform)
18589 buf->putBitDouble(value);
18590 buf->putBitDouble(surface->draftAngle);
18591 buf->putBitDouble(surface->draftStartDistance);
18592 buf->putBitDouble(surface->draftEndDistance);
18593 buf->putBitDouble(surface->twistAngle);
18594 putBool(surface->solid);
18595 putBool(surface->closeToAxis);
18596 break;
18597 }
18598 case DRW::SWEPTSURFACE: {
18599 const auto *surface = static_cast<const DRW_SweptSurface *>(this);
18600 if (surface->classVersion > 10
18601 || surface->sweepEntityId > std::numeric_limits<std::int32_t>::max()
18602 || surface->pathEntityId > std::numeric_limits<std::int32_t>::max()
18603 || surface->sweepData.size() > DRW_SweptSurface::kMaxSweepDataSize
18604 || surface->pathData.size() > DRW_SweptSurface::kMaxSweepDataSize
18605 || !std::isfinite(surface->draftAngle)
18606 || !std::isfinite(surface->draftStartDistance)
18607 || !std::isfinite(surface->draftEndDistance)
18608 || !std::isfinite(surface->twistAngle)
18609 || !std::isfinite(surface->scaleFactor)
18610 || !std::isfinite(surface->alignAngle)
18611 || !finiteSurfaceCoord(surface->referenceVector)
18612 || surface->sweepAlignmentFlags < 0
18613 || surface->sweepAlignmentFlags > 0xFFFF
18614 || surface->pathFlags < 0 || surface->pathFlags > 0xFFFF
18615 || !finiteSurfaceMatrix(surface->sweepEntityTransform)
18616 || !finiteSurfaceMatrix(surface->pathEntityTransform))
18617 return false;
18618 buf->putBitLong(static_cast<std::int32_t>(surface->classVersion));
18619 buf->putBitLong(static_cast<std::int32_t>(surface->sweepEntityId));
18620 buf->putBitLong(static_cast<std::int32_t>(surface->sweepData.size()));
18621 if (!surface->sweepData.empty())
18622 buf->putBytes(surface->sweepData.data(), surface->sweepData.size());
18623 buf->putBitLong(static_cast<std::int32_t>(surface->pathEntityId));
18624 buf->putBitLong(static_cast<std::int32_t>(surface->pathData.size()));
18625 if (!surface->pathData.empty())
18626 buf->putBytes(surface->pathData.data(), surface->pathData.size());
18627 buf->putBitDouble(surface->draftAngle);
18628 buf->putBitDouble(surface->draftStartDistance);
18629 buf->putBitDouble(surface->draftEndDistance);
18630 buf->putBitDouble(surface->twistAngle);
18631 buf->putBitDouble(surface->scaleFactor);
18632 buf->putBitDouble(surface->alignAngle);
18633 for (double value : surface->sweepEntityTransform)
18634 buf->putBitDouble(value);
18635 for (double value : surface->pathEntityTransform)
18636 buf->putBitDouble(value);
18637 putBool(surface->solid);
18638 buf->putBitShort(static_cast<std::uint16_t>(surface->sweepAlignmentFlags));
18639 buf->putBitShort(static_cast<std::uint16_t>(surface->pathFlags));
18640 putBool(surface->alignStart);
18641 putBool(surface->bank);
18642 putBool(surface->basePointSet);
18643 putBool(surface->sweepEntityTransformComputed);
18644 putBool(surface->pathEntityTransformComputed);
18645 if (!finiteSurfaceCoord(surface->referenceVector))
18646 return false;
18647 putCoord(surface->referenceVector);
18648 break;
18649 }
18650 case DRW::NURBSURFACE: {
18651 if (version < DRW::AC1027)
18652 break;
18653 const auto *surface = static_cast<const DRW_NurbsSurface *>(this);
18654 if (!finiteSurfaceCoord(surface->uvec1)
18655 || !finiteSurfaceCoord(surface->vvec1)
18656 || !finiteSurfaceCoord(surface->uvec2)
18657 || !finiteSurfaceCoord(surface->vvec2))
18658 return false;
18659 buf->putBitShort(surface->short170);
18660 putBool(surface->cvHullDisplay);
18661 buf->put3BitDouble(surface->uvec1);
18662 buf->put3BitDouble(surface->vvec1);
18663 buf->put3BitDouble(surface->uvec2);
18664 buf->put3BitDouble(surface->vvec2);
18665 break;
18666 }
18667 case DRW::PLANESURFACE:
18668 break;
18669 default:
18670 return false;
18671 }
18672 }
18673
18674 if (!encodeDwgEntHandle(version, buf, handleBuf))
18675 return false;
18676 if (eType == DRW::LOFTEDSURFACE) {
18677 dwgBufferW *hb = handleBuf != nullptr ? handleBuf : buf;
18678 for (std::uint32_t ref : crossSectionHandles)
18679 putSurfaceHandle(hb, ref);
18680 for (std::uint32_t ref : guideCurveHandles)
18681 putSurfaceHandle(hb, ref);
18682 putSurfaceHandle(hb,
18683 static_cast<const DRW_LoftedSurface *>(this)
18684 ->pathCurveHandle);
18685 }
18686 return true;
18687}
18688
18689bool DRW_Dimension::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
18690 switch (code) {
18691 case 1:
18692 text = reader->getUtf8String();
18693 break;
18694 case 2:
18695 name = reader->getString();
18696 break;
18697 case 3:
18698 style = reader->getUtf8String();
18699 break;
18700 case 70:
18701 type = reader->getInt32();
18702 break;
18703 case 71:
18704 align = reader->getInt32();
18705 break;
18706 case 72:
18707 linesty = reader->getInt32();
18708 break;
18709 case 10:
18710 defPoint.x = reader->getDouble();
18711 break;
18712 case 20:
18713 defPoint.y = reader->getDouble();
18714 break;
18715 case 30:
18716 defPoint.z = reader->getDouble();
18717 break;
18718 case 11:
18719 textPoint.x = reader->getDouble();
18720 break;
18721 case 21:
18722 textPoint.y = reader->getDouble();
18723 break;
18724 case 31:
18725 textPoint.z = reader->getDouble();
18726 break;
18727 case 12:
18728 clonePoint.x = reader->getDouble();
18729 break;
18730 case 22:
18731 clonePoint.y = reader->getDouble();
18732 break;
18733 case 32:
18734 clonePoint.z = reader->getDouble();
18735 break;
18736 case 13:
18737 def1.x = reader->getDouble();
18738 break;
18739 case 23:
18740 def1.y = reader->getDouble();
18741 break;
18742 case 33:
18743 def1.z = reader->getDouble();
18744 break;
18745 case 14:
18746 def2.x = reader->getDouble();
18747 break;
18748 case 24:
18749 def2.y = reader->getDouble();
18750 break;
18751 case 34:
18752 def2.z = reader->getDouble();
18753 break;
18754 case 15:
18755 circlePoint.x = reader->getDouble();
18756 break;
18757 case 25:
18758 circlePoint.y = reader->getDouble();
18759 break;
18760 case 35:
18761 circlePoint.z = reader->getDouble();
18762 break;
18763 case 16:
18764 arcPoint.x = reader->getDouble();
18765 break;
18766 case 26:
18767 arcPoint.y = reader->getDouble();
18768 break;
18769 case 36:
18770 arcPoint.z = reader->getDouble();
18771 break;
18772 case 41:
18773 linefactor = reader->getDouble();
18774 break;
18775 case 53:
18776 rot = reader->getDouble();
18777 break;
18778 case 50:
18779 angle = reader->getDouble();
18780 break;
18781 case 52:
18782 oblique = reader->getDouble();
18783 break;
18784 case 40:
18785 length = reader->getDouble();
18786 break;
18787 case 51:
18788 hdir = reader->getDouble();
18789 break;
18790 case 42:
18791 measureValue = reader->getDouble();
18792 break;
18793 case 74:
18794 flipArrow1 = reader->getInt32() != 0;
18795 break;
18796 case 75:
18797 flipArrow2 = reader->getInt32() != 0;
18798 break;
18799 case 76:
18800 genTol = reader->getInt32() != 0;
18801 break;
18802 case 77:
18803 limGen = reader->getInt32() != 0;
18804 break;
18805 case 43:
18806 tolPlus = reader->getDouble();
18807 break;
18808 case 44:
18809 tolMinus = reader->getDouble();
18810 break;
18811 case 45:
18812 tolScale = reader->getDouble();
18813 break;
18814 case 78:
18815 tolDecimals = reader->getInt32();
18816 break;
18817 case 79:
18818 tolAlign = reader->getInt32();
18819 break;
18820 case 80:
18821 tolZero = reader->getInt32();
18822 break;
18823 case 81:
18824 altTolDecimals = reader->getInt32();
18825 break;
18826 case 82:
18827 altZero = reader->getInt32();
18828 break;
18829 case 83:
18830 altTolZero = reader->getInt32();
18831 break;
18832 case 84:
18833 textMove = reader->getInt32();
18834 break;
18835 default:
18836 return DRW_Entity::parseCode(code, reader);
18837 }
18838
18839 return true;
18840}
18841
18842bool DRW_Dimension::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs)
18843{
18844 DRW_UNUSED( version)(void)version;
18845 DRW_UNUSED( buf)(void)buf;
18846 DRW_UNUSED( bs)(void)bs;
18847
18848 DRW_DBG("DRW_Dimension::parseDwg(): base class implemntation should never be called direct!\n")DRW_dbg::dbg("DRW_Dimension::parseDwg(): base class implemntation should never be called direct!\n"
)
;
18849
18850 return false;
18851}
18852
18853void DRW_Dimension::resetDwgState() {
18854 DRW_Entity::reset();
18855 type = 0;
18856 name.clear();
18857 defPoint = DRW_Coord{0.0, 0.0, 0.0};
18858 textPoint = DRW_Coord{0.0, 0.0, 0.0};
18859 text.clear();
18860 style = "STANDARD";
18861 align = 5;
18862 linesty = 1;
18863 linefactor = 1.0;
18864 rot = 0.0;
18865 extPoint = DRW_Coord{0.0, 0.0, 1.0};
18866 hdir = 0.0;
18867 clonePoint = DRW_Coord{0.0, 0.0, 0.0};
18868 def1 = DRW_Coord{0.0, 0.0, 0.0};
18869 def2 = DRW_Coord{0.0, 0.0, 0.0};
18870 angle = 0.0;
18871 oblique = 0.0;
18872 circlePoint = DRW_Coord{0.0, 0.0, 0.0};
18873 arcPoint = DRW_Coord{0.0, 0.0, 0.0};
18874 length = 0.0;
18875 measureValue = 0.0;
18876 flipArrow1 = false;
18877 flipArrow2 = false;
18878 genTol = false;
18879 limGen = false;
18880 tolPlus = 0.0;
18881 tolMinus = 0.0;
18882 tolScale = 0.0;
18883 tolDecimals = 0;
18884 tolAlign = 0;
18885 tolZero = 0;
18886 altTolDecimals = 0;
18887 altZero = 0;
18888 altTolZero = 0;
18889 textMove = 0;
18890 dimStyleH = dwgHandle{};
18891 blockH = dwgHandle{};
18892}
18893
18894bool DRW_Dimension::parseDwg(DRW::Version version, dwgBuffer *buf, dwgBuffer *sBuf, std::uint32_t bs /*= 0*/) {
18895 (void)sBuf;
18896 resetDwgState();
18897 if (buf == nullptr)
18898 return false;
18899
18900 dwgBuffer sBuff = *buf;
18901 dwgBuffer *commonStringBuffer = buf;
18902 if (version > DRW::AC1018) {//2007+
18903 commonStringBuffer = &sBuff; //separate buffer for strings
18904 }
18905
18906 if (!DRW_Entity::parseDwg(version, buf, commonStringBuffer, bs))
18907 return false;
18908
18909 DRW_DBG("\n***************************** parsing dimension *********************************************")DRW_dbg::dbg("\n***************************** parsing dimension *********************************************"
)
;
18910 const std::uint64_t bodyEndBit = dwgDataEndBit;
18911 dwgBuffer bodyProbe = buf->forkIndependent();
18912 dwgBuffer stringProbe = sBuff;
18913 dwgBuffer *textBuffer = &bodyProbe;
18914 if (version > DRW::AC1018)
18915 textBuffer = &stringProbe;
18916 std::uint64_t stringStartBit = bodyEndBit;
18917 std::uint64_t stringEndBit = bodyEndBit;
18918 if (version > DRW::AC1018) {
18919 stringStartBit = currentDwgBit(textBuffer);
18920 if (version > DRW::AC1021) {
18921 std::uint64_t ignoredBodyEndBit = 0;
18922 if (!entityBodyDataEndBit(*textBuffer, version, objSize,
18923 ignoredBodyEndBit, &stringEndBit))
18924 return false;
18925 }
18926 }
18927
18928 std::uint8_t parsedDimVersion = 0;
18929 if (version > DRW::AC1021) { //2010+
18930 if (!readBoundedRawChar8(bodyProbe, bodyEndBit, parsedDimVersion))
18931 return false;
18932 DRW_DBG("\ndimVersion: ")DRW_dbg::dbg("\ndimVersion: "); DRW_DBG(parsedDimVersion)DRW_dbg::dbg(parsedDimVersion);
18933 }
18934
18935 DRW_Coord parsedExtrusion;
18936 DRW_Coord parsedTextPoint;
18937 DRW_Coord ignoredInsertionPoint;
18938 double parsedTextPointZ = 0.0;
18939 double ignoredInsertionRotation = 0.0;
18940 std::uint8_t parsedType = 0;
18941 if (!readBoundedBitCoord(bodyProbe, bodyEndBit, parsedExtrusion)
18942 || !readBoundedRawDouble(bodyProbe, bodyEndBit, parsedTextPoint.x)
18943 || !readBoundedRawDouble(bodyProbe, bodyEndBit, parsedTextPoint.y)
18944 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedTextPointZ)
18945 || !readBoundedRawChar8(bodyProbe, bodyEndBit, parsedType)) {
18946 return false;
18947 }
18948 parsedTextPoint.z = parsedTextPointZ;
18949
18950 // ODA §20.4.22: Extrusion is plain 3BD (NOT BE) — confirmed by libreDWG dwg_spec_shared.h
18951 DRW_DBG("\nextPoint: ")DRW_dbg::dbg("\nextPoint: "); DRW_DBGPT(parsedExtrusion.x, parsedExtrusion.y,DRW_dbg::dbgPT(parsedExtrusion.x, parsedExtrusion.y, parsedExtrusion
.z)
18952 parsedExtrusion.z)DRW_dbg::dbgPT(parsedExtrusion.x, parsedExtrusion.y, parsedExtrusion
.z)
;
18953 DRW_DBG("\ntextPoint: ")DRW_dbg::dbg("\ntextPoint: "); DRW_DBGPT(parsedTextPoint.x, parsedTextPoint.y,DRW_dbg::dbgPT(parsedTextPoint.x, parsedTextPoint.y, parsedTextPoint
.z)
18954 parsedTextPoint.z)DRW_dbg::dbgPT(parsedTextPoint.x, parsedTextPoint.y, parsedTextPoint
.z)
;
18955 DRW_DBG("\ntype (70) read: ")DRW_dbg::dbg("\ntype (70) read: "); DRW_DBG(parsedType)DRW_dbg::dbg(parsedType);
18956 const std::uint8_t parsedDimensionType =
18957 static_cast<std::uint8_t>((parsedType & 1) ? parsedType & 0x7F
18958 : parsedType | 0x80);
18959 int parsedTypeValue = (parsedDimensionType & 2) ? parsedDimensionType | 0x20
18960 : parsedDimensionType & 0xDF;
18961 DRW_DBG(" type (70) set: ")DRW_dbg::dbg(" type (70) set: "); DRW_DBG(parsedTypeValue)DRW_dbg::dbg(parsedTypeValue);
18962 //clear last 3 bits to set integer dim type
18963 parsedTypeValue &= 0xF8;
18964 UTF8STRINGstd::string parsedText;
18965 if (version > DRW::AC1018 && stringStartBit >= stringEndBit) {
18966 parsedText.clear();
18967 } else if (!readBoundedVariableText(*textBuffer, stringEndBit, version,
18968 parsedText)) {
18969 return false;
18970 }
18971 DRW_DBG("\nforced dim text: ")DRW_dbg::dbg("\nforced dim text: "); DRW_DBG(parsedText.c_str())DRW_dbg::dbg(parsedText.c_str());
18972 double parsedRot = 0.0;
18973 double parsedHDir = 0.0;
18974 if (!readBoundedBitDouble(bodyProbe, bodyEndBit, parsedRot)
18975 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedHDir)
18976 || !readBoundedBitCoord(bodyProbe, bodyEndBit, ignoredInsertionPoint)
18977 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
18978 ignoredInsertionRotation)) {
18979 return false;
18980 }
18981 DRW_DBG("\ninspoint: ")DRW_dbg::dbg("\ninspoint: "); DRW_DBGPT(ignoredInsertionPoint.x,DRW_dbg::dbgPT(ignoredInsertionPoint.x, ignoredInsertionPoint
.y, ignoredInsertionPoint.z)
18982 ignoredInsertionPoint.y,DRW_dbg::dbgPT(ignoredInsertionPoint.x, ignoredInsertionPoint
.y, ignoredInsertionPoint.z)
18983 ignoredInsertionPoint.z)DRW_dbg::dbgPT(ignoredInsertionPoint.x, ignoredInsertionPoint
.y, ignoredInsertionPoint.z)
;
18984 DRW_DBG(" insRot_code54: ")DRW_dbg::dbg(" insRot_code54: "); DRW_DBG(ignoredInsertionRotation)DRW_dbg::dbg(ignoredInsertionRotation);
18985
18986 std::uint16_t parsedAlign = 5;
18987 std::uint16_t parsedLineStyle = 1;
18988 double parsedLineFactor = 1.0;
18989 double parsedMeasureValue = 0.0;
18990 bool parsedFlipArrow1 = false;
18991 bool parsedFlipArrow2 = false;
18992 if (version > DRW::AC1014) { //2000+
18993 if (!readBoundedBitShort(bodyProbe, bodyEndBit, parsedAlign)
18994 || !readBoundedBitShort(bodyProbe, bodyEndBit, parsedLineStyle)
18995 || !readBoundedBitDouble(bodyProbe, bodyEndBit, parsedLineFactor)
18996 || !readBoundedBitDouble(bodyProbe, bodyEndBit,
18997 parsedMeasureValue))
18998 return false;
18999 DRW_DBG("\n actMeas_code42: ")DRW_dbg::dbg("\n actMeas_code42: "); DRW_DBG(parsedMeasureValue)DRW_dbg::dbg(parsedMeasureValue);
19000 if (version > DRW::AC1018) { //2007+
19001 bool ignoredBit = false;
19002 if (!readBoundedBit(bodyProbe, bodyEndBit, ignoredBit)
19003 || !readBoundedBit(bodyProbe, bodyEndBit, parsedFlipArrow1)
19004 || !readBoundedBit(bodyProbe, bodyEndBit, parsedFlipArrow2))
19005 return false;
19006 DRW_DBG("\n2007, unk, flip1, flip2: ")DRW_dbg::dbg("\n2007, unk, flip1, flip2: "); DRW_DBG(ignoredBit)DRW_dbg::dbg(ignoredBit);
19007 DRW_DBG(parsedFlipArrow1)DRW_dbg::dbg(parsedFlipArrow1); DRW_DBG(parsedFlipArrow2)DRW_dbg::dbg(parsedFlipArrow2);
19008 }
19009 }
19010 DRW_Coord parsedClonePoint;
19011 if (!readBoundedRawDouble(bodyProbe, bodyEndBit, parsedClonePoint.x)
19012 || !readBoundedRawDouble(bodyProbe, bodyEndBit, parsedClonePoint.y))
19013 return false;
19014 parsedClonePoint.z = 0.0;
19015 if (!bodyProbe.isGood() || !textBuffer->isGood()
19016 || currentDwgBit(&bodyProbe) > bodyEndBit
19017 || currentDwgBit(textBuffer) > stringEndBit
19018 || !std::isfinite(parsedExtrusion.x)
19019 || !std::isfinite(parsedExtrusion.y)
19020 || !std::isfinite(parsedExtrusion.z)
19021 || !std::isfinite(parsedTextPoint.x)
19022 || !std::isfinite(parsedTextPoint.y)
19023 || !std::isfinite(parsedTextPoint.z)
19024 || !std::isfinite(ignoredInsertionPoint.x)
19025 || !std::isfinite(ignoredInsertionPoint.y)
19026 || !std::isfinite(ignoredInsertionPoint.z)
19027 || !std::isfinite(parsedRot)
19028 || !std::isfinite(parsedHDir)
19029 || !std::isfinite(ignoredInsertionRotation)
19030 || !std::isfinite(parsedLineFactor)
19031 || !std::isfinite(parsedMeasureValue)
19032 || !std::isfinite(parsedClonePoint.x)
19033 || !std::isfinite(parsedClonePoint.y))
19034 return false;
19035
19036 extPoint = parsedExtrusion;
19037 textPoint = parsedTextPoint;
19038 type = parsedTypeValue;
19039 text = parsedText;
19040 rot = parsedRot;
19041 hdir = parsedHDir;
19042 align = parsedAlign;
19043 linesty = parsedLineStyle;
19044 linefactor = parsedLineFactor;
19045 measureValue = parsedMeasureValue;
19046 flipArrow1 = parsedFlipArrow1;
19047 flipArrow2 = parsedFlipArrow2;
19048 clonePoint = parsedClonePoint;
19049 *buf = bodyProbe;
19050 return true;
19051}
19052
19053bool DRW_Dimension::parseDwgDimensionHandles(DRW::Version version,
19054 dwgBuffer *buf,
19055 std::uint64_t handleEndBit) {
19056 if (buf == nullptr)
19057 return false;
19058 dwgBuffer probe = buf->forkIndependent();
19059 if (!parseDwgEntHandle(version, &probe, true, handleEndBit))
19060 return false;
19061 dwgHandle parsedDimStyle;
19062 dwgHandle parsedBlock;
19063 if (!readBoundedDwgHandle(probe, handleEndBit, 0, false, parsedDimStyle)
19064 || !readBoundedDwgHandle(probe, handleEndBit, 0, false, parsedBlock))
19065 return false;
19066 dimStyleH = parsedDimStyle;
19067 blockH = parsedBlock;
19068 *buf = probe;
19069 return true;
19070}
19071
19072bool DRW_DimAligned::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19073 DRW_Dimension::resetDwgState();
19074 dwgBuffer *sourceBuf = buf;
19075 auto fail = [this, sourceBuf]() {
19076 if (sourceBuf != nullptr)
19077 sourceBuf->invalidate();
19078 DRW_Dimension::resetDwgState();
19079 return false;
19080 };
19081 if (sourceBuf == nullptr)
19082 return fail();
19083
19084 dwgBuffer probe = sourceBuf->forkIndependent();
19085 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19086 return fail();
19087 buf = &probe;
19088
19089 if (oType == 0x15)
19090 DRW_DBG("\n***************************** parsing dim linear *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim linear *********************************************\n"
)
;
19091 else
19092 DRW_DBG("\n***************************** parsing dim aligned *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim aligned *********************************************\n"
)
;
19093 const std::uint64_t bodyEndBit = dwgDataEndBit;
19094 DRW_Coord parsedDef1;
19095 DRW_Coord parsedDef2;
19096 DRW_Coord parsedDimPoint;
19097 double parsedOblique = 0.0;
19098 double parsedAngle = 0.0;
19099 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
19100 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
19101 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDimPoint)
19102 || !readBoundedBitDouble(*buf, bodyEndBit, parsedOblique))
19103 return fail();
19104 if (oType == 0x15
19105 && !readBoundedBitDouble(*buf, bodyEndBit, parsedAngle))
19106 return fail();
19107 const auto finite = [](const DRW_Coord& point) {
19108 return std::isfinite(point.x) && std::isfinite(point.y)
19109 && std::isfinite(point.z);
19110 };
19111 if (!std::isfinite(parsedOblique) || !std::isfinite(parsedAngle)
19112 || !finite(parsedDef1) || !finite(parsedDef2)
19113 || !finite(parsedDimPoint)) {
19114 return fail();
19115 }
19116 setPt3(parsedDef1); //def1
19117 setPt4(parsedDef2);
19118 setDefPoint(parsedDimPoint);
19119 setOb52(parsedOblique * ARAD57.29577951308232); // radians → degrees
19120 if (oType == 0x15)
19121 setAn50(parsedAngle * ARAD57.29577951308232);
19122 else
19123 type |= 1;
19124 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
19125
19126 std::uint64_t handleEndBit = 0;
19127 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19128 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19129 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n"
)
;
19130 return fail();
19131 }
19132 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19133 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19134
19135 // RS crc; //RS */
19136 if (!buf->isGood())
19137 return fail();
19138 *sourceBuf = probe;
19139 return true;
19140 }
19141
19142bool DRW_DimRadial::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19143 DRW_Dimension::resetDwgState();
19144 dwgBuffer *sourceBuf = buf;
19145 auto fail = [this, sourceBuf]() {
19146 if (sourceBuf != nullptr)
19147 sourceBuf->invalidate();
19148 DRW_Dimension::resetDwgState();
19149 return false;
19150 };
19151 if (sourceBuf == nullptr)
19152 return fail();
19153
19154 dwgBuffer probe = sourceBuf->forkIndependent();
19155 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19156 return fail();
19157 buf = &probe;
19158
19159 DRW_DBG("\n***************************** parsing dim radial *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim radial *********************************************\n"
)
;
19160 const std::uint64_t bodyEndBit = dwgDataEndBit;
19161 DRW_Coord parsedDefPoint;
19162 DRW_Coord parsedCenter;
19163 double parsedLength = 0.0;
19164 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19165 || !readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
19166 || !readBoundedBitDouble(*buf, bodyEndBit, parsedLength)
19167 || !std::isfinite(parsedDefPoint.x)
19168 || !std::isfinite(parsedDefPoint.y)
19169 || !std::isfinite(parsedDefPoint.z)
19170 || !std::isfinite(parsedCenter.x)
19171 || !std::isfinite(parsedCenter.y)
19172 || !std::isfinite(parsedCenter.z)
19173 || !std::isfinite(parsedLength))
19174 return fail();
19175 setDefPoint(parsedDefPoint); //code 10
19176 DRW_DBG("defPoint: ")DRW_dbg::dbg("defPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19177 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19178 setPt5(parsedCenter); //center pt code 15
19179 DRW_DBG("\ncenter point: ")DRW_dbg::dbg("\ncenter point: "); DRW_DBGPT(parsedCenter.x, parsedCenter.y,DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
19180 parsedCenter.z)DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
;
19181 setRa40(parsedLength); //leader length code 40
19182 DRW_DBG("\nleader length: ")DRW_dbg::dbg("\nleader length: "); DRW_DBG(getRa40())DRW_dbg::dbg(getRa40());
19183 type |= 4;
19184 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
19185 if (!buf->isGood())
19186 return fail();
19187
19188 std::uint64_t handleEndBit = 0;
19189 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19190 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19191 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimRadial::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimRadial::parseDwg()\n"
)
;
19192 return fail();
19193 }
19194 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19195 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19196
19197 // RS crc; //RS */
19198 if (!buf->isGood())
19199 return fail();
19200 *sourceBuf = probe;
19201 return true;
19202}
19203
19204// DRW_DimLargeRadial (AcDbRadialDimensionLarge, LARGE_RADIAL_DIMENSION).
19205// DXF group-code parser: the AcDbRadialDimensionLarge subclass overloads codes
19206// 13/14/15/40 (chord / override center / jog point / jog angle), so gate them on
19207// the subclass marker (like DRW_DimArc). The chord point is stored as the radial
19208// diameter point so the existing addDimRadial consumer renders center→chord.
19209bool DRW_DimLargeRadial::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
19210 if (code == 100) {
19211 std::string s = reader->getString();
19212 if (s == "AcDbRadialDimensionLarge") {
19213 m_largeRadialSubclassSeen = true;
19214 return true;
19215 }
19216 return DRW_Dimension::parseCode(code, reader);
19217 }
19218 if (m_largeRadialSubclassSeen) {
19219 DRW_Coord chord;
19220 switch (code) {
19221 case 13: chord = getPt5(); chord.x = reader->getDouble(); setPt5(chord); return true;
19222 case 23: chord = getPt5(); chord.y = reader->getDouble(); setPt5(chord); return true;
19223 case 33: chord = getPt5(); chord.z = reader->getDouble(); setPt5(chord); return true;
19224 case 14: overrideCenterPoint.x = reader->getDouble(); return true;
19225 case 24: overrideCenterPoint.y = reader->getDouble(); return true;
19226 case 34: overrideCenterPoint.z = reader->getDouble(); return true;
19227 case 15: jogPoint.x = reader->getDouble(); return true;
19228 case 25: jogPoint.y = reader->getDouble(); return true;
19229 case 35: jogPoint.z = reader->getDouble(); return true;
19230 case 40: jogAngle = reader->getDouble(); return true;
19231 default: break;
19232 }
19233 }
19234 return DRW_Dimension::parseCode(code, reader);
19235}
19236
19237// DRW_DimLargeRadial DWG body: five subclass reads then the dim-style and
19238// anon-block handles. The three subclass points are ordered
19239// definition point, JOG point, jog angle, CHORD point, OVERRIDDEN center
19240// so that the decoded fields match the DXF group codes (chord=13, override=14,
19241// jog=15) and libdxfrw's own DXF parseCode. The read-only reference parser
19242// (parseLargeRadialDimension) labels the 2nd/4th/5th reads chord/override/jog,
19243// i.e. a cyclic rotation of the point roles; that is inconsistent with the DXF
19244// semantics and with an ODA File Converter DXF↔DWG round-trip (which preserves
19245// codes 13/14/15 exactly). Verified by large_radial_dim_dwg_tests.cpp against
19246// an ODA-synthesized fixture, cross-checked with the dwg-parser's DXF read.
19247// Only the field labels change vs. the reference parser — the read sizes/order
19248// (3BD, 3BD, BD, 3BD, 3BD) are identical, so buffer alignment is unchanged.
19249bool DRW_DimLargeRadial::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19250 DRW_Dimension::resetDwgState();
19251 overrideCenterPoint = DRW_Coord{0.0, 0.0, 0.0};
19252 jogPoint = DRW_Coord{0.0, 0.0, 0.0};
19253 jogAngle = 0.0;
19254 m_largeRadialSubclassSeen = false;
19255 dwgBuffer *sourceBuf = buf;
19256 auto fail = [this, sourceBuf]() {
19257 if (sourceBuf != nullptr)
19258 sourceBuf->invalidate();
19259 DRW_Dimension::resetDwgState();
19260 overrideCenterPoint = DRW_Coord{0.0, 0.0, 0.0};
19261 jogPoint = DRW_Coord{0.0, 0.0, 0.0};
19262 jogAngle = 0.0;
19263 m_largeRadialSubclassSeen = false;
19264 return false;
19265 };
19266 if (sourceBuf == nullptr)
19267 return fail();
19268
19269 dwgBuffer probe = sourceBuf->forkIndependent();
19270 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19271 return fail();
19272 buf = &probe;
19273 const std::uint64_t bodyEndBit = dwgDataEndBit;
19274 DRW_Coord parsedDefPoint;
19275 DRW_Coord parsedJogPoint;
19276 DRW_Coord parsedChordPoint;
19277 DRW_Coord parsedOverrideCenter;
19278 double parsedJogAngle = 0.0;
19279 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19280 || !readBoundedBitCoord(*buf, bodyEndBit, parsedJogPoint)
19281 || !readBoundedBitDouble(*buf, bodyEndBit, parsedJogAngle)
19282 || !readBoundedBitCoord(*buf, bodyEndBit, parsedChordPoint)
19283 || !readBoundedBitCoord(*buf, bodyEndBit, parsedOverrideCenter)
19284 || !std::isfinite(parsedDefPoint.x)
19285 || !std::isfinite(parsedDefPoint.y)
19286 || !std::isfinite(parsedDefPoint.z)
19287 || !std::isfinite(parsedJogPoint.x)
19288 || !std::isfinite(parsedJogPoint.y)
19289 || !std::isfinite(parsedJogPoint.z)
19290 || !std::isfinite(parsedChordPoint.x)
19291 || !std::isfinite(parsedChordPoint.y)
19292 || !std::isfinite(parsedChordPoint.z)
19293 || !std::isfinite(parsedOverrideCenter.x)
19294 || !std::isfinite(parsedOverrideCenter.y)
19295 || !std::isfinite(parsedOverrideCenter.z)
19296 || !std::isfinite(parsedJogAngle))
19297 return fail();
19298 setDefPoint(parsedDefPoint); // definition point (code 10)
19299 jogPoint = parsedJogPoint; // jog vertex (code 15)
19300 jogAngle = parsedJogAngle; // jog transverse angle (code 40)
19301 setPt5(parsedChordPoint); // chord point (code 13)
19302 overrideCenterPoint = parsedOverrideCenter; // overridden center (code 14)
19303 type |= 4; // radial dimension type bit
19304 if (!buf->isGood())
19305 return fail();
19306 std::uint64_t handleEndBit = 0;
19307 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19308 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19309 return fail();
19310 }
19311 *sourceBuf = probe;
19312 return true;
19313}
19314
19315bool DRW_DimDiametric::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19316 DRW_Dimension::resetDwgState();
19317 dwgBuffer *sourceBuf = buf;
19318 auto fail = [this, sourceBuf]() {
19319 if (sourceBuf != nullptr)
19320 sourceBuf->invalidate();
19321 DRW_Dimension::resetDwgState();
19322 return false;
19323 };
19324 if (sourceBuf == nullptr)
19325 return fail();
19326
19327 dwgBuffer probe = sourceBuf->forkIndependent();
19328 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19329 return fail();
19330 buf = &probe;
19331
19332 DRW_DBG("\n***************************** parsing dim diametric *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim diametric *********************************************\n"
)
;
19333 const std::uint64_t bodyEndBit = dwgDataEndBit;
19334 DRW_Coord parsedCenter;
19335 DRW_Coord parsedDefPoint;
19336 double parsedLength = 0.0;
19337 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
19338 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19339 || !readBoundedBitDouble(*buf, bodyEndBit, parsedLength)
19340 || !std::isfinite(parsedCenter.x)
19341 || !std::isfinite(parsedCenter.y)
19342 || !std::isfinite(parsedCenter.z)
19343 || !std::isfinite(parsedDefPoint.x)
19344 || !std::isfinite(parsedDefPoint.y)
19345 || !std::isfinite(parsedDefPoint.z)
19346 || !std::isfinite(parsedLength))
19347 return fail();
19348 setPt5(parsedCenter); //center pt code 15
19349 DRW_DBG("center point: ")DRW_dbg::dbg("center point: "); DRW_DBGPT(parsedCenter.x, parsedCenter.y,DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
19350 parsedCenter.z)DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
;
19351 setDefPoint(parsedDefPoint); //code 10
19352 DRW_DBG("\ndefPoint: ")DRW_dbg::dbg("\ndefPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19353 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19354 setRa40(parsedLength); //leader length code 40
19355 DRW_DBG("\nleader length: ")DRW_dbg::dbg("\nleader length: "); DRW_DBG(getRa40())DRW_dbg::dbg(getRa40());
19356 type |= 3;
19357 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
19358 if (!buf->isGood())
19359 return fail();
19360
19361 std::uint64_t handleEndBit = 0;
19362 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19363 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19364 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimDiametric::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimDiametric::parseDwg()\n"
)
;
19365 return fail();
19366 }
19367 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19368 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19369
19370 // RS crc; //RS */
19371 if (!buf->isGood())
19372 return fail();
19373 *sourceBuf = probe;
19374 return true;
19375}
19376
19377bool DRW_DimAngular::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19378 DRW_Dimension::resetDwgState();
19379 dwgBuffer *sourceBuf = buf;
19380 auto fail = [this, sourceBuf]() {
19381 if (sourceBuf != nullptr)
19382 sourceBuf->invalidate();
19383 DRW_Dimension::resetDwgState();
19384 return false;
19385 };
19386 if (sourceBuf == nullptr)
19387 return fail();
19388
19389 dwgBuffer probe = sourceBuf->forkIndependent();
19390 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19391 return fail();
19392 buf = &probe;
19393
19394 DRW_DBG("\n***************************** parsing dim angular *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim angular *********************************************\n"
)
;
19395 const std::uint64_t bodyEndBit = dwgDataEndBit;
19396 DRW_Coord parsedArcPoint;
19397 DRW_Coord parsedDef1;
19398 DRW_Coord parsedDef2;
19399 DRW_Coord parsedCenter;
19400 DRW_Coord parsedDefPoint;
19401 if (!readBoundedRawDouble(*buf, bodyEndBit, parsedArcPoint.x)
19402 || !readBoundedRawDouble(*buf, bodyEndBit, parsedArcPoint.y)
19403 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
19404 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
19405 || !readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
19406 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint))
19407 return fail();
19408 parsedArcPoint.z = 0.0;
19409 const auto finite = [](const DRW_Coord& point) {
19410 return std::isfinite(point.x) && std::isfinite(point.y)
19411 && std::isfinite(point.z);
19412 };
19413 if (!finite(parsedArcPoint) || !finite(parsedDef1) || !finite(parsedDef2)
19414 || !finite(parsedCenter) || !finite(parsedDefPoint))
19415 return fail();
19416 setPt6(parsedArcPoint); //code 16
19417 DRW_DBG("arc Point: ")DRW_dbg::dbg("arc Point: "); DRW_DBGPT(parsedArcPoint.x, parsedArcPoint.y,DRW_dbg::dbgPT(parsedArcPoint.x, parsedArcPoint.y, parsedArcPoint
.z)
19418 parsedArcPoint.z)DRW_dbg::dbgPT(parsedArcPoint.x, parsedArcPoint.y, parsedArcPoint
.z)
;
19419 setPt3(parsedDef1); //def1 code 13
19420 DRW_DBG("\ndef1: ")DRW_dbg::dbg("\ndef1: "); DRW_DBGPT(parsedDef1.x, parsedDef1.y, parsedDef1.z)DRW_dbg::dbgPT(parsedDef1.x, parsedDef1.y, parsedDef1.z);
19421 setPt4(parsedDef2); //def2 code 14
19422 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(parsedDef2.x, parsedDef2.y, parsedDef2.z)DRW_dbg::dbgPT(parsedDef2.x, parsedDef2.y, parsedDef2.z);
19423 setPt5(parsedCenter); //center pt code 15
19424 DRW_DBG("\ncenter point: ")DRW_dbg::dbg("\ncenter point: "); DRW_DBGPT(parsedCenter.x, parsedCenter.y,DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
19425 parsedCenter.z)DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
;
19426 setDefPoint(parsedDefPoint); //code 10
19427 DRW_DBG("\ndefPoint: ")DRW_dbg::dbg("\ndefPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19428 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19429 type |= 0x02;
19430 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
19431 if (!buf->isGood())
19432 return fail();
19433
19434 std::uint64_t handleEndBit = 0;
19435 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19436 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19437 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAngular::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAngular::parseDwg()\n"
)
;
19438 return fail();
19439 }
19440 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19441 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19442
19443 // RS crc; //RS */
19444 if (!buf->isGood())
19445 return fail();
19446 *sourceBuf = probe;
19447 return true;
19448}
19449
19450bool DRW_DimAngular3p::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19451 DRW_Dimension::resetDwgState();
19452 dwgBuffer *sourceBuf = buf;
19453 auto fail = [this, sourceBuf]() {
19454 if (sourceBuf != nullptr)
19455 sourceBuf->invalidate();
19456 DRW_Dimension::resetDwgState();
19457 return false;
19458 };
19459 if (sourceBuf == nullptr)
19460 return fail();
19461
19462 dwgBuffer probe = sourceBuf->forkIndependent();
19463 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19464 return fail();
19465 buf = &probe;
19466
19467 DRW_DBG("\n***************************** parsing dim angular3p *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim angular3p *********************************************\n"
)
;
19468 const std::uint64_t bodyEndBit = dwgDataEndBit;
19469 DRW_Coord parsedDefPoint;
19470 DRW_Coord parsedDef1;
19471 DRW_Coord parsedDef2;
19472 DRW_Coord parsedCenter;
19473 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19474 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
19475 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
19476 || !readBoundedBitCoord(*buf, bodyEndBit, parsedCenter))
19477 return fail();
19478 const auto finite = [](const DRW_Coord& point) {
19479 return std::isfinite(point.x) && std::isfinite(point.y)
19480 && std::isfinite(point.z);
19481 };
19482 if (!finite(parsedDefPoint) || !finite(parsedDef1) || !finite(parsedDef2)
19483 || !finite(parsedCenter))
19484 return fail();
19485 setDefPoint(parsedDefPoint); //code 10
19486 DRW_DBG("defPoint: ")DRW_dbg::dbg("defPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19487 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19488 setPt3(parsedDef1); //def1 code 13
19489 DRW_DBG("\ndef1: ")DRW_dbg::dbg("\ndef1: "); DRW_DBGPT(parsedDef1.x, parsedDef1.y, parsedDef1.z)DRW_dbg::dbgPT(parsedDef1.x, parsedDef1.y, parsedDef1.z);
19490 setPt4(parsedDef2); //def2 code 14
19491 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(parsedDef2.x, parsedDef2.y, parsedDef2.z)DRW_dbg::dbgPT(parsedDef2.x, parsedDef2.y, parsedDef2.z);
19492 setPt5(parsedCenter); //center pt code 15
19493 DRW_DBG("\ncenter point: ")DRW_dbg::dbg("\ncenter point: "); DRW_DBGPT(parsedCenter.x, parsedCenter.y,DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
19494 parsedCenter.z)DRW_dbg::dbgPT(parsedCenter.x, parsedCenter.y, parsedCenter.z
)
;
19495 type |= 0x05;
19496 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(type)DRW_dbg::dbg(type); DRW_DBG("\n")DRW_dbg::dbg("\n");
19497 if (!buf->isGood())
19498 return fail();
19499
19500 std::uint64_t handleEndBit = 0;
19501 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19502 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19503 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAngular3p::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAngular3p::parseDwg()\n"
)
;
19504 return fail();
19505 }
19506 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19507 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19508
19509 // RS crc; //RS */
19510 if (!buf->isGood())
19511 return fail();
19512 *sourceBuf = probe;
19513 return true;
19514}
19515
19516bool DRW_DimOrdinate::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
19517 DRW_Dimension::resetDwgState();
19518 dwgBuffer *sourceBuf = buf;
19519 auto fail = [this, sourceBuf]() {
19520 if (sourceBuf != nullptr)
19521 sourceBuf->invalidate();
19522 DRW_Dimension::resetDwgState();
19523 return false;
19524 };
19525 if (sourceBuf == nullptr)
19526 return fail();
19527
19528 dwgBuffer probe = sourceBuf->forkIndependent();
19529 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19530 return fail();
19531 buf = &probe;
19532
19533 DRW_DBG("\n***************************** parsing dim ordinate *********************************************\n")DRW_dbg::dbg("\n***************************** parsing dim ordinate *********************************************\n"
)
;
19534 const std::uint64_t bodyEndBit = dwgDataEndBit;
19535 DRW_Coord parsedDefPoint;
19536 DRW_Coord parsedDef1;
19537 DRW_Coord parsedDef2;
19538 std::uint8_t type2 = 0;
19539 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19540 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
19541 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
19542 || !readBoundedRawChar8(*buf, bodyEndBit, type2))
19543 return fail();
19544 const auto finite = [](const DRW_Coord& point) {
19545 return std::isfinite(point.x) && std::isfinite(point.y)
19546 && std::isfinite(point.z);
19547 };
19548 if (!finite(parsedDefPoint) || !finite(parsedDef1) || !finite(parsedDef2))
19549 return fail();
19550 setDefPoint(parsedDefPoint);
19551 DRW_DBG("defPoint: ")DRW_dbg::dbg("defPoint: "); DRW_DBGPT(parsedDefPoint.x, parsedDefPoint.y,DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
19552 parsedDefPoint.z)DRW_dbg::dbgPT(parsedDefPoint.x, parsedDefPoint.y, parsedDefPoint
.z)
;
19553 setPt3(parsedDef1); //def1
19554 DRW_DBG("\ndef1: ")DRW_dbg::dbg("\ndef1: "); DRW_DBGPT(parsedDef1.x, parsedDef1.y, parsedDef1.z)DRW_dbg::dbgPT(parsedDef1.x, parsedDef1.y, parsedDef1.z);
19555 setPt4(parsedDef2);
19556 DRW_DBG("\ndef2: ")DRW_dbg::dbg("\ndef2: "); DRW_DBGPT(parsedDef2.x, parsedDef2.y, parsedDef2.z)DRW_dbg::dbgPT(parsedDef2.x, parsedDef2.y, parsedDef2.z);
19557 DRW_DBG("type2 (70) read: ")DRW_dbg::dbg("type2 (70) read: "); DRW_DBG(type2)DRW_dbg::dbg(type2);
19558 // 0B.1: x-vs-y ordinate flag is DXF group-70 bit 6 (0x40), matching the
19559 // filter (rs_filterdxfrw.cpp `type & 64`) and the DWG parseCode path.
19560 // (Previously set bit 7/0x80, which the filter never checks.) The clear
19561 // mask 0xBF already clears 0x40. The DIMENSION base type byte (bit 7) is
19562 // a separate field — see :6141/:6409/:6660, NOT touched here.
19563 int parsedType = type;
19564 parsedType = (type2 & 1) ? parsedType | 0x40 : parsedType & 0xBF;
19565 DRW_DBG(" type (70) set: ")DRW_dbg::dbg(" type (70) set: "); DRW_DBG(parsedType)DRW_dbg::dbg(parsedType);
19566 parsedType |= 6;
19567 DRW_DBG("\n type (70) final: ")DRW_dbg::dbg("\n type (70) final: "); DRW_DBG(parsedType)DRW_dbg::dbg(parsedType); DRW_DBG("\n")DRW_dbg::dbg("\n");
19568
19569 std::uint64_t handleEndBit = 0;
19570 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19571 || !parseDwgDimensionHandles(version, buf, handleEndBit)) {
19572 DRW_DBG("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n")DRW_dbg::dbg("Failed: parseDwgEntHandle() in DRW_DimAligned::parseDwg()\n"
)
;
19573 return fail();
19574 }
19575 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19576 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
19577
19578 // RS crc; //RS */
19579 if (!buf->isGood())
19580 return fail();
19581 type = parsedType;
19582 *sourceBuf = probe;
19583 return true;
19584}
19585
19586// ----------------------------------------------------------------------------
19587// DRW_Dimension shared base encoder (R2000 / AC1015)
19588// ----------------------------------------------------------------------------
19589bool DRW_Dimension::encodeDwgDimBase(DRW::Version version, dwgBufferW *buf,
19590 dwgBufferW *strBuf) const {
19591 // ODA §20.4.22: version RC present for R2010+ (mirrors parseDwg read at version > AC1021)
19592 if (version > DRW::AC1021)
19593 buf->putRawChar8(0);
19594 // R2007+: the dim text below is routed to strBuf (the separate string
19595 // stream) via the (strBuf ? strBuf : buf) selector in putVariableText.
19596 buf->put3BitDouble(extPoint); // 3BD per ODA §20.4.22 (NOT BE, NO padding bits)
19597 buf->putRawDouble(textPoint.x);
19598 buf->putRawDouble(textPoint.y);
19599 buf->putBitDouble(textPoint.z);
19600 // Reverse the parseDwg type-byte transformation:
19601 // parseDwg bit0=0 → type bit7 set; bit0=1 → type bit7 clear
19602 // parseDwg bit1=1 → type bit5 set; bit1=0 → type bit5 clear
19603 std::uint8_t rawByte = static_cast<std::uint8_t>(
19604 ((type & 0x80) ? 0 : 1) | ((type & 0x20) ? 2 : 0));
19605 buf->putRawChar8(rawByte);
19606 (strBuf ? strBuf : buf)->putVariableText(version, text);
19607 buf->putBitDouble(rot);
19608 buf->putBitDouble(hdir);
19609 // ins_scale (3BD) of the dimension's anonymous block — not stored by the
19610 // reader, but ODA/libreDWG default it to (1,1,1) (dwg.spec FIELD_3BD_1), not
19611 // (0,0,0). A zero scale is degenerate for ODA consumers. (write-review #46)
19612 const DRW_Coord insScale{1.0, 1.0, 1.0};
19613 buf->put3BitDouble(insScale);
19614 buf->putBitDouble(0.0); // ins_rotation (code 54) — default 0, not stored
19615 // R2000 (version > AC1014): alignment, spacing, line factor, measure
19616 buf->putBitShort(static_cast<std::uint16_t>(align));
19617 buf->putBitShort(static_cast<std::uint16_t>(linesty));
19618 buf->putBitDouble(linefactor);
19619 buf->putBitDouble(measureValue);
19620 if (version > DRW::AC1018) {
19621 buf->putBit(0); // unknown R2007+ bit
19622 buf->putBit(flipArrow1 ? 1 : 0);
19623 buf->putBit(flipArrow2 ? 1 : 0);
19624 }
19625 buf->putRawDouble(clonePoint.x);
19626 buf->putRawDouble(clonePoint.y);
19627 return true;
19628}
19629
19630// Helper: emit dimStyleH (defaults to STANDARD=0x15) and blockH.
19631static void putDimHandles(dwgBufferW *buf, const dwgHandle& dimStyleH, const dwgHandle& blockH,
19632 dwgBufferW *hBuf = nullptr) {
19633 dwgBufferW *hb = hBuf ? hBuf : buf;
19634 dwgHandle dsH;
19635 dsH.code = 5;
19636 dsH.ref = (dimStyleH.ref == 0) ? 0x15 : dimStyleH.ref;
19637 dsH.size = 0;
19638 if (dsH.ref != 0) { std::uint32_t t = dsH.ref; while (t != 0) { t >>= 8; ++dsH.size; } }
19639 hb->putHandle(dsH);
19640
19641 dwgHandle bhH;
19642 bhH.code = (blockH.ref == 0) ? 0 : 5;
19643 bhH.ref = blockH.ref;
19644 bhH.size = 0;
19645 if (bhH.ref != 0) { std::uint32_t t = bhH.ref; while (t != 0) { t >>= 8; ++bhH.size; } }
19646 hb->putHandle(bhH);
19647}
19648
19649// ----------------------------------------------------------------------------
19650// DRW_DimAligned::encodeDwg (oType=22)
19651// ----------------------------------------------------------------------------
19652bool DRW_DimAligned::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
19653 (void)bs;
19654 oType = 22;
19655 if (!encodeDwgCommon(version, buf)) return false;
19656 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
19657 buf->put3BitDouble(getPt3()); // def1
19658 buf->put3BitDouble(getPt4()); // def2
19659 buf->put3BitDouble(getDefPoint()); // defPoint
19660 buf->putBitDouble(getOb52() / ARAD57.29577951308232); // oblique: degrees → radians
19661 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
19662 putDimHandles(buf, dimStyleH, blockH, handleBuf);
19663 return true;
19664}
19665
19666// ----------------------------------------------------------------------------
19667// DRW_DimLinear::encodeDwg (oType=21)
19668// ----------------------------------------------------------------------------
19669bool DRW_DimLinear::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
19670 (void)bs;
19671 oType = 21;
19672 if (!encodeDwgCommon(version, buf)) return false;
19673 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
19674 buf->put3BitDouble(getPt3()); // def1
19675 buf->put3BitDouble(getPt4()); // def2
19676 buf->put3BitDouble(getDefPoint()); // defPoint
19677 buf->putBitDouble(getOb52() / ARAD57.29577951308232); // oblique: degrees → radians
19678 buf->putBitDouble(getAn50() / ARAD57.29577951308232); // rotation angle: degrees → radians
19679 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
19680 putDimHandles(buf, dimStyleH, blockH, handleBuf);
19681 return true;
19682}
19683
19684// ----------------------------------------------------------------------------
19685// DRW_DimRadial::encodeDwg (oType=25)
19686// ----------------------------------------------------------------------------
19687bool DRW_DimRadial::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
19688 (void)bs;
19689 oType = 25;
19690 if (!encodeDwgCommon(version, buf)) return false;
19691 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
19692 buf->put3BitDouble(getDefPoint()); // center point (code 10)
19693 buf->put3BitDouble(getPt5()); // diameter point (code 15)
19694 buf->putBitDouble(getRa40()); // leader length (code 40)
19695 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
19696 putDimHandles(buf, dimStyleH, blockH, handleBuf);
19697 return true;
19698}
19699
19700// ----------------------------------------------------------------------------
19701// DRW_DimLargeRadial::encodeDwg (oType=519, custom AcDbRadialDimensionLarge)
19702// ----------------------------------------------------------------------------
19703bool DRW_DimLargeRadial::encodeDwg(DRW::Version version, dwgBufferW *buf,
19704 std::uint32_t bs, dwgBufferW *strBuf,
19705 dwgBufferW *handleBuf) {
19706 (void)bs;
19707 oType = kDwgClassNum;
19708 if (!encodeDwgCommon(version, buf)) return false;
19709 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
19710 buf->put3BitDouble(getCenterPoint()); // definition point (code 10)
19711 buf->put3BitDouble(jogPoint); // jog vertex (code 15)
19712 buf->putBitDouble(jogAngle); // jog transverse angle (code 40)
19713 buf->put3BitDouble(getChordPoint()); // chord point (code 13)
19714 buf->put3BitDouble(overrideCenterPoint); // overridden center (code 14)
19715 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
19716 putDimHandles(buf, dimStyleH, blockH, handleBuf);
19717 return true;
19718}
19719
19720// ----------------------------------------------------------------------------
19721// DRW_DimDiametric::encodeDwg (oType=26)
19722// ----------------------------------------------------------------------------
19723bool DRW_DimDiametric::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
19724 (void)bs;
19725 oType = 26;
19726 if (!encodeDwgCommon(version, buf)) return false;
19727 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
19728 buf->put3BitDouble(getPt5()); // first diameter point (code 15) — matches parseDwg order
19729 buf->put3BitDouble(getDefPoint()); // opposite point (code 10)
19730 buf->putBitDouble(getRa40()); // leader length (code 40)
19731 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
19732 putDimHandles(buf, dimStyleH, blockH, handleBuf);
19733 return true;
19734}
19735
19736// ----------------------------------------------------------------------------
19737// DRW_DimAngular::encodeDwg (oType=24, 2-line angular)
19738// ----------------------------------------------------------------------------
19739bool DRW_DimAngular::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
19740 (void)bs;
19741 oType = 24;
19742 if (!encodeDwgCommon(version, buf)) return false;
19743 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
19744 // arcPoint is 2RD (not 3BD) in parseDwg — only x and y
19745 buf->putRawDouble(getPt6().x);
19746 buf->putRawDouble(getPt6().y);
19747 buf->put3BitDouble(getPt3()); // def1 (line 1 start)
19748 buf->put3BitDouble(getPt4()); // def2 (line 1 end)
19749 buf->put3BitDouble(getPt5()); // circlePoint (center)
19750 buf->put3BitDouble(getDefPoint()); // defPoint
19751 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
19752 putDimHandles(buf, dimStyleH, blockH, handleBuf);
19753 return true;
19754}
19755
19756// ----------------------------------------------------------------------------
19757// DRW_DimAngular3p::encodeDwg (oType=23, 3-point angular)
19758// ----------------------------------------------------------------------------
19759bool DRW_DimAngular3p::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
19760 (void)bs;
19761 oType = 23;
19762 if (!encodeDwgCommon(version, buf)) return false;
19763 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
19764 buf->put3BitDouble(getDefPoint()); // defPoint (code 10)
19765 buf->put3BitDouble(getPt3()); // def1 (code 13)
19766 buf->put3BitDouble(getPt4()); // def2 (code 14)
19767 buf->put3BitDouble(getPt5()); // circlePoint / vertex (code 15)
19768 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
19769 putDimHandles(buf, dimStyleH, blockH, handleBuf);
19770 return true;
19771}
19772
19773// ----------------------------------------------------------------------------
19774// DRW_DimArc::parseCode (DXF group-code parser)
19775// ----------------------------------------------------------------------------
19776bool DRW_DimArc::parseCode(int code, const std::unique_ptr<dxfReader>& reader) {
19777 if (code == 100) {
19778 std::string s = reader->getString();
19779 if (s == "AcDbArcDimension") {
19780 m_arcSubclassSeen = true;
19781 return true;
19782 }
19783 // Fall through for AcDbEntity / AcDbDimension so base classes see them
19784 return DRW_Dimension::parseCode(code, reader);
19785 }
19786 if (m_arcSubclassSeen) {
19787 switch (code) {
19788 case 40: arcStartAngle = reader->getDouble(); return true;
19789 case 41: arcEndAngle = reader->getDouble(); return true;
19790 case 70: arcSymbol = reader->getInt32(); return true;
19791 case 71: isPartial = reader->getInt32() != 0; return true;
19792 }
19793 }
19794 switch (code) {
19795 case 17: leaderPt2.x = reader->getDouble(); return true;
19796 case 27: leaderPt2.y = reader->getDouble(); return true;
19797 case 37: leaderPt2.z = reader->getDouble(); return true;
19798 }
19799 return DRW_Dimension::parseCode(code, reader);
19800}
19801
19802// ----------------------------------------------------------------------------
19803// DRW_DimArc::parseDwg (ODA DWG spec §20.4.19)
19804// ----------------------------------------------------------------------------
19805bool DRW_DimArc::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
19806 DRW_Dimension::resetDwgState();
19807 leaderPt2 = DRW_Coord{0.0, 0.0, 0.0};
19808 arcStartAngle = 0.0;
19809 arcEndAngle = 0.0;
19810 arcSymbol = 0;
19811 isPartial = false;
19812 hasLeader = false;
19813 m_arcSubclassSeen = false;
19814 dwgBuffer *sourceBuf = buf;
19815 auto fail = [this, sourceBuf]() {
19816 if (sourceBuf != nullptr)
19817 sourceBuf->invalidate();
19818 DRW_Dimension::resetDwgState();
19819 leaderPt2 = DRW_Coord{0.0, 0.0, 0.0};
19820 arcStartAngle = 0.0;
19821 arcEndAngle = 0.0;
19822 arcSymbol = 0;
19823 isPartial = false;
19824 hasLeader = false;
19825 m_arcSubclassSeen = false;
19826 return false;
19827 };
19828 if (sourceBuf == nullptr)
19829 return fail();
19830
19831 dwgBuffer probe = sourceBuf->forkIndependent();
19832 if (!DRW_Dimension::parseDwg(version, &probe, nullptr, bs))
19833 return fail();
19834 buf = &probe;
19835 const std::uint64_t bodyEndBit = dwgDataEndBit;
19836 DRW_Coord parsedDefPoint;
19837 DRW_Coord parsedDef1;
19838 DRW_Coord parsedDef2;
19839 DRW_Coord parsedCenter;
19840 DRW_Coord parsedLeaderPoint1;
19841 DRW_Coord parsedLeaderPoint2;
19842 bool parsedPartial = false;
19843 double parsedStartAngle = 0.0;
19844 double parsedEndAngle = 0.0;
19845 bool parsedHasLeader = false;
19846 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedDefPoint)
19847 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef1)
19848 || !readBoundedBitCoord(*buf, bodyEndBit, parsedDef2)
19849 || !readBoundedBitCoord(*buf, bodyEndBit, parsedCenter)
19850 || !readBoundedBit(*buf, bodyEndBit, parsedPartial)
19851 || !readBoundedBitDouble(*buf, bodyEndBit, parsedStartAngle)
19852 || !readBoundedBitDouble(*buf, bodyEndBit, parsedEndAngle)
19853 || !readBoundedBit(*buf, bodyEndBit, parsedHasLeader)
19854 // ODA §20.4.19: leader points are unconditional in the stream.
19855 || !readBoundedBitCoord(*buf, bodyEndBit, parsedLeaderPoint1)
19856 || !readBoundedBitCoord(*buf, bodyEndBit, parsedLeaderPoint2))
19857 return fail();
19858 const auto finite = [](const DRW_Coord& point) {
19859 return std::isfinite(point.x) && std::isfinite(point.y)
19860 && std::isfinite(point.z);
19861 };
19862 if (!finite(parsedDefPoint) || !finite(parsedDef1) || !finite(parsedDef2)
19863 || !finite(parsedCenter) || !finite(parsedLeaderPoint1)
19864 || !finite(parsedLeaderPoint2) || !std::isfinite(parsedStartAngle)
19865 || !std::isfinite(parsedEndAngle))
19866 return fail();
19867 setDefPoint(parsedDefPoint);
19868 setPt3(parsedDef1);
19869 setPt4(parsedDef2);
19870 setPt5(parsedCenter);
19871 setPt6(parsedLeaderPoint1);
19872 leaderPt2 = parsedLeaderPoint2;
19873 isPartial = parsedPartial;
19874 arcStartAngle = parsedStartAngle;
19875 arcEndAngle = parsedEndAngle;
19876 hasLeader = parsedHasLeader;
19877 std::uint64_t handleEndBit = 0;
19878 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs, handleEndBit)
19879 || !parseDwgDimensionHandles(version, buf, handleEndBit))
19880 return fail();
19881 *sourceBuf = probe;
19882 return true;
19883}
19884
19885// ----------------------------------------------------------------------------
19886// DRW_DimArc::encodeDwg (oType=500 — dynamic class, classNum from writeDwgClasses)
19887// ----------------------------------------------------------------------------
19888bool DRW_DimArc::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
19889 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
19890 (void)bs;
19891 oType = DRW_DimArc::kDwgClassNum; // assigned in writeDwgClasses; reader resolves via classesmap
19892 if (!encodeDwgCommon(version, buf)) return false;
19893 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
19894 buf->put3BitDouble(getDefPoint()); // arc dim-line arc point (code 10)
19895 buf->put3BitDouble(getPt3()); // extension line 1 (code 13)
19896 buf->put3BitDouble(getPt4()); // extension line 2 (code 14)
19897 buf->put3BitDouble(getPt5()); // arc center (code 15)
19898 buf->putBit(isPartial ? 1 : 0);
19899 buf->putBitDouble(arcStartAngle);
19900 buf->putBitDouble(arcEndAngle);
19901 buf->putBit(hasLeader ? 1 : 0);
19902 // ODA §20.4.19: leader points are UNCONDITIONAL — always written; default to ext-line pts
19903 DRW_Coord lp1 = hasLeader ? getPt6() : getPt3();
19904 DRW_Coord lp2 = hasLeader ? leaderPt2 : getPt4();
19905 buf->put3BitDouble(lp1); // leader point 1 (code 16)
19906 buf->put3BitDouble(lp2); // leader point 2 (code 17)
19907 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
19908 putDimHandles(buf, dimStyleH, blockH, handleBuf);
19909 return true;
19910}
19911
19912// ----------------------------------------------------------------------------
19913// DRW_DimOrdinate::encodeDwg (oType=20)
19914// ----------------------------------------------------------------------------
19915bool DRW_DimOrdinate::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs, dwgBufferW *strBuf, dwgBufferW *handleBuf) {
19916 (void)bs;
19917 oType = 20;
19918 if (!encodeDwgCommon(version, buf)) return false;
19919 if (!encodeDwgDimBase(version, buf, strBuf)) return false;
19920 buf->put3BitDouble(getDefPoint()); // origin/definition point (code 10)
19921 buf->put3BitDouble(getPt3()); // feature location point (code 13)
19922 buf->put3BitDouble(getPt4()); // leader end point (code 14)
19923 // type2 byte encodes the x-vs-y ordinate flag (bit 6 / 0x40 of type, per
19924 // 0B.1) — keeps the DWG byte round-trip self-consistent with the parse
19925 // side while making the filter's `type & 64` check fire.
19926 std::uint8_t type2byte = (type & 0x40) ? 1 : 0;
19927 buf->putRawChar8(type2byte);
19928 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
19929 putDimHandles(buf, dimStyleH, blockH, handleBuf);
19930 return true;
19931}
19932
19933bool DRW_Leader::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
19934 switch (code) {
19935 case 3:
19936 style = reader->getUtf8String();
19937 break;
19938 case 71:
19939 arrow = reader->getInt32();
19940 break;
19941 case 72:
19942 leadertype = reader->getInt32();
19943 break;
19944 case 73:
19945 flag = reader->getInt32();
19946 break;
19947 case 74:
19948 hookline = reader->getInt32();
19949 break;
19950 case 75:
19951 hookflag = reader->getInt32();
19952 break;
19953 case 76:
19954 vertnum = reader->getInt32();
19955 hasVertexCount = true;
19956 break;
19957 case 77:
19958 coloruse = reader->getInt32();
19959 break;
19960 case 40:
19961 textheight = reader->getDouble();
19962 break;
19963 case 41:
19964 textwidth = reader->getDouble();
19965 break;
19966 case 10:
19967 vertexpoint= std::make_shared<DRW_Coord>();
19968 vertexlist.push_back(vertexpoint);
19969 vertexpoint->x = reader->getDouble();
19970 break;
19971 case 20:
19972 if(vertexpoint)
19973 vertexpoint->y = reader->getDouble();
19974 break;
19975 case 30:
19976 if(vertexpoint)
19977 vertexpoint->z = reader->getDouble();
19978 break;
19979 case 340:
19980 annotHandle = reader->getHandleString();
19981 break;
19982 case 210:
19983 extrusionPoint.x = reader->getDouble();
19984 break;
19985 case 220:
19986 extrusionPoint.y = reader->getDouble();
19987 break;
19988 case 230:
19989 extrusionPoint.z = reader->getDouble();
19990 break;
19991 case 211:
19992 horizdir.x = reader->getDouble();
19993 break;
19994 case 221:
19995 horizdir.y = reader->getDouble();
19996 break;
19997 case 231:
19998 horizdir.z = reader->getDouble();
19999 break;
20000 case 212:
20001 offsetblock.x = reader->getDouble();
20002 break;
20003 case 222:
20004 offsetblock.y = reader->getDouble();
20005 break;
20006 case 232:
20007 offsetblock.z = reader->getDouble();
20008 break;
20009 case 213:
20010 offsettext.x = reader->getDouble();
20011 break;
20012 case 223:
20013 offsettext.y = reader->getDouble();
20014 break;
20015 case 233:
20016 offsettext.z = reader->getDouble();
20017 break;
20018 default:
20019 return DRW_Entity::parseCode(code, reader);
20020 }
20021
20022 return true;
20023}
20024
20025bool DRW_Leader::validatePayloadFields() const {
20026 const auto finite = [](const DRW_Coord& point) {
20027 return std::isfinite(point.x) && std::isfinite(point.y)
20028 && std::isfinite(point.z);
20029 };
20030 if ((arrow != 0 && arrow != 1) || (hookline != 0 && hookline != 1)
20031 || (hookflag != 0 && hookflag != 1) || leadertype < 0
20032 || leadertype > 1 || flag < 0 || flag > 3
20033 || coloruse < 0
20034 || coloruse > static_cast<int>(std::numeric_limits<std::uint16_t>::max())
20035 || vertexlist.size() > static_cast<std::size_t>(kMaxLeaderVertices)
20036 || !std::isfinite(textheight) || !std::isfinite(textwidth)
20037 || !finite(origin) || !finite(extrusionPoint) || !finite(horizdir)
20038 || !finite(offsetblock) || !finite(offsettext)) {
20039 return false;
20040 }
20041 return std::all_of(vertexlist.begin(), vertexlist.end(),
20042 [&finite](const std::shared_ptr<DRW_Coord>& point) {
20043 return point != nullptr && finite(*point);
20044 });
20045}
20046
20047bool DRW_Leader::validateDxf() const {
20048 if (!validatePayloadFields())
20049 return false;
20050 if (!hasVertexCount)
20051 return true;
20052 return vertnum >= 0
20053 && vertnum <= kMaxLeaderVertices
20054 && static_cast<std::size_t>(vertnum) == vertexlist.size();
20055}
20056
20057void DRW_Leader::resetDwgState() {
20058 DRW_Entity::reset();
20059 style.clear();
20060 arrow = 1;
20061 leadertype = 0;
20062 flag = 3;
20063 hookline = 1;
20064 hookflag = 1;
20065 textheight = 1.0;
20066 textwidth = 1.0;
20067 vertnum = 0;
20068 coloruse = 7;
20069 annotHandle = 0;
20070 origin = DRW_Coord{0.0, 0.0, 0.0};
20071 extrusionPoint = DRW_Coord{0.0, 0.0, 1.0};
20072 horizdir = DRW_Coord{1.0, 0.0, 0.0};
20073 offsetblock = DRW_Coord{0.0, 0.0, 0.0};
20074 offsettext = DRW_Coord{0.0, 0.0, 0.0};
20075 vertexlist.clear();
20076 vertexpoint.reset();
20077 hasVertexCount = false;
20078 dimStyleH = dwgHandle{};
20079 AnnotH = dwgHandle{};
20080}
20081
20082bool DRW_Leader::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
20083 resetDwgState();
20084 dwgBuffer *sourceBuf = buf;
20085 auto fail = [this, sourceBuf]() {
20086 if (sourceBuf != nullptr)
20087 sourceBuf->invalidate();
20088 resetDwgState();
20089 return false;
20090 };
20091 if (sourceBuf == nullptr)
20092 return fail();
20093
20094 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
20095 buf = &bodyProbe;
20096
20097 dwgBuffer sBuff = buf->forkIndependent();
20098 dwgBuffer *sBuf = buf;
20099 if (version > DRW::AC1018) {//2007+
20100 sBuf = &sBuff; //separate buffer for strings
20101 }
20102 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
20103 if (!ret || !buf->isGood() || !sBuf->isGood())
20104 return fail();
20105 DRW_DBG("\n***************************** parsing leader *********************************************\n")DRW_dbg::dbg("\n***************************** parsing leader *********************************************\n"
)
;
20106 const std::uint64_t bodyEndBit = dwgDataEndBit;
20107 bool ignoredBit = false;
20108 std::uint16_t ignoredShort = 0;
20109 std::uint16_t parsedAnnotationType = 0;
20110 std::uint16_t parsedLeaderType = 0;
20111 if (!readBoundedBit(*buf, bodyEndBit, ignoredBit)
20112 || !readBoundedBitShort(*buf, bodyEndBit, parsedAnnotationType)
20113 || !readBoundedBitShort(*buf, bodyEndBit, parsedLeaderType))
20114 return fail();
20115 if (parsedAnnotationType > 3 || parsedLeaderType > 1)
20116 return fail();
20117 DRW_DBG(" Path type ")DRW_dbg::dbg(" Path type "); DRW_DBG(parsedLeaderType)DRW_dbg::dbg(parsedLeaderType);
20118 std::uint32_t parsedPointCount = 0;
20119 if (!readTableBodyCount(version, buf, objSize, kMaxLeaderVertices, 6,
20120 parsedPointCount))
20121 return fail();
20122 const std::int32_t nPt = static_cast<std::int32_t>(parsedPointCount);
20123 DRW_DBG(" Num pts ")DRW_dbg::dbg(" Num pts "); DRW_DBG(nPt)DRW_dbg::dbg(nPt);
20124 std::uint64_t pointBits = 0;
20125 if (!dwgSafety::multiply(parsedPointCount, 6, pointBits)
20126 || !proxyEntityHasBits(*buf, bodyEndBit, pointBits))
20127 return fail();
20128
20129 // add vertexes
20130 std::vector<std::shared_ptr<DRW_Coord>> parsedVertices;
20131 if (!DRW::reserve(parsedVertices, nPt))
20132 return fail();
20133 for (std::uint32_t i = 0; i < parsedPointCount; i++){
20134 DRW_Coord vertex;
20135 if (!readBoundedBitCoord(*buf, bodyEndBit, vertex))
20136 return fail();
20137 parsedVertices.push_back(std::make_shared<DRW_Coord>(vertex));
20138 DRW_DBG("\nvertex ")DRW_dbg::dbg("\nvertex "); DRW_DBGPT(vertex.x, vertex.y, vertex.z)DRW_dbg::dbgPT(vertex.x, vertex.y, vertex.z);
20139 }
20140 DRW_Coord parsedOrigin;
20141 DRW_Coord parsedExtrusion;
20142 DRW_Coord parsedHorizdir;
20143 DRW_Coord parsedOffsetblock;
20144 DRW_Coord parsedOffsettext{0.0, 0.0, 0.0};
20145 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedOrigin)
20146 || !readBoundedBitCoord(*buf, bodyEndBit, parsedExtrusion)
20147 || !readBoundedBitCoord(*buf, bodyEndBit, parsedHorizdir)
20148 || !readBoundedBitCoord(*buf, bodyEndBit, parsedOffsetblock))
20149 return fail();
20150 DRW_DBG("\norigin ")DRW_dbg::dbg("\norigin "); DRW_DBGPT(parsedOrigin.x, parsedOrigin.y, parsedOrigin.z)DRW_dbg::dbgPT(parsedOrigin.x, parsedOrigin.y, parsedOrigin.z
)
;
20151 // ODA §20.4.47: Extrusion is plain 3DPOINT (3BD), not BE — confirmed by libreDWG dwg.spec:3439
20152 if (leaderHasEndpointProjection(version)) {
20153 if (!readBoundedBitCoord(*buf, bodyEndBit, parsedOffsettext))
20154 return fail();
20155 DRW_DBG("\nEndptproj ")DRW_dbg::dbg("\nEndptproj "); DRW_DBGPT(parsedOffsettext.x, parsedOffsettext.y, parsedOffsettext.z)DRW_dbg::dbgPT(parsedOffsettext.x, parsedOffsettext.y, parsedOffsettext
.z)
;
20156 }
20157 if (version < DRW::AC1015) { //R14 -
20158 double ignoredDouble = 0.0;
20159 if (!readBoundedBitDouble(*buf, bodyEndBit, ignoredDouble))
20160 return fail();
20161 }
20162 double parsedTextheight = 0.0;
20163 double parsedTextwidth = 0.0;
20164 bool parsedHookline = false;
20165 bool parsedArrow = false;
20166 std::uint16_t parsedColoruse = 7;
20167 if ((leaderHasTextBox(version)
20168 && (!readBoundedBitDouble(*buf, bodyEndBit, parsedTextheight)
20169 || !readBoundedBitDouble(*buf, bodyEndBit, parsedTextwidth)))
20170 || !readBoundedBit(*buf, bodyEndBit, parsedHookline)
20171 || !readBoundedBit(*buf, bodyEndBit, parsedArrow))
20172 return fail();
20173 DRW_DBG("\ntextheight ")DRW_dbg::dbg("\ntextheight "); DRW_DBG(parsedTextheight)DRW_dbg::dbg(parsedTextheight); DRW_DBG(" textwidth ")DRW_dbg::dbg(" textwidth "); DRW_DBG(parsedTextwidth)DRW_dbg::dbg(parsedTextwidth);
20174 DRW_DBG(" hookline ")DRW_dbg::dbg(" hookline "); DRW_DBG(parsedHookline)DRW_dbg::dbg(parsedHookline); DRW_DBG(" arrow flag ")DRW_dbg::dbg(" arrow flag "); DRW_DBG(parsedArrow)DRW_dbg::dbg(parsedArrow);
20175
20176 // ODA §20.4.47 places arrowhead type immediately after the hook and
20177 // arrow flags in every DWG version. R2000+ keeps the field even though
20178 // the remaining legacy arrowhead data is absent.
20179 if (!readBoundedBitShort(*buf, bodyEndBit, ignoredShort))
20180 return fail();
20181 if (version < DRW::AC1015) { //R14 -
20182 double ignoredDimSize = 0.0;
20183 if (!readBoundedBitDouble(*buf, bodyEndBit, ignoredDimSize)
20184 || !readBoundedBit(*buf, bodyEndBit, ignoredBit)
20185 || !readBoundedBit(*buf, bodyEndBit, ignoredBit)
20186 || !readBoundedBitShort(*buf, bodyEndBit, ignoredShort)
20187 || !readBoundedBitShort(*buf, bodyEndBit, parsedColoruse)
20188 || !readBoundedBit(*buf, bodyEndBit, ignoredBit)
20189 || !readBoundedBit(*buf, bodyEndBit, ignoredBit))
20190 return fail();
20191 } else { //R2000+
20192 if (!readBoundedBit(*buf, bodyEndBit, ignoredBit)
20193 || !readBoundedBit(*buf, bodyEndBit, ignoredBit))
20194 return fail();
20195 }
20196 DRW_DBG("\n")DRW_dbg::dbg("\n");
20197 std::uint64_t handleEndBit = 0;
20198 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
20199 handleEndBit))
20200 return fail();
20201
20202 dwgBuffer handleProbe = buf->forkIndependent();
20203 ret = DRW_Entity::parseDwgEntHandle(version, &handleProbe, true,
20204 handleEndBit);
20205 if (!ret || !handleProbe.isGood())
20206 return fail();
20207 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
20208 dwgHandle parsedAnnotH;
20209 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
20210 parsedAnnotH))
20211 return fail();
20212 dwgHandle parsedDimStyleH;
20213 if (!readBoundedDwgHandle(handleProbe, handleEndBit, handle, false,
20214 parsedDimStyleH))
20215 return fail(); /* H 7 STYLE (hard pointer) */
20216 DRW_DBG("annot block Handle: ")DRW_dbg::dbg("annot block Handle: "); DRW_DBGHL(parsedAnnotH.code, parsedAnnotH.size, parsedAnnotH.ref)DRW_dbg::dbgHL(parsedAnnotH.code, parsedAnnotH.size, parsedAnnotH
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
20217 DRW_DBG("dim style Handle: ")DRW_dbg::dbg("dim style Handle: "); DRW_DBGHL(parsedDimStyleH.code, parsedDimStyleH.size, parsedDimStyleH.ref)DRW_dbg::dbgHL(parsedDimStyleH.code, parsedDimStyleH.size, parsedDimStyleH
.ref)
; DRW_DBG("\n")DRW_dbg::dbg("\n");
20218 DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); DRW_DBG(handleProbe.numRemainingBytes())DRW_dbg::dbg(handleProbe.numRemainingBytes()); DRW_DBG("\n")DRW_dbg::dbg("\n");
20219// RS crc; //RS */
20220 const auto finite = [](const DRW_Coord& point) {
20221 return std::isfinite(point.x) && std::isfinite(point.y)
20222 && std::isfinite(point.z);
20223 };
20224 if (!handleProbe.isGood() || !sBuf->isGood()
20225 || !finite(parsedOrigin) || !finite(parsedExtrusion)
20226 || !finite(parsedHorizdir) || !finite(parsedOffsetblock)
20227 || !finite(parsedOffsettext) || !std::isfinite(parsedTextheight)
20228 || !std::isfinite(parsedTextwidth)
20229 || !std::all_of(parsedVertices.begin(), parsedVertices.end(),
20230 [](const std::shared_ptr<DRW_Coord>& point) {
20231 return point != nullptr
20232 && std::isfinite(point->x)
20233 && std::isfinite(point->y)
20234 && std::isfinite(point->z);
20235 }))
20236 return fail();
20237 *sourceBuf = handleProbe;
20238 flag = static_cast<int>(parsedAnnotationType);
20239 leadertype = parsedLeaderType;
20240 vertnum = nPt;
20241 origin = parsedOrigin;
20242 extrusionPoint = parsedExtrusion;
20243 horizdir = parsedHorizdir;
20244 offsetblock = parsedOffsetblock;
20245 offsettext = parsedOffsettext;
20246 textheight = parsedTextheight;
20247 textwidth = parsedTextwidth;
20248 hookline = parsedHookline;
20249 arrow = parsedArrow;
20250 coloruse = static_cast<int>(parsedColoruse);
20251 AnnotH = parsedAnnotH;
20252 annotHandle = AnnotH.ref;
20253 dimStyleH = parsedDimStyleH;
20254 vertexlist = std::move(parsedVertices);
20255 return true;
20256}
20257
20258// DXF CONTEXT_DATA{} nested-block state machine (§20.4.86). The nested blocks
20259// open with 300 "CONTEXT_DATA{" / 302 "LEADER{" / 304 "LEADER_LINE{" and close
20260// with the distinct codes 301 / 303 / 305, so the open block is tracked with a
20261// single state int (no stack needed). The numeric group codes are overloaded
20262// by block — e.g. 40 is the overall scale in CONTEXT, the landing distance in
20263// LEADER and the arrow size in LEADER_LINE; 10/20/30 are the content base point,
20264// the connection point and a polyline vertex respectively — so they are routed
20265// per state into `context`. Returns true when the code belongs to the context
20266// block (consumed); false at entity level so parseCode handles it.
20267bool DRW_MLeader::parseDxfContextCode(int code, const std::unique_ptr<dxfReader>& reader){
20268 try {
20269 switch (code) { // block open/close markers
20270 case 300:
20271 if (m_dxfCtxState != 0 || reader->getString() != "CONTEXT_DATA{")
20272 return false;
20273 m_dxfCtxState = 1;
20274 return true;
20275 case 301:
20276 if (m_dxfCtxState != 1 || reader->getString() != "}")
20277 return false;
20278 m_dxfCtxState = 0;
20279 return true;
20280 case 302:
20281 if (m_dxfCtxState != 1 || reader->getString() != "LEADER{"
20282 || context.roots.size() >= static_cast<std::size_t>(kMaxMLeaderItems))
20283 return false;
20284 context.roots.emplace_back();
20285 m_dxfCtxState = 2;
20286 return true;
20287 case 303:
20288 if (m_dxfCtxState != 2 || reader->getString() != "}")
20289 return false;
20290 m_dxfCtxState = 1;
20291 return true;
20292 case 304:
20293 if (m_dxfCtxState == 1) {
20294 context.textLabel = reader->getUtf8String();
20295 return true;
20296 }
20297 if (m_dxfCtxState != 2 || reader->getString() != "LEADER_LINE{"
20298 || context.roots.empty()
20299 || context.roots.back().leaderLines.size()
20300 >= static_cast<std::size_t>(kMaxMLeaderItems))
20301 return false;
20302 context.roots.back().leaderLines.emplace_back();
20303 m_dxfCtxState = 3;
20304 return true;
20305 case 305:
20306 if (m_dxfCtxState != 3 || reader->getString() != "}")
20307 return false;
20308 m_dxfCtxState = 2;
20309 return true;
20310 default: break;
20311 }
20312
20313 if (m_dxfCtxState == 0)
20314 return false; // entity level — parseCode handles it
20315
20316 if (m_dxfCtxState == 3) { // LEADER_LINE{}: a polyline + overrides
20317 DRW_MLeaderRoot* root = context.roots.empty() ? nullptr : &context.roots.back();
20318 DRW_MLeaderLeaderLine* line =
20319 (root && !root->leaderLines.empty()) ? &root->leaderLines.back() : nullptr;
20320 if (line) switch (code) {
20321 case 10:
20322 if (line->points.size() >= static_cast<std::size_t>(kMaxLeaderVertices))
20323 return false;
20324 line->points.emplace_back(reader->getDouble(), 0.0, 0.0);
20325 return true;
20326 case 20: if (!line->points.empty()) line->points.back().y = reader->getDouble(); return true;
20327 case 30: if (!line->points.empty()) line->points.back().z = reader->getDouble(); return true;
20328 case 40: line->arrowSize = reader->getDouble(); return true;
20329 case 90: line->segmentIndex = reader->getInt32(); return true;
20330 case 91: line->leaderLineIndex = reader->getInt32(); return true;
20331 case 92: line->color = reader->getInt32(); return true;
20332 case 93: line->overrideFlags = reader->getInt32(); return true;
20333 case 170: line->leaderType = reader->getInt32(); return true;
20334 case 171: line->lineWeight = reader->getInt32(); return true;
20335 default: break;
20336 }
20337 return true; // swallow other line codes
20338 }
20339
20340 if (m_dxfCtxState == 2) { // LEADER{}: one root attachment
20341 DRW_MLeaderRoot* root = context.roots.empty() ? nullptr : &context.roots.back();
20342 if (root) switch (code) {
20343 case 290: root->isContentValid = (reader->getInt32() != 0); return true;
20344 case 291: root->unknown291 = (reader->getInt32() != 0); return true;
20345 case 10: root->connectionPoint.x = reader->getDouble(); return true;
20346 case 20: root->connectionPoint.y = reader->getDouble(); return true;
20347 case 30: root->connectionPoint.z = reader->getDouble(); return true;
20348 case 11: root->direction.x = reader->getDouble(); return true;
20349 case 21: root->direction.y = reader->getDouble(); return true;
20350 case 31: root->direction.z = reader->getDouble(); return true;
20351 case 90: root->leaderIndex = reader->getInt32(); return true;
20352 case 40: root->landingDistance = reader->getDouble(); return true;
20353 case 271: root->attachmentDirection = reader->getInt32(); return true;
20354 default: break;
20355 }
20356 return true; // swallow other leader codes
20357 }
20358
20359 switch (code) { // m_dxfCtxState == 1: CONTEXT_DATA{}
20360 case 40: context.overallScale = reader->getDouble(); return true;
20361 case 10: context.contentBasePoint.x = reader->getDouble(); return true;
20362 case 20: context.contentBasePoint.y = reader->getDouble(); return true;
20363 case 30: context.contentBasePoint.z = reader->getDouble(); return true;
20364 case 41: context.textHeight = reader->getDouble(); return true;
20365 case 140: context.arrowHeadSize = reader->getDouble(); return true;
20366 case 145: context.landingGap = reader->getDouble(); return true;
20367 case 174: context.styleLeftAttach = reader->getInt32(); return true;
20368 case 175: context.styleRightAttach = reader->getInt32(); return true;
20369 case 176: context.textAlignType = reader->getInt32(); return true;
20370 case 177: context.attachmentType = reader->getInt32(); return true;
20371 case 290: context.hasTextContents = (reader->getInt32() != 0); return true;
20372 /* text-content branch */
20373 case 11: context.textNormal.x = reader->getDouble(); return true;
20374 case 21: context.textNormal.y = reader->getDouble(); return true;
20375 case 31: context.textNormal.z = reader->getDouble(); return true;
20376 case 12: context.textLocation.x = reader->getDouble(); return true;
20377 case 22: context.textLocation.y = reader->getDouble(); return true;
20378 case 32: context.textLocation.z = reader->getDouble(); return true;
20379 case 13: context.textDirection.x = reader->getDouble(); return true;
20380 case 23: context.textDirection.y = reader->getDouble(); return true;
20381 case 33: context.textDirection.z = reader->getDouble(); return true;
20382 case 42: context.textRotation = reader->getDouble(); return true;
20383 case 43: context.boundaryWidth = reader->getDouble(); return true;
20384 case 44: context.boundaryHeight = reader->getDouble(); return true;
20385 case 45: context.lineSpacingFactor = reader->getDouble(); return true;
20386 case 170: context.lineSpacingStyle = reader->getInt32(); return true;
20387 case 90: context.textColor = reader->getInt32(); return true;
20388 case 171: context.alignment = reader->getInt32(); return true;
20389 case 172: context.flowDirection = reader->getInt32(); return true;
20390 case 91: context.bgFillColor = reader->getInt32(); return true;
20391 case 141: context.bgScaleFactor = reader->getDouble(); return true;
20392 case 92: context.bgTransparency = reader->getInt32(); return true;
20393 case 291: context.bgFillEnabled = (reader->getInt32() != 0); return true;
20394 case 292: context.bgMaskFillOn = (reader->getInt32() != 0); return true;
20395 case 173: context.columnType = reader->getInt32(); return true;
20396 case 293: context.textHeightAuto = (reader->getInt32() != 0); return true;
20397 case 142: context.columnWidth = reader->getDouble(); return true;
20398 case 143: context.columnGutter = reader->getDouble(); return true;
20399 case 294: context.columnFlowReversed = (reader->getInt32() != 0); return true;
20400 case 144:
20401 if (context.columnSizes.size() >= static_cast<std::size_t>(kMaxMLeaderItems))
20402 return false;
20403 context.columnSizes.push_back(reader->getDouble());
20404 return true;
20405 case 295: context.wordBreak = (reader->getInt32() != 0); return true;
20406 /* block-content branch */
20407 case 296: context.hasContentsBlock = (reader->getInt32() != 0); return true;
20408 case 14: context.blockNormal.x = reader->getDouble(); return true;
20409 case 24: context.blockNormal.y = reader->getDouble(); return true;
20410 case 34: context.blockNormal.z = reader->getDouble(); return true;
20411 case 15: context.blockLocation.x = reader->getDouble(); return true;
20412 case 25: context.blockLocation.y = reader->getDouble(); return true;
20413 case 35: context.blockLocation.z = reader->getDouble(); return true;
20414 case 16: context.blockScale.x = reader->getDouble(); return true;
20415 case 26: context.blockScale.y = reader->getDouble(); return true;
20416 case 36: context.blockScale.z = reader->getDouble(); return true;
20417 case 46: context.blockRotation = reader->getDouble(); return true;
20418 case 93: context.blockColor = reader->getInt32(); return true;
20419 /* common tail */
20420 case 110: case 120: case 130:
20421 case 111: case 121: case 131:
20422 case 112: case 122: case 132:
20423 // The helper answers true for every code in this set, so this
20424 // matches the nine `return true` arms it replaces.
20425 return readCoordTripletCode(code, reader, context.basePoint,
20426 context.baseDirection,
20427 context.baseVertical);
20428 case 297: context.isNormalReversed = (reader->getInt32() != 0); return true;
20429 case 272: context.styleBottomAttach = reader->getInt32(); return true;
20430 case 273: context.styleTopAttach = reader->getInt32(); return true;
20431 default: return true; // swallow any other context code
20432 }
20433 } catch (...) {
20434 return false;
20435 }
20436}
20437
20438bool DRW_MLeader::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
20439 // The embedded CONTEXT_DATA{} block (§20.4.86) is routed by the nested-block
20440 // state machine; the remaining (entity-level) fields are read below and
20441 // mirror the DWG body parser.
20442 if (parseDxfContextCode(code, reader))
20443 return true;
20444 // A failed nested parse must not fall through to the permissive base
20445 // parser: that would silently discard malformed controls or over-limit
20446 // collections and still publish the entity.
20447 if (m_dxfCtxState != 0 || (code >= 300 && code <= 305))
20448 return false;
20449 switch (code) {
20450 case 170: leaderType = reader->getInt32(); break;
20451 case 171: leaderLineWeight = reader->getInt32(); break;
20452 case 172: styleContentType = reader->getInt32(); break;
20453 case 173: styleLeftAttach = reader->getInt32(); break;
20454 case 95: styleRightAttach = reader->getInt32(); break;
20455 case 174: styleTextAngleType = reader->getInt32(); break;
20456 case 175: unknown175 = reader->getInt32(); break;
20457 case 176: styleAttachmentType = reader->getInt32(); break;
20458 case 178: ipeAlign = reader->getInt32(); break;
20459 case 179: justification = reader->getInt32(); break;
20460 case 271: attachmentDirection = reader->getInt32(); break;
20461 case 272: styleBottomAttach = reader->getInt32(); break;
20462 case 273: styleTopAttach = reader->getInt32(); break;
20463 case 90: overrideFlags = reader->getInt32(); break;
20464 case 91: leaderColor = reader->getInt32(); break;
20465 case 92: styleTextColor = reader->getInt32(); break;
20466 case 93: styleBlockColor = reader->getInt32(); break;
20467 case 41: landingDistance = reader->getDouble(); break;
20468 case 42: defaultArrowHeadSize = reader->getDouble(); break;
20469 case 43: styleBlockRotation = reader->getDouble(); break;
20470 case 45: scaleFactor = reader->getDouble(); break;
20471 case 290: landingEnabled = (reader->getInt32() != 0); break;
20472 case 291: doglegEnabled = (reader->getInt32() != 0); break;
20473 case 292: styleTextFrameEnabled = (reader->getInt32() != 0); break;
20474 case 293: isAnnotative = (reader->getInt32() != 0); break;
20475 case 294: isTextDirectionNegative = (reader->getInt32() != 0); break;
20476 case 295: leaderExtendedToText = (reader->getInt32() != 0); break;
20477 default:
20478 return DRW_Entity::parseCode(code, reader);
20479 }
20480 return true;
20481}
20482
20483// Helper: parse one AcDbMLeaderObjectContextData::LeaderRoot entry (§20.4.86).
20484//
20485// Each root has: connection point + direction, optional break pairs, leader
20486// index, landing distance, then a count-and-list of leader lines. Lines
20487// themselves carry: point list, break-info pairs, and (R2010+) per-line
20488// style overrides. The handles inside (line-type / arrow per leader line)
20489// are deferred to the entity-level handle stream and not stored here.
20490static bool parseMLeaderRoot(DRW::Version version, dwgBuffer *buf,
20491 std::uint32_t objectSize,
20492 std::uint64_t bodyEndBit,
20493 DRW_MLeaderRoot& root) {
20494 // Layout per libreDWG dwg2.spec:1316-1366 (Dwg_LEADER_Node + Dwg_LEADER_Line).
20495 // The two 3BD coords at the head of the node are conditional on the
20496 // preceding B flags; reading them unconditionally drifts the bit stream
20497 // when either flag is 0.
20498 bool hasLastPt = false;
20499 bool hasDogleg = false;
20500 if (!readBoundedBit(*buf, bodyEndBit, hasLastPt)
20501 || !readBoundedBit(*buf, bodyEndBit, hasDogleg))
20502 return false; // 290 has_lastleaderlinepoint, 291 has_dogleg
20503 root.isContentValid = hasLastPt;
20504 root.unknown291 = hasDogleg;
20505 if (hasLastPt) {
20506 if (!readBoundedBitCoord(*buf, bodyEndBit, root.connectionPoint))
20507 return false;
20508 }
20509 if (hasDogleg) {
20510 if (!readBoundedBitCoord(*buf, bodyEndBit, root.direction))
20511 return false;
20512 }
20513
20514 std::uint32_t nBreaks = 0;
20515 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems, 12,
20516 nBreaks))
20517 return false;
20518 if (!DRW::reserve(root.breaks, static_cast<int>(nBreaks)))
20519 return false;
20520 for (std::uint32_t i = 0; i < nBreaks; ++i) {
20521 DRW_Coord a;
20522 DRW_Coord b;
20523 if (!readBoundedBitCoord(*buf, bodyEndBit, a)
20524 || !readBoundedBitCoord(*buf, bodyEndBit, b))
20525 return false;
20526 root.breaks.emplace_back(a, b);
20527 }
20528
20529 if (!readBoundedBitLong(*buf, bodyEndBit, root.leaderIndex)
20530 || !readBoundedBitDouble(*buf, bodyEndBit, root.landingDistance))
20531 return false;
20532
20533 std::uint32_t nLines = 0;
20534 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems, 6,
20535 nLines))
20536 return false;
20537 if (!DRW::reserve(root.leaderLines, static_cast<int>(nLines)))
20538 return false;
20539 for (std::uint32_t i = 0; i < nLines; ++i) {
20540 DRW_MLeaderLeaderLine line;
20541 // Per libreDWG: BL num_points, points, BL num_breaks, breaks, BL line_index.
20542 // The previous 5-BL layout (brkInfoCount + segmentIndex + nPairs +
20543 // pairs + leaderLineIndex) inserted two spurious BL reads, drifting
20544 // every subsequent entity-level field (overallScale, contentType, …).
20545 std::uint32_t nPts = 0;
20546 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems, 6,
20547 nPts))
20548 return false;
20549 if (!DRW::reserve(line.points, static_cast<int>(nPts)))
20550 return false;
20551 for (std::uint32_t j = 0; j < nPts; ++j) {
20552 DRW_Coord point;
20553 if (!readBoundedBitCoord(*buf, bodyEndBit, point))
20554 return false;
20555 line.points.push_back(point);
20556 }
20557 std::uint32_t nLineBreaks = 0;
20558 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems,
20559 12, nLineBreaks))
20560 return false;
20561 for (std::uint32_t j = 0; j < nLineBreaks; ++j) {
20562 DRW_Coord a;
20563 DRW_Coord b;
20564 if (!readBoundedBitCoord(*buf, bodyEndBit, a)
20565 || !readBoundedBitCoord(*buf, bodyEndBit, b))
20566 return false;
20567 line.breaks.emplace_back(a, b);
20568 }
20569 if (!readBoundedBitLong(*buf, bodyEndBit, line.leaderLineIndex))
20570 return false;
20571
20572 // R2010+ per-line override block. The spec marks this block "R2010"
20573 // (§20.4.86 page 215); the override flags BL 93 says which fields
20574 // were overridden. The handle fields (340 line-type, 341 arrow) are
20575 // deferred to the trailing handle stream.
20576 if (version >= DRW::AC1024) {
20577 if (!readBoundedBitShort(*buf, bodyEndBit, line.leaderType))
20578 return false;
20579 std::uint32_t parsedColor = 0;
20580 if (!readBoundedCmColor(*buf, nullptr, bodyEndBit, version,
20581 parsedColor))
20582 return false;
20583 line.color = static_cast<int>(parsedColor);
20584 // line type handle 340 — read from handles section later
20585 if (!readBoundedBitLong(*buf, bodyEndBit, line.lineWeight)
20586 || !readBoundedBitDouble(*buf, bodyEndBit, line.arrowSize)
20587 || !readBoundedBitLong(*buf, bodyEndBit,
20588 line.overrideFlags))
20589 return false;
20590 // arrow handle 341 — handles section
20591 }
20592 root.leaderLines.push_back(std::move(line));
20593 }
20594
20595 if (version >= DRW::AC1024) {
20596 if (!readBoundedBitShort(*buf, bodyEndBit, root.attachmentDirection))
20597 return false;
20598 }
20599
20600 return buf->isGood();
20601}
20602
20603// Helper: parse the AcDbMLeaderObjectContextData (§20.4.86) payload, the
20604// large embedded block at the start of the MLEADER body that carries the
20605// leader geometry plus either text or block content.
20606static bool parseMLeaderAnnotContext(DRW::Version version, dwgBuffer *buf,
20607 dwgBuffer *sBuf,
20608 std::uint32_t objectSize,
20609 std::uint64_t bodyEndBit,
20610 std::uint64_t stringEndBit,
20611 DRW_MLeaderAnnotContext& ctx) {
20612 // NOTE: when AcDbMLeaderObjectContextData is embedded INSIDE the MLEADER
20613 // entity body (rather than serialized as a standalone object), the
20614 // AcDbObjectContextData base preamble (BS version, B has-file-ext-dict,
20615 // B default-flag) does NOT appear in the bit stream — those fields are
20616 // standalone-object metadata. The embedded AnnotContext starts directly
20617 // with the leader-roots count. Its standalone-object scale handle is
20618 // likewise absent from this embedded payload.
20619
20620 // Number of leader roots.
20621 std::int32_t nRoots = 0;
20622 if (!readBoundedBitLong(*buf, bodyEndBit, nRoots))
20623 return false;
20624 if (nRoots == 0) {
20625 if (!proxyEntityHasBits(*buf, bodyEndBit, 7))
20626 return false;
20627 bool rootCountBits[7] = {};
20628 for (bool& rootCountBit : rootCountBits) {
20629 if (!readBoundedBit(*buf, bodyEndBit, rootCountBit))
20630 return false;
20631 }
20632 nRoots = rootCountBits[5] ? 2 : 1;
20633 }
20634 if (!isValidCount(nRoots, kMaxMLeaderItems)) return false;
20635 std::uint64_t minimumRootBits = 0;
20636 if (!dwgSafety::multiply(static_cast<std::uint64_t>(nRoots), 10,
20637 minimumRootBits)
20638 || !proxyEntityHasBits(*buf, bodyEndBit, minimumRootBits)) {
20639 return false;
20640 }
20641 ctx.roots.clear();
20642 if (!DRW::reserve(ctx.roots, nRoots))
20643 return false;
20644 for (std::int32_t i = 0; i < nRoots; ++i) {
20645 DRW_MLeaderRoot root;
20646 if (!parseMLeaderRoot(version, buf, objectSize, bodyEndBit, root))
20647 return false;
20648 ctx.roots.push_back(std::move(root));
20649 }
20650
20651 // Common content fields.
20652 if (!readBoundedBitDouble(*buf, bodyEndBit, ctx.overallScale)
20653 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.contentBasePoint)
20654 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.textHeight)
20655 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.arrowHeadSize)
20656 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.landingGap)
20657 || !readBoundedBitShort(*buf, bodyEndBit, ctx.styleLeftAttach)
20658 || !readBoundedBitShort(*buf, bodyEndBit, ctx.styleRightAttach)
20659 || !readBoundedBitShort(*buf, bodyEndBit, ctx.textAlignType)
20660 || !readBoundedBitShort(*buf, bodyEndBit, ctx.attachmentType)
20661 || !readBoundedBit(*buf, bodyEndBit, ctx.hasTextContents))
20662 return false;
20663
20664 if (ctx.hasTextContents) {
20665 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
20666 ctx.textLabel))
20667 return false;
20668 if (!readBoundedBitCoord(*buf, bodyEndBit, ctx.textNormal))
20669 return false;
20670 // text style handle 340 — handles section
20671 if (!readBoundedBitCoord(*buf, bodyEndBit, ctx.textLocation)
20672 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.textDirection)
20673 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.textRotation)
20674 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.boundaryWidth)
20675 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.boundaryHeight)
20676 || !readBoundedBitDouble(*buf, bodyEndBit,
20677 ctx.lineSpacingFactor)
20678 || !readBoundedBitShort(*buf, bodyEndBit, ctx.lineSpacingStyle))
20679 return false;
20680 std::uint32_t parsedTextColor = 0;
20681 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
20682 parsedTextColor, nullptr, nullptr, nullptr,
20683 nullptr, stringEndBit))
20684 return false;
20685 ctx.textColor = static_cast<int>(parsedTextColor);
20686 if (!readBoundedBitShort(*buf, bodyEndBit, ctx.alignment)
20687 || !readBoundedBitShort(*buf, bodyEndBit, ctx.flowDirection))
20688 return false;
20689 std::uint32_t parsedBgFillColor = 0;
20690 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
20691 parsedBgFillColor, nullptr, nullptr, nullptr,
20692 nullptr, stringEndBit))
20693 return false;
20694 ctx.bgFillColor = static_cast<int>(parsedBgFillColor);
20695 if (!readBoundedBitDouble(*buf, bodyEndBit, ctx.bgScaleFactor)
20696 || !readBoundedBitLong(*buf, bodyEndBit, ctx.bgTransparency)
20697 || !readBoundedBit(*buf, bodyEndBit, ctx.bgFillEnabled)
20698 || !readBoundedBit(*buf, bodyEndBit, ctx.bgMaskFillOn)
20699 || !readBoundedBitShort(*buf, bodyEndBit, ctx.columnType)
20700 || !readBoundedBit(*buf, bodyEndBit, ctx.textHeightAuto)
20701 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.columnWidth)
20702 || !readBoundedBitDouble(*buf, bodyEndBit, ctx.columnGutter)
20703 || !readBoundedBit(*buf, bodyEndBit, ctx.columnFlowReversed))
20704 return false;
20705 std::uint32_t nColSizes = 0;
20706 if (!readTableBodyCount(version, buf, objectSize, kMaxMLeaderItems, 2,
20707 nColSizes))
20708 return false;
20709 if (!DRW::reserve(ctx.columnSizes, static_cast<int>(nColSizes)))
20710 return false;
20711 for (std::uint32_t i = 0; i < nColSizes; ++i) {
20712 double columnSize = 0.0;
20713 if (!readBoundedBitDouble(*buf, bodyEndBit, columnSize))
20714 return false;
20715 ctx.columnSizes.push_back(columnSize);
20716 }
20717 bool ignoredBit = false;
20718 if (!readBoundedBit(*buf, bodyEndBit, ctx.wordBreak)
20719 || !readBoundedBit(*buf, bodyEndBit, ignoredBit))
20720 return false; // unknown trailing bit
20721 } else {
20722 if (!readBoundedBit(*buf, bodyEndBit, ctx.hasContentsBlock))
20723 return false;
20724 if (ctx.hasContentsBlock) {
20725 // BlockTableRecord handle 341 — deferred
20726 if (!readBoundedBitCoord(*buf, bodyEndBit, ctx.blockNormal)
20727 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.blockLocation)
20728 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.blockScale)
20729 || !readBoundedBitDouble(*buf, bodyEndBit,
20730 ctx.blockRotation))
20731 return false;
20732 std::uint32_t parsedBlockColor = 0;
20733 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version,
20734 parsedBlockColor, nullptr, nullptr, nullptr,
20735 nullptr, stringEndBit))
20736 return false;
20737 ctx.blockColor = static_cast<int>(parsedBlockColor);
20738 for (size_t i = 0; i < 16; ++i) {
20739 if (!readBoundedBitDouble(*buf, bodyEndBit,
20740 ctx.blockTransform[i]))
20741 return false;
20742 }
20743 }
20744 }
20745
20746 // Common tail.
20747 if (!readBoundedBitCoord(*buf, bodyEndBit, ctx.basePoint)
20748 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.baseDirection)
20749 || !readBoundedBitCoord(*buf, bodyEndBit, ctx.baseVertical)
20750 || !readBoundedBit(*buf, bodyEndBit, ctx.isNormalReversed))
20751 return false;
20752
20753 if (version >= DRW::AC1024) {
20754 if (!readBoundedBitShort(*buf, bodyEndBit, ctx.styleTopAttach)
20755 || !readBoundedBitShort(*buf, bodyEndBit, ctx.styleBottomAttach))
20756 return false;
20757 }
20758
20759 return buf->isGood() && sBuf->isGood();
20760}
20761
20762static bool encodeMLeaderRoot(DRW::Version version, dwgBufferW *buf,
20763 const DRW_MLeaderRoot& root) {
20764 if (root.breaks.size() > DRW_MLeader::kMaxLeaderPoints
20765 || root.leaderLines.size() > DRW_MLeader::kMaxLeaderLines)
20766 return false;
20767
20768 buf->putBit(root.isContentValid ? 1 : 0);
20769 buf->putBit(root.unknown291 ? 1 : 0);
20770 if (root.isContentValid)
20771 buf->put3BitDouble(root.connectionPoint);
20772 if (root.unknown291)
20773 buf->put3BitDouble(root.direction);
20774
20775 buf->putBitLong(static_cast<std::int32_t>(root.breaks.size()));
20776 for (const auto& brk : root.breaks) {
20777 buf->put3BitDouble(brk.first);
20778 buf->put3BitDouble(brk.second);
20779 }
20780
20781 buf->putBitLong(root.leaderIndex);
20782 buf->putBitDouble(root.landingDistance);
20783
20784 buf->putBitLong(static_cast<std::int32_t>(root.leaderLines.size()));
20785 for (const DRW_MLeaderLeaderLine& line : root.leaderLines) {
20786 if (line.points.size() > DRW_MLeader::kMaxLeaderPoints
20787 || line.breaks.size() > DRW_MLeader::kMaxLeaderPoints)
20788 return false;
20789 buf->putBitLong(static_cast<std::int32_t>(line.points.size()));
20790 for (const DRW_Coord& point : line.points)
20791 buf->put3BitDouble(point);
20792
20793 buf->putBitLong(static_cast<std::int32_t>(line.breaks.size()));
20794 for (const auto& brk : line.breaks) {
20795 buf->put3BitDouble(brk.first);
20796 buf->put3BitDouble(brk.second);
20797 }
20798 buf->putBitLong(line.leaderLineIndex);
20799
20800 if (version >= DRW::AC1024) {
20801 buf->putBitShort(line.leaderType);
20802 buf->putCmColor(version, static_cast<std::uint16_t>(line.color));
20803 buf->putBitLong(line.lineWeight);
20804 buf->putBitDouble(line.arrowSize);
20805 buf->putBitLong(line.overrideFlags);
20806 }
20807 }
20808
20809 if (version >= DRW::AC1024)
20810 buf->putBitShort(root.attachmentDirection);
20811
20812 return true;
20813}
20814
20815static bool encodeMLeaderAnnotContext(DRW::Version version, dwgBufferW *buf,
20816 dwgBufferW *strBuf,
20817 const DRW_MLeaderAnnotContext& ctx) {
20818 if (ctx.roots.size() > DRW_MLeader::kMaxRoots
20819 || ctx.columnSizes.size() > DRW_MLeader::kMaxLeaderPoints)
20820 return false;
20821 if (ctx.hasContentsBlock)
20822 return false;
20823
20824 buf->putBitLong(static_cast<std::int32_t>(ctx.roots.size()));
20825 for (const DRW_MLeaderRoot& root : ctx.roots) {
20826 if (!encodeMLeaderRoot(version, buf, root))
20827 return false;
20828 }
20829
20830 buf->putBitDouble(ctx.overallScale);
20831 buf->put3BitDouble(ctx.contentBasePoint);
20832 buf->putBitDouble(ctx.textHeight);
20833 buf->putBitDouble(ctx.arrowHeadSize);
20834 buf->putBitDouble(ctx.landingGap);
20835 buf->putBitShort(ctx.styleLeftAttach);
20836 buf->putBitShort(ctx.styleRightAttach);
20837 buf->putBitShort(ctx.textAlignType);
20838 buf->putBitShort(ctx.attachmentType);
20839 buf->putBit(ctx.hasTextContents ? 1 : 0);
20840
20841 if (ctx.hasTextContents) {
20842 (strBuf ? strBuf : buf)->putVariableText(version, ctx.textLabel);
20843 buf->put3BitDouble(ctx.textNormal);
20844 buf->put3BitDouble(ctx.textLocation);
20845 buf->put3BitDouble(ctx.textDirection);
20846 buf->putBitDouble(ctx.textRotation);
20847 buf->putBitDouble(ctx.boundaryWidth);
20848 buf->putBitDouble(ctx.boundaryHeight);
20849 buf->putBitDouble(ctx.lineSpacingFactor);
20850 buf->putBitShort(ctx.lineSpacingStyle);
20851 buf->putCmColor(version, static_cast<std::uint16_t>(ctx.textColor));
20852 buf->putBitShort(ctx.alignment);
20853 buf->putBitShort(ctx.flowDirection);
20854 buf->putCmColor(version, static_cast<std::uint16_t>(ctx.bgFillColor));
20855 buf->putBitDouble(ctx.bgScaleFactor);
20856 buf->putBitLong(ctx.bgTransparency);
20857 buf->putBit(ctx.bgFillEnabled ? 1 : 0);
20858 buf->putBit(ctx.bgMaskFillOn ? 1 : 0);
20859 buf->putBitShort(ctx.columnType);
20860 buf->putBit(ctx.textHeightAuto ? 1 : 0);
20861 buf->putBitDouble(ctx.columnWidth);
20862 buf->putBitDouble(ctx.columnGutter);
20863 buf->putBit(ctx.columnFlowReversed ? 1 : 0);
20864 buf->putBitLong(static_cast<std::int32_t>(ctx.columnSizes.size()));
20865 for (double columnSize : ctx.columnSizes)
20866 buf->putBitDouble(columnSize);
20867 buf->putBit(ctx.wordBreak ? 1 : 0);
20868 buf->putBit(0);
20869 } else {
20870 buf->putBit(0); // hasContentsBlock
20871 }
20872
20873 buf->put3BitDouble(ctx.basePoint);
20874 buf->put3BitDouble(ctx.baseDirection);
20875 buf->put3BitDouble(ctx.baseVertical);
20876 buf->putBit(ctx.isNormalReversed ? 1 : 0);
20877
20878 if (version >= DRW::AC1024) {
20879 buf->putBitShort(ctx.styleTopAttach);
20880 buf->putBitShort(ctx.styleBottomAttach);
20881 }
20882
20883 return true;
20884}
20885
20886void DRW_MLeader::resetDwgState() {
20887 DRW_Entity::reset();
20888 context = DRW_MLeaderAnnotContext{};
20889 classVersion = 2;
20890 styleHandle = dwgHandle{};
20891 overrideFlags = 0;
20892 leaderType = 1;
20893 leaderColor = 0;
20894 leaderLineTypeHandle = dwgHandle{};
20895 leaderLineWeight = 0;
20896 landingEnabled = true;
20897 doglegEnabled = true;
20898 landingDistance = 0.0;
20899 arrowHeadHandle = dwgHandle{};
20900 defaultArrowHeadSize = 0.0;
20901 styleContentType = 2;
20902 styleTextStyleHandle = dwgHandle{};
20903 styleLeftAttach = 0;
20904 styleRightAttach = 0;
20905 styleTextAngleType = 0;
20906 unknown175 = 0;
20907 styleTextColor = 0;
20908 styleTextFrameEnabled = false;
20909 styleBlockHandle = dwgHandle{};
20910 styleBlockColor = 0;
20911 styleBlockScale = DRW_Coord{1.0, 1.0, 1.0};
20912 styleBlockRotation = 0.0;
20913 styleAttachmentType = 0;
20914 isAnnotative = false;
20915 arrowHeads.clear();
20916 blockLabels.clear();
20917 isTextDirectionNegative = false;
20918 ipeAlign = 0;
20919 justification = 0;
20920 scaleFactor = 1.0;
20921 attachmentDirection = 0;
20922 styleTopAttach = 0;
20923 styleBottomAttach = 0;
20924 leaderExtendedToText = false;
20925 m_dxfCtxState = 0;
20926}
20927
20928bool DRW_MLeader::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
20929 resetDwgState();
20930 dwgBuffer *sourceBuf = buf;
20931 auto fail = [this, sourceBuf]() {
20932 if (sourceBuf != nullptr)
20933 sourceBuf->invalidate();
20934 resetDwgState();
20935 return false;
20936 };
20937 if (sourceBuf == nullptr)
20938 return fail();
20939
20940 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
20941 buf = &bodyProbe;
20942
20943 dwgBuffer sBuff = buf->forkIndependent();
20944 dwgBuffer *sBuf = buf;
20945 if (version > DRW::AC1018) { // 2007+
20946 sBuf = &sBuff;
20947 }
20948 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
20949 if (!ret) return fail();
20950 DRW_DBG("\n***************************** parsing MLEADER ***************\n")DRW_dbg::dbg("\n***************************** parsing MLEADER ***************\n"
)
;
20951 const std::uint64_t bodyEndBit = dwgDataEndBit;
20952 std::uint64_t stringEndBit = bodyEndBit;
20953 if (version > DRW::AC1021) {
20954 std::uint64_t ignoredBodyEndBit = 0;
20955 if (!entityBodyDataEndBit(*sBuf, version, objSize,
20956 ignoredBodyEndBit, &stringEndBit))
20957 return fail();
20958 }
20959
20960 // R2010b+ class version (BS, default 2; <=R2004 was 1). libreDWG
20961 // dwg2.spec:1303-1306. Absent in R2007 streams; reading it would drift.
20962 if (version >= DRW::AC1024) {
20963 if (!readBoundedBitShort(*buf, bodyEndBit, classVersion))
20964 return fail();
20965 if (classVersion > 10) {
20966 DRW_DBG("\nMLEADER: implausible classVersion=")DRW_dbg::dbg("\nMLEADER: implausible classVersion=");
20967 DRW_DBG(static_cast<int>(classVersion))DRW_dbg::dbg(static_cast<int>(classVersion));
20968 DRW_DBG(", aborting body\n")DRW_dbg::dbg(", aborting body\n");
20969 return fail();
20970 }
20971 }
20972
20973 // Phase 4 — embedded AcDbMLeaderObjectContextData / MLeaderAnnotContext.
20974 if (!parseMLeaderAnnotContext(version, buf, sBuf, objSize, bodyEndBit,
20975 stringEndBit, context)) {
20976 DRW_DBG("\nMLEADER: AnnotContext parse failed\n")DRW_dbg::dbg("\nMLEADER: AnnotContext parse failed\n");
20977 return fail();
20978 }
20979
20980 // Phase 3 — entity-level fields per §20.4.48 (after the AnnotContext).
20981 // Many handle slots are deferred to the trailing handle stream and not
20982 // stored here yet (resolution comes in Phase 7).
20983 if (!readBoundedBitLong(*buf, bodyEndBit, overrideFlags)
20984 || !readBoundedBitShort(*buf, bodyEndBit, leaderType))
20985 return fail();
20986 std::uint32_t parsedColor = 0;
20987 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version, parsedColor,
20988 nullptr, nullptr, nullptr, nullptr,
20989 stringEndBit))
20990 return fail();
20991 leaderColor = static_cast<int>(parsedColor);
20992 // leader line type handle 341 — handle stream
20993 if (!readBoundedBitLong(*buf, bodyEndBit, leaderLineWeight)
20994 || !readBoundedBit(*buf, bodyEndBit, landingEnabled)
20995 || !readBoundedBit(*buf, bodyEndBit, doglegEnabled)
20996 || !readBoundedBitDouble(*buf, bodyEndBit, landingDistance))
20997 return fail();
20998 // arrow head handle 342 — handle stream
20999 if (!readBoundedBitDouble(*buf, bodyEndBit, defaultArrowHeadSize)
21000 || !readBoundedBitShort(*buf, bodyEndBit, styleContentType))
21001 return fail();
21002 // text style handle 343 — handle stream
21003 if (!readBoundedBitShort(*buf, bodyEndBit, styleLeftAttach)
21004 || !readBoundedBitShort(*buf, bodyEndBit, styleRightAttach)
21005 || !readBoundedBitShort(*buf, bodyEndBit, styleTextAngleType)
21006 || !readBoundedBitShort(*buf, bodyEndBit, unknown175))
21007 return fail();
21008 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version, parsedColor,
21009 nullptr, nullptr, nullptr, nullptr,
21010 stringEndBit))
21011 return fail();
21012 styleTextColor = static_cast<int>(parsedColor);
21013 if (!readBoundedBit(*buf, bodyEndBit, styleTextFrameEnabled))
21014 return fail();
21015 // style block handle 344 — handle stream (optional)
21016 if (!readBoundedCmColor(*buf, sBuf, bodyEndBit, version, parsedColor,
21017 nullptr, nullptr, nullptr, nullptr,
21018 stringEndBit))
21019 return fail();
21020 styleBlockColor = static_cast<int>(parsedColor);
21021 if (!readBoundedBitCoord(*buf, bodyEndBit, styleBlockScale)
21022 || !readBoundedBitDouble(*buf, bodyEndBit, styleBlockRotation)
21023 || !readBoundedBitShort(*buf, bodyEndBit, styleAttachmentType)
21024 || !readBoundedBit(*buf, bodyEndBit, isAnnotative))
21025 return fail();
21026
21027 // R2007 arrays (pre-R2010 only): per spec §20.4.48. Bounds-check the
21028 // counts; a misaligned bit stream would produce huge nonsense values.
21029 if (version < DRW::AC1024) {
21030 std::uint32_t nArrows = 0;
21031 if (!readTableBodyCount(version, buf, objSize, kMaxMLeaderItems, 1,
21032 nArrows))
21033 return fail();
21034 if (!DRW::reserve(arrowHeads, static_cast<int>(nArrows)))
21035 return fail();
21036 for (std::uint32_t i = 0; i < nArrows; ++i) {
21037 ArrowHeadEntry e;
21038 if (!readBoundedBit(*buf, bodyEndBit, e.isDefault))
21039 return fail();
21040 arrowHeads.push_back(e);
21041 }
21042 std::uint32_t nLabels = 0;
21043 if (!readTableBodyCount(version, buf, objSize, kMaxMLeaderItems, 4,
21044 nLabels))
21045 return fail();
21046 if (!DRW::reserve(blockLabels, static_cast<int>(nLabels)))
21047 return fail();
21048 for (std::uint32_t i = 0; i < nLabels; ++i) {
21049 BlockLabelEntry e;
21050 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
21051 e.labelText))
21052 return fail();
21053 if (!readBoundedBitShort(*buf, bodyEndBit, e.uiIndex)
21054 || !readBoundedBitDouble(*buf, bodyEndBit, e.width))
21055 return fail();
21056 blockLabels.push_back(std::move(e));
21057 }
21058 if (!readBoundedBit(*buf, bodyEndBit, isTextDirectionNegative)
21059 || !readBoundedBitShort(*buf, bodyEndBit, ipeAlign)
21060 || !readBoundedBitShort(*buf, bodyEndBit, justification)
21061 || !readBoundedBitDouble(*buf, bodyEndBit, scaleFactor))
21062 return fail();
21063 } else { // R2010+
21064 if (!readBoundedBitShort(*buf, bodyEndBit, attachmentDirection)
21065 || !readBoundedBitShort(*buf, bodyEndBit, styleTopAttach)
21066 || !readBoundedBitShort(*buf, bodyEndBit, styleBottomAttach))
21067 return fail();
21068 }
21069 if (version >= DRW::AC1027) { // R2013+
21070 if (!readBoundedBit(*buf, bodyEndBit, leaderExtendedToText))
21071 return fail();
21072 }
21073
21074 if (!buf->isGood() || !sBuf->isGood()
21075 || currentDwgBit(buf) > bodyEndBit
21076 || currentDwgBit(sBuf) > stringEndBit)
21077 return fail();
21078
21079 std::uint64_t handleEndBit = 0;
21080 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
21081 handleEndBit))
21082 return fail();
21083 // Common entity handles first (owner/reactors/xdic/layer/ltype/...) —
21084 // entity-specific handles follow in declared order from libreDWG
21085 // dwg2.spec. Read order in the trailing handle stream:
21086 // 1. (R2010b+ only) per-leader-line ltype + arrow handles, in the
21087 // same iteration order as the body block.
21088 // 2. AnnotContext content handle (text_style 340 if hasTextContents,
21089 // else block_table 341 if hasContentsBlock).
21090 // 3. mleaderstyle (340), line_ltype (341), arrow_handle (342),
21091 // text_style (343), block_style (344) entity-level handles.
21092 // 4. (R14-R2007 only) per-arrowhead + per-blocklabel handles.
21093 ret = DRW_Entity::parseDwgEntHandle(version, buf, true, handleEndBit);
21094 if (!ret)
21095 return fail();
21096
21097 auto safeHandle = [&](dwgHandle& slot, const char* tag) {
21098 if (!readBoundedDwgHandle(*buf, handleEndBit, 0, false, slot))
21099 return false;
21100 DRW_DBG(" ")DRW_dbg::dbg(" "); DRW_DBG(tag)DRW_dbg::dbg(tag); DRW_DBG(": ")DRW_dbg::dbg(": ");
21101 DRW_DBGHL(slot.code, slot.size, slot.ref)DRW_dbg::dbgHL(slot.code, slot.size, slot.ref); DRW_DBG("\n")DRW_dbg::dbg("\n");
21102 return true;
21103 };
21104
21105 // 1. R2010b+ per-line handles, in body iteration order.
21106 if (version >= DRW::AC1024) {
21107 for (auto& root : context.roots) {
21108 for (auto& line : root.leaderLines) {
21109 if (!safeHandle(line.lineTypeHandle, "line.ltype")) return fail();
21110 if (!safeHandle(line.arrowHandle, "line.arrow")) return fail();
21111 }
21112 }
21113 }
21114
21115 // 2. AnnotContext content handle.
21116 if (context.hasTextContents) {
21117 if (!safeHandle(context.textStyleHandle, "ctx.text_style")) return fail();
21118 } else if (context.hasContentsBlock) {
21119 if (!safeHandle(context.blockTableRecordHandle, "ctx.block_table")) return fail();
21120 }
21121
21122 // 3. Entity-level handles.
21123 if (!safeHandle(styleHandle, "mleaderstyle")) return fail();
21124 if (!safeHandle(leaderLineTypeHandle, "line_ltype")) return fail();
21125 if (!safeHandle(arrowHeadHandle, "arrow_handle")) return fail();
21126 if (!safeHandle(styleTextStyleHandle, "text_style")) return fail();
21127 if (!safeHandle(styleBlockHandle, "block_style")) return fail();
21128
21129 // 4. R14-R2007 per-arrowhead + per-blocklabel handles (counts came
21130 // from the body-side arrays read earlier).
21131 if (version < DRW::AC1024) {
21132 for (auto& a : arrowHeads)
21133 if (!safeHandle(a.handle, "arrowheads.handle")) return fail();
21134 for (auto& bl : blockLabels)
21135 if (!safeHandle(bl.attDefHandle, "blocklabels.attdef")) return fail();
21136 }
21137
21138 const int rb = buf->numRemainingBytes();
21139 DRW_DBG("\nMLEADER tail rb=")DRW_dbg::dbg("\nMLEADER tail rb="); DRW_DBG(rb)DRW_dbg::dbg(rb); DRW_DBG("\n")DRW_dbg::dbg("\n");
21140 if (rb > 4) {
21141 DRW_DBG("MLEADER: handle-stream tail ")DRW_dbg::dbg("MLEADER: handle-stream tail "); DRW_DBG(rb)DRW_dbg::dbg(rb);
21142 DRW_DBG(" bytes unconsumed (handle ")DRW_dbg::dbg(" bytes unconsumed (handle ");
21143 DRW_DBGH(handle)DRW_dbg::dbgH(handle); DRW_DBG(") — review tail handle list\n")DRW_dbg::dbg(") — review tail handle list\n");
21144 }
21145
21146 if (!buf->isGood() || !sBuf->isGood())
21147 return fail();
21148 *sourceBuf = *buf;
21149 return true;
21150}
21151
21152bool DRW_MLeader::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
21153 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
21154 (void)bs;
21155 if (version < DRW::AC1024)
21156 return false;
21157
21158 oType = kDwgClassNum;
21159 if (!encodeDwgCommon(version, buf, strBuf))
21160 return false;
21161
21162 buf->putBitShort(classVersion == 0 ? 2 : classVersion);
21163 if (!encodeMLeaderAnnotContext(version, buf, strBuf, context))
21164 return false;
21165
21166 buf->putBitLong(overrideFlags);
21167 buf->putBitShort(leaderType);
21168 buf->putCmColor(version, static_cast<std::uint16_t>(leaderColor));
21169 buf->putBitLong(leaderLineWeight);
21170 buf->putBit(landingEnabled ? 1 : 0);
21171 buf->putBit(doglegEnabled ? 1 : 0);
21172 buf->putBitDouble(landingDistance);
21173 buf->putBitDouble(defaultArrowHeadSize);
21174 buf->putBitShort(styleContentType);
21175 buf->putBitShort(styleLeftAttach);
21176 buf->putBitShort(styleRightAttach);
21177 buf->putBitShort(styleTextAngleType);
21178 buf->putBitShort(unknown175);
21179 buf->putCmColor(version, static_cast<std::uint16_t>(styleTextColor));
21180 buf->putBit(styleTextFrameEnabled ? 1 : 0);
21181 buf->putCmColor(version, static_cast<std::uint16_t>(styleBlockColor));
21182 buf->put3BitDouble(styleBlockScale);
21183 buf->putBitDouble(styleBlockRotation);
21184 buf->putBitShort(styleAttachmentType);
21185 buf->putBit(isAnnotative ? 1 : 0);
21186 buf->putBitShort(attachmentDirection);
21187 buf->putBitShort(styleTopAttach);
21188 buf->putBitShort(styleBottomAttach);
21189 if (version >= DRW::AC1027)
21190 buf->putBit(leaderExtendedToText ? 1 : 0);
21191
21192 if (!encodeDwgEntHandle(version, buf, handleBuf))
21193 return false;
21194
21195 dwgBufferW *hb = handleBuf ? handleBuf : buf;
21196 for (const DRW_MLeaderRoot& root : context.roots) {
21197 for (const DRW_MLeaderLeaderLine& line : root.leaderLines) {
21198 putHardPointerHandle(hb, line.lineTypeHandle.ref);
21199 putHardPointerHandle(hb, line.arrowHandle.ref);
21200 }
21201 }
21202
21203 if (context.hasTextContents) {
21204 putHardPointerHandle(hb, context.textStyleHandle.ref);
21205 } else if (context.hasContentsBlock) {
21206 putHardPointerHandle(hb, context.blockTableRecordHandle.ref);
21207 }
21208
21209 putHardPointerHandle(hb, styleHandle.ref);
21210 putHardPointerHandle(hb, leaderLineTypeHandle.ref);
21211 putHardPointerHandle(hb, arrowHeadHandle.ref);
21212 putHardPointerHandle(hb, styleTextStyleHandle.ref);
21213 putHardPointerHandle(hb, styleBlockHandle.ref);
21214
21215 return true;
21216}
21217
21218bool DRW_Viewport::parseCode(int code, const std::unique_ptr<dxfReader>& reader){
21219 switch (code) {
21220 case 40:
21221 pswidth = reader->getDouble();
21222 break;
21223 case 41:
21224 psheight = reader->getDouble();
21225 break;
21226 case 68:
21227 vpstatus = reader->getInt32();
21228 break;
21229 case 69:
21230 vpID = reader->getInt32();
21231 break;
21232 case 12:
21233 centerPX = reader->getDouble();
21234 break;
21235 case 22:
21236 centerPY = reader->getDouble();
21237 break;
21238 case 13:
21239 snapPX = reader->getDouble();
21240 break;
21241 case 23:
21242 snapPY = reader->getDouble();
21243 break;
21244 case 14:
21245 snapSpPX = reader->getDouble();
21246 break;
21247 case 24:
21248 snapSpPY = reader->getDouble();
21249 break;
21250 case 15:
21251 gridSpX = reader->getDouble();
21252 break;
21253 case 25:
21254 gridSpY = reader->getDouble();
21255 break;
21256 case 16:
21257 viewDir.x = reader->getDouble();
21258 break;
21259 case 26:
21260 viewDir.y = reader->getDouble();
21261 break;
21262 case 36:
21263 viewDir.z = reader->getDouble();
21264 break;
21265 case 17:
21266 viewTarget.x = reader->getDouble();
21267 break;
21268 case 27:
21269 viewTarget.y = reader->getDouble();
21270 break;
21271 case 37:
21272 viewTarget.z = reader->getDouble();
21273 break;
21274 case 42:
21275 viewLength = reader->getDouble();
21276 break;
21277 case 43:
21278 frontClip = reader->getDouble();
21279 break;
21280 case 44:
21281 backClip = reader->getDouble();
21282 break;
21283 case 45:
21284 viewHeight = reader->getDouble();
21285 break;
21286 case 50:
21287 snapAngle = reader->getDouble();
21288 break;
21289 case 51:
21290 twistAngle = reader->getDouble();
21291 break;
21292 case 46:
21293 circleZoom = reader->getInt32();
21294 break;
21295 case 61:
21296 majorGridLines = reader->getInt32();
21297 break;
21298 case 63:
21299 ambientColor = static_cast<std::uint32_t>(reader->getInt32());
21300 break;
21301 case 72:
21302 circleZoom = reader->getInt32();
21303 break;
21304 case 90:
21305 statusFlags = reader->getInt32();
21306 break;
21307 case 1:
21308 styleSheet = reader->getUtf8String();
21309 break;
21310 case 281:
21311 renderMode = reader->getInt32();
21312 break;
21313 case 71:
21314 ucsPerViewport = reader->getInt32() != 0;
21315 break;
21316 case 74:
21317 ucsAtOrigin = reader->getInt32() != 0;
21318 break;
21319 case 110: case 120: case 130:
21320 case 111: case 121: case 131:
21321 case 112: case 122: case 132:
21322 readCoordTripletCode(code, reader, ucsOrigin, ucsXAxis, ucsYAxis);
21323 break;
21324 case 146:
21325 ucsElevation = reader->getDouble();
21326 break;
21327 case 76:
21328 break;
21329 case 79:
21330 ucsOrthographicType = reader->getInt32();
21331 break;
21332 case 148:
21333 shadePlotMode = reader->getInt32();
21334 break;
21335 case 170:
21336 shadePlotMode = reader->getInt32();
21337 break;
21338 case 292:
21339 useDefaultLighting = reader->getInt32() != 0;
21340 break;
21341 case 282:
21342 defaultLightingType = reader->getInt32();
21343 break;
21344 case 451:
21345 brightness = reader->getDouble();
21346 break;
21347 case 452:
21348 contrast = reader->getDouble();
21349 break;
21350 case 141:
21351 brightness = reader->getDouble();
21352 break;
21353 case 142:
21354 contrast = reader->getDouble();
21355 break;
21356 case 421:
21357 ambientColorRgb = reader->getInt32();
21358 break;
21359 case 431:
21360 ambientColorName = reader->getUtf8String();
21361 break;
21362 case 331:
21363 frozenLayerHandles.push_back(
21364 static_cast<std::uint32_t>(reader->getHandleString()));
21365 break;
21366 case 332:
21367 backgroundHandle = static_cast<std::uint32_t>(reader->getHandleString());
21368 break;
21369 case 333:
21370 shadePlotHandle = static_cast<std::uint32_t>(reader->getHandleString());
21371 break;
21372 case 340:
21373 clipBoundaryHandle = static_cast<std::uint32_t>(reader->getHandleString());
21374 break;
21375 case 345:
21376 namedUcsHandle = static_cast<std::uint32_t>(reader->getHandleString());
21377 break;
21378 case 346:
21379 baseUcsHandle = static_cast<std::uint32_t>(reader->getHandleString());
21380 break;
21381 case 361:
21382 m_sunHandle = static_cast<std::uint32_t>(reader->getHandleString());
21383 break;
21384 case 348:
21385 visualStyleHandle = static_cast<std::uint32_t>(reader->getHandleString());
21386 fullVisualStyleHandle = visualStyleHandle;
21387 break;
21388 case 349:
21389 shadePlotHandle = static_cast<std::uint32_t>(reader->getHandleString());
21390 break;
21391 default:
21392 return DRW_Point::parseCode(code, reader);
21393 }
21394
21395 return true;
21396}
21397
21398bool DRW_Viewport::validatePayloadFields() const {
21399 const auto finite = [](const DRW_Coord& point) {
21400 return std::isfinite(point.x) && std::isfinite(point.y)
21401 && std::isfinite(point.z);
21402 };
21403 const auto fitsSignedShort = [](int value) {
21404 return value >= static_cast<int>(std::numeric_limits<std::int16_t>::min())
21405 && value <= static_cast<int>(std::numeric_limits<std::int16_t>::max());
21406 };
21407 const auto fitsByte = [](int value) {
21408 return value >= 0
21409 && value <= static_cast<int>(std::numeric_limits<std::uint8_t>::max());
21410 };
21411 const auto fitsBitShort = [](int value) {
21412 return value >= 0
21413 && value <= static_cast<int>(std::numeric_limits<std::uint16_t>::max());
21414 };
21415 const bool integralCircleZoom = std::isfinite(circleZoom)
21416 && std::trunc(circleZoom) == circleZoom;
21417 return finite(basePoint) && finite(viewTarget) && finite(viewDir)
21418 && finite(ucsOrigin) && finite(ucsXAxis) && finite(ucsYAxis)
21419 && std::isfinite(pswidth) && std::isfinite(psheight)
21420 && std::isfinite(centerPX) && std::isfinite(centerPY)
21421 && std::isfinite(snapPX) && std::isfinite(snapPY)
21422 && std::isfinite(snapSpPX) && std::isfinite(snapSpPY)
21423 && std::isfinite(gridSpX) && std::isfinite(gridSpY)
21424 && std::isfinite(viewLength) && std::isfinite(frontClip)
21425 && std::isfinite(backClip) && std::isfinite(viewHeight)
21426 && std::isfinite(snapAngle) && std::isfinite(twistAngle)
21427 && std::isfinite(ucsElevation) && std::isfinite(brightness)
21428 && std::isfinite(contrast) && fitsSignedShort(vpstatus)
21429 && fitsSignedShort(vpID) && integralCircleZoom
21430 && circleZoom >= 0.0
21431 && circleZoom <= static_cast<double>(std::numeric_limits<std::uint16_t>::max())
21432 && fitsBitShort(majorGridLines) && fitsByte(renderMode)
21433 && fitsBitShort(ucsOrthographicType) && fitsBitShort(shadePlotMode)
21434 && fitsByte(defaultLightingType)
21435 && ambientColor <= std::numeric_limits<std::uint16_t>::max()
21436 && ambientColorRgb >= -1 && ambientColorRgb <= 0xFFFFFF
21437 && frozenLayerHandles.size() <= kMaxViewportFrozenLayers;
21438}
21439
21440bool DRW_Viewport::validateDxf() const {
21441 return validatePayloadFields();
21442}
21443//ex 22 dec 34
21444void DRW_Viewport::resetDwgState() {
21445 DRW_Point::resetDwgState();
21446 pswidth = 205.0;
21447 psheight = 156.0;
21448 vpstatus = 0;
21449 vpID = 0;
21450 centerPX = 128.5;
21451 centerPY = 97.5;
21452 snapPX = snapPY = 0.0;
21453 snapSpPX = snapSpPY = 10.0;
21454 gridSpX = gridSpY = 10.0;
21455 viewDir = DRW_Coord{0.0, 0.0, 1.0};
21456 viewTarget = DRW_Coord{0.0, 0.0, 0.0};
21457 viewLength = 50.0;
21458 frontClip = backClip = 0.0;
21459 viewHeight = 1.0;
21460 snapAngle = twistAngle = 0.0;
21461 circleZoom = 100.0;
21462 majorGridLines = 0;
21463 statusFlags = 0;
21464 styleSheet.clear();
21465 renderMode = 0;
21466 ucsAtOrigin = false;
21467 ucsPerViewport = false;
21468 ucsOrigin = DRW_Coord{0.0, 0.0, 0.0};
21469 ucsXAxis = DRW_Coord{1.0, 0.0, 0.0};
21470 ucsYAxis = DRW_Coord{0.0, 1.0, 0.0};
21471 ucsElevation = 0.0;
21472 ucsOrthographicType = 0;
21473 shadePlotMode = 0;
21474 useDefaultLighting = true;
21475 defaultLightingType = 1;
21476 brightness = contrast = 0.0;
21477 ambientColor = 250;
21478 ambientColorRgb = -1;
21479 ambientColorMethod = 0;
21480 ambientColorName.clear();
21481 vpHeaderHandle = 0;
21482 clipBoundaryHandle = 0;
21483 namedUcsHandle = 0;
21484 baseUcsHandle = 0;
21485 backgroundHandle = 0;
21486 visualStyleHandle = 0;
21487 shadePlotHandle = 0;
21488 m_sunHandle = 0;
21489 frozenLayerHandles.clear();
21490 frozenLyCount = 0;
21491}
21492
21493bool DRW_Viewport::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs){
21494 resetDwgState();
21495 dwgBuffer *sourceBuf = buf;
21496 auto fail = [this, sourceBuf]() {
21497 if (sourceBuf != nullptr)
21498 sourceBuf->invalidate();
21499 resetDwgState();
21500 return false;
21501 };
21502 if (sourceBuf == nullptr)
21503 return fail();
21504
21505 dwgBuffer bodyProbe = sourceBuf->forkIndependent();
21506 buf = &bodyProbe;
21507 dwgBuffer sBuff = bodyProbe.forkIndependent();
21508 dwgBuffer *sBuf = buf;
21509 if (version > DRW::AC1018) {//2007+
21510 sBuf = &sBuff; //separate buffer for strings
21511 }
21512 bool ret = DRW_Entity::parseDwg(version, buf, sBuf, bs);
21513 if (!ret)
21514 return fail();
21515 DRW_DBG("\n***************************** parsing viewport *****************************************\n")DRW_dbg::dbg("\n***************************** parsing viewport *****************************************\n"
)
;
21516 const std::uint64_t bodyEndBit = dwgDataEndBit;
21517 std::uint64_t stringEndBit = bodyEndBit;
21518 if (version > DRW::AC1021) {
21519 std::uint64_t ignoredBodyEndBit = 0;
21520 if (!entityBodyDataEndBit(*sBuf, version, objSize,
21521 ignoredBodyEndBit, &stringEndBit))
21522 return fail();
21523 }
21524 if (!readBoundedBitCoord(*buf, bodyEndBit, basePoint)
21525 || !readBoundedBitDouble(*buf, bodyEndBit, pswidth)
21526 || !readBoundedBitDouble(*buf, bodyEndBit, psheight))
21527 return fail();
21528 DRW_DBG("center ")DRW_dbg::dbg("center "); DRW_DBGPT(basePoint.x, basePoint.y, basePoint.z)DRW_dbg::dbgPT(basePoint.x, basePoint.y, basePoint.z);
21529 DRW_DBG("\nWidth: ")DRW_dbg::dbg("\nWidth: "); DRW_DBG(pswidth)DRW_dbg::dbg(pswidth); DRW_DBG(", Height: ")DRW_dbg::dbg(", Height: "); DRW_DBG(psheight)DRW_dbg::dbg(psheight); DRW_DBG("\n")DRW_dbg::dbg("\n");
21530 //RLZ TODO: complete in dxf
21531 if (version > DRW::AC1014) {//2000+
21532 if (!readBoundedBitCoord(*buf, bodyEndBit, viewTarget)
21533 || !readBoundedBitCoord(*buf, bodyEndBit, viewDir)
21534 || !readBoundedBitDouble(*buf, bodyEndBit, twistAngle)
21535 || !readBoundedBitDouble(*buf, bodyEndBit, viewHeight)
21536 || !readBoundedBitDouble(*buf, bodyEndBit, viewLength)
21537 || !readBoundedBitDouble(*buf, bodyEndBit, frontClip)
21538 || !readBoundedBitDouble(*buf, bodyEndBit, backClip)
21539 || !readBoundedBitDouble(*buf, bodyEndBit, snapAngle)
21540 || !readBoundedRawDouble(*buf, bodyEndBit, centerPX)
21541 || !readBoundedRawDouble(*buf, bodyEndBit, centerPY)
21542 || !readBoundedRawDouble(*buf, bodyEndBit, snapPX)
21543 || !readBoundedRawDouble(*buf, bodyEndBit, snapPY)
21544 || !readBoundedRawDouble(*buf, bodyEndBit, snapSpPX)
21545 || !readBoundedRawDouble(*buf, bodyEndBit, snapSpPY)
21546 || !readBoundedRawDouble(*buf, bodyEndBit, gridSpX)
21547 || !readBoundedRawDouble(*buf, bodyEndBit, gridSpY))
21548 return fail();
21549 DRW_DBG("view Target ")DRW_dbg::dbg("view Target "); DRW_DBGPT(viewTarget.x, viewTarget.y, viewTarget.z)DRW_dbg::dbgPT(viewTarget.x, viewTarget.y, viewTarget.z);
21550 DRW_DBG("\nview direction ")DRW_dbg::dbg("\nview direction "); DRW_DBGPT(viewDir.x, viewDir.y, viewDir.z)DRW_dbg::dbgPT(viewDir.x, viewDir.y, viewDir.z);
21551 DRW_DBG("\nView twist Angle: ")DRW_dbg::dbg("\nView twist Angle: "); DRW_DBG(twistAngle)DRW_dbg::dbg(twistAngle);
21552 DRW_DBG("\nview Height: ")DRW_dbg::dbg("\nview Height: "); DRW_DBG(viewHeight)DRW_dbg::dbg(viewHeight);
21553 DRW_DBG(" Lens Length: ")DRW_dbg::dbg(" Lens Length: "); DRW_DBG(viewLength)DRW_dbg::dbg(viewLength);
21554 DRW_DBG("\nfront Clip Z: ")DRW_dbg::dbg("\nfront Clip Z: "); DRW_DBG(frontClip)DRW_dbg::dbg(frontClip);
21555 DRW_DBG(" back Clip Z: ")DRW_dbg::dbg(" back Clip Z: "); DRW_DBG(backClip)DRW_dbg::dbg(backClip);
21556 DRW_DBG("\n snap Angle: ")DRW_dbg::dbg("\n snap Angle: "); DRW_DBG(snapAngle)DRW_dbg::dbg(snapAngle);
21557 DRW_DBG("\nview center X: ")DRW_dbg::dbg("\nview center X: "); DRW_DBG(centerPX)DRW_dbg::dbg(centerPX); DRW_DBG(", Y: ")DRW_dbg::dbg(", Y: "); DRW_DBG(centerPX)DRW_dbg::dbg(centerPX);
21558 DRW_DBG("\nSnap base point X: ")DRW_dbg::dbg("\nSnap base point X: "); DRW_DBG(snapPX)DRW_dbg::dbg(snapPX); DRW_DBG(", Y: ")DRW_dbg::dbg(", Y: "); DRW_DBG(snapPY)DRW_dbg::dbg(snapPY);
21559 DRW_DBG("\nSnap spacing X: ")DRW_dbg::dbg("\nSnap spacing X: "); DRW_DBG(snapSpPX)DRW_dbg::dbg(snapSpPX); DRW_DBG(", Y: ")DRW_dbg::dbg(", Y: "); DRW_DBG(snapSpPY)DRW_dbg::dbg(snapSpPY);
21560 std::uint16_t parsedCircleZoom = 0;
21561 if (!readBoundedBitShort(*buf, bodyEndBit, parsedCircleZoom))
21562 return fail();
21563 circleZoom = parsedCircleZoom;
21564 }
21565 if (version > DRW::AC1018) {//2007+
21566 std::uint16_t parsedMajorGridLines = 0;
21567 if (!readBoundedBitShort(*buf, bodyEndBit, parsedMajorGridLines))
21568 return fail();
21569 majorGridLines = parsedMajorGridLines;
21570 }
21571 if (version > DRW::AC1014) {//2000+
21572 std::int32_t parsedFrozenLyCount = 0;
21573 if (!readBoundedBitLong(*buf, bodyEndBit, parsedFrozenLyCount))
21574 return fail();
21575 if (!isValidCount(parsedFrozenLyCount, kMaxViewportFrozenLayers))
21576 return fail();
21577 frozenLyCount = static_cast<std::uint32_t>(parsedFrozenLyCount);
21578 DRW_DBG("Frozen Layer count?: ")DRW_dbg::dbg("Frozen Layer count?: "); DRW_DBG(frozenLyCount)DRW_dbg::dbg(frozenLyCount); DRW_DBG("\n")DRW_dbg::dbg("\n");
21579 if (!readBoundedBitLong(*buf, bodyEndBit, statusFlags))
21580 return fail();
21581 if (!readBoundedVariableText(*sBuf, stringEndBit, version,
21582 styleSheet))
21583 return fail();
21584 std::uint8_t parsedRenderMode = 0;
21585 if (!readBoundedRawChar8(*buf, bodyEndBit, parsedRenderMode))
21586 return fail();
21587 renderMode = parsedRenderMode;
21588 // ODA order is UCS-at-origin (74), then UCS-per-viewport (71).
21589 if (!readBoundedBit(*buf, bodyEndBit, ucsAtOrigin)
21590 || !readBoundedBit(*buf, bodyEndBit, ucsPerViewport)
21591 || !readBoundedBitCoord(*buf, bodyEndBit, ucsOrigin)
21592 || !readBoundedBitCoord(*buf, bodyEndBit, ucsXAxis)
21593 || !readBoundedBitCoord(*buf, bodyEndBit, ucsYAxis)
21594 || !readBoundedBitDouble(*buf, bodyEndBit, ucsElevation))
21595 return fail();
21596 std::uint16_t parsedUcsOrthographicType = 0;
21597 if (!readBoundedBitShort(*buf, bodyEndBit,
21598 parsedUcsOrthographicType))
21599 return fail();
21600 ucsOrthographicType = parsedUcsOrthographicType;
21601 }
21602 if (version > DRW::AC1015) {//2004+
21603 std::uint16_t parsedShadePlotMode = 0;
21604 if (!readBoundedBitShort(*buf, bodyEndBit, parsedShadePlotMode))
21605 return fail();
21606 shadePlotMode = parsedShadePlotMode;
21607 }
21608 if (version > DRW::AC1018) {//2007+
21609 std::uint8_t parsedDefaultLightingType = 0;
21610 if (!readBoundedBit(*buf, bodyEndBit, useDefaultLighting)
21611 || !readBoundedRawChar8(*buf, bodyEndBit,
21612 parsedDefaultLightingType)
21613 || !readBoundedBitDouble(*buf, bodyEndBit, brightness)
21614 || !readBoundedBitDouble(*buf, bodyEndBit, contrast))
21615 return fail();
21616 defaultLightingType = parsedDefaultLightingType;
21617 // ODA §20.4.38: ambient color is CMC, not ENC — confirmed by libreDWG dwg.spec:2512
21618 bool hasRgbColor = false;
21619 int parsedAmbientColorRgb = -1;
21620 UTF8STRINGstd::string parsedAmbientColorName;
21621 UTF8STRINGstd::string parsedAmbientColorBookName;
21622 dwgBuffer colorProbe = buf->forkIndependent();
21623 dwgBuffer colorStringProbe = sBuf->forkIndependent();
21624 const std::uint32_t parsedAmbientColor = colorProbe.getCmColor(
21625 version, &parsedAmbientColorRgb, &colorStringProbe,
21626 &parsedAmbientColorName, &parsedAmbientColorBookName,
21627 &hasRgbColor);
21628 if (!colorProbe.isGood() || !colorStringProbe.isGood()
21629 || currentDwgBit(&colorProbe) > bodyEndBit
21630 || currentDwgBit(&colorStringProbe) > stringEndBit)
21631 return fail();
21632 *buf = colorProbe;
21633 *sBuf = colorStringProbe;
21634 ambientColor = parsedAmbientColor;
21635 ambientColorRgb = parsedAmbientColorRgb;
21636 ambientColorName = parsedAmbientColorBookName.empty()
21637 ? std::move(parsedAmbientColorName)
21638 : (parsedAmbientColorBookName + "$"
21639 + parsedAmbientColorName);
21640 if (!hasRgbColor)
21641 ambientColorRgb = -1;
21642 }
21643 const auto finite = [](const DRW_Coord& point) {
21644 return std::isfinite(point.x) && std::isfinite(point.y)
21645 && std::isfinite(point.z);
21646 };
21647 if (!finite(basePoint) || !finite(viewTarget) || !finite(viewDir)
21648 || !finite(ucsOrigin) || !finite(ucsXAxis) || !finite(ucsYAxis)
21649 || !std::isfinite(pswidth) || !std::isfinite(psheight)
21650 || !std::isfinite(centerPX) || !std::isfinite(centerPY)
21651 || !std::isfinite(snapPX) || !std::isfinite(snapPY)
21652 || !std::isfinite(snapSpPX) || !std::isfinite(snapSpPY)
21653 || !std::isfinite(gridSpX) || !std::isfinite(gridSpY)
21654 || !std::isfinite(viewLength) || !std::isfinite(frontClip)
21655 || !std::isfinite(backClip) || !std::isfinite(viewHeight)
21656 || !std::isfinite(snapAngle) || !std::isfinite(twistAngle)
21657 || !std::isfinite(ucsElevation) || !std::isfinite(brightness)
21658 || !std::isfinite(contrast))
21659 return fail();
21660 if (!validatePayloadFields())
21661 return fail();
21662 std::uint64_t handleEndBit = 0;
21663 if (!dwgHandleStreamEndBit(*sourceBuf, version, objSize, bs,
21664 handleEndBit))
21665 return fail();
21666 ret = DRW_Entity::parseDwgEntHandle(version, buf, true, handleEndBit);
21667 if (!ret || !buf->isGood() || !sBuf->isGood())
21668 return fail();
21669
21670 if (!buf->isGood() || !sBuf->isGood()
21671 || currentDwgBit(sBuf) > stringEndBit)
21672 return fail();
21673
21674 dwgBuffer handleProbe = buf->forkIndependent();
21675 auto readHandleRef = [&handleProbe, handleEndBit](std::uint32_t& target) {
21676 dwgHandle parsed;
21677 if (!readBoundedDwgHandle(handleProbe, handleEndBit, 0, false,
21678 parsed)) {
21679 return false;
21680 }
21681 target = parsed.ref;
21682 return true;
21683 };
21684 if (version < DRW::AC1015) {//R13 & R14 only
21685 if (!readHandleRef(vpHeaderHandle))
21686 return fail();
21687 }
21688 if (version > DRW::AC1014) {//2000+
21689 if (!DRW::reserve(frozenLayerHandles, static_cast<int>(frozenLyCount)))
21690 return fail();
21691 for (std::uint32_t i = 0; i < frozenLyCount; ++i) {
21692 std::uint32_t ref = 0;
21693 if (!readHandleRef(ref))
21694 return fail();
21695 frozenLayerHandles.push_back(ref);
21696 }
21697 if (!readHandleRef(clipBoundaryHandle))
21698 return fail();
21699 if (version == DRW::AC1015) {//2000 only
21700 if (!readHandleRef(vpHeaderHandle))
21701 return fail();
21702 }
21703 if (!readHandleRef(namedUcsHandle) || !readHandleRef(baseUcsHandle))
21704 return fail();
21705 }
21706 if (version > DRW::AC1018) {//2007+
21707 if (!readHandleRef(backgroundHandle)
21708 || !readHandleRef(visualStyleHandle)
21709 || !readHandleRef(shadePlotHandle))
21710 return fail();
21711 }
21712 if (version > DRW::AC1018 && !readHandleRef(m_sunHandle))
21713 return fail();
21714 if (!handleProbe.isGood() || !sBuf->isGood())
21715 return fail();
21716 *sourceBuf = handleProbe;
21717 return true;
21718}
21719
21720// ---------------------------------------------------------------------------
21721// Write helpers shared by the new encoders below.
21722// ---------------------------------------------------------------------------
21723
21724namespace {
21725// ODA handle reference codes: 3 hard owner, 4 soft pointer, 5 hard pointer.
21726static void putTypedHandle(dwgBufferW *hb, std::uint32_t ref,
21727 std::uint8_t code) {
21728 dwgHandle h;
21729 h.code = ref == 0 ? 0 : code;
21730 h.ref = ref;
21731 h.ref64 = ref;
21732 h.size = 0;
21733 if (h.ref != 0) {
21734 std::uint32_t t = h.ref;
21735 while (t != 0) { t >>= 8; ++h.size; }
21736 }
21737 hb->putHandle(h);
21738}
21739
21740static void putAbsHandle(dwgBufferW *hb, std::uint32_t ref) {
21741 putTypedHandle(hb, ref, 5);
21742}
21743} // namespace
21744
21745// ---------------------------------------------------------------------------
21746// DRW_MLine::encodeDwg — OT=47 (AC1015/AC1018/AC1024)
21747// ---------------------------------------------------------------------------
21748
21749bool DRW_MLine::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
21750 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
21751 (void)bs; (void)strBuf;
21752 if (!validatePayloadFields())
21753 return false;
21754 oType = 47;
21755 if (!encodeDwgCommon(version, buf)) return false;
21756
21757 buf->putBitDouble(scale);
21758 buf->putRawChar8(justification);
21759 buf->put3BitDouble(basePoint);
21760 buf->putExtrusion(extPoint, false); // false = pre-2000 style (getExtrusion(false) in parser)
21761 buf->putBitShort(static_cast<std::uint16_t>(openClosed));
21762 buf->putRawChar8(numLines);
21763 buf->putBitShort(numVerts);
21764
21765 for (const auto& vtx : vertlist) {
21766 buf->put3BitDouble(vtx.position);
21767 buf->put3BitDouble(vtx.vertexDir);
21768 buf->put3BitDouble(vtx.miterDir);
21769 for (int li = 0; li < static_cast<int>(numLines); ++li) {
21770 const auto& segs = (li < static_cast<int>(vtx.segParms.size()))
21771 ? vtx.segParms[li] : std::vector<double>{};
21772 const auto& fills = (li < static_cast<int>(vtx.areaFillParms.size()))
21773 ? vtx.areaFillParms[li] : std::vector<double>{};
21774 buf->putBitShort(static_cast<std::uint16_t>(segs.size()));
21775 for (double s : segs) buf->putBitDouble(s);
21776 buf->putBitShort(static_cast<std::uint16_t>(fills.size()));
21777 for (double f : fills) buf->putBitDouble(f);
21778 }
21779 }
21780
21781 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
21782
21783 // MLINE style handle — extra handle after standard entity handles.
21784 if (version > DRW::AC1014) {
21785 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
21786 putAbsHandle(hb, styleHandle);
21787 }
21788 return true;
21789}
21790
21791// ---------------------------------------------------------------------------
21792// DRW_Vertex::encodeDwg — OT varies by flags
21793// ---------------------------------------------------------------------------
21794
21795bool DRW_Vertex::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
21796 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
21797 (void)bs; (void)strBuf;
21798 if (buf == nullptr || !validatePayloadFields())
21799 return false;
21800 switch (m_dwgSubtype) {
21801 case DwgSubtype::Vertex2D: oType = 0x0A; break;
21802 case DwgSubtype::Vertex3D: oType = 0x0B; break;
21803 case DwgSubtype::Mesh: oType = 0x0C; break;
21804 case DwgSubtype::Polyface: oType = 0x0D; break;
21805 case DwgSubtype::PolyfaceFace: oType = 0x0E; break;
21806 case DwgSubtype::Auto:
21807 if ((flags & 64) != 0)
21808 oType = 0x0D; // VERTEX_PFACE coordinate vertex
21809 else if ((flags & 128) != 0)
21810 oType = 0x0E; // VERTEX_PFACE_FACE
21811 else if ((flags & 16) != 0)
21812 oType = 0x0C; // VERTEX_MESH
21813 else if ((flags & 32) != 0 || (flags & 8) != 0)
21814 oType = 0x0B; // VERTEX_3D
21815 else
21816 oType = 0x0A; // VERTEX_2D
21817 break;
21818 }
21819
21820 if (!encodeDwgCommon(version, buf)) return false;
21821
21822 if (oType == 0x0A) {
21823 buf->putRawChar8(static_cast<std::uint8_t>(flags));
21824 buf->put3BitDouble(basePoint);
21825 buf->putBitDouble(stawidth);
21826 buf->putBitDouble(endwidth);
21827 buf->putBitDouble(bulge);
21828 if (version > DRW::AC1021)
21829 buf->putBitLong(static_cast<std::int32_t>(identifier));
21830 buf->putBitDouble(tgdir);
21831 } else if (oType == 0x0B || oType == 0x0C || oType == 0x0D) {
21832 buf->putRawChar8(static_cast<std::uint8_t>(flags));
21833 buf->put3BitDouble(basePoint);
21834 } else { // 0x0E pface face
21835 buf->putBitShort(static_cast<std::uint16_t>(vindex1));
21836 buf->putBitShort(static_cast<std::uint16_t>(vindex2));
21837 buf->putBitShort(static_cast<std::uint16_t>(vindex3));
21838 buf->putBitShort(static_cast<std::uint16_t>(vindex4));
21839 }
21840
21841 return encodeDwgEntHandle(version, buf, handleBuf);
21842}
21843
21844bool DRW_SeqEnd::parseDwg(DRW::Version version, dwgBuffer *buf, std::uint32_t bs) {
21845 DRW_Entity::reset();
21846 if (buf == nullptr)
21847 return false;
21848
21849 auto fail = [this, buf]() {
21850 buf->invalidate();
21851 DRW_Entity::reset();
21852 return false;
21853 };
21854 dwgBuffer probe = buf->forkIndependent();
21855 if (!DRW_Entity::parseDwgCommon(version, &probe, bs)
21856 || !probe.isGood())
21857 return fail();
21858 std::uint64_t handleEndBit = 0;
21859 if (!dwgHandleStreamEndBit(*buf, version, objSize, bs, handleEndBit)
21860 || !DRW_Entity::parseDwgEntHandle(version, &probe, true,
21861 handleEndBit)
21862 || !probe.isGood())
21863 return fail();
21864
21865 *buf = probe;
21866 return true;
21867}
21868
21869bool DRW_SeqEnd::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
21870 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
21871 (void)bs; (void)strBuf;
21872 oType = 0x06;
21873 if (!encodeDwgCommon(version, buf))
21874 return false;
21875 return encodeDwgEntHandle(version, buf, handleBuf);
21876}
21877
21878// ---------------------------------------------------------------------------
21879// DRW_Polyline::encodeDwg — OT varies by flags; vertex handles emitted here.
21880// ---------------------------------------------------------------------------
21881
21882bool DRW_Polyline::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
21883 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
21884 (void)bs; (void)strBuf;
21885 if (buf == nullptr || !validatePayloadFields())
21886 return false;
21887 // Determine object type from stored flags (mirror of parseDwg dispatch).
21888 if (flags & 64) oType = 0x1D; // POLYLINE_PFACE
21889 else if (flags & 16) oType = 0x1E; // POLYLINE_MESH
21890 else if (flags & 8) oType = 0x10; // POLYLINE_3D
21891 else oType = 0x0F; // POLYLINE_2D
21892
21893 if (!encodeDwgCommon(version, buf)) return false;
21894
21895 if (oType == 0x0F) {
21896 buf->putBitShort(static_cast<std::uint16_t>(flags));
21897 buf->putBitShort(static_cast<std::uint16_t>(curvetype));
21898 buf->putBitDouble(defstawidth);
21899 buf->putBitDouble(defendwidth);
21900 buf->putThickness(thickness, version > DRW::AC1014);
21901 buf->putBitDouble(basePoint.z);
21902 buf->putExtrusion(extPoint, version > DRW::AC1014);
21903 } else if (oType == 0x10) {
21904 // curvetype → 2 RC flag bytes (mirror of parser decode)
21905 std::uint8_t rc1 = 0;
21906 if (curvetype == 5) rc1 = 1;
21907 else if (curvetype == 6) rc1 = 2;
21908 else if (curvetype == 8) rc1 = 3;
21909 buf->putRawChar8(rc1);
21910 buf->putRawChar8(static_cast<std::uint8_t>(flags & 1)); // bit 0 = closed
21911 } else if (oType == 0x1D) {
21912 buf->putBitShort(static_cast<std::uint16_t>(vertexcount));
21913 buf->putBitShort(static_cast<std::uint16_t>(facecount));
21914 } else { // 0x1E MESH
21915 buf->putBitShort(static_cast<std::uint16_t>(flags & ~16)); // strip reader-added bit 4
21916 buf->putBitShort(static_cast<std::uint16_t>(curvetype));
21917 buf->putBitShort(static_cast<std::uint16_t>(vertexcount)); // M count
21918 buf->putBitShort(static_cast<std::uint16_t>(facecount)); // N count
21919 buf->putBitShort(static_cast<std::uint16_t>(smoothM)); // mDensity, DXF 73
21920 buf->putBitShort(static_cast<std::uint16_t>(smoothN)); // nDensity, DXF 74
21921 }
21922
21923 // AC2004+ (>AC1015): emit vertex count before the handle section.
21924 std::int32_t ooCount = static_cast<std::int32_t>(vertlist.size());
21925 if (version > DRW::AC1015)
21926 buf->putBitLong(ooCount);
21927
21928 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
21929
21930 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
21931
21932 if (version < DRW::AC1018) {
21933 // R2000-: first/last vertex handles (absolute hard pointers).
21934 putAbsHandle(hb, vertlist.empty() ? 0u : vertlist.front()->handle);
21935 putAbsHandle(hb, vertlist.empty() ? 0u : vertlist.back()->handle);
21936 } else {
21937 // R2004+: one handle per vertex.
21938 for (const auto& v : vertlist)
21939 putAbsHandle(hb, v ? v->handle : 0u);
21940 }
21941 putAbsHandle(hb, seqEndH.ref);
21942
21943 return true;
21944}
21945
21946// ---------------------------------------------------------------------------
21947// DRW_Leader::encodeDwg — OT=45 (AC1015/AC1018/AC1024)
21948// ---------------------------------------------------------------------------
21949
21950bool DRW_Leader::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
21951 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
21952 (void)bs; (void)strBuf;
21953 if (buf == nullptr || !validatePayloadFields())
21954 return false;
21955 oType = 45;
21956 if (!encodeDwgCommon(version, buf)) return false;
21957
21958 buf->putBit(0); // unknown bit
21959 buf->putBitShort(static_cast<std::uint16_t>(flag)); // annotation type
21960 buf->putBitShort(static_cast<std::int16_t>(leadertype)); // pathType (DXF code 72)
21961 buf->putBitLong(static_cast<std::int32_t>(vertexlist.size()));
21962 for (const auto& vp : vertexlist)
21963 buf->put3BitDouble(*vp);
21964 buf->put3BitDouble(origin); // leader plane origin
21965 // ODA §20.4.47: Extrusion is plain 3DPOINT (3BD), not BE — matches parseDwg.
21966 buf->put3BitDouble(extrusionPoint);
21967
21968 buf->put3BitDouble(horizdir);
21969 buf->put3BitDouble(offsetblock);
21970
21971 if (leaderHasEndpointProjection(version))
21972 buf->put3BitDouble(offsettext); // endpoint projection
21973
21974 if (version < DRW::AC1015)
21975 buf->putBitDouble(0.0); // dimgap (pre-R2000)
21976
21977 if (leaderHasTextBox(version)) {
21978 buf->putBitDouble(textheight);
21979 buf->putBitDouble(textwidth);
21980 }
21981
21982 buf->putBit(static_cast<std::uint8_t>(hookline));
21983 buf->putBit(static_cast<std::uint8_t>(arrow));
21984
21985 buf->putBitShort(0); // arrowHeadType (present in every DWG version)
21986 if (version < DRW::AC1015) {
21987 buf->putBitDouble(0.0); // dimasz
21988 buf->putBit(0); // unk
21989 buf->putBit(0); // unk
21990 buf->putBitShort(0); // unk short
21991 buf->putBitShort(static_cast<std::uint16_t>(coloruse)); // byBlock color
21992 buf->putBit(0); // unk
21993 buf->putBit(0); // unk
21994 } else {
21995 buf->putBit(0); // unk
21996 buf->putBit(0); // unk
21997 }
21998
21999 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
22000
22001 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
22002 putAbsHandle(hb, 0); // AnnotH — null (no annotation entity)
22003 putAbsHandle(hb, 0x15); // dimStyleH — hard ptr to STANDARD (handle 0x15)
22004
22005 return buf->isGood() && hb->isGood();
22006}
22007
22008// ---------------------------------------------------------------------------
22009// DRW_Viewport::encodeDwg — OT=34 (AC1015/AC1018/AC1024)
22010// ---------------------------------------------------------------------------
22011
22012bool DRW_Viewport::encodeDwg(DRW::Version version, dwgBufferW *buf, std::uint32_t bs,
22013 dwgBufferW *strBuf, dwgBufferW *handleBuf) {
22014 (void)bs;
22015 if (buf == nullptr || !validatePayloadFields())
22016 return false;
22017 oType = 34;
22018 // Use strBuf for TV strings in AC1024; for AC1015/AC1018 strings go inline.
22019 dwgBufferW *sb = (strBuf && version > DRW::AC1018) ? strBuf : buf;
22020 if (!encodeDwgCommon(version, buf)) return false;
22021
22022 buf->putBitDouble(basePoint.x);
22023 buf->putBitDouble(basePoint.y);
22024 buf->putBitDouble(basePoint.z);
22025 buf->putBitDouble(pswidth);
22026 buf->putBitDouble(psheight);
22027
22028 if (version > DRW::AC1014) {
22029 buf->putBitDouble(viewTarget.x);
22030 buf->putBitDouble(viewTarget.y);
22031 buf->putBitDouble(viewTarget.z);
22032 buf->putBitDouble(viewDir.x);
22033 buf->putBitDouble(viewDir.y);
22034 buf->putBitDouble(viewDir.z);
22035 buf->putBitDouble(twistAngle);
22036 buf->putBitDouble(viewHeight);
22037 buf->putBitDouble(viewLength); // lens length
22038 buf->putBitDouble(frontClip);
22039 buf->putBitDouble(backClip);
22040 buf->putBitDouble(snapAngle);
22041 buf->putRawDouble(centerPX);
22042 buf->putRawDouble(centerPY);
22043 buf->putRawDouble(snapPX);
22044 buf->putRawDouble(snapPY);
22045 buf->putRawDouble(snapSpPX);
22046 buf->putRawDouble(snapSpPY);
22047 buf->putRawDouble(gridSpX);
22048 buf->putRawDouble(gridSpY);
22049 buf->putBitShort(static_cast<std::uint16_t>(circleZoom));
22050 }
22051
22052 if (version > DRW::AC1018)
22053 buf->putBitShort(static_cast<std::uint16_t>(majorGridLines));
22054
22055 if (version > DRW::AC1014) {
22056 buf->putBitLong(static_cast<std::int32_t>(frozenLayerHandles.size()));
22057 buf->putBitLong(statusFlags);
22058 sb->putVariableText(version, styleSheet);
22059 buf->putRawChar8(static_cast<std::uint8_t>(renderMode));
22060 buf->putBit(ucsAtOrigin ? 1 : 0);
22061 buf->putBit(ucsPerViewport ? 1 : 0);
22062 buf->put3BitDouble(ucsOrigin);
22063 buf->put3BitDouble(ucsXAxis);
22064 buf->put3BitDouble(ucsYAxis);
22065 buf->putBitDouble(ucsElevation);
22066 buf->putBitShort(static_cast<std::uint16_t>(ucsOrthographicType));
22067 }
22068
22069 if (version > DRW::AC1015)
22070 buf->putBitShort(static_cast<std::uint16_t>(shadePlotMode));
22071
22072 if (version > DRW::AC1018) {
22073 buf->putBit(useDefaultLighting ? 1 : 0);
22074 buf->putRawChar8(defaultLightingType);
22075 buf->putBitDouble(brightness);
22076 buf->putBitDouble(contrast);
22077 if (ambientColorRgb >= 0) {
22078 buf->putCmColor(version, static_cast<std::uint16_t>(ambientColor),
22079 ambientColorRgb, ambientColorName, "", strBuf);
22080 } else {
22081 buf->putCmColor(version, static_cast<std::uint16_t>(ambientColor));
22082 }
22083 }
22084
22085 if (!encodeDwgEntHandle(version, buf, handleBuf)) return false;
22086
22087 dwgBufferW *hb = (handleBuf != nullptr) ? handleBuf : buf;
22088
22089 if (version < DRW::AC1015) {
22090 putTypedHandle(hb, vpHeaderHandle, 5);
22091 }
22092 if (version > DRW::AC1014) {
22093 const std::uint8_t frozenCode = version == DRW::AC1015 ? 5 : 4;
22094 for (const std::uint32_t ref : frozenLayerHandles)
22095 putTypedHandle(hb, ref, frozenCode);
22096 putTypedHandle(hb, clipBoundaryHandle, 4);
22097 if (version == DRW::AC1015)
22098 putTypedHandle(hb, vpHeaderHandle, 5);
22099 putTypedHandle(hb, namedUcsHandle, 5);
22100 putTypedHandle(hb, baseUcsHandle, 5);
22101 }
22102 if (version > DRW::AC1018) {
22103 putTypedHandle(hb, backgroundHandle, 4);
22104 putTypedHandle(hb, visualStyleHandle, 5);
22105 putTypedHandle(hb, shadePlotHandle, 4);
22106 putTypedHandle(hb, m_sunHandle, 3);
22107 }
22108
22109 return buf->isGood() && sb->isGood() && hb->isGood();
22110}