refactor: excel parse
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
# Copyright (c) 2016-2022, Manfred Moitzi
|
||||
# License: MIT License
|
||||
from __future__ import annotations
|
||||
from typing import (
|
||||
Iterable,
|
||||
Optional,
|
||||
TYPE_CHECKING,
|
||||
Sequence,
|
||||
Any,
|
||||
Iterator,
|
||||
)
|
||||
from functools import partial
|
||||
import logging
|
||||
from .tags import DXFTag
|
||||
from .types import POINT_CODES, NONE_TAG, VALID_XDATA_GROUP_CODES
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ezdxf.eztypes import Tags
|
||||
|
||||
logger = logging.getLogger("ezdxf")
|
||||
|
||||
|
||||
def tag_reorder_layer(tagger: Iterable[DXFTag]) -> Iterator[DXFTag]:
|
||||
"""Reorder coordinates of legacy DXF Entities, for now only LINE.
|
||||
|
||||
Input Raw tag filter.
|
||||
|
||||
Args:
|
||||
tagger: low level tagger
|
||||
|
||||
"""
|
||||
|
||||
collector: Optional[list] = None
|
||||
for tag in tagger:
|
||||
if tag.code == 0:
|
||||
if collector is not None:
|
||||
# stop collecting if inside a supported entity
|
||||
entity = _s(collector[0].value)
|
||||
yield from COORDINATE_FIXING_TOOLBOX[entity](collector) # type: ignore
|
||||
collector = None
|
||||
|
||||
if _s(tag.value) in COORDINATE_FIXING_TOOLBOX:
|
||||
collector = [tag]
|
||||
# do not yield collected tag yet
|
||||
tag = NONE_TAG
|
||||
else: # tag.code != 0
|
||||
if collector is not None:
|
||||
collector.append(tag)
|
||||
# do not yield collected tag yet
|
||||
tag = NONE_TAG
|
||||
if tag is not NONE_TAG:
|
||||
yield tag
|
||||
|
||||
|
||||
# invalid point codes if not part of a point started with 1010, 1011, 1012, 1013
|
||||
INVALID_Y_CODES = {code + 10 for code in POINT_CODES}
|
||||
INVALID_Z_CODES = {code + 20 for code in POINT_CODES}
|
||||
# A single group code 38 is an elevation tag (e.g. LWPOLYLINE)
|
||||
# Is (18, 28, 38?) is a valid point code?
|
||||
INVALID_Z_CODES.remove(38)
|
||||
INVALID_CODES = INVALID_Y_CODES | INVALID_Z_CODES
|
||||
X_CODES = POINT_CODES
|
||||
|
||||
|
||||
def filter_invalid_point_codes(tagger: Iterable[DXFTag]) -> Iterable[DXFTag]:
|
||||
"""Filter invalid and misplaced point group codes.
|
||||
|
||||
- removes x-axis without following y-axis
|
||||
- removes y- and z-axis without leading x-axis
|
||||
|
||||
Args:
|
||||
tagger: low level tagger
|
||||
|
||||
"""
|
||||
|
||||
def entity() -> str:
|
||||
if handle_tag:
|
||||
return f"in entity #{_s(handle_tag[1])}"
|
||||
else:
|
||||
return ""
|
||||
|
||||
expected_code = -1
|
||||
z_code = 0
|
||||
point: list[Any] = []
|
||||
handle_tag = None
|
||||
for tag in tagger:
|
||||
code = tag[0]
|
||||
if code == 5: # ignore DIMSTYLE entity
|
||||
handle_tag = tag
|
||||
if point and code != expected_code:
|
||||
# at least x, y axis is required else ignore point
|
||||
if len(point) > 1:
|
||||
yield from point
|
||||
else:
|
||||
logger.info(
|
||||
f"remove misplaced x-axis tag: {str(point[0])}" + entity()
|
||||
)
|
||||
point.clear()
|
||||
|
||||
if code in X_CODES:
|
||||
expected_code = code + 10
|
||||
z_code = code + 20
|
||||
point.append(tag)
|
||||
elif code == expected_code:
|
||||
point.append(tag)
|
||||
expected_code += 10
|
||||
if expected_code > z_code:
|
||||
expected_code = -1
|
||||
else:
|
||||
# ignore point group codes without leading x-axis
|
||||
if code not in INVALID_CODES:
|
||||
yield tag
|
||||
else:
|
||||
axis = "y-axis" if code in INVALID_Y_CODES else "z-axis"
|
||||
logger.info(
|
||||
f"remove misplaced {axis} tag: {str(tag)}" + entity()
|
||||
)
|
||||
|
||||
if len(point) == 1:
|
||||
logger.info(f"remove misplaced x-axis tag: {str(point[0])}" + entity())
|
||||
elif len(point) > 1:
|
||||
yield from point
|
||||
|
||||
|
||||
def fix_coordinate_order(tags: Tags, codes: Sequence[int] = (10, 11)):
|
||||
def extend_codes():
|
||||
for code in codes:
|
||||
yield code # x tag
|
||||
yield code + 10 # y tag
|
||||
yield code + 20 # z tag
|
||||
|
||||
def get_coords(code: int):
|
||||
# if x or y coordinate is missing, it is a DXFStructureError
|
||||
# but here is not the location to validate the DXF structure
|
||||
try:
|
||||
yield coordinates[code]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
yield coordinates[code + 10]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
yield coordinates[code + 20]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
coordinate_codes = frozenset(extend_codes())
|
||||
coordinates = {}
|
||||
remaining_tags = []
|
||||
insert_pos = None
|
||||
for tag in tags:
|
||||
# separate tags
|
||||
if tag.code in coordinate_codes:
|
||||
coordinates[tag.code] = tag
|
||||
if insert_pos is None:
|
||||
insert_pos = tags.index(tag)
|
||||
else:
|
||||
remaining_tags.append(tag)
|
||||
|
||||
if len(coordinates) == 0:
|
||||
# no coordinates found, this is probably a DXFStructureError,
|
||||
# but here is not the location to validate the DXF structure,
|
||||
# just do nothing.
|
||||
return tags
|
||||
|
||||
ordered_coords = []
|
||||
for code in codes:
|
||||
ordered_coords.extend(get_coords(code))
|
||||
remaining_tags[insert_pos:insert_pos] = ordered_coords
|
||||
return remaining_tags
|
||||
|
||||
|
||||
COORDINATE_FIXING_TOOLBOX = {
|
||||
"LINE": partial(fix_coordinate_order, codes=(10, 11)),
|
||||
}
|
||||
|
||||
|
||||
def filter_invalid_xdata_group_codes(
|
||||
tags: Iterable[DXFTag],
|
||||
) -> Iterator[DXFTag]:
|
||||
return (tag for tag in tags if tag.code in VALID_XDATA_GROUP_CODES)
|
||||
|
||||
|
||||
def filter_invalid_handles(tags: Iterable[DXFTag]) -> Iterator[DXFTag]:
|
||||
line = -1
|
||||
handle_code = 5
|
||||
structure_tag = ""
|
||||
for tag in tags:
|
||||
line += 2
|
||||
if tag.code == 0:
|
||||
structure_tag = tag.value
|
||||
if _s(tag.value) == "DIMSTYLE":
|
||||
handle_code = 105
|
||||
else:
|
||||
handle_code = 5
|
||||
elif tag.code == handle_code:
|
||||
try:
|
||||
int(tag.value, 16)
|
||||
except ValueError:
|
||||
logger.warning(
|
||||
f'skipped invalid handle "{_s(tag.value)}" in '
|
||||
f'DXF entity "{_s(structure_tag)}" near line {line}'
|
||||
)
|
||||
continue
|
||||
yield tag
|
||||
|
||||
|
||||
def _s(b) -> str:
|
||||
if isinstance(b, bytes):
|
||||
return b.decode(encoding="ascii", errors="ignore")
|
||||
return b
|
||||
Reference in New Issue
Block a user