ruby-gdsii 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/LICENSE.txt +20 -0
  2. data/README.txt +113 -0
  3. data/bin/rgds-debug +43 -0
  4. data/bin/rgds-dump +38 -0
  5. data/bin/rgds-join +98 -0
  6. data/bin/rgds-layers +53 -0
  7. data/bin/rgds-sremove +135 -0
  8. data/bin/rgds-ssplit +113 -0
  9. data/bin/rgds-stats +134 -0
  10. data/bin/rgds-structs +41 -0
  11. data/bin/rgds-tree +166 -0
  12. data/bin/rgds2rb +99 -0
  13. data/lib/gdsii.rb +137 -0
  14. data/lib/gdsii/aref.rb +243 -0
  15. data/lib/gdsii/bnf.rb +309 -0
  16. data/lib/gdsii/boundary.rb +53 -0
  17. data/lib/gdsii/box.rb +65 -0
  18. data/lib/gdsii/byte_order.rb +36 -0
  19. data/lib/gdsii/element.rb +172 -0
  20. data/lib/gdsii/group.rb +98 -0
  21. data/lib/gdsii/library.rb +518 -0
  22. data/lib/gdsii/mixins.rb +378 -0
  23. data/lib/gdsii/node.rb +65 -0
  24. data/lib/gdsii/path.rb +169 -0
  25. data/lib/gdsii/property.rb +108 -0
  26. data/lib/gdsii/record.rb +606 -0
  27. data/lib/gdsii/record/consts.rb +384 -0
  28. data/lib/gdsii/record/datatypes/ascii.rb +145 -0
  29. data/lib/gdsii/record/datatypes/bitarray.rb +101 -0
  30. data/lib/gdsii/record/datatypes/data.rb +111 -0
  31. data/lib/gdsii/record/datatypes/int2.rb +67 -0
  32. data/lib/gdsii/record/datatypes/int4.rb +65 -0
  33. data/lib/gdsii/record/datatypes/nodata.rb +60 -0
  34. data/lib/gdsii/record/datatypes/real4.rb +51 -0
  35. data/lib/gdsii/record/datatypes/real8.rb +120 -0
  36. data/lib/gdsii/sref.rb +61 -0
  37. data/lib/gdsii/strans.rb +133 -0
  38. data/lib/gdsii/structure.rb +352 -0
  39. data/lib/gdsii/text.rb +203 -0
  40. data/pkg/ruby-gdsii.gem +23 -0
  41. data/samples/hello.gds +0 -0
  42. data/samples/hello.out.rb +84 -0
  43. data/samples/hello.rb +94 -0
  44. data/test/baseline/dcp1.gds +0 -0
  45. data/test/baseline/h_write.gds +0 -0
  46. data/test/h_pthru.rb +22 -0
  47. data/test/h_write.rb +117 -0
  48. data/test/hs_pthru.rb +31 -0
  49. data/test/l_pthru.rb +23 -0
  50. data/test/test_gds_group.rb +379 -0
  51. data/test/test_gds_record.rb +99 -0
  52. metadata +118 -0
@@ -0,0 +1,384 @@
1
+ module Gdsii
2
+
3
+ #
4
+ # Class to store information about each record type. The Gdsii::RECORD_INFO
5
+ # constant is an array with an index of the record type number and the value
6
+ # being instances of this class. For example:
7
+ #
8
+ # Gdsii::RECORD_INFO[Gdsii::GRT_HEADER].name #=> 'HEADER'
9
+ # Gdsii::RECORD_INFO[Gdsii::GRT_XY].min_len #=> 4
10
+ #
11
+ # See the Gdsii module for a complete listing of record type constants
12
+ # (Gdsii::GRT_*).
13
+ #
14
+ class RecInfo
15
+
16
+ # Name of this record (should be the same as the Gdsii::GRT_* name but
17
+ # without the "Gdsii::GRT_".
18
+ attr_reader :name
19
+
20
+ # Record type is an integer of the constant Gdsii::GRT_* for this record
21
+ # type.
22
+ attr_reader :type
23
+
24
+ # Boolean as to whether or not this record is a valid GDS record (some
25
+ # records are not valid to the GDS specification)
26
+ attr_reader :valid
27
+
28
+ # Data type is an integer of the constant Gdsii::GDT_* for this record.
29
+ # This represents the data type expected for this record.
30
+ attr_reader :data_type
31
+
32
+ # Size indicates how many bytes are necessary to store each element of
33
+ # this record. For example, the size of a Gdsii::GRT_XY record is 8 with a
34
+ # record size of 4 bytes for each record (since it's a Gdsii::GDT_INT4).
35
+ # This means that the minimum number of items needed (#min_items) is 2.
36
+ attr_reader :size
37
+
38
+ # Minimum length (in bytes) to store this record
39
+ attr_reader :min_len
40
+
41
+ # Maximum length (in bytes) to store this record
42
+ attr_reader :max_len
43
+
44
+ # Object constructor. Intended to be used internally only to add elements
45
+ # to the Gdsii::RECORD_INFO array.
46
+ def initialize(name, data_type, valid, size, min_len, max_len)
47
+ @name = name
48
+ @data_type = data_type
49
+ @valid = valid
50
+ @size = size
51
+ @min_len = min_len
52
+ @max_len = max_len
53
+ end
54
+
55
+ # Returns the minimum number of items necessary for this record type.
56
+ def min_items
57
+ case @data_type
58
+ when GDT_NO_DATA : 0
59
+ when GDT_ASCII : (@size == 0) ? 1 : @min_len/@size
60
+ else
61
+ @min_len/@size
62
+ end
63
+ end
64
+
65
+ # Returns the maximum number of items necessary for this record type.
66
+ def max_items
67
+ case @data_type
68
+ when GDT_NO_DATA : 0
69
+ when GDT_ASCII : (@size == 0) ? 1 : @max_len/@size
70
+ else
71
+ @max_len/@size
72
+ end
73
+ end
74
+
75
+ # Returns true if this object has only a single datum; false if it can
76
+ # have multiple data.
77
+ def single_data_value?
78
+ if @data_type == GDT_ASCII
79
+ @size == 0
80
+ else
81
+ @size == @min_len and @size == @max_len
82
+ end
83
+ end
84
+
85
+ # Converts this record to a string (returns the record's name)
86
+ def to_s(); @name; end
87
+
88
+ end
89
+
90
+ ############################################################################
91
+
92
+ #
93
+ # Class to store information about each record data type. The
94
+ # Gdsii::DATATYPE_INFO array has an index of GDT_* integer values with the
95
+ # value being an instance of this class. For example:
96
+ #
97
+ # Gdsii::RECORD_INFO[Gdsii::GDT_ASCII].name #=> 'ASCII'
98
+ # Gdsii::RECORD_INFO[Gdsii::GDT_REAL4].valid #=> false
99
+ #
100
+ class RecDataTypeInfo
101
+
102
+ # Name of this record data type (should be the same as the Gdsii::GDT_* name
103
+ # but without the "Gdsii::GDT_".
104
+ attr_reader :name
105
+
106
+ # Boolean value indicating whether or not this record data type is valid.
107
+ attr_reader :valid
108
+
109
+ # Integer value indicating the size (in bytes) required for this data
110
+ # type. The exception is Gdsii::GDT_ASCII which has a size of 0 but in
111
+ # actuality has a variable-length size.
112
+ attr_reader :size
113
+
114
+ # Object constructor. Intended to be used internally only to add elements
115
+ # to the Gdsii::DATATYPE_INFO array.
116
+ def initialize(name, valid, size)
117
+ @name = name
118
+ @valid = valid
119
+ @size = size
120
+ end
121
+
122
+ # Returns the data type's name
123
+ def to_s(); @name; end
124
+
125
+ end
126
+
127
+ ############################################################################
128
+
129
+ # These numbers correspond to GDSII format indicating the path types
130
+ if not defined?(PATHTYPE_FLUSH)
131
+ PATHTYPE_FLUSH = 0
132
+ PATHTYPE_ROUND = 1
133
+ PATHTYPE_EXTEND = 2
134
+ PATHTYPE_CUSTOM = 4
135
+
136
+ # These numbers correspond to GDSII format
137
+ FORMAT_GDSII_ARCHIVE = 0
138
+ FORMAT_GDSII_FILTERED = 1
139
+ FORMAT_EDSIII_ARCHIVE = 2
140
+ FORMAT_EDSIII_FILTERED = 3
141
+
142
+ # These are GDSII record numbers and correspond to the GDSII number in the
143
+ # GDSII file specification
144
+ GRT_HEADER = 0
145
+ GRT_BGNLIB = 1
146
+ GRT_LIBNAME = 2
147
+ GRT_UNITS = 3
148
+ GRT_ENDLIB = 4
149
+ GRT_BGNSTR = 5
150
+ GRT_STRNAME = 6
151
+ GRT_ENDSTR = 7
152
+ GRT_BOUNDARY = 8
153
+ GRT_PATH = 9
154
+ GRT_SREF = 10
155
+
156
+ GRT_AREF = 11
157
+ GRT_TEXT = 12
158
+ GRT_LAYER = 13
159
+ GRT_DATATYPE = 14
160
+ GRT_WIDTH = 15
161
+ GRT_XY = 16
162
+ GRT_ENDEL = 17
163
+ GRT_SNAME = 18
164
+ GRT_COLROW = 19
165
+ GRT_TEXTNODE = 20
166
+
167
+ GRT_NODE = 21
168
+ GRT_TEXTTYPE = 22
169
+ GRT_PRESENTATION = 23
170
+ GRT_SPACING = 24
171
+ GRT_STRING = 25
172
+ GRT_STRANS = 26
173
+ GRT_MAG = 27
174
+ GRT_ANGLE = 28
175
+ GRT_UINTEGER = 29
176
+ GRT_USTRING = 30
177
+
178
+ GRT_REFLIBS = 31
179
+ GRT_FONTS = 32
180
+ GRT_PATHTYPE = 33
181
+ GRT_GENERATIONS = 34
182
+ GRT_ATTRTABLE = 35
183
+ GRT_STYPTABLE = 36
184
+ GRT_STRTYPE = 37
185
+ GRT_ELFLAGS = 38
186
+ GRT_ELKEY = 39
187
+ GRT_LINKTYPE = 40
188
+
189
+ GRT_LINKKEYS = 41
190
+ GRT_NODETYPE = 42
191
+ GRT_PROPATTR = 43
192
+ GRT_PROPVALUE = 44
193
+ GRT_BOX = 45
194
+ GRT_BOXTYPE = 46
195
+ GRT_PLEX = 47
196
+ GRT_BGNEXTN = 48
197
+ GRT_ENDEXTN = 49
198
+ GRT_TAPENUM = 50
199
+
200
+ GRT_TAPECODE = 51
201
+ GRT_STRCLASS = 52
202
+ GRT_RESERVED = 53
203
+ GRT_FORMAT = 54
204
+ GRT_MASK = 55
205
+ GRT_ENDMASKS = 56
206
+ GRT_LIBDIRSIZE = 57
207
+ GRT_SRFNAME = 58
208
+ GRT_LIBSECUR = 59
209
+ GRT_BORDER = 60
210
+
211
+ GRT_SOFTFENCE = 61
212
+ GRT_HARDFENCE = 62
213
+ GRT_SOFTWIRE = 63
214
+ GRT_HARDWIRE = 64
215
+ GRT_PATHPORT = 65
216
+ GRT_NODEPORT = 66
217
+ GRT_USERCONSTRAINT = 67
218
+ GRT_SPACER_ERROR = 68
219
+ GRT_CONTACT = 69
220
+
221
+ # GDSII record data types
222
+ GDT_NO_DATA = 0
223
+ GDT_BITARRAY = 1
224
+ GDT_INT2 = 2
225
+ GDT_INT4 = 3
226
+ GDT_REAL4 = 4
227
+ GDT_REAL8 = 5
228
+ GDT_ASCII = 6
229
+
230
+ end
231
+
232
+ if not defined?(RECORD_INFO)
233
+
234
+ # Gdsii::RECORD_INFO is an array of Gdsii::RecInfo objects. The array order
235
+ # is significant in that the index of the array is the value of the record
236
+ # and corresponds to the Gdsii::GRT_* constant values. This allows easy
237
+ # validation lookup based upon the record type constants. Example:
238
+ #
239
+ # Gdsii::RECORD_INFO[Gdsii::GRT_HEADER].name # => "HEADER"
240
+ # Gdsii::RECORD_INFO[Gdsii::GRT_HEADER].valid # => true
241
+ # Gdsii::RECORD_INFO[Gdsii::GRT_HEADER].data_type # => 2
242
+ #
243
+ RECORD_INFO =
244
+ [
245
+ # name data_type valid size minlen maxlen num
246
+ RecInfo.new('HEADER', GDT_INT2, true, 2, 2, 2), # 0 - GDS version
247
+ RecInfo.new('BGNLIB', GDT_INT2, true, 2, 24, 24), # 1 - Modification & access time
248
+ RecInfo.new('LIBNAME', GDT_ASCII, true, 0, 0, 65530), # 2
249
+ RecInfo.new('UNITS', GDT_REAL8, true, 8, 16, 16), # 3
250
+ RecInfo.new('ENDLIB', GDT_NO_DATA, true, 0, 0, 0), # 4
251
+ RecInfo.new('BGNSTR', GDT_INT2, true, 2, 24, 24), # 5
252
+
253
+ RecInfo.new('STRNAME', GDT_ASCII, true, 0, 2, 512), # 6
254
+ RecInfo.new('ENDSTR', GDT_NO_DATA, true, 0, 0, 0), # 7
255
+ RecInfo.new('BOUNDARY', GDT_NO_DATA, true, 0, 0, 0), # 8
256
+ RecInfo.new('PATH', GDT_NO_DATA, true, 0, 0, 0), # 9
257
+ RecInfo.new('SREF', GDT_NO_DATA, true, 0, 0, 0), # 10
258
+
259
+ RecInfo.new('AREF', GDT_NO_DATA, true, 0, 0, 0), # 11
260
+ RecInfo.new('TEXT', GDT_NO_DATA, true, 0, 0, 0), # 12
261
+ RecInfo.new('LAYER', GDT_INT2, true, 2, 2, 2), # 13
262
+ RecInfo.new('DATATYPE', GDT_INT2, true, 2, 2, 2), # 14
263
+ RecInfo.new('WIDTH', GDT_INT4, true, 4, 4, 4), # 15
264
+
265
+ RecInfo.new('XY', GDT_INT4, true, 4, 8, 65528), # 16
266
+ RecInfo.new('ENDEL', GDT_NO_DATA, true, 0, 0, 0), # 17
267
+ RecInfo.new('SNAME', GDT_ASCII, true, 0, 2, 65530), # 18
268
+ RecInfo.new('COLROW', GDT_INT2, true, 2, 4, 4), # 19
269
+ RecInfo.new('TEXTNODE', GDT_NO_DATA, true, 0, 0, 0), # 20
270
+
271
+ RecInfo.new('NODE', GDT_NO_DATA, true, 0, 0, 0), # 21
272
+ RecInfo.new('TEXTTYPE', GDT_INT2, true, 2, 2, 2), # 22
273
+ RecInfo.new('PRESENTATION', GDT_BITARRAY, true, 2, 2, 2), # 23
274
+ RecInfo.new('SPACING', 0, false, 0, 0, 0), # 24
275
+ RecInfo.new('STRING', GDT_ASCII, true, 0, 2, 512), # 25
276
+
277
+ RecInfo.new('STRANS', GDT_BITARRAY, true, 2, 2, 2), # 26
278
+ RecInfo.new('MAG', GDT_REAL8, true, 8, 8, 8), # 27
279
+ RecInfo.new('ANGLE', GDT_REAL8, true, 8, 8, 8), # 28
280
+ RecInfo.new('UINTEGER', 0, false, 0, 0, 0), # 29
281
+ RecInfo.new('USTRING', 0, false, 0, 0, 0), # 30
282
+
283
+ RecInfo.new('REFLIBS', GDT_ASCII, true, 44, 88, 748), # 31
284
+ RecInfo.new('FONTS', GDT_ASCII, true, 44, 176, 176), # 32 - paths to text font def files
285
+ RecInfo.new('PATHTYPE', GDT_INT2, true, 2, 2, 2), # 33
286
+ RecInfo.new('GENERATIONS', GDT_INT2, true, 2, 2, 2), # 34
287
+ RecInfo.new('ATTRTABLE', GDT_ASCII, true, 0, 2, 44), # 35 - path of attr def file
288
+
289
+ RecInfo.new('STYPTABLE', 0, false, 0, 0, 0), # 36
290
+ RecInfo.new('STRTYPE', 0, false, 0, 0, 0), # 37
291
+ RecInfo.new('ELFLAGS', GDT_BITARRAY, true, 2, 2, 2), # 38
292
+ RecInfo.new('ELKEY', 0, false, 0, 0, 0), # 39
293
+ RecInfo.new('LINKTYPE', 0, false, 0, 0, 0), # 40
294
+
295
+ RecInfo.new('LINKKEYS', 0, false, 0, 0, 0), # 41
296
+ RecInfo.new('NODETYPE', GDT_INT2, true, 2, 2, 2), # 42
297
+ RecInfo.new('PROPATTR', GDT_INT2, true, 2, 2, 2), # 43
298
+ RecInfo.new('PROPVALUE', GDT_ASCII, true, 0, 2, 126), # 44
299
+ RecInfo.new('BOX', GDT_NO_DATA, true, 0, 0, 0), # 45
300
+
301
+ RecInfo.new('BOXTYPE', GDT_INT2, true, 2, 2, 2), # 46
302
+ RecInfo.new('PLEX', GDT_INT4, true, 4, 4, 4), # 47
303
+ RecInfo.new('BGNEXTN', GDT_INT4, true, 4, 4, 4), # 48
304
+ RecInfo.new('ENDEXTN', GDT_INT4, true, 4, 4, 4), # 49
305
+ RecInfo.new('TAPENUM', GDT_INT2, true, 2, 2, 2), # 50
306
+
307
+ RecInfo.new('TAPECODE', GDT_INT2, true, 2, 12, 12), # 51
308
+ RecInfo.new('STRCLASS', GDT_BITARRAY, true, 2, 2, 2), # 52
309
+ RecInfo.new('RESERVED', 0, false, 0, 0, 0), # 53
310
+ RecInfo.new('FORMAT', GDT_INT2, true, 2, 2, 2), # 54 - GdsiiFormat type
311
+ RecInfo.new('MASK', GDT_ASCII, true, 0, 2, 65530), # 55
312
+
313
+ RecInfo.new('ENDMASKS', GDT_NO_DATA, true, 0, 0, 0), # 56
314
+ RecInfo.new('LIBDIRSIZE', GDT_INT2, true, 2, 2, 2), # 57 - # of pages in lib dir
315
+ RecInfo.new('SRFNAME', GDT_ASCII, true, 0, 2, 65530), # 58 - name of spacing rules file
316
+ RecInfo.new('LIBSECUR', GDT_INT2, true, 2, 6, 192), # 59 - array of ACL data
317
+ RecInfo.new('BORDER', GDT_NO_DATA, true, 0, 0, 0), # 60
318
+
319
+ RecInfo.new('SOFTFENCE', GDT_NO_DATA, true, 0, 0, 0), # 61
320
+ RecInfo.new('HARDFENCE', GDT_NO_DATA, true, 0, 0, 0), # 62
321
+ RecInfo.new('SOFTWIRE', GDT_NO_DATA, true, 0, 0, 0), # 63
322
+ RecInfo.new('HARDWIRE', GDT_NO_DATA, true, 0, 0, 0), # 64
323
+ RecInfo.new('PATHPORT', GDT_NO_DATA, true, 0, 0, 0), # 65
324
+
325
+ RecInfo.new('NODEPORT', GDT_NO_DATA, true, 0, 0, 0), # 66
326
+ RecInfo.new('USERCONSTRAINT', GDT_NO_DATA, true, 0, 0, 0), # 67
327
+ RecInfo.new('SPACER_ERROR', GDT_NO_DATA, true, 0, 0, 0), # 68
328
+ RecInfo.new('CONTACT', GDT_NO_DATA, true, 0, 0, 0), # 69
329
+ ]
330
+
331
+ #
332
+ # Gdsii::DATATYPE_INFO is an array of Gdsii::RecDataTypeInfo objects. The array
333
+ # order is significant in that the index of the array is the value of the
334
+ # record data type constant. This allows easy validation lookup based upon
335
+ # the record data type constants. Example:
336
+ #
337
+ # Gdsii::DATATYPE_INFO[Gdsii::GRT_REAL8].name # => "REAL8"
338
+ # Gdsii::DATATYPE_INFO[Gdsii::GRT_REAL8].valid # => true
339
+ # Gdsii::DATATYPE_INFO[Gdsii::GRT_REAL8].size # => 8
340
+ #
341
+ DATATYPE_INFO =
342
+ [
343
+ RecDataTypeInfo.new('NO_DATA', true, 0 ), # 0
344
+ RecDataTypeInfo.new('BITARRAY', true, 2 ), # 1
345
+ RecDataTypeInfo.new('INT2', true, 2 ), # 2
346
+ RecDataTypeInfo.new('INT4', true, 4 ), # 3
347
+ RecDataTypeInfo.new('REAL4', false, 4 ), # 4
348
+ RecDataTypeInfo.new('REAL8', true, 8 ), # 5
349
+ RecDataTypeInfo.new('ASCII', true, 0 ), # 6 ; string len is variable
350
+ ]
351
+ end
352
+
353
+ # Returns the name for given record type if it is found; if not, then
354
+ # the record number formatted as a String is returned
355
+ def grt_name(grt_number)
356
+ if grt_number.class == Fixnum
357
+ if (0..RECORD_INFO.length-1).member?(grt_number)
358
+ RECORD_INFO[grt_number].name
359
+ else
360
+ grt_number.to_s
361
+ end
362
+ else
363
+ grt_number.inspect
364
+ end
365
+ end
366
+
367
+ # Returns the name for given record data type if it is found; if not, then
368
+ # the record data type number formatted as a String is returned
369
+ def gdt_name(gdt_number)
370
+ if gdt_number.class == Fixnum
371
+ if (0..DATATYPE_INFO.length-1).member?(gdt_number)
372
+ DATATYPE_INFO[gdt_number].name
373
+ else
374
+ gdt_number.to_s
375
+ end
376
+ else
377
+ gdt_number.inspect
378
+ end
379
+ end
380
+
381
+ module_function :grt_name
382
+ module_function :gdt_name
383
+
384
+ end
@@ -0,0 +1,145 @@
1
+ require 'gdsii/record/datatypes/data'
2
+
3
+ module Gdsii
4
+
5
+ module RecData
6
+
7
+ #
8
+ # Class for ASCII data type
9
+ #
10
+ class Ascii < Data
11
+
12
+ # Value is an array of strings. Most Gdsii::RecData::Ascii records only
13
+ # have a single value.
14
+ attr_reader :value
15
+
16
+ # Construct an Gdsii::RecData::Ascii data object. The value is an array
17
+ # of strings (String).
18
+ def initialize(value, record=nil)
19
+ super(GDT_ASCII, value, record)
20
+ end
21
+
22
+ # Set the value for this object; verify that the value items are of
23
+ # type String (or at least can be coerced using "to_s").
24
+ def value=(value)
25
+ @value = Data.coerce_value(value, String, :to_s)
26
+ end
27
+
28
+ # Returns the size of the record *data* in bytes. This will *always*
29
+ # return an even-length (multiple of 2) number since odd-length GDSII
30
+ # strings are padded with null characters when written to a file. If the
31
+ # record type has a specific size (i.e. Gdsii::GRT_FONTS and
32
+ # Gdsii::GRT_REFLIBS) then that size will automatically be used for each
33
+ # element in the value array.
34
+ def byte_size()
35
+ if @record and RECORD_INFO[@record.type].size > 0 then
36
+ RECORD_INFO[@record.type].size * @value.length
37
+ else
38
+ sum = 0
39
+ @value.each do |val|
40
+ sum += (val.length % 2 == 0) ? val.length : val.length + 1
41
+ end
42
+ sum
43
+ end
44
+ end
45
+
46
+ # Return value with stripped off trailing null characters (which are
47
+ # present when reading this record from a file). For example, assuming
48
+ # that a record is read from a file and is a null padded string
49
+ # "hello\0", then:
50
+ #
51
+ # record.inspect #=> ["hello\0"]
52
+ # record.unpad.inspect #=> ["hello"]
53
+ #
54
+ def unpad()
55
+ new_arr = []
56
+ @value.each do |string|
57
+ string = string.dup # to avoid changing the original
58
+ while (string[-1] == 0)
59
+ string.chop!
60
+ end
61
+ new_arr.push string
62
+ end
63
+ new_arr
64
+ end
65
+
66
+ # Same as #unpad but modifies the value of this object.
67
+ def unpad!()
68
+ @value = unpad
69
+ end
70
+
71
+ # Pad the value with a null character if the string is odd (but do not
72
+ # change the value itself). If a desired string length is given, then
73
+ # the string will be padded by the length given. For example, if we
74
+ # created a record with the string "hello", then padding will add a
75
+ # null character since "hello" has an odd number of characters:
76
+ #
77
+ # record = Gdsii::RecType::Ascii.new(["hello"])
78
+ # record.pad.inspect #=> ["hello\0"]
79
+ #
80
+ def pad(str_length=nil)
81
+ new_arr = []
82
+ @value.each_with_index do |string, i|
83
+ string = string.dup # to avoid changing the original
84
+ if str_length.nil? then
85
+ # pad if the string is odd or use the size property if there is a
86
+ # predefined size
87
+ if @record and (size=RECORD_INFO[@record.type].size) > 0 then
88
+ new_arr.push [string].pack("a#{size}")
89
+ elsif (len=string.length)%2 == 1 then
90
+ new_arr.push [string].pack("a#{len+1}")
91
+ else
92
+ new_arr.push string
93
+ end
94
+ else
95
+ # A desired string length was given; ensure that the requested
96
+ # length is a multiple of 2 and is not less than the string given.
97
+ if str_length%2 == 1 then
98
+ raise ArgumentError,
99
+ "Desired string length must be a multiple of 2"
100
+ elsif str_length < string.length then
101
+ raise ArgumentError,
102
+ "Desired string length given #{str_length} is less than actual string length #{string.length}"
103
+ else
104
+ new_arr.push [string].pack("a#{str_length}")
105
+ end
106
+ end
107
+ end
108
+ new_arr
109
+ end
110
+
111
+ # Same as #pad except the value of this object is modified.
112
+ def pad!(str_length=nil)
113
+ @value = pad(str_length)
114
+ end
115
+
116
+ # Reads an ASCII record from the given file for the length of bytes
117
+ # given and returns a new Gdsii::RecData::Ascii object.
118
+ def Ascii.read(file, byte_count)
119
+ # Verify byte count is even
120
+ if byte_count%2 == 1 then
121
+ raise ArgumentError,
122
+ "GDT_ASCII records must have an even length; requested: #{byte_count}"
123
+ end
124
+
125
+ # read the string in; unpad; and return the new Gdsii::Ascii object
126
+ raw = file.read(byte_count)
127
+ string = raw.unpack("a#{byte_count}")
128
+ data = Ascii.new(string)
129
+ data.unpad!
130
+ end
131
+
132
+ # Writes the string values in this Gdsii::RecData::Ascii object to the
133
+ # given file as a GDSII ASCII record.
134
+ def write(file)
135
+ padded_str = self.pad
136
+ file.write padded_str
137
+ end
138
+
139
+ # Joins all strings in the array with spaces and returns the joined
140
+ # string.
141
+ def to_s(); self.unpad[0] end
142
+
143
+ end
144
+ end
145
+ end