A Lute Rose | AJR Format

#
JDR Binary Format

Jpgfdraw's native
file format is a binary format written in the big-endian fashion. Since all
binary data files are platform independent, there should be no problems transferring the files between processorsA.1. Integers are stored as 32-bit integers, single precision numbers are stored as 32-bit floats, double precision numbers are stored as 64-bit doubles and characters are stored as 16-bit Unicode characters. (For more details see [2, Chapter 12] or `http://java.sun.com/j2se/1.5.0/docs/api/java/io/DataInput.html`)

The current JDR file format version is 1.6.

If you use the `uk.ac.uea.cmp.nlct.jdr` package, you can load and save a
file using the `JDR.load()` and `JDR.save()` methods, otherwise the
file format is as follows:

1. To read a data stream in
   (where `filename` is a string containing the file name):

       DataInputStream din =
          new DataInputStream(new FileInputStream(filename));

   or to write a data stream:

       DataOutputStream dout =
          new DataOutputStream(new FileOutputStream(filename));

2. At the start of the file, there must be the three characters `JDR` stored as 16-bit Unicode characters. To write:

       dout.writeChars("JDR");

   To read:

       char[] str = new char[3];
       for (int i = 0; i < 3; i++) str[i] = din.readChar();
       if (!(new String(str)).equals("JDR"))
       {
          // not a JDR file error code
       }

3. The
   file format version comes next. This may be 1.0, 1.1, 1.2, 1.3, 1.4, 1.5 or 1.6. The file version number is stored as a string not a number. First the length of the string (`fileVersion`) is stored, then follows the string itself. To write:

       dout.writeInt(fileVersion.length())
       dout.writeChars(fileVersion)

   To read:

       int n = din.readInt();
       char[] version = new char[n];
       for (int i = 0; i < n; i++) version[i] = din.readChar();

4. Next is a value indicating whether or not the Jpgfdraw |
   | are stored.

   JDR1.3 onwards
   In version 1.3 onwards, this value is a byte, and may take one of three values: 0 (no settings), 1 (all settings) or 2 (paper size only). To omit the settings information:
       dout.writeByte((byte)0);
   To save all the settings: `dout.writeByte((byte)1); // code to save the settings (see below)` To save only the paper size: `dout.writeByte((byte)2); // code to save the paper size (see below)` To read: `switch (din.readByte()) { case 0: // do nothing break; case 1: // read settings (see below) break; case 2: // read paper size (see below) break; default // throw exception }`
   JDR1.3 onwards

   JDR1.0-1.2
   In versions prior to 1.3, this value is a boolean value. To save the settings: `dout.writeBoolean(true); // code to save the settings (see below)` To omit the settings information:
       dout.writeBoolean(false);
   To read: `if (din.readBoolean()) { // read settings (see below) }`
   JDR1.0-1.2

5. The settings information is stored as follows:
   a
     A boolean variable (`grid`) indicating whether or not to display the grid. To write:

         dout.writeBoolean(grid);

     To read:

         boolean grid = din.readBoolean();

   b
     A boolean variable (`gridLock`) indicating whether or not to lock the grid. To write:

         dout.writeBoolean(gridLock);

     To read:

         boolean gridLock = din.readBoolean();

   c
     A boolean variable (`showRulers`) indicating whether or not to show the
     . To write:

         dout.writeBoolean(showRulers);

     To read:

         boolean showRulers = din.readBoolean();

   d
     A 32-bit integer indicating which tool to select. This must be an integer between 0 and 7 (inclusive). Table A.1 indicates the integer ID for each tool. To write (where `tool` is an integer):

         dout.writeInt(tool);

     To read:

         int tool = din.readInt();
         if (tool < 0 || tool > 7)
         {
            // insert invalid tool id error
         }

     Table A.1: Tool Identifiers
     ID | Tool
     --- | ---
     0 | Select
     1 | Open Line Path
     2 | Closed Line Path
     3 | Open Curve Path
     4 | Closed Curve Path
     5 | Rectangle
     6 | Ellipse
     7 | Text

   e
     A 32-bit integer indicating the normal font size. (This is used as the default in the font settings dialog box, and as the normal size font for the LaTeX font size conversions.) To write:

         dout.writeInt(normalSize);

     To read:

         int normalSize = din.readInt();

   f
     The paper size (see below).

   g
     The grid style:

     JDR1.0-1.5
     1. An 8-bit byte representing the unit used for the rulers and grid. This should be one of: 0 ( ), 1 (inches), 2 (centimetres) or 3 ( ). To write:
            dout.writeByte(unitType);
        To read:
            byte unitType = din.readByte();
     2. Two 32-bit integers representing the major grid divisions and the subdivisions, respectively. To write:
            dout.writeInt(majorDivisions);
            dout.writeInt(subdivisions);
        To read:
            int majorDivisions = din.readInt();
            int subdivisions = din.readInt();
     JDR1.0-1.5

     JDR1.6 onwards
     An 8-bit byte representing the grid style ID. This may be: 0 A rectangular grid. This is then followed by: An 8-bit byte representing the unit ID (as above). A 64-bit double representing the major grid division. A 32-bit integer representing the grid subdivision. 1 A radial grid. This is then followed by: An 8-bit byte representing the unit ID (as above). A 64-bit double representing the major grid division. A 32-bit integer representing the grid subdivision. A 32-bit integer representing the number of spokes.
     JDR1.6 onwards

6. The paper size is specified as an 8-bit byte. For versions before 1.3, this must be an integer in the range 0 to 18 (inclusive), otherwise it must be in the range 0 to 72 (inclusive). Table A.2 indicates the integer ID for each paper size, and Table A.3 shows additional values for version 1.3. If the paper size has an ID of 18 (user defined), then there must follow the paper width (64-bit double in points), height (64-bit double in points). For versions prior to 1.3, the user defined setting must also be followed by a boolean variable to indicate whether or not the orientation is portrait (`true`) or landscape (`false`).

   JDR1.0-1.2
   To write:
       dout.writeByte(paperSize);
       if (paperSize == 18) // user defined paper size
       {
          dout.writeDouble(paperWidth);
          dout.writeDouble(paperHeight);
          dout.writeBoolean(isPortrait);
       }
   To read:
       byte paperSize = din.readByte();
       if (paperSize < 0 || paperSize > 18)
       {
          // insert invalid paper size id code
       }
       else if (paperSize == 18) // user defined paper size
       {
          double paperWidth  = din.readDouble();
          double paperHeight = din.readDouble();
          boolean isPortrait = din.readBoolean();
       }
   JDR1.0-1.2

   JDR1.3 onwards
   To write:
       dout.writeByte(paperSize);
       if (paperSize == 18) // user defined paper size
       {
          dout.writeDouble(paperWidth);
          dout.writeDouble(paperHeight);
       }
   To read:
       byte paperSize = din.readByte();
       if (paperSize < 0 || paperSize > 72)
       {
          // insert invalid paper size id code
       }
       else if (paperSize == 18) // user defined paper size
       {
          double paperWidth  = din.readDouble();
          double paperHeight = din.readDouble();
       }
   JDR1.3 onwards

   Table A.2: Paper Size Identifiers
   ID | Paper Size | ID | Paper Size
   --- | --- | --- | ---
   0 | A0 (portrait) | 9 | A0 (landscape)
   1 | A1 (portrait) | 10 | A1 (landscape)
   2 | A2 (portrait) | 11 | A2 (landscape)
   3 | A3 (portrait) | 12 | A3 (landscape)
   4 | A4 (portrait) | 13 | A4 (landscape)
   5 | A5 (portrait) | 14 | A5 (landscape)
   6 | letter (portrait) | 15 | letter (landscape)
   7 | legal (portrait) | 16 | legal (landscape)
   8 | executive (portrait) | 17 | executive (landscape)
   18 | user defined |  |

   Table A.3: Additional Paper Size Identifiers (JDR v1.3 onwards)
   ID | Paper Size | ID | Paper Size
   --- | --- | --- | ---
   19 | A6 (portrait) | 46 | A6 (landscape)
   20 | A7 (portrait) | 47 | A7 (landscape)
   21 | A8 (portrait) | 48 | A8 (landscape)
   22 | A9 (portrait) | 49 | A9 (landscape)
   23 | A10 (portrait) | 50 | A10 (landscape)
   24 | B0 (portrait) | 51 | B0 (landscape)
   25 | B1 (portrait) | 52 | B1 (landscape)
   26 | B2 (portrait) | 53 | B2 (landscape)
   27 | B3 (portrait) | 54 | B3 (landscape)
   28 | B4 (portrait) | 55 | B4 (landscape)
   29 | B5 (portrait) | 56 | B5 (landscape)
   30 | B6 (portrait) | 57 | B6 (landscape)
   31 | B7 (portrait) | 58 | B7 (landscape)
   32 | B8 (portrait) | 59 | B8 (landscape)
   33 | B9 (portrait) | 60 | B9 (landscape)
   34 | B10 (portrait) | 61 | B10 (landscape)
   35 | C0 (portrait) | 62 | C0 (landscape)
   36 | C1 (portrait) | 63 | C1 (landscape)
   37 | C2 (portrait) | 64 | C2 (landscape)
   38 | C3 (portrait) | 65 | C3 (landscape)
   39 | C4 (portrait) | 66 | C4 (landscape)
   40 | C5 (portrait) | 67 | C5 (landscape)
   41 | C6 (portrait) | 68 | C6 (landscape)
   42 | C7 (portrait) | 69 | C7 (landscape)
   43 | C8 (portrait) | 70 | C8 (landscape)
   44 | C9 (portrait) | 71 | C9 (landscape)
   45 | C10 (portrait) | 72 | C10 (landscape)

7. The
   that constitute the picture are now stored. When saving to a file, an outer grouping is implied that is not evident whilst using Jpgfdraw. This means that there should always be a single group structure saved to file which contains all the
   that constitute the picture. Each
   is then recursively stored. For example, if a picture contains a
   , a
   and a
   , in the
   file these three objects will be stored as a single group structure containing the three objects. If in Jpgfdraw you explicitly
   all the objects, then in the
   file, the outermost implicit group will contain only one
   which will be this
   .

   Each
   has the following format:

   JDR1.0 & 1.1
   <id-char><object-specs><fflag>[<flowframe-specs>]
   JDR1.0 & 1.1

   JDR1.2 onwards
   <id-char><object-specs><fflag>[<flowframe-specs>]<description-specs>
   JDR1.2 onwards

   where <id-char> is a character determining the object type:

   JDR1.0-1.4
   `G`-- ; `P`-- ; `T`-- ; `I`-- .
   JDR1.0-1.4

   JDR1.5
   As versions 1.0-1.4. Additionally: `X`--
   JDR1.5

   JDR1.5
   As versions 1.5. Additionally: `R`-- ; `C`-- ; `L`-- .
   JDR1.5

   The object specifications <object-specs> vary according to the object type and are described below. <fflag> is a boolean variable indicating whether or not this object has flowframe data associated with it. If true, then the flowframe specifications <flowframe-specs> should follow (see below), otherwise <flowframe-specs> should be omitted. Note that JDR version 1.2 and above contains <description-specs>, which was omitted in earlier versions. To write:

   `if (/* test to see if object is a group */)
   {
   dout.writeChar('G');
   // save group specification (see below)
   }
   else if (/* test to see if object is a path */)
   {
   dout.writeChar('P');
   // save path specification (see below)
   }
   else if (/* test to see if object is a text area */)
   {
   dout.writeChar('T');
   // save text area specification (see below)
   }
   else if (/* test to see if object is a bitmap */)
   {
   dout.writeChar('I');
   // save bitmap specification (see below)
   }
   else if (/* test if object is text-path and version > 1.4 */)
   {
   dout.writeChar('X');
   // save text-path specification (see below)
   }
   else if (/* test if object is rotational-pattern and version > 1.5 */)
   {
   dout.writeChar('R');
   // save rotational-pattern specification (see below)
   }
   else if (/* test if object is scaled-pattern and version > 1.5 */)
   {
   dout.writeChar('C');
   // save scaled-pattern specification (see below)
   }
   else if (/* test if object is spiral-pattern and version > 1.5 */)
   {
   dout.writeChar('L');
   // save spiral-pattern specification (see below)
   }
   // boolean fflag indicates object has flow frame data
   dout.writeBoolean(fflag);
   if (fflag)
   {
   // save flow frame data (see below)
   }
   // if version 1.2 or above save description (see below)
   `

   To read:

   `char c = din.readChar();
   if (c == 'G')
   {
   // read group data (see below)
   }
   else if (c == 'P')
   {
   // read path data (see below)
   }
   else if (c == 'T')
   {
   // read text area data (see below)
   }
   else if (c == 'I')
   {
   // read bitmap data (see below)
   }
   else if (c == 'X')
   {
   // read text-path data (see below)
   }
   else if (c == 'R')
   {
   // read rotational-pattern data (see below)
   }
   else if (c == 'C')
   {
   // read scaled-pattern data (see below)
   }
   else if (c == 'L')
   {
   // read spiral-pattern data (see below)
   }
   else
   {
   // insert invalid object id code
   }
   if (din.readBoolean())
   {
   // read flow frame data (see below)
   }
   // if version 1.2 or above read description (see below)
   `

   1. Group data, `G`, is stored as follows:
      <n><object data>+
      where <n> is an integer indicating the number of
      within the
      , there should then follow <n> lots of <object data>, where <object data> is the data for each
      within the group, where the object data is as described above. To write:

      `// int n is the number of objects in the group
      dout.writeInt(n);
      for (int i = 0; i < n; i++)
      {
      // save the ith object in the group (see above)
      }
      `

      To read:

      `int n = din.readInt();
      for (int i = 0; i < n; i++)
      {
      // read in the ith object in the group (see above)
      }
      `

   2. Path data, `P`, is stored as follows:

      JDR1.0-1.2
      <line color><fill color><line style>`O`|`C`<n><segment data>+
      JDR1.0-1.2

      JDR1.3 onwards
      <line color><fill color><line style>`O`|`C`<n><start point><segment data>+
      JDR1.3 onwards

      where <line color> and <fill color> contain the line and fill color data (see below), <line style> is the line style data (see below). The character `O` or `C` indicates whether the path is open or closed, <n> is an integer indicating the number of segments that constitute the path. This should be followed by <n> lots of <segment data> (described below). Version 1.3 has removed the redundancy present in earlier versions.

      JDR1.0-1.2
      To write: `// save line color data (see below) // save fill color data (see below) // save line style data (see below) // boolean closed indicates whether or not the path // is closed dout.writeChar(closed ? 'C' : 'O'); // int n is the number of segments in the path dout.writeInt(n); for (int i = 0; i < n; i++) { // save data for segment i (see below) }` To read: `// read line color data (see below) // read fill color data (see below) // read line style data (see below) char c = din.readChar(); if (c == 'O') { // make it an open path } else if (c == 'C') { // make it a closed path } else { // insert invalid identifier code } int n = din.readInt(); for (int i = 0; i < n; i++) { // read data for segment i (see below) }`
      JDR1.0-1.2

      JDR1.3 onwards
      JDR v1.3 onwards requires that the starting point <start point> follows the number of segments (<n>). The starting point is stored as two double precision numbers. To write: `// save line color data (see below) // save fill color data (see below) // save line style data (see below) // boolean closed indicates whether or not the path // is closed dout.writeChar(closed ? 'C' : 'O'); // int n is the number of segments in the path dout.writeInt(n); // double x, y is the starting position of the path dout.writeDouble(x); dout.writeDouble(y); for (int i = 0; i < n; i++) { // save data for segment i (see below) }` To read: `// read line color data (see below) // read fill color data (see below) // read line style data (see below) char c = din.readChar(); if (c == 'O') { // make it an open path } else if (c == 'C') { // make it a closed path } else { // insert invalid identifier code } int n = din.readInt(); double x = din.readDouble(); double y = din.readDouble(); for (int i = 0; i < n; i++) { // read data for segment i (see below) }`
      JDR1.3 onwards

      1. Color data is stored as follows: <col-id>[<color-specs>], where <col-id> is a character representing the color type. Available types are listed in Table A.4. Note that if <col-id> is `T` (transparent) <color-specs> is omitted.

         Table A.4: Available Color Types
         Type | ID | Version
         --- | --- | ---
         Transparent | `T` | 1.0 onwards
         RGB | `R` | 1.0 onwards
         CMYK | `C` | 1.0 onwards
         Linear Gradient | `G` | 1.0 onwards
         Radial Gradient | `D` | 1.3 onwards
         Gray | `Y` | 1.4 onwards
         HSB | `S` | 1.4 onwards

         To write

         `if (/* test if transparent */)
         {
         dout.writeChar('T');
         }
         else if (/* test if single RGB color */)
         {
         dout.writeChar('R');
         // save single RGB color data (see below)
         }
         else if (/* test if single CMYK color */)
         {
         dout.writeChar('C');
         // save single CMYK color data (see below)
         }
         else if (/* test if linear gradient color */)
         {
         dout.writeChar('G');
         // save linear gradient color data (see below)
         }
         else if (/* test if radial and JDR version >= 1.3 */)
         {
         dout.writeChar('D');
         // save radial gradient color data (see below)
         }
         else if (/* test if HSB color and JDR version >= 1.4 */)
         {
         dout.writeChar('S');
         // save HSB color data (see below)
         }
         else if (/* test if gray and JDR version >= 1.4 */)
         {
         dout.writeChar('Y');
         // save gray data (see below)
         }
         `

         To read:

         `char c = din.readChar();
         if (c == 'T')
         {
         // set to transparent
         }
         else if (c == 'R')
         {
         // read single RGB color data (see below)
         }
         else if (c == 'C')
         {
         // read single CMKY color data (see below)
         }
         else if (c == 'G')
         {
         // read linear gradient color data (see below)
         }
         else if (c == 'D' /* and JDR version >= 1.3 */)
         {
         // read radial gradient color data (see below)
         }
         else if (c == 'S' /* and JDR version >= 1.4 */)
         {
         // read HSB color data (see below)
         }
         else if (c == 'Y' /* and JDR version >= 1.4 */)
         {
         // read gray data (see below)
         }
         else
         {
         // insert invalid color identifier code
         }
         `

         1. Single RGB color data is specified as:
            <R><G><B><A>
            where each element is a 32-bit single precision floating point number between 0 and 1 (inclusive), and <R> represents the red value, <G> represents the green value, <B> represents the blue value, and <A> represents the alpha (transparency) value. To write:

                dout.writeFloat(red);
                dout.writeFloat(green);
                dout.writeFloat(blue);
                dout.writeFloat(alpha);

            To read:

                float red   = din.readFloat();
                // check lies in range [0,1]
                float green = din.readFloat();
                // check lies in range [0,1]
                float blue  = din.readFloat();
                // check lies in range [0,1]
                float alpha = din.readFloat();
                // check lies in range [0,1]

         2. Single CMYK color data is specified as:
            <C><M><Y><K><A>
            where each element is a 32-bit floating point value between 0 and 1 (inclusive), and <C> represents the cyan value, <M> represents the magenta value, <Y> represents the yellow value, <K> represents the black value, and <A> represents the alpha (transparency) value. To write:

                dout.writeFloat(cyan);
                dout.writeFloat(magenta);
                dout.writeFloat(yellow);
                dout.writeFloat(black);
                dout.writeFloat(alpha);

            To read:

                float cyan   = din.readFloat();
                // check lies in range [0,1]
                float magenta = din.readFloat();
                // check lies in range [0,1]
                float yellow  = din.readFloat();
                // check lies in range [0,1]
                float black  = din.readFloat();
                // check lies in range [0,1]
                float alpha = din.readFloat();
                // check lies in range [0,1]

         3. As from version 1.4, HSB color data is specified as:
            <H><S><B><A>
            where each element is a 32-bit single precision floating point number and all except <H> lie in the range 0-1 (inclusive). <H> represents the hue value in the range [0,360), <S> represents the saturation value, <B> represents the brightness value, and <A> represents the alpha (transparency) value. To write:

                dout.writeFloat(hue);
                dout.writeFloat(saturation);
                dout.writeFloat(brightness);
                dout.writeFloat(alpha);

            To read:

                float hue         = din.readFloat();
                // check lies in range [0,360)
                float saturation  = din.readFloat();
                // check lies in range [0,1]
                float brightness  = din.readFloat();
                // check lies in range [0,1]
                float alpha       = din.readFloat();
                // check lies in range [0,1]

         4. As from version 1.4, gray data is specified as:
            <G><A>
            where each element is a 32-bit single precision floating point number between 0 and 1 (inclusive), and <G> represents the gray value, and <A> represents the alpha (transparency) value. To write:

                dout.writeFloat(grey);
                dout.writeFloat(alpha);

            To read:

                float grey   = din.readFloat();
                // check lies in range [0,1]
                float alpha = din.readFloat();
                // check lies in range [0,1]

         5. Linear gradient color data is specified as:
            <start-col-id><start-col-specs><end-col-id><end-col-specs><direction>
            where <start-col-id> is the color identifier for the starting color and <start-col-specs> is the color specification, and <end-col-id> is the color identifier for the end color and <end-col-specs> is the color specification. The color identifiers may be any of those listed in Table A.4 except the linear or radial gradient types. The color specifications are as described above. The gradient direction, <direction>, is a 32-bit integer and may only take one of the following values: 0 (North), 1 (North East), 2 (East), 3 (South East), 4 (South), 5 (South West), 6 (West) and 7 (North West). To write:

            `if (/* start color RGB */)
            {
            dout.writeChar('R');
            // save RGB single color data (see above)
            }
            else if (/* start color CMYK */)
            {
            dout.writeChar('C');
            // save CMYK single color data (see above)
            }
            else if (/* start color HSB and version >= 1.4 */)
            {
            dout.writeChar('S');
            // save HSB single color data (see above)
            }
            else if (/* start color gray and version >= 1.4 */)
            {
            dout.writeChar('Y');
            // save HSB single gray data (see above)
            }
            else
            {
            // insert invalid color type code
            }
            if (/* end color RGB */)
            {
            dout.writeChar('R');
            // save RGB single color data (see above)
            }
            else if (/* end color CMYK */)
            {
            dout.writeChar('C');
            // save CMYK single color data (see above)
            }
            else if (/* end color HSB and version >= 1.4 */)
            {
            dout.writeChar('S');
            // save HSB single color data (see above)
            }
            else if (/* end color gray and version >= 1.4 */)
            {
            dout.writeChar('Y');
            // save HSB single gray data (see above)
            }
            else
            {
            // insert invalid color type code
            }
            dout.writeInt(direction);
            `

            To read:

            `char c = din.readChar();
            if (c == 'R')
            {
            // read RGB single color data (see above)
            }
            else if (c == 'C')
            {
            // read CMYK single color data (see above)
            }
            else if (c == 'S')
            {
            // read HSB single color data (see above)
            }
            else if (c == 'Y')
            {
            // read gray data (see above)
            }
            else
            {
            // insert invalid start color identifier code
            }
            c = din.readChar();
            if (c == 'R')
            {
            // read RGB single color data (see above)
            }
            else if (c == 'C')
            {
            // read CMYK single color data (see above)
            }
            else if (c == 'S')
            {
            // read HSB single color data (see above)
            }
            else if (c == 'Y')
            {
            // read gray data (see above)
            }
            else
            {
            // insert invalid end color identifier code
            }
            int direction = din.readInt();
            // check direction is in range [0,7]
            `

         6. Radial gradient color data is not available for versions prior to JDR v1.3. The radial color data is specified as:
            <start-col-id><start-col-specs><end-col-id><end-col-specs><start location>
            where <start-col-id> is the color identifier for the starting color and <start-col-specs> is the color specification, and <end-col-id> is the color identifier for the end color and <end-col-specs> is the color specification. The color identifiers may be any of those listed in Table A.4 except the linear or radial gradient types. The color specifications are as described above. The starting location, <start location>, is a 32-bit integer and may only take one of the following values: 0 (North), 1 (North East), 2 (East), 3 (South East), 4 (South), 5 (South West), 6 (West), 7 (North West) and 8 (Center). To write:

            `if (/* start color RGB */)
            {
            dout.writeChar('R');
            // save RGB single color data (see above)
            }
            else if (/* start color CMYK */)
            {
            dout.writeChar('C');
            // save CMYK single color data (see above)
            }
            else if (/* start color HSB and version >= 1.4 */)
            {
            dout.writeChar('S');
            // save HSB single color data (see above)
            }
            else if (/* start color gray and version >= 1.4 */)
            {
            dout.writeChar('Y');
            // save HSB single gray data (see above)
            }
            else
            {
            // insert invalid color type code
            }
            if (/* end color RGB */)
            {
            dout.writeChar('R');
            // save RGB single color data (see above)
            }
            else if (/* end color CMYK */)
            {
            dout.writeChar('C');
            // save CMYK single color data (see above)
            }
            else if (/* end color HSB and version >= 1.4 */)
            {
            dout.writeChar('S');
            // save HSB single color data (see above)
            }
            else if (/* end color gray and version >= 1.4 */)
            {
            dout.writeChar('Y');
            // save HSB single gray data (see above)
            }
            else
            {
            // insert invalid color type code
            }
            dout.writeInt(startLocation);
            `

            To read:

            `char c = din.readChar();
            if (c == 'R')
            {
            // read RGB single color data (see above)
            }
            else if (c == 'C')
            {
            // read CMYK single color data (see above)
            }
            else if (c == 'S')
            {
            // read HSB single color data (see above)
            }
            else if (c == 'Y')
            {
            // read gray data (see above)
            }
            else
            {
            // insert invalid start color identifier code
            }
            c = din.readChar();
            if (c == 'R')
            {
            // read RGB single color data (see above)
            }
            else if (c == 'C')
            {
            // read CMYK single color data (see above)
            }
            else if (c == 'S')
            {
            // read HSB single color data (see above)
            }
            else if (c == 'Y')
            {
            // read gray data (see above)
            }
            else
            {
            // insert invalid end color identifier code
            }
            int startLocation = din.readInt();
            // check startLocation is in range [0,8]
            `

      2. The line style data has changed from file version 1.0 to 1.1 to take into account the inclusion of mid point markers, and is stored as follows:

         JDR1.0
         <linewidth><dash><cap><join>[<mitre-limit>]<winding><start arrow><end arrow>
         JDR1.0

         JDR1.1 and above
         <linewidth><dash><cap><join>[<mitre-limit>]<winding><start arrow><mid marker><end arrow>
         JDR1.1 and above

         where:
         1. <linewidth> the line width (in points) stored as a 32-bit floating point number. To write:

                dout.writeFloat(linewidth);

            To read:

                float linewidth = din.readFloat();
                // check linewidth isn't negative

         2. <dash> is the dash pattern. This is stored as:
            <n>[<pattern>+<offset>]
            where <n> is 0 if there is no dash pattern (i.e. a solid line) or the number of patterns. There should be an even number of patterns, the odd numbered patterns represent the dash length, the even number of patterns represent the dash gap. The patterns should be stored as a 32-bit floating point number (in points). Lastly, the offset should be a 32-bit float (in points). Note that if <n> is 0, there should be no <pattern> or <offset>. To write:

                dout.writeInt(n);
                for (int i = 0; i < n; i++)
                {
                   dout.writeFloat(pattern[i]);
                }
                if (n > 0) dout.writeFloat(offset);

            To read:

                int n = din.readInt();
                if (n < 0)
                {
                   // insert invalid pattern length code
                }
                else if (n > 0)
                {
                   float[] pattern = new float[n];
                   for (int i = 0; i < n; i++)
                   {
                      pattern[i] = din.readFloat();
                   }
                   float offset = din.readFloat();
                }
                else
                {
                   // solid line
                }

         3. <cap> is the cap style, this is an 8-bit byte. It may only have one of the following values: 0 (butt), 1 (round) or 2 (square). To write:

                dout.writeByte(cap);

            To read:

                byte cap = din.readByte();
                // check cap is in the range [0,2]

         4. <join> is the join style, this is an 8-bit byte. It may only have one of the following values: 0 (mitre), 1 (round) or 2 (bevel). To write:

                dout.writeByte(join);

            To read:

                byte join = din.readByte();
                // check join is in the range [0,2]

         5. <mitre-limit> is the mitre-limit, this is a 32-bit float, and should only be stored if the join style is a mitre. To write:

                if (join == 0)
                {
                   dout.writeFloat(mitreLimit);
                }

            To read:

                if (join == 0)
                {
                   float mitreLimit = din.readFloat();
                }

         6. <winding> is the winding rule, this is an 8-bit byte. It may only have one of the following values: 0 (Even-Odd) or 1 (Non Zero). To write:

                dout.writeByte(windingRule);

            To read:

                byte windingRule = din.readByte();
                // check it's either 0 or 1

         7. <start arrow> and <end arrow> are the starting and ending arrow styles. The <mid marker> is the style for the mid-point markers. Each marker type (start/mid/end) has the same format, but the file format varies as follows:

            JDR1.0
            <id>[<size><is double><is reversed>] where <id> is an 8-bit byte identifying the arrow type. This may be one of: 0 (none), 1 (pointed), 2 (triangle), 3 (circle), 4 (diamond), 5 (square), 6 (bar) or 7 (single). <size> is 32-bit float representing the arrow size. (Some arrows only have a fixed size, but a size must still be present.) <is double> is a boolean value indicating whether the arrow head is a double arrow (<true>) or a single arrow (<false>). <is reversed> is a boolean value indicating whether the arrow head has been reversed. The values <size><is double><is reversed> are omitted if <id> equals 0 (no arrow head). To write:
                dout.writeByte(arrowType);
                if (arrowType != 0)
                {
                   dout.writeFloat(arrowSize);
                   dout.writeBoolean(arrowDouble);
                   dout.writeBoolean(arrowReversed);
                }
            To read:
                byte arrowType = din.readByte();
                // omitted code to check arrowType is in range [0,7]
                if (arrowType != 0)
                {
                   float arrowSize = din.readFloat();
                   boolean arrowDouble = din.readBoolean();
                   boolean arrowReversed = din.readBoolean();
                }
            JDR1.0

            JDR1.1-1.3
            <id>[<marker data>] where <id> is an 8-bit byte identifying the marker type. If <id> is 0, then <marker data> should be omitted, otherwise it should be present. Valid <id> values are listed in Table A.5. To write:
                dout.writeByte(markerType);
            To read:
                byte markerType = din.readByte();
                // omitted code to check markerType has valid value
                if (markerType != 0)
                {
                   // read in marker data
                }
            - The <marker data> is stored as follows: <size><repeat><is reversed><orient data><color data><overlay><composite data> where: <size> is a 32-bit float representing the marker size (some markers will ignore this attribute, but it must still be present in the file.) To write:
                  dout.writeFloat(markerSize);
              To read:
                  float markerSize = din.readFloat();
            - <repeat> is an 8-bit byte identifying the repeat factor (a value of 1 indicates a single marker, a value of 2 indicates a double marker, a value of 3 indicates a triple marker.) To write:
                  dout.writeByte(markerRepeat);
              To read:
                  byte markerRepeat = din.readByte();
                  // check lies in range [1-3]
            - <is reversed> is a boolean value indicating whether or not the marker has been reversed. To write:
                  dout.writeBoolean(markerReversed);
              To read:
                  boolean markerReversed = din.readBoolean();
            - <orient data> is the marker orientation data. This has the form <auto-orient>[<angle>] where <auto-orient> is a boolean value indicating whether the marker should be oriented along the path. If <auto-orient> is true, <angle> should be omitted, otherwise <angle> should be a float representing the orientation angle (in Radians). To write:
                  dout.writeBoolean(autoOrient);
                  if (!autoOrient) dout.writeFloat(angle);
              To read:
                  boolean autoOrient = din.readBoolean();
                  float angle = 0.0f;
                  if (!autoOrient) angle = din.readFloat();
            - <color data> is the marker color. This has the same form as the line/fill/text color data defined earlier, except a transparent value indicates the color should be derived from the path to which the marker is attached, and there is no provision for gradient paint markers. <overlay> is a boolean value indicating whether to overlay composite markers. To write:
                  dout.writeBoolean(overlay);
              To read:
                  boolean overlay = din.readBoolean();
            <composite data> is the data for composite markers. This has the same format as the <marker data>. If the <composite data> has a marker id of 0, then the marker is not a composite marker. Although the format allows for nested composite markers, Jpgfdraw's marker settings dialog boxes do not allow for it.
            Table A.5: Marker IDs
            0 | No marker | 11 | Box Filled
            1 | Pointed | 12 | Box Open
            2 | Triangle | 13 | Cross
            3 | Circle | 14 | Plus
            4 | Diamond | 15 | Star
            5 | Square bracket | 16 | Triangle Up Filled
            6 | Bar | 17 | Triangle Up Open
            7 | Single | 18 | Triangle Down Filled
            8 | Round bracket | 19 | Triangle Down Open
            9 | Dot Filled | 20 | Rhombus Filled
            10 | Dot Open | 21 | Rhombus Open
            JDR1.1-1.3

            JDR1.4 onwards
            Table A.6: Additional Marker IDs (JDR 1.4)
            22 | Pentagon Filled | 41 | Half Cusp Down | 60 | Open Semicircle
            23 | Pentagon Open | 42 | Alt Single | 61 | Filled Semicircle
            24 | Hexagon Filled | 43 | Alt Single Open | 62 | Open 5 Pointed star
            25 | Hexagon Open | 44 | Triangle Open | 63 | Filled 5 Pointed star
            26 | Octagon Filled | 45 | Circle Open | 64 | Asterisk
            27 | Octagon Open | 46 | Diamond Open | 65 | Scissors Down Filled
            28 | Pointed 60 | 47 | Brace | 66 | Scissors Up Filled
            29 | Pointed 45 | 48 | Rectangle Cap | 67 | Scissors Down Open
            30 | Hooks | 49 | Chevron Cap | 68 | Scissors Up Open
            31 | Hook up | 50 | Fast Cap | 69 | Heart Right Filled
            32 | Hook Down | 51 | Round Cap | 70 | Heart Right Open
            33 | Half Pointed Up | 52 | Triangle Cap | 71 | Heart Filled
            34 | Half Pointed Down | 53 | Inverted Triangle Cap | 72 | Heart Open
            35 | Half Pointed 60 Up | 54 | Inverted Chevron Cap | 73 | Snowflake
            36 | Half Pointed 60 Down | 55 | Inverted Fast Cap | 74 | Star Chevron Open
            37 | Half Pointed 45 Up | 56 | Alt Bar | 75 | Star Chevron Filled
            38 | Half Pointed 45 Down | 57 | Alt Round | 76 | Star 6 Filled
            39 | Cusp | 58 | Alt Square | 77 | Star 6 Open
            40 | Half Cusp Up | 59 | Alt Brace | 78 | Equilateral Filled
            |  |  |  | 79 | Equilateral Open
            Table A.7: Additional Marker IDs (JDR 1.6)
            80 | Ball Cap | 85 | Forward Triple Leaf Cap
            81 | Leaf Cap | 86 | Back Triple Leaf Cap
            82 | Double Leaf Cap | 87 | Forward Double Leaf Cap
            83 | Triple Leaf Cap | 88 | Back Double Leaf Cap
            84 | Club Cap | 89 | Cutout Bulge Cap
            <id> [<marker data> ] where <id> is an 8-bit byte identifying the marker type. If <id> is 0, then <marker data> should be omitted, otherwise it should be present. Valid <id> values are listed in Table A.5 and Table A.6. Additional markers listed in Table A.7 are also available for version 1.6 onwards. To write:
                dout.writeByte(markerType);
            To read:
                byte markerType = din.readByte();
                // omitted code to check markerType has valid value
                if (markerType != 0)
                {
                   // read in marker data
                }
            The <marker data> is stored as follows: <size> <repeat> <is reversed> <orient data> <color data> <overlay> [<user offset flag> [<user offset>] <repeat offset flag> [<repeat offset>]] <composite data> where: <user offset flag> [<user offset>] <repeat offset flag> [<repeat offset>] are only specified if <overlay> is false. <user offset> and <repeat offset> are only specified if <user offset flag> or <repeat offset flag> are true, respectively. The remaining values are as for JDR versions 1.1-1.3 described above. <user offset flag> is a boolean value indicating whether the marker offset is specified by the user (true) or determined automatically (false). <user offset> is a 32-bit float indicating the marker offset from the vertex. <repeat offset flag> is a number indicating whether the repeat offset (i.e. gap between repeat markers) is specified by the user (true) or determined automatically (false). <repeat offset> is a 32-bit float indicating the gap between repeat markers. To write:
                if (overlay)
                {
                   dout.writeBoolean(userOffsetFlag);

                   if (userOffsetFlag)
                   {
                      dout.writeFloat(userOffset);
                   }

                   dout.writeBoolean(repeatOffsetFlag);

                   if (repeatOffsetFlag)
                   {
                      dout.writeFloat(repeatOffset);
                   }
                }
            To read:
                if (overlay)
                {
                   boolean userOffsetFlag = din.readBoolean();

                   if (userOffsetFlag)
                   {
                      float userOffset = din.readFloat();
                   }

                   boolean repeatOffsetFlag = din.readBoolean();

                   if (repeatOffsetFlag)
                   {
                      float repeatOffset = din.readFloat();
                   }
                }
            JDR1.4 onwards

      3. Segments are stored as follows:
         <id><specs>
         where <id> is a character representing the segment type. This can be one of: `B` (cubic Bézier), `L` (line) or `M` (move). To write:

         `if (/* test if Bézier */)
         {
         dout.writeChar('B');
         // save Bézier data (see below)
         }
         else if (/* test if line */)
         {
         dout.writeChar('L');
         //save line data (see below)
         }
         else
         {
         dout.writeChar('M');
         // save move to data (see below)
         }
         `

         To read:

         `char c = din.readChar();
         if (c == 'B')
         {
         // read Bézier data (see below)
         }
         else if (c == 'L')
         {
         // read line data (see below)
         }
         else if (c == 'M')
         {
         // read move to data (see below)
         }
         else
         {
         // insert invalid segment identifier code
         }
         `

         1. Bézier segments are stored as follows:

            JDR1.0-1.2
            <c0x><c0y><c1x><c1y><c2x><c2y><c3x><c3y>
            JDR1.0-1.2

            JDR1.3 onwards
            <c1x><c1y><c2x><c2y><c3x><c3y>
            JDR1.3 onwards

            where <c0x> and <c0y> are the x and y co-ordinates of the starting point, <c1x> and <c1y> are the x and y co-ordinates of the first curvature control point, <c2x> and <c2y> are the x and y co-ordinates of the second curvature control point, and <c3x> and <c3y> are the x and y co-ordinates of the end point. All values are stored as 64-bit doubles. To write:

            JDR1.0-1.2
                for (int i = 0; i < 4; i++)
                {
                   dout.writeDouble(x[i]);
                   dout.writeDouble(y[i]);
                }
            To read:
                double[] x = new double[4];
                double[] y = new double[4];
                for (int i = 0; i < 4; i++)
                {
                   x[i] = din.readDouble();
                   y[i] = din.readDouble();
                }
            JDR1.0-1.2

            JDR1.3 onwards
                for (int i = 0; i < 3; i++)
                {
                   dout.writeDouble(x[i]);
                   dout.writeDouble(y[i]);
                }
            To read:
                double[] x = new double[3];
                double[] y = new double[3];
                for (int i = 0; i < 3; i++)
                {
                   x[i] = din.readDouble();
                   y[i] = din.readDouble();
                }
            JDR1.3 onwards

         2. Line and move to (gap) segments are stored as follows:

            JDR1.0-1.2
            <x0><y0><x1><y1>
            JDR1.0-1.2

            JDR1.3 onwards
            <x1><y1>
            JDR1.3 onwards

            where <x0> and <y0> are the x and y co-ordinates of the starting point and <x1> and <y1> are the x and y co-ordinates of the end point.

            JDR1.0-1.2
            To write:
                for (int i = 0; i < 2; i++)
                {
                   dout.writeDouble(x[i]);
                   dout.writeDouble(y[i]);
                }
            To read:
                double[] x = new double[2];
                double[] y = new double[2];
                for (int i = 0; i < 2; i++)
                {
                   x[i] = din.readDouble();
                   y[i] = din.readDouble();
                }
            JDR1.0-1.2

            JDR1.3 onwards
            To write:
                dout.writeDouble(x1);
                dout.writeDouble(y1);
            To read:
                x1 = din.readDouble();
                y1 = din.readDouble();
            JDR1.3 onwards

   3. data is stored as follows:
      <fam-length><family><shape><series><size><transformation><latex-flag>[<latex-specs>]<text color><text-length><text>
      where:
      1. <fam-length> is a 32-bit integer that is the length of the font family name, and <family> is the font family name. To write:

             // String family contains the name of the font family
             dout.writeInt(family.length());
             dout.writeChars(family);

         To read:

             int n = din.readInt();
             char[] fam = new char[n];
             for (int i = 0; i < n; i++)
             {
                fam[i] = din.readChar();
             }
             String family = new String(fam);

      2. <shape> is an 8-bit byte representing the font shape. This can have one of two values: 0 (upright) or 1 (italic). To write:

             dout.writeByte(shape);

         to read:

             byte shape = din.readByte();
             // check shape is either 0 or 1

      3. <series> is an 8-bit byte representing the font series. This can have one of two values: 0 (medium) or 1 (bold). To write:

             dout.writeByte(series);

         To read:

             byte series = din.readByte();
             // check series is either 0 or 1

      4. <size> is the font size stored as a 32-bit integer. To write:

             dout.writeInt(size);

         To read:

             int size = din.readInt();
             // check size is not negative

      5. <transformation> is the transformation. The origin is taken to be the leftmost point along the baseline of the text. The transformation is stored as:
         <m0><m1><m2><m3><m4><m5>
         where each element is stored as a 64-bit double precision number. The transformation matrix used is given by:

         To write:

             for (int i = 0; i < 6; i++)
             {
                dout.writeDouble(matrix[i]);
             }

         To read:

             double[] matrix = new double[6];
             for (int i = 0; i < 6; i++)
             {
                matrix[i] = din.readDouble();
             }

      6. <latex-flag> is a boolean variable indicating whether or not the <latex-specs> are present. To write:

         `dout.writeBoolean(latexFlag)
         if (latexFlag)
         {
         // save LaTeX specs (see below)
         }
         `

         To read:

         `boolean latexFlag = din.readBoolean();
         if (latexFlag)
         {
         // read LaTeX specs (see below)
         }
         `

      7. <latex-specs> contains the LaTeX information, and has the format:
         <lfam-length>[<lfamily>]<lseries-length>[<lseries>]<lshape-length>[<lshape>]<lsize-length>[<lsize>]<halign><valign><ltext-length>[<ltext>]
         where:
         1. <lfam-length> is an integer indicating the number of characters in <lfamily> where <lfamily> is a string containing the LaTeX family declaration (e.g. `\rmfamily`). If <lfam-length> is zero, <lfamily> is omitted. (<lfam-length> must not be negative.) To write:

                // String lfamily contains the LaTeX family declaration
                dout.writeInt(lfamily.length());
                dout.writeChars(lfamily);

            To read:

                int n = din.readInt();
                if (n < 0)
                {
                   // insert code to throw error if n is negative
                }
                else if (n > 0)
                {
                   char[] fam = new char[n];
                   for (int i = 0; i < n; i++)
                   {
                      fam[i] = din.readChar();
                   }
                   String lfamily = new String(fam);
                }

         2. <lseries-length> is an integer indicating the number of characters in <lseries> where <lseries> is a string containing the LaTeX series declaration (e.g. `\bfseries`). If <lseries-length> is zero, <lseries> is omitted. (<lseries-length> must not be negative.) To write:

                // String lseries contains the LaTeX series declaration
                dout.writeInt(lseries.length());
                dout.writeChars(lseries);

            To read:

                int n = din.readInt();
                if (n < 0)
                {
                   // insert code to throw error if n is negative
                }
                else if (n > 0)
                {
                   char[] str = new char[n];
                   for (int i = 0; i < n; i++)
                   {
                      str[i] = din.readChar();
                   }
                   String lseries = new String(str);
                }

         3. <lshape-length> is an integer indicating the number of characters in <lshape> where <lshape> is a string containing the LaTeX shape declaration (e.g. `\itshape`). If <lshape-length> is zero, <lshape> is omitted. (<lshape-length> must not be negative.) To write:

                // String lshape contains the LaTeX shape declaration
                dout.writeInt(lshape.length());
                dout.writeChars(lshape);

            To read:

                int n = din.readInt();
                if (n < 0)
                {
                   // insert code to throw error if n is negative
                }
                else if (n > 0)
                {
                   char[] str = new char[n];
                   for (int i = 0; i < n; i++)
                   {
                      str[i] = din.readChar();
                   }
                   String lshape = new String(str);
                }

         4. <lsize-length> is an integer indicating the number of characters in <lsize> where <lsize> is a string containing the LaTeX size declaration (e.g. `\large`). If <lsize-length> is zero, <lsize> is omitted. (<lsize-length> must not be negative.) To write:

                // String lsize contains the LaTeX size declaration
                dout.writeInt(lsize.length());
                dout.writeChars(lsize);

            To read:

                int n = din.readInt();
                if (n < 0)
                {
                   // insert code to throw error if n is negative
                }
                else if (n > 0)
                {
                   char[] str = new char[n];
                   for (int i = 0; i < n; i++)
                   {
                      str[i] = din.readChar();
                   }
                   String lsize = new String(str);
                }

         5. <halign> is an 8-bit byte indicating the horizontal alignment of the `\pgfbox` command. It may only take one of the following values: 0 (left), 1 (center) or 2 (right). To write:

                dout.writeByte(halign);

            To read:

                byte halign = din.readByte();
                if (halign < 0 || halign > 2)
                {
                   // insert code to throw invalid halign exception
                }

         6. <valign> is an 8-bit byte indicating the vertical alignment of the `\pgfbox` command. It may only take one of the following values: 0 (top), 1 (center), 2 (base) or 3 (bottom). To write:

                dout.writeByte(valign);

            To read:

                byte valign = din.readByte();
                if (valign < 0 || valign > 3)
                {
                   // insert code to throw invalid valign exception
                }

         7. <ltext-length> is an integer indicating the number of characters in <ltext> where <ltext> is a string containing the LaTeX alternative to <text>. If <ltext-length> is zero, <ltext> is omitted. (<ltext-length> must not be negative.) To write:

                // String ltext contains the alternative LaTeX text
                dout.writeInt(ltext.length());
                dout.writeChars(ltext);

            To read:

                int n = din.readInt();
                if (n < 0)
                {
                   // insert code to throw error if n is negative
                }
                else if (n > 0)
                {
                   char[] str = new char[n];
                   for (int i = 0; i < n; i++)
                   {
                      str[i] = din.readChar();
                   }
                   String ltext = new String(str);
                }

      8. <text color> is the text color. This has the same format as the path line and fill colors described above.

      9. <text length> is the number of characters contained in the
         (stored as a 32-bit integer) and <text> are the characters contained in the
         . <text length> must be strictly positive. To write:

             // String text contains the text
             dout.writeInt(text.length());
             dout.writeChars(text);

         To read:

             int n = din.readInt();
             if (n <= 0)
             {
                // insert code to throw invalid length exception
             }
             char[] str = new char[n];
             for (int i = 0; i < n; i++)
             {
                str[i] = din.readChar();
             }
             String text = new String(str);

   4. are not available for versions below 1.5. For newer versions, the specifications are stored as follows:
      <text color><fam-length><family><shape><series><size><transformation><latex-flag>[<latex-specs>]<text-length><text>`O`|`C`<n><start point><segment data>+
      where the text information (<text color> to <text>) is as described above for
      . The remaining information is as described above for
      .

   5. are not available for versions below 1.6. For newer versions, the specifications are stored as follows:
      <shape-specs><anchor-x><anchor-y><angle><replicas><mode><show>
      where:
      1. <shape-specs> are the underlying object's specifications as described above. (Bitmap and text specifications not permitted.)

      2. <anchor-x> is a 64-bit double representing the x-coordinate of the anchor point.

      3. <anchor-y> is a 64-bit double representing the y-coordinate of the anchor point.

      4. <angle> is a 64-bit double representing the angle of rotation.

      5. <replicas> is a 32-bit integer representing the number of replicas.

      6. <mode> is a boolean variable, true if single-path mode.

      7. <show> is a boolean variable, true if the underlying path is visible.

   6. are not available for versions below 1.6. For newer versions, the specifications are stored as follows:
      <shape-specs><anchor-x><anchor-y><adjust-x><adjust-y><scale-x><scale-y><replicas><mode><show>
      where <shape-specs>, <anchor-x>, <anchor-y>, <replicas>, <mode> and <show> are as above. Additionally:
      1. <adjust-x> is a 64-bit double representing the x-coordinate of the adjust control point.

      2. <adjust-y> is a 64-bit double representing the y-coordinate of the adjust control point.

      3. <scale-x> is a 64-bit double representing the x-scale factor.

      4. <scale-y> is a 64-bit double representing the y-scale factor.

   7. are not available for versions below 1.6. For newer versions, the specifications are stored as follows:
      <shape-specs><anchor-x><anchor-y><adjust-x><adjust-y><angle><distance><replicas><mode><show>
      where <shape-specs>, <anchor-x>, <anchor-y>, <adjust-x>, <adjust-y>, <replicas>, <mode> and <show> are as above. Additionally:
      1. <angle> is a 64-bit double representing the spiral angle parameter.

      2. <distance> is a 64-bit double representing the spiral distance parameter.

   8. Bitmap data are stored as follows:
      <filename-length><filename><latex-flag>[<latex-bitmap-specs>]<transformation>
      where:
      1. <filename-length> is an integer indicating the number of characters in the file name, and <filename> is the file name. Note that <filename-length> must be strictly positive. To write:

             // String filename contains the file name
             dout.writeInt(filename.length());
             dout.writeChars(filename);

         To read:

             int n = din.readInt();
             if (n <= 0)
             {
                // insert code to throw exception
             }
             char[] str = new char[n];
             for (int i = 0; i < n; i++)
             {
                str[i] = din.readChar();
             }

      2. <latex-flag> is a boolean variable indicating whether or not the <latex-bitmap-specs> is present. To write:

         `dout.writeBooleanlatexFlag;
         if (latexFlag)
         {
         // save LaTeX bitmap information (see below)
         }
         `

         To read:

         `boolean latexFlag = din.readBoolean();
         if (latexFlag)
         {
         // read LaTeX bitmap information (see below)
         }
         `

      3. <latex-bitmaps-specs> has the following format:
         <lfilename-length>[<lfilename>]<imgcmd-length>[<imgcmd>]
         where <lfilename-length> is an integer indicating the number of characters in <lfilename>, and <lfilename> is the LaTeX link to the bitmap file. If <lfilename-length> is zero, <lfilename> is omitted. <imgcmd-length> is an integer indicating the number of characters in <imgcmd>, where <imgcmd> is a string containing the LaTeX command name to include the bitmap (e.g. `\pgfimage`.) If <imgcmd-length> is zero, <imgcmd> is omitted. To write

             // String lfilename contains the LaTeX link to the bitmap
             dout.writeInt(lfilename.length());
             dout.writeChars(lfilename);
             // String imgcmd contains the LaTeX image command
             dout.writeInt(imgcmd.length());
             dout.writeChars(imgcmd);

         To read:

             int n = din.readInt();
             if (n < 0)
             {
                // insert code to throw invalid length exception
             }
             else if (n > 0)
             {
                char[] str = new char[n];
                for (int i = 0; i < n; i++)
                {
                   str[i] = din.readChar();
                }
                String lfilename = new String(str);
             }
             n = din.readInt();
             if (n < 0)
             {
                // insert code to throw invalid length exception
             }
             else if (n > 0)
             {
                char[] cmd = new char[n];
                for (int i = 0; i < n; i++)
                {
                   cmd[i] = din.readChar();
                }
                String imgcmd = new String(cmd);
             }

      4. <transformation> is the transformation matrix, and has the same format as the
         transformation matrix (see above.) The origin is the bottom left corner of the
         .

8. Flow frame data is stored as follows:
   1. The frame type is stored as an 8-bit byte. This may only take one of the following values: 0 (static), 1 (flow), 2 (dynamic) and 3 (typeblock). There should only be one typeblock and this should belong to the outermost implicit group. To write:

          dout.writeByte(type)

      To read:

          byte type = din.readByte();
          // check valid value

   2. If <type> is not equal to 3 (i.e. is not the typeblock), the following information should also be saved: a boolean value (`border`) indicating whether or not the frame should have a border, the identification label (`label`) stored as an integer (the number of characters in `label`) followed by that many characters, and the page list (`pages`) should likewise be stored as an integer (the number of characters in `pages`) followed by that many characters. To write:

          if (type != 3)
          {
             dout.writeBoolean(border);
             dout.writeInt(label.length());
             dout.writeChars(label);
             dout.writeInt(pages.length());
             dout.writeChars(pages);
          }

      To read:

          if (type != 3)
          {
             boolean border = din.readBoolean();
             int n = din.readInt();
             // throw exception if n < 0
             String label = "";
             char[] c;
             if (n > 0)
             {
                c = new char[n];
                for (int i = 0; i < n; i++)
                {
                   c[i] = din.readChar();
                }
                label = new String(c);
             }
             n = din.readInt();
             // throw exception if n < 0
             String pages = "";
             if (n > 0)
             {
                c = new char[n];
                for (int i = 0; i < n; i++)
                {
                   c[i] = din.readChar();
                }
                pages = new String(c);
             }
          }

   3. Store margin information as a 32-bit single precision floating point number, in the following order: top, bottom, left, right. To write:

          dout.writeFloat(top);
          dout.writeFloat(bottom);
          dout.writeFloat(left);
          dout.writeFloat(right);

      To read:

          float top    = din.readFloat();
          float bottom = din.readFloat();
          float left   = din.readFloat();
          float right  = din.readFloat();

   4. JDR v1.2 and above contains extra information if the frame type is either 0 (static frame) or 2 (dynamic frame) which relates to the paragraph shape. This is a byte that can be 0 (standard shape), 1 (use `\parshape`) or 2 (use `\shapepar`). To write:

          if (type == 0 || type == 2)
          {
             dout.writeByte(shape);
          }

      To read

          if (type == 0 || type == 2)
          {
             byte shape = din.readByte();
          }

   5. JDR v1.3 onwards contains additional information if the frame type is either 0 (static frame) or 2 (dynamic frame) which relates to the vertical alignment of material in the frame. This is a byte that can be 0 (top), 1 (center) or 2 (bottom). To write:

          if (type == 0 || type == 2)
          {
             dout.writeByte(valign);
          }

      To read

          if (type == 0 || type == 2)
          {
             byte valign = din.readByte();
          }

9. JDR v1.2 and above also optionally contains the object's description. This is saved in the form:
   <desc-length>[<description>]
   where <desc-length> is an integer indicating the length of the description string. This may be zero, in which case <description> is omitted, otherwise <description> is a sequence of <desc-length> characters that make up the description. To write:

       // description is a String
       int n = description.length();
       dout.writeInt(n);
       if (n > 0)
       {
          dout.writeChars(description);
       }

   To read:

       int n = din.readInt();
       String description = "";
       if (n > 0)
       {
          char[] desc = new char[n];
          for (int i = 0; i < n; i++)
          {
             desc[i] = din.readChar();
          }
          description = new String(desc);
       }

#### Footnotes

... processors A.1
  although some fonts may not be available, and links to bitmaps may be unresolved

A Lute Rose | AJR Format