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