1#! /usr/bin/python
2# -*- coding: utf-8 -*-
3# Copyright © 2010 Eugeniy Meshcheryakov <eugen@debian.org>
4# This file is licensed under GNU Lesser General Public License version 3 or later.
5from __future__ import print_function
6from gdsii.library import Library
7from gdsii import elements
8import sys
9from yaml.dumper import Dumper
10from yaml import events
11
12# standard tags
13STR = 'tag:yaml.org,2002:str'
14TIMESTAMP = 'tag:yaml.org,2002:timestamp'
15FLOAT = 'tag:yaml.org,2002:float'
16INT = 'tag:yaml.org,2002:int'
17SEQ = 'tag:yaml.org,2002:seq'
18MAP = 'tag:yaml.org,2002:map'
19
20# non-standard tags
21LIBRARY = 'tag:gdsii,2010:library'
22STRUCTURE = 'tag:gdsii,2010:structure'
23
24BOUNDARY = 'tag:gdsii,2010:element:boundary'
25PATH = 'tag:gdsii,2010:element:path'
26SREF = 'tag:gdsii,2010:element:sref'
27AREF = 'tag:gdsii,2010:element:aref'
28TEXT = 'tag:gdsii,2010:element:text'
29NODE = 'tag:gdsii,2010:element:node'
30BOX = 'tag:gdsii,2010:element:box'
31
32def emit_string(dumper, prop, tag, value):
33    dumper.emit(events.ScalarEvent(None, STR, (True, False), prop))
34    dumper.emit(events.ScalarEvent(None, tag, (True, False), value))
35
36def start_named_seq(dumper, name):
37    dumper.emit(events.ScalarEvent(None, STR, (True, False), name))
38    dumper.emit(events.SequenceStartEvent(None, SEQ, True))
39
40def end_named_seq(dumper):
41    dumper.emit(events.SequenceEndEvent())
42
43def simple_dumper(name, tag):
44    def dump_fn(dumper, obj):
45        value = getattr(obj, name)
46        emit_string(dumper, name, tag, str(value))
47    return dump_fn
48
49def simple_string_dumper(name):
50    def dump_fn(dumper, obj):
51        value = getattr(obj, name)
52        emit_string(dumper, name, STR, value.decode())
53    return dump_fn
54
55def timestamp_dumper(name):
56    def dump_fn(dumper, obj):
57        value = getattr(obj, name)
58        emit_string(dumper, name, TIMESTAMP, value.isoformat(' '))
59    return dump_fn
60
61def optional_dumper(name, tag):
62    def dump_fn(dumper, obj):
63        try:
64            value = getattr(obj, name)
65        except AttributeError:
66            value = None
67        if value is not None:
68            emit_string(dumper, name, tag, str(value))
69    return dump_fn
70
71def optional_flags_dumper(name, tag):
72    def dump_fn(dumper, obj):
73        try:
74            value = getattr(obj, name)
75        except AttributeError:
76            value = None
77        if value is not None:
78            emit_string(dumper, name, tag, '0x%x'%value)
79    return dump_fn
80
81def xy_dumper(name):
82    def dump_fn(dumper, obj):
83        points = getattr(obj, name)
84        start_named_seq(dumper, name)
85        for point in points:
86            dumper.emit(events.SequenceStartEvent(None, SEQ, True, flow_style=True))
87            dumper.emit(events.ScalarEvent(None, INT, (True, False), str(point[0])))
88            dumper.emit(events.ScalarEvent(None, INT, (True, False), str(point[1])))
89            dumper.emit(events.SequenceEndEvent())
90        end_named_seq(dumper)
91    return dump_fn
92
93def properties_dumper(name):
94    def dump_fn(dumper, obj):
95        properties = getattr(obj, name)
96        if (properties):
97            start_named_seq(dumper, name)
98            for (prop, value) in properties:
99                dumper.emit(events.MappingStartEvent(None, MAP, True))
100                dumper.emit(events.ScalarEvent(None, INT, (True, False), str(prop)))
101                dumper.emit(events.ScalarEvent(None, STR, (True, False), value.decode()))
102                dumper.emit(events.MappingEndEvent())
103            end_named_seq(dumper)
104    return dump_fn
105
106def strans_dumper(name):
107    my_dumper = optional_flags_dumper('strans', INT)
108    mag = optional_dumper('mag', FLOAT)
109    angle = optional_dumper('angle', FLOAT)
110    def dump_fn(dumper, obj):
111        try:
112            value = getattr(obj, name)
113        except AttributeError:
114            value = None
115        if value is not None:
116            my_dumper(dumper, obj)
117            mag(dumper, obj)
118            angle(dumper, obj)
119    return dump_fn
120
121elflags = optional_flags_dumper('elflags', INT)
122plex = optional_dumper('plex', INT)
123layer = simple_dumper('layer', INT)
124data_type = simple_dumper('data_type', INT)
125path_type = optional_dumper('path_type', INT)
126width = optional_dumper('width', INT)
127bgn_extn = optional_dumper('bgn_extn', INT)
128end_extn = optional_dumper('end_extn', INT)
129struct_name = simple_string_dumper('struct_name')
130strans = strans_dumper('strans')
131cols = simple_dumper('cols', INT)
132rows = simple_dumper('rows', INT)
133text_type = optional_dumper('text_type', INT)
134presentation = optional_flags_dumper('presentation', INT)
135string = simple_string_dumper('string')
136node_type = simple_dumper('node_type', INT)
137box_type = simple_dumper('box_type', INT)
138xy = xy_dumper('xy')
139properties = properties_dumper('properties')
140
141DUMPERS = (
142    (elements.Boundary, BOUNDARY, (elflags, plex, layer, data_type, xy, properties)),
143    (elements.Path, PATH, (elflags, plex, layer, data_type, path_type, width, bgn_extn, end_extn, xy, properties)),
144    (elements.SRef, SREF, (elflags, plex, struct_name, strans, xy, properties)),
145    (elements.ARef, AREF, (elflags, plex, struct_name, strans, cols, rows, xy, properties)),
146    (elements.Text, TEXT, (elflags, plex, layer, text_type, presentation, path_type, width, strans, xy, string, properties)),
147    (elements.Node, NODE, (elflags, plex, layer, node_type, xy)),
148    (elements.Box, BOX, (elflags, plex, layer, box_type, xy, properties))
149)
150
151def dump_element(dumper, elem):
152    for rec in DUMPERS:
153        if isinstance(elem, rec[0]):
154            tag = rec[1]
155            dumpers = rec[2]
156            dumper.emit(events.MappingStartEvent(None, tag, False))
157            for fn in dumpers:
158                fn(dumper, elem)
159            dumper.emit(events.MappingEndEvent())
160            break
161    else:
162        raise Exception('Unsupported element: %s' % str(elem))
163
164name = simple_string_dumper('name')
165mod_time = timestamp_dumper('mod_time')
166acc_time = timestamp_dumper('acc_time')
167strclass = optional_dumper('strclass', INT)
168
169def dump_structure(dumper, struc):
170    dumper.emit(events.MappingStartEvent(None, STRUCTURE, False))
171    name(dumper, struc)
172    mod_time(dumper, struc)
173    acc_time(dumper, struc)
174    strclass(dumper, struc)
175    start_named_seq(dumper, 'elements')
176    for elem in struc:
177        dump_element(dumper, elem)
178    end_named_seq(dumper)
179    dumper.emit(events.MappingEndEvent())
180
181physical_unit = simple_dumper('physical_unit', FLOAT)
182logical_unit = simple_dumper('logical_unit', FLOAT)
183libdirsize = optional_dumper('libdirsize', INT)
184
185def dump_library(dumper, lib):
186    dumper.emit(events.StreamStartEvent(encoding='utf-8'))
187    dumper.emit(events.DocumentStartEvent(explicit=False))
188
189    dumper.emit(events.MappingStartEvent(None, LIBRARY, False))
190    emit_string(dumper, 'version', INT, '0x%x'%lib.version)
191    name(dumper, lib)
192    mod_time(dumper, lib)
193    acc_time(dumper, lib)
194    libdirsize(dumper, lib)
195    # TODO
196    physical_unit(dumper, lib)
197    logical_unit(dumper, lib)
198    start_named_seq(dumper, 'structures')
199    for struc in lib:
200        dump_structure(dumper, struc)
201    end_named_seq(dumper)
202    dumper.emit(events.MappingEndEvent())
203
204    dumper.emit(events.DocumentEndEvent(explicit=False))
205    dumper.emit(events.StreamEndEvent())
206
207def main(name):
208    with open(name, 'rb') as a_file:
209        lib = Library.load(stream=a_file)
210    dumper = Dumper(sys.stdout)
211    dump_library(dumper, lib)
212
213def usage(prog):
214    print('Usage: %s <file.gds>' % prog)
215
216if __name__ == '__main__':
217    if (len(sys.argv) > 1):
218        main(sys.argv[1])
219    else:
220        usage(sys.argv[0])
221        sys.exit(1)
222    sys.exit(0)
223