Files
pdm/creoparams/drawing.py
T

101 lines
3.2 KiB
Python
Raw Normal View History

"""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 <a> <b> <testo> 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,
}