Initial commit of OpenSPARC T2 design and verification files.
[OpenSPARC-T2-DV] / tools / perl-5.8.0 / lib / site_perl / 5.8.0 / GDS2.pm
CommitLineData
86530b38
AT
1package GDS2;
2{
3require 5.006;
4$GDS2::VERSION = '1.3.1';
5## Note: '@ ( # )' used by the what command E.g. what GDS2.pm
6$GDS2::revision = '@(#) $RCSfile: GDS2.pm,v $ $Revision: 1.64 $ $Date: 2003-03-21 15:32:23-06 $';
7use strict;
8use Config;
9use IO::File;
10use warnings;
11#use Attribute::Profiled;
12no strict qw( refs );
13my $G_timer;
14use constant TRUE => 1;
15use constant FALSE => 0;
16use constant TIMER_ON => FALSE;
17if (TIMER_ON)
18{
19 #use Benchmark::Timer;
20 $G_timer = new Benchmark::Timer;
21}
22my $haveFlock=TRUE; ## some systems still may not have this...manually change
23if ($haveFlock)
24{
25 use Fcntl q(:flock); # import LOCK_* constants
26}
27my $isLittleEndian = 0; #default
28$isLittleEndian = 1 if ($Config{'byteorder'} =~ m|^1|); ## mswin32 cygwin vms
29
30# POD documentation is sprinkled throughout the file in an
31# attempt at Literate Programming style (which Perl partly supports ...
32# see http://www.literateprogramming.com/ )
33# Search for the strings '=head' or run perldoc on this file.
34
35# You can run this file through either pod2man or pod2html to produce
36# documentation in manual or html file format
37
38# Author: Ken Schumack (c) 1999,2000,2001,2002,2003. All rights reserved.
39# source code may be used and modified freely, but this copyright notice
40# must remain attached to the file. You may modify this module as you
41# wish, but if you create a modified version, please attach a note
42# listing the modifications you have made, and send a copy to me at
43# schumack@cpan.org
44#
45# Contributor Modification: Peter Baumbach 2002-01-11
46# returnRecordAsPerl() was created to facilitate the creation of
47# parameterized gds2 data with perl.
48#
49
50################################################################################
51## GDS2 STREAM RECORD DATATYPES
52use constant NO_DATA => 0;
53use constant BIT_ARRAY => 1;
54use constant INTEGER_2 => 2;
55use constant INTEGER_4 => 3;
56use constant REAL_4 => 4; ## NOT supported
57use constant REAL_8 => 5;
58use constant ACSII_STRING => 6;
59################################################################################
60
61################################################################################
62## GDS2 STREAM RECORD TYPES
63use constant HEADER => 0; ## 2-byte Signed Integer
64use constant BGNLIB => 1; ## 2-byte Signed Integer
65use constant LIBNAME => 2; ## ASCII String
66use constant UNITS => 3; ## 8-byte Real
67use constant ENDLIB => 4; ## no data present
68use constant BGNSTR => 5; ## 2-byte Signed Integer
69use constant STRNAME => 6; ## ASCII String
70use constant ENDSTR => 7; ## no data present
71use constant BOUNDARY => 8; ## no data present
72use constant PATH => 9; ## no data present
73use constant SREF => 10; ## no data present
74use constant AREF => 11; ## no data present
75use constant TEXT => 12; ## no data present
76use constant LAYER => 13; ## 2-byte Signed Integer
77use constant DATATYPE => 14; ## 2-byte Signed Integer
78use constant WIDTH => 15; ## 4-byte Signed Integer
79use constant XY => 16; ## 2-byte Signed Integer
80use constant ENDEL => 17; ## no data present
81use constant SNAME => 18; ## ASCII String
82use constant COLROW => 19; ## 2 2-byte Signed Integer
83use constant TEXTNODE => 20; ## no data present
84use constant NODE => 21; ## no data present
85use constant TEXTTYPE => 22; ## 2-byte Signed Integer
86use constant PRESENTATION => 23; ## Bit Array
87use constant SPACING => 24; ## discontinued
88use constant STRING => 25; ## ASCII String
89use constant STRANS => 26; ## Bit Array
90use constant MAG => 27; ## 8-byte Real
91use constant ANGLE => 28; ## 8-byte Real
92use constant UINTEGER => 29; ## UNKNOWN User int, used only in Calma V2.0
93use constant USTRING => 30; ## UNKNOWN User string, used only in Calma V2.0
94use constant REFLIBS => 31; ## ASCII String
95use constant FONTS => 32; ## ASCII String
96use constant PATHTYPE => 33; ## 2-byte Signed Integer
97use constant GENERATIONS => 34; ## 2-byte Signed Integer
98use constant ATTRTABLE => 35; ## ASCII String
99use constant STYPTABLE => 36; ## ASCII String "Unreleased feature"
100use constant STRTYPE => 37; ## 2-byte Signed Integer "Unreleased feature"
101use constant EFLAGS => 38; ## BIT_ARRAY Flags for template and exterior data. bits 15 to 0, l to r 0=template,
102 ## 1=external data, others unused
103use constant ELKEY => 39; ## INTEGER_4 "Unreleased feature"
104use constant LINKTYPE => 40; ## UNKNOWN "Unreleased feature"
105use constant LINKKEYS => 41; ## UNKNOWN "Unreleased feature"
106use constant NODETYPE => 42; ## INTEGER_2 Nodetype specification. On Calma this could be 0 to 63, GDSII allows 0 to 255.
107 ## Of course a 2 byte integer allows up to 65535...
108use constant PROPATTR => 43; ## INTEGER_2 Property number.
109use constant PROPVALUE => 44; ## STRING Property value. On GDSII, 128 characters max, unless an SREF, AREF, or NODE,
110 ## which may have 512 characters.
111use constant BOX => 45; ## NO_DATA The beginning of a BOX element.
112use constant BOXTYPE => 46; ## INTEGER_2 Boxtype specification.
113use constant PLEX => 47; ## INTEGER_4 Plex number and plexhead flag. The least significant bit of the most significant
114 ## byte is the plexhead flag.
115use constant BGNEXTN => 48; ## INTEGER_4 Path extension beginning for pathtype 4 in Calma CustomPlus. In database units,
116 ## may be negative.
117use constant ENDEXTN => 49; ## INTEGER_4 Path extension end for pathtype 4 in Calma CustomPlus. In database units, may be negative.
118use constant TAPENUM => 50; ## INTEGER_2 Tape number for multi-reel stream file.
119use constant TAPECODE => 51; ## INTEGER_2 Tape code to verify that the reel is from the proper set. 12 bytes that are
120 ## supposed to form a unique tape code.
121use constant STRCLASS => 52; ## BIT_ARRAY Calma use only.
122use constant RESERVED => 53; ## INTEGER_4 Used to be NUMTYPES per Calma GDSII Stream Format Manual, v6.0.
123use constant FORMAT => 54; ## INTEGER_2 Archive or Filtered flag. 0: Archive 1: filtered
124use constant MASK => 55; ## STRING Only in filtered streams. Layers and datatypes used for mask in a filtered
125 ## stream file. A string giving ranges of layers and datatypes separated by a semicolon.
126 ## There may be more than one mask in a stream file.
127use constant ENDMASKS => 56; ## NO_DATA The end of mask descriptions.
128use constant LIBDIRSIZE => 57; ## INTEGER_2 Number of pages in library director, a GDSII thing, it seems to have only been
129 ## used when Calma INFORM was creating a new library.
130use constant SRFNAME => 58; ## STRING Calma "Sticks"(c) rule file name.
131use constant LIBSECUR => 59; ## INTEGER_2 Access control list stuff for CalmaDOS, ancient. INFORM used this when creating
132 ## a new library. Had 1 to 32 entries with group numbers, user numbers and access rights.
133#################################################################################################
134
135my %RecordTypeNumbers=(
136'HEADER' => 0,
137'BGNLIB' => 1,
138'LIBNAME' => 2,
139'UNITS' => 3,
140'ENDLIB' => 4,
141'BGNSTR' => 5,
142'STRNAME' => 6,
143'ENDSTR' => 7,
144'BOUNDARY' => 8,
145'PATH' => 9,
146'SREF' => 10,
147'AREF' => 11,
148'TEXT' => 12,
149'LAYER' => 13,
150'DATATYPE' => 14,
151'WIDTH' => 15,
152'XY' => 16,
153'ENDEL' => 17,
154'SNAME' => 18,
155'COLROW' => 19,
156'TEXTNODE' => 20,
157'NODE' => 21,
158'TEXTTYPE' => 22,
159'PRESENTATION'=> 23,
160'SPACING' => 24,
161'STRING' => 25,
162'STRANS' => 26,
163'MAG' => 27,
164'ANGLE' => 28,
165'UINTEGER' => 29,
166'USTRING' => 30,
167'REFLIBS' => 31,
168'FONTS' => 32,
169'PATHTYPE' => 33,
170'GENERATIONS' => 34,
171'ATTRTABLE' => 35,
172'STYPTABLE' => 36,
173'STRTYPE' => 37,
174'EFLAGS' => 38,
175'ELKEY' => 39,
176'LINKTYPE' => 40,
177'LINKKEYS' => 41,
178'NODETYPE' => 42,
179'PROPATTR' => 43,
180'PROPVALUE' => 44,
181'BOX' => 45,
182'BOXTYPE' => 46,
183'PLEX' => 47,
184'BGNEXTN' => 48,
185'ENDEXTN' => 49,
186'TAPENUM' => 50,
187'TAPECODE' => 51,
188'STRCLASS' => 52,
189'RESERVED' => 53,
190'FORMAT' => 54,
191'MASK' => 55,
192'ENDMASKS' => 56,
193'LIBDIRSIZE' => 57,
194'SRFNAME' => 58,
195'LIBSECUR' => 59,
196);
197
198my @RecordTypeStrings=( ## for ascii print of GDS
199'HEADER',
200'BGNLIB',
201'LIBNAME',
202'UNITS',
203'ENDLIB',
204'BGNSTR',
205'STRNAME',
206'ENDSTR',
207'BOUNDARY',
208'PATH',
209'SREF',
210'AREF',
211'TEXT',
212'LAYER',
213'DATATYPE',
214'WIDTH',
215'XY',
216'ENDEL',
217'SNAME',
218'COLROW',
219'TEXTNODE',
220'NODE',
221'TEXTTYPE',
222'PRESENTATION',
223'SPACING',
224'STRING',
225'STRANS',
226'MAG',
227'ANGLE',
228'UINTEGER',
229'USTRING',
230'REFLIBS',
231'FONTS',
232'PATHTYPE',
233'GENERATIONS',
234'ATTRTABLE',
235'STYPTABLE',
236'STRTYPE',
237'EFLAGS',
238'ELKEY',
239'LINKTYPE',
240'LINKKEYS',
241'NODETYPE',
242'PROPATTR',
243'PROPVALUE',
244'BOX',
245'BOXTYPE',
246'PLEX',
247'BGNEXTN',
248'ENDEXTN',
249'TAPENUM',
250'TAPECODE',
251'STRCLASS',
252'RESERVED',
253'FORMAT',
254'MASK',
255'ENDMASKS',
256'LIBDIRSIZE',
257'SRFNAME',
258'LIBSECUR',
259);
260
261###################################################
262my %RecordTypeData=(
263'HEADER' => INTEGER_2,
264'BGNLIB' => INTEGER_2,
265'LIBNAME' => ACSII_STRING,
266'UNITS' => REAL_8,
267'ENDLIB' => NO_DATA,
268'BGNSTR' => INTEGER_2,
269'STRNAME' => ACSII_STRING,
270'ENDSTR' => NO_DATA,
271'BOUNDARY' => NO_DATA,
272'PATH' => NO_DATA,
273'SREF' => NO_DATA,
274'AREF' => NO_DATA,
275'TEXT' => NO_DATA,
276'LAYER' => INTEGER_2,
277'DATATYPE' => INTEGER_2,
278'WIDTH' => INTEGER_4,
279'XY' => INTEGER_4,
280'ENDEL' => NO_DATA,
281'SNAME' => ACSII_STRING,
282'COLROW' => INTEGER_2,
283'TEXTNODE' => NO_DATA,
284'NODE' => NO_DATA,
285'TEXTTYPE' => INTEGER_2,
286'PRESENTATION' => BIT_ARRAY,
287'SPACING' => -1, #INTEGER_4, discontinued
288'STRING' => ACSII_STRING,
289'STRANS' => BIT_ARRAY,
290'MAG' => REAL_8,
291'ANGLE' => REAL_8,
292'UINTEGER' => -1, #INTEGER_4, no longer used
293'USTRING' => -1, #ACSII_STRING, no longer used
294'REFLIBS' => ACSII_STRING,
295'FONTS' => ACSII_STRING,
296'PATHTYPE' => INTEGER_2,
297'GENERATIONS' => INTEGER_2,
298'ATTRTABLE' => ACSII_STRING,
299'STYPTABLE' => ACSII_STRING, # unreleased feature
300'STRTYPE' => INTEGER_2, #INTEGER_2, unreleased feature
301'EFLAGS' => BIT_ARRAY,
302'ELKEY' => INTEGER_4, #INTEGER_4, unreleased feature
303'LINKTYPE' => INTEGER_2, #unreleased feature
304'LINKKEYS' => INTEGER_4, #unreleased feature
305'NODETYPE' => INTEGER_2,
306'PROPATTR' => INTEGER_2,
307'PROPVALUE' => ACSII_STRING,
308'BOX' => NO_DATA,
309'BOXTYPE' => INTEGER_2,
310'PLEX' => INTEGER_4,
311'BGNEXTN' => INTEGER_4,
312'ENDEXTN' => INTEGER_4,
313'TAPENUM' => INTEGER_2,
314'TAPECODE' => INTEGER_2,
315'STRCLASS' => -1,
316'RESERVED' => INTEGER_4,
317'FORMAT' => INTEGER_2,
318'MASK' => ACSII_STRING,
319'ENDMASKS' => NO_DATA,
320'LIBDIRSIZE' => -1, #INTEGER_2
321'SRFNAME' => ACSII_STRING,
322'LIBSECUR' => -1, #INTEGER_2,
323);
324
325# This is the default class for the GDS2 object to use when all else fails.
326$GDS2::DefaultClass = 'GDS2' unless defined $GDS2::DefaultClass;
327my $StrSpace='';
328my $ElmSpace='';
329my $G_epsilon=0.00001; ## to take care of floating point representation problems
330
331=pod
332=head1 NAME
333
334GDS2 - GDS2 stream module
335
336
337=head1 Description
338
339This is GDS2, a module for quickly creating programs to read and/or write GDS2 files.
340
341Send feedback/suggestions to
342schumack@cpan.org
343
344
345=head1 Create Method
346
347=cut
348
349################################################################################
350
351=head2 new - open gds2 file
352
353 usage:
354 my $gds2File = new GDS2(-fileName => "filename.gds2"); ## to read
355 my $gds2File2 = new GDS2(-fileName => ">filename.gds2"); ## to write
356
357=cut
358
359sub new #: Profiled
360{
361 my($class,%arg) = @_;
362 my $self = {};
363 bless $self,$class || ref $class || $GDS2::DefaultClass;
364 my $fileName = $arg{'-fileName'};
365 if (! defined $fileName)
366 {
367 die "new expects a gds2 file name. Missing -fileName => 'name' $!";
368 }
369 my $resolution = $arg{'-resolution'};
370 if (! defined $resolution)
371 {
372 $resolution=1000;
373 }
374 die "new expects a positive integer resolution. ($resolution) $!" if (($resolution <= 0) || ($resolution !~ m|^\d+$|));
375 my $lockMode = LOCK_SH; ## default
376 my $openModStr = substr($fileName,0,2); ### looking for > or >>
377 $openModStr =~ s|^\s+||;
378 $openModStr =~ s|[^\+>]+||g;
379 my $openModeNum = O_RDONLY;
380 if ($openModStr =~ m|^\+|)
381 {
382 warn("Ignoring '+' in open mode"); ## not handling this yet...
383 $openModStr =~ s|\++||;
384 }
385 if ($openModStr eq '>')
386 {
387 $openModeNum = O_WRONLY|O_CREAT;
388 $lockMode = LOCK_EX;
389 $fileName =~ s|^$openModStr||;
390 }
391 elsif ($openModStr eq '>>')
392 {
393 $openModeNum = O_WRONLY|O_APPEND;
394 $lockMode = LOCK_EX;
395 $fileName =~ s|^$openModStr||;
396 }
397 my $fileHandle = new IO::File;
398 $fileHandle -> open("$fileName",$openModeNum) or die "Unable to open $fileName because $!";
399 if ($haveFlock)
400 {
401 flock($fileHandle,$lockMode) or die "File lock on $fileName failed because $!";
402 }
403 binmode $fileHandle,':raw'; ## may need Perl 5.6 for discipline
404 $self -> {'FileHandle'} = $fileHandle;
405 $self -> {'FileName'} = $fileName; ## the gds2 filename
406 $self -> {'GDSLENGTH'} = 0; ## total file size so far
407 $self -> {'EOLIB'} = 0; ## end of library flag
408 $self -> {'HEADER'} = -1; ## in header flag
409 $self -> {'INDATA'} = 0; ## in data flag
410 $self -> {'Length'} = 0; ## length of data
411 $self -> {'DataType'} = -1; ## one of 7 gds datatypes
412 $self -> {'UUnits'} = 0; ## for gds2 file
413 $self -> {'DBUnits'} = 0; ## for gds2 file
414 $self -> {'Record'} = ''; ## the whole record as found in gds2 file
415 $self -> {'RecordType'} = -1;
416 $self -> {'DataIndex'} = 0;
417 $self -> {'RecordData'} = ('');
418 $self -> {'CurrentDataList'} = '';
419 $self -> {'InStr'} = 0; ##flag for write error checking
420 $self -> {'InElm'} = 0; ##flag for write error checking
421 $self -> {'Resolution'} = $resolution;
422 $self -> {'UsingPrettyPrint'} = 0; ## print as string ...
423 $self;
424}
425################################################################################
426
427=head2 close - close gds2 file
428
429 usage:
430 $gds2File -> close;
431 -or-
432 $gds2File -> close(-markEnd=>1); ## experimental -- some systems have trouble closing files
433 $gds2File -> close(-pad=>2048); ## experimental -- pad end with \0's till file size is a
434 ## multiple of number. Note: old reel to reel tapes on Calma
435 ## systems used 2048 byte blocks
436
437=cut
438
439sub close #: Profiled
440{
441 my($self,%arg) = @_;
442 my $markEnd = $arg{'-markEnd'};
443 my $pad = $arg{'-pad'};
444 if ((defined $markEnd)&&($markEnd))
445 {
446 my $fh = $self -> {'FileHandle'};
447 print $fh "\x1a\x04"; # a ^Z and a ^D
448 $self -> {'GDSLENGTH'} += 2;
449 }
450 if ((defined $pad)&&($pad > 0))
451 {
452 my $fh = $self -> {'FileHandle'};
453 my $fileSize = $self -> tellSize;
454 my $padSize = $pad - ($fileSize % $pad);
455 $padSize=0 if ($padSize == $pad);
456 for (my $i=0; $i < $padSize; $i++)
457 {
458 print $fh "\0"; ## a null
459 }
460 }
461 $self -> {'FileHandle'} -> close;
462}
463################################################################################
464
465################################################################################
466
467=head1 High Level Write Methods
468
469=cut
470
471################################################################################
472
473=head2 printInitLib() - Does all the things needed to start a library
474
475 usage:
476 $gds2File -> printInitLib(-name => "testlib", ##writes HEADER,BGNLIB,LIBNAME,and UNITS records
477 -isoDate => 0|1 ## (optional) use ISO 4 digit date 2001 vs 101
478 );
479 ## defaults to current date for library date and 1e-3 and 1e-9 for units
480
481 note:
482 remember to close library with printEndlib()
483
484=cut
485
486sub printInitLib #: Profiled
487{
488 my($self,%arg) = @_;
489 my $libName = $arg{'-name'};
490 if (! defined $libName)
491 {
492 die "printInitLib expects a library name. Missing -name => 'name' $!";
493 }
494 my $isoDate = $arg{'-isoDate'};
495 if (! defined $isoDate)
496 {
497 $isoDate = FALSE;
498 }
499 elsif ($isoDate != 0)
500 {
501 $isoDate = TRUE;
502 }
503 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
504 $mon++;
505 $year += 1900 if ($isoDate); ## Cadence likes year left "as is". GDS format supports year number up to 65535 -- 101 vs 2001
506 $self -> printGds2Record(-type => 'HEADER',-data => 3); ## GDS2 HEADER
507 $self -> printGds2Record(-type => 'BGNLIB',-data => [$year,$mon,$mday,$hour,$min,$sec,$year,$mon,$mday,$hour,$min,$sec]);
508 $self -> printGds2Record(-type => 'LIBNAME',-data => $libName);
509 $self -> printGds2Record(-type => 'UNITS',-data => [0.001,1e-9]);
510}
511################################################################################
512
513=head2 printBgnstr - Does all the things needed to start a structure definition
514
515 usage:
516 $gds2File -> printBgnstr(-name => "nand3" ## writes BGNSTR and STRNAME records
517 -isoDate => 1|0 ## (optional) use ISO 4 digit date 2001 vs 101
518 );
519
520 note:
521 remember to close with printEndstr()
522
523=cut
524
525sub printBgnstr #: Profiled
526{
527 my($self,%arg) = @_;
528
529 my $strName = $arg{'-name'};
530 if (! defined $strName)
531 {
532 die "bgnStr expects a structure name. Missing -name => 'name' $!";
533 }
534 my $createTime = $arg{'-createTime'};
535 my $isoDate = $arg{'-isoDate'};
536 if (! defined $isoDate)
537 {
538 $isoDate = FALSE;
539 }
540 elsif ($isoDate != 0)
541 {
542 $isoDate = TRUE;
543 }
544 my ($csec,$cmin,$chour,$cmday,$cmon,$cyear,$cwday,$cyday,$cisdst);
545 if (defined $createTime)
546 {
547 ($csec,$cmin,$chour,$cmday,$cmon,$cyear,$cwday,$cyday,$cisdst) = localtime($createTime);
548 }
549 else
550 {
551 ($csec,$cmin,$chour,$cmday,$cmon,$cyear,$cwday,$cyday,$cisdst) = localtime(time);
552 }
553 $cmon++;
554
555 my $modTime = $arg{'-modTime'};
556 my ($msec,$mmin,$mhour,$mmday,$mmon,$myear,$mwday,$myday,$misdst);
557 if (defined $modTime)
558 {
559 ($msec,$mmin,$mhour,$mmday,$mmon,$myear,$mwday,$myday,$misdst) = localtime($modTime);
560 }
561 else
562 {
563 ($msec,$mmin,$mhour,$mmday,$mmon,$myear,$mwday,$myday,$misdst) = localtime(time);
564 }
565 $mmon++;
566
567 if ($isoDate)
568 {
569 $cyear += 1900; ## 2001 vs 101
570 $myear += 1900;
571 }
572 $self -> printGds2Record(-type => 'BGNSTR',-data => [$cyear,$cmon,$cmday,$chour,$cmin,$csec,$myear,$mmon,$mmday,$mhour,$mmin,$msec]);
573 $self -> printGds2Record(-type => 'STRNAME',-data => $strName);
574}
575################################################################################
576
577=head2 printPath - prints a gds2 path
578
579 usage:
580 $gds2File -> printPath(
581 -layer=>#,
582 -dataType=>#, ##optional
583 -pathType=>#,
584 -width=>#.#,
585 -unitWidth=>#, ## (optional) directly specify width in data base units (vs -width which is multipled by resolution)
586 -xy=>\@array, ## array of reals
587 -xyInt=>\@array, ## array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
588 );
589
590 note:
591 layer defaults to 0 if -layer not used
592 pathType defaults to 0 if -pathType not used
593 pathType 0 = square end
594 1 = round end
595 2 = square - extended 1/2 width
596 4 = custom plus variable path extension...
597 width defaults to 0.0 if -width not used
598
599=cut
600
601# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
602# <path>::= PATH [ELFLAGS] [PLEX] LAYER DATATYPE [PATHTYPE] [WIDTH] XY
603sub printPath #: Profiled
604{
605 my($self,%arg) = @_;
606 my $resolution = $self -> {'Resolution'};
607 my $layer = $arg{'-layer'};
608 if (! defined $layer)
609 {
610 $layer=0;
611 }
612 my $dataType = $arg{'-dataType'};
613 if (! defined $dataType)
614 {
615 $dataType=0;
616 }
617 my $pathType = $arg{'-pathType'};
618 if (! defined $pathType)
619 {
620 $pathType=0;
621 }
622 my $bgnExtn = $arg{'-bgnExtn'};
623 if (! defined $bgnExtn)
624 {
625 $bgnExtn=0;
626 }
627 my $endExtn = $arg{'-endExtn'};
628 if (! defined $endExtn)
629 {
630 $endExtn=0;
631 }
632 my $unitWidth = $arg{'-unitWidth'};
633 my $widthReal = $arg{'-width'};
634 my $width = 0;
635 if ((defined $unitWidth)&&($unitWidth >= 0))
636 {
637 $width=int($unitWidth);
638 }
639 if ((defined $widthReal)&&($widthReal >= 0.0))
640 {
641 $width = int(($widthReal*$resolution)+$G_epsilon);
642 }
643 #### -xyInt most useful if reading and modifying... -xy if creating from scratch
644 my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
645 my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
646 my @xyTmp=(); ##don't pollute array passed in
647 if (! ((defined $xy) || (defined $xyInt)))
648 {
649 die "printPath expects an xy array reference. Missing -xy => \\\@array $!";
650 }
651 if (defined $xyInt)
652 {
653 $xy = $xyInt;
654 $resolution=1;
655 }
656 $self -> printGds2Record(-type => 'PATH');
657 $self -> printGds2Record(-type => 'LAYER',-data => $layer);
658 $self -> printGds2Record(-type => 'DATATYPE',-data => $dataType);
659 $self -> printGds2Record(-type => 'PATHTYPE',-data => $pathType) if ($pathType);
660 $self -> printGds2Record(-type => 'WIDTH',-data => $width) if ($width);
661 if ($pathType == 4)
662 {
663 $self -> printGds2Record(-type => 'BGNEXTN',-data => $bgnExtn); ## int used with resolution
664 $self -> printGds2Record(-type => 'ENDEXTN',-data => $endExtn); ## int used with resolution
665 }
666 for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
667 {
668 if ($xy -> [$i] >= 0) { push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
669 else { push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
670 }
671 if ($bgnExtn || $endExtn) ## we have to convert
672 {
673 my $bgnX1 = $xyTmp[0];
674 my $bgnY1 = $xyTmp[1];
675 my $bgnX2 = $xyTmp[2];
676 my $bgnY2 = $xyTmp[3];
677 my $endX1 = $xyTmp[$#xyTmp - 1];
678 my $endY1 = $xyTmp[$#xyTmp];
679 my $endX2 = $xyTmp[$#xyTmp - 3];
680 my $endY2 = $xyTmp[$#xyTmp - 2];
681 if ($bgnExtn)
682 {
683 if ($bgnX1 == $bgnX2) #vertical ...modify 1st Y
684 {
685 if ($bgnY1 < $bgnY2) ## points down
686 {
687 $xyTmp[1] -= $bgnExtn;
688 $xyTmp[1] += int($width/2) if ($pathType != 0);
689 }
690 else ## points up
691 {
692 $xyTmp[1] += $bgnExtn;
693 $xyTmp[1] -= int($width/2) if ($pathType != 0);
694 }
695 }
696 elsif ($bgnY1 == $bgnY2) #horizontal ...modify 1st X
697 {
698 if ($bgnX1 < $bgnX2) ## points left
699 {
700 $xyTmp[0] -= $bgnExtn;
701 $xyTmp[0] += int($width/2) if ($pathType != 0);
702 }
703 else ## points up
704 {
705 $xyTmp[0] += $bgnExtn;
706 $xyTmp[0] -= int($width/2) if ($pathType != 0);
707 }
708 }
709 }
710
711 if ($endExtn)
712 {
713 if ($endX1 == $endX2) #vertical ...modify last Y
714 {
715 if ($endY1 < $endY2) ## points down
716 {
717 $xyTmp[$#xyTmp] -= $endExtn;
718 $xyTmp[$#xyTmp] += int($width/2) if ($pathType != 0);
719 }
720 else ## points up
721 {
722 $xyTmp[$#xyTmp] += $endExtn;
723 $xyTmp[$#xyTmp] -= int($width/2) if ($pathType != 0);
724 }
725 }
726 elsif ($endY1 == $endY2) #horizontal ...modify last X
727 {
728 if ($endX1 < $endX2) ## points left
729 {
730 $xyTmp[$#xyTmp - 1] -= $endExtn;
731 $xyTmp[$#xyTmp - 1] += int($width/2) if ($pathType != 0);
732 }
733 else ## points up
734 {
735 $xyTmp[$#xyTmp - 1] += $endExtn;
736 $xyTmp[$#xyTmp - 1] -= int($width/2) if ($pathType != 0);
737 }
738 }
739 }
740 }
741 $self -> printGds2Record(-type => 'XY',-data => \@xyTmp);
742 $self -> printGds2Record(-type => 'ENDEL');
743}
744################################################################################
745
746=head2 printBoundary - prints a gds2 boundary
747
748 usage:
749 $gds2File -> printBoundary(
750 -layer=>#,
751 -dataType=>#,
752 -xy=>\@array, ## array of reals
753 -xyInt=>\@array, ## array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
754 );
755
756 note:
757 layer defaults to 0 if -layer not used
758 dataType defaults to 0 if -dataType not used
759
760=cut
761
762# <boundary>::= BOUNDARY [ELFLAGS] [PLEX] LAYER DATATYPE XY
763# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
764sub printBoundary #: Profiled
765{
766 my($self,%arg) = @_;
767 my $resolution = $self -> {'Resolution'};
768 my $layer = $arg{'-layer'};
769 if (! defined $layer)
770 {
771 $layer=0;
772 }
773 my $dataType = $arg{'-dataType'};
774 if (! defined $dataType)
775 {
776 $dataType=0;
777 }
778 #### -xyInt most useful if reading and modifying... -xy if creating from scratch
779 my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
780 my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
781 my @xyTmp=(); ##don't pollute array passed in
782 if (! ((defined $xy) || (defined $xyInt)))
783 {
784 die "printBoundary expects an xy array reference. Missing -xy => \\\@array $!";
785 }
786 if (defined $xyInt)
787 {
788 $xy = $xyInt;
789 $resolution=1;
790 }
791 $self -> printGds2Record(-type => 'BOUNDARY');
792 $self -> printGds2Record(-type => 'LAYER',-data => $layer);
793 $self -> printGds2Record(-type => 'DATATYPE',-data => $dataType);
794 if (my $numPoints=$#$xy+1 < 6)
795 {
796 die "printBoundary expects an xy array of at leasts 3 coordinates $!";
797 }
798 for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
799 {
800 if ($xy -> [$i] >= 0) {push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
801 else {push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
802 }
803 ## gds expects square to have 5 coords (closure)
804 if (($xy -> [0] != ($xy -> [($#$xy - 1)])) && ($xy -> [1] != ($xy -> [$#$xy])))
805 {
806 if ($xy -> [0] >= 0) {push @xyTmp,int((($xy -> [0])*$resolution)+$G_epsilon);}
807 else {push @xyTmp,int((($xy -> [0])*$resolution)-$G_epsilon);}
808 if ($xy -> [1] >= 0) {push @xyTmp,int((($xy -> [1])*$resolution)+$G_epsilon);}
809 else {push @xyTmp,int((($xy -> [1])*$resolution)-$G_epsilon);}
810 }
811 $self -> printGds2Record(-type => 'XY',-data => \@xyTmp);
812 $self -> printGds2Record(-type => 'ENDEL');
813}
814################################################################################
815
816=head2 printSref - prints a gds2 Structure REFerence
817
818 usage:
819 $gds2File -> printSref(
820 -name=>string, ## Name of structure
821 -xy=>\@array, ## array of reals
822 -xyInt=>\@array, ## array of internal ints (optional -wks better than -xy if you are modifying an existing GDS2 file)
823 -angle=>#.#, ## (optional) Default is 0.0
824 -mag=>#.#, ## (optional) Default is 1.0
825 -reflect=>0|1 ## (optional)
826 );
827
828 note:
829 best not to specify angle or mag if not needed
830
831=cut
832
833#<SREF>::= SREF [ELFLAGS] [PLEX] SNAME [<strans>] XY
834# <strans>::= STRANS [MAG] [ANGLE]
835# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
836sub printSref #: Profiled
837{
838 my($self,%arg) = @_;
839 my $useSTRANS=FALSE;
840 my $resolution = $self -> {'Resolution'};
841 my $sname = $arg{'-name'};
842 if (! defined $sname)
843 {
844 die "printSref expects a name string. Missing -name => 'text' $!";
845 }
846 #### -xyInt most useful if reading and modifying... -xy if creating from scratch
847 my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
848 my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
849 if (! ((defined $xy) || (defined $xyInt)))
850 {
851 die "printSref expects an xy array reference. Missing -xy => \\\@array $!";
852 }
853 if (defined $xyInt)
854 {
855 $xy = $xyInt;
856 $resolution=1;
857 }
858 $self -> printGds2Record(-type => 'SREF');
859 $self -> printGds2Record(-type => 'SNAME',-data => $sname);
860 my $reflect = $arg{'-reflect'};
861 if ((! defined $reflect)||($reflect <= 0))
862 {
863 $reflect=0;
864 }
865 else
866 {
867 $reflect=1;
868 $useSTRANS=TRUE;
869 }
870 my $mag = $arg{'-mag'};
871 if ((! defined $mag)||($mag <= 0))
872 {
873 $mag=0;
874 }
875 else
876 {
877 $useSTRANS=TRUE;
878 }
879 my $angle = $arg{'-angle'};
880 if (! defined $angle)
881 {
882 $angle=0;
883 }
884 else
885 {
886 $angle=posAngle($angle);
887 $useSTRANS=TRUE;
888 }
889 if ($useSTRANS)
890 {
891 my $data=$reflect.'000000000000000'; ## 16 'bit' string
892 $self -> printGds2Record(-type => 'STRANS',-data => $data);
893 $self -> printGds2Record(-type => 'MAG',-data => $mag) if ($mag);
894 $self -> printGds2Record(-type => 'ANGLE',-data => $angle) if ($angle);
895 }
896 my @xyTmp=(); ##don't pollute array passed in
897 for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
898 {
899 if ($xy -> [$i] >= 0) {push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
900 else {push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
901 }
902 $self -> printGds2Record(-type => 'XY',-data => \@xyTmp);
903 $self -> printGds2Record(-type => 'ENDEL');
904}
905################################################################################
906
907=head2 printAref - prints a gds2 Array REFerence
908
909 usage:
910 $gds2File -> printAref(
911 -name=>string, ## Name of structure
912 -columns=>#, ## Default is 1
913 -rows=>#, ## Default is 1
914 -xy=>\@array, ## array of reals
915 -xyInt=>\@array, ## array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
916 -angle=>#.#, ## (optional) Default is 0.0
917 -mag=>#.#, ## (optional) Default is 1.0
918 -reflect=>0|1 ## (optional)
919 );
920
921 note:
922 best not to specify angle or mag if not needed
923
924=cut
925
926#<AREF>::= AREF [ELFLAGS] [PLEX] SNAME [<strans>] COLROW XY
927# <strans>::= STRANS [MAG] [ANGLE]
928# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
929sub printAref #: Profiled
930{
931 my($self,%arg) = @_;
932 my $useSTRANS=FALSE;
933 my $resolution = $self -> {'Resolution'};
934 my $sname = $arg{'-name'};
935 if (! defined $sname)
936 {
937 die "printAref expects a sname string. Missing -name => 'text' $!";
938 }
939 #### -xyInt most useful if reading and modifying... -xy if creating from scratch
940 my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
941 my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
942 if (! ((defined $xy) || (defined $xyInt)))
943 {
944 die "printAref expects an xy array reference. Missing -xy => \\\@array $!";
945 }
946 if (defined $xyInt)
947 {
948 $xy = $xyInt;
949 $resolution=1;
950 }
951 $self -> printGds2Record(-type => 'AREF');
952 $self -> printGds2Record(-type => 'SNAME',-data => $sname);
953 my $reflect = $arg{'-reflect'};
954 if ((! defined $reflect)||($reflect <= 0))
955 {
956 $reflect=0;
957 }
958 else
959 {
960 $reflect=1;
961 $useSTRANS=TRUE;
962 }
963 my $mag = $arg{'-mag'};
964 if ((! defined $mag)||($mag <= 0))
965 {
966 $mag=0;
967 }
968 else
969 {
970 $useSTRANS=TRUE;
971 }
972 my $angle = $arg{'-angle'};
973 if (! defined $angle)
974 {
975 $angle=0;
976 }
977 else
978 {
979 $angle=posAngle($angle);
980 $useSTRANS=TRUE;
981 }
982 if ($useSTRANS)
983 {
984 my $data=$reflect.'000000000000000'; ## 16 'bit' string
985 $self -> printGds2Record(-type => 'STRANS',-data => $data);
986 $self -> printGds2Record(-type => 'MAG',-data => $mag) if ($mag);
987 $self -> printGds2Record(-type => 'ANGLE',-data => $angle) if ($angle);
988 }
989 my $columns = $arg{'-columns'};
990 if ((! defined $columns)||($columns <= 0))
991 {
992 $columns=1;
993 }
994 else
995 {
996 $columns=int($columns);
997 }
998 my $rows = $arg{'-rows'};
999 if ((! defined $rows)||($rows <= 0))
1000 {
1001 $rows=1;
1002 }
1003 else
1004 {
1005 $rows=int($rows);
1006 }
1007 $self -> printGds2Record(-type => 'COLROW',-data => [$columns,$rows]);
1008 my @xyTmp=(); ##don't pollute array passed in
1009 for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
1010 {
1011 if ($xy -> [$i] >= 0) {push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
1012 else {push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
1013 }
1014 $self -> printGds2Record(-type => 'XY',-data => \@xyTmp);
1015 $self -> printGds2Record(-type => 'ENDEL');
1016}
1017################################################################################
1018
1019=head2 printText - prints a gds2 Text
1020
1021 usage:
1022 $gds2File -> printText(
1023 -string=>string,
1024 -layer=>#, ## Default is 0
1025 -textType=>#, ## Default is 0
1026 -font=>#, ## 0-3
1027 -top, or -middle, -bottom, ##optional vertical presentation
1028 -left, or -center, or -right, ##optional horizontal presentation
1029 -xy=>\@array, ## array of reals
1030 -xyInt=>\@array, ## array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
1031 -x=>#.#, ## optional way of passing in x value
1032 -y=>#.#, ## optional way of passing in y value
1033 -angle=>#.#, ## (optional) Default is 0.0
1034 -mag=>#.#, ## (optional) Default is 1.0
1035 -reflect=>#, ## (optional) Default is 0
1036 );
1037
1038 note:
1039 best not to specify reflect, angle or mag if not needed
1040
1041=cut
1042
1043#<text>::= TEXT [ELFLAGS] [PLEX] LAYER <textbody>
1044# <textbody>::= TEXTTYPE [PRESENTATION] [PATHTYPE] [WIDTH] [<strans>] XY STRING
1045# <strans>::= STRANS [MAG] [ANGLE]
1046################################################################################
1047sub printText #: Profiled
1048{
1049 my($self,%arg) = @_;
1050 my $useSTRANS=FALSE;
1051 my $string = $arg{'-string'};
1052 if (! defined $string)
1053 {
1054 die "printText expects a string. Missing -string => 'text' $!";
1055 }
1056 my $resolution = $self -> {'Resolution'};
1057 my $x = $arg{'-x'};
1058 my $y = $arg{'-y'};
1059 #### -xyInt most useful if reading and modifying... -xy if creating from scratch
1060 my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
1061 my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
1062 if (defined $xyInt)
1063 {
1064 $xy = $xyInt;
1065 $resolution=1;
1066 }
1067 if (defined $xy)
1068 {
1069 $x = $xy -> [0];
1070 $y = $xy -> [1];
1071 }
1072
1073 my $x2 = $arg{'-x'};
1074 if (defined $x2)
1075 {
1076 $x = $x2;
1077 }
1078 if (! defined $x)
1079 {
1080 die "printText expects a x coord. Missing -xy=>\@array or -x => 'num' $!";
1081 }
1082 if ($x>=0) {$x = int(($x*$resolution)+$G_epsilon);}
1083 else {$x = int(($x*$resolution)-$G_epsilon);}
1084
1085 my $y2 = $arg{'-y'};
1086 if (defined $y2)
1087 {
1088 $y = $y2;
1089 }
1090 if (! defined $y)
1091 {
1092 die "printText expects a y coord. Missing -xy=>\@array or -y => 'num' $!";
1093 }
1094 if ($y>=0) {$y = int(($y*$resolution)+$G_epsilon);}
1095 else {$y = int(($y*$resolution)-$G_epsilon);}
1096
1097 my $layer = $arg{'-layer'};
1098 if (! defined $layer)
1099 {
1100 $layer=0;
1101 }
1102 my $textType = $arg{'-textType'};
1103 if (! defined $textType)
1104 {
1105 $textType=0;
1106 }
1107 my $reflect = $arg{'-reflect'};
1108 if ((! defined $reflect)||($reflect <= 0))
1109 {
1110 $reflect=0;
1111 }
1112 else
1113 {
1114 $reflect=1;
1115 $useSTRANS=TRUE;
1116 }
1117
1118 my $font = $arg{'-font'};
1119 if ((! defined $font) || ($font < 0) || ($font > 3))
1120 {
1121 $font=0;
1122 }
1123 $font = sprintf("%02d",$font);
1124
1125 my $vertical;
1126 my $top = $arg{'-top'};
1127 my $middle = $arg{'-middle'};
1128 my $bottom = $arg{'-bottom'};
1129 if (defined $top) {$vertical = '00';}
1130 elsif (defined $bottom) {$vertical = '10';}
1131 else {$vertical = '01';} ## middle
1132 my $horizontal;
1133 my $left = $arg{'-left'};
1134 my $center = $arg{'-center'};
1135 my $right = $arg{'-right'};
1136 if (defined $left) {$horizontal = '00';}
1137 elsif (defined $right) {$horizontal = '10';}
1138 else {$horizontal = '01';} ## center
1139 my $presString = "0000000000$font$vertical$horizontal";
1140
1141 my $mag = $arg{'-mag'};
1142 if ((! defined $mag)||($mag <= 0))
1143 {
1144 $mag=0;
1145 }
1146 my $angle = $arg{'-angle'};
1147 if (! defined $angle)
1148 {
1149 $angle=0;
1150 }
1151 else
1152 {
1153 $angle=posAngle($angle);
1154 }
1155 $self -> printGds2Record(-type=>'TEXT');
1156 $self -> printGds2Record(-type=>'LAYER',-data=>$layer);
1157 $self -> printGds2Record(-type=>'TEXTTYPE',-data=>$textType);
1158 $self -> printGds2Record(-type => 'PRESENTATION',-data => $presString) if (defined $font || defined $top || defined $middle || defined $bottom || defined $bottom || defined $left || defined $center || defined $right);
1159 if ($useSTRANS)
1160 {
1161 my $data=$reflect.'000000000000000'; ## 16 'bit' string
1162 $self -> printGds2Record(-type=>'STRANS',-data=>$data);
1163 }
1164 $self -> printGds2Record(-type=>'MAG',-data=>$mag) if ($mag);
1165 $self -> printGds2Record(-type=>'ANGLE',-data=>$angle) if ($angle);
1166 $self -> printGds2Record(-type=>'XY',-data=>[$x,$y]);
1167 $self -> printGds2Record(-type=>'STRING',-data=>$string);
1168 $self -> printGds2Record(-type=>'ENDEL');
1169}
1170################################################################################
1171
1172=head1 Low Level Generic Write Methods
1173
1174=cut
1175
1176################################################################################
1177
1178=head2 saveGds2Record() - low level method to create a gds2 record given record type
1179 and data (if required). Data of more than one item should be given as a list.
1180
1181 NOTE: THIS ONLY USES GDS2 OBJECT TO GET RESOLUTION
1182
1183 usage:
1184 saveGds2Record(
1185 -type=>string,
1186 -data=>data_If_Needed, ##optional for some types
1187 -scale=>#.#, ##optional number to scale data to. I.E -scale=>0.5 #default is NOT to scale
1188 -snap=>#.#, ##optional number to snap data to I.E. -snap=>0.005 #default is 1 resolution unit, typically 0.001
1189 );
1190
1191 examples:
1192 my $gds2File = new GDS2(-fileName => ">$fileName");
1193 my $record = $gds2File -> saveGds2Record(-type=>'header',-data=>3);
1194 $gds2FileOut -> printGds2Record(-type=>'record',-data=>$record);
1195
1196
1197=cut
1198
1199sub saveGds2Record #: Profiled
1200{
1201 my ($self,%arg) = @_;
1202 my $record='';
1203
1204 my $type = $arg{'-type'};
1205 if (! defined $type)
1206 {
1207 die "saveGds2Record expects a type name. Missing -type => 'name' $!";
1208 }
1209 else
1210 {
1211 $type = uc $type;
1212 }
1213
1214 my $saveEnd=$\;
1215 $\='';
1216
1217 my @data = $arg{'-data'};
1218 my $dataString = $arg{'-asciiData'};
1219 die "saveGds2Record can not handle both -data and -asciiData options $!" if ((defined $dataString)&&((defined $data[0])&&($data[0] ne '')));
1220
1221 my $data = '';
1222 if ($type eq 'RECORD') ## special case...
1223 {
1224 return $data[0];
1225 }
1226 else
1227 {
1228 my $numDataElements = 0;
1229 my $resolution = $self -> {'Resolution'};
1230
1231 my $scale = $arg{'-scale'};
1232 if (! defined $scale)
1233 {
1234 $scale=1;
1235 }
1236 if ($scale <= 0)
1237 {
1238 die "saveGds2Record expects a positive scale -scale => $scale $!";
1239 }
1240
1241 my $snap = $arg{'-snap'};
1242 if (! defined $snap) ## default is one resolution unit
1243 {
1244 $snap = 1;
1245 }
1246 else
1247 {
1248 $snap = $snap*$resolution; ## i.e. 0.001 -> 1
1249 }
1250 if ($snap < 1)
1251 {
1252 die "saveGds2Record expects a snap >= 1/resolution -snap => $snap $!";
1253 }
1254
1255 if ((defined $data[0])&&($data[0] ne ''))
1256 {
1257 $data = $data[0];
1258 $numDataElements = @$data;
1259 if ($numDataElements) ## passed in anonymous array
1260 {
1261 @data = @$data; ## deref
1262 }
1263 else
1264 {
1265 $numDataElements = @data;
1266 }
1267 }
1268
1269 my $recordDataType = $RecordTypeData{$type};
1270 if (defined $dataString)
1271 {
1272 $dataString=~s|^\s+||; ## clean-up
1273 $dataString=~s|\s+$||;
1274 $dataString=~s|\s+| |g if ($dataString !~ m|'|); ## don't compress spaces in strings...
1275 $dataString=~s|'$||; #'for strings
1276 $dataString=~s|^'||; #'for strings
1277 if (($recordDataType == BIT_ARRAY)||($recordDataType == ACSII_STRING))
1278 {
1279 $data = $dataString;
1280 }
1281 else
1282 {
1283 $dataString=~s|\s*[\s,;:/\\]+\s*| |g; ## incase commas etc... (non-std) were added by hand
1284 @data = split(' ',$dataString);
1285 $numDataElements = @data;
1286 if ($recordDataType == INTEGER_4)
1287 {
1288 my @xyTmp=();
1289 for(my $i=0;$i<$numDataElements;$i++) ## e.g. 3.4 in -> 3400 out
1290 {
1291 if ($data[$i]>=0) {push @xyTmp,int((($data[$i])*$resolution)+$G_epsilon);}
1292 else {push @xyTmp,int((($data[$i])*$resolution)-$G_epsilon);}
1293 }
1294 @data=@xyTmp;
1295 }
1296 }
1297 }
1298 my $byte;
1299 my $length = 0;
1300 if ($recordDataType == BIT_ARRAY)
1301 {
1302 $length = 2;
1303 }
1304 elsif ($recordDataType == INTEGER_2)
1305 {
1306 $length = 2 * $numDataElements;
1307 }
1308 elsif ($recordDataType == INTEGER_4)
1309 {
1310 $length = 4 * $numDataElements;
1311 }
1312 elsif ($recordDataType == REAL_8)
1313 {
1314 $length = 8 * $numDataElements;
1315 }
1316 elsif ($recordDataType == ACSII_STRING)
1317 {
1318 my $slen = length $data;
1319 $length = $slen + ($slen % 2); ## needs to be an even number
1320 }
1321
1322 my $recordLength = pack 'S',($length + 4); #1 2 bytes for length 3rd for recordType 4th for dataType
1323 $record .= $recordLength;
1324 my $recordType = pack 'C',$RecordTypeNumbers{$type};
1325 $record .= $recordType;
1326
1327 my $dataType = pack 'C',$RecordTypeData{$type};
1328 $record .= $dataType;
1329
1330 if ($recordDataType == BIT_ARRAY) ## bit array
1331 {
1332 my $bitLength = $length * 8;
1333 $record .= pack("B$bitLength",$data);
1334 }
1335 elsif ($recordDataType == INTEGER_2) ## 2 byte signed integer
1336 {
1337 foreach my $num (@data)
1338 {
1339 $record .= pack('s',$num);
1340 }
1341 }
1342 elsif ($recordDataType == INTEGER_4) ## 4 byte signed integer
1343 {
1344 foreach my $num (@data)
1345 {
1346 $num = scaleNum($num,$scale) if ($scale != 1);
1347 $num = snapNum($num,$snap) if ($snap != 1);
1348 $record .= pack('i',$num);
1349 }
1350 }
1351 elsif ($recordDataType == REAL_8) ## 8 byte real
1352 {
1353 foreach my $num (@data)
1354 {
1355 my $real = $num;
1356 my $negative = 0;
1357 if($num < 0.0)
1358 {
1359 $negative = 1;
1360 $real = 0 - $num;
1361 }
1362
1363 my $exponent = 0;
1364 while($real >= 1.0)
1365 {
1366 $exponent++;
1367 $real = ($real / 16.0);
1368 }
1369
1370 if ($real != 0)
1371 {
1372 while($real < 0.0625)
1373 {
1374 --$exponent;
1375 $real = ($real * 16.0);
1376 }
1377 }
1378
1379 if($negative) { $exponent += 192; }
1380 else { $exponent += 64; }
1381 $record .= pack('C',$exponent);
1382
1383 for (my $i=1; $i<=7; $i++)
1384 {
1385 if ($real>=0) {$byte = int(($real*256.0)+$G_epsilon);}
1386 else {$byte = int(($real*256.0)-$G_epsilon);}
1387 $record .= pack('C',$byte);
1388 $real = $real * 256.0 - ($byte + 0.0);
1389 }
1390 }
1391 }
1392 elsif ($recordDataType == ACSII_STRING) ## ascii string (null padded)
1393 {
1394 $record .= pack("a$length",$data);
1395 }
1396 }
1397 $\=$saveEnd;
1398 $record;
1399}
1400################################################################################
1401
1402=head2 printGds2Record() - low level method to print a gds2 record given record type
1403 and data (if required). Data of more than one item should be given as a list.
1404
1405 usage:
1406 printGds2Record(
1407 -type=>string,
1408 -data=>data_If_Needed, ##optional for some types
1409 -scale=>#.#, ##optional number to scale data to. I.E -scale=>0.5 #default is NOT to scale
1410 -snap=>#.#, ##optional number to snap data to I.E. -snap=>0.005 #default is 1 resolution unit, typically 0.001
1411 );
1412
1413 examples:
1414 my $gds2File = new GDS2(-fileName => ">$fileName");
1415
1416 $gds2File -> printGds2Record(-type=>'header',-data=>3);
1417 $gds2File -> printGds2Record(-type=>'bgnlib',-data=>[99,12,1,22,33,0,99,12,1,22,33,9]);
1418 $gds2File -> printGds2Record(-type=>'libname',-data=>"testlib");
1419 $gds2File -> printGds2Record(-type=>'units',-data=>[0.001, 1e-9]);
1420 $gds2File -> printGds2Record(-type=>'bgnstr',-data=>[99,12,1,22,33,0,99,12,1,22,33,9]);
1421 ...
1422 $gds2File -> printGds2Record(-type=>'endstr');
1423 $gds2File -> printGds2Record(-type=>'endlib');
1424
1425 Note: the special record type of 'record' can be used to copy a complete record
1426 just read in:
1427 while (my $record = $gds2FileIn -> readGds2Record())
1428 {
1429 $gds2FileOut -> printGds2Record(-type=>'record',-data=>$record);
1430 }
1431
1432=cut
1433
1434sub printGds2Record #: Profiled
1435{
1436 my ($self,%arg) = @_;
1437
1438 my $type = $arg{'-type'};
1439 if (! defined $type)
1440 {
1441 die "printGds2Record expects a type name. Missing -type => 'name' $!";
1442 }
1443 else
1444 {
1445 $type = uc $type;
1446 }
1447
1448 my $fh = $self -> {'FileHandle'};
1449 my $saveEnd=$\;
1450 $\='';
1451
1452 my @data = $arg{'-data'};
1453 my $dataString = $arg{'-asciiData'};
1454 die "printGds2Record can not handle both -data and -asciiData options $!" if ((defined $dataString)&&((defined $data[0])&&($data[0] ne '')));
1455
1456 my $data = '';
1457 my $recordLength; ## 1st 2 bytes for length 3rd for recordType 4th for dataType
1458 if ($type eq 'RECORD') ## special case...
1459 {
1460 if ($isLittleEndian)
1461 {
1462 my $length = substr($data[0],0,2);
1463 $recordLength = unpack 'v',$length;
1464 $self -> {'GDSLENGTH'} += $recordLength;
1465 $length = reverse $length;
1466 print($fh $length);
1467
1468 my $recordType = substr($data[0],2,1);
1469 print($fh $recordType);
1470 $recordType = unpack 'C',$recordType;
1471 $type = $RecordTypeStrings[$recordType]; ## will use code below.....
1472
1473 my $dataType = substr($data[0],3,1);
1474 print($fh $dataType);
1475 $dataType = unpack 'C',$dataType;
1476 if ($recordLength > 4)
1477 {
1478 my $lengthLeft = $recordLength - 4; ## length left
1479 my $recordDataType = $RecordTypeData{$type};
1480
1481 if (($recordDataType == INTEGER_2) || ($recordDataType == BIT_ARRAY))
1482 {
1483 my $binData = unpack 'b*',$data[0];
1484 my $intData = substr($binData,32); #skip 1st 4 bytes (length, recordType dataType)
1485
1486 my ($byteInt2String,$byte2);
1487 for(my $i=0; $i<($lengthLeft/2); $i++)
1488 {
1489 $byteInt2String = reverse(substr($intData,0,16,''));
1490 $byte2=pack 'B16',reverse($byteInt2String);
1491 print($fh $byte2);
1492 }
1493 }
1494 elsif ($recordDataType == INTEGER_4)
1495 {
1496 my $binData = unpack 'b*',$data[0];
1497 my $intData = substr($binData,32); #skip 1st 4 bytes (length, recordType dataType)
1498 my ($byteInt4String,$byte4);
1499 for(my $i=0; $i<($lengthLeft/4); $i++)
1500 {
1501 $byteInt4String = reverse(substr($intData,0,32,''));
1502 $byte4=pack 'B32',reverse($byteInt4String);
1503 print($fh $byte4);
1504 }
1505 }
1506 elsif ($recordDataType == REAL_8)
1507 {
1508 my $binData = unpack 'b*',$data[0];
1509 my $realData = substr($binData,32); #skip 1st 4 bytes (length, recordType dataType)
1510 my ($bit64String,$mantissa,$byteString,$byte);
1511 for(my $i=0; $i<($lengthLeft/8); $i++)
1512 {
1513 $bit64String = substr($realData,($i*64),64);
1514 print($fh pack 'b8',$bit64String);
1515 $mantissa = substr($bit64String,8,56);
1516 for(my $j=0; $j<7; $j++)
1517 {
1518 $byteString = substr($mantissa,($j*8),8);
1519 $byte=pack 'b8',$byteString;
1520 print($fh $byte);
1521 }
1522 }
1523 }
1524 elsif ($recordDataType == ACSII_STRING) ## ascii string (null padded)
1525 {
1526 print($fh pack("a$lengthLeft",substr($data[0],4)));
1527 }
1528 elsif ($recordDataType == REAL_4) ## 4 byte real
1529 {
1530 die "4-byte reals are not supported $!";
1531 }
1532 }
1533 }
1534 else
1535 {
1536 print($fh $data[0]);
1537 $recordLength = length $data[0];
1538 $self -> {'GDSLENGTH'} += $recordLength;
1539 }
1540 }
1541 else #if ($type ne 'RECORD')
1542 {
1543 my $numDataElements = 0;
1544 my $resolution = $self -> {'Resolution'};
1545
1546 my $scale = $arg{'-scale'};
1547 if (! defined $scale)
1548 {
1549 $scale=1;
1550 }
1551 if ($scale <= 0)
1552 {
1553 die "printGds2Record expects a positive scale -scale => $scale $!";
1554 }
1555
1556 my $snap = $arg{'-snap'};
1557 if (! defined $snap) ## default is one resolution unit
1558 {
1559 $snap = 1;
1560 }
1561 else
1562 {
1563 $snap = int(($snap*$resolution)+$G_epsilon); ## i.e. 0.001 -> 1
1564 }
1565 if ($snap < 1)
1566 {
1567 die "printGds2Record expects a snap >= 1/resolution -snap => $snap $!";
1568 }
1569
1570 if ((defined $data[0])&&($data[0] ne ''))
1571 {
1572 $data = $data[0];
1573 $numDataElements = @$data;
1574 if ($numDataElements) ## passed in anonymous array
1575 {
1576 @data = @$data; ## deref
1577 }
1578 else
1579 {
1580 $numDataElements = @data;
1581 }
1582 }
1583
1584 my $recordDataType = $RecordTypeData{$type};
1585
1586 if (defined $dataString)
1587 {
1588 $dataString=~s|^\s+||; ## clean-up
1589 $dataString=~s|\s+$||;
1590 $dataString=~s|\s+| |g if ($dataString !~ m|'|); ## don't compress spaces in strings...
1591 $dataString=~s|'$||; #'# for strings
1592 $dataString=~s|^'||; #'# for strings
1593 if (($recordDataType == BIT_ARRAY)||($recordDataType == ACSII_STRING))
1594 {
1595 $data = $dataString;
1596 }
1597 else
1598 {
1599 $dataString=~s|\s*[\s,;:/\\]+\s*| |g; ## in case commas etc... (non-std) were added by hand
1600 @data = split(' ',$dataString);
1601 $numDataElements = @data;
1602 if ($recordDataType == INTEGER_4)
1603 {
1604 my @xyTmp=();
1605 for(my $i=0;$i<$numDataElements;$i++) ## e.g. 3.4 in -> 3400 out
1606 {
1607 if ($data[$i]>=0) {push @xyTmp,int((($data[$i])*$resolution)+$G_epsilon);}
1608 else {push @xyTmp,int((($data[$i])*$resolution)-$G_epsilon);}
1609 }
1610 @data=@xyTmp;
1611 }
1612 }
1613 }
1614 my $byte;
1615 my $length = 0;
1616 if ($recordDataType == BIT_ARRAY)
1617 {
1618 $length = 2;
1619 }
1620 elsif ($recordDataType == INTEGER_2)
1621 {
1622 $length = 2 * $numDataElements;
1623 }
1624 elsif ($recordDataType == INTEGER_4)
1625 {
1626 $length = 4 * $numDataElements;
1627 }
1628 elsif ($recordDataType == REAL_8)
1629 {
1630 $length = 8 * $numDataElements;
1631 }
1632 elsif ($recordDataType == ACSII_STRING)
1633 {
1634 my $slen = length $data;
1635 $length = $slen + ($slen % 2); ## needs to be an even number
1636 }
1637 $self -> {'GDSLENGTH'} += $length;
1638 if ($isLittleEndian)
1639 {
1640 $recordLength = pack 'v',($length + 4);
1641 $recordLength = reverse $recordLength;
1642 }
1643 else
1644 {
1645 $recordLength = pack 'S',($length + 4);
1646 }
1647 print($fh $recordLength);
1648
1649 my $recordType = pack 'C',$RecordTypeNumbers{$type};
1650 $recordType = reverse $recordType if ($isLittleEndian);
1651 print($fh $recordType);
1652
1653 my $dataType = pack 'C',$RecordTypeData{$type};
1654 $dataType = reverse $dataType if ($isLittleEndian);
1655 print($fh $dataType);
1656
1657 if ($recordDataType == BIT_ARRAY) ## bit array
1658 {
1659 my $bitLength = $length * 8;
1660 my $value = pack("B$bitLength",$data);
1661 print($fh $value);
1662 }
1663 elsif ($recordDataType == INTEGER_2) ## 2 byte signed integer
1664 {
1665 my $value;
1666 foreach my $num (@data)
1667 {
1668 $value = pack('s',$num);
1669 $value = reverse $value if ($isLittleEndian);
1670 print($fh $value);
1671 }
1672 }
1673 elsif ($recordDataType == INTEGER_4) ## 4 byte signed integer
1674 {
1675 my $value;
1676 foreach my $num (@data)
1677 {
1678 $num = scaleNum($num,$scale) if ($scale != 1);
1679 $num = snapNum($num,$snap) if ($snap != 1);
1680 $value = pack('i',$num);
1681 $value = reverse $value if ($isLittleEndian);
1682 print($fh $value);
1683 }
1684 }
1685 elsif ($recordDataType == REAL_8) ## 8 byte real
1686 {
1687 my ($real,$negative,$exponent,$value);
1688 foreach my $num (@data)
1689 {
1690 $real = $num;
1691 $negative = 0;
1692 if($num < 0.0)
1693 {
1694 $negative = 1;
1695 $real = 0 - $num;
1696 }
1697
1698 $exponent = 0;
1699 while($real >= 1.0)
1700 {
1701 $exponent++;
1702 $real = ($real / 16.0);
1703 }
1704
1705 if ($real != 0)
1706 {
1707 while($real < 0.0625)
1708 {
1709 --$exponent;
1710 $real = ($real * 16.0);
1711 }
1712 }
1713 if($negative) { $exponent += 192; }
1714 else { $exponent += 64; }
1715 $value = pack('C',$exponent);
1716 $value = reverse $value if ($isLittleEndian);
1717 print($fh $value);
1718
1719 for (my $i=1; $i<=7; $i++)
1720 {
1721 if ($real>=0) {$byte = int(($real*256.0)+$G_epsilon);}
1722 else {$byte = int(($real*256.0)-$G_epsilon);}
1723 my $value = pack('C',$byte);
1724 $value = reverse $value if ($isLittleEndian);
1725 print($fh $value);
1726 $real = $real * 256.0 - ($byte + 0.0);
1727 }
1728 }
1729 }
1730 elsif ($recordDataType == ACSII_STRING) ## ascii string (null padded)
1731 {
1732 print($fh pack("a$length",$data));
1733 }
1734 }
1735 $\=$saveEnd;
1736}
1737################################################################################
1738
1739=head2 printRecord - prints a record just read
1740
1741 usage:
1742 $gds2File -> printRecord(
1743 -data => $record
1744 );
1745
1746=cut
1747
1748sub printRecord #: Profiled
1749{
1750 my ($self,%arg) = @_;
1751 my $record = $arg{'-data'};
1752 if (! defined $record)
1753 {
1754 die "printGds2Record expects a data record. Missing -data => \$record $!";
1755 }
1756 my $type = $arg{'-type'};
1757 if (defined $type)
1758 {
1759 die "printRecord does not take -type. Perhaps you meant to use printGds2Record? $!";
1760 }
1761 $self -> printGds2Record(-type=>'record',-data=>$record);
1762}
1763################################################################################
1764
1765################################################################################
1766
1767=head1 Low Level Generic Read Methods
1768
1769=cut
1770
1771################################################################################
1772
1773=head2 readGds2Record - reads record header and data section
1774
1775 usage:
1776 while ($gds2File -> readGds2Record)
1777 {
1778 if ($gds2File -> returnRecordTypeString eq 'LAYER')
1779 {
1780 $layersFound[$gds2File -> layer] = 1;
1781 }
1782 }
1783
1784=cut
1785
1786sub readGds2Record #: Profiled
1787{
1788 my $self = shift;
1789 $self -> readGds2RecordHeader();
1790 $self -> readGds2RecordData();
1791 $self -> {'Record'};
1792}
1793################################################################################
1794
1795=head2 readGds2RecordHeader - only reads gds2 record header section (2 bytes)
1796
1797=cut
1798
1799sub readGds2RecordHeader #: Profiled
1800{
1801 my $self = shift;
1802 $self -> skipGds2RecordData() if (($self -> {'HEADER'} >= 0) && (! $self -> {'INDATA'})) ; # in HEADER not in data
1803 $self -> {'Record'} = '';
1804 $self -> {'RecordType'} = -1;
1805 $self -> {'HEADER'} = 1;
1806 $self -> {'INDATA'} = 0;
1807 return '' if ($self -> {'EOLIB'}); ## no sense reading null padding..
1808 my $data;
1809 if (read($self -> {'FileHandle'},$data,2)) ### length
1810 {
1811 $data = reverse $data if ($isLittleEndian);
1812 $self -> {'Record'} = $data;
1813 $self -> {'Length'} = unpack 'S',$data;
1814 $self -> {'GDSLENGTH'} += $self -> {'Length'};
1815 }
1816 else
1817 {
1818 return 0;
1819 }
1820
1821 if (read($self -> {'FileHandle'},$data,1)) ## record type
1822 {
1823 $data = reverse $data if ($isLittleEndian);
1824 $self -> {'Record'} .= $data;
1825 $self -> {'RecordType'} = unpack 'C',$data;
1826 $self -> {'EOLIB'} = 1 if (($self -> {'RecordType'}) == ENDLIB);
1827
1828 if ($self -> {'UsingPrettyPrint'})
1829 {
1830 $StrSpace = '' if (($self -> {'RecordType'}) == ENDSTR);
1831 $StrSpace = ' ' if (($self -> {'RecordType'}) == BGNSTR);
1832
1833 $ElmSpace = ' ' if ((($self -> {'RecordType'}) == TEXT) || (($self -> {'RecordType'}) == PATH) ||
1834 (($self -> {'RecordType'}) == BOUNDARY) || (($self -> {'RecordType'}) == SREF) ||
1835 (($self -> {'RecordType'}) == AREF));
1836 $ElmSpace = '' if (($self -> {'RecordType'}) == ENDEL);
1837 }
1838 }
1839 else
1840 {
1841 return 0;
1842 }
1843
1844 if (read($self -> {'FileHandle'},$data,1)) ## data type
1845 {
1846 $data = reverse $data if ($isLittleEndian);
1847 $self -> {'Record'} .= $data;
1848 $self -> {'DataType'} = unpack 'C',$data;
1849 }
1850 else
1851 {
1852 return 0;
1853 }
1854 return 1;
1855}
1856################################################################################
1857
1858=head2 readGds2RecordData - only reads record data section
1859
1860 slightly faster if you just want a certain thing...
1861 usage:
1862 while ($gds2File -> readGds2RecordHeader)
1863 {
1864 if ($gds2File -> returnRecordTypeString eq 'LAYER')
1865 {
1866 $gds2File -> readGds2RecordData;
1867 $layersFound[$gds2File -> returnLayer] = 1;
1868 }
1869 }
1870
1871=cut
1872
1873sub readGds2RecordData #: Profiled
1874{
1875 my $self = shift;
1876 $self -> readGds2RecordHeader() if ($self -> {'HEADER'} <= 0);
1877 return $self -> {'Record'} if ($self -> {'DataType'} == NO_DATA); # no sense going on...
1878 $self -> {'HEADER'} = 0; # not in HEADER
1879 $self -> {'INDATA'} = 1; # rather in DATA
1880 my $resolution = $self -> {'Resolution'};
1881 my $bytesLeft = $self -> {'Length'} - 4; ## 4 should have been just read by readGds2RecordHeader
1882 $self -> {'RecordData'} = ('');
1883 $self -> {'CurrentDataList'} = '';
1884 my $data;
1885 if ($self -> {'DataType'} == BIT_ARRAY) ## bit array
1886 {
1887 $self -> {'DataIndex'}=0;
1888 read($self -> {'FileHandle'},$data,$bytesLeft);
1889 $data = reverse $data if ($isLittleEndian);
1890 my $bitsLeft = $bytesLeft * 8;
1891 $self -> {'Record'} .= $data;
1892 $self -> {'RecordData'}[0] = unpack "B$bitsLeft",$data;
1893 $self -> {'CurrentDataList'} = ($self -> {'RecordData'}[0]);
1894 }
1895 elsif ($self -> {'DataType'} == INTEGER_2) ## 2 byte signed integer
1896 {
1897 my $tmpListString = '';
1898 my $i = 0;
1899 while ($bytesLeft)
1900 {
1901 read($self -> {'FileHandle'},$data,2);
1902 $data = reverse $data if ($isLittleEndian);
1903 $self -> {'Record'} .= $data;
1904 $self -> {'RecordData'}[$i] = unpack 's',$data;
1905 $tmpListString .= ',';
1906 $tmpListString .= $self -> {'RecordData'}[$i];
1907 $i++;
1908 $bytesLeft -= 2;
1909 }
1910 $self -> {'DataIndex'} = $i - 1;
1911 $self -> {'CurrentDataList'} = $tmpListString;
1912 }
1913 elsif ($self -> {'DataType'} == INTEGER_4) ## 4 byte signed integer
1914 {
1915 my $tmpListString = '';
1916 my $i = 0;
1917 while ($bytesLeft)
1918 {
1919 read($self -> {'FileHandle'},$data,4);
1920 $data = reverse $data if ($isLittleEndian);
1921 $self -> {'Record'} .= $data;
1922 $self -> {'RecordData'}[$i] = unpack 'i',$data;
1923 $tmpListString .= ',';
1924 $tmpListString .= $self -> {'RecordData'}[$i];
1925 $i++;
1926 $bytesLeft -= 4;
1927 }
1928 $self -> {'DataIndex'} = $i - 1;
1929 $self -> {'CurrentDataList'} = $tmpListString;
1930 }
1931 elsif ($self -> {'DataType'} == REAL_4) ## 4 byte real
1932 {
1933 die "4-byte reals are not supported $!";
1934 }
1935 elsif ($self -> {'DataType'} == REAL_8) ## 8 byte real
1936 {
1937 my $tmpListString = '';
1938 my $i = 0;
1939 my ($negative,$exponent,$mantdata,$byteString,$byte,$mantissa,$real);
1940 while ($bytesLeft)
1941 {
1942 read($self -> {'FileHandle'},$data,1); ## sign bit and 7 exponent bits
1943 #$data = reverse $data if ($isLittleEndian);
1944 $self -> {'Record'} .= $data;
1945 $negative = unpack 'B',$data; ## sign bit
1946 $exponent = unpack 'C',$data;
1947 if ($negative)
1948 {
1949 $exponent -= 192; ## 128 + 64
1950 }
1951 else
1952 {
1953 $exponent -= 64;
1954 }
1955 read($self -> {'FileHandle'},$data,7); ## mantissa bits
1956 $mantdata = unpack 'b*',$data;
1957 $self -> {'Record'} .= $data;
1958 $mantissa = 0.0;
1959 for(my $j=0; $j<7; $j++)
1960 {
1961 $byteString = substr($mantdata,0,8,'');
1962 $byte = pack 'b*',$byteString;
1963 $byte = unpack 'C',$byte;
1964 $mantissa += $byte / (256.0**($j+1));
1965 }
1966 $real = $mantissa * (16**$exponent);
1967 $real = (0 - $real) if ($negative);
1968 if ($RecordTypeStrings[$self -> {'RecordType'}] eq 'UNITS')
1969 {
1970 $self -> {'UUnits'} = $real if ($self -> {'UUnits'} == 0);
1971 $self -> {'DBUnits'} = $real if ($self -> {'DBUnits'} == 0);
1972 }
1973 else
1974 {
1975 ### this works because UUnits and DBUnits are 1st reals in GDS2 file
1976 $real = int(($real+($self -> {'UUnits'}/$resolution))/$self -> {'UUnits'})*$self -> {'UUnits'} if ($self -> {'UUnits'} != 0); ## "rounds" off
1977 }
1978 $self -> {'RecordData'}[$i] = $real;
1979 $tmpListString .= ',';
1980 $tmpListString .= $self -> {'RecordData'}[$i];
1981 $i++;
1982 $bytesLeft -= 8;
1983 }
1984 $self -> {'DataIndex'} = $i - 1;
1985 $self -> {'CurrentDataList'} = $tmpListString;
1986 }
1987 elsif ($self -> {'DataType'} == ACSII_STRING) ## ascii string (null padded)
1988 {
1989 $self -> {'DataIndex'} = 0;
1990 read($self -> {'FileHandle'},$data,$bytesLeft);
1991 $self -> {'Record'} .= $data;
1992 $self -> {'RecordData'}[0] = unpack "a$bytesLeft",$data;
1993 $self -> {'RecordData'}[0] =~ s|\0||g; ## take off ending nulls
1994 $self -> {'CurrentDataList'} = ($self -> {'RecordData'}[0]);
1995 }
1996 $self -> {'Record'};
1997}
1998################################################################################
1999
2000=head1 Low Level Generic Evaluation Methods
2001
2002=cut
2003
2004################################################################################
2005
2006=head2 returnRecordType - returns current (read) record type as integer
2007
2008 usage:
2009 if ($gds2File -> returnRecordType == 6)
2010 {
2011 print "found STRNAME";
2012 }
2013
2014=cut
2015
2016sub returnRecordType #: Profiled
2017{
2018 my $self = shift;
2019 $self -> {'RecordType'};
2020}
2021################################################################################
2022
2023=head2 returnRecordTypeString - returns current (read) record type as string
2024
2025 usage:
2026 if ($gds2File -> returnRecordTypeString eq 'LAYER')
2027 {
2028 code goes here...
2029 }
2030
2031=cut
2032
2033sub returnRecordTypeString #: Profiled
2034{
2035 my $self = shift;
2036 $RecordTypeStrings[($self -> {'RecordType'})];
2037}
2038################################################################################
2039
2040=head2 returnRecordAsString - returns current (read) record as a string
2041
2042 usage:
2043 while ($gds2File -> readGds2Record)
2044 {
2045 print $gds2File -> returnRecordAsString;
2046 }
2047
2048=cut
2049
2050sub returnRecordAsString() #: Profiled
2051{
2052 my $self = shift;
2053 my $string = '';
2054 $self -> {'UsingPrettyPrint'} = 1;
2055 $string .= $StrSpace if ($self -> {'RecordType'} != BGNSTR);
2056 $string .= $ElmSpace if (!(($self -> {'RecordType'} == TEXT) || ($self -> {'RecordType'} == PATH) ||
2057 ($self -> {'RecordType'} == BOUNDARY) || ($self -> {'RecordType'} == SREF) ||
2058 ($self -> {'RecordType'} == AREF)));
2059 my $recordType = $RecordTypeStrings[$self -> {'RecordType'}];
2060 $string .= $recordType;
2061 my $i = 0;
2062 while ($i <= $self -> {'DataIndex'})
2063 {
2064 if ($self -> {'DataType'} == BIT_ARRAY)
2065 {
2066 my $bitString = $self -> {'RecordData'}[$i];
2067 if ($isLittleEndian)
2068 {
2069 $bitString =~ m|(........)(........)|;
2070 $bitString = "$2$1";
2071 }
2072 $string .= ' '.$bitString;
2073 }
2074 elsif (
2075 ($self -> {'DataType'} == INTEGER_2) ||
2076 ($self -> {'DataType'} == REAL_8)
2077 )
2078 {
2079 $string .= ' '.$self -> {'RecordData'}[$i];
2080 $string =~ s|(\d)\.e|$1e| if ($recordType eq 'UNITS'); ## perl on Cygwin prints "1.e-9" others "1e-9"
2081 }
2082 elsif ($self -> {'DataType'} == INTEGER_4)
2083 {
2084 $string .= ' '.$self -> {'RecordData'}[$i]*($self -> {'UUnits'});
2085 }
2086 elsif ($self -> {'DataType'} == ACSII_STRING)
2087 {
2088 $string .= " '".$self -> {'RecordData'}[$i]."'";
2089 }
2090 $i++;
2091 }
2092 $string;
2093}
2094################################################################################
2095
2096=head2 returnXyAsArray - returns current (read) XY record as an array
2097
2098 usage:
2099 $gds2File -> returnXyAsArray(
2100 -asInteger => 0|1 ## (optional) default is true. Return integer
2101 ## array or if false return array of reals.
2102 -withClosure => 0|1 ## (optional) default is true. Whether to
2103 ##return a rectangle with 5 or 4 points.
2104 );
2105
2106 example:
2107 while ($gds2File -> readGds2Record)
2108 {
2109 my @xy = $gds2File -> returnXyAsArray if ($gds2File -> isXy);
2110 }
2111
2112=cut
2113
2114sub returnXyAsArray() #: Profiled
2115{
2116 my($self,%arg) = @_;
2117 my $asInteger = $arg{'-asInteger'};
2118 if (! defined $asInteger)
2119 {
2120 $asInteger = 1;
2121 }
2122 my $withClosure = $arg{'-withClosure'};
2123 if (! defined $withClosure)
2124 {
2125 $withClosure = 1;
2126 }
2127 my @xys=();
2128 if ($self -> isXy)
2129 {
2130 my $i = 0;
2131 my $stopPoint = $self -> {'DataIndex'};
2132 $stopPoint -= 2 if (! $withClosure);
2133 while ($i <= $stopPoint)
2134 {
2135 if ($asInteger)
2136 {
2137 push @xys,($self -> {'RecordData'}[$i]);
2138 }
2139 else
2140 {
2141 push @xys,($self -> {'RecordData'}[$i]*($self -> {'UUnits'}));
2142 }
2143 $i++;
2144 }
2145 }
2146 @xys;
2147}
2148################################################################################
2149
2150
2151=head2 returnRecordAsPerl - returns current (read) record as a perl command to facilitate the creation of parameterized gds2 data with perl.
2152
2153 usage:
2154 #!/usr/local/bin/perl
2155 use GDS2;
2156 my $gds2File = new GDS2(-fileName=>"test.gds");
2157 while ($gds2File -> readGds2Record)
2158 {
2159 print $gds2File -> returnRecordAsPerl;
2160 }
2161
2162=cut
2163
2164sub returnRecordAsPerl() #: Profiled
2165{
2166 my($self,%arg) = @_;
2167 my $gds2File = $arg{'-gds2File'};
2168 if (! defined $gds2File)
2169 {
2170 $gds2File = '$gds2File';
2171 }
2172 my $PGR = $arg{'-printGds2Record'};
2173 if (! defined $PGR)
2174 {
2175 $PGR = 'printGds2Record';
2176 }
2177 my $string = '';
2178 $self -> {'UsingPrettyPrint'} = 1;
2179 $string .= $StrSpace if ($self -> {'RecordType'} != BGNSTR);
2180 $string .= $ElmSpace if (!(($self -> {'RecordType'} == TEXT) || ($self -> {'RecordType'} == PATH) ||
2181 ($self -> {'RecordType'} == BOUNDARY) || ($self -> {'RecordType'} == SREF) ||
2182 ($self -> {'RecordType'} == AREF)));
2183 if (
2184 ($self -> {'RecordType'} == TEXT) ||
2185 ($self -> {'RecordType'} == PATH) ||
2186 ($self -> {'RecordType'} == BOUNDARY) ||
2187 ($self -> {'RecordType'} == SREF) ||
2188 ($self -> {'RecordType'} == AREF) ||
2189 ($self -> {'RecordType'} == ENDEL) ||
2190 ($self -> {'RecordType'} == ENDSTR) ||
2191 ($self -> {'RecordType'} == ENDLIB)
2192 )
2193 {
2194 $string .= $gds2File.'->'.$PGR.'(-type=>'."'".$RecordTypeStrings[$self -> {'RecordType'}]."'".');';
2195 }
2196 else
2197 {
2198 $string .= $gds2File.'->'.$PGR.'(-type=>'."'".$RecordTypeStrings[$self -> {'RecordType'}]."',-data=>";
2199 my $i = 0;
2200 my $maxi = $self -> {'DataIndex'};
2201 if ($maxi >= 1) {$string .= '['}
2202 while ($i <= $maxi)
2203 {
2204 if ($self -> {'DataType'} == BIT_ARRAY)
2205 {
2206 $string .= "'".$self -> {'RecordData'}[$i]."'";
2207 }
2208 elsif ($self -> {'DataType'} == INTEGER_2)
2209 {
2210 $string .= $self -> {'RecordData'}[$i];
2211 }
2212 elsif ($self -> {'DataType'} == INTEGER_4)
2213 {
2214 $string .= $self -> {'RecordData'}[$i];
2215 }
2216 elsif ($self -> {'DataType'} == REAL_8)
2217 {
2218 $string .= $self -> {'RecordData'}[$i];
2219 }
2220 elsif ($self -> {'DataType'} == ACSII_STRING)
2221 {
2222 $string .= "'".$self -> {'RecordData'}[$i]."'";
2223 }
2224 if ($i < $maxi) {$string .= ', '}
2225 $i++;
2226 }
2227 if ($maxi >= 1) {$string .= ']'}
2228 $string .= ');';
2229 }
2230 $string;
2231}
2232################################################################################
2233
2234
2235=head1 Low Level Specific Write Methods
2236
2237=cut
2238
2239################################################################################
2240
2241=head2 printAngle - prints ANGLE record
2242
2243 usage:
2244 $gds2File -> printAngle(-num=>#.#);
2245
2246=cut
2247
2248sub printAngle #: Profiled
2249{
2250 my($self,%arg) = @_;
2251 my $angle = $arg{'-num'};
2252 $angle=0 if (! defined $angle);
2253 $angle=posAngle($angle);
2254 $self -> printGds2Record(-type => 'ANGLE',-data => $angle) if ($angle);
2255}
2256################################################################################
2257
2258=head2 printAttrtable - prints ATTRTABLE record
2259
2260 usage:
2261 $gds2File -> printAttrtable(-string=>$string);
2262
2263=cut
2264
2265sub printAttrtable #: Profiled
2266{
2267 my($self,%arg) = @_;
2268 my $string = $arg{'-string'};
2269 if (! defined $string)
2270 {
2271 die "printAttrtable expects a string. Missing -string => 'text' $!";
2272 }
2273 $self -> printGds2Record(-type => 'ATTRTABLE',-data => $string);
2274}
2275################################################################################
2276
2277=head2 printBgnextn - prints BGNEXTN record
2278
2279 usage:
2280 $gds2File -> printBgnextn(-num=>#.#);
2281
2282=cut
2283
2284sub printBgnextn #: Profiled
2285{
2286 my($self,%arg) = @_;
2287 my $num = $arg{'-num'};
2288 if (! defined $num)
2289 {
2290 die "printBgnextn expects a extension number. Missing -num => #.# $!";
2291 }
2292 my $resolution = $self -> {'Resolution'};
2293 if ($num >= 0) {$num = int(($num*$resolution)+$G_epsilon);}
2294 else {$num = int(($num*$resolution)-$G_epsilon);}
2295 $self -> printGds2Record(-type => 'BGNEXTN',-data => $num);
2296}
2297################################################################################
2298
2299=head2 printBgnlib - prints BGNLIB record
2300
2301 usage:
2302 $gds2File -> printBgnlib(
2303 -isoDate => 0|1 ## (optional) use ISO 4 digit date 2001 vs 101
2304 );
2305
2306=cut
2307
2308sub printBgnlib #: Profiled
2309{
2310 my($self,%arg) = @_;
2311 my $isoDate = $arg{'-isoDate'};
2312 if (! defined $isoDate)
2313 {
2314 $isoDate = 0;
2315 }
2316 elsif ($isoDate != 0)
2317 {
2318 $isoDate = 1;
2319 }
2320 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
2321 $mon++;
2322 $year += 1900 if ($isoDate); ## Cadence likes year left "as is". GDS format supports year number up to 65535 -- 101 vs 2001
2323 $self -> printGds2Record(-type=>'BGNLIB',-data=>[$year,$mon,$mday,$hour,$min,$sec,$year,$mon,$mday,$hour,$min,$sec]);
2324}
2325################################################################################
2326
2327=head2 printBox - prints BOX record
2328
2329 usage:
2330 $gds2File -> printBox;
2331
2332=cut
2333
2334sub printBox #: Profiled
2335{
2336 my $self = shift;
2337 $self -> printGds2Record(-type => 'BOX');
2338}
2339################################################################################
2340
2341=head2 printBoxtype - prints BOXTYPE record
2342
2343 usage:
2344 $gds2File -> printBoxtype(-num=>#);
2345
2346=cut
2347
2348sub printBoxtype #: Profiled
2349{
2350 my($self,%arg) = @_;
2351 my $num = $arg{'-num'};
2352 if (! defined $num)
2353 {
2354 die "printBoxtype expects a number. Missing -num => # $!";
2355 }
2356 $self -> printGds2Record(-type => 'BOXTYPE',-data => $num);
2357}
2358################################################################################
2359
2360=head2 printColrow - prints COLROW record
2361
2362 usage:
2363 $gds2File -> printBoxtype(-columns=>#, -rows=>#);
2364
2365=cut
2366
2367sub printColrow #: Profiled
2368{
2369 my($self,%arg) = @_;
2370 my $columns = $arg{'-columns'};
2371 if ((! defined $columns)||($columns <= 0))
2372 {
2373 $columns=1;
2374 }
2375 else
2376 {
2377 $columns=int($columns);
2378 }
2379 my $rows = $arg{'-rows'};
2380 if ((! defined $rows)||($rows <= 0))
2381 {
2382 $rows=1;
2383 }
2384 else
2385 {
2386 $rows=int($rows);
2387 }
2388 $self -> printGds2Record(-type => 'COLROW',-data => [$columns,$rows]);
2389}
2390################################################################################
2391
2392=head2 printDatatype - prints DATATYPE record
2393
2394 usage:
2395 $gds2File -> printDatatype(-num=>#);
2396
2397=cut
2398
2399sub printDatatype #: Profiled
2400{
2401 my($self,%arg) = @_;
2402 my $dataType = $arg{'-num'};
2403 if (! defined $dataType)
2404 {
2405 $dataType=0;
2406 }
2407 $self -> printGds2Record(-type => 'DATATYPE',-data => $dataType);
2408}
2409################################################################################
2410
2411sub printEflags #: Profiled
2412{
2413 my $self = shift;
2414 die "EFLAGS type not supported $!";
2415}
2416################################################################################
2417
2418=head2 printElkey - prints ELKEY record
2419
2420 usage:
2421 $gds2File -> printElkey(-num=>#);
2422
2423=cut
2424
2425sub printElkey #: Profiled
2426{
2427 my($self,%arg) = @_;
2428 my $num = $arg{'-num'};
2429 if (! defined $num)
2430 {
2431 die "printElkey expects a number. Missing -num => #.# $!";
2432 }
2433 $self -> printGds2Record(-type => 'ELKEY',-data => $num);
2434}
2435################################################################################
2436
2437=head2 printEndel - closes an element definition
2438
2439=cut
2440
2441sub printEndel #: Profiled
2442{
2443 my $self = shift;
2444 $self -> printGds2Record(-type => 'ENDEL');
2445}
2446################################################################################
2447
2448=head2 printEndextn - prints path end extension record
2449
2450 usage:
2451 $gds2File printEndextn -> (-num=>#.#);
2452
2453=cut
2454
2455sub printEndextn #: Profiled
2456{
2457 my($self,%arg) = @_;
2458 my $num = $arg{'-num'};
2459 if (! defined $num)
2460 {
2461 die "printEndextn expects a extension number. Missing -num => #.# $!";
2462 }
2463 my $resolution = $self -> {'Resolution'};
2464 if ($num >= 0) {$num = int(($num*$resolution)+$G_epsilon);}
2465 else {$num = int(($num*$resolution)-$G_epsilon);}
2466 $self -> printGds2Record(-type => 'ENDEXTN',-data => $num);
2467}
2468################################################################################
2469
2470=head2 printEndlib - closes a library definition
2471
2472=cut
2473
2474sub printEndlib #: Profiled
2475{
2476 my $self = shift;
2477 $self -> printGds2Record(-type => 'ENDLIB');
2478}
2479################################################################################
2480
2481=head2 printEndstr - closes a structure definition
2482
2483=cut
2484
2485sub printEndstr #: Profiled
2486{
2487 my $self = shift;
2488 $self -> printGds2Record(-type => 'ENDSTR');
2489}
2490################################################################################
2491
2492=head2 printEndmasks - prints a ENDMASKS
2493
2494=cut
2495
2496sub printEndmasks #: Profiled
2497{
2498 my $self = shift;
2499 $self -> printGds2Record(-type => 'ENDMASKS');
2500}
2501################################################################################
2502
2503=head2 printFonts - prints a FONTS record
2504
2505 usage:
2506 $gds2File -> printFonts(-string=>'names_of_font_files');
2507
2508=cut
2509
2510sub printFonts #: Profiled
2511{
2512 my($self,%arg) = @_;
2513 my $string = $arg{'-string'};
2514 if (! defined $string)
2515 {
2516 die "printFonts expects a string. Missing -string => 'text' $!";
2517 }
2518 $self -> printGds2Record(-type => 'FONTS',-data => $string);
2519}
2520################################################################################
2521
2522sub printFormat #: Profiled
2523{
2524 my($self,%arg) = @_;
2525 my $num = $arg{'-num'};
2526 if (! defined $num)
2527 {
2528 die "printFormat expects a number. Missing -num => #.# $!";
2529 }
2530 $self -> printGds2Record(-type => 'FORMAT',-data => $num);
2531}
2532################################################################################
2533
2534sub printGenerations #: Profiled
2535{
2536 my $self = shift;
2537 $self -> printGds2Record(-type => 'GENERATIONS');
2538}
2539################################################################################
2540
2541=head2 printHeader - Prints a rev 3 header
2542
2543 usage:
2544 $gds2File -> printHeader(
2545 -num => # ## optional, defaults to 3. valid revs are 0,3,4,5,and 600
2546 );
2547
2548=cut
2549
2550sub printHeader #: Profiled
2551{
2552 my($self,%arg) = @_;
2553 my $rev = $arg{'-num'};
2554 if (! defined $rev)
2555 {
2556 $rev=3;
2557 }
2558 $self -> printGds2Record(-type=>'HEADER',-data=>$rev);
2559}
2560################################################################################
2561
2562=head2 printLayer - prints a LAYER number
2563
2564 usage:
2565 $gds2File -> printLayer(
2566 -num => # ## optional, defaults to 0.
2567 );
2568
2569=cut
2570
2571sub printLayer #: Profiled
2572{
2573 my($self,%arg) = @_;
2574 my $layer = $arg{'-num'};
2575 if (! defined $layer)
2576 {
2577 $layer=0;
2578 }
2579 $self -> printGds2Record(-type => 'LAYER',-data => $layer);
2580}
2581################################################################################
2582
2583sub printLibdirsize #: Profiled
2584{
2585 my $self = shift;
2586 $self -> printGds2Record(-type => 'LIBDIRSIZE');
2587}
2588################################################################################
2589
2590=head2 printLibname - Prints library name
2591
2592 usage:
2593 printLibname(-name=>$name);
2594
2595=cut
2596
2597sub printLibname #: Profiled
2598{
2599 my($self,%arg) = @_;
2600 my $libName = $arg{'-name'};
2601 if (! defined $libName)
2602 {
2603 die "printLibname expects a library name. Missing -name => 'name' $!";
2604 }
2605 $self -> printGds2Record(-type => 'LIBNAME',-data => $libName);
2606}
2607################################################################################
2608
2609sub printLibsecur #: Profiled
2610{
2611 my $self = shift;
2612 $self -> printGds2Record(-type => 'LIBSECUR');
2613}
2614################################################################################
2615
2616sub printLinkkeys #: Profiled
2617{
2618 my($self,%arg) = @_;
2619 my $num = $arg{'-num'};
2620 if (! defined $num)
2621 {
2622 die "printLinkkeys expects a number. Missing -num => #.# $!";
2623 }
2624 $self -> printGds2Record(-type => 'LINKKEYS',-data => $num);
2625}
2626################################################################################
2627
2628sub printLinktype #: Profiled
2629{
2630 my($self,%arg) = @_;
2631 my $num = $arg{'-num'};
2632 if (! defined $num)
2633 {
2634 die "printLinktype expects a number. Missing -num => #.# $!";
2635 }
2636 $self -> printGds2Record(-type => 'LINKTYPE',-data => $num);
2637}
2638################################################################################
2639
2640=head2 printPathtype - prints a PATHTYPE number
2641
2642 usage:
2643 $gds2File -> printPathtype(
2644 -num => # ## optional, defaults to 0.
2645 );
2646
2647=cut
2648
2649sub printPathtype #: Profiled
2650{
2651 my($self,%arg) = @_;
2652 my $pathType = $arg{'-num'};
2653 $pathType=0 if (! defined $pathType);
2654 $self -> printGds2Record(-type => 'PATHTYPE',-data => $pathType) if ($pathType);
2655}
2656################################################################################
2657
2658=head2 printMag - prints a MAG number
2659
2660 usage:
2661 $gds2File -> printMag(
2662 -num => #.# ## optional, defaults to 0.0
2663 );
2664
2665=cut
2666
2667sub printMag #: Profiled
2668{
2669 my($self,%arg) = @_;
2670 my $mag = $arg{'-num'};
2671 $mag=0 if ((! defined $mag)||($mag <= 0));
2672 $self -> printGds2Record(-type => 'MAG',-data => $mag)if ($mag);
2673}
2674################################################################################
2675
2676sub printMask #: Profiled
2677{
2678 my($self,%arg) = @_;
2679 my $string = $arg{'-string'};
2680 if (! defined $string)
2681 {
2682 die "printMask expects a string. Missing -string => 'text' $!";
2683 }
2684 $self -> printGds2Record(-type => 'MASK',-data => $string);
2685}
2686################################################################################
2687
2688sub printNode #: Profiled
2689{
2690 my $self = shift;
2691 $self -> printGds2Record(-type => 'NODE');
2692}
2693################################################################################
2694
2695=head2 printNodetype - prints a NODETYPE number
2696
2697 usage:
2698 $gds2File -> printNodetype(
2699 -num => #
2700 );
2701
2702=cut
2703
2704sub printNodetype #: Profiled
2705{
2706 my($self,%arg) = @_;
2707 my $num = $arg{'-num'};
2708 if (! defined $num)
2709 {
2710 die "printNodetype expects a number. Missing -num => # $!";
2711 }
2712 $self -> printGds2Record(-type => 'NODETYPE',-data => $num);
2713}
2714################################################################################
2715
2716sub printPlex #: Profiled
2717{
2718 my($self,%arg) = @_;
2719 my $num = $arg{'-num'};
2720 if (! defined $num)
2721 {
2722 die "printPlex expects a number. Missing -num => #.# $!";
2723 }
2724 $self -> printGds2Record(-type => 'PLEX',-data => $num);
2725}
2726################################################################################
2727
2728=head2 printPresentation - prints a text presentation record
2729
2730 usage:
2731 $gds2File -> printPresentation(
2732 -font => #, ##optional, defaults to 0, valid numbers are 0-3
2733 -top, ||-middle, || -bottom, ## vertical justification
2734 -left, ||-center, || -right, ## horizontal justification
2735 );
2736
2737 example:
2738 gds2File -> printPresentation(-font=>0,-top,-left);
2739
2740=cut
2741
2742sub printPresentation #: Profiled
2743{
2744 my($self,%arg) = @_;
2745 my $font = $arg{'-font'};
2746 if ((! defined $font) || ($font < 0) || ($font > 3))
2747 {
2748 $font=0;
2749 }
2750 $font = sprintf("%02d",$font);
2751
2752 my $vertical;
2753 my $top = $arg{'-top'};
2754 my $middle = $arg{'-middle'};
2755 my $bottom = $arg{'-bottom'};
2756 if (defined $top) {$vertical = '00';}
2757 elsif (defined $bottom) {$vertical = '10';}
2758 else {$vertical = '01';} ## middle
2759 my $horizontal;
2760 my $left = $arg{'-left'};
2761 my $center = $arg{'-center'};
2762 my $right = $arg{'-right'};
2763 if (defined $left) {$horizontal = '00';}
2764 elsif (defined $right) {$horizontal = '10';}
2765 else {$horizontal = '01';} ## center
2766
2767 my $bitstring = "0000000000$font$vertical$horizontal";
2768 $self -> printGds2Record(-type => 'PRESENTATION',-data => $bitstring);
2769}
2770################################################################################
2771
2772=head2 printPropattr - prints a property id number
2773
2774 usage:
2775 $gds2File -> printPropattr( -num => # );
2776
2777=cut
2778
2779sub printPropattr #: Profiled
2780{
2781 my($self,%arg) = @_;
2782 my $num = $arg{'-num'};
2783 if (! defined $num)
2784 {
2785 die "printPropattr expects a number. Missing -num => # $!";
2786 }
2787 $self -> printGds2Record(-type => 'PROPATTR',-data => $num);
2788}
2789################################################################################
2790
2791=head2 printPropvalue - prints a property value string
2792
2793 usage:
2794 $gds2File -> printPropvalue( -string => $string );
2795
2796=cut
2797
2798sub printPropvalue #: Profiled
2799{
2800 my($self,%arg) = @_;
2801 my $string = $arg{'-string'};
2802 if (! defined $string)
2803 {
2804 die "printPropvalue expects a string. Missing -string => 'text' $!";
2805 }
2806 $self -> printGds2Record(-type => 'PROPVALUE',-data => $string);
2807}
2808################################################################################
2809
2810sub printReflibs #: Profiled
2811{
2812 my($self,%arg) = @_;
2813 my $string = $arg{'-string'};
2814 if (! defined $string)
2815 {
2816 die "printReflibs expects a string. Missing -string => 'text' $!";
2817 }
2818 $self -> printGds2Record(-type => 'REFLIBS',-data => $string);
2819}
2820################################################################################
2821
2822sub printReserved #: Profiled
2823{
2824 my($self,%arg) = @_;
2825 my $num = $arg{'-num'};
2826 if (! defined $num)
2827 {
2828 die "printReserved expects a number. Missing -num => #.# $!";
2829 }
2830 $self -> printGds2Record(-type => 'RESERVED',-data => $num);
2831}
2832################################################################################
2833
2834=head2 printSname - prints a SNAME string
2835
2836 usage:
2837 $gds2File -> printSname( -name => $cellName );
2838
2839=cut
2840
2841sub printSname #: Profiled
2842{
2843 my($self,%arg) = @_;
2844 my $string = $arg{'-name'};
2845 if (! defined $string)
2846 {
2847 die "printSname expects a cell name. Missing -name => 'text' $!";
2848 }
2849 $self -> printGds2Record(-type => 'SNAME',-data => $string);
2850}
2851################################################################################
2852
2853sub printSpacing #: Profiled
2854{
2855 my $self = shift;
2856 die "SPACING type not supported $!";
2857}
2858################################################################################
2859
2860sub printSrfname #: Profiled
2861{
2862 my $self = shift;
2863 $self -> printGds2Record(-type => 'SRFNAME');
2864}
2865################################################################################
2866
2867=head2 printStrans - prints a STRANS record
2868
2869 usage:
2870 $gds2File -> printStrans( -reflect );
2871
2872=cut
2873
2874sub printStrans #: Profiled
2875{
2876 my($self,%arg) = @_;
2877 my $reflect = $arg{'-reflect'};
2878 if ((! defined $reflect)||($reflect <= 0))
2879 {
2880 $reflect=0;
2881 }
2882 else
2883 {
2884 $reflect=1;
2885 }
2886 my $data=$reflect.'000000000000000'; ## 16 'bit' string
2887 $self -> printGds2Record(-type => 'STRANS',-data => $data);
2888}
2889################################################################################
2890
2891sub printStrclass #: Profiled
2892{
2893 my $self = shift;
2894 $self -> printGds2Record(-type => 'STRCLASS');
2895}
2896################################################################################
2897
2898=head2 printString - prints a STRING record
2899
2900 usage:
2901 $gds2File -> printSname( -string => $text );
2902
2903=cut
2904
2905sub printString #: Profiled
2906{
2907 my($self,%arg) = @_;
2908 my $string = $arg{'-string'};
2909 if (! defined $string)
2910 {
2911 die "printString expects a string. Missing -string => 'text' $!";
2912 }
2913 $self -> printGds2Record(-type => 'STRING',-data => $string);
2914}
2915################################################################################
2916
2917=head2 printStrname - prints a structure name string
2918
2919 usage:
2920 $gds2File -> printStrname( -name => $cellName );
2921
2922=cut
2923
2924sub printStrname #: Profiled
2925{
2926 my($self,%arg) = @_;
2927 my $strName = $arg{'-name'};
2928 if (! defined $strName)
2929 {
2930 die "printStrname expects a structure name. Missing -name => 'name' $!";
2931 }
2932 $self -> printGds2Record(-type => 'STRNAME',-data => $strName);
2933}
2934################################################################################
2935
2936sub printStrtype #: Profiled
2937{
2938 my $self = shift;
2939 die "STRTYPE type not supported $!";
2940}
2941################################################################################
2942
2943sub printStyptable #: Profiled
2944{
2945 my($self,%arg) = @_;
2946 my $string = $arg{'-string'};
2947 if (! defined $string)
2948 {
2949 die "printStyptable expects a string. Missing -string => 'text' $!";
2950 }
2951 $self -> printGds2Record(-type => 'STYPTABLE',-data => $string);
2952}
2953################################################################################
2954
2955sub printTapecode #: Profiled
2956{
2957 my($self,%arg) = @_;
2958 my $num = $arg{'-num'};
2959 if (! defined $num)
2960 {
2961 die "printTapecode expects a number. Missing -num => #.# $!";
2962 }
2963 $self -> printGds2Record(-type => 'TAPECODE',-data => $num);
2964}
2965################################################################################
2966
2967sub printTapenum #: Profiled
2968{
2969 my($self,%arg) = @_;
2970 my $num = $arg{'-num'};
2971 if (! defined $num)
2972 {
2973 die "printTapenum expects a number. Missing -num => #.# $!";
2974 }
2975 $self -> printGds2Record(-type => 'TAPENUM',-data => $num);
2976}
2977################################################################################
2978
2979sub printTextnode #: Profiled
2980{
2981 my $self = shift;
2982 $self -> printGds2Record(-type => 'TEXTNODE');
2983}
2984################################################################################
2985
2986=head2 printTexttype - prints a text type number
2987
2988 usage:
2989 $gds2File -> printTexttype( -num => # );
2990
2991=cut
2992
2993sub printTexttype #: Profiled
2994{
2995 my($self,%arg) = @_;
2996 my $num = $arg{'-num'};
2997 if (! defined $num)
2998 {
2999 die "printTexttype expects a number. Missing -num => # $!";
3000 }
3001 $num = 0 if ($num < 0);
3002 $self -> printGds2Record(-type => 'TEXTTYPE',-data => $num);
3003}
3004################################################################################
3005
3006sub printUinteger #: Profiled
3007{
3008 my $self = shift;
3009 die "UINTEGER type not supported $!";
3010}
3011################################################################################
3012
3013=head2 printUnits - Prints units record.
3014
3015 Defaults to 1e-3 and 1e-9
3016
3017=cut
3018
3019sub printUnits #: Profiled
3020{
3021 my $self = shift;
3022 $self -> printGds2Record(-type => 'UNITS',-data => [0.001,1e-9]);
3023}
3024################################################################################
3025
3026sub printUstring #: Profiled
3027{
3028 my $self = shift;
3029 die "USTRING type not supported $!";
3030}
3031################################################################################
3032
3033=head2 printPropattr - prints a width number
3034
3035 usage:
3036 $gds2File -> printWidth( -num => # );
3037
3038=cut
3039
3040sub printWidth #: Profiled
3041{
3042 my($self,%arg) = @_;
3043 my $width = $arg{'-num'};
3044 if ((! defined $width)||($width <= 0))
3045 {
3046 $width=0;
3047 }
3048 $self -> printGds2Record(-type => 'WIDTH',-data => $width) if ($width);
3049}
3050################################################################################
3051
3052=head2 printXy - prints an XY array
3053
3054 usage:
3055 $gds2File -> printXy( -xy => \@array );
3056
3057=cut
3058
3059sub printXy #: Profiled
3060{
3061 my($self,%arg) = @_;
3062 #### -xyInt most useful if reading and modifying... -xy if creating from scratch
3063 my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
3064 my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
3065 my $resolution = $self -> {'Resolution'};
3066 if (! ((defined $xy) || (defined $xyInt)))
3067 {
3068 die "printXy expects an xy array reference. Missing -xy => \\\@array $!";
3069 }
3070 if (defined $xyInt)
3071 {
3072 $xy = $xyInt;
3073 $resolution=1;
3074 }
3075 my @xyTmp=(); ##don't pollute array passed in
3076 for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
3077 {
3078 if ($xy -> [$i] >= 0) {push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
3079 else {push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
3080 }
3081 $self -> printGds2Record(-type => 'XY',-data => \@xyTmp);
3082}
3083################################################################################
3084
3085
3086################################################################################
3087
3088=head1 Low Level Specific Evaluation Methods
3089
3090=cut
3091
3092################################################################################
3093
3094=head2 returnDatatype - returns datatype # if record is DATATYPE else returns -1
3095
3096 usage:
3097 $dataTypesFound[$gds2File -> returnDatatype] = 1;
3098
3099=cut
3100
3101sub returnDatatype #: Profiled
3102{
3103 my $self = shift;
3104 ## 2 byte signed integer
3105 if ($self -> isDatatype) { $self -> {'RecordData'}[0]; }
3106 else { -1; }
3107}
3108################################################################################
3109
3110=head2 returnPathtype - returns pathtype # if record is PATHTYPE else returns -1
3111
3112 usage:
3113
3114=cut
3115
3116sub returnPathtype #: Profiled
3117{
3118 my $self = shift;
3119 ## 2 byte signed integer
3120 if ($self -> isPathtype) { $self -> {'RecordData'}[0]; }
3121 else { -1; }
3122}
3123################################################################################
3124
3125=head2 returnTexttype - returns texttype # if record is TEXTTYPE else returns -1
3126
3127 usage:
3128 $TextTypesFound[$gds2File -> returnTexttype] = 1;
3129
3130=cut
3131
3132sub returnTexttype #: Profiled
3133{
3134 my $self = shift;
3135 ## 2 byte signed integer
3136 if ($self -> isTexttype) { $self -> {'RecordData'}[0]; }
3137 else { -1; }
3138}
3139################################################################################
3140
3141=head2 returnWidth - returns width # if record is WIDTH else returns -1
3142
3143 usage:
3144
3145=cut
3146
3147sub returnWidth #: Profiled
3148{
3149 my $self = shift;
3150 ## 4 byte signed integer
3151 if ($self -> isWidth) { $self -> {'RecordData'}[0]; }
3152 else { -1; }
3153}
3154################################################################################
3155
3156
3157=head2 returnLayer - returns layer # if record is LAYER else returns -1
3158
3159 usage:
3160 $layersFound[$gds2File -> returnLayer] = 1;
3161
3162=cut
3163
3164sub returnLayer #: Profiled
3165{
3166 my $self = shift;
3167 ## 2 byte signed integer
3168 if ($self -> isLayer) { $self -> {'RecordData'}[0]; }
3169 else { -1; }
3170}
3171################################################################################
3172
3173=head2 returnBgnextn - returns bgnextn if record is BGNEXTN else returns 0
3174
3175 usage:
3176
3177=cut
3178
3179sub returnBgnextn #: Profiled
3180{
3181 my $self = shift;
3182 ## 2 byte signed integer
3183 if ($self -> isBgnextn) { $self -> {'RecordData'}[0]; }
3184 else { 0; }
3185}
3186################################################################################
3187
3188=head2 returnEndextn- returns endextn if record is ENDEXTN else returns 0
3189
3190 usage:
3191
3192=cut
3193
3194sub returnEndextn #: Profiled
3195{
3196 my $self = shift;
3197 ## 2 byte signed integer
3198 if ($self -> isEndextn) { $self -> {'RecordData'}[0]; }
3199 else { 0; }
3200}
3201################################################################################
3202
3203=head2 returnString - return string if record type is STRING else ''
3204
3205=cut
3206
3207sub returnString #: Profiled
3208{
3209 my $self = shift;
3210 if ($self -> isString) { $self -> {'RecordData'}[0]; }
3211 else { ''; }
3212}
3213################################################################################
3214
3215=head2 returnSname - return string if record type is SNAME else ''
3216
3217=cut
3218
3219sub returnSname #: Profiled
3220{
3221 my $self = shift;
3222 if ($self -> isSname) { $self -> {'RecordData'}[0]; }
3223 else { ''; }
3224}
3225################################################################################
3226
3227=head2 returnStrname - return string if record type is STRNAME else ''
3228
3229=cut
3230
3231sub returnStrname #: Profiled
3232{
3233 my $self = shift;
3234 if ($self -> isStrname) { $self -> {'RecordData'}[0]; }
3235 else { ''; }
3236}
3237################################################################################
3238
3239################################################################################
3240
3241=head1 Low Level Specific Boolean Methods
3242
3243=cut
3244
3245################################################################################
3246
3247=head2 isAref - return 0 or 1 depending on whether current record is an aref
3248
3249=cut
3250
3251sub isAref #: Profiled
3252{
3253 my $self = shift;
3254 if ($self -> {'RecordType'} == AREF) { 1; }
3255 else { 0; }
3256}
3257################################################################################
3258
3259=head2 isBgnlib - return 0 or 1 depending on whether current record is a bgnlib
3260
3261=cut
3262
3263sub isBgnlib #: Profiled
3264{
3265 my $self = shift;
3266 if ($self -> {'RecordType'} == BGNLIB) { 1; }
3267 else { 0; }
3268}
3269################################################################################
3270
3271=head2 isBgnstr - return 0 or 1 depending on whether current record is a bgnstr
3272
3273=cut
3274
3275sub isBgnstr #: Profiled
3276{
3277 my $self = shift;
3278 if ($self -> {'RecordType'} == BGNSTR) { 1; }
3279 else { 0; }
3280}
3281################################################################################
3282
3283=head2 isBoundary - return 0 or 1 depending on whether current record is a boundary
3284
3285=cut
3286
3287sub isBoundary #: Profiled
3288{
3289 my $self = shift;
3290 if ($self -> {'RecordType'} == BOUNDARY) { 1; }
3291 else { 0; }
3292}
3293################################################################################
3294
3295=head2 isDatatype - return 0 or 1 depending on whether current record is datatype
3296
3297=cut
3298
3299sub isDatatype #: Profiled
3300{
3301 my $self = shift;
3302 if ($self -> {'RecordType'} == DATATYPE) { 1; }
3303 else { 0; }
3304}
3305################################################################################
3306
3307=head2 isEndlib - return 0 or 1 depending on whether current record is endlib
3308
3309=cut
3310
3311sub isEndlib #: Profiled
3312{
3313 my $self = shift;
3314 if ($self -> {'RecordType'} == ENDLIB) { 1; }
3315 else { 0; }
3316}
3317################################################################################
3318
3319=head2 isEndel - return 0 or 1 depending on whether current record is endel
3320
3321=cut
3322
3323sub isEndel #: Profiled
3324{
3325 my $self = shift;
3326 if ($self -> {'RecordType'} == ENDEL) { 1; }
3327 else { 0; }
3328}
3329################################################################################
3330
3331=head2 isEndstr - return 0 or 1 depending on whether current record is endstr
3332
3333=cut
3334
3335sub isEndstr #: Profiled
3336{
3337 my $self = shift;
3338 if ($self -> {'RecordType'} == ENDSTR) { 1; }
3339 else { 0; }
3340}
3341################################################################################
3342
3343
3344=head2 isHeader - return 0 or 1 depending on whether current record is a header
3345
3346=cut
3347
3348sub isHeader #: Profiled
3349{
3350 my $self = shift;
3351 if ($self -> {'RecordType'} == HEADER) { 1; }
3352 else { 0; }
3353}
3354################################################################################
3355
3356=head2 isLibname - return 0 or 1 depending on whether current record is a libname
3357
3358=cut
3359
3360sub isLibname #: Profiled
3361{
3362 my $self = shift;
3363 if ($self -> {'RecordType'} == LIBNAME) { 1; }
3364 else { 0; }
3365}
3366################################################################################
3367
3368=head2 isPath - return 0 or 1 depending on whether current record is a path
3369
3370=cut
3371
3372sub isPath #: Profiled
3373{
3374 my $self = shift;
3375 if ($self -> {'RecordType'} == PATH) { 1; }
3376 else { 0; }
3377}
3378################################################################################
3379
3380=head2 isSref - return 0 or 1 depending on whether current record is an sref
3381
3382=cut
3383
3384sub isSref #: Profiled
3385{
3386 my $self = shift;
3387 if ($self -> {'RecordType'} == SREF) { 1; }
3388 else { 0; }
3389}
3390################################################################################
3391
3392=head2 isSrfname - return 0 or 1 depending on whether current record is an srfname
3393
3394=cut
3395
3396sub isSrfname #: Profiled
3397{
3398 my $self = shift;
3399 if ($self -> {'RecordType'} == SRFNAME) { 1; }
3400 else { 0; }
3401}
3402################################################################################
3403
3404=head2 isText - return 0 or 1 depending on whether current record is a text
3405
3406=cut
3407
3408sub isText #: Profiled
3409{
3410 my $self = shift;
3411 if ($self -> {'RecordType'} == TEXT) { 1; }
3412 else { 0; }
3413}
3414################################################################################
3415
3416=head2 isUnits - return 0 or 1 depending on whether current record is units
3417
3418=cut
3419
3420sub isUnits #: Profiled
3421{
3422 my $self = shift;
3423 if ($self -> {'RecordType'} == UNITS) { 1; }
3424 else { 0; }
3425}
3426################################################################################
3427
3428=head2 isLayer - return 0 or 1 depending on whether current record is layer
3429
3430=cut
3431
3432sub isLayer #: Profiled
3433{
3434 my $self = shift;
3435 if ($self -> {'RecordType'} == LAYER) { 1; }
3436 else { 0; }
3437}
3438################################################################################
3439
3440=head2 isStrname - return 0 or 1 depending on whether current record is strname
3441
3442=cut
3443
3444sub isStrname #: Profiled
3445{
3446 my $self = shift;
3447 if ($self -> {'RecordType'} == STRNAME) { 1; }
3448 else { 0; }
3449}
3450################################################################################
3451
3452=head2 isWidth - return 0 or 1 depending on whether current record is width
3453
3454=cut
3455
3456sub isWidth #: Profiled
3457{
3458 my $self = shift;
3459 if ($self -> {'RecordType'} == WIDTH) { 1; }
3460 else { 0; }
3461}
3462################################################################################
3463
3464=head2 isXy - return 0 or 1 depending on whether current record is xy
3465
3466=cut
3467
3468sub isXy #: Profiled
3469{
3470 my $self = shift;
3471 if ($self -> {'RecordType'} == XY) { 1; }
3472 else { 0; }
3473}
3474################################################################################
3475
3476=head2 isSname - return 0 or 1 depending on whether current record is sname
3477
3478=cut
3479
3480sub isSname #: Profiled
3481{
3482 my $self = shift;
3483 if ($self -> {'RecordType'} == SNAME) { 1; }
3484 else { 0; }
3485}
3486################################################################################
3487
3488=head2 isColrow - return 0 or 1 depending on whether current record is colrow
3489
3490=cut
3491
3492sub isColrow #: Profiled
3493{
3494 my $self = shift;
3495 if ($self -> {'RecordType'} == COLROW) { 1; }
3496 else { 0; }
3497}
3498################################################################################
3499
3500=head2 isTextnode - return 0 or 1 depending on whether current record is a textnode
3501
3502=cut
3503
3504sub isTextnode #: Profiled
3505{
3506 my $self = shift;
3507 if ($self -> {'RecordType'} == TEXTNODE) { 1; }
3508 else { 0; }
3509}
3510################################################################################
3511
3512=head2 isNode - return 0 or 1 depending on whether current record is a node
3513
3514=cut
3515
3516sub isNode #: Profiled
3517{
3518 my $self = shift;
3519 if ($self -> {'RecordType'} == NODE) { 1; }
3520 else { 0; }
3521}
3522################################################################################
3523
3524=head2 isTexttype - return 0 or 1 depending on whether current record is a texttype
3525
3526=cut
3527
3528sub isTexttype #: Profiled
3529{
3530 my $self = shift;
3531 if ($self -> {'RecordType'} == TEXTTYPE) { 1; }
3532 else { 0; }
3533}
3534################################################################################
3535
3536=head2 isPresentation - return 0 or 1 depending on whether current record is a presentation
3537
3538=cut
3539
3540sub isPresentation #: Profiled
3541{
3542 my $self = shift;
3543 if ($self -> {'RecordType'} == PRESENTATION) { 1; }
3544 else { 0; }
3545}
3546################################################################################
3547
3548=head2 isSpacing - return 0 or 1 depending on whether current record is a spacing
3549
3550=cut
3551
3552sub isSpacing #: Profiled
3553{
3554 my $self = shift;
3555 if ($self -> {'RecordType'} == SPACING) { 1; }
3556 else { 0; }
3557}
3558################################################################################
3559
3560=head2 isString - return 0 or 1 depending on whether current record is a string
3561
3562=cut
3563
3564sub isString #: Profiled
3565{
3566 my $self = shift;
3567 if ($self -> {'RecordType'} == STRING) { 1; }
3568 else { 0; }
3569}
3570################################################################################
3571
3572=head2 isStrans - return 0 or 1 depending on whether current record is a strans
3573
3574=cut
3575
3576sub isStrans #: Profiled
3577{
3578 my $self = shift;
3579 if ($self -> {'RecordType'} == STRANS) { 1; }
3580 else { 0; }
3581}
3582################################################################################
3583
3584=head2 isMag - return 0 or 1 depending on whether current record is a mag
3585
3586=cut
3587
3588sub isMag #: Profiled
3589{
3590 my $self = shift;
3591 if ($self -> {'RecordType'} == MAG) { 1; }
3592 else { 0; }
3593}
3594################################################################################
3595
3596=head2 isAngle - return 0 or 1 depending on whether current record is a angle
3597
3598=cut
3599
3600sub isAngle #: Profiled
3601{
3602 my $self = shift;
3603 if ($self -> {'RecordType'} == ANGLE) { 1; }
3604 else { 0; }
3605}
3606################################################################################
3607
3608=head2 isUinteger - return 0 or 1 depending on whether current record is a uinteger
3609
3610=cut
3611
3612sub isUinteger #: Profiled
3613{
3614 my $self = shift;
3615 if ($self -> {'RecordType'} == UINTEGER) { 1; }
3616 else { 0; }
3617}
3618################################################################################
3619
3620=head2 isUstring - return 0 or 1 depending on whether current record is a ustring
3621
3622=cut
3623
3624sub isUstring #: Profiled
3625{
3626 my $self = shift;
3627 if ($self -> {'RecordType'} == USTRING) { 1; }
3628 else { 0; }
3629}
3630################################################################################
3631
3632=head2 isReflibs - return 0 or 1 depending on whether current record is a reflibs
3633
3634=cut
3635
3636sub isReflibs #: Profiled
3637{
3638 my $self = shift;
3639 if ($self -> {'RecordType'} == REFLIBS) { 1; }
3640 else { 0; }
3641}
3642################################################################################
3643
3644=head2 isFonts - return 0 or 1 depending on whether current record is a fonts
3645
3646=cut
3647
3648sub isFonts #: Profiled
3649{
3650 my $self = shift;
3651 if ($self -> {'RecordType'} == FONTS) { 1; }
3652 else { 0; }
3653}
3654################################################################################
3655
3656=head2 isPathtype - return 0 or 1 depending on whether current record is a pathtype
3657
3658=cut
3659
3660sub isPathtype #: Profiled
3661{
3662 my $self = shift;
3663 if ($self -> {'RecordType'} == PATHTYPE) { 1; }
3664 else { 0; }
3665}
3666################################################################################
3667
3668=head2 isGenerations - return 0 or 1 depending on whether current record is a generations
3669
3670=cut
3671
3672sub isGenerations #: Profiled
3673{
3674 my $self = shift;
3675 if ($self -> {'RecordType'} == GENERATIONS) { 1; }
3676 else { 0; }
3677}
3678################################################################################
3679
3680=head2 isAttrtable - return 0 or 1 depending on whether current record is a attrtable
3681
3682=cut
3683
3684sub isAttrtable #: Profiled
3685{
3686 my $self = shift;
3687 if ($self -> {'RecordType'} == ATTRTABLE) { 1; }
3688 else { 0; }
3689}
3690################################################################################
3691
3692=head2 isStyptable - return 0 or 1 depending on whether current record is a styptable
3693
3694=cut
3695
3696sub isStyptable #: Profiled
3697{
3698 my $self = shift;
3699 if ($self -> {'RecordType'} == STYPTABLE) { 1; }
3700 else { 0; }
3701}
3702################################################################################
3703
3704=head2 isStrtype - return 0 or 1 depending on whether current record is a strtype
3705
3706=cut
3707
3708sub isStrtype #: Profiled
3709{
3710 my $self = shift;
3711 if ($self -> {'RecordType'} == STRTYPE) { 1; }
3712 else { 0; }
3713}
3714################################################################################
3715
3716=head2 isEflags - return 0 or 1 depending on whether current record is a eflags
3717
3718=cut
3719
3720sub isEflags #: Profiled
3721{
3722 my $self = shift;
3723 if ($self -> {'RecordType'} == EFLAGS) { 1; }
3724 else { 0; }
3725}
3726################################################################################
3727
3728=head2 isElkey - return 0 or 1 depending on whether current record is a elkey
3729
3730=cut
3731
3732sub isElkey #: Profiled
3733{
3734 my $self = shift;
3735 if ($self -> {'RecordType'} == ELKEY) { 1; }
3736 else { 0; }
3737}
3738################################################################################
3739
3740=head2 isLinktype - return 0 or 1 depending on whether current record is a linktype
3741
3742=cut
3743
3744sub isLinktype #: Profiled
3745{
3746 my $self = shift;
3747 if ($self -> {'RecordType'} == LINKTYPE) { 1; }
3748 else { 0; }
3749}
3750################################################################################
3751
3752=head2 isLinkkeys - return 0 or 1 depending on whether current record is a linkkeys
3753
3754=cut
3755
3756sub isLinkkeys #: Profiled
3757{
3758 my $self = shift;
3759 if ($self -> {'RecordType'} == LINKKEYS) { 1; }
3760 else { 0; }
3761}
3762################################################################################
3763
3764=head2 isNodetype - return 0 or 1 depending on whether current record is a nodetype
3765
3766=cut
3767
3768sub isNodetype #: Profiled
3769{
3770 my $self = shift;
3771 if ($self -> {'RecordType'} == NODETYPE) { 1; }
3772 else { 0; }
3773}
3774################################################################################
3775
3776=head2 isPropattr - return 0 or 1 depending on whether current record is a propattr
3777
3778=cut
3779
3780sub isPropattr #: Profiled
3781{
3782 my $self = shift;
3783 if ($self -> {'RecordType'} == PROPATTR) { 1; }
3784 else { 0; }
3785}
3786################################################################################
3787
3788=head2 isPropvalue - return 0 or 1 depending on whether current record is a propvalue
3789
3790=cut
3791
3792sub isPropvalue #: Profiled
3793{
3794 my $self = shift;
3795 if ($self -> {'RecordType'} == PROPVALUE) { 1; }
3796 else { 0; }
3797}
3798################################################################################
3799
3800=head2 isBox - return 0 or 1 depending on whether current record is a box
3801
3802=cut
3803
3804sub isBox #: Profiled
3805{
3806 my $self = shift;
3807 if ($self -> {'RecordType'} == BOX) { 1; }
3808 else { 0; }
3809}
3810################################################################################
3811
3812=head2 isBoxtype - return 0 or 1 depending on whether current record is a boxtype
3813
3814=cut
3815
3816sub isBoxtype #: Profiled
3817{
3818 my $self = shift;
3819 if ($self -> {'RecordType'} == BOXTYPE) { 1; }
3820 else { 0; }
3821}
3822################################################################################
3823
3824=head2 isPlex - return 0 or 1 depending on whether current record is a plex
3825
3826=cut
3827
3828sub isPlex #: Profiled
3829{
3830 my $self = shift;
3831 if ($self -> {'RecordType'} == PLEX) { 1; }
3832 else { 0; }
3833}
3834################################################################################
3835
3836=head2 isBgnextn - return 0 or 1 depending on whether current record is a bgnextn
3837
3838=cut
3839
3840sub isBgnextn #: Profiled
3841{
3842 my $self = shift;
3843 if ($self -> {'RecordType'} == BGNEXTN) { 1; }
3844 else { 0; }
3845}
3846################################################################################
3847
3848=head2 isEndextn - return 0 or 1 depending on whether current record is a endextn
3849
3850=cut
3851
3852sub isEndextn #: Profiled
3853{
3854 my $self = shift;
3855 if ($self -> {'RecordType'} == ENDEXTN) { 1; }
3856 else { 0; }
3857}
3858################################################################################
3859
3860=head2 isTapenum - return 0 or 1 depending on whether current record is a tapenum
3861
3862=cut
3863
3864sub isTapenum #: Profiled
3865{
3866 my $self = shift;
3867 if ($self -> {'RecordType'} == TAPENUM) { 1; }
3868 else { 0; }
3869}
3870################################################################################
3871
3872=head2 isTapecode - return 0 or 1 depending on whether current record is a tapecode
3873
3874=cut
3875
3876sub isTapecode #: Profiled
3877{
3878 my $self = shift;
3879 if ($self -> {'RecordType'} == TAPECODE) { 1; }
3880 else { 0; }
3881}
3882################################################################################
3883
3884=head2 isStrclass - return 0 or 1 depending on whether current record is a strclass
3885
3886=cut
3887
3888sub isStrclass #: Profiled
3889{
3890 my $self = shift;
3891 if ($self -> {'RecordType'} == STRCLASS) { 1; }
3892 else { 0; }
3893}
3894################################################################################
3895
3896=head2 isReserved - return 0 or 1 depending on whether current record is a reserved
3897
3898=cut
3899
3900sub isReserved #: Profiled
3901{
3902 my $self = shift;
3903 if ($self -> {'RecordType'} == RESERVED) { 1; }
3904 else { 0; }
3905}
3906################################################################################
3907
3908=head2 isFormat - return 0 or 1 depending on whether current record is a format
3909
3910=cut
3911
3912sub isFormat #: Profiled
3913{
3914 my $self = shift;
3915 if ($self -> {'RecordType'} == FORMAT) { 1; }
3916 else { 0; }
3917}
3918################################################################################
3919
3920=head2 isMask - return 0 or 1 depending on whether current record is a mask
3921
3922=cut
3923
3924sub isMask #: Profiled
3925{
3926 my $self = shift;
3927 if ($self -> {'RecordType'} == MASK) { 1; }
3928 else { 0; }
3929}
3930################################################################################
3931
3932=head2 isEndmasks - return 0 or 1 depending on whether current record is a endmasks
3933
3934=cut
3935
3936sub isEndmasks #: Profiled
3937{
3938 my $self = shift;
3939 if ($self -> {'RecordType'} == ENDMASKS) { 1; }
3940 else { 0; }
3941}
3942################################################################################
3943
3944=head2 isLibdirsize - return 0 or 1 depending on whether current record is a libdirsize
3945
3946=cut
3947
3948sub isLibdirsize #: Profiled
3949{
3950 my $self = shift;
3951 if ($self -> {'RecordType'} == LIBDIRSIZE) { 1; }
3952 else { 0; }
3953}
3954################################################################################
3955
3956=head2 isLibsecur - return 0 or 1 depending on whether current record is a libsecur
3957
3958=cut
3959
3960sub isLibsecur #: Profiled
3961{
3962 my $self = shift;
3963 if ($self -> {'RecordType'} == LIBSECUR) { 1; }
3964 else { 0; }
3965}
3966################################################################################
3967
3968################################################################################
3969## support functions
3970
3971sub getRecordData #: Profiled
3972{
3973 my $self = shift;
3974 my $dt = $self -> {'DataType'};
3975 if ($dt==NO_DATA)
3976 {
3977 return '';
3978 }
3979 elsif ($dt==INTEGER_2 || $dt==INTEGER_4 || $dt==REAL_8)
3980 {
3981 my $stuff = $self -> {'CurrentDataList'};
3982 $stuff =~ s|^,||;
3983 return(split(/,/,$stuff));
3984 }
3985 elsif ($dt==ACSII_STRING)
3986 {
3987 my $stuff = $self -> {'CurrentDataList'};
3988 $stuff =~ s|\0||g;
3989 return($stuff);
3990 }
3991 else ## bit_array
3992 {
3993 return ($self -> {'CurrentDataList'});
3994 }
3995}
3996################################################################################
3997
3998sub readRecordTypeAndData #: Profiled
3999{
4000 my $self = shift;
4001 return ($RecordTypeStrings[$self -> {'RecordType'}],$self -> {'RecordData'});
4002}
4003################################################################################
4004
4005sub skipGds2RecordData #: Profiled
4006{
4007 my $self = shift;
4008 $self -> readGds2RecordHeader() if ($self -> {'HEADER'} <= 0); ## safety
4009 if (TIMER_ON)
4010 {
4011 $G_timer -> reset();
4012 $G_timer -> start('skipGds2RecordData');
4013 }
4014 $self -> {'HEADER'} = 0;
4015 $self -> {'INDATA'} = 1;
4016 ## 4 should have been just read by readGds2RecordHeader
4017 seek($self -> {'FileHandle'},$self -> {'Length'} - 4,SEEK_CUR); ## seek seems to run a little faster than read
4018 $self -> {'DataIndex'} = -1;
4019 if (TIMER_ON)
4020 {
4021 $G_timer -> stop;
4022 $G_timer -> report;
4023 }
4024 return 1;
4025}
4026################################################################################
4027
4028### return number of XY coords if XY record
4029sub returnNumCoords #: Profiled
4030{
4031 my $self = shift;
4032 if ($self -> {'RecordType'} == XY) ## 4 byte signed integer
4033 {
4034 int(($self -> {'Length'} - 4) / 8);
4035 }
4036 else
4037 {
4038 0;
4039 }
4040}
4041################################################################################
4042
4043sub roundNum #: Profiled
4044{
4045 my $self = shift;
4046 my $num = shift;
4047 my $places = shift;
4048 sprintf("%.${places}f",$num);
4049}
4050################################################################################
4051
4052sub scaleNum($$) #: Profiled
4053{
4054 my $num=shift;
4055 my $scale=shift;
4056 die "1st number passed into scaleNum() must be an integer $!" if ($num !~ m|^-?\d+|);
4057 $num = $num * $scale;
4058 $num = int($num+0.5) if ($num =~ m|\.|);
4059 $num;
4060}
4061################################################################################
4062
4063sub snapNum($$) #: Profiled
4064{
4065 my $num=shift;
4066 die "1st number passed into snapNum() must be an integer $!" if ($num !~ m|^-?\d+$|);
4067 my $snap=shift;
4068 my $snapLength = length("$snap");
4069 my $lean=1; ##init
4070 $lean = -1 if($num < 0);
4071 ## snap to grid..
4072 my $littlePart=substr($num,-$snapLength,$snapLength);
4073 if($num<0)
4074 {
4075 $littlePart = -$littlePart;
4076 }
4077 $littlePart = int(($littlePart/$snap)+(0.5*$lean))*$snap;
4078 my $bigPart=substr($num,0,-$snapLength);
4079 if ($bigPart =~ m|^[-]?$|)
4080 {
4081 $bigPart=0;
4082 }
4083 else
4084 {
4085 $bigPart *= 10**$snapLength;
4086 }
4087 $num = $bigPart + $littlePart;
4088 $num;
4089}
4090
4091sub DESTROY #: Profiled
4092{
4093 my $self = shift;
4094 #warn "DESTROYing $self";
4095}
4096
4097################################################################################
4098## some vendor tools have trouble w/ negative angles and angles >= 360
4099## so we normalize to positive equivalent
4100################################################################################
4101sub posAngle($) #: Profiled
4102{
4103 my $angle = shift;
4104 $angle += 360.0 while ($angle < 0.0);
4105 $angle -= 360.0 while ($angle >= 360.0);
4106 $angle;
4107}
4108
4109=head2 tellSize - return current byte position (NOT zero based)
4110
4111 usage:
4112 my $position = $gds2File -> tellSize;
4113
4114
4115=cut
4116
4117################################################################################
4118sub tellSize() #: Profiled
4119{
4120 my $self = shift;
4121 $self -> {'GDSLENGTH'};
4122}
4123
4124################################################################################
4125sub subbyte() ## GDS2::version();
4126{
4127 my($what,$where,$howmuch) = @_;
4128 unpack("x$where C$howmuch", $what);
4129}
4130
4131################################################################################
4132sub version() ## GDS2::version();
4133{
4134 return $GDS2::VERSION;
4135}
4136
4137################################################################################
4138sub revision() ## GDS2::revision();
4139{
4140 return $GDS2::revision;
4141}
4142
4143
41441;
4145}
4146
4147################################################################################
4148
4149__END__
4150
4151=pod
4152
4153=head1 Examples
4154
4155 Layer change:
4156 here's a bare bones script to change all layer 59 to 66 given a file to
4157 read and a new file to create.
4158 #!/usr/local/bin/perl -w
4159 use strict;
4160 use GDS2;
4161 my $fileName1 = $ARGV[0];
4162 my $fileName2 = $ARGV[1];
4163
4164 my $gds2File1 = new GDS2(-fileName => $fileName1);
4165 my $gds2File2 = new GDS2(-fileName => ">$fileName2");
4166
4167 while (my $record = $gds2File1 -> readGds2Record)
4168 {
4169 if ($gds2File1 -> returnLayer == 59)
4170 {
4171 $gds2File2 -> printLayer(-num=>66);
4172 }
4173 else
4174 {
4175 $gds2File2 -> printRecord(-data=>$record);
4176 }
4177 }
4178
4179
4180 Gds2 dump:
4181 here's a program to dump the contents of a stream file.
4182 #!/usr/local/bin/perl -w
4183 use GDS2;
4184 $\="\n";
4185
4186 my $gds2File = new GDS2(-fileName=>$ARGV[0]);
4187 while ($gds2File -> readGds2Record)
4188 {
4189 print $gds2File -> returnRecordAsString;
4190 }
4191
4192
4193 Create a complete GDS2 stream file from scratch:
4194 #!/usr/local/bin/perl -w
4195 use GDS2;
4196 my $gds2File = new GDS2(-fileName=>'>test.gds');
4197 $gds2File -> printInitLib(-name=>'testlib');
4198 $gds2File -> printBgnstr(-name=>'test');
4199 $gds2File -> printPath(
4200 -layer=>6,
4201 -pathType=>0,
4202 -width=>2.4,
4203 -xy=>[0,0, 10.5,0, 10.5,3.3],
4204 );
4205 $gds2File -> printSref(
4206 -name=>'contact',
4207 -xy=>[4,5.5],
4208 );
4209 $gds2File -> printAref(
4210 -name=>'contact',
4211 -columns=>2,
4212 -rows=>3,
4213 -xy=>[0,0],
4214 );
4215 $gds2File -> printEndstr;
4216 $gds2File -> printBgnstr(-name => 'contact');
4217 $gds2File -> printBoundary(
4218 -layer=>10,
4219 -xy=>[0,0, 1,0, 1,1, 0,1],
4220 );
4221 $gds2File -> printEndstr;
4222 $gds2File -> printEndlib();
4223
4224=head1 GDS2 Stream Format
4225
4226 #########################################################################################
4227 #
4228 # Gds2 stream format is composed of variable length records. The mininum
4229 # length record is 4 bytes. The 1st 2 bytes of a record contain a count (in 8 bit
4230 # bytes) of the total record length. The 3rd byte of the header is the record
4231 # type. The 4th byte describes the type of data contained w/in the record. The
4232 # 5th through last bytes are data.
4233 #
4234 # If the output file is a mag tape, then the records of the library are written
4235 # out in 2048-byte physical blocks. Records may overlap block boundaries.
4236 # For this reason I think gds2 is often padded with null bytes so that the
4237 # file size ends up being a multiple of 2048.
4238 #
4239 # A null word consists of 2 consecutive zero bytes. Use null words to fill the
4240 # space between:
4241 # o the last record of a library and the end of its block
4242 # o the last record of a tape in a mult-reel stream file.
4243 #
4244 # DATA TYPE VALUE RECORD
4245 # --------- ----- -----------------------
4246 # no data present 0 4 byte header + 0
4247 #
4248 # Bit Array 1 4 byte header + 2 bytes data
4249 #
4250 # 2byte Signed Int 2 SMMMMMMM MMMMMMMM -> S - sign ; M - magnitude.
4251 # Twos complement format, with the most significant byte first.
4252 # I.E.
4253 # 0x0001 = 1
4254 # 0x0002 = 2
4255 # 0x0089 = 137
4256 # 0xffff = -1
4257 # 0xfffe = -2
4258 # 0xff77 = -137
4259 #
4260 # 4byte Signed Int 3 SMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM
4261 #
4262 # 8byte Real 5 SEEEEEEE MMMMMMMM MMMMMMMM MMMMMMMM E-expon in excess-64
4263 # MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM representation
4264 #
4265 # Mantissa == pos fraction >=1/16 && <1 bit 8==1/2, 9==1/4 etc...
4266 # The first bit is the sign (1 = negative), the next 7 bits
4267 # are the exponent, you have to subtract 64 from this number to
4268 # get the real value. The next seven bytes are the mantissa in
4269 # 4 word floating point representation.
4270 #
4271 #
4272 # string 6 odd length strings must be padded w/ null character and
4273 # byte count++
4274 #
4275 #########################################################################################
4276
4277
4278=head1 Backus-naur representation of GDS2 Stream Syntax
4279
4280 ################################################################################
4281 # <STREAM FORMAT>::= HEADER BGNLIB [LIBDIRSIZE] [SRFNAME] [LIBSECR] #
4282 # LIBNAME [REFLIBS] [FONTS] [ATTRTABLE] [GENERATIONS] #
4283 # [<FormatType>] UNITS {<structure>}* ENDLIB #
4284 # #
4285 # <FormatType>::= FORMAT | FORMAT {MASK}+ ENDMASKS #
4286 # #
4287 # <structure>::= BGNSTR STRNAME [STRCLASS] {<element>}* ENDSTR #
4288 # #
4289 # <element>::= {<boundary> | <path> | <SREF> | <AREF> | <text> | #
4290 # <node> | <box} {<property>}* ENDEL #
4291 # #
4292 # <boundary>::= BOUNDARY [ELFLAGS] [PLEX] LAYER DATATYPE XY #
4293 # #
4294 # <path>::= PATH [ELFLAGS] [PLEX] LAYER DATATYPE [PATHTYPE] #
4295 # [WIDTH] [BGNEXTN] [ENDEXTN] [XY] #
4296 # #
4297 # <SREF>::= SREF [ELFLAGS] [PLEX] SNAME [<strans>] XY #
4298 # #
4299 # <AREF>::= AREF [ELFLAGS] [PLEX] SNAME [<strans>] COLROW XY #
4300 # #
4301 # <text>::= TEXT [ELFLAGS] [PLEX] LAYER <textbody> #
4302 # #
4303 # <textbody>::= TEXTTYPE [PRESENTATION] [PATHTYPE] [WIDTH] [<strans>] XY #
4304 # STRING #
4305 # #
4306 # <strans>::= STRANS [MAG] [ANGLE] #
4307 # #
4308 # <node>::= NODE [ELFLAGS] [PLEX] LAYER NODETYPE XY #
4309 # #
4310 # <box>::= BOX [ELFLAGS] [PLEX] LAYER BOXTYPE XY #
4311 # #
4312 # <property>::= PROPATTR PROPVALUE #
4313 ################################################################################
4314
4315
4316=head1 GDS2 Stream Record Datatypes
4317
4318 ################################################################################
4319 NO_DATA = 0;
4320 BIT_ARRAY = 1;
4321 INTEGER_2 = 2;
4322 INTEGER_4 = 3;
4323 REAL_4 = 4; ## NOT supported, never really used
4324 REAL_8 = 5;
4325 ACSII_STRING = 6;
4326 ################################################################################
4327
4328
4329=head1 GDS2 Stream Record Types
4330
4331 ################################################################################
4332 HEADER = 0; ## 2-byte Signed Integer
4333 BGNLIB = 1; ## 2-byte Signed Integer
4334 LIBNAME = 2; ## ASCII String
4335 UNITS = 3; ## 8-byte Real
4336 ENDLIB = 4; ## no data present
4337 BGNSTR = 5; ## 2-byte Signed Integer
4338 STRNAME = 6; ## ASCII String
4339 ENDSTR = 7; ## no data present
4340 BOUNDARY = 8; ## no data present
4341 PATH = 9; ## no data present
4342 SREF = 10; ## no data present
4343 AREF = 11; ## no data present
4344 TEXT = 12; ## no data present
4345 LAYER = 13; ## 2-byte Signed Integer
4346 DATATYPE = 14; ## 2-byte Signed Integer
4347 WIDTH = 15; ## 4-byte Signed Integer
4348 XY = 16; ## 4-byte Signed Integer
4349 ENDEL = 17; ## no data present
4350 SNAME = 18; ## ASCII String
4351 COLROW = 19; ## 2 2-byte Signed Integer <= 32767
4352 TEXTNODE = 20; ## no data present
4353 NODE = 21; ## no data present
4354 TEXTTYPE = 22; ## 2-byte Signed Integer
4355 PRESENTATION = 23; ## Bit Array. One word (2 bytes) of bit flags. Bits 11 and
4356 ## 12 together specify the font 00->font 0 11->font 3.
4357 ## Bits 13 and 14 specify the vertical presentation, 15
4358 ## and 16 the horizontal presentation. 00->'top/left' 01->
4359 ## middle/center 10->bottom/right bits 1-10 were reserved
4360 ## for future use and should be 0.
4361 SPACING = 24; ## discontinued
4362 STRING = 25; ## ASCII String <= 512 characters
4363 STRANS = 26; ## Bit Array: 2 bytes of bit flags for graphic presentation
4364 ## The 1st (high order or leftmost) bit specifies
4365 ## reflection. If set then reflection across the X-axis
4366 ## is applied before rotation. The 14th bit flags
4367 ## absolute mag, the 15th absolute angle, the other bits
4368 ## were reserved for future use and should be 0.
4369 MAG = 27; ## 8-byte Real
4370 ANGLE = 28; ## 8-byte Real
4371 UINTEGER = 29; ## UNKNOWN User int, used only in Calma V2.0
4372 USTRING = 30; ## UNKNOWN User string, used only in Calma V2.0
4373 REFLIBS = 31; ## ASCII String
4374 FONTS = 32; ## ASCII String
4375 PATHTYPE = 33; ## 2-byte Signed Integer
4376 GENERATIONS = 34; ## 2-byte Signed Integer
4377 ATTRTABLE = 35; ## ASCII String
4378 STYPTABLE = 36; ## ASCII String "Unreleased feature"
4379 STRTYPE = 37; ## 2-byte Signed Integer "Unreleased feature"
4380 EFLAGS = 38; ## BIT_ARRAY Flags for template and exterior data.
4381 ## bits 15 to 0, l to r 0=template, 1=external data, others unused
4382 ELKEY = 39; ## INTEGER_4 "Unreleased feature"
4383 LINKTYPE = 40; ## UNKNOWN "Unreleased feature"
4384 LINKKEYS = 41; ## UNKNOWN "Unreleased feature"
4385 NODETYPE = 42; ## INTEGER_2 Nodetype specification. On Calma this could be 0 to 63,
4386 ## GDSII allows 0 to 255. Of course a 16 bit integer allows up to 65535...
4387 PROPATTR = 43; ## INTEGER_2 Property number.
4388 PROPVALUE = 44; ## STRING Property value. On GDSII, 128 characters max, unless an
4389 ## SREF, AREF, or NODE, which may have 512 characters.
4390 BOX = 45; ## NO_DATA The beginning of a BOX element.
4391 BOXTYPE = 46; ## INTEGER_2 Boxtype specification.
4392 PLEX = 47; ## INTEGER_4 Plex number and plexhead flag. The least significant bit of
4393 ## the most significant byte is the plexhead flag.
4394 BGNEXTN = 48; ## INTEGER_4 Path extension beginning for pathtype 4 in Calma CustomPlus.
4395 ## In database units, may be negative.
4396 ENDEXTN = 49; ## INTEGER_4 Path extension end for pathtype 4 in Calma CustomPlus. In
4397 ## database units, may be negative.
4398 TAPENUM = 50; ## INTEGER_2 Tape number for multi-reel stream file.
4399 TAPECODE = 51; ## INTEGER_2 Tape code to verify that the reel is from the proper set.
4400 ## 12 bytes that are supposed to form a unique tape code.
4401 STRCLASS = 52; ## BIT_ARRAY Calma use only.
4402 RESERVED = 53; ## INTEGER_4 Used to be NUMTYPES per Calma GDSII Stream Format Manual, v6.0.
4403 FORMAT = 54; ## INTEGER_2 Archive or Filtered flag. 0: Archive 1: filtered
4404 MASK = 55; ## STRING Only in filtered streams. Layers and datatypes used for mask
4405 ## in a filtered stream file. A string giving ranges of layers and datatypes
4406 ## separated by a semicolon. There may be more than one mask in a stream file.
4407 ENDMASKS = 56; ## NO_DATA The end of mask descriptions.
4408 LIBDIRSIZE = 57; ## INTEGER_2 Number of pages in library director, a GDSII thing, it seems
4409 ## to have only been used when Calma INFORM was creating a new library.
4410 SRFNAME = 58; ## STRING Calma "Sticks"(c) rule file name.
4411 LIBSECUR = 59; ## INTEGER_2 Access control list stuff for CalmaDOS, ancient. INFORM used
4412 ## this when creating a new library. Had 1 to 32 entries with group
4413 ## numbers, user numbers and access rights.
4414
4415
4416=cut
4417