GDS2(3)

NAME
GDS2 - GDS2 stream module

SYNOPSIS
This is GDS2, a module for creating programs to read and/or write GDS2 files.

Send feedback/suggestions to perl -le '$_=q(Zpbhgnpe@pvnt.uxa);$_=~tr/n-sa-gt-zh-mZ/a-zS/;print;'

COPYRIGHT
Author: Ken Schumack (c) 1999-2017. All rights reserved. This module is free software. It may be used, redistributed and/or modified under the terms of the Perl Artistic License. ( see http://www.perl.com/pub/a/language/misc/Artistic.html )
 Have fun, Ken

Schumack@cpan.org

DESCRIPTION
GDS2 allows you to read and write GDS2 files record by record in a stream fashion which inherently uses little memory. It is capable but not fast. If you have large files you may be happier using the C/C++ http://sourceforge.net/projects/gds2/ which can easily be used by Perl.

Examples
  Layer change:
    here's a bare bones script to change all layer 59 to 66 given a file to
    read and a new file to create.
    #!/usr/bin/perl -w
    use strict;
    use GDS2;
    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);
        }
        else
        {
            $gds2File2 -> printRecord(-data=>$record);
        }
    }

  Gds2 dump:
    here's a complete program to dump the contents of a stream file.
    #!/usr/bin/perl -w
    use GDS2;
    $\="\n";
    my $gds2File = new GDS2(-fileName=>$ARGV[0]);
    while ($gds2File -> readGds2Record)
    {
        print $gds2File -> returnRecordAsString;
    }

  Gds2 dump in GDT format: which is smaller and easier to parse - http://sourceforge.net/projects/gds2/
    #!/usr/bin/perl -w
    use GDS2;
    my $gds2File = new GDS2(-fileName=>$ARGV[0]);
    while ($gds2File -> readGds2Record)
    {
        print $gds2File -> returnRecordAsString(-compact=>1);
    }

  Dump from the command line of a bzip2 compressed file:
  perl -MGDS2 -MFileHandle -MIPC::Open3 -e '$f1=new FileHandle;$f0=new FileHandle;open3($f0,$f1,$f1,"bzcat test.gds.bz2");$gds=new GDS2(-fileHandle=>$f1);while($gds->readGds2Record){print $gds->returnRecordAsString(-compact=>1)}'

  Create a complete GDS2 stream file from scratch:
    #!/usr/bin/perl -w
    use GDS2;
    my $gds2File = new GDS2(-fileName=>'>test.gds');
    $gds2File -> printInitLib(-name=>'testlib');
    $gds2File -> printBgnstr(-name=>'test');
    $gds2File -> printPath(
                    -layer=>6,
                    -pathType=>0,
                    -width=>2.4,
                    -xy=>[0,0, 10.5,0, 10.5,3.3],
                 );
    $gds2File -> printSref(
                    -name=>'contact',
                    -xy=>[4,5.5],
                 );
    $gds2File -> printAref(
                    -name=>'contact',
                    -columns=>2,
                    -rows=>3,
                    -xy=>[0,0, 10,0, 0,15],
                 );
    $gds2File -> printEndstr;
    $gds2File -> printBgnstr(-name => 'contact');
    $gds2File -> printBoundary(
                    -layer=>10,
                    -xy=>[0,0, 1,0, 1,1, 0,1],
                 );
    $gds2File -> printEndstr;
    $gds2File -> printEndlib();

################################################################################

METHODS

  new - open gds2 file
  usage:
  my $gds2File  = new GDS2(-fileName => "filename.gds2"); ## to read
  my $gds2File2 = new GDS2(-fileName => ">filename.gds2"); ## to write

  -or- provide your own fileHandle:

  my $gds2File  = new GDS2(-fileHandle => $fh); ## e.g. to attach to a compression/decompression pipe

  fileNum - file number...
  usage:

  close - close gds2 file
  usage:
  $gds2File -> close;
   -or-
  $gds2File -> close(-markEnd=>1); ## -- some systems have trouble closing files
  $gds2File -> close(-pad=>2048);  ## -- 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

High Level Write Methods

  printInitLib() - Does all the things needed to start a library, writes HEADER,BGNLIB,LIBNAME,and UNITS records
The default is to create a library with a default unit of 1 micron that has a resolution of 1000. To get this set uUnit to 0.001 (1/1000) and the dbUnit to 1/1000th of a micron (1e-9).
   usage:
     $gds2File -> printInitLib(-name => "testlib", ## required
                        -isoDate => 0|1 ## (optional) use ISO 4 digit date 2001 vs 101
                        -uUnit => real number ## (optional) default is 0.001
                        -dbUnit => real number ## (optional) default is 1e-9
                        );

     ## defaults to current date for library date

   note:
     remember to close library with printEndlib()

  printBgnstr - Does all the things needed to start a structure definition
   usage:
    $gds2File -> printBgnstr(-name => "nand3" ## writes BGNSTR and STRNAME records
                             -isoDate => 1|0  ## (optional) use ISO 4 digit date 2001 vs 101
                             );

   note:
     remember to close with printEndstr()

  printPath - prints a gds2 path
  usage:
    $gds2File -> printPath(
                    -layer=>#,
                    -dataType=>#,     ##optional
                    -pathType=>#,
                    -width=>#.#,
                    -unitWidth=>#,    ## (optional) directly specify width in data base units (vs -width which is multipled by resolution)

                    -xy=>\@array,     ## array of reals
                      # -or-
                    -xyInt=>\@array,  ## array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
                  );

  note:
    layer defaults to 0 if -layer not used
    pathType defaults to 0 if -pathType not used
      pathType 0 = square end
               1 = round end
               2 = square - extended 1/2 width
               4 = custom plus variable path extension...
    width defaults to 0.0 if -width not used

  printBoundary - prints a gds2 boundary
  usage:
    $gds2File -> printBoundary(
                    -layer=>#,
                    -dataType=>#,

                    -xy=>\@array,     ## ref to array of reals
                      # -or-
                    -xyInt=>\@array,  ## ref to array of internal ints (optional -wks better if you are modifying an existing GDS2 file)
                 );

  note:
    layer defaults to 0 if -layer not used
    dataType defaults to 0 if -dataType not used

  printSref - prints a gds2 Structure REFerence
  usage:
    $gds2File -> printSref(
                    -name=>string,   ## Name of structure

                    -xy=>\@array,    ## ref to array of reals
                      # -or-
                    -xyInt=>\@array, ## ref to 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)
                 );

  note:
    best not to specify angle or mag if not needed

  printAref - prints a gds2 Array REFerence
  usage:
    $gds2File -> printAref(
                    -name=>string,   ## Name of structure
                    -columns=>#,     ## Default is 1
                    -rows=>#,        ## Default is 1

                    -xy=>\@array,    ## ref to array of reals
                      # -or-
                    -xyInt=>\@array, ## ref to 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)
                 );

  note:
    best not to specify angle or mag if not needed
    xyList: 1st coord: origin, 2nd coord: X of col * xSpacing + origin, 3rd coord: Y of row * ySpacing + origin

  printText - prints a gds2 Text
  usage:
    $gds2File -> printText(
                    -string=>string,
                    -layer=>#,      ## Default is 0
                    -textType=>#,   ## Default is 0
                    -font=>#,       ## 0-3
                    -top, or -middle, -bottom,     ##optional vertical presentation
                    -left, or -center, or -right,  ##optional horizontal presentation

                    -xy=>\@array,     ## ref to array of reals
                      # -or-
                    -xyInt=>\@array,  ## ref to 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
                 );

  note:
    best not to specify reflect, angle or mag if not needed

Low Level Generic Write Methods

  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

  usage:
    saveGds2Record(
            -type=>string,
            -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
    );

  examples:
    my $gds2File = new GDS2(-fileName => ">$fileName");
    my $record = $gds2File -> saveGds2Record(-type=>'header',-data=>3);
    $gds2FileOut -> printGds2Record(-type=>'record',-data=>$record);

  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.
  usage:
    printGds2Record(
            -type=>string,
            -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
    );

  examples:
    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
  just read in:
    while (my $record = $gds2FileIn -> readGds2Record())
    {
        $gds2FileOut -> printGds2Record(-type=>'record',-data=>$record);
    }

  printRecord - prints a record just read
  usage:
    $gds2File -> printRecord(
                  -data => $record
                );

Low Level Generic Read Methods

  readGds2Record - reads record header and data section
  usage:
  while ($gds2File -> readGds2Record)
  {
      if ($gds2File -> returnRecordTypeString eq 'LAYER')
      {
          $layersFound[$gds2File -> layer] = 1;
      }
  }

  readGds2RecordHeader - only reads gds2 record header section (2 bytes)
  slightly faster if you just want a certain thing...
  usage:
  while ($gds2File -> readGds2RecordHeader)
  {
      if ($gds2File -> returnRecordTypeString eq 'LAYER')
      {
          $gds2File -> readGds2RecordData;
          $layersFound[$gds2File -> returnLayer] = 1;
      }
  }

  readGds2RecordData - only reads record data section
  slightly faster if you just want a certain thing...
  usage:
  while ($gds2File -> readGds2RecordHeader)
  {
      if ($gds2File -> returnRecordTypeString eq 'LAYER')
      {
          $gds2File -> readGds2RecordData;
          $layersFound[$gds2File -> returnLayer] = 1;
      }
  }

Low Level Generic Evaluation Methods

  returnRecordType - returns current (read) record type as integer
  usage:
  if ($gds2File -> returnRecordType == 6)
  {
      print "found STRNAME";
  }

  returnRecordTypeString - returns current (read) record type as string
  usage:
  if ($gds2File -> returnRecordTypeString eq 'LAYER')
  {
      code goes here...
  }

  returnRecordAsString - returns current (read) record as a string
  usage:
  while ($gds2File -> readGds2Record)
  {
      print $gds2File -> returnRecordAsString(-compact=>1);
  }

  returnXyAsArray - returns current (read) XY record as an array
  usage:
    $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.
               );

  example:
  while ($gds2File -> readGds2Record)
  {
      my @xy = $gds2File -> returnXyAsArray if ($gds2File -> isXy);
  }

  returnRecordAsPerl - returns current (read) record as a perl command to facilitate the creation of parameterized gds2 data with perl.
  usage:
  #!/usr/local/bin/perl
  use GDS2;
  my $gds2File = new GDS2(-fileName=>"test.gds");
  while ($gds2File -> readGds2Record)
  {
      print $gds2File -> returnRecordAsPerl;
  }

Low Level Specific Write Methods

  printAngle - prints ANGLE record
  usage:
    $gds2File -> printAngle(-num=>#.#);

  printAttrtable - prints ATTRTABLE record
  usage:
    $gds2File -> printAttrtable(-string=>$string);

  printBgnextn - prints BGNEXTN record
  usage:
    $gds2File -> printBgnextn(-num=>#.#);

  printBgnlib - prints BGNLIB record
  usage:
    $gds2File -> printBgnlib(
                            -isoDate => 0|1 ## (optional) use ISO 4 digit date 2001 vs 101
                           );

  printBox - prints BOX record
  usage:
    $gds2File -> printBox;

  printBoxtype - prints BOXTYPE record
  usage:
    $gds2File -> printBoxtype(-num=>#);

  printColrow - prints COLROW record
  usage:
    $gds2File -> printBoxtype(-columns=>#, -rows=>#);

  printDatatype - prints DATATYPE record
  usage:
    $gds2File -> printDatatype(-num=>#);

  printElkey - prints ELKEY record
  usage:
    $gds2File -> printElkey(-num=>#);

  printEndel - closes an element definition

  printEndextn - prints path end extension record
  usage:
    $gds2File printEndextn -> (-num=>#.#);

  printEndlib - closes a library definition

  printEndstr - closes a structure definition

  printEndmasks - prints a ENDMASKS

  printFonts - prints a FONTS record
  usage:
    $gds2File -> printFonts(-string=>'names_of_font_files');

  printHeader - Prints a rev 3 header
  usage:
    $gds2File -> printHeader(
                  -num => #  ## optional, defaults to 3. valid revs are 0,3,4,5,and 600
                );

  printLayer - prints a LAYER number
  usage:
    $gds2File -> printLayer(
                  -num => #  ## optional, defaults to 0.
                );

  printLibname - Prints library name
  usage:
    printLibname(-name=>$name);

  printPathtype - prints a PATHTYPE number
  usage:
    $gds2File -> printPathtype(
                  -num => #  ## optional, defaults to 0.
                );

  printMag - prints a MAG number
  usage:
    $gds2File -> printMag(
                  -num => #.#  ## optional, defaults to 0.0
                );

  printNodetype - prints a NODETYPE number
  usage:
    $gds2File -> printNodetype(
                  -num => #
                );

  printPresentation - prints a text presentation record
  usage:
    $gds2File -> printPresentation(
                  -font => #,  ##optional, defaults to 0, valid numbers are 0-3
                  -top, ||-middle, || -bottom, ## vertical justification
                  -left, ||-center, || -right, ## horizontal justification
                );

  example:
    gds2File -> printPresentation(-font=>0,-top,-left);

  printPropattr - prints a property id number
  usage:
    $gds2File -> printPropattr( -num => # );

  printPropvalue - prints a property value string
  usage:
    $gds2File -> printPropvalue( -string => $string );

  printSname - prints a SNAME string
  usage:
    $gds2File -> printSname( -name => $cellName );

  printStrans - prints a STRANS record
  usage:
    $gds2File -> printStrans( -reflect );

  printString - prints a STRING record
  usage:
    $gds2File -> printSname( -string => $text );

  printStrname - prints a structure name string
  usage:
    $gds2File -> printStrname( -name => $cellName );

  printTexttype - prints a text type number
  usage:
    $gds2File -> printTexttype( -num => # );

  printUnits - Prints units record.
  options:
    -uUnit   => real number ## (optional) default is 0.001
    -dbUnit  => real number ## (optional) default is 1e-9

  printWidth - prints a width number
  usage:
    $gds2File -> printWidth( -num => # );

  printXy - prints an XY array
  usage:
    $gds2File -> printXy( -xyInt => \@arrayGds2Ints );
    -or-
    $gds2File -> printXy( -xy => \@arrayReals );

    -xyInt most useful if reading and modifying... -xy if creating from scratch

Low Level Specific Evaluation Methods

  returnFilePosition - return current byte position (NOT zero based)
  usage:
    my $position = $gds2File -> returnFilePosition;

  returnBgnextn - returns bgnextn if record is BGNEXTN else returns 0
  usage:

  returnDatatype - returns datatype # if record is DATATYPE else returns -1
  usage:
    $dataTypesFound[$gds2File -> returnDatatype] = 1;

  returnEndextn- returns endextn if record is ENDEXTN else returns 0
  usage:

  returnLayer - returns layer # if record is LAYER else returns -1
  usage:
    $layersFound[$gds2File -> returnLayer] = 1;

  returnPathtype - returns pathtype # if record is PATHTYPE else returns -1
  usage:

  returnPropattr - returns propattr # if record is PROPATTR else returns -1
  usage:

  returnPropvalue - returns propvalue string if record is PROPVALUE else returns ''
  usage:

  returnSname - return string if record type is SNAME else ''

  returnString - return string if record type is STRING else ''

  returnStrname - return string if record type is STRNAME else ''

  returnTexttype - returns texttype # if record is TEXTTYPE else returns -1
  usage:
    $TextTypesFound[$gds2File -> returnTexttype] = 1;

  returnWidth - returns width # if record is WIDTH else returns -1
  usage:

Low Level Specific Boolean Methods

  isAref - return 0 or 1 depending on whether current record is an aref

  isBgnlib - return 0 or 1 depending on whether current record is a bgnlib

  isBgnstr - return 0 or 1 depending on whether current record is a bgnstr

  isBoundary - return 0 or 1 depending on whether current record is a boundary

  isDatatype - return 0 or 1 depending on whether current record is datatype

  isEndlib - return 0 or 1 depending on whether current record is endlib

  isEndel - return 0 or 1 depending on whether current record is endel

  isEndstr - return 0 or 1 depending on whether current record is endstr

  isHeader - return 0 or 1 depending on whether current record is a header

  isLibname - return 0 or 1 depending on whether current record is a libname

  isPath - return 0 or 1 depending on whether current record is a path

  isSref - return 0 or 1 depending on whether current record is an sref

  isSrfname - return 0 or 1 depending on whether current record is an srfname

  isText - return 0 or 1 depending on whether current record is a text

  isUnits - return 0 or 1 depending on whether current record is units

  isLayer - return 0 or 1 depending on whether current record is layer

  isStrname - return 0 or 1 depending on whether current record is strname

  isWidth - return 0 or 1 depending on whether current record is width

  isXy - return 0 or 1 depending on whether current record is xy

  isSname - return 0 or 1 depending on whether current record is sname

  isColrow - return 0 or 1 depending on whether current record is colrow

  isTextnode - return 0 or 1 depending on whether current record is a textnode

  isNode - return 0 or 1 depending on whether current record is a node

  isTexttype - return 0 or 1 depending on whether current record is a texttype

  isPresentation - return 0 or 1 depending on whether current record is a presentation

  isSpacing - return 0 or 1 depending on whether current record is a spacing

  isString - return 0 or 1 depending on whether current record is a string

  isStrans - return 0 or 1 depending on whether current record is a strans

  isMag - return 0 or 1 depending on whether current record is a mag

  isAngle - return 0 or 1 depending on whether current record is a angle

  isUinteger - return 0 or 1 depending on whether current record is a uinteger

  isUstring - return 0 or 1 depending on whether current record is a ustring

  isReflibs - return 0 or 1 depending on whether current record is a reflibs

  isFonts - return 0 or 1 depending on whether current record is a fonts

  isPathtype - return 0 or 1 depending on whether current record is a pathtype

  isGenerations - return 0 or 1 depending on whether current record is a generations

  isAttrtable - return 0 or 1 depending on whether current record is a attrtable

  isStyptable - return 0 or 1 depending on whether current record is a styptable

  isStrtype - return 0 or 1 depending on whether current record is a strtype

  isEflags - return 0 or 1 depending on whether current record is a eflags

  isElkey - return 0 or 1 depending on whether current record is a elkey

  isLinktype - return 0 or 1 depending on whether current record is a linktype

  isLinkkeys - return 0 or 1 depending on whether current record is a linkkeys

  isNodetype - return 0 or 1 depending on whether current record is a nodetype

  isPropattr - return 0 or 1 depending on whether current record is a propattr

  isPropvalue - return 0 or 1 depending on whether current record is a propvalue

  isBox - return 0 or 1 depending on whether current record is a box

  isBoxtype - return 0 or 1 depending on whether current record is a boxtype

  isPlex - return 0 or 1 depending on whether current record is a plex

  isBgnextn - return 0 or 1 depending on whether current record is a bgnextn

  isEndextn - return 0 or 1 depending on whether current record is a endextn

  isTapenum - return 0 or 1 depending on whether current record is a tapenum

  isTapecode - return 0 or 1 depending on whether current record is a tapecode

  isStrclass - return 0 or 1 depending on whether current record is a strclass

  isReserved - return 0 or 1 depending on whether current record is a reserved

  isFormat - return 0 or 1 depending on whether current record is a format

  isMask - return 0 or 1 depending on whether current record is a mask

  isEndmasks - return 0 or 1 depending on whether current record is a endmasks

  isLibdirsize - return 0 or 1 depending on whether current record is a libdirsize

  isLibsecur - return 0 or 1 depending on whether current record is a libsecur

  recordSize - return current record size
  usage:
    my $len = $gds2File -> recordSize;

  dataSize - return current record size - 4 (length of data)
  usage:
    my $dataLen = $gds2File -> dataSize;

  returnUnitsAsArray - return user units and database units as a 2 element array
  usage:
    my ($uu,$dbu) = $gds2File -> returnUnitsAsArray;

  version - return GDS2 module version string

  version - return GDS2 module revision string

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
 # space between:
 #     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.
 #
 # DATA TYPE        VALUE  RECORD
 # ---------        -----  -----------------------
 # 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.
 #                        I.E.
 #                        0x0001 = 1
 #                        0x0002 = 2
 #                        0x0089 = 137
 #                        0xffff = -1
 #                        0xfffe = -2
 #                        0xff77 = -137
 #
 # 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
 #                        byte count++
 #
 #########################################################################################

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 #
 #                     STRING                                                   #
 #                                                                              #
 #  <strans>::=        STRANS [MAG] [ANGLE]                                     #
 #                                                                              #
 #  <node>::=          NODE [ELFLAGS] [PLEX] LAYER NODETYPE XY                  #
 #                                                                              #
 #  <box>::=           BOX [ELFLAGS] [PLEX] LAYER BOXTYPE XY                    #
 #                                                                              #
 #  <property>::=      PROPATTR PROPVALUE                                       #
 ################################################################################

GDS2 Stream Record Datatypes
 ################################################################################
 NO_REC_DATA   =  0;
 BIT_ARRAY     =  1;
 INTEGER_2     =  2;
 INTEGER_4     =  3;
 REAL_4        =  4; ## NOT supported, never really used
 REAL_8        =  5;
 ACSII_STRING  =  6;
 ################################################################################

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.
 MAG           = 27;   ## 8-byte Real
 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.