jfreeze-ruby-gdsii 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. data/CHANGELOG.txt +6 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.txt +113 -0
  4. data/Rakefile +30 -0
  5. data/bin/rgds-debug +43 -0
  6. data/bin/rgds-dump +38 -0
  7. data/bin/rgds-join +98 -0
  8. data/bin/rgds-layers +53 -0
  9. data/bin/rgds-sremove +136 -0
  10. data/bin/rgds-ssplit +113 -0
  11. data/bin/rgds-stats +134 -0
  12. data/bin/rgds-structs +41 -0
  13. data/bin/rgds-tree +167 -0
  14. data/bin/rgds2rb +99 -0
  15. data/lib/gdsii.rb +137 -0
  16. data/lib/gdsii/aref.rb +243 -0
  17. data/lib/gdsii/bnf.rb +309 -0
  18. data/lib/gdsii/boundary.rb +53 -0
  19. data/lib/gdsii/box.rb +65 -0
  20. data/lib/gdsii/byte_order.rb +36 -0
  21. data/lib/gdsii/element.rb +172 -0
  22. data/lib/gdsii/group.rb +98 -0
  23. data/lib/gdsii/library.rb +518 -0
  24. data/lib/gdsii/mixins.rb +378 -0
  25. data/lib/gdsii/node.rb +65 -0
  26. data/lib/gdsii/path.rb +169 -0
  27. data/lib/gdsii/property.rb +108 -0
  28. data/lib/gdsii/record.rb +606 -0
  29. data/lib/gdsii/record/consts.rb +384 -0
  30. data/lib/gdsii/record/datatypes/ascii.rb +145 -0
  31. data/lib/gdsii/record/datatypes/bitarray.rb +101 -0
  32. data/lib/gdsii/record/datatypes/data.rb +111 -0
  33. data/lib/gdsii/record/datatypes/int2.rb +67 -0
  34. data/lib/gdsii/record/datatypes/int4.rb +65 -0
  35. data/lib/gdsii/record/datatypes/nodata.rb +60 -0
  36. data/lib/gdsii/record/datatypes/real4.rb +51 -0
  37. data/lib/gdsii/record/datatypes/real8.rb +120 -0
  38. data/lib/gdsii/sref.rb +61 -0
  39. data/lib/gdsii/strans.rb +133 -0
  40. data/lib/gdsii/structure.rb +352 -0
  41. data/lib/gdsii/text.rb +203 -0
  42. data/pkg/ruby-gdsii.gem +46 -0
  43. data/samples/hello.gds +0 -0
  44. data/samples/hello.out.rb +84 -0
  45. data/samples/hello.rb +94 -0
  46. data/test/baseline/dcp1.gds +0 -0
  47. data/test/baseline/h_write.gds +0 -0
  48. data/test/h_pthru.rb +22 -0
  49. data/test/h_write.rb +117 -0
  50. data/test/hs_pthru.rb +31 -0
  51. data/test/l_pthru.rb +23 -0
  52. data/test/test_gds_group.rb +379 -0
  53. data/test/test_gds_record.rb +99 -0
  54. metadata +117 -0
@@ -0,0 +1,172 @@
1
+ require 'gdsii/group'
2
+ require 'gdsii/property'
3
+
4
+ module Gdsii
5
+
6
+ #
7
+ # Generic class to be inherited by various GDSII elements (i.e. things that
8
+ # can be added to a Structure).
9
+ #
10
+ class Element < Group
11
+
12
+ # No BNF for generic Element; refer to actual Element itself (i.e.
13
+ # Boundary, Path, etc.)
14
+
15
+ #
16
+ # Generic element constructor. Not intended to be called directly but
17
+ # rather inherited and called through sub-classes such as Gdsii::Boundary.
18
+ #
19
+ def initialize()
20
+ super()
21
+ @records[GRT_ENDEL] = Record.new(GRT_ENDEL)
22
+ @records[Properties] = @properties = Properties.new
23
+ end
24
+
25
+ #
26
+ # Shortcut for Properties#add. For example, instead of:
27
+ #
28
+ # bnd.properties.add Property(1, 'testprop')
29
+ #
30
+ # It could be simply:
31
+ #
32
+ # bnd.add Property(1, 'testprop')
33
+ #
34
+ def add(*args); properties.add(*args); end
35
+
36
+ #
37
+ # Shortcut for Properties#remove. For example, instead of:
38
+ #
39
+ # bnd.properties.remove {|p| p.attr == 1}
40
+ #
41
+ # It could be simply:
42
+ #
43
+ # bnd.remove {|p| p.attr == 1}
44
+ #
45
+ def remove(*args); properties.remove(*args); end
46
+
47
+ #
48
+ # Access the Properties object for this element
49
+ #
50
+ def properties(); @properties; end
51
+
52
+ class << self
53
+ alias :read_el :read
54
+ end
55
+
56
+ def Element.read(file, *args)
57
+ rec = Record.peek(file)
58
+ case rec.type
59
+ when GRT_BOUNDARY : Boundary.read_el(file, *args)
60
+ when GRT_TEXT : Text.read_el(file, *args)
61
+ when GRT_PATH : Path.read_el(file, *args)
62
+ when GRT_SREF : SRef.read_el(file, *args)
63
+ when GRT_AREF : ARef.read_el(file, *args)
64
+ when GRT_BOX : Box.read_el(file, *args)
65
+ when GRT_NODE : Node.read_el(file, *args)
66
+ else
67
+ # end of the element, increment the counter and move on
68
+ nil
69
+ end
70
+ end
71
+
72
+ #
73
+ # After a Structure header has been read (see Structure#read_each_header)
74
+ # then elements may be processed as a file is read using Element#read_each.
75
+ # See Structure#read_each_header for an example.
76
+ #
77
+ # Compare this with Structure#seek_next which also advances the file handle
78
+ # to the next structure but does not yield any elements (if only a file
79
+ # pointer advancement is needed and elements can be ignored).
80
+ #
81
+ def Element.read_each(file)
82
+ while (group = Element.read(file)) do
83
+ yield group
84
+ end
85
+ # rip out ENDEL - TODO: make sure that it's ENDEL
86
+ Record.read(file)
87
+ end
88
+
89
+ #
90
+ # Returns true if this is a Boundary element
91
+ #
92
+ def is_boundary?; self.class == Boundary; end
93
+
94
+ #
95
+ # Returns true if this is a Path element
96
+ #
97
+ def is_path?; self.class == Path; end
98
+
99
+ #
100
+ # Returns true if this is a Text element
101
+ #
102
+ def is_text?; self.class == Text; end
103
+
104
+ #
105
+ # Returns true if this is a SRef element
106
+ #
107
+ def is_sref?; self.class == SRef; end
108
+
109
+ #
110
+ # Returns true if this is a ARef element
111
+ #
112
+ def is_aref?; self.class == ARef; end
113
+
114
+ #
115
+ # Returns true if this is a Box element
116
+ #
117
+ def is_box?; self.class == Box; end
118
+
119
+ #
120
+ # Returns true if this is a Node element
121
+ #
122
+ def is_node?; self.class == Node; end
123
+
124
+ end
125
+
126
+ ############################################################################
127
+
128
+ #
129
+ # Class to hold a collection of Element objects. This is used in the
130
+ # Structure class BNF.
131
+ #
132
+ # Elements include: Boundary, Path, SRef, ARef, Text, Node, Box
133
+ #
134
+ class Elements < Group
135
+
136
+ include Access::EnumerableGroup
137
+
138
+ #
139
+ # Elements BNF description:
140
+ #
141
+ # <structure> ::= {<element>}*
142
+ # <element> ::= {<boundary> | <path> | <sref> | <aref> |
143
+ # <text> | <node> | <box>} {<property>}* ENDEL
144
+ #
145
+ self.bnf_spec = BnfSpec.new(
146
+ BnfItem.new(Element, true, true)
147
+ )
148
+
149
+ #
150
+ # Create an Elements object.
151
+ #
152
+ def initialize(elements=[])
153
+ super()
154
+ @records[Element] = @list = elements
155
+ end
156
+
157
+
158
+ #######################
159
+ ## PROTECTED METHODS ##
160
+ #######################
161
+
162
+ protected
163
+
164
+ # Used by Access::EnumerableGroup to validate addition
165
+ def validate_addition(object)
166
+ unless object.kind_of?(Element)
167
+ raise TypeError, "Invalid addition: #{object.class}; expecting Gdsii::Element"
168
+ end
169
+ end
170
+
171
+ end
172
+ end
@@ -0,0 +1,98 @@
1
+ require 'gdsii/mixins'
2
+ require 'gdsii/record'
3
+ require 'gdsii/bnf'
4
+
5
+ module Gdsii
6
+
7
+ #
8
+ # Generic base class for a GDSII grouping of records (i.e. a boundary object
9
+ # and all records associated with it). This class will not be used directly
10
+ # but will be inherited by the various record groupings.
11
+ #
12
+ class Group
13
+
14
+ extend Read
15
+
16
+ attr_reader :records
17
+
18
+ #
19
+ # Constructor of a generic GDSII record grouping. Not intended to be
20
+ # called directly but rather from subclasses which inherit this class.
21
+ #
22
+ def initialize()
23
+ @records = BnfRecords.new(self.class)
24
+ end
25
+
26
+ #
27
+ # Simply yields itself for configuration (i.e. makes for prettier code).
28
+ # The object itself is returned.
29
+ #
30
+ def configure()
31
+ yield self
32
+ self
33
+ end
34
+
35
+ #
36
+ # Write the record grouping to a file. A file name as a String can be
37
+ # passed in which case a new file by the given name is opened in write
38
+ # mode ('w'). Alternatively, a file object may be passed in which case
39
+ # the record grouping are written to the file object. Examples (assumes
40
+ # "object" has been initialized and descends from this class):
41
+ #
42
+ # object.write('mydesign.gds')
43
+ #
44
+ # or
45
+ #
46
+ # # Note: 'wb' is required for DOS/Windows compatibility
47
+ # File.open('otherdesign.gds', 'wb') do |file|
48
+ # object.write(file)
49
+ # end
50
+ #
51
+ def write(file, alt_bnf=nil)
52
+ # If the file specified is a string, then open it up for writing. If it
53
+ # is a file open it for writing if it is not already open
54
+ if file.class == String
55
+ file = File.open(file,"wb")
56
+ elsif file.class == File
57
+ file = File.open(file,"wb") if file.closed?
58
+ else
59
+ raise TypeError, "Invalid file object given: #{file}"
60
+ end
61
+
62
+ # Write to file according to BNF
63
+ @records.write(file, alt_bnf)
64
+ end
65
+
66
+ #
67
+ # Runs a code block to validate the object if the validate attribute is
68
+ # set. This is typically run to check record grouping integrity during
69
+ # read/write of GDSII files.
70
+ #
71
+ def validate()
72
+ if @validate
73
+ @validate.call
74
+ end
75
+ end
76
+
77
+ #
78
+ # Set class instance variables to be used in subclasses.
79
+ #
80
+ class << self
81
+
82
+ # Set the class bnf array
83
+ def bnf_spec=(value); @bnf = value; end
84
+
85
+ # Return class bnf array
86
+ def bnf_spec(); @bnf; end
87
+
88
+ # Set the BNF key for this class
89
+ def bnf_key=(value); @bnf_key = value; end
90
+
91
+ # Get the BNF key for this class (default is the instantiating class)
92
+ def bnf_key(); (@bnf_key.nil?) ? self : @bnf_key ; end
93
+
94
+ end
95
+
96
+ end
97
+ end
98
+
@@ -0,0 +1,518 @@
1
+ require 'time'
2
+ require 'gdsii/mixins'
3
+ require 'gdsii/group'
4
+ require 'gdsii/structure'
5
+
6
+ module Gdsii
7
+
8
+ DEF_LIB_VERSION = 5
9
+ DEF_LIB_UNITS = [0.001,1.0e-09]
10
+
11
+ #
12
+ # Represents a GDSII Library.
13
+ #
14
+ class Library < Group
15
+
16
+ include Access::GdsiiTime
17
+
18
+ #
19
+ # Library BNF description:
20
+ #
21
+ # <lib> ::= HEADER <libheader> {<structure>}* ENDLIB
22
+ # <libheader> ::= BGNLIB [LIBDIRSIZE] [SRFNAME] [LIBSECUR]
23
+ # LIBNAME [REFLIBS] [FONTS] [ATTRTABLE] [GENERATIONS]
24
+ # [<FormatType>] UNITS
25
+ # <FormatType> ::= FORMAT | FORMAT {MASK}+ ENDMASKS
26
+ #
27
+ self.bnf_spec = BnfSpec.new(
28
+ BnfItem.new(GRT_HEADER),
29
+ BnfItem.new(GRT_BGNLIB),
30
+ BnfItem.new(GRT_LIBDIRSIZE,true),
31
+ BnfItem.new(GRT_SRFNAME, true),
32
+ BnfItem.new(GRT_LIBSECUR, true),
33
+ BnfItem.new(GRT_LIBNAME),
34
+ BnfItem.new(GRT_REFLIBS, true),
35
+ BnfItem.new(GRT_FONTS, true),
36
+ BnfItem.new(GRT_ATTRTABLE, true),
37
+ BnfItem.new(GRT_GENERATIONS, true),
38
+ BnfItem.new(GRT_FORMAT, true),
39
+ BnfItem.new(GRT_MASK, true, true),
40
+ BnfItem.new(GRT_ENDMASKS, true),
41
+ BnfItem.new(GRT_UNITS),
42
+ BnfItem.new(Structures, true),
43
+ BnfItem.new(GRT_ENDLIB)
44
+ )
45
+
46
+ #
47
+ # Create a new GDSII Library object.
48
+ #
49
+ # lib = Library.new('MYDESIGN.DB')
50
+ #
51
+ # The units may be specified during construction:
52
+ #
53
+ # lib2 = Library.new('OTHER.DB', [0.001, 1e-9])
54
+ #
55
+ def initialize(name=nil, units=DEF_LIB_UNITS)
56
+ super()
57
+ @records[Structure] = []
58
+ @records[GRT_ENDLIB] = Record.new(GRT_ENDLIB)
59
+
60
+ self.header = DEF_LIB_VERSION
61
+ self.name = name unless name.nil?
62
+ self.units = units
63
+
64
+ # Set modify/access time to the current time
65
+ now = Time.now
66
+ self.modify_time = now
67
+ self.access_time = now
68
+
69
+ yield self if block_given?
70
+ end
71
+
72
+ #
73
+ # Access to the Structures object. See Structures for a listing of
74
+ # methods.
75
+ #
76
+ def structures(); @records.get(Structures); end
77
+
78
+ #
79
+ # Shortcut for Structures#add. For example, instead of:
80
+ #
81
+ # lib = Library.new('MYLIB.DB')
82
+ # lib.structures.add Structure.new('test')
83
+ #
84
+ # It could be simply:
85
+ #
86
+ # lib.add Structure.new('test')
87
+ #
88
+ def add(*args); structures.add(*args); end
89
+
90
+ #
91
+ # Shortcut for Structures#remove. For example, instead of:
92
+ #
93
+ # lib.structures.remove {|s| true}
94
+ #
95
+ # It could be simply:
96
+ #
97
+ # lib.remove {|s| true}
98
+ #
99
+ def remove(*args); structures.remove(*args); end
100
+
101
+ #
102
+ # Get the Library LIBNAME record (returns Record).
103
+ #
104
+ def name_record() @records.get(GRT_LIBNAME); end
105
+
106
+ #
107
+ # Get the Library name (returns String).
108
+ #
109
+ def name() @records.get_data(GRT_LIBNAME); end
110
+
111
+ #
112
+ # Set the Library name.
113
+ #
114
+ def name=(val) @records.set(GRT_LIBNAME, val); end
115
+
116
+ #
117
+ # Get the header record (returns Record).
118
+ #
119
+ def header_record() @records.get(GRT_HEADER); end
120
+
121
+ #
122
+ # Get the header number; this is the GDSII version (returns Fixnum).
123
+ #
124
+ def header() @records.get_data(GRT_HEADER); end
125
+
126
+ #
127
+ # Set the header number; this is the GDSII version. Valid numbers are
128
+ # 3, 4, 5, 6, and 7. The default version used is defined by the
129
+ # constant DEF_LIB_VER.
130
+ #
131
+ def header=(val) @records.set(GRT_HEADER, val); end
132
+
133
+ alias :version= :header=
134
+ alias :version :header
135
+
136
+ #
137
+ # Get the library directory size LIBDIRSIZE record (returns Record).
138
+ #
139
+ def dirsize_record() @records.get(GRT_LIBDIRSIZE); end
140
+
141
+ #
142
+ # Get the number of pages in the library directory (returns Fixnum). This
143
+ # is likely an old Calma record and is likely unused except in rare
144
+ # circumstances.
145
+ #
146
+ def dirsize() @records.get_data(GRT_LIBDIRSIZE); end
147
+
148
+ #
149
+ # Set the number of pages in the library directory (see #dirsize for more
150
+ # information).
151
+ #
152
+ def dirsize=(val) @records.set(GRT_LIBDIRSIZE, val); end
153
+
154
+ #
155
+ # Get the Library SRFNAME record (returns Record).
156
+ #
157
+ def srfname_record() @records.get(GRT_SRFNAME); end
158
+
159
+ #
160
+ # Get the Library Calma sticks rule file name (returns String). This
161
+ # is likely unused except in rare circumstances.
162
+ #
163
+ def srfname() @records.get_data(GRT_SRFNAME); end
164
+
165
+ #
166
+ # Set the Library Calma sticks rule file name (see #srfname for details).
167
+ #
168
+ def srfname=(val) @records.set(GRT_SRFNAME, val); end
169
+
170
+ #
171
+ # Get the library security LIBSECUR record (returns Record).
172
+ #
173
+ def secur_record() @records.get(GRT_LIBSECUR); end
174
+
175
+ #
176
+ # Get the secur number (returns Fixnum). This is an array of 1-32
177
+ # elements of an array of 3 elements; each containing Fixnum representing
178
+ # (respectively): group number, user number, and access rights. Since this
179
+ # appears to be rarely used, no high-level methods are given to access this
180
+ # record. Returns an Array of Fixnum.
181
+ #
182
+ def secur() @records.get_data(GRT_LIBSECUR); end
183
+
184
+ #
185
+ # Set the library security number (see #secur for details)
186
+ #
187
+ def secur=(val) @records.set(GRT_LIBSECUR, val); end
188
+
189
+ #
190
+ # Get the fonts record (returns Record).
191
+ #
192
+ def fonts_record() @records.get(GRT_FONTS); end
193
+
194
+ #
195
+ # Get the array of paths to font definition files. If this record exists,
196
+ # then exactly 4 array elements should exist. Each array element is a
197
+ # String with a maximum of 44 characters. Returns Array of Strings.
198
+ #
199
+ def fonts() @records.get_data(GRT_FONTS); end
200
+
201
+ #
202
+ # Set the path to 4 font definition files. See #fonts for more details.
203
+ #
204
+ def fonts=(val) @records.set(GRT_FONTS, val); end
205
+
206
+ #
207
+ # Get the attribute table file location ATTRTABLE record (returns Record).
208
+ #
209
+ def attrtable_record() @records.get(GRT_ATTRTABLE); end
210
+
211
+ #
212
+ # Get the attribute table file location. This is a String with a maximum
213
+ # of 44 characters in length. Returns String.
214
+ #
215
+ def attrtable() @records.get_data(GRT_ATTRTABLE); end
216
+
217
+ #
218
+ # Set the attribute table file location. See #attrtable for more details.
219
+ #
220
+ def attrtable=(val) @records.set(GRT_ATTRTABLE, val); end
221
+
222
+ #
223
+ # Get the generations record (returns Record).
224
+ #
225
+ def generations_record() @records.get(GRT_GENERATIONS); end
226
+
227
+ #
228
+ # Get the generations number (returns Fixnum). This number represents
229
+ # how many structures should be retained as backup. This is likely
230
+ # rarely used.
231
+ #
232
+ def generations() @records.get_data(GRT_GENERATIONS); end
233
+
234
+ #
235
+ # Set the generations number. See #generations for details.
236
+ #
237
+ def generations=(val) @records.set(GRT_GENERATIONS, val); end
238
+
239
+ #
240
+ # Get the format record (returns Record).
241
+ #
242
+ def format_record() @records.get(GRT_FORMAT); end
243
+
244
+ #
245
+ # Get the format number (returns Fixnum). This number is used to indicate
246
+ # if the stream file is an archive and/or filtered:
247
+ #
248
+ # 0: Archive
249
+ # 1: Filtered
250
+ #
251
+ def format() @records.get_data(GRT_FORMAT); end
252
+
253
+ #
254
+ # Set the format number. See #format for details.
255
+ #
256
+ def format=(val) @records.set(GRT_FORMAT, val); end
257
+
258
+ #
259
+ # True if #format == 0 indicating archive status; false if not.
260
+ #
261
+ def archive_format?(); format == 0; end
262
+
263
+ #
264
+ # True if #format == 1 indicating filtered status; false if not.
265
+ #
266
+ def filtered_format?(); format == 1; end
267
+
268
+ #
269
+ # Get the mask record (returns Record).
270
+ #
271
+ def mask_record() @records.get(GRT_MASK); end
272
+
273
+ #
274
+ # Get the MASK record (returns Array of String). This is only used in
275
+ # filtered (see #format) stream files. This string represents ranges of
276
+ # layers and datatypes separated by a semicolon. There can be more than
277
+ # one MASK defined.
278
+ #
279
+ def mask() @records.get_data(GRT_MASK); end
280
+
281
+ #
282
+ # Set the mask number. See #mask for details.
283
+ #
284
+ def mask=(val) @records.set(GRT_MASK, val); end
285
+
286
+ #
287
+ # Get the units record (returns Record).
288
+ #
289
+ def units_record() @records.get(GRT_UNITS); end
290
+
291
+ #
292
+ # Get the units Array (returns 2 element Array of Float). It may be easier
293
+ # to use the #db_units, #user_units, and/or #m_units methods instead. The
294
+ # units record has two parts, respectively:
295
+ #
296
+ # 1. User units
297
+ # 2. Database units
298
+ #
299
+ # The units in meters can be found by dividing database units by user units
300
+ # (this calculation is done in #m_units).
301
+ #
302
+ def units(); @records.get_data(GRT_UNITS); end
303
+
304
+ #
305
+ # Set the units number. See #units for details. It may be easier to use
306
+ # #db_units= or #user_units= instead.
307
+ #
308
+ def units=(val)
309
+ if val.class == Array
310
+ if val.length == 2
311
+ @user_units, @database_units = val
312
+ update_units
313
+ else
314
+ raise ArgumentError, "UNITS Array must have exactly 2 elements"
315
+ end
316
+ else
317
+ raise TypeError, "Expecting 2 element Array; given: #{val.class}"
318
+ end
319
+ end
320
+
321
+ #
322
+ # Returns the user units (returns Float). See #units for details.
323
+ #
324
+ def user_units(); @user_units; end
325
+
326
+ #
327
+ # Sets the user units. See #units for details.
328
+ #
329
+ def user_units=(val)
330
+ @user_units = val
331
+ update_units
332
+ end
333
+
334
+ #
335
+ # Returns the database units (returns Float). See #units for details.
336
+ #
337
+ def database_units(); @database_units; end
338
+
339
+ #
340
+ # Sets the database units. See #units for details.
341
+ #
342
+ def database_units=(val)
343
+ @database_units = val
344
+ update_units
345
+ end
346
+
347
+ #
348
+ # Get the units in meters (returns Float). Both user and database
349
+ # units must be set. The formula is:
350
+ #
351
+ # m_units = database_units / user_units
352
+ #
353
+ def m_units()
354
+ ((u=user_units) and (d=database_units)) ? d/u : nil
355
+ end
356
+
357
+ #
358
+ # Get the bgnlib record (returns Record).
359
+ #
360
+ def bgnlib_record() @records.get(GRT_BGNLIB); end
361
+
362
+ #
363
+ # Get the bgnlib number (returns Fixnum). This holds the modify/access
364
+ # time stamp for the library. It is probably easier to not access this
365
+ # directly but to use #modify_time and #access_time instead.
366
+ #
367
+ def bgnlib() @records.get_data(GRT_BGNLIB); end
368
+
369
+ #
370
+ # Set the bgnlib number. The value is a Fixnum representation of the
371
+ # modify/access time stamp for the library. It is probably easier to
372
+ # not access this directly but to use #modify_time= and #access_time=
373
+ # instead.
374
+ #
375
+ def bgnlib=(val) @records.set(GRT_BGNLIB, val); end
376
+
377
+ #
378
+ # Accepts a Time object and sets the modify time for the library.
379
+ #
380
+ # struct.modify_time = Time.now
381
+ #
382
+ def modify_time=(time)
383
+ @modify_time = time
384
+ update_times
385
+ end
386
+
387
+ #
388
+ # Returns the modify time for this library (returns Time)
389
+ #
390
+ def modify_time(); @modify_time; end
391
+
392
+ #
393
+ # Accepts a Time object and sets the access time for the library.
394
+ #
395
+ # struct.access_time = Time.now
396
+ #
397
+ def access_time=(time)
398
+ @access_time = time
399
+ update_times
400
+ end
401
+
402
+ #
403
+ # Returns the access time for this library (returns Time)
404
+ #
405
+ def access_time(); @access_time; end
406
+
407
+ #
408
+ # Reads the Library header data of a GDSII file but does not read any
409
+ # Structure records. The Library object is returned (also yielded if
410
+ # a block is given).
411
+ #
412
+ # File.open(file_name, 'rb') do |file|
413
+ # Library.read_header(file) do |lib|
414
+ # puts "The GDSII library name is #{lib.name}"
415
+ # end
416
+ # end
417
+ #
418
+ # See Structure#read_each and Structure#read_each_header for more
419
+ # detailed examples
420
+ #
421
+ def Library.read_header(file)
422
+ Library.read(file, nil, nil, :before_group) do |lib|
423
+ yield lib if block_given?
424
+ break lib
425
+ end
426
+ end
427
+
428
+ #
429
+ # Writes only the header portion of the Library to a file. This is useful
430
+ # when streamlined writing is desired (for better performance or when
431
+ # writing GDSII as another GDSII is being read). Be sure to either:
432
+ #
433
+ # 1. Call #write_footer after writing the header and any Structure
434
+ # objects; Or
435
+ # 2. Pass a block whereupon the footer will automatically be added after
436
+ # the block is processed.
437
+ #
438
+ # Example 1 (manually writing header/footer):
439
+ #
440
+ # File.open(in_file, 'rb') do |inf|
441
+ # File.open(out_file, 'wb') do |outf|
442
+ # Library.read_header(inf) do |lib|
443
+ # lib.write_header(outf)
444
+ # Structure.read_each_header(inf) do |struct|
445
+ # struct.write_header(outf)
446
+ # Element.read_each(inf) {|element| element.write(outf)}
447
+ # struct.write_footer(outf)
448
+ # end
449
+ # lib.write_footer(outf)
450
+ # end
451
+ # end
452
+ # end
453
+ #
454
+ # Example 2 (using a block):
455
+ #
456
+ # File.open(in_file, 'rb') do |inf|
457
+ # File.open(out_file, 'wb') do |outf|
458
+ # Library.read_header(inf) do |lib|
459
+ # lib.write_header(outf) do
460
+ # Structure.read_each_header(inf) do |struct|
461
+ # struct.write_header(outf) do
462
+ # Element.read_each(inf) {|element| element.write(outf)}
463
+ # end
464
+ # end
465
+ # end
466
+ # end
467
+ # end
468
+ # end
469
+ #
470
+ def write_header(file)
471
+ # alter the BNF to exclude Structures and ENDLIB; then write to file
472
+ # according to the modified BNF
473
+ alt_bnf = BnfSpec.new(*Library.bnf_spec.bnf_items[0..-3])
474
+ self.write(file, alt_bnf)
475
+
476
+ # if block is given, then yield to that block and then write the
477
+ # footer afterwards
478
+ if block_given?
479
+ yield
480
+ self.write_footer(file)
481
+ end
482
+ end
483
+
484
+ #
485
+ # Writes only the Library footer (just ENDLIB record) to file. To be used
486
+ # with #write_header.
487
+ #
488
+ def write_footer(file)
489
+ Record.new(GRT_ENDLIB).write(file)
490
+ end
491
+
492
+ #####################
493
+ ## PRIVATE METHODS ##
494
+ #####################
495
+
496
+ private
497
+
498
+ # Used by #modify_time and #access_time
499
+ def update_times()
500
+ if modify_time and access_time
501
+ self.bgnlib = build_time(modify_time) + build_time(access_time)
502
+ else
503
+ self.bgnlib = nil
504
+ end
505
+ end
506
+
507
+ # Used by various units setting methods
508
+ def update_units()
509
+ if @user_units and @database_units
510
+ @records.set(GRT_UNITS, [@user_units, @database_units])
511
+ else
512
+ @records.set(GRT_UNITS, nil)
513
+ end
514
+ end
515
+
516
+ end
517
+ end
518
+