Skip to content

Issue/1131 Tangent entities - #1337

Open
DomCR wants to merge 10 commits into
masterfrom
issue/1131_tarch-tch-custom-entities
Open

DomCR wants to merge 10 commits into
masterfrom
issue/1131_tarch-tch-custom-entities

Conversation

@DomCR

@DomCR DomCR commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Description

Transfer PR for:

Tasks done:

  • Code cleanup
  • Refactor and standardize the reader.
  • Stablish the data structure of the TEXT entities as a placeholder.

DomCR and others added 4 commits August 19, 2026 16:10
Tianzheng (TArch) draws its architectural objects as ObjectARX custom objects: a
wall, a stair and a dimension are each one object of a class ACadSharp has no
reader for, so every one of them arrives as an UnknownEntity and the drawing
loses its content. In China most architectural drawings are made this way.

This reads the classes whose object is text -- TCH_TEXT, TCH_MTEXT and
TCH_DRAWINGNAME -- and converts them to TextEntity. On the sample drawing in
issue #1131 that is 13 objects, and all 13 come out with the value, the position
and the height that the drawing's own flattened export draws them at. The other
31 objects are left whole and named, so nothing is dropped.

The reading is in the reader. `DwgObjectReader.Tangent` recognises the classes,
takes the object's data and fills a `TchText`; the entity holds fields and
converts them, and never sees the bytes. That split is forced by the layout
rather than chosen: these classes write their numbers as text into a string
stream, and the stream is described from the end of the object rather than
stepped to, so the reader has to see the object whole before it can hand over
fields. Walking the filer straight through it does not reach them.

The types live in `Entities/Tangent` and the readers in `IO/DWG/Tangent`, which
is where the AEC and Mechanical support puts its own. One `case TchEntity:`
covers every class in both writers, so the next class added needs no new line
there, and the reflection-driven tests skip the base type for the same reason.
Each fixture is one object's class specific data, taken from the sample drawing
byte for byte, so the tests need no drawing to run and no folder to be configured.

What they pin is what the export draws: the position of `Wall & Window & Door`
to four places, its height at 350 where the object records 3.5 and the drawing is
at 1:100, the content of a single line text, of a multiline one with its line
breaks, and of a title block's name. A multiline text is the case that read as
nothing for a while, because the filter that told text from a field rejected the
line breaks while every single line text still came through.
feat: read native Tianzheng text objects
@DomCR DomCR added feature New feature added minor Updates the minor version on merge. labels Oct 24, 2026
DomCR added 4 commits October 24, 2026 12:38
Refactor Tianzheng entities around a documented base class and
standard-entity conversion contract. Enhance text parsing, transforms,
scale handling, class preservation, and DWG support, with expanded tests.
Add shared Tianzheng metadata parsing and support multiline text and drawing-name entities. Rework class-specific text parsing, register new object types, validate malformed data, and obsolete the legacy text reader.
- Dispatch Tianzheng objects by DXF class via `readTangentObject`
- Parse fixed headers and encrypted text attributes for `TCH_TEXT`
- Warn on invalid or undecodable ciphertext values
- Limit tangent detection to objects without standard templates
- Remove legacy generalized parsing and MText/DrawingName support
- Relocate `IDwgStreamReader` documentation without API changes
Introduce `TchMText` and centralized Tianzheng DXF class names. Extend DWG reading and dispatch to parse multiline text objects, validate headers and data, and emit warnings for malformed content.
@DomCR

DomCR commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Tianzheng (TArch) TCH_TEXT / TCH_MTEXT – Findings

1. AC1021 sample (issue-1131_TZ.dwg)

Object stream (after readCommonEntityData)

  • Fixed 82-bit header: 0A 11 90 55 05 90 39 03 81 04 + 2 bits 01
    • Identical for TCH_TEXT (828, 829, 935) and TCH_MTEXT (949), so it's a shared Tianzheng header.
    • Reading it as BitLong, BitShort or BitDouble gives no sensible values. Meaning unknown.
  • Insertion point: 3 BitDoubles in the order Y, 0, X at offset +82 (all 12 entities).

String stream (numbers are TgDataEncrypt ciphertext)

Field TCH_TEXT (11) TCH_MTEXT (949)
ClassId {F8F0F12F-…} {DF708B01-…}
Scale 100 100
unknown double / int 0 / 0 0 / 0
TDBXDATAMAP_BLOCK_BEGIN_ … END_ T20d1..T20d4 T20d1..T20d4
StyleId {B0713E6B-…} same
unknown0 0 0
Value text multiline (\r\n)
Height 3.5 3.5
rotation? 0 0
unknown1 7 10
unknown2 0 0
Width – 200
LineSpacingFactor – 1.4
  • Both classes use the same layout up to unknown2. MText adds Width and LineSpacingFactor.
  • unknown1 depends on the class: alignment code or version number per class.
  • Rotation and the other unknowns are 0 in every sample, so they can't be identified yet.

2. AC1027 sample (TCH_TEXT handle 19)

How the DWG reader handles the two versions

AC1021 AC1027 (R2010+)
String stream located updateHandleReader() using the object-size (RL) field getEntityType() using the MC handle-stream size
Common entity data R2007 fields + visual style bits

Standard entities read correctly in both versions, so the common data is aligned.

Layout of handle 19

  • Object data after the common entity data: 312 bits
    07 34 E7 D6 CE D5 6A 18 50 20 08 08 FD 19 E1 C4 EC 20 21 42
    1E 22 8F E1 20 D4 30 90 60 00 00 00 00 00 00 51 02 05 00
    
  • String stream: only TDBXDATAMAP_BLOCK_BEGIN_ and TDBXDATAMAP_BLOCK_END_ (empty DataMap).
  • 19 bits remain before the handle section.
  • Different header, no point at +82, no ClassId, ciphertext, StyleId or text value.
  • The binary-looking strings and the OverflowException from an earlier run came from reading past the end of the string stream.

3. TArch version issue

AC1021 file AC1027 file
Header 0A 11 90 55 … 07 34 E7 D6 …
Point Y/0/X at +82 not found
String preamble ClassId, Scale, … none
Text content in the string stream none

Possible causes (not confirmed):

  1. Different TArch versions wrote each file. The format depends on the TArch release, and the header acts as a version marker.
  2. TArch switches layout by target DWG version in its own dwgOutFields.
  3. Handle 19 isn't a drawn text. It's early in the file, with an empty DataMap and no content, so it may be a template or default object.

The reader currently supports only the AC1021 TArch layout if it's aligned with the dwg version and not the TArch app version. With other files it produces wrong values or reads past the end of the string stream.

This may be a critical issue when reading the entities, if TArch has multiple formats for the different version that makes the job of reading the value much harder and the stability cannot be granted.

@DomCR DomCR self-assigned this Oct 25, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature added minor Updates the minor version on merge.

Projects

None yet

2 participants