Copyright (c) Hyperion Entertainment and contributors.
DR2D IFF 2-D Objects
2-D Object standard format
FORM DR2D
Description by Ross Cunniff and John Orr
A standard IFF FORM to describe 2D drawings has been sorely needed for a long time. Several commercial drawing packages have been available for some time but none has established its file format as the Amiga standard. The absence of a 2D drawing standard hinders the development of applications that use 2D drawings as it forces each application to understand several private standards instead of a single one. Without a standard, data exchange for both the developer and the user is difficult, if not impossible.
The DR2D FORM fills this void. This FORM was developed by Taliesin, Inc. for use as the native file format for their two-dimensional structured drawing package, ProVector. Saxon Industries and Soft Logik Publishing Corporation are planning to support this new FORM in the near future.
Many of the values stored in the DR2D FORM are stored as IEEE single precision floating point numbers. These numbers consist of 32 bits, arranged as follows:
_______________________________________________________________________ | s e e e e e e e | e m m m m m m m | m m m m m m m m | m m m m m m m m | ----------------------------------------------------------------------- 31 24 23 16 15 8 7 0
where:
s is the sign of the number where 1 is negative and 0 is positive. e is the 8 bit exponent in excess 127 form. This number is the power of two to which the mantissa is raised (Excess 127 form means that 127 is added to the exponent before packing it into the IEEE number.) m is the 23 bit mantissa. It ranges from 1.0000000 to 1.999999..., where the leading base-ten one is assumed.
An IEEE single precision with the value of 0.0000000 has all its bits cleared.
The DR2D Chunks
FORM
FORM (0x464F524D) /* All drawings are a FORM */
struct FORMstruct { ULONG ID; /* DR2D */ ULONG Size; };
DR2D
DR2D (0x44523244) /* ID of 2D drawing */
The DR2D chunks are broken up into three groups: the global drawing attribute chunks, the object attribute chunks, and the object chunks. The global drawing attribute chunks describe elements of a 2D drawing that are common to many objects in the drawing. Document preferences, palette information, and custom fill patterns are typical document-wide settings defined in global drawing attribute chunks. The object attribute chunks are used to set certain properties of the object chunk(s) that follows the object attribute chunk. The current fill pattern, dash pattern, and line color are all set using an object attribute chunk. Object chunks describe the actual DR2D drawing. Polygons, text, and bitmaps are found in these chunks.
The Global Drawing Attribute Chunks
The following chunks describe global attributes of a DR2D document.
DRHD
DRHD (0x44524844) /* Drawing header */
The DRHD chunk contains the upper left and lower right extremes of the document in (X, Y) coordinates. This chunk is required and should only appear once in a document in the outermost layer of the DR2D file (DR2Ds can be nested).
struct DRHDstruct { ULONG ID; ULONG Size; /* Always 16 */ IEEE XLeft, YTop, XRight, YBot; };
The point (XLeft,YTop) is the upper left corner of the project and the point (XRight,YBot) is its lower right corner. These coordinates not only supply the size and position of the document in a coordinate system, they also supply the project's orientation. If XLeft < XRight, the X-axis increases toward the right. If YTop < YBot, the Y-axis increases toward the bottom. Other combinations are possible; for example in Cartesian coordinates, XLeft would be less than XRight but YTop would be greater than YBot.
PPRF
PPRF (0x50505249) /* Page preferences */
The PPRF chunk contains preference settings for ProVector. Although this chunk is not required, its use is encouraged because it contains some important environment information.
struct PPRFstruct { ULONG ID; ULONG Size; char Prefs[Size]; };
DR2D stores preferences as a concatenation of several null-terminated strings, in the Prefs[] array. The strings can appear in any order. The currently supported strings are:
Units=<unit-type> Portrait=<boolean> PageType=<page-type> GridSize=<number>
where:
<unit-type> is either Inch, Cm, or Pica <boolean> is either True or False <page-type> is either Standard, Legal, B4, B5, A3, A4, A5, or Custom <number> is a floating-point number
The DR2D FORM does not require this chunk to explicitly state all the possible preferences. In the absence of any particular preference string, a DR2D reader should fall back on the default value. The defaults are:
Units=Inch Portrait=True PageType=Standard GridSize=1.0
CMAP
CMAP (0x434D4150) /* Color map (Same as ILBM CMAP) */
This chunk is identical to the ILBM CMAP chunk as described in the IFF ILBM documentation.
struct CMAPstruct { ULONG ID; ULONG Size; UBYTE ColorMap[Size]; };
ColorMap is an array of 24-bit RGB color values. The 24-bit value is spread across three bytes, the first of which contains the red intensity, the next contains the green intensity, and the third contains the blue intensity. Because DR2D stores its colors with 24-bit accuracy, DR2D readers must not make the mistake that some ILBM readers do in assuming the CMAP chunk colors correspond directly to Amiga color registers.
FONS
FONS (0x464F4E53) /* Font chunk (Same as FTXT FONS chunk) */
The FONS chunk contains information about a font used in the DR2D FORM. ProVector does not include support for Amiga fonts. Instead, ProVector uses fonts defined in the OFNT FORM which is documented later in this article.
struct FONSstruct { ULONG ID; ULONG Size; UBYTE FontID; /* ID the font is referenced by */ UBYTE Pad1; /* Always 0 */ UBYTE Proportional; /* Is it proportional? */ UBYTE Serif; /* does it have serifs? */ CHAR Name[Size-4]; /* The name of the font */ };
The UBYTE FontID field is the number DR2D assigns to this font. References to this font by other DR2D chunks are made using this number.
The Proportional and Serif fields indicate properties of this font. Specifically, Proportional indicates if this font is proportional, and Serif indicates if this font has serifs. These two options were created to allow for font substitution in case the specified font is not available. They are set according to these values:
0 The DR2D writer didn't know if this font is proportional/has serifs. 1 No, this font is not proportional/does not have serifs. 2 Yes, this font is proportional/does have serifs.
The last field, Name[], is a NULL terminated string containing the name of the font.
DASH
DASH (0x44415348) /* Line dash pattern for edges */
This chunk describes the on-off dash pattern associated with a line.
struct DASHstruct { ULONG ID; ULONG Size; USHORT DashID; /* ID of the dash pattern */ USHORT NumDashes; /* Should always be even */ IEEE Dashes[NumDashes]; /* On-off pattern */ };
DashID is the number assigned to this specific dash pattern. References to this dash pattern by other DR2D chunks are made using this number.
The Dashes[] array contains the actual dash pattern. The first number in the array (element 0) is the length of the "on" portion of the pattern. The second number (element 1) specifies the "off" portion of the pattern. If there are more entries in the Dashes array, the pattern will continue. Even-index elements specify the length of an "on" span, while odd-index elements specify the length of an "off" span. There must be an even number of entries. These lengths are not in the same units as specified in the PPRF chunk, but are multiples of the line width, so a line of width 2.5 and a dash pattern of 1.0, 2.0 would have an "on" span of length 1.0 x 2.5 = 2.5 followed by an "off" span of length 2.0 x 2.5 = 5. The following figure shows several dash pattern examples. Notice that for lines longer than the dash pattern, the pattern repeats.
By convention, DashID 0 is reserved to mean "No line pattern at all", i.e. the edges are invisible. This DASH pattern should not be defined by a DR2D DASH chunk. Again by convention, a NumDashes of 0 means that the line is solid.
AROW
AROW (0x41524F57) /* An arrow-head pattern */
The AROW chunk describes an arrowhead pattern. DR2D open polygons (OPLY) can have arrowheads attached to their endpoints. See the description of the OPLY chunk later in this article for more information on the OPLY chunk.
#define ARROW_FIRST 0x01 /* Draw an arrow on the OPLY's first point */ #define ARROW_LAST 0x02 /* Draw an arrow on the OPLY's last point */ struct AROWstruct { ULONG ID; ULONG Size; UBYTE Flags; /* Flags, from ARROW_*, above */ UBYTE Pad0; /* Should always 0 */ USHORT ArrowID; /* Name of the arrow head */ USHORT NumPoints; IEEE ArrowPoints[NumPoints*2]; };
The Flags field specifies which end(s) of an OPLY to place an arrowhead based on the #defines above. ArrowID is the number by which an OPLY will reference this arrowhead pattern.
The coordinates in the array ArrowPoints[] define the arrowhead's shape. These points form a closed polygon. See the section on the OPLY/CPLY object chunks for a descriptionof how DR2D defines shapes. The arrowhead is drawn in the same coordinate system relative to the endpoint of the OPLY the arrowhead is attached to. The arrowhead's origin (0,0) coincides with the OPLY's endpoint. DR2D assumes that the arrowhead represented in the AROW chunk is pointing to the right so the proper rotation can be applied to the arrowhead. The arrow is filled according to the current fill pattern set in the ATTR object attribute chunk.
FILL
FILL (0x46494C4C) /* Object-oriented fill pattern */
The FILL chunk defines a fill pattern. This chunk is only valid inside nested DR2D FORMs. The GRUP object chunk section of this article contans an example of the FILL chunk.
struct FILLstruct { ULONG ID; ULONG Size; USHORT FillID; /* ID of the fill */ };
FillID is the number by which the ATTR object attribute chunk references fill patterns. The FILL chunk must be the first chunk inside a nested DR2D FORM. A FILL is followed by one DR2D object plus any of the object attribute chunks (ATTR, BBOX) associated with the object.
DR2D makes a "tile" out of the fill pattern, giving it a virtual bounding box based on the extreme X and Y values of the FILL's object (Figure A). The bounding box shown in Figure A surrounding the pattern (the two ellipses) is invisible to the user. In concept, this rectangle is pasted on the page left to right, top to bottom like floor tiles (Figure B). Again, the bounding boxes are not visible. The only portion of this tiled pattern that is visible is the part that overlaps the object (Figure C) being filled. The object's path is called a clipping path, as it "clips" its shape from the tiled pattern (Figure D). Note that the fill is only masked on top of underlying objects, so any "holes" in the pattern will act as a window, leaving visible underlying objects.
LAYR
LAYR (0x4C415952) /* Define a layer */
A DR2D project is broken up into one or more layers. Each DR2D object is in one of these layers. Layers provide several useful features. Any particular layer can be "turned off", so that the objects in the layer are not displayed. This eliminates the unnecessary display of objects not currently needed on the screen. Also, the user can lock a layer to protect the layer's objects from accidental changes.
struct LAYRstruct { ULONG ID; ULONG Size; USHORT LayerID; /* ID of the layer */ char LayerName[16]; /* Null terminated and padded */ UBYTE Flags; /* Flags, from LF_*, below */ UBYTE Pad0; /* Always 0 */ };
LayerID is the number assigned to this layer. As the field's name indicates, LayerName[] is the NULL terminated name of the layer. Flags is a bit field who's bits are set according to the #defines below:
#define LF_ACTIVE 0x01 /* Active for editing */ #define LF_DISPLAYED 0x02 /* Displayed on the screen */
If the LF_ACTIVE bit is set, this layer is unlocked. A set LF_DISPLAYED bit indicates that this layer is currently visible on the screen. A cleared LF_DISPLAYED bit implies that LF_ACTIVE is not set. The reason for this is to keep the user from accidentally editing layers that are invisible.
The Object Attribute Chunks
ATTR
ATTR (0x41545452) /* Object attributes */
The ATTR chunk sets various attributes for the objects that follow it. The attributes stay in effect until the next ATTR changes the attributes, or the enclosing FORM ends, whichever comes first.
/* Various fill types */ #define FT_NONE 0 /* No fill */ #define FT_COLOR 1 /* Fill with color from palette */ #define FT_OBJECTS 2 /* Fill with tiled objects */ struct ATTRstruct { ULONG ID; ULONG Size; UBYTE FillType; /* One of FT_*, above */ UBYTE JoinType; /* One of JT_*, below */ UBYTE DashPattern; /* ID of edge dash pattern */ UBYTE ArrowHead; /* ID of arrowhead to use */ USHORT FillValue; /* Color or object with which to fill */ USHORT EdgeValue; /* Edge color index */ USHORT WhichLayer; /* ID of layer it's in */ IEEE EdgeThick; /* Line width */ };
FillType specifies what kind of fill to use on this ATTR chunk's objects. A value of FT_NONE means that this ATTR chunk's objects are not filled. FT_COLOR indicates that the objects should be filled in with a color. That color's ID (from the CMAP chunk) is stored in the FillValue field. If FillType is equal to FT_OBJECTS, FillValue contains the ID of a fill pattern defined in a FILL chunk.
JoinType determines which style of line join to use when connecting the edges of line segments. The field contains one of these four values:
/* Join types */ #define JT_NONE 0 /* Don't do line joins */ #define JT_MITER 1 /* Mitered join */ #define JT_BEVEL 2 /* Beveled join */ #define JT_ROUND 3 /* Round join */
DashPattern and ArrowHead contain the ID of the dash pattern and arrow head for this ATTR's objects. A DashPattern of zero means that there is no dash pattern so lines will be invisible. If ArrowHead is 0, OPLYs have no arrow head. EdgeValue is the color of the line segments. WhichLayer contains the ID of the layer this ATTR's objects are in. EdgeThick is the width of this ATTR's line segments.
BBOX
BBOX (0x42424F48) /* Bounding box of next object in FORM */
The BBOX chunk supplies the dimensions and position of a bounding box
surrounding the DR2D object that follows this chunk in the FORM. A
BBOX chunk can apply to a FILL or AROW as well as a DR2D object. The
BBOX chunk appears just before its DR2D object, FILL, or AROW chunk.
struct BBOXstruct { ULONG ID; ULONG Size; IEEE XMin, YMin, /* Bounding box of obj. */ XMax, YMax; /* including line width */ };
In a Cartesian coordinate system, the point (XMin, YMin) is the coordinate of the lower left hand corner of the bounding box and (XMax, YMax) is the upper right. These coordinates take into consideration the width of the lines making up the bounding box.
XTRN
XTRN (0x5854524E) /* Externally controlled object */
The XTRN chunk was created primarily to allow ProVector to link DR2D objects to ARexx functions.
struct XTRNstruct { ULONG ID; ULONG Size; short ApplCallBacks; /* From #defines, below */ short ApplNameLength; char ApplName[ApplNameLength]; /* Name of ARexx func to call */ };
ApplName[] contains the name of the ARexx script ProVector calls when the user manipulates the object in some way. The ApplCallBacks field specifies the particular action that triggers calling the ARexx script according to the #defines listed below.
/* Flags for ARexx script callbacks */ #define X_CLONE 0x0001 /* The object has been cloned */ #define X_MOVE 0x0002 /* The object has been moved */ #define X_ROTATE 0x0004 /* The object has been rotated */ #define X_RESIZE 0x0008 /* The object has been resized */ #define X_CHANGE 0x0010 /* An attribute (see ATTR) of the object has changed */ #define X_DELETE 0x0020 /* The object has been deleted */ #define X_CUT 0x0040 /* The object has been deleted, but stored in the clipboard */ #define X_COPY 0x0080 /* The object has been copied to the clipboard */ #define X_UNGROUP 0x0100 /* The object has been ungrouped */
For example, given the XTRN object:
FORM xxxx DR2D { XTRN xxxx { X_RESIZE | X_MOVE, 10, "Dimension" } ATTR xxxx { 0, 0, 1, 0, 0, 0, 0.0 } FORM xxxx DR2D { GRUP xxxx { 2 } STXT xxxx { 0, 0.5, 1.0, 6.0, 5.0, 0.0, 4, "3.0" } OPLY xxxx { 2, { 5.5, 5.5, 8.5, 5.5 } } } }
ProVector would call the ARexx script named Dimension if the user resized or moved this object. What exactly ProVector sends depends upon what the user does to the object. The following list shows what string(s) ProVector sends according to which flag(s) are set. The parameters are described below.
X_CLONE "appl CLONE objID dx dy" X_MOVE "appl MOVE objID dx dy" X_ROTATE "appl ROTATE objID cx cy angle" X_RESIZE "appl RESIZE objID cx cy sx sy" X_CHANGE "appl CHANGE objID et ev ft fv ew jt fn" X_DELETE "appl DELETE objID" X_CUT "appl CUT objID" X_COPY "appl COPY objID" X_UNGROUP "appl UNGROUP objID"
where:
appl is the name of the ARexx script CLONE, MOVE, ROTATE, RESIZE, etc. are literal strings objID is the object ID that ProVector assigns to this object (dx, dy) is the position offset of the CLONE or MOVE (cx, cy) is the point around which the object is rotated or resized angle is the angle (in degrees) the object is rotated sx and sy are the scaling factors in the horizontal and vertical directions, respectively. et is the edge type (the dash pattern index) ev is the edge value (the edge color index) ft is the fill type fv is the fill index ew is the edge weight jt is the join type fn is the font name
The X_CHANGE message reflects changes to the attributes found in the ATTR chunk.
If the user resized the XTRN object shown above by factor of 2, ProVector would call the ARexx script Dimension like this:
Dimension RESIZE 1985427 7.0 4.75 2.0 2.0
The Object Chunks
The following chunks define the objects available in the DR2D FORM.
VBM
VBM (0x56424D20) /* Virtual BitMap */
The VBM chunk contains the position, dimensions, and file name of an ILBM image.
struct VBMstruct { IEEE XPos, YPos, /* Virtual coords */ XSize, YSize, /* Virtual size */ Rotation; /* in degrees */ USHORT PathLen; /* Length of dir path */ char Path[PathLen]; /* Null-terminated path of file */ };
The coordinate (XPos, YPos) is the position of the upper left hand corner of the bitmap and the XSize and YSize fields supply the x and y dimensions to which the image should be scaled. Rotation tells how many degrees to rotate the ILBM around its upper left hand corner. ProVector does not currently support rotation of bitmaps and will ignore this value. Path contains the name of the ILBM file and may also contain a partial or full path to the file. DR2D readers should not assume the path is correct. The full path to an ILBM on one system may not match the path to the same ILBM on another system. If a DR2D reader cannot locate an ILBM file based on the full path name or the file name itself (looking in the current directory), it should ask the user where to find the image.
CPLY and OPLY
CPLY (0x43504C59) /* Closed polygon */ OPLY (0x4F504C59) /* Open polygon */
Polygons are the basic components of almost all 2D objects in the DR2D FORM. Lines, squares, circles, and arcs are all examples of DR2D polygons. There are two types of DR2D polygons, the open polygon (OPLY) and the closed polygon (CPLY). The difference between a closed and open polygon is that the computer adds a line segment connecting the endpoints of a closed polygon so that it is a continuous path. An open polygon's endpoints do not have to meet, like the endpoints of a line segment.
struct POLYstruct { ULONG ID; ULONG Size; USHORT NumPoints; IEEE PolyPoints[2*NumPoints]; };
The NumPoints field contains the number of points in the polygon and the PolyPoints array contains the (X, Y) coordinates of the points of the non-curved parts of polygons. The even index elements are X coordinates and the odd index elements are Y coordinates.
DR2D uses Bezier cubic sections, or cubic splines, to describe curves in polygons. A set of four coordinates (P1 through P4) defines the shape of a cubic spline. The first coordinate (P1) is the point where the curve begins. The line from the first to the second coordinate (P1 to P2) is tangent to the curve at the first point. The line from P3 to P4 is tangent to the cubic section, where it ends at P4.
The coordinates describing the cubic section are stored in the PolyPoints[] array with the coordinates of the normal points. DR2D inserts an indicator point before a set of cubic section points to differentiate a normal point from the points that describe a curve. An indicator point has an X value of 0xFFFFFFFF. The indicator point's Y value is a bit field. If this bit field's low-order bit is set, the points that follow the indicator point make up a cubic section.
The second lowest order bit in the indicator point's bit field is the MOVETO flag. If this bit is set, the point (or set of cubic section points) starts a new polygon, or subpolygon. This subpolygon will appear to be completely separate from other polygons but there is an important connection between a polygon and its subpolygon. Subpolygons make it possible to create holes in polygons. An example of a polygon with a hole is the letter "O". The "O" is a filled circular polygon with a smaller circular polygon within it. The reason the inner polygon isn't covered up when the outer polygon is filled is that DR2D fills are done using the even-odd rule.
The even-odd rule determines if a point is "inside" a polygon by drawing a ray outward from that point and counting the number of path segments the ray crosses. If the number is even, the point is outside the object and shouldn't be filled. Conversely, an odd number of crossings means the point is inside and should be filled. DR2D only applies the even-odd rule to a polygon and its subpolygons, so no other objects are considered in the calculations.
Taliesin, Inc. supplied the following algorithm to illustrate the format of DR2D polygons. OPLYs, CPLYs, AROWs, and ProVector's outline fonts all use the same format:
typedef union { IEEE num; LONG bits; } Coord; #define INDICATOR 0xFFFFFFFF #define IND_SPLINE 0x00000001 #define IND_MOVETO 0x00000002 /* A common pitfall in attempts to support DR2D has been to fail to recognize the case when an INDICATOR point indicates the following coordinate to be the first point of BOTH a Bezier cubic and a sub-polygon, ie. the value of the flag = (IND_CURVE | IND_MOVETO) */ Coord Temp0, Temp1; int FirstPoint, i, Increment; /* Initialize the path */ NewPath(); FirstPoint = 1; /* Draw the path */ i = 0; while( i < NumPoints ) { Temp0.num = PolyPoints[2*i]; Temp1.num = PolyPoints[2*i + 1]; if( Temp0.bits == INDICATOR ) { /* Increment past the indicator */ Increment = 1; if( Temp1.bits & IND_MOVETO ) { /* Close and fill, if appropriate */ if( ID == CPLY ) { FillPath(); } else { StrokePath(); } /* Set up the new path */ NewPath(); FirstPoint = 1; } if( Temp1.bits & IND_CURVE ) { /* The next 4 points are Bezier cubic control points */ if( FirstPoint ) MoveTo( PolyPoints[2*i + 2], PolyPoints[2*i + 3] ); else LineTo( PolyPoints[2*i + 2], PolyPoints[2*i + 3] ); CurveTo( PolyPoints[2*i + 4], PolyPoints[2*i + 5], PolyPoints[2*i + 6], PolyPoints[2*i + 7], PolyPoints[2*i + 8], PolyPoints[2*i + 9] ); FirstPoint = 0; /* Increment past the control points */ Increment += 4; } } else { if( FirstPoint ) MoveTo( PolyPoints[2*i], PolyPoints[2*i + 1] ); else LineTo( PolyPoints[2*i], PolyPoints[2*i + 1] ); FirstPoint = 0; /* Increment past the last endpoint */ Increment = 1; } /* Add the increment */ i += Increment; } /* Close the last path */ if( ID == CPLY ) { FillPath(); } else { StrokePath(); }
GRUP
GRUP (0x47525550) /* Group */
The GRUP chunk combines several DR2D objects into one. This chunk is only valid inside nested DR2D FORMs, and must be the first chunk in the FORM.
struct GROUPstruct { ULONG ID; ULONG Size; USHORT NumObjs; };
The NumObjs field contains the number of objects contained in this group. Note that the layer of the GRUP FORM overrides the layer of objects within the GRUP. The following example illustrates the layout of the GRUP (and FILL) chunk.
FORM { DR2D /* Top-level drawing... */ DRHD { ... } /* Confirmed by presence of DRHD chunk */ CMAP { ... } /* Various other things... */ FONS { ... } FORM { DR2D /* A nested form... */ FILL { 1 } /* Ah! The fill-pattern table */ CPLY { ... } /* with only 1 object */ } FORM { DR2D /* Yet another nested form */ GRUP { ..., 3 } /* Ah! A group of 3 objects */ TEXT { ... } CPLY { ... } OPLY { ... } } FORM { DR2D /* Still another nested form */ GRUP { ..., 2 } /* A GRUP with 2 objects */ OPLY { ... } TEXT { ... } } }
STXT
STXT (0x53545854) /* Simple text */
The STXT chunk contains a text string along with some information on how and where to render the text.
struct STXTstruct { ULONG ID; ULONG Size; UBYTE Pad0; /* Always 0 (for future expansion) */ UBYTE WhichFont; /* Which font to use */ IEEE CharW, CharH, /* W/H of an individual char */ BaseX, BaseY, /* Start of baseline */ Rotation; /* Angle of text (in degrees) */ USHORT NumChars; char TextChars[NumChars]; };
The text string is in the character array, TextChars[]. The ID of the font used to render the text is WhichFont. The font's ID is set in a FONS chunk. The starting point of the baseline of the text is (BaseX, BaseY). This is the point around which the text is rotated. If the Rotation field is zero (degrees), the text's baseline will originate at (BaseX, BaseY) and move to the right. CharW and CharH are used to scale the text after rotation. CharW is the average character width and CharH is the average character height. The CharW/H fields are comparable to an X and Y font size.
TPTH
TPTH (0x54505448) /* A text string along a path */
This chunk defines a path (polygon) and supplies a string to render along the edge of the path.
struct TPTHstruct { ULONG ID; ULONG Size; UBYTE Justification; /* see defines, below */ UBYTE WhichFont; /* Which font to use */ IEEE CharW, CharH; /* W/H of an individual char */ USHORT NumChars; /* Number of chars in the string */ USHORT NumPoints; /* Number of points in the path */ char TextChars[NumChars];/* PAD TO EVEN #! */ IEEE Path[2*NumPoints]; /* The path on which the text lies */ };
WhichFont contains the ID of the font used to render the text. Justification controls how the text is justified on the line. Justification can be one of the following values:
#define J_LEFT 0x00 /* Left justified */ #define J_RIGHT 0x01 /* Right justified */ #define J_CENTER 0x02 /* Center text */ #define J_SPREAD 0x03 /* Spread text across path */
CharW and CharH are the average width and height of the font characters and are akin to X and Y font sizes, respectively. A negative FontH implies that the font is upsidedown. Note that CharW must not be negative. NumChars is the number of characters in the TextChars[] string, the string containing the text to be rendered. NumPoints is the number of points in the Path[] array. Path[] is the path along which the text is rendered. The path itself is not rendered. The points of Path[] are in the same format as the points of a DR2D polygon.
A Simple DR2D Example
Here is a (symbolic) DR2D FORM:
FORM { DR2D DRHD 16 { 0.0, 0.0, 10.0, 8.0 } CMAP 6 { 0,0,0, 255,255,255 } FONS 9 { 1, 0, 1, 0, "Roman" } 0 DASH 12 { 1, 2, {1.0, 1.0} } ATTR 14 { 0, 0, 1, 0, 0, 0, 0, 0.0 } BBOX 16 { 2.0, 2.0, 8.0, 6.0 } FORM { DR2D GRUP 2 { 2 } BBOX 16 { 3.0, 4.0, 7.0, 5.0 } STXT 36 { 0,1, 0.5, 1.0, 3.0, 5.0, 0.0, 12, "Hello, World" } BBOX 16 { 2.0, 2.0, 8.0, 6.0 } OPLY 42 { 5, {2.0,2.0, 8.0,2.0, 8.0,6.0, 2.0,6.0, 2.0,2.0 } } }
The OFNT FORM
OFNT
OFNT (0x4F464E54) /* ID of outline font file */
ProVector's outline fonts are stored in an IFF FORM called OFNT. This IFF is a separate file from a DR2D. DR2D's FONS chunk refers only to fonts defined in the OFNT form.
OFHD
OFHD (0x4F464844) /* ID of OutlineFontHeaDer */
This chunk contains some basic information on the font.
struct OFHDstruct { char FontName[32]; /* Font name, null padded */ short FontAttrs; /* See FA_*, below */ IEEE FontTop, /* Typical height above baseline */ FontBot, /* Typical descent below baseline */ FontWidth; /* Typical width, i.e. of the letter O */ }; #define FA_BOLD 0x0001 #define FA_OBLIQUE 0x0002 #define FA_SERIF 0x0004
The FontName field is a NULL terminated string containing the name of this font. FontAttrs is a bit field with flags for several font attributes. The flags, as defined above, are bold, oblique, and serif. The unused higher order bits are reserved for later use. The other fields describe the average dimensions of the characters in this font. FontTop is the average height above the baseline, FontBot is the average descent below the baseline, and FontWidth is the average character width.
KERN
KERN (0x4B45524C) /* Kerning pair */
The KERN chunk describes a kerning pair. A kerning pair sets the distance between a specific pair of characters.
struct KERNstruct { short Ch1, Ch2; /* The pair to kern (allows for 16 bits...) */ IEEE XDisplace, /* Amount to displace -left +right */ YDisplace; /* Amount to displace -down +up */ };
The Ch1 and Ch2 fields contain the pair of characters to kern. These characters are typically stored as ASCII codes. Notice that OFNT stores the characters as a 16-bit value. Normally, characters are stored as 8-bit values. The wary programmer will be sure to cast assigns properly to avoid problems with assigning an 8-bit value to a 16-bit variable. The remaining fields, XDisplace and YDisplace, supply the baseline shift from Ch1 to Ch2.
CHDF
CHDF (0x43484446) /* Character definition */
This chunk defines the shape of ProVector's outline fonts.
struct CHDFstruct { short Ch; /* The character we're defining (ASCII) */ short NumPoints; /* The number of points in the definition */ IEEE XWidth, /* Position for next char on baseline - X */ YWidth; /* Position for next char on baseline - Y */ /* IEEE Points[2*NumPoints]*/ /* The actual points */ }; #define INDICATOR 0xFFFFFFFF /* If X == INDICATOR, Y is an action */ #define IND_SPLINE 0x00000001 /* Next 4 pts are spline control pts */ #define IND_MOVETO 0x00000002 /* Start new subpoly */ #define IND_STROKE 0x00000004 /* Stroke previous path */ #define IND_FILL 0x00000008 /* Fill previous path */
Ch is the value (normally ASCII) of the character outline this chunk defines. Like Ch1 and Ch2 in the KERN chunk, Ch is stored as a 16-bit value. (XWidth,YWidth) is the offset to the baseline for the following character. OFNT outlines are defined using the same method used to define DR2D's polygons (see the description of OPLY/CPLY for details).
Because the OFNT FORM does not have an ATTR chunk, it needed an alternative to make fills and strokes possible. There are two extra bits used in font indicator points not found in polygon indicator points, the IND_STROKE and IND_FILL bits (see defines above). These two defines describe how to render the current path when rendering fonts.
The current path remains invisible until the path is either filled and/or stroked. When the IND_FILL bit is set, the currently defined path is filled in with the current fill pattern (as specified in the current ATTR chunk). A set IND_STROKE bit indicates that the currently defined path itself should be rendered. The current ATTR's chunk dictates the width of the line, as well as several other attributes of the line. These two bits apply only to the OFNT FORM and should not be used in describing DR2D polygons.