diff --git a/gds/.gitignore b/gds/.gitignore deleted file mode 100644 index bee8a64..0000000 --- a/gds/.gitignore +++ /dev/null @@ -1 +0,0 @@ -__pycache__ diff --git a/gds/__init__.py b/gds/__init__.py deleted file mode 100644 index c960330..0000000 --- a/gds/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .library import Library -from .structure import Structure -from .elements import * - -from .record import * -from .reader import Reader -from .parser import * diff --git a/gds/elements.py b/gds/elements.py deleted file mode 100644 index fafa6aa..0000000 --- a/gds/elements.py +++ /dev/null @@ -1,116 +0,0 @@ -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) - - - diff --git a/gds/library.py b/gds/library.py deleted file mode 100644 index 4c11c9c..0000000 --- a/gds/library.py +++ /dev/null @@ -1,81 +0,0 @@ -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) - - \ No newline at end of file diff --git a/gds/parser.py b/gds/parser.py deleted file mode 100644 index 44abf53..0000000 --- a/gds/parser.py +++ /dev/null @@ -1,407 +0,0 @@ -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 - - diff --git a/gds/reader.py b/gds/reader.py deleted file mode 100644 index 4c37e98..0000000 --- a/gds/reader.py +++ /dev/null @@ -1,148 +0,0 @@ -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 \ No newline at end of file diff --git a/gds/record.py b/gds/record.py deleted file mode 100644 index 9c8ff63..0000000 --- a/gds/record.py +++ /dev/null @@ -1,52 +0,0 @@ -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 diff --git a/gds/structure.py b/gds/structure.py deleted file mode 100644 index 50f9b70..0000000 --- a/gds/structure.py +++ /dev/null @@ -1,14 +0,0 @@ -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 = [] \ No newline at end of file