add implementation
This commit is contained in:
parent
1d437f498c
commit
cac8f9dade
7
__init__.py
Normal file
7
__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from .library import Library
|
||||||
|
from .structure import Structure
|
||||||
|
from .elements import *
|
||||||
|
|
||||||
|
from .record import *
|
||||||
|
from .reader import Reader
|
||||||
|
from .parser import *
|
116
elements.py
Normal file
116
elements.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Transformation(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.mirror_x = 0
|
||||||
|
|
||||||
|
self.absolute_rotation = 0
|
||||||
|
self.absolute_magnification = 0
|
||||||
|
|
||||||
|
self.zoom = 1
|
||||||
|
self.rotation = 0
|
||||||
|
|
||||||
|
class Element(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.elflags = 0
|
||||||
|
self.plex = 0
|
||||||
|
|
||||||
|
class Drawable(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.layer = 0
|
||||||
|
self.datatype = 0
|
||||||
|
|
||||||
|
class Boundary(Element, Drawable):
|
||||||
|
def __init__(self):
|
||||||
|
self.points = []
|
||||||
|
|
||||||
|
class Path(Element, Drawable):
|
||||||
|
class Styles(Enum):
|
||||||
|
SQUARE_ENDS = 0
|
||||||
|
ROUNDED_ENDS = 1
|
||||||
|
OFFSET_ENDS = 2
|
||||||
|
CUSTOM_END = 4
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.extendEnd = [0,0] # extend past start and end
|
||||||
|
self.width = 0
|
||||||
|
self.pathStyle = Path.Styles.SQUARE_ENDS
|
||||||
|
|
||||||
|
self.points = []
|
||||||
|
|
||||||
|
class Text(Element, Drawable):
|
||||||
|
class VJust(Enum):
|
||||||
|
Top = 0
|
||||||
|
Middle = 1
|
||||||
|
Bottom = 2
|
||||||
|
|
||||||
|
class HJust(Enum):
|
||||||
|
Left = 0
|
||||||
|
Center = 1
|
||||||
|
Right = 2
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# text info
|
||||||
|
self.string = ""
|
||||||
|
self.position = (0,0)
|
||||||
|
|
||||||
|
# presentation
|
||||||
|
self.fontnumber = 0
|
||||||
|
self.verticalJustification = Text.VJust.Top
|
||||||
|
self.horizontalJustification = Text.HJust.Left
|
||||||
|
|
||||||
|
# optional path info
|
||||||
|
self.pathStype = Path.Styles.SQUARE_ENDS
|
||||||
|
self.pathWidth = 0
|
||||||
|
|
||||||
|
self.transformation = Transformation()
|
||||||
|
|
||||||
|
|
||||||
|
class Box(Element, Drawable):
|
||||||
|
def __init__(self):
|
||||||
|
self.points = []
|
||||||
|
|
||||||
|
class SRef(Element):
|
||||||
|
"""
|
||||||
|
A Structure Reference defines a single instance of a different structure in
|
||||||
|
the structure we call parent here
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.position = (0,0)
|
||||||
|
self.structure = ""
|
||||||
|
|
||||||
|
self.transformation = Transformation()
|
||||||
|
|
||||||
|
self.parent = None
|
||||||
|
|
||||||
|
class ARef(SRef):
|
||||||
|
"""
|
||||||
|
An Array Reference is similar to the SRef, but it defines the instances in a grid
|
||||||
|
with size instances spaces evenly between the bound coordinates
|
||||||
|
|
||||||
|
with:
|
||||||
|
|
||||||
|
v1 = bounds[0] - position
|
||||||
|
v2 = bounds[1] - position
|
||||||
|
i = xth instance index
|
||||||
|
j = yth instance index
|
||||||
|
|
||||||
|
this will result in an array like this:
|
||||||
|
j\i | 0 | 1 | ... | size[0]
|
||||||
|
----+-----------------+---------------------------+-----+--------------------
|
||||||
|
0 | (position) | (position + v1) | ... | (bounds[0])
|
||||||
|
1 | (position + v2) | (position + v1*i + v2*j) | ... | (bounds[0] + v2)
|
||||||
|
... | ... | ... | ... | ...
|
||||||
|
size[1] | (bounds[2]) | (bounds[2] + v1) | ... | (bounds[0] + bounds[1])
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
super(ARef, self).__init__()
|
||||||
|
|
||||||
|
# positions of last instance in X and Y direction
|
||||||
|
self.bounds = [(0,0), (0,0)]
|
||||||
|
# number of instances in X and Y direction
|
||||||
|
self.size = (0,0)
|
||||||
|
|
||||||
|
|
||||||
|
|
81
library.py
Normal file
81
library.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from .elements import SRef
|
||||||
|
from .reader import ProgressGetter
|
||||||
|
|
||||||
|
class Library(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.version = 0
|
||||||
|
self.name = "NONAME"
|
||||||
|
|
||||||
|
self.last_access = datetime.now()
|
||||||
|
self.last_mod = datetime.now()
|
||||||
|
|
||||||
|
# unit setup
|
||||||
|
self.units_per_dbunit = 1
|
||||||
|
self.meters_per_unit = 1
|
||||||
|
|
||||||
|
self.structures = {}
|
||||||
|
|
||||||
|
class LinkError(Exception):
|
||||||
|
element = None
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
normalizes given coordinate according to library units
|
||||||
|
"""
|
||||||
|
def normalize_coord(self, coord, to_meters=False):
|
||||||
|
fact = self.units_per_dbunit
|
||||||
|
if to_meters:
|
||||||
|
fact *= self.meters_per_unit
|
||||||
|
|
||||||
|
return (coord[0] * fact, coord[1] * fact)
|
||||||
|
|
||||||
|
def link_refs(self, root, progress_callback=None):
|
||||||
|
for ref in root.references:
|
||||||
|
if isinstance(ref.structure, str):
|
||||||
|
try:
|
||||||
|
element.structure = self.structures[ref.structure]
|
||||||
|
except KeyError:
|
||||||
|
err = LinkError("dangeling sref (structure {} is not defined in library)".format(ref.structure))
|
||||||
|
err.element = root
|
||||||
|
raise err
|
||||||
|
|
||||||
|
return root
|
||||||
|
|
||||||
|
def link_all_refs(self, progress_callback=None):
|
||||||
|
class Progress(ProgressGetter):
|
||||||
|
total = 0
|
||||||
|
current = 0
|
||||||
|
|
||||||
|
def __init__(self, lib):
|
||||||
|
for key, value in lib.structures.items():
|
||||||
|
self.total += len(value.references)
|
||||||
|
|
||||||
|
def progress(self):
|
||||||
|
return float(self.current) / self.total
|
||||||
|
|
||||||
|
def inc(self):
|
||||||
|
self.current += 1
|
||||||
|
|
||||||
|
count = Progress(self)
|
||||||
|
|
||||||
|
for key, value in self.structures.items():
|
||||||
|
for element in value.references:
|
||||||
|
if isinstance(element.structure, str):
|
||||||
|
# try to resolve link
|
||||||
|
try:
|
||||||
|
ref = self.structures[element.structure]
|
||||||
|
element.structure = ref
|
||||||
|
except KeyError:
|
||||||
|
err = LinkError("dangeling sref (structure {} is not defined in library)".format(element.structure))
|
||||||
|
err.element = element
|
||||||
|
|
||||||
|
raise err
|
||||||
|
|
||||||
|
count.inc()
|
||||||
|
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(count)
|
||||||
|
|
||||||
|
|
407
parser.py
Normal file
407
parser.py
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
from . import *
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class errors(Enum):
|
||||||
|
UNEXPECTED_EOF = "unexpected EOF"
|
||||||
|
EXPECTED_HEADER = "expected file header"
|
||||||
|
EXPECTED_BGNLIB = "expected beginning of library"
|
||||||
|
EXPECTED_LIBNAME = "Library name is missing"
|
||||||
|
EXPECTED_ENDMASK = "MASK records was not terminated by ENDMASK"
|
||||||
|
EXPECTED_STRNAME = "encountered nameless structure"
|
||||||
|
EXPECTED_UNITS = "no unit record in file"
|
||||||
|
EXPECTED_LAYER = "expected a layer record in element"
|
||||||
|
EXPECTED_POINTS = "expected coordinates for element"
|
||||||
|
EXPECTED_POINT = "expected coordinate for element"
|
||||||
|
EXPECTED_DATATYPE = "expected datatype"
|
||||||
|
EXPECTED_ENDEL = "end of element is missing"
|
||||||
|
EXPECTED_TEXTTYPE = "expected TEXTTYPE"
|
||||||
|
EXPECTED_STRING = "textelement is missing text"
|
||||||
|
EXPECTED_BOXTYPE = "expected BOXTYPE"
|
||||||
|
EXPECTED_SNAME = "expected SNAME"
|
||||||
|
EXPECTED_COLROW = "expected COLROW"
|
||||||
|
AREF_MISSING_POINTS = "aref needs three points"
|
||||||
|
INVALID_PATHTYPE = "pathtype must be in range [0,2]"
|
||||||
|
|
||||||
|
class Warnings(Enum):
|
||||||
|
EXPECTED_ENDLIB = "missing end of library"
|
||||||
|
|
||||||
|
class ParserError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Parser(Reader):
|
||||||
|
def __init__(self, file, progress_callback=None):
|
||||||
|
super(Parser, self).__init__(file)
|
||||||
|
|
||||||
|
self._token = None
|
||||||
|
self.progress_callback = progress_callback
|
||||||
|
|
||||||
|
# parser state
|
||||||
|
self.Library = Library()
|
||||||
|
self.current_structure = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def token(self):
|
||||||
|
return self._token
|
||||||
|
|
||||||
|
@token.setter
|
||||||
|
def token(self, t):
|
||||||
|
self._token = t
|
||||||
|
|
||||||
|
def next_token(self, throw=False):
|
||||||
|
self.token = self.read_record()
|
||||||
|
|
||||||
|
if not self.token and throw:
|
||||||
|
raise ParserError(errors.UNEXPECTED_EOF)
|
||||||
|
|
||||||
|
# if self.token:
|
||||||
|
# print(self.token.ident.name)
|
||||||
|
|
||||||
|
return self.token
|
||||||
|
|
||||||
|
def parse_lib(self):
|
||||||
|
# read header
|
||||||
|
if not self.next_token() or self.token.ident != Records.HEADER:
|
||||||
|
raise ParserError(errors.EXPECTED_HEADER)
|
||||||
|
|
||||||
|
self.Library.version = self.read_short()
|
||||||
|
|
||||||
|
# must see BGNLIB
|
||||||
|
if not self.next_token() or self.token.ident != Records.BGNLIB:
|
||||||
|
raise ParserError(errors.EXPECTED_BGNLIB)
|
||||||
|
|
||||||
|
self.Library.last_mod = self.read_date()
|
||||||
|
self.Library.last_access = self.read_date()
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.LIBNAME:
|
||||||
|
raise ParserError(errors.EXPECTED_LIBNAME)
|
||||||
|
|
||||||
|
self.Library.name = self.read_ascii(self.token.len)
|
||||||
|
|
||||||
|
# read optional records
|
||||||
|
while self.next_token():
|
||||||
|
if self.token.ident == Records.REFLIBS or \
|
||||||
|
self.token.ident == Records.FONTS or \
|
||||||
|
self.token.ident == Records.ATTRTABLE or \
|
||||||
|
self.token.ident == Records.GENERATIONS:
|
||||||
|
# skip these records
|
||||||
|
self.skip(self.token.len)
|
||||||
|
|
||||||
|
elif self.token.ident == Records.FORMAT:
|
||||||
|
self.skip(self.token.len)
|
||||||
|
|
||||||
|
# look for optional mask
|
||||||
|
while self.next_token().ident == Records.MASK:
|
||||||
|
self.skip(self.token.len)
|
||||||
|
|
||||||
|
if self.token.ident != Records.ENDMASK:
|
||||||
|
raise ParserError(errors.EXPECTED_ENDMASK)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# continue
|
||||||
|
break
|
||||||
|
|
||||||
|
if self.token.ident != Records.UNITS:
|
||||||
|
raise ParserError(errors.EXPECTED_UNITS)
|
||||||
|
|
||||||
|
# read units
|
||||||
|
self.Library.units_per_dbunit = self.read_double()
|
||||||
|
self.Library.meters_per_unit = self.read_double()
|
||||||
|
|
||||||
|
while self.next_token() and self.parse_structure():
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.token.ident != Records.ENDLIB:
|
||||||
|
print(Warnings.EXPECTED_ENDLIB.value)
|
||||||
|
|
||||||
|
# tell callback that the process completed
|
||||||
|
if self.progress_callback:
|
||||||
|
self.progress_callback(self)
|
||||||
|
|
||||||
|
def parse_structure(self):
|
||||||
|
self.structure = Structure()
|
||||||
|
|
||||||
|
if self.token.ident != Records.BGNSTR:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.structure.last_mod = self.read_date()
|
||||||
|
self.structure.last_access = self.read_date()
|
||||||
|
|
||||||
|
# read sname
|
||||||
|
if not self.next_token() or self.token.ident != Records.STRNAME:
|
||||||
|
raise ParserError(errors.EXPECTED_STRNAME)
|
||||||
|
|
||||||
|
self.structure.name = self.read_ascii(self.token.len)
|
||||||
|
|
||||||
|
while self.next_token() and self.token.ident != Records.ENDSTR:
|
||||||
|
if self.token.ident == Records.BOUNDARY:
|
||||||
|
self.parse_boundary()
|
||||||
|
elif self.token.ident == Records.PATH:
|
||||||
|
self.parse_path()
|
||||||
|
elif self.token.ident == Records.TEXT:
|
||||||
|
self.parse_text()
|
||||||
|
elif self.token.ident == Records.SREF:
|
||||||
|
self.parse_sref()
|
||||||
|
elif self.token.ident == Records.AREF:
|
||||||
|
self.parse_aref()
|
||||||
|
else:
|
||||||
|
self.skip(self.token.len)
|
||||||
|
|
||||||
|
if self.progress_callback:
|
||||||
|
self.progress_callback(self)
|
||||||
|
|
||||||
|
self.Library.structures[self.structure.name] = self.structure
|
||||||
|
self.structure = None
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def parse_element(self, element):
|
||||||
|
if self.token.ident == Records.ELFLAGS:
|
||||||
|
element.elflags = self.read_short()
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
if self.token.ident == Records.PLEX:
|
||||||
|
element.plex = self.read_int()
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
def parse_layer(self, element):
|
||||||
|
if self.token.ident != Records.LAYER:
|
||||||
|
raise ParserError(errors.EXPECTED_LAYER)
|
||||||
|
|
||||||
|
element.layer = self.read_ushort()
|
||||||
|
|
||||||
|
def parse_boundary(self):
|
||||||
|
element = Boundary()
|
||||||
|
self.next_token(True)
|
||||||
|
self.parse_element(element)
|
||||||
|
self.parse_layer(element)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.DATATYPE:
|
||||||
|
raise ParserError(errors.EXPECTED_DATATYPE)
|
||||||
|
|
||||||
|
element.datatype = self.read_short()
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.XY:
|
||||||
|
raise ParserError(errors.EXPECTED_POINTS)
|
||||||
|
|
||||||
|
element.points = self.read_coords(self.token.len)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.ENDEL:
|
||||||
|
raise ParserError(errors.EXPECTED_ENDEL)
|
||||||
|
|
||||||
|
self.structure.elements.append(element)
|
||||||
|
|
||||||
|
def parse_path(self):
|
||||||
|
element = Path()
|
||||||
|
self.next_token(True)
|
||||||
|
self.parse_element(element)
|
||||||
|
self.parse_layer(element)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.DATATYPE:
|
||||||
|
raise ParserError(errors.EXPECTED_DATATYPE)
|
||||||
|
|
||||||
|
element.datatype = self.read_short()
|
||||||
|
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
if self.token.ident == Records.PATHTYPE:
|
||||||
|
pathtype = self.read_short()
|
||||||
|
if pathtype < 0 or pathtype > 4 or pathtype == 3:
|
||||||
|
raise ParserError(errors.INVALID_PATHTYPE)
|
||||||
|
|
||||||
|
element.pathStyle = Path.Styles(pathtype)
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
if self.token.ident == Records.WIDTH:
|
||||||
|
element.width = self.read_int()
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
if self.token.ident == Records.BGNEXTN:
|
||||||
|
element.extendEnd[0] = self.read_int()
|
||||||
|
self.next_token(True)
|
||||||
|
elif element.pathStyle == Path.Styles.OFFSET_ENDS:
|
||||||
|
element.extendEnd[0] = element.width/2
|
||||||
|
|
||||||
|
if self.token.ident == Records.ENDEXTN:
|
||||||
|
element.extendEnd[1] = self.read_int()
|
||||||
|
self.next_token(True)
|
||||||
|
elif element.pathStyle == Path.Styles.OFFSET_ENDS:
|
||||||
|
element.extendEnd[1] = element.width/2
|
||||||
|
|
||||||
|
if self.token.ident != Records.XY:
|
||||||
|
raise ParserError(errors.EXPECTED_POINTS)
|
||||||
|
|
||||||
|
element.points = self.read_coords(self.token.len)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.ENDEL:
|
||||||
|
raise ParserError(errors.EXPECTED_ENDEL)
|
||||||
|
|
||||||
|
self.structure.elements.append(element)
|
||||||
|
|
||||||
|
def parse_text(self):
|
||||||
|
element = Text()
|
||||||
|
self.next_token(True)
|
||||||
|
self.parse_element(element)
|
||||||
|
self.parse_layer(element)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.TEXTTYPE:
|
||||||
|
raise ParserError(errors.EXPECTED_TEXTTYPE)
|
||||||
|
|
||||||
|
element.datatype = self.read_short()
|
||||||
|
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
if self.token.ident == Records.PRESENTATION:
|
||||||
|
temp = self.read_short()
|
||||||
|
element.fontnumber = (temp>>10)&0x03
|
||||||
|
element.verticalJustification = Text.VJust((temp>>12)&0x03)
|
||||||
|
element.horizontalJustification = Text.HJust((temp>>14)&0X03)
|
||||||
|
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
if self.token.ident == Records.PATHTYPE:
|
||||||
|
element.pathStype = Path.Styles(self.read_short())
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
if self.token.ident == Records.WIDTH:
|
||||||
|
element.pathWidth = self.read_short()
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
self.parse_strans(element.transformation)
|
||||||
|
|
||||||
|
if self.token.ident != Records.XY:
|
||||||
|
raise ParserError(errors.EXPECTED_POINT)
|
||||||
|
|
||||||
|
element.point = self.read_coord()
|
||||||
|
|
||||||
|
# skip potential array (if given)
|
||||||
|
self.skip(self.token.len - 8)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.STRING:
|
||||||
|
raise ParserError(errors.EXPECTED_STRING)
|
||||||
|
|
||||||
|
element.string = self.read_ascii(self.token.len)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.ENDEL:
|
||||||
|
raise ParserError(errors.EXPECTED_ENDEL)
|
||||||
|
|
||||||
|
self.structure.elements.append(element)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_strans(self, trans:Transformation):
|
||||||
|
if self.token.ident != Records.STRANS:
|
||||||
|
return
|
||||||
|
|
||||||
|
flags = self.read_ushort()
|
||||||
|
if flags & 0x01:
|
||||||
|
trans.mirror_x = True
|
||||||
|
|
||||||
|
if flags & 0x2000:
|
||||||
|
trans.absolute_magnification = True
|
||||||
|
|
||||||
|
if flags & 0x4000:
|
||||||
|
trans.absolute_rotation = True
|
||||||
|
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
if self.token.ident == Records.MAG:
|
||||||
|
trans.zoom = self.read_double()
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
if self.token.ident == Records.ANGLE:
|
||||||
|
trans.rotation = self.read_double()
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
def parse_box(self):
|
||||||
|
element = Box()
|
||||||
|
self.next_token(True)
|
||||||
|
self.parse_element(element)
|
||||||
|
self.parse_layer(element)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.BOXTYPE:
|
||||||
|
raise ParserError(errors.EXPECTED_BOXTYPE)
|
||||||
|
|
||||||
|
element.datatype = self.read_short()
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.XY:
|
||||||
|
raise ParserError(errors.EXPECTED_POINTS)
|
||||||
|
|
||||||
|
element.points = self.read_coords(self.token.len)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.ENDEL:
|
||||||
|
raise ParserError(errors.EXPECTED_ENDEL)
|
||||||
|
|
||||||
|
self.structure.elements.append(element)
|
||||||
|
|
||||||
|
def parse_sref(self):
|
||||||
|
element = SRef()
|
||||||
|
element.parent = self.structure
|
||||||
|
|
||||||
|
if not self.next_token():
|
||||||
|
raise ParserError(errors.EXPECTED_SNAME)
|
||||||
|
|
||||||
|
self.parse_element(element)
|
||||||
|
|
||||||
|
if self.token.ident != Records.SNAME:
|
||||||
|
raise ParserError(errors.EXPECTED_SNAME)
|
||||||
|
|
||||||
|
element.structure = self.read_ascii(self.token.len)
|
||||||
|
|
||||||
|
self.next_token(True)
|
||||||
|
self.parse_strans(element.transformation)
|
||||||
|
|
||||||
|
if self.token.ident != Records.XY:
|
||||||
|
raise ParserError(errors.EXPECTED_POINT)
|
||||||
|
|
||||||
|
element.position = self.read_coord()
|
||||||
|
self.skip(self.token.len - 8)
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.ENDEL:
|
||||||
|
raise ParserError(errors.EXPECTED_ENDEL)
|
||||||
|
|
||||||
|
self.structure.references.append(element)
|
||||||
|
|
||||||
|
def parse_aref(self):
|
||||||
|
element = ARef()
|
||||||
|
element.parent = self.structure
|
||||||
|
|
||||||
|
if not self.next_token():
|
||||||
|
raise ParserError(errors.EXPECTED_SNAME)
|
||||||
|
|
||||||
|
self.parse_element(element)
|
||||||
|
|
||||||
|
if self.token.ident != Records.SNAME:
|
||||||
|
raise ParserError(errors.EXPECTED_SNAME)
|
||||||
|
|
||||||
|
element.structure = self.read_ascii(self.token.len)
|
||||||
|
|
||||||
|
self.next_token(True)
|
||||||
|
|
||||||
|
self.parse_strans(element.transformation)
|
||||||
|
|
||||||
|
if self.token.ident != Records.COLROW:
|
||||||
|
raise ParserError(errors.EXPECTED_COLROW)
|
||||||
|
|
||||||
|
element.size = (self.read_short(), self.read_short())
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.XY:
|
||||||
|
raise ParserError(errors.EXPECTED_POINT)
|
||||||
|
|
||||||
|
if self.token.len != 3*8:
|
||||||
|
raise ParserError(errors.AREF_MISSING_POINTS)
|
||||||
|
|
||||||
|
element.position = self.read_coord()
|
||||||
|
element.bounds[0] = self.read_coord()
|
||||||
|
element.bounds[1] = self.read_coord()
|
||||||
|
|
||||||
|
if not self.next_token() or self.token.ident != Records.ENDEL:
|
||||||
|
raise ParserError(errors.EXPECTED_ENDEL)
|
||||||
|
|
||||||
|
self.structure.references.append(element)
|
||||||
|
|
||||||
|
def parse_file(file, progress_func=None):
|
||||||
|
parser = Parser(file, progress_callback=progress_func)
|
||||||
|
parser.parse_lib()
|
||||||
|
|
||||||
|
return parser.Library
|
||||||
|
|
||||||
|
|
148
reader.py
Normal file
148
reader.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from .record import *
|
||||||
|
import abc
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
class ProgressGetter(object, metaclass=abc.ABCMeta):
|
||||||
|
@abc.abstractproperty
|
||||||
|
def current(self):
|
||||||
|
pass
|
||||||
|
@abc.abstractproperty
|
||||||
|
def total(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def progress(self):
|
||||||
|
raise NotImplementedError("getting the progess is not implemented")
|
||||||
|
|
||||||
|
class Reader(ProgressGetter):
|
||||||
|
@property
|
||||||
|
def current(self):
|
||||||
|
return float(self.stream.tell())
|
||||||
|
|
||||||
|
# returns the read progress in percent
|
||||||
|
def progress(self):
|
||||||
|
if self.total <= 0:
|
||||||
|
return 1.0
|
||||||
|
|
||||||
|
return self.current / self.total
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total(self):
|
||||||
|
return self._total
|
||||||
|
|
||||||
|
def __init__(self, file):
|
||||||
|
self.stream = file
|
||||||
|
|
||||||
|
# find file size
|
||||||
|
self.stream.seek(0, 2)
|
||||||
|
self._total = self.stream.tell()
|
||||||
|
self.stream.seek(0, 0)
|
||||||
|
|
||||||
|
def skip(self, n):
|
||||||
|
self.stream.read(n)
|
||||||
|
|
||||||
|
def read_uint(self):
|
||||||
|
temp = self.stream.read(4)
|
||||||
|
if len(temp) != 4:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return int(temp[3]) | int(temp[2]) << 8 | int(temp[1]) << 16 | int(temp[0]) << 24
|
||||||
|
|
||||||
|
def read_ushort(self):
|
||||||
|
temp = self.stream.read(2)
|
||||||
|
if len(temp) != 2:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return int(temp[1]) | int(temp[0]) << 8
|
||||||
|
|
||||||
|
def read_short(self):
|
||||||
|
temp = self.read_ushort()
|
||||||
|
|
||||||
|
if temp == None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return ctypes.c_short(temp).value
|
||||||
|
|
||||||
|
def read_int(self):
|
||||||
|
temp = self.read_uint()
|
||||||
|
|
||||||
|
if temp == None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return ctypes.c_int(temp).value
|
||||||
|
|
||||||
|
def read_double(self):
|
||||||
|
temp = self.stream.read(8)
|
||||||
|
if len(temp) != 8:
|
||||||
|
return None
|
||||||
|
|
||||||
|
result = 0
|
||||||
|
|
||||||
|
for i in temp:
|
||||||
|
if int(i) != 0:
|
||||||
|
# read double
|
||||||
|
for j in range(1,8):
|
||||||
|
result += float(temp[j])/(2.0**(j*8))
|
||||||
|
|
||||||
|
exp = int(temp[0]) & 0x7F
|
||||||
|
exp -= 64
|
||||||
|
result *= 16**exp
|
||||||
|
|
||||||
|
if int(temp[0]) & 0x80:
|
||||||
|
result += -1
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
# double is Zero
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def read_ascii(self, len):
|
||||||
|
# removes zero terminators
|
||||||
|
# as well as trailing and beginning whitespaces
|
||||||
|
return self.stream.read(len).decode("ASCII").replace("\x00", "").strip()
|
||||||
|
|
||||||
|
def read_record(self):
|
||||||
|
result = Record()
|
||||||
|
try:
|
||||||
|
result.len = self.read_ushort()
|
||||||
|
result.ident = Records(self.read_ushort())
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
result.len -= 4 # remove record header len
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def read_date(self):
|
||||||
|
# date
|
||||||
|
year = self.read_ushort()
|
||||||
|
month = self.read_ushort()
|
||||||
|
day = self.read_ushort()
|
||||||
|
|
||||||
|
# time
|
||||||
|
hour = self.read_ushort()
|
||||||
|
minute = self.read_ushort()
|
||||||
|
second = self.read_ushort()
|
||||||
|
|
||||||
|
return datetime(year=year, month=month, day=day, hour=hour, minute=minute, second=second)
|
||||||
|
|
||||||
|
def read_coord(self):
|
||||||
|
X = self.read_int()
|
||||||
|
Y = self.read_int()
|
||||||
|
|
||||||
|
return (X,Y)
|
||||||
|
|
||||||
|
def read_coords(self, len):
|
||||||
|
len /= 8
|
||||||
|
result = []
|
||||||
|
while len > 0:
|
||||||
|
point = self.read_coord()
|
||||||
|
if not point:
|
||||||
|
return None
|
||||||
|
|
||||||
|
result.append(point)
|
||||||
|
len -= 1
|
||||||
|
|
||||||
|
return result
|
52
record.py
Normal file
52
record.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Records(Enum):
|
||||||
|
UNKNOWN = 0x0000
|
||||||
|
HEADER = 0x0002
|
||||||
|
BGNLIB = 0x0102
|
||||||
|
LIBNAME = 0x0206
|
||||||
|
REFLIBS = 0x1F06
|
||||||
|
FONTS = 0x2006
|
||||||
|
ATTRTABLE = 0x2306
|
||||||
|
GENERATIONS = 0x2202
|
||||||
|
FORMAT = 0x3602
|
||||||
|
MASK = 0x3706
|
||||||
|
ENDMASKS = 0x3800
|
||||||
|
UNITS = 0x0305
|
||||||
|
ENDLIB = 0x0400
|
||||||
|
BGNSTR = 0x0502
|
||||||
|
STRNAME = 0x0606
|
||||||
|
ENDEL = 0x1100
|
||||||
|
ENDSTR = 0x0700
|
||||||
|
BOUNDARY = 0x0800
|
||||||
|
PATH = 0x0900
|
||||||
|
SREF = 0x0A00
|
||||||
|
AREF = 0x0B00
|
||||||
|
TEXT = 0x0C00
|
||||||
|
NODE = 0x1500
|
||||||
|
BOX = 0x2D00
|
||||||
|
ELFLAGS = 0x2601
|
||||||
|
PLEX = 0x2F03
|
||||||
|
LAYER = 0x0D02
|
||||||
|
DATATYPE = 0x0E02
|
||||||
|
XY = 0x1003
|
||||||
|
PATHTYPE = 0x2102
|
||||||
|
WIDTH = 0x0F03
|
||||||
|
BGNEXTN = 0x3003
|
||||||
|
ENDEXTN = 0x3103
|
||||||
|
SNAME = 0x1206
|
||||||
|
STRANS = 0x1A01
|
||||||
|
MAG = 0x1B05
|
||||||
|
ANGLE = 0x1C05
|
||||||
|
COLROW = 0x1302
|
||||||
|
TEXTTYPE = 0x1602
|
||||||
|
PRESENTATION = 0x1701
|
||||||
|
NODETYPE = 0x2A02
|
||||||
|
BOXTYPE = 0x2E02
|
||||||
|
STRING = 0x1906
|
||||||
|
PROPATTR = 0x2B02
|
||||||
|
PROPVALUE = 0x2C06
|
||||||
|
|
||||||
|
class Record(object):
|
||||||
|
ident = Records.UNKNOWN
|
||||||
|
len = 0
|
14
structure.py
Normal file
14
structure.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class Structure(object):
|
||||||
|
def __init__(self):
|
||||||
|
# metainfo
|
||||||
|
self.creation_date = datetime.now()
|
||||||
|
self.last_mod = datetime.now()
|
||||||
|
self.name = "NONAME"
|
||||||
|
|
||||||
|
# contains all the low level elements
|
||||||
|
self.elements = []
|
||||||
|
|
||||||
|
# contains all sref and aref elements
|
||||||
|
self.references = []
|
Loading…
Reference in New Issue
Block a user