$GDS2::VERSION
= '1.3.1';
## Note: '@ ( # )' used by the what command E.g. what GDS2.pm
$GDS2::revision
= '@(#) $RCSfile: GDS2.pm,v $ $Revision: 1.64 $ $Date: 2003-03-21 15:32:23-06 $';
#use Attribute::Profiled;
use constant TIMER_ON
=> FALSE
;
$G_timer = new Benchmark
::Timer
;
my $haveFlock=TRUE
; ## some systems still may not have this...manually change
use Fcntl q
(:flock); # import LOCK_* constants
my $isLittleEndian = 0; #default
$isLittleEndian = 1 if ($Config{'byteorder'} =~ m
|^1|); ## mswin32 cygwin vms
# POD documentation is sprinkled throughout the file in an
# attempt at Literate Programming style (which Perl partly supports ...
# see http://www.literateprogramming.com/ )
# Search for the strings '=head' or run perldoc on this file.
# You can run this file through either pod2man or pod2html to produce
# documentation in manual or html file format
# Author: Ken Schumack (c) 1999,2000,2001,2002,2003. All rights reserved.
# source code may be used and modified freely, but this copyright notice
# must remain attached to the file. You may modify this module as you
# wish, but if you create a modified version, please attach a note
# listing the modifications you have made, and send a copy to me at
# Contributor Modification: Peter Baumbach 2002-01-11
# returnRecordAsPerl() was created to facilitate the creation of
# parameterized gds2 data with perl.
################################################################################
## GDS2 STREAM RECORD DATATYPES
use constant NO_DATA
=> 0;
use constant BIT_ARRAY
=> 1;
use constant INTEGER_2
=> 2;
use constant INTEGER_4
=> 3;
use constant REAL_4
=> 4; ## NOT supported
use constant REAL_8
=> 5;
use constant ACSII_STRING
=> 6;
################################################################################
################################################################################
## GDS2 STREAM RECORD TYPES
use constant HEADER
=> 0; ## 2-byte Signed Integer
use constant BGNLIB
=> 1; ## 2-byte Signed Integer
use constant LIBNAME
=> 2; ## ASCII String
use constant UNITS
=> 3; ## 8-byte Real
use constant ENDLIB
=> 4; ## no data present
use constant BGNSTR
=> 5; ## 2-byte Signed Integer
use constant STRNAME
=> 6; ## ASCII String
use constant ENDSTR
=> 7; ## no data present
use constant BOUNDARY
=> 8; ## no data present
use constant PATH
=> 9; ## no data present
use constant SREF
=> 10; ## no data present
use constant AREF
=> 11; ## no data present
use constant TEXT
=> 12; ## no data present
use constant LAYER
=> 13; ## 2-byte Signed Integer
use constant DATATYPE
=> 14; ## 2-byte Signed Integer
use constant WIDTH
=> 15; ## 4-byte Signed Integer
use constant XY
=> 16; ## 2-byte Signed Integer
use constant ENDEL
=> 17; ## no data present
use constant SNAME
=> 18; ## ASCII String
use constant COLROW
=> 19; ## 2 2-byte Signed Integer
use constant TEXTNODE
=> 20; ## no data present
use constant NODE
=> 21; ## no data present
use constant TEXTTYPE
=> 22; ## 2-byte Signed Integer
use constant PRESENTATION
=> 23; ## Bit Array
use constant SPACING
=> 24; ## discontinued
use constant STRING
=> 25; ## ASCII String
use constant STRANS
=> 26; ## Bit Array
use constant MAG
=> 27; ## 8-byte Real
use constant ANGLE
=> 28; ## 8-byte Real
use constant UINTEGER
=> 29; ## UNKNOWN User int, used only in Calma V2.0
use constant USTRING
=> 30; ## UNKNOWN User string, used only in Calma V2.0
use constant REFLIBS
=> 31; ## ASCII String
use constant FONTS
=> 32; ## ASCII String
use constant PATHTYPE
=> 33; ## 2-byte Signed Integer
use constant GENERATIONS
=> 34; ## 2-byte Signed Integer
use constant ATTRTABLE
=> 35; ## ASCII String
use constant STYPTABLE
=> 36; ## ASCII String "Unreleased feature"
use constant STRTYPE
=> 37; ## 2-byte Signed Integer "Unreleased feature"
use constant EFLAGS
=> 38; ## BIT_ARRAY Flags for template and exterior data. bits 15 to 0, l to r 0=template,
## 1=external data, others unused
use constant ELKEY
=> 39; ## INTEGER_4 "Unreleased feature"
use constant LINKTYPE
=> 40; ## UNKNOWN "Unreleased feature"
use constant LINKKEYS
=> 41; ## UNKNOWN "Unreleased feature"
use constant NODETYPE
=> 42; ## INTEGER_2 Nodetype specification. On Calma this could be 0 to 63, GDSII allows 0 to 255.
## Of course a 2 byte integer allows up to 65535...
use constant PROPATTR
=> 43; ## INTEGER_2 Property number.
use constant PROPVALUE
=> 44; ## STRING Property value. On GDSII, 128 characters max, unless an SREF, AREF, or NODE,
## which may have 512 characters.
use constant BOX
=> 45; ## NO_DATA The beginning of a BOX element.
use constant BOXTYPE
=> 46; ## INTEGER_2 Boxtype specification.
use constant PLEX
=> 47; ## INTEGER_4 Plex number and plexhead flag. The least significant bit of the most significant
## byte is the plexhead flag.
use constant BGNEXTN
=> 48; ## INTEGER_4 Path extension beginning for pathtype 4 in Calma CustomPlus. In database units,
use constant ENDEXTN
=> 49; ## INTEGER_4 Path extension end for pathtype 4 in Calma CustomPlus. In database units, may be negative.
use constant TAPENUM
=> 50; ## INTEGER_2 Tape number for multi-reel stream file.
use constant TAPECODE
=> 51; ## INTEGER_2 Tape code to verify that the reel is from the proper set. 12 bytes that are
## supposed to form a unique tape code.
use constant STRCLASS
=> 52; ## BIT_ARRAY Calma use only.
use constant RESERVED
=> 53; ## INTEGER_4 Used to be NUMTYPES per Calma GDSII Stream Format Manual, v6.0.
use constant FORMAT
=> 54; ## INTEGER_2 Archive or Filtered flag. 0: Archive 1: filtered
use constant MASK
=> 55; ## STRING Only in filtered streams. Layers and datatypes used for mask in a filtered
## stream file. A string giving ranges of layers and datatypes separated by a semicolon.
## There may be more than one mask in a stream file.
use constant ENDMASKS
=> 56; ## NO_DATA The end of mask descriptions.
use constant LIBDIRSIZE
=> 57; ## INTEGER_2 Number of pages in library director, a GDSII thing, it seems to have only been
## used when Calma INFORM was creating a new library.
use constant SRFNAME
=> 58; ## STRING Calma "Sticks"(c) rule file name.
use constant LIBSECUR
=> 59; ## INTEGER_2 Access control list stuff for CalmaDOS, ancient. INFORM used this when creating
## a new library. Had 1 to 32 entries with group numbers, user numbers and access rights.
#################################################################################################
my @RecordTypeStrings=( ## for ascii print of GDS
###################################################
'LIBNAME' => ACSII_STRING
,
'STRNAME' => ACSII_STRING
,
'PRESENTATION' => BIT_ARRAY
,
'SPACING' => -1, #INTEGER_4, discontinued
'STRING' => ACSII_STRING
,
'UINTEGER' => -1, #INTEGER_4, no longer used
'USTRING' => -1, #ACSII_STRING, no longer used
'REFLIBS' => ACSII_STRING
,
'GENERATIONS' => INTEGER_2
,
'ATTRTABLE' => ACSII_STRING
,
'STYPTABLE' => ACSII_STRING
, # unreleased feature
'STRTYPE' => INTEGER_2
, #INTEGER_2, unreleased feature
'ELKEY' => INTEGER_4
, #INTEGER_4, unreleased feature
'LINKTYPE' => INTEGER_2
, #unreleased feature
'LINKKEYS' => INTEGER_4
, #unreleased feature
'PROPVALUE' => ACSII_STRING
,
'LIBDIRSIZE' => -1, #INTEGER_2
'SRFNAME' => ACSII_STRING
,
'LIBSECUR' => -1, #INTEGER_2,
# This is the default class for the GDS2 object to use when all else fails.
$GDS2::DefaultClass
= 'GDS2' unless defined $GDS2::DefaultClass
;
my $G_epsilon=0.00001; ## to take care of floating point representation problems
GDS2 - GDS2 stream module
This is GDS2, a module for quickly creating programs to read and/or write GDS2 files.
Send feedback/suggestions to
################################################################################
=head2 new - open gds2 file
my $gds2File = new GDS2(-fileName => "filename.gds2"); ## to read
my $gds2File2 = new GDS2(-fileName => ">filename.gds2"); ## to write
bless $self,$class || ref $class || $GDS2::DefaultClass
;
my $fileName = $arg{'-fileName'};
die "new expects a gds2 file name. Missing -fileName => 'name' $!";
my $resolution = $arg{'-resolution'};
if (! defined $resolution)
die "new expects a positive integer resolution. ($resolution) $!" if (($resolution <= 0) || ($resolution !~ m
|^\d
+$|));
my $lockMode = LOCK_SH
; ## default
my $openModStr = substr($fileName,0,2); ### looking for > or >>
$openModStr =~ s
|[^\
+>]+||g
;
my $openModeNum = O_RDONLY
;
if ($openModStr =~ m
|^\
+|)
warn("Ignoring '+' in open mode"); ## not handling this yet...
$openModeNum = O_WRONLY
|O_CREAT
;
$fileName =~ s
|^$openModStr||;
elsif ($openModStr eq '>>')
$openModeNum = O_WRONLY
|O_APPEND
;
$fileName =~ s
|^$openModStr||;
my $fileHandle = new IO
::File
;
$fileHandle -> open("$fileName",$openModeNum) or die "Unable to open $fileName because $!";
flock($fileHandle,$lockMode) or die "File lock on $fileName failed because $!";
binmode $fileHandle,':raw'; ## may need Perl 5.6 for discipline
$self -> {'FileHandle'} = $fileHandle;
$self -> {'FileName'} = $fileName; ## the gds2 filename
$self -> {'GDSLENGTH'} = 0; ## total file size so far
$self -> {'EOLIB'} = 0; ## end of library flag
$self -> {'HEADER'} = -1; ## in header flag
$self -> {'INDATA'} = 0; ## in data flag
$self -> {'Length'} = 0; ## length of data
$self -> {'DataType'} = -1; ## one of 7 gds datatypes
$self -> {'UUnits'} = 0; ## for gds2 file
$self -> {'DBUnits'} = 0; ## for gds2 file
$self -> {'Record'} = ''; ## the whole record as found in gds2 file
$self -> {'RecordType'} = -1;
$self -> {'DataIndex'} = 0;
$self -> {'RecordData'} = ('');
$self -> {'CurrentDataList'} = '';
$self -> {'InStr'} = 0; ##flag for write error checking
$self -> {'InElm'} = 0; ##flag for write error checking
$self -> {'Resolution'} = $resolution;
$self -> {'UsingPrettyPrint'} = 0; ## print as string ...
################################################################################
=head2 close - close gds2 file
$gds2File -> close(-markEnd=>1); ## experimental -- some systems have trouble closing files
$gds2File -> close(-pad=>2048); ## experimental -- pad end with \0's till file size is a
## multiple of number. Note: old reel to reel tapes on Calma
## systems used 2048 byte blocks
my $markEnd = $arg{'-markEnd'};
if ((defined $markEnd)&&($markEnd))
my $fh = $self -> {'FileHandle'};
print $fh "\x1a\x04"; # a ^Z and a ^D
$self -> {'GDSLENGTH'} += 2;
if ((defined $pad)&&($pad > 0))
my $fh = $self -> {'FileHandle'};
my $fileSize = $self -> tellSize
;
my $padSize = $pad - ($fileSize % $pad);
$padSize=0 if ($padSize == $pad);
for (my $i=0; $i < $padSize; $i++)
print $fh "\0"; ## a null
$self -> {'FileHandle'} -> close;
################################################################################
################################################################################
=head1 High Level Write Methods
################################################################################
=head2 printInitLib() - Does all the things needed to start a library
$gds2File -> printInitLib(-name => "testlib", ##writes HEADER,BGNLIB,LIBNAME,and UNITS records
-isoDate => 0|1 ## (optional) use ISO 4 digit date 2001 vs 101
## defaults to current date for library date and 1e-3 and 1e-9 for units
remember to close library with printEndlib()
sub printInitLib
#: Profiled
my $libName = $arg{'-name'};
die "printInitLib expects a library name. Missing -name => 'name' $!";
my $isoDate = $arg{'-isoDate'};
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900 if ($isoDate); ## Cadence likes year left "as is". GDS format supports year number up to 65535 -- 101 vs 2001
$self -> printGds2Record
(-type
=> 'HEADER',-data
=> 3); ## GDS2 HEADER
$self -> printGds2Record
(-type
=> 'BGNLIB',-data
=> [$year,$mon,$mday,$hour,$min,$sec,$year,$mon,$mday,$hour,$min,$sec]);
$self -> printGds2Record
(-type
=> 'LIBNAME',-data
=> $libName);
$self -> printGds2Record
(-type
=> 'UNITS',-data
=> [0.001,1e-9]);
################################################################################
=head2 printBgnstr - Does all the things needed to start a structure definition
$gds2File -> printBgnstr(-name => "nand3" ## writes BGNSTR and STRNAME records
-isoDate => 1|0 ## (optional) use ISO 4 digit date 2001 vs 101
remember to close with printEndstr()
sub printBgnstr
#: Profiled
my $strName = $arg{'-name'};
die "bgnStr expects a structure name. Missing -name => 'name' $!";
my $createTime = $arg{'-createTime'};
my $isoDate = $arg{'-isoDate'};
my ($csec,$cmin,$chour,$cmday,$cmon,$cyear,$cwday,$cyday,$cisdst);
($csec,$cmin,$chour,$cmday,$cmon,$cyear,$cwday,$cyday,$cisdst) = localtime($createTime);
($csec,$cmin,$chour,$cmday,$cmon,$cyear,$cwday,$cyday,$cisdst) = localtime(time);
my $modTime = $arg{'-modTime'};
my ($msec,$mmin,$mhour,$mmday,$mmon,$myear,$mwday,$myday,$misdst);
($msec,$mmin,$mhour,$mmday,$mmon,$myear,$mwday,$myday,$misdst) = localtime($modTime);
($msec,$mmin,$mhour,$mmday,$mmon,$myear,$mwday,$myday,$misdst) = localtime(time);
$cyear += 1900; ## 2001 vs 101
$self -> printGds2Record
(-type
=> 'BGNSTR',-data
=> [$cyear,$cmon,$cmday,$chour,$cmin,$csec,$myear,$mmon,$mmday,$mhour,$mmin,$msec]);
$self -> printGds2Record
(-type
=> 'STRNAME',-data
=> $strName);
################################################################################
=head2 printPath - prints a gds2 path
-unitWidth=>#, ## (optional) directly specify width in data base units (vs -width which is multipled by resolution)
-xy=>\@array, ## array of reals
-xyInt=>\@array, ## array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
layer defaults to 0 if -layer not used
pathType defaults to 0 if -pathType not used
2 = square - extended 1/2 width
4 = custom plus variable path extension...
width defaults to 0.0 if -width not used
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# <path>::= PATH [ELFLAGS] [PLEX] LAYER DATATYPE [PATHTYPE] [WIDTH] XY
sub printPath
#: Profiled
my $resolution = $self -> {'Resolution'};
my $layer = $arg{'-layer'};
my $dataType = $arg{'-dataType'};
my $pathType = $arg{'-pathType'};
my $bgnExtn = $arg{'-bgnExtn'};
my $endExtn = $arg{'-endExtn'};
my $unitWidth = $arg{'-unitWidth'};
my $widthReal = $arg{'-width'};
if ((defined $unitWidth)&&($unitWidth >= 0))
if ((defined $widthReal)&&($widthReal >= 0.0))
$width = int(($widthReal*$resolution)+$G_epsilon);
#### -xyInt most useful if reading and modifying... -xy if creating from scratch
my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
my @xyTmp=(); ##don't pollute array passed in
if (! ((defined $xy) || (defined $xyInt)))
die "printPath expects an xy array reference. Missing -xy => \\\@array $!";
$self -> printGds2Record
(-type
=> 'PATH');
$self -> printGds2Record
(-type
=> 'LAYER',-data
=> $layer);
$self -> printGds2Record
(-type
=> 'DATATYPE',-data
=> $dataType);
$self -> printGds2Record
(-type
=> 'PATHTYPE',-data
=> $pathType) if ($pathType);
$self -> printGds2Record
(-type
=> 'WIDTH',-data
=> $width) if ($width);
$self -> printGds2Record
(-type
=> 'BGNEXTN',-data
=> $bgnExtn); ## int used with resolution
$self -> printGds2Record
(-type
=> 'ENDEXTN',-data
=> $endExtn); ## int used with resolution
for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
if ($xy -> [$i] >= 0) { push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
else { push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
if ($bgnExtn || $endExtn) ## we have to convert
my $endX1 = $xyTmp[$#xyTmp - 1];
my $endY1 = $xyTmp[$#xyTmp];
my $endX2 = $xyTmp[$#xyTmp - 3];
my $endY2 = $xyTmp[$#xyTmp - 2];
if ($bgnX1 == $bgnX2) #vertical ...modify 1st Y
if ($bgnY1 < $bgnY2) ## points down
$xyTmp[1] += int($width/2) if ($pathType != 0);
$xyTmp[1] -= int($width/2) if ($pathType != 0);
elsif ($bgnY1 == $bgnY2) #horizontal ...modify 1st X
if ($bgnX1 < $bgnX2) ## points left
$xyTmp[0] += int($width/2) if ($pathType != 0);
$xyTmp[0] -= int($width/2) if ($pathType != 0);
if ($endX1 == $endX2) #vertical ...modify last Y
if ($endY1 < $endY2) ## points down
$xyTmp[$#xyTmp] -= $endExtn;
$xyTmp[$#xyTmp] += int($width/2) if ($pathType != 0);
$xyTmp[$#xyTmp] += $endExtn;
$xyTmp[$#xyTmp] -= int($width/2) if ($pathType != 0);
elsif ($endY1 == $endY2) #horizontal ...modify last X
if ($endX1 < $endX2) ## points left
$xyTmp[$#xyTmp - 1] -= $endExtn;
$xyTmp[$#xyTmp - 1] += int($width/2) if ($pathType != 0);
$xyTmp[$#xyTmp - 1] += $endExtn;
$xyTmp[$#xyTmp - 1] -= int($width/2) if ($pathType != 0);
$self -> printGds2Record
(-type
=> 'XY',-data
=> \
@xyTmp);
$self -> printGds2Record
(-type
=> 'ENDEL');
################################################################################
=head2 printBoundary - prints a gds2 boundary
$gds2File -> printBoundary(
-xy=>\@array, ## array of reals
-xyInt=>\@array, ## array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
layer defaults to 0 if -layer not used
dataType defaults to 0 if -dataType not used
# <boundary>::= BOUNDARY [ELFLAGS] [PLEX] LAYER DATATYPE XY
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
sub printBoundary
#: Profiled
my $resolution = $self -> {'Resolution'};
my $layer = $arg{'-layer'};
my $dataType = $arg{'-dataType'};
#### -xyInt most useful if reading and modifying... -xy if creating from scratch
my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
my @xyTmp=(); ##don't pollute array passed in
if (! ((defined $xy) || (defined $xyInt)))
die "printBoundary expects an xy array reference. Missing -xy => \\\@array $!";
$self -> printGds2Record
(-type
=> 'BOUNDARY');
$self -> printGds2Record
(-type
=> 'LAYER',-data
=> $layer);
$self -> printGds2Record
(-type
=> 'DATATYPE',-data
=> $dataType);
if (my $numPoints=$#$xy+1 < 6)
die "printBoundary expects an xy array of at leasts 3 coordinates $!";
for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
if ($xy -> [$i] >= 0) {push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
else {push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
## gds expects square to have 5 coords (closure)
if (($xy -> [0] != ($xy -> [($#$xy - 1)])) && ($xy -> [1] != ($xy -> [$#$xy])))
if ($xy -> [0] >= 0) {push @xyTmp,int((($xy -> [0])*$resolution)+$G_epsilon);}
else {push @xyTmp,int((($xy -> [0])*$resolution)-$G_epsilon);}
if ($xy -> [1] >= 0) {push @xyTmp,int((($xy -> [1])*$resolution)+$G_epsilon);}
else {push @xyTmp,int((($xy -> [1])*$resolution)-$G_epsilon);}
$self -> printGds2Record
(-type
=> 'XY',-data
=> \
@xyTmp);
$self -> printGds2Record
(-type
=> 'ENDEL');
################################################################################
=head2 printSref - prints a gds2 Structure REFerence
-name=>string, ## Name of structure
-xy=>\@array, ## array of reals
-xyInt=>\@array, ## array of internal ints (optional -wks better than -xy if you are modifying an existing GDS2 file)
-angle=>#.#, ## (optional) Default is 0.0
-mag=>#.#, ## (optional) Default is 1.0
-reflect=>0|1 ## (optional)
best not to specify angle or mag if not needed
#<SREF>::= SREF [ELFLAGS] [PLEX] SNAME [<strans>] XY
# <strans>::= STRANS [MAG] [ANGLE]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
sub printSref
#: Profiled
my $resolution = $self -> {'Resolution'};
my $sname = $arg{'-name'};
die "printSref expects a name string. Missing -name => 'text' $!";
#### -xyInt most useful if reading and modifying... -xy if creating from scratch
my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
if (! ((defined $xy) || (defined $xyInt)))
die "printSref expects an xy array reference. Missing -xy => \\\@array $!";
$self -> printGds2Record
(-type
=> 'SREF');
$self -> printGds2Record
(-type
=> 'SNAME',-data
=> $sname);
my $reflect = $arg{'-reflect'};
if ((! defined $reflect)||($reflect <= 0))
if ((! defined $mag)||($mag <= 0))
my $angle = $arg{'-angle'};
my $data=$reflect.'000000000000000'; ## 16 'bit' string
$self -> printGds2Record
(-type
=> 'STRANS',-data
=> $data);
$self -> printGds2Record
(-type
=> 'MAG',-data
=> $mag) if ($mag);
$self -> printGds2Record
(-type
=> 'ANGLE',-data
=> $angle) if ($angle);
my @xyTmp=(); ##don't pollute array passed in
for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
if ($xy -> [$i] >= 0) {push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
else {push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
$self -> printGds2Record
(-type
=> 'XY',-data
=> \
@xyTmp);
$self -> printGds2Record
(-type
=> 'ENDEL');
################################################################################
=head2 printAref - prints a gds2 Array REFerence
-name=>string, ## Name of structure
-columns=>#, ## Default is 1
-rows=>#, ## Default is 1
-xy=>\@array, ## array of reals
-xyInt=>\@array, ## array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
-angle=>#.#, ## (optional) Default is 0.0
-mag=>#.#, ## (optional) Default is 1.0
-reflect=>0|1 ## (optional)
best not to specify angle or mag if not needed
#<AREF>::= AREF [ELFLAGS] [PLEX] SNAME [<strans>] COLROW XY
# <strans>::= STRANS [MAG] [ANGLE]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
sub printAref
#: Profiled
my $resolution = $self -> {'Resolution'};
my $sname = $arg{'-name'};
die "printAref expects a sname string. Missing -name => 'text' $!";
#### -xyInt most useful if reading and modifying... -xy if creating from scratch
my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
if (! ((defined $xy) || (defined $xyInt)))
die "printAref expects an xy array reference. Missing -xy => \\\@array $!";
$self -> printGds2Record
(-type
=> 'AREF');
$self -> printGds2Record
(-type
=> 'SNAME',-data
=> $sname);
my $reflect = $arg{'-reflect'};
if ((! defined $reflect)||($reflect <= 0))
if ((! defined $mag)||($mag <= 0))
my $angle = $arg{'-angle'};
my $data=$reflect.'000000000000000'; ## 16 'bit' string
$self -> printGds2Record
(-type
=> 'STRANS',-data
=> $data);
$self -> printGds2Record
(-type
=> 'MAG',-data
=> $mag) if ($mag);
$self -> printGds2Record
(-type
=> 'ANGLE',-data
=> $angle) if ($angle);
my $columns = $arg{'-columns'};
if ((! defined $columns)||($columns <= 0))
my $rows = $arg{'-rows'};
if ((! defined $rows)||($rows <= 0))
$self -> printGds2Record
(-type
=> 'COLROW',-data
=> [$columns,$rows]);
my @xyTmp=(); ##don't pollute array passed in
for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
if ($xy -> [$i] >= 0) {push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
else {push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
$self -> printGds2Record
(-type
=> 'XY',-data
=> \
@xyTmp);
$self -> printGds2Record
(-type
=> 'ENDEL');
################################################################################
=head2 printText - prints a gds2 Text
-layer=>#, ## Default is 0
-textType=>#, ## Default is 0
-top, or -middle, -bottom, ##optional vertical presentation
-left, or -center, or -right, ##optional horizontal presentation
-xy=>\@array, ## array of reals
-xyInt=>\@array, ## array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
-x=>#.#, ## optional way of passing in x value
-y=>#.#, ## optional way of passing in y value
-angle=>#.#, ## (optional) Default is 0.0
-mag=>#.#, ## (optional) Default is 1.0
-reflect=>#, ## (optional) Default is 0
best not to specify reflect, angle or mag if not needed
#<text>::= TEXT [ELFLAGS] [PLEX] LAYER <textbody>
# <textbody>::= TEXTTYPE [PRESENTATION] [PATHTYPE] [WIDTH] [<strans>] XY STRING
# <strans>::= STRANS [MAG] [ANGLE]
################################################################################
sub printText
#: Profiled
my $string = $arg{'-string'};
die "printText expects a string. Missing -string => 'text' $!";
my $resolution = $self -> {'Resolution'};
#### -xyInt most useful if reading and modifying... -xy if creating from scratch
my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
die "printText expects a x coord. Missing -xy=>\@array or -x => 'num' $!";
if ($x>=0) {$x = int(($x*$resolution)+$G_epsilon);}
else {$x = int(($x*$resolution)-$G_epsilon);}
die "printText expects a y coord. Missing -xy=>\@array or -y => 'num' $!";
if ($y>=0) {$y = int(($y*$resolution)+$G_epsilon);}
else {$y = int(($y*$resolution)-$G_epsilon);}
my $layer = $arg{'-layer'};
my $textType = $arg{'-textType'};
my $reflect = $arg{'-reflect'};
if ((! defined $reflect)||($reflect <= 0))
my $font = $arg{'-font'};
if ((! defined $font) || ($font < 0) || ($font > 3))
$font = sprintf("%02d",$font);
my $middle = $arg{'-middle'};
my $bottom = $arg{'-bottom'};
if (defined $top) {$vertical = '00';}
elsif (defined $bottom) {$vertical = '10';}
else {$vertical = '01';} ## middle
my $left = $arg{'-left'};
my $center = $arg{'-center'};
my $right = $arg{'-right'};
if (defined $left) {$horizontal = '00';}
elsif (defined $right) {$horizontal = '10';}
else {$horizontal = '01';} ## center
my $presString = "0000000000$font$vertical$horizontal";
if ((! defined $mag)||($mag <= 0))
my $angle = $arg{'-angle'};
$self -> printGds2Record
(-type
=>'TEXT');
$self -> printGds2Record
(-type
=>'LAYER',-data
=>$layer);
$self -> printGds2Record
(-type
=>'TEXTTYPE',-data
=>$textType);
$self -> printGds2Record
(-type
=> 'PRESENTATION',-data
=> $presString) if (defined $font || defined $top || defined $middle || defined $bottom || defined $bottom || defined $left || defined $center || defined $right);
my $data=$reflect.'000000000000000'; ## 16 'bit' string
$self -> printGds2Record
(-type
=>'STRANS',-data
=>$data);
$self -> printGds2Record
(-type
=>'MAG',-data
=>$mag) if ($mag);
$self -> printGds2Record
(-type
=>'ANGLE',-data
=>$angle) if ($angle);
$self -> printGds2Record
(-type
=>'XY',-data
=>[$x,$y]);
$self -> printGds2Record
(-type
=>'STRING',-data
=>$string);
$self -> printGds2Record
(-type
=>'ENDEL');
################################################################################
=head1 Low Level Generic Write Methods
################################################################################
=head2 saveGds2Record() - low level method to create a gds2 record given record type
and data (if required). Data of more than one item should be given as a list.
NOTE: THIS ONLY USES GDS2 OBJECT TO GET RESOLUTION
-data=>data_If_Needed, ##optional for some types
-scale=>#.#, ##optional number to scale data to. I.E -scale=>0.5 #default is NOT to scale
-snap=>#.#, ##optional number to snap data to I.E. -snap=>0.005 #default is 1 resolution unit, typically 0.001
my $gds2File = new GDS2(-fileName => ">$fileName");
my $record = $gds2File -> saveGds2Record(-type=>'header',-data=>3);
$gds2FileOut -> printGds2Record(-type=>'record',-data=>$record);
sub saveGds2Record
#: Profiled
my $type = $arg{'-type'};
die "saveGds2Record expects a type name. Missing -type => 'name' $!";
my @data = $arg{'-data'};
my $dataString = $arg{'-asciiData'};
die "saveGds2Record can not handle both -data and -asciiData options $!" if ((defined $dataString)&&((defined $data[0])&&($data[0] ne '')));
if ($type eq 'RECORD') ## special case...
my $resolution = $self -> {'Resolution'};
my $scale = $arg{'-scale'};
die "saveGds2Record expects a positive scale -scale => $scale $!";
my $snap = $arg{'-snap'};
if (! defined $snap) ## default is one resolution unit
$snap = $snap*$resolution; ## i.e. 0.001 -> 1
die "saveGds2Record expects a snap >= 1/resolution -snap => $snap $!";
if ((defined $data[0])&&($data[0] ne ''))
$numDataElements = @
$data;
if ($numDataElements) ## passed in anonymous array
$numDataElements = @data;
my $recordDataType = $RecordTypeData{$type};
$dataString=~s
|^\s
+||; ## clean-up
$dataString=~s
|\s
+| |g
if ($dataString !~ m
|'|); ## don't compress spaces
in strings
...
$dataString=~s
|'$||; #'for strings
$dataString=~s
|^'||; #'for strings
if (($recordDataType == BIT_ARRAY
)||($recordDataType == ACSII_STRING
))
$dataString=~s
|\s
*[\s
,;:/\\]+\s
*| |g
; ## incase commas etc... (non-std) were added by hand
@data = split(' ',$dataString);
$numDataElements = @data;
if ($recordDataType == INTEGER_4
)
for(my $i=0;$i<$numDataElements;$i++) ## e.g. 3.4 in -> 3400 out
if ($data[$i]>=0) {push @xyTmp,int((($data[$i])*$resolution)+$G_epsilon);}
else {push @xyTmp,int((($data[$i])*$resolution)-$G_epsilon);}
if ($recordDataType == BIT_ARRAY
)
elsif ($recordDataType == INTEGER_2
)
$length = 2 * $numDataElements;
elsif ($recordDataType == INTEGER_4
)
$length = 4 * $numDataElements;
elsif ($recordDataType == REAL_8
)
$length = 8 * $numDataElements;
elsif ($recordDataType == ACSII_STRING
)
$length = $slen + ($slen % 2); ## needs to be an even number
my $recordLength = pack 'S',($length + 4); #1 2 bytes for length 3rd for recordType 4th for dataType
$record .= $recordLength;
my $recordType = pack 'C',$RecordTypeNumbers{$type};
my $dataType = pack 'C',$RecordTypeData{$type};
if ($recordDataType == BIT_ARRAY
) ## bit array
my $bitLength = $length * 8;
$record .= pack("B$bitLength",$data);
elsif ($recordDataType == INTEGER_2
) ## 2 byte signed integer
$record .= pack('s',$num);
elsif ($recordDataType == INTEGER_4
) ## 4 byte signed integer
$num = scaleNum
($num,$scale) if ($scale != 1);
$num = snapNum
($num,$snap) if ($snap != 1);
$record .= pack('i',$num);
elsif ($recordDataType == REAL_8
) ## 8 byte real
if($negative) { $exponent += 192; }
else { $exponent += 64; }
$record .= pack('C',$exponent);
for (my $i=1; $i<=7; $i++)
if ($real>=0) {$byte = int(($real*256.0)+$G_epsilon);}
else {$byte = int(($real*256.0)-$G_epsilon);}
$record .= pack('C',$byte);
$real = $real * 256.0 - ($byte + 0.0);
elsif ($recordDataType == ACSII_STRING
) ## ascii string (null padded)
$record .= pack("a$length",$data);
################################################################################
=head2 printGds2Record() - low level method to print a gds2 record given record type
and data (if required). Data of more than one item should be given as a list.
-data=>data_If_Needed, ##optional for some types
-scale=>#.#, ##optional number to scale data to. I.E -scale=>0.5 #default is NOT to scale
-snap=>#.#, ##optional number to snap data to I.E. -snap=>0.005 #default is 1 resolution unit, typically 0.001
my $gds2File = new GDS2(-fileName => ">$fileName");
$gds2File -> printGds2Record(-type=>'header',-data=>3);
$gds2File -> printGds2Record(-type=>'bgnlib',-data=>[99,12,1,22,33,0,99,12,1,22,33,9]);
$gds2File -> printGds2Record(-type=>'libname',-data=>"testlib");
$gds2File -> printGds2Record(-type=>'units',-data=>[0.001, 1e-9]);
$gds2File -> printGds2Record(-type=>'bgnstr',-data=>[99,12,1,22,33,0,99,12,1,22,33,9]);
$gds2File -> printGds2Record(-type=>'endstr');
$gds2File -> printGds2Record(-type=>'endlib');
Note: the special record type of 'record' can be used to copy a complete record
while (my $record = $gds2FileIn -> readGds2Record())
$gds2FileOut -> printGds2Record(-type=>'record',-data=>$record);
sub printGds2Record
#: Profiled
my $type = $arg{'-type'};
die "printGds2Record expects a type name. Missing -type => 'name' $!";
my $fh = $self -> {'FileHandle'};
my @data = $arg{'-data'};
my $dataString = $arg{'-asciiData'};
die "printGds2Record can not handle both -data and -asciiData options $!" if ((defined $dataString)&&((defined $data[0])&&($data[0] ne '')));
my $recordLength; ## 1st 2 bytes for length 3rd for recordType 4th for dataType
if ($type eq 'RECORD') ## special case...
my $length = substr($data[0],0,2);
$recordLength = unpack 'v',$length;
$self -> {'GDSLENGTH'} += $recordLength;
$length = reverse $length;
my $recordType = substr($data[0],2,1);
$recordType = unpack 'C',$recordType;
$type = $RecordTypeStrings[$recordType]; ## will use code below.....
my $dataType = substr($data[0],3,1);
$dataType = unpack 'C',$dataType;
my $lengthLeft = $recordLength - 4; ## length left
my $recordDataType = $RecordTypeData{$type};
if (($recordDataType == INTEGER_2
) || ($recordDataType == BIT_ARRAY
))
my $binData = unpack 'b*',$data[0];
my $intData = substr($binData,32); #skip 1st 4 bytes (length, recordType dataType)
my ($byteInt2String,$byte2);
for(my $i=0; $i<($lengthLeft/2); $i++)
$byteInt2String = reverse(substr($intData,0,16,''));
$byte2=pack 'B16',reverse($byteInt2String);
elsif ($recordDataType == INTEGER_4
)
my $binData = unpack 'b*',$data[0];
my $intData = substr($binData,32); #skip 1st 4 bytes (length, recordType dataType)
my ($byteInt4String,$byte4);
for(my $i=0; $i<($lengthLeft/4); $i++)
$byteInt4String = reverse(substr($intData,0,32,''));
$byte4=pack 'B32',reverse($byteInt4String);
elsif ($recordDataType == REAL_8
)
my $binData = unpack 'b*',$data[0];
my $realData = substr($binData,32); #skip 1st 4 bytes (length, recordType dataType)
my ($bit64String,$mantissa,$byteString,$byte);
for(my $i=0; $i<($lengthLeft/8); $i++)
$bit64String = substr($realData,($i*64),64);
print($fh pack 'b8',$bit64String);
$mantissa = substr($bit64String,8,56);
$byteString = substr($mantissa,($j*8),8);
$byte=pack 'b8',$byteString;
elsif ($recordDataType == ACSII_STRING
) ## ascii string (null padded)
print($fh pack("a$lengthLeft",substr($data[0],4)));
elsif ($recordDataType == REAL_4
) ## 4 byte real
die "4-byte reals are not supported $!";
$recordLength = length $data[0];
$self -> {'GDSLENGTH'} += $recordLength;
else #if ($type ne 'RECORD')
my $resolution = $self -> {'Resolution'};
my $scale = $arg{'-scale'};
die "printGds2Record expects a positive scale -scale => $scale $!";
my $snap = $arg{'-snap'};
if (! defined $snap) ## default is one resolution unit
$snap = int(($snap*$resolution)+$G_epsilon); ## i.e. 0.001 -> 1
die "printGds2Record expects a snap >= 1/resolution -snap => $snap $!";
if ((defined $data[0])&&($data[0] ne ''))
$numDataElements = @
$data;
if ($numDataElements) ## passed in anonymous array
$numDataElements = @data;
my $recordDataType = $RecordTypeData{$type};
$dataString=~s
|^\s
+||; ## clean-up
$dataString=~s
|\s
+| |g
if ($dataString !~ m
|'|); ## don't compress spaces
in strings
...
$dataString=~s
|'$||; #'# for strings
$dataString=~s
|^'||; #'# for strings
if (($recordDataType == BIT_ARRAY
)||($recordDataType == ACSII_STRING
))
$dataString=~s
|\s
*[\s
,;:/\\]+\s
*| |g
; ## in case commas etc... (non-std) were added by hand
@data = split(' ',$dataString);
$numDataElements = @data;
if ($recordDataType == INTEGER_4
)
for(my $i=0;$i<$numDataElements;$i++) ## e.g. 3.4 in -> 3400 out
if ($data[$i]>=0) {push @xyTmp,int((($data[$i])*$resolution)+$G_epsilon);}
else {push @xyTmp,int((($data[$i])*$resolution)-$G_epsilon);}
if ($recordDataType == BIT_ARRAY
)
elsif ($recordDataType == INTEGER_2
)
$length = 2 * $numDataElements;
elsif ($recordDataType == INTEGER_4
)
$length = 4 * $numDataElements;
elsif ($recordDataType == REAL_8
)
$length = 8 * $numDataElements;
elsif ($recordDataType == ACSII_STRING
)
$length = $slen + ($slen % 2); ## needs to be an even number
$self -> {'GDSLENGTH'} += $length;
$recordLength = pack 'v',($length + 4);
$recordLength = reverse $recordLength;
$recordLength = pack 'S',($length + 4);
print($fh $recordLength);
my $recordType = pack 'C',$RecordTypeNumbers{$type};
$recordType = reverse $recordType if ($isLittleEndian);
my $dataType = pack 'C',$RecordTypeData{$type};
$dataType = reverse $dataType if ($isLittleEndian);
if ($recordDataType == BIT_ARRAY
) ## bit array
my $bitLength = $length * 8;
my $value = pack("B$bitLength",$data);
elsif ($recordDataType == INTEGER_2
) ## 2 byte signed integer
$value = reverse $value if ($isLittleEndian);
elsif ($recordDataType == INTEGER_4
) ## 4 byte signed integer
$num = scaleNum
($num,$scale) if ($scale != 1);
$num = snapNum
($num,$snap) if ($snap != 1);
$value = reverse $value if ($isLittleEndian);
elsif ($recordDataType == REAL_8
) ## 8 byte real
my ($real,$negative,$exponent,$value);
if($negative) { $exponent += 192; }
else { $exponent += 64; }
$value = pack('C',$exponent);
$value = reverse $value if ($isLittleEndian);
for (my $i=1; $i<=7; $i++)
if ($real>=0) {$byte = int(($real*256.0)+$G_epsilon);}
else {$byte = int(($real*256.0)-$G_epsilon);}
my $value = pack('C',$byte);
$value = reverse $value if ($isLittleEndian);
$real = $real * 256.0 - ($byte + 0.0);
elsif ($recordDataType == ACSII_STRING
) ## ascii string (null padded)
print($fh pack("a$length",$data));
################################################################################
=head2 printRecord - prints a record just read
$gds2File -> printRecord(
sub printRecord
#: Profiled
my $record = $arg{'-data'};
die "printGds2Record expects a data record. Missing -data => \$record $!";
my $type = $arg{'-type'};
die "printRecord does not take -type. Perhaps you meant to use printGds2Record? $!";
$self -> printGds2Record
(-type
=>'record',-data
=>$record);
################################################################################
################################################################################
=head1 Low Level Generic Read Methods
################################################################################
=head2 readGds2Record - reads record header and data section
while ($gds2File -> readGds2Record)
if ($gds2File -> returnRecordTypeString eq 'LAYER')
$layersFound[$gds2File -> layer] = 1;
sub readGds2Record
#: Profiled
$self -> readGds2RecordHeader
();
$self -> readGds2RecordData
();
################################################################################
=head2 readGds2RecordHeader - only reads gds2 record header section (2 bytes)
sub readGds2RecordHeader
#: Profiled
$self -> skipGds2RecordData
() if (($self -> {'HEADER'} >= 0) && (! $self -> {'INDATA'})) ; # in HEADER not in data
$self -> {'Record'} = '';
$self -> {'RecordType'} = -1;
return '' if ($self -> {'EOLIB'}); ## no sense reading null padding..
if (read($self -> {'FileHandle'},$data,2)) ### length
$data = reverse $data if ($isLittleEndian);
$self -> {'Record'} = $data;
$self -> {'Length'} = unpack 'S',$data;
$self -> {'GDSLENGTH'} += $self -> {'Length'};
if (read($self -> {'FileHandle'},$data,1)) ## record type
$data = reverse $data if ($isLittleEndian);
$self -> {'Record'} .= $data;
$self -> {'RecordType'} = unpack 'C',$data;
$self -> {'EOLIB'} = 1 if (($self -> {'RecordType'}) == ENDLIB
);
if ($self -> {'UsingPrettyPrint'})
$StrSpace = '' if (($self -> {'RecordType'}) == ENDSTR
);
$StrSpace = ' ' if (($self -> {'RecordType'}) == BGNSTR
);
$ElmSpace = ' ' if ((($self -> {'RecordType'}) == TEXT
) || (($self -> {'RecordType'}) == PATH
) ||
(($self -> {'RecordType'}) == BOUNDARY
) || (($self -> {'RecordType'}) == SREF
) ||
(($self -> {'RecordType'}) == AREF
));
$ElmSpace = '' if (($self -> {'RecordType'}) == ENDEL
);
if (read($self -> {'FileHandle'},$data,1)) ## data type
$data = reverse $data if ($isLittleEndian);
$self -> {'Record'} .= $data;
$self -> {'DataType'} = unpack 'C',$data;
################################################################################
=head2 readGds2RecordData - only reads record data section
slightly faster if you just want a certain thing...
while ($gds2File -> readGds2RecordHeader)
if ($gds2File -> returnRecordTypeString eq 'LAYER')
$gds2File -> readGds2RecordData;
$layersFound[$gds2File -> returnLayer] = 1;
sub readGds2RecordData
#: Profiled
$self -> readGds2RecordHeader
() if ($self -> {'HEADER'} <= 0);
return $self -> {'Record'} if ($self -> {'DataType'} == NO_DATA
); # no sense going on...
$self -> {'HEADER'} = 0; # not in HEADER
$self -> {'INDATA'} = 1; # rather in DATA
my $resolution = $self -> {'Resolution'};
my $bytesLeft = $self -> {'Length'} - 4; ## 4 should have been just read by readGds2RecordHeader
$self -> {'RecordData'} = ('');
$self -> {'CurrentDataList'} = '';
if ($self -> {'DataType'} == BIT_ARRAY
) ## bit array
$self -> {'DataIndex'}=0;
read($self -> {'FileHandle'},$data,$bytesLeft);
$data = reverse $data if ($isLittleEndian);
my $bitsLeft = $bytesLeft * 8;
$self -> {'Record'} .= $data;
$self -> {'RecordData'}[0] = unpack "B$bitsLeft",$data;
$self -> {'CurrentDataList'} = ($self -> {'RecordData'}[0]);
elsif ($self -> {'DataType'} == INTEGER_2
) ## 2 byte signed integer
read($self -> {'FileHandle'},$data,2);
$data = reverse $data if ($isLittleEndian);
$self -> {'Record'} .= $data;
$self -> {'RecordData'}[$i] = unpack 's',$data;
$tmpListString .= $self -> {'RecordData'}[$i];
$self -> {'DataIndex'} = $i - 1;
$self -> {'CurrentDataList'} = $tmpListString;
elsif ($self -> {'DataType'} == INTEGER_4
) ## 4 byte signed integer
read($self -> {'FileHandle'},$data,4);
$data = reverse $data if ($isLittleEndian);
$self -> {'Record'} .= $data;
$self -> {'RecordData'}[$i] = unpack 'i',$data;
$tmpListString .= $self -> {'RecordData'}[$i];
$self -> {'DataIndex'} = $i - 1;
$self -> {'CurrentDataList'} = $tmpListString;
elsif ($self -> {'DataType'} == REAL_4
) ## 4 byte real
die "4-byte reals are not supported $!";
elsif ($self -> {'DataType'} == REAL_8
) ## 8 byte real
my ($negative,$exponent,$mantdata,$byteString,$byte,$mantissa,$real);
read($self -> {'FileHandle'},$data,1); ## sign bit and 7 exponent bits
#$data = reverse $data if ($isLittleEndian);
$self -> {'Record'} .= $data;
$negative = unpack 'B',$data; ## sign bit
$exponent = unpack 'C',$data;
$exponent -= 192; ## 128 + 64
read($self -> {'FileHandle'},$data,7); ## mantissa bits
$mantdata = unpack 'b*',$data;
$self -> {'Record'} .= $data;
$byteString = substr($mantdata,0,8,'');
$byte = pack 'b*',$byteString;
$byte = unpack 'C',$byte;
$mantissa += $byte / (256.0**($j+1));
$real = $mantissa * (16**$exponent);
$real = (0 - $real) if ($negative);
if ($RecordTypeStrings[$self -> {'RecordType'}] eq 'UNITS')
$self -> {'UUnits'} = $real if ($self -> {'UUnits'} == 0);
$self -> {'DBUnits'} = $real if ($self -> {'DBUnits'} == 0);
### this works because UUnits and DBUnits are 1st reals in GDS2 file
$real = int(($real+($self -> {'UUnits'}/$resolution))/$self -> {'UUnits'})*$self -> {'UUnits'} if ($self -> {'UUnits'} != 0); ## "rounds" off
$self -> {'RecordData'}[$i] = $real;
$tmpListString .= $self -> {'RecordData'}[$i];
$self -> {'DataIndex'} = $i - 1;
$self -> {'CurrentDataList'} = $tmpListString;
elsif ($self -> {'DataType'} == ACSII_STRING
) ## ascii string (null padded)
$self -> {'DataIndex'} = 0;
read($self -> {'FileHandle'},$data,$bytesLeft);
$self -> {'Record'} .= $data;
$self -> {'RecordData'}[0] = unpack "a$bytesLeft",$data;
$self -> {'RecordData'}[0] =~ s
|\
0||g
; ## take off ending nulls
$self -> {'CurrentDataList'} = ($self -> {'RecordData'}[0]);
################################################################################
=head1 Low Level Generic Evaluation Methods
################################################################################
=head2 returnRecordType - returns current (read) record type as integer
if ($gds2File -> returnRecordType == 6)
sub returnRecordType
#: Profiled
################################################################################
=head2 returnRecordTypeString - returns current (read) record type as string
if ($gds2File -> returnRecordTypeString eq 'LAYER')
sub returnRecordTypeString
#: Profiled
$RecordTypeStrings[($self -> {'RecordType'})];
################################################################################
=head2 returnRecordAsString - returns current (read) record as a string
while ($gds2File -> readGds2Record)
print $gds2File -> returnRecordAsString;
sub returnRecordAsString
() #: Profiled
$self -> {'UsingPrettyPrint'} = 1;
$string .= $StrSpace if ($self -> {'RecordType'} != BGNSTR
);
$string .= $ElmSpace if (!(($self -> {'RecordType'} == TEXT
) || ($self -> {'RecordType'} == PATH
) ||
($self -> {'RecordType'} == BOUNDARY
) || ($self -> {'RecordType'} == SREF
) ||
($self -> {'RecordType'} == AREF
)));
my $recordType = $RecordTypeStrings[$self -> {'RecordType'}];
while ($i <= $self -> {'DataIndex'})
if ($self -> {'DataType'} == BIT_ARRAY
)
my $bitString = $self -> {'RecordData'}[$i];
$bitString =~ m
|(........)(........)|;
$string .= ' '.$bitString;
($self -> {'DataType'} == INTEGER_2
) ||
($self -> {'DataType'} == REAL_8
)
$string .= ' '.$self -> {'RecordData'}[$i];
$string =~ s
|(\d
)\
.e
|$1e| if ($recordType eq 'UNITS'); ## perl on Cygwin prints "1.e-9" others "1e-9"
elsif ($self -> {'DataType'} == INTEGER_4
)
$string .= ' '.$self -> {'RecordData'}[$i]*($self -> {'UUnits'});
elsif ($self -> {'DataType'} == ACSII_STRING
)
$string .= " '".$self -> {'RecordData'}[$i]."'";
################################################################################
=head2 returnXyAsArray - returns current (read) XY record as an array
$gds2File -> returnXyAsArray(
-asInteger => 0|1 ## (optional) default is true. Return integer
## array or if false return array of reals.
-withClosure => 0|1 ## (optional) default is true. Whether to
##return a rectangle with 5 or 4 points.
while ($gds2File -> readGds2Record)
my @xy = $gds2File -> returnXyAsArray if ($gds2File -> isXy);
sub returnXyAsArray
() #: Profiled
my $asInteger = $arg{'-asInteger'};
if (! defined $asInteger)
my $withClosure = $arg{'-withClosure'};
if (! defined $withClosure)
my $stopPoint = $self -> {'DataIndex'};
$stopPoint -= 2 if (! $withClosure);
push @xys,($self -> {'RecordData'}[$i]);
push @xys,($self -> {'RecordData'}[$i]*($self -> {'UUnits'}));
################################################################################
=head2 returnRecordAsPerl - returns current (read) record as a perl command to facilitate the creation of parameterized gds2 data with perl.
my $gds2File = new GDS2(-fileName=>"test.gds");
while ($gds2File -> readGds2Record)
print $gds2File -> returnRecordAsPerl;
sub returnRecordAsPerl
() #: Profiled
my $gds2File = $arg{'-gds2File'};
my $PGR = $arg{'-printGds2Record'};
$PGR = 'printGds2Record';
$self -> {'UsingPrettyPrint'} = 1;
$string .= $StrSpace if ($self -> {'RecordType'} != BGNSTR
);
$string .= $ElmSpace if (!(($self -> {'RecordType'} == TEXT
) || ($self -> {'RecordType'} == PATH
) ||
($self -> {'RecordType'} == BOUNDARY
) || ($self -> {'RecordType'} == SREF
) ||
($self -> {'RecordType'} == AREF
)));
($self -> {'RecordType'} == TEXT
) ||
($self -> {'RecordType'} == PATH
) ||
($self -> {'RecordType'} == BOUNDARY
) ||
($self -> {'RecordType'} == SREF
) ||
($self -> {'RecordType'} == AREF
) ||
($self -> {'RecordType'} == ENDEL
) ||
($self -> {'RecordType'} == ENDSTR
) ||
($self -> {'RecordType'} == ENDLIB
)
$string .= $gds2File.'->'.$PGR.'(-type=>'."'".$RecordTypeStrings[$self -> {'RecordType'}]."'".');';
$string .= $gds2File.'->'.$PGR.'(-type=>'."'".$RecordTypeStrings[$self -> {'RecordType'}]."',-data=>";
my $maxi = $self -> {'DataIndex'};
if ($maxi >= 1) {$string .= '['}
if ($self -> {'DataType'} == BIT_ARRAY
)
$string .= "'".$self -> {'RecordData'}[$i]."'";
elsif ($self -> {'DataType'} == INTEGER_2
)
$string .= $self -> {'RecordData'}[$i];
elsif ($self -> {'DataType'} == INTEGER_4
)
$string .= $self -> {'RecordData'}[$i];
elsif ($self -> {'DataType'} == REAL_8
)
$string .= $self -> {'RecordData'}[$i];
elsif ($self -> {'DataType'} == ACSII_STRING
)
$string .= "'".$self -> {'RecordData'}[$i]."'";
if ($i < $maxi) {$string .= ', '}
if ($maxi >= 1) {$string .= ']'}
################################################################################
=head1 Low Level Specific Write Methods
################################################################################
=head2 printAngle - prints ANGLE record
$gds2File -> printAngle(-num=>#.#);
sub printAngle
#: Profiled
my $angle = $arg{'-num'};
$angle=0 if (! defined $angle);
$self -> printGds2Record
(-type
=> 'ANGLE',-data
=> $angle) if ($angle);
################################################################################
=head2 printAttrtable - prints ATTRTABLE record
$gds2File -> printAttrtable(-string=>$string);
sub printAttrtable
#: Profiled
my $string = $arg{'-string'};
die "printAttrtable expects a string. Missing -string => 'text' $!";
$self -> printGds2Record
(-type
=> 'ATTRTABLE',-data
=> $string);
################################################################################
=head2 printBgnextn - prints BGNEXTN record
$gds2File -> printBgnextn(-num=>#.#);
sub printBgnextn
#: Profiled
die "printBgnextn expects a extension number. Missing -num => #.# $!";
my $resolution = $self -> {'Resolution'};
if ($num >= 0) {$num = int(($num*$resolution)+$G_epsilon);}
else {$num = int(($num*$resolution)-$G_epsilon);}
$self -> printGds2Record
(-type
=> 'BGNEXTN',-data
=> $num);
################################################################################
=head2 printBgnlib - prints BGNLIB record
$gds2File -> printBgnlib(
-isoDate => 0|1 ## (optional) use ISO 4 digit date 2001 vs 101
sub printBgnlib
#: Profiled
my $isoDate = $arg{'-isoDate'};
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900 if ($isoDate); ## Cadence likes year left "as is". GDS format supports year number up to 65535 -- 101 vs 2001
$self -> printGds2Record
(-type
=>'BGNLIB',-data
=>[$year,$mon,$mday,$hour,$min,$sec,$year,$mon,$mday,$hour,$min,$sec]);
################################################################################
=head2 printBox - prints BOX record
$self -> printGds2Record
(-type
=> 'BOX');
################################################################################
=head2 printBoxtype - prints BOXTYPE record
$gds2File -> printBoxtype(-num=>#);
sub printBoxtype
#: Profiled
die "printBoxtype expects a number. Missing -num => # $!";
$self -> printGds2Record
(-type
=> 'BOXTYPE',-data
=> $num);
################################################################################
=head2 printColrow - prints COLROW record
$gds2File -> printBoxtype(-columns=>#, -rows=>#);
sub printColrow
#: Profiled
my $columns = $arg{'-columns'};
if ((! defined $columns)||($columns <= 0))
my $rows = $arg{'-rows'};
if ((! defined $rows)||($rows <= 0))
$self -> printGds2Record
(-type
=> 'COLROW',-data
=> [$columns,$rows]);
################################################################################
=head2 printDatatype - prints DATATYPE record
$gds2File -> printDatatype(-num=>#);
sub printDatatype
#: Profiled
my $dataType = $arg{'-num'};
$self -> printGds2Record
(-type
=> 'DATATYPE',-data
=> $dataType);
################################################################################
sub printEflags
#: Profiled
die "EFLAGS type not supported $!";
################################################################################
=head2 printElkey - prints ELKEY record
$gds2File -> printElkey(-num=>#);
sub printElkey
#: Profiled
die "printElkey expects a number. Missing -num => #.# $!";
$self -> printGds2Record
(-type
=> 'ELKEY',-data
=> $num);
################################################################################
=head2 printEndel - closes an element definition
sub printEndel
#: Profiled
$self -> printGds2Record
(-type
=> 'ENDEL');
################################################################################
=head2 printEndextn - prints path end extension record
$gds2File printEndextn -> (-num=>#.#);
sub printEndextn
#: Profiled
die "printEndextn expects a extension number. Missing -num => #.# $!";
my $resolution = $self -> {'Resolution'};
if ($num >= 0) {$num = int(($num*$resolution)+$G_epsilon);}
else {$num = int(($num*$resolution)-$G_epsilon);}
$self -> printGds2Record
(-type
=> 'ENDEXTN',-data
=> $num);
################################################################################
=head2 printEndlib - closes a library definition
sub printEndlib
#: Profiled
$self -> printGds2Record
(-type
=> 'ENDLIB');
################################################################################
=head2 printEndstr - closes a structure definition
sub printEndstr
#: Profiled
$self -> printGds2Record
(-type
=> 'ENDSTR');
################################################################################
=head2 printEndmasks - prints a ENDMASKS
sub printEndmasks
#: Profiled
$self -> printGds2Record
(-type
=> 'ENDMASKS');
################################################################################
=head2 printFonts - prints a FONTS record
$gds2File -> printFonts(-string=>'names_of_font_files');
sub printFonts
#: Profiled
my $string = $arg{'-string'};
die "printFonts expects a string. Missing -string => 'text' $!";
$self -> printGds2Record
(-type
=> 'FONTS',-data
=> $string);
################################################################################
sub printFormat
#: Profiled
die "printFormat expects a number. Missing -num => #.# $!";
$self -> printGds2Record
(-type
=> 'FORMAT',-data
=> $num);
################################################################################
sub printGenerations
#: Profiled
$self -> printGds2Record
(-type
=> 'GENERATIONS');
################################################################################
=head2 printHeader - Prints a rev 3 header
$gds2File -> printHeader(
-num => # ## optional, defaults to 3. valid revs are 0,3,4,5,and 600
sub printHeader
#: Profiled
$self -> printGds2Record
(-type
=>'HEADER',-data
=>$rev);
################################################################################
=head2 printLayer - prints a LAYER number
-num => # ## optional, defaults to 0.
sub printLayer
#: Profiled
my $layer = $arg{'-num'};
$self -> printGds2Record
(-type
=> 'LAYER',-data
=> $layer);
################################################################################
sub printLibdirsize
#: Profiled
$self -> printGds2Record
(-type
=> 'LIBDIRSIZE');
################################################################################
=head2 printLibname - Prints library name
printLibname(-name=>$name);
sub printLibname
#: Profiled
my $libName = $arg{'-name'};
die "printLibname expects a library name. Missing -name => 'name' $!";
$self -> printGds2Record
(-type
=> 'LIBNAME',-data
=> $libName);
################################################################################
sub printLibsecur
#: Profiled
$self -> printGds2Record
(-type
=> 'LIBSECUR');
################################################################################
sub printLinkkeys
#: Profiled
die "printLinkkeys expects a number. Missing -num => #.# $!";
$self -> printGds2Record
(-type
=> 'LINKKEYS',-data
=> $num);
################################################################################
sub printLinktype
#: Profiled
die "printLinktype expects a number. Missing -num => #.# $!";
$self -> printGds2Record
(-type
=> 'LINKTYPE',-data
=> $num);
################################################################################
=head2 printPathtype - prints a PATHTYPE number
$gds2File -> printPathtype(
-num => # ## optional, defaults to 0.
sub printPathtype
#: Profiled
my $pathType = $arg{'-num'};
$pathType=0 if (! defined $pathType);
$self -> printGds2Record
(-type
=> 'PATHTYPE',-data
=> $pathType) if ($pathType);
################################################################################
=head2 printMag - prints a MAG number
-num => #.# ## optional, defaults to 0.0
$mag=0 if ((! defined $mag)||($mag <= 0));
$self -> printGds2Record
(-type
=> 'MAG',-data
=> $mag)if ($mag);
################################################################################
sub printMask
#: Profiled
my $string = $arg{'-string'};
die "printMask expects a string. Missing -string => 'text' $!";
$self -> printGds2Record
(-type
=> 'MASK',-data
=> $string);
################################################################################
sub printNode
#: Profiled
$self -> printGds2Record
(-type
=> 'NODE');
################################################################################
=head2 printNodetype - prints a NODETYPE number
$gds2File -> printNodetype(
sub printNodetype
#: Profiled
die "printNodetype expects a number. Missing -num => # $!";
$self -> printGds2Record
(-type
=> 'NODETYPE',-data
=> $num);
################################################################################
sub printPlex
#: Profiled
die "printPlex expects a number. Missing -num => #.# $!";
$self -> printGds2Record
(-type
=> 'PLEX',-data
=> $num);
################################################################################
=head2 printPresentation - prints a text presentation record
$gds2File -> printPresentation(
-font => #, ##optional, defaults to 0, valid numbers are 0-3
-top, ||-middle, || -bottom, ## vertical justification
-left, ||-center, || -right, ## horizontal justification
gds2File -> printPresentation(-font=>0,-top,-left);
sub printPresentation
#: Profiled
my $font = $arg{'-font'};
if ((! defined $font) || ($font < 0) || ($font > 3))
$font = sprintf("%02d",$font);
my $middle = $arg{'-middle'};
my $bottom = $arg{'-bottom'};
if (defined $top) {$vertical = '00';}
elsif (defined $bottom) {$vertical = '10';}
else {$vertical = '01';} ## middle
my $left = $arg{'-left'};
my $center = $arg{'-center'};
my $right = $arg{'-right'};
if (defined $left) {$horizontal = '00';}
elsif (defined $right) {$horizontal = '10';}
else {$horizontal = '01';} ## center
my $bitstring = "0000000000$font$vertical$horizontal";
$self -> printGds2Record
(-type
=> 'PRESENTATION',-data
=> $bitstring);
################################################################################
=head2 printPropattr - prints a property id number
$gds2File -> printPropattr( -num => # );
sub printPropattr
#: Profiled
die "printPropattr expects a number. Missing -num => # $!";
$self -> printGds2Record
(-type
=> 'PROPATTR',-data
=> $num);
################################################################################
=head2 printPropvalue - prints a property value string
$gds2File -> printPropvalue( -string => $string );
sub printPropvalue
#: Profiled
my $string = $arg{'-string'};
die "printPropvalue expects a string. Missing -string => 'text' $!";
$self -> printGds2Record
(-type
=> 'PROPVALUE',-data
=> $string);
################################################################################
sub printReflibs
#: Profiled
my $string = $arg{'-string'};
die "printReflibs expects a string. Missing -string => 'text' $!";
$self -> printGds2Record
(-type
=> 'REFLIBS',-data
=> $string);
################################################################################
sub printReserved
#: Profiled
die "printReserved expects a number. Missing -num => #.# $!";
$self -> printGds2Record
(-type
=> 'RESERVED',-data
=> $num);
################################################################################
=head2 printSname - prints a SNAME string
$gds2File -> printSname( -name => $cellName );
sub printSname
#: Profiled
my $string = $arg{'-name'};
die "printSname expects a cell name. Missing -name => 'text' $!";
$self -> printGds2Record
(-type
=> 'SNAME',-data
=> $string);
################################################################################
sub printSpacing
#: Profiled
die "SPACING type not supported $!";
################################################################################
sub printSrfname
#: Profiled
$self -> printGds2Record
(-type
=> 'SRFNAME');
################################################################################
=head2 printStrans - prints a STRANS record
$gds2File -> printStrans( -reflect );
sub printStrans
#: Profiled
my $reflect = $arg{'-reflect'};
if ((! defined $reflect)||($reflect <= 0))
my $data=$reflect.'000000000000000'; ## 16 'bit' string
$self -> printGds2Record
(-type
=> 'STRANS',-data
=> $data);
################################################################################
sub printStrclass
#: Profiled
$self -> printGds2Record
(-type
=> 'STRCLASS');
################################################################################
=head2 printString - prints a STRING record
$gds2File -> printSname( -string => $text );
sub printString
#: Profiled
my $string = $arg{'-string'};
die "printString expects a string. Missing -string => 'text' $!";
$self -> printGds2Record
(-type
=> 'STRING',-data
=> $string);
################################################################################
=head2 printStrname - prints a structure name string
$gds2File -> printStrname( -name => $cellName );
sub printStrname
#: Profiled
my $strName = $arg{'-name'};
die "printStrname expects a structure name. Missing -name => 'name' $!";
$self -> printGds2Record
(-type
=> 'STRNAME',-data
=> $strName);
################################################################################
sub printStrtype
#: Profiled
die "STRTYPE type not supported $!";
################################################################################
sub printStyptable
#: Profiled
my $string = $arg{'-string'};
die "printStyptable expects a string. Missing -string => 'text' $!";
$self -> printGds2Record
(-type
=> 'STYPTABLE',-data
=> $string);
################################################################################
sub printTapecode
#: Profiled
die "printTapecode expects a number. Missing -num => #.# $!";
$self -> printGds2Record
(-type
=> 'TAPECODE',-data
=> $num);
################################################################################
sub printTapenum
#: Profiled
die "printTapenum expects a number. Missing -num => #.# $!";
$self -> printGds2Record
(-type
=> 'TAPENUM',-data
=> $num);
################################################################################
sub printTextnode
#: Profiled
$self -> printGds2Record
(-type
=> 'TEXTNODE');
################################################################################
=head2 printTexttype - prints a text type number
$gds2File -> printTexttype( -num => # );
sub printTexttype
#: Profiled
die "printTexttype expects a number. Missing -num => # $!";
$self -> printGds2Record
(-type
=> 'TEXTTYPE',-data
=> $num);
################################################################################
sub printUinteger
#: Profiled
die "UINTEGER type not supported $!";
################################################################################
=head2 printUnits - Prints units record.
Defaults to 1e-3 and 1e-9
sub printUnits
#: Profiled
$self -> printGds2Record
(-type
=> 'UNITS',-data
=> [0.001,1e-9]);
################################################################################
sub printUstring
#: Profiled
die "USTRING type not supported $!";
################################################################################
=head2 printPropattr - prints a width number
$gds2File -> printWidth( -num => # );
sub printWidth
#: Profiled
my $width = $arg{'-num'};
if ((! defined $width)||($width <= 0))
$self -> printGds2Record
(-type
=> 'WIDTH',-data
=> $width) if ($width);
################################################################################
=head2 printXy - prints an XY array
$gds2File -> printXy( -xy => \@array );
#### -xyInt most useful if reading and modifying... -xy if creating from scratch
my $xyInt = $arg{'-xyInt'}; ## $xyInt should be a reference to an array of internal GDS2 format integers
my $xy = $arg{'-xy'}; ## $xy should be a reference to an array of reals
my $resolution = $self -> {'Resolution'};
if (! ((defined $xy) || (defined $xyInt)))
die "printXy expects an xy array reference. Missing -xy => \\\@array $!";
my @xyTmp=(); ##don't pollute array passed in
for(my $i=0;$i<=$#$xy;$i++) ## e.g. 3.4 in -> 3400 out
if ($xy -> [$i] >= 0) {push @xyTmp,int((($xy -> [$i])*$resolution)+$G_epsilon);}
else {push @xyTmp,int((($xy -> [$i])*$resolution)-$G_epsilon);}
$self -> printGds2Record
(-type
=> 'XY',-data
=> \
@xyTmp);
################################################################################
################################################################################
=head1 Low Level Specific Evaluation Methods
################################################################################
=head2 returnDatatype - returns datatype # if record is DATATYPE else returns -1
$dataTypesFound[$gds2File -> returnDatatype] = 1;
sub returnDatatype
#: Profiled
if ($self -> isDatatype
) { $self -> {'RecordData'}[0]; }
################################################################################
=head2 returnPathtype - returns pathtype # if record is PATHTYPE else returns -1
sub returnPathtype
#: Profiled
if ($self -> isPathtype
) { $self -> {'RecordData'}[0]; }
################################################################################
=head2 returnTexttype - returns texttype # if record is TEXTTYPE else returns -1
$TextTypesFound[$gds2File -> returnTexttype] = 1;
sub returnTexttype
#: Profiled
if ($self -> isTexttype
) { $self -> {'RecordData'}[0]; }
################################################################################
=head2 returnWidth - returns width # if record is WIDTH else returns -1
sub returnWidth
#: Profiled
if ($self -> isWidth
) { $self -> {'RecordData'}[0]; }
################################################################################
=head2 returnLayer - returns layer # if record is LAYER else returns -1
$layersFound[$gds2File -> returnLayer] = 1;
sub returnLayer
#: Profiled
if ($self -> isLayer
) { $self -> {'RecordData'}[0]; }
################################################################################
=head2 returnBgnextn - returns bgnextn if record is BGNEXTN else returns 0
sub returnBgnextn
#: Profiled
if ($self -> isBgnextn
) { $self -> {'RecordData'}[0]; }
################################################################################
=head2 returnEndextn- returns endextn if record is ENDEXTN else returns 0
sub returnEndextn
#: Profiled
if ($self -> isEndextn
) { $self -> {'RecordData'}[0]; }
################################################################################
=head2 returnString - return string if record type is STRING else ''
sub returnString
#: Profiled
if ($self -> isString
) { $self -> {'RecordData'}[0]; }
################################################################################
=head2 returnSname - return string if record type is SNAME else ''
sub returnSname
#: Profiled
if ($self -> isSname
) { $self -> {'RecordData'}[0]; }
################################################################################
=head2 returnStrname - return string if record type is STRNAME else ''
sub returnStrname
#: Profiled
if ($self -> isStrname
) { $self -> {'RecordData'}[0]; }
################################################################################
################################################################################
=head1 Low Level Specific Boolean Methods
################################################################################
=head2 isAref - return 0 or 1 depending on whether current record is an aref
if ($self -> {'RecordType'} == AREF
) { 1; }
################################################################################
=head2 isBgnlib - return 0 or 1 depending on whether current record is a bgnlib
if ($self -> {'RecordType'} == BGNLIB
) { 1; }
################################################################################
=head2 isBgnstr - return 0 or 1 depending on whether current record is a bgnstr
if ($self -> {'RecordType'} == BGNSTR
) { 1; }
################################################################################
=head2 isBoundary - return 0 or 1 depending on whether current record is a boundary
sub isBoundary
#: Profiled
if ($self -> {'RecordType'} == BOUNDARY
) { 1; }
################################################################################
=head2 isDatatype - return 0 or 1 depending on whether current record is datatype
sub isDatatype
#: Profiled
if ($self -> {'RecordType'} == DATATYPE
) { 1; }
################################################################################
=head2 isEndlib - return 0 or 1 depending on whether current record is endlib
if ($self -> {'RecordType'} == ENDLIB
) { 1; }
################################################################################
=head2 isEndel - return 0 or 1 depending on whether current record is endel
if ($self -> {'RecordType'} == ENDEL
) { 1; }
################################################################################
=head2 isEndstr - return 0 or 1 depending on whether current record is endstr
if ($self -> {'RecordType'} == ENDSTR
) { 1; }
################################################################################
=head2 isHeader - return 0 or 1 depending on whether current record is a header
if ($self -> {'RecordType'} == HEADER
) { 1; }
################################################################################
=head2 isLibname - return 0 or 1 depending on whether current record is a libname
sub isLibname
#: Profiled
if ($self -> {'RecordType'} == LIBNAME
) { 1; }
################################################################################
=head2 isPath - return 0 or 1 depending on whether current record is a path
if ($self -> {'RecordType'} == PATH
) { 1; }
################################################################################
=head2 isSref - return 0 or 1 depending on whether current record is an sref
if ($self -> {'RecordType'} == SREF
) { 1; }
################################################################################
=head2 isSrfname - return 0 or 1 depending on whether current record is an srfname
sub isSrfname
#: Profiled
if ($self -> {'RecordType'} == SRFNAME
) { 1; }
################################################################################
=head2 isText - return 0 or 1 depending on whether current record is a text
if ($self -> {'RecordType'} == TEXT
) { 1; }
################################################################################
=head2 isUnits - return 0 or 1 depending on whether current record is units
if ($self -> {'RecordType'} == UNITS
) { 1; }
################################################################################
=head2 isLayer - return 0 or 1 depending on whether current record is layer
if ($self -> {'RecordType'} == LAYER
) { 1; }
################################################################################
=head2 isStrname - return 0 or 1 depending on whether current record is strname
sub isStrname
#: Profiled
if ($self -> {'RecordType'} == STRNAME
) { 1; }
################################################################################
=head2 isWidth - return 0 or 1 depending on whether current record is width
if ($self -> {'RecordType'} == WIDTH
) { 1; }
################################################################################
=head2 isXy - return 0 or 1 depending on whether current record is xy
if ($self -> {'RecordType'} == XY
) { 1; }
################################################################################
=head2 isSname - return 0 or 1 depending on whether current record is sname
if ($self -> {'RecordType'} == SNAME
) { 1; }
################################################################################
=head2 isColrow - return 0 or 1 depending on whether current record is colrow
if ($self -> {'RecordType'} == COLROW
) { 1; }
################################################################################
=head2 isTextnode - return 0 or 1 depending on whether current record is a textnode
sub isTextnode
#: Profiled
if ($self -> {'RecordType'} == TEXTNODE
) { 1; }
################################################################################
=head2 isNode - return 0 or 1 depending on whether current record is a node
if ($self -> {'RecordType'} == NODE
) { 1; }
################################################################################
=head2 isTexttype - return 0 or 1 depending on whether current record is a texttype
sub isTexttype
#: Profiled
if ($self -> {'RecordType'} == TEXTTYPE
) { 1; }
################################################################################
=head2 isPresentation - return 0 or 1 depending on whether current record is a presentation
sub isPresentation
#: Profiled
if ($self -> {'RecordType'} == PRESENTATION
) { 1; }
################################################################################
=head2 isSpacing - return 0 or 1 depending on whether current record is a spacing
sub isSpacing
#: Profiled
if ($self -> {'RecordType'} == SPACING
) { 1; }
################################################################################
=head2 isString - return 0 or 1 depending on whether current record is a string
if ($self -> {'RecordType'} == STRING
) { 1; }
################################################################################
=head2 isStrans - return 0 or 1 depending on whether current record is a strans
if ($self -> {'RecordType'} == STRANS
) { 1; }
################################################################################
=head2 isMag - return 0 or 1 depending on whether current record is a mag
if ($self -> {'RecordType'} == MAG
) { 1; }
################################################################################
=head2 isAngle - return 0 or 1 depending on whether current record is a angle
if ($self -> {'RecordType'} == ANGLE
) { 1; }
################################################################################
=head2 isUinteger - return 0 or 1 depending on whether current record is a uinteger
sub isUinteger
#: Profiled
if ($self -> {'RecordType'} == UINTEGER
) { 1; }
################################################################################
=head2 isUstring - return 0 or 1 depending on whether current record is a ustring
sub isUstring
#: Profiled
if ($self -> {'RecordType'} == USTRING
) { 1; }
################################################################################
=head2 isReflibs - return 0 or 1 depending on whether current record is a reflibs
sub isReflibs
#: Profiled
if ($self -> {'RecordType'} == REFLIBS
) { 1; }
################################################################################
=head2 isFonts - return 0 or 1 depending on whether current record is a fonts
if ($self -> {'RecordType'} == FONTS
) { 1; }
################################################################################
=head2 isPathtype - return 0 or 1 depending on whether current record is a pathtype
sub isPathtype
#: Profiled
if ($self -> {'RecordType'} == PATHTYPE
) { 1; }
################################################################################
=head2 isGenerations - return 0 or 1 depending on whether current record is a generations
sub isGenerations
#: Profiled
if ($self -> {'RecordType'} == GENERATIONS
) { 1; }
################################################################################
=head2 isAttrtable - return 0 or 1 depending on whether current record is a attrtable
sub isAttrtable
#: Profiled
if ($self -> {'RecordType'} == ATTRTABLE
) { 1; }
################################################################################
=head2 isStyptable - return 0 or 1 depending on whether current record is a styptable
sub isStyptable
#: Profiled
if ($self -> {'RecordType'} == STYPTABLE
) { 1; }
################################################################################
=head2 isStrtype - return 0 or 1 depending on whether current record is a strtype
sub isStrtype
#: Profiled
if ($self -> {'RecordType'} == STRTYPE
) { 1; }
################################################################################
=head2 isEflags - return 0 or 1 depending on whether current record is a eflags
if ($self -> {'RecordType'} == EFLAGS
) { 1; }
################################################################################
=head2 isElkey - return 0 or 1 depending on whether current record is a elkey
if ($self -> {'RecordType'} == ELKEY
) { 1; }
################################################################################
=head2 isLinktype - return 0 or 1 depending on whether current record is a linktype
sub isLinktype
#: Profiled
if ($self -> {'RecordType'} == LINKTYPE
) { 1; }
################################################################################
=head2 isLinkkeys - return 0 or 1 depending on whether current record is a linkkeys
sub isLinkkeys
#: Profiled
if ($self -> {'RecordType'} == LINKKEYS
) { 1; }
################################################################################
=head2 isNodetype - return 0 or 1 depending on whether current record is a nodetype
sub isNodetype
#: Profiled
if ($self -> {'RecordType'} == NODETYPE
) { 1; }
################################################################################
=head2 isPropattr - return 0 or 1 depending on whether current record is a propattr
sub isPropattr
#: Profiled
if ($self -> {'RecordType'} == PROPATTR
) { 1; }
################################################################################
=head2 isPropvalue - return 0 or 1 depending on whether current record is a propvalue
sub isPropvalue
#: Profiled
if ($self -> {'RecordType'} == PROPVALUE
) { 1; }
################################################################################
=head2 isBox - return 0 or 1 depending on whether current record is a box
if ($self -> {'RecordType'} == BOX
) { 1; }
################################################################################
=head2 isBoxtype - return 0 or 1 depending on whether current record is a boxtype
sub isBoxtype
#: Profiled
if ($self -> {'RecordType'} == BOXTYPE
) { 1; }
################################################################################
=head2 isPlex - return 0 or 1 depending on whether current record is a plex
if ($self -> {'RecordType'} == PLEX
) { 1; }
################################################################################
=head2 isBgnextn - return 0 or 1 depending on whether current record is a bgnextn
sub isBgnextn
#: Profiled
if ($self -> {'RecordType'} == BGNEXTN
) { 1; }
################################################################################
=head2 isEndextn - return 0 or 1 depending on whether current record is a endextn
sub isEndextn
#: Profiled
if ($self -> {'RecordType'} == ENDEXTN
) { 1; }
################################################################################
=head2 isTapenum - return 0 or 1 depending on whether current record is a tapenum
sub isTapenum
#: Profiled
if ($self -> {'RecordType'} == TAPENUM
) { 1; }
################################################################################
=head2 isTapecode - return 0 or 1 depending on whether current record is a tapecode
sub isTapecode
#: Profiled
if ($self -> {'RecordType'} == TAPECODE
) { 1; }
################################################################################
=head2 isStrclass - return 0 or 1 depending on whether current record is a strclass
sub isStrclass
#: Profiled
if ($self -> {'RecordType'} == STRCLASS
) { 1; }
################################################################################
=head2 isReserved - return 0 or 1 depending on whether current record is a reserved
sub isReserved
#: Profiled
if ($self -> {'RecordType'} == RESERVED
) { 1; }
################################################################################
=head2 isFormat - return 0 or 1 depending on whether current record is a format
if ($self -> {'RecordType'} == FORMAT
) { 1; }
################################################################################
=head2 isMask - return 0 or 1 depending on whether current record is a mask
if ($self -> {'RecordType'} == MASK
) { 1; }
################################################################################
=head2 isEndmasks - return 0 or 1 depending on whether current record is a endmasks
sub isEndmasks
#: Profiled
if ($self -> {'RecordType'} == ENDMASKS
) { 1; }
################################################################################
=head2 isLibdirsize - return 0 or 1 depending on whether current record is a libdirsize
sub isLibdirsize
#: Profiled
if ($self -> {'RecordType'} == LIBDIRSIZE
) { 1; }
################################################################################
=head2 isLibsecur - return 0 or 1 depending on whether current record is a libsecur
sub isLibsecur
#: Profiled
if ($self -> {'RecordType'} == LIBSECUR
) { 1; }
################################################################################
################################################################################
sub getRecordData
#: Profiled
my $dt = $self -> {'DataType'};
elsif ($dt==INTEGER_2
|| $dt==INTEGER_4
|| $dt==REAL_8
)
my $stuff = $self -> {'CurrentDataList'};
return(split(/,/,$stuff));
elsif ($dt==ACSII_STRING
)
my $stuff = $self -> {'CurrentDataList'};
return ($self -> {'CurrentDataList'});
################################################################################
sub readRecordTypeAndData
#: Profiled
return ($RecordTypeStrings[$self -> {'RecordType'}],$self -> {'RecordData'});
################################################################################
sub skipGds2RecordData
#: Profiled
$self -> readGds2RecordHeader
() if ($self -> {'HEADER'} <= 0); ## safety
$G_timer -> start
('skipGds2RecordData');
## 4 should have been just read by readGds2RecordHeader
seek($self -> {'FileHandle'},$self -> {'Length'} - 4,SEEK_CUR
); ## seek seems to run a little faster than read
$self -> {'DataIndex'} = -1;
################################################################################
### return number of XY coords if XY record
sub returnNumCoords
#: Profiled
if ($self -> {'RecordType'} == XY
) ## 4 byte signed integer
int(($self -> {'Length'} - 4) / 8);
################################################################################
sprintf("%.${places}f",$num);
################################################################################
sub scaleNum
($$) #: Profiled
die "1st number passed into scaleNum() must be an integer $!" if ($num !~ m
|^-?\d
+|);
$num = int($num+0.5) if ($num =~ m
|\
.|);
################################################################################
sub snapNum
($$) #: Profiled
die "1st number passed into snapNum() must be an integer $!" if ($num !~ m
|^-?\d
+$|);
my $snapLength = length("$snap");
my $littlePart=substr($num,-$snapLength,$snapLength);
$littlePart = -$littlePart;
$littlePart = int(($littlePart/$snap)+(0.5*$lean))*$snap;
my $bigPart=substr($num,0,-$snapLength);
if ($bigPart =~ m
|^[-]?
$|)
$bigPart *= 10**$snapLength;
$num = $bigPart + $littlePart;
#warn "DESTROYing $self";
################################################################################
## some vendor tools have trouble w/ negative angles and angles >= 360
## so we normalize to positive equivalent
################################################################################
sub posAngle
($) #: Profiled
$angle += 360.0 while ($angle < 0.0);
$angle -= 360.0 while ($angle >= 360.0);
=head2 tellSize - return current byte position (NOT zero based)
my $position = $gds2File -> tellSize;
################################################################################
sub tellSize
() #: Profiled
################################################################################
sub subbyte
() ## GDS2::version();
my($what,$where,$howmuch) = @_;
unpack("x$where C$howmuch", $what);
################################################################################
sub version
() ## GDS2::version();
################################################################################
sub revision
() ## GDS2::revision();
################################################################################
here's a bare bones script to change all layer 59 to 66 given a file to
read and a new file to create.
my $fileName1 = $ARGV[0];
my $fileName2 = $ARGV[1];
my $gds2File1 = new GDS2(-fileName => $fileName1);
my $gds2File2 = new GDS2(-fileName => ">$fileName2");
while (my $record = $gds2File1 -> readGds2Record)
if ($gds2File1 -> returnLayer == 59)
$gds2File2 -> printLayer(-num=>66);
$gds2File2 -> printRecord(-data=>$record);
here's a program to dump the contents of a stream file.
my $gds2File = new GDS2(-fileName=>$ARGV[0]);
while ($gds2File -> readGds2Record)
print $gds2File -> returnRecordAsString;
Create a complete GDS2 stream file from scratch:
my $gds2File = new GDS2(-fileName=>'>test.gds');
$gds2File -> printInitLib(-name=>'testlib');
$gds2File -> printBgnstr(-name=>'test');
-xy=>[0,0, 10.5,0, 10.5,3.3],
$gds2File -> printEndstr;
$gds2File -> printBgnstr(-name => 'contact');
$gds2File -> printBoundary(
-xy=>[0,0, 1,0, 1,1, 0,1],
$gds2File -> printEndstr;
$gds2File -> printEndlib();
=head1 GDS2 Stream Format
#########################################################################################
# Gds2 stream format is composed of variable length records. The mininum
# length record is 4 bytes. The 1st 2 bytes of a record contain a count (in 8 bit
# bytes) of the total record length. The 3rd byte of the header is the record
# type. The 4th byte describes the type of data contained w/in the record. The
# 5th through last bytes are data.
# If the output file is a mag tape, then the records of the library are written
# out in 2048-byte physical blocks. Records may overlap block boundaries.
# For this reason I think gds2 is often padded with null bytes so that the
# file size ends up being a multiple of 2048.
# A null word consists of 2 consecutive zero bytes. Use null words to fill the
# o the last record of a library and the end of its block
# o the last record of a tape in a mult-reel stream file.
# --------- ----- -----------------------
# no data present 0 4 byte header + 0
# Bit Array 1 4 byte header + 2 bytes data
# 2byte Signed Int 2 SMMMMMMM MMMMMMMM -> S - sign ; M - magnitude.
# Twos complement format, with the most significant byte first.
# 4byte Signed Int 3 SMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM
# 8byte Real 5 SEEEEEEE MMMMMMMM MMMMMMMM MMMMMMMM E-expon in excess-64
# MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM representation
# Mantissa == pos fraction >=1/16 && <1 bit 8==1/2, 9==1/4 etc...
# The first bit is the sign (1 = negative), the next 7 bits
# are the exponent, you have to subtract 64 from this number to
# get the real value. The next seven bytes are the mantissa in
# 4 word floating point representation.
# string 6 odd length strings must be padded w/ null character and
#########################################################################################
=head1 Backus-naur representation of GDS2 Stream Syntax
################################################################################
# <STREAM FORMAT>::= HEADER BGNLIB [LIBDIRSIZE] [SRFNAME] [LIBSECR] #
# LIBNAME [REFLIBS] [FONTS] [ATTRTABLE] [GENERATIONS] #
# [<FormatType>] UNITS {<structure>}* ENDLIB #
# <FormatType>::= FORMAT | FORMAT {MASK}+ ENDMASKS #
# <structure>::= BGNSTR STRNAME [STRCLASS] {<element>}* ENDSTR #
# <element>::= {<boundary> | <path> | <SREF> | <AREF> | <text> | #
# <node> | <box} {<property>}* ENDEL #
# <boundary>::= BOUNDARY [ELFLAGS] [PLEX] LAYER DATATYPE XY #
# <path>::= PATH [ELFLAGS] [PLEX] LAYER DATATYPE [PATHTYPE] #
# [WIDTH] [BGNEXTN] [ENDEXTN] [XY] #
# <SREF>::= SREF [ELFLAGS] [PLEX] SNAME [<strans>] XY #
# <AREF>::= AREF [ELFLAGS] [PLEX] SNAME [<strans>] COLROW XY #
# <text>::= TEXT [ELFLAGS] [PLEX] LAYER <textbody> #
# <textbody>::= TEXTTYPE [PRESENTATION] [PATHTYPE] [WIDTH] [<strans>] XY #
# <strans>::= STRANS [MAG] [ANGLE] #
# <node>::= NODE [ELFLAGS] [PLEX] LAYER NODETYPE XY #
# <box>::= BOX [ELFLAGS] [PLEX] LAYER BOXTYPE XY #
# <property>::= PROPATTR PROPVALUE #
################################################################################
=head1 GDS2 Stream Record Datatypes
################################################################################
REAL_4 = 4; ## NOT supported, never really used
################################################################################
=head1 GDS2 Stream Record Types
################################################################################
HEADER = 0; ## 2-byte Signed Integer
BGNLIB = 1; ## 2-byte Signed Integer
LIBNAME = 2; ## ASCII String
UNITS = 3; ## 8-byte Real
ENDLIB = 4; ## no data present
BGNSTR = 5; ## 2-byte Signed Integer
STRNAME = 6; ## ASCII String
ENDSTR = 7; ## no data present
BOUNDARY = 8; ## no data present
PATH = 9; ## no data present
SREF = 10; ## no data present
AREF = 11; ## no data present
TEXT = 12; ## no data present
LAYER = 13; ## 2-byte Signed Integer
DATATYPE = 14; ## 2-byte Signed Integer
WIDTH = 15; ## 4-byte Signed Integer
XY = 16; ## 4-byte Signed Integer
ENDEL = 17; ## no data present
SNAME = 18; ## ASCII String
COLROW = 19; ## 2 2-byte Signed Integer <= 32767
TEXTNODE = 20; ## no data present
NODE = 21; ## no data present
TEXTTYPE = 22; ## 2-byte Signed Integer
PRESENTATION = 23; ## Bit Array. One word (2 bytes) of bit flags. Bits 11 and
## 12 together specify the font 00->font 0 11->font 3.
## Bits 13 and 14 specify the vertical presentation, 15
## and 16 the horizontal presentation. 00->'top/left' 01->
## middle/center 10->bottom/right bits 1-10 were reserved
## for future use and should be 0.
SPACING = 24; ## discontinued
STRING = 25; ## ASCII String <= 512 characters
STRANS = 26; ## Bit Array: 2 bytes of bit flags for graphic presentation
## The 1st (high order or leftmost) bit specifies
## reflection. If set then reflection across the X-axis
## is applied before rotation. The 14th bit flags
## absolute mag, the 15th absolute angle, the other bits
## were reserved for future use and should be 0.
ANGLE = 28; ## 8-byte Real
UINTEGER = 29; ## UNKNOWN User int, used only in Calma V2.0
USTRING = 30; ## UNKNOWN User string, used only in Calma V2.0
REFLIBS = 31; ## ASCII String
FONTS = 32; ## ASCII String
PATHTYPE = 33; ## 2-byte Signed Integer
GENERATIONS = 34; ## 2-byte Signed Integer
ATTRTABLE = 35; ## ASCII String
STYPTABLE = 36; ## ASCII String "Unreleased feature"
STRTYPE = 37; ## 2-byte Signed Integer "Unreleased feature"
EFLAGS = 38; ## BIT_ARRAY Flags for template and exterior data.
## bits 15 to 0, l to r 0=template, 1=external data, others unused
ELKEY = 39; ## INTEGER_4 "Unreleased feature"
LINKTYPE = 40; ## UNKNOWN "Unreleased feature"
LINKKEYS = 41; ## UNKNOWN "Unreleased feature"
NODETYPE = 42; ## INTEGER_2 Nodetype specification. On Calma this could be 0 to 63,
## GDSII allows 0 to 255. Of course a 16 bit integer allows up to 65535...
PROPATTR = 43; ## INTEGER_2 Property number.
PROPVALUE = 44; ## STRING Property value. On GDSII, 128 characters max, unless an
## SREF, AREF, or NODE, which may have 512 characters.
BOX = 45; ## NO_DATA The beginning of a BOX element.
BOXTYPE = 46; ## INTEGER_2 Boxtype specification.
PLEX = 47; ## INTEGER_4 Plex number and plexhead flag. The least significant bit of
## the most significant byte is the plexhead flag.
BGNEXTN = 48; ## INTEGER_4 Path extension beginning for pathtype 4 in Calma CustomPlus.
## In database units, may be negative.
ENDEXTN = 49; ## INTEGER_4 Path extension end for pathtype 4 in Calma CustomPlus. In
## database units, may be negative.
TAPENUM = 50; ## INTEGER_2 Tape number for multi-reel stream file.
TAPECODE = 51; ## INTEGER_2 Tape code to verify that the reel is from the proper set.
## 12 bytes that are supposed to form a unique tape code.
STRCLASS = 52; ## BIT_ARRAY Calma use only.
RESERVED = 53; ## INTEGER_4 Used to be NUMTYPES per Calma GDSII Stream Format Manual, v6.0.
FORMAT = 54; ## INTEGER_2 Archive or Filtered flag. 0: Archive 1: filtered
MASK = 55; ## STRING Only in filtered streams. Layers and datatypes used for mask
## in a filtered stream file. A string giving ranges of layers and datatypes
## separated by a semicolon. There may be more than one mask in a stream file.
ENDMASKS = 56; ## NO_DATA The end of mask descriptions.
LIBDIRSIZE = 57; ## INTEGER_2 Number of pages in library director, a GDSII thing, it seems
## to have only been used when Calma INFORM was creating a new library.
SRFNAME = 58; ## STRING Calma "Sticks"(c) rule file name.
LIBSECUR = 59; ## INTEGER_2 Access control list stuff for CalmaDOS, ancient. INFORM used
## this when creating a new library. Had 1 to 32 entries with group
## numbers, user numbers and access rights.