jfreeze-ruby-gdsii 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+