"""Lettura del contenuto dei disegni Creo (`.drw`). Un disegno non contiene la tabella parametri del modello: contiene un blocco `PictureObj`, compresso con compress(1), con la rappresentazione vettoriale della tavola. Dentro ci sono le primitive grafiche (linee, archi, polilinee, testi con font, colore e rotazione) e, soprattutto, **tutti i testi**: voci del cartiglio, note, tolleranze. Qui estraiamo i testi, che rendono le tavole ricercabili per contenuto. La geometria e' presente e decodificabile (le coordinate sono double IEEE con i byte bassi omessi) ma il suo disegno non e' implementato. I testi seguono uno schema regolare: 18 e3 e3 43 00 dove il secondo byte vale 2*len+2. Verifichiamo la lunghezza dichiarata contro quella reale: e' un controllo a costo nullo che scarta i falsi riscontri. """ from __future__ import annotations import re from . import ugc TEXT_MARK = b"\x18\xe3\xe3\x43" MAX_TEXT = 512 _STRING_BYTES = frozenset( set(range(0x20, 0x7F)) | (set(range(0xA0, 0x100)) - {0xE1, 0xE3, 0xF6, 0xF7}) ) #: Primitive grafiche note, utili per descrivere il contenuto della tavola. _PRIMITIVE_RE = re.compile(rb"value\((prim_[a-z_]+)\)") def _decode(raw: bytes) -> str: try: return raw.decode("utf-8") except UnicodeDecodeError: return raw.decode("cp1252", errors="replace") def iter_texts(blob: bytes) -> "list[str]": """Testi presenti in un blocco PGL decompresso, in ordine di comparsa.""" texts, pos = [], 0 while True: pos = blob.find(TEXT_MARK, pos) if pos < 0: return texts start = pos + len(TEXT_MARK) + 2 declared = blob[pos + len(TEXT_MARK) + 1] end = start limit = min(len(blob), start + MAX_TEXT) while end < limit and blob[end] != 0x00: if blob[end] not in _STRING_BYTES: break end += 1 length = end - start # Lo schema dichiara 2*len+2: se non torna, non e' un testo. if length and end < limit and blob[end] == 0x00 and declared == (2 * length + 2) % 256: texts.append(_decode(blob[start:end])) pos = end else: pos += 1 #: Blocchi che contengono la rappresentazione grafica della tavola. GRAPHIC_BLOCKS = ("PictureObj", "PicturePrimdata", "PicturePersistTable") def read(data: bytes) -> dict: """Estrae testi e primitive dai blocchi grafici della tavola.""" blocks = [b for b in ugc.iter_blocks(data) if b[0].split("#", 1)[0] in GRAPHIC_BLOCKS] blob = b"".join(payload for _, payload, _ in blocks) compressed = sum(1 for _, _, was_compressed in blocks if was_compressed) texts = iter_texts(blob) seen, unique = set(), [] for text in texts: stripped = text.strip() if stripped and stripped not in seen: seen.add(stripped) unique.append(stripped) primitives = sorted({m.decode() for m in _PRIMITIVE_RE.findall(blob)}) return { "graphic_blocks": len(blocks), "compressed_blocks": compressed, "decompressed_bytes": len(blob), "primitives": primitives, "text_count": len(texts), "texts": unique, }