D X F I X ========= An open, programmable application permits translation of ASCII or binary DXF files to input compatible with any release of AutoCAD. by John Walker Revision 2 by Duff Kurland -- February 18, 1991 Of one power even God is deprived, and that is the power of making what is past never to have been. -- Aristotle, 340 B.C. Ever since the first incremental update to AutoCAD(R), the issue of downward compatibility has been raised by users. Unlike many software products, AutoCAD has always taken great pains to maintain 100% upward compatibility from release to release--any drawing made with an earlier release of AutoCAD can be edited with any subsequent release. The converse, however, is not the case. Once a drawing is edited with a given release of AutoCAD it cannot be loaded by a prior release. In fact, prior to Release 10, DXF(tm) files written by a later version of AutoCAD could not be loaded into an earlier version without manual modification. In Release 10, we introduced code in DXFIN to ignore header variables, symbol tables, and entity fields not defined by the version of AutoCAD loading the DXF file. While this fixes many of the problems encountered in loading DXF written by a later release of AutoCAD, experience has proven it inadequate; Release 10 still cannot read DXF written by Release 11 without manual editing of the file. This shouldn't be surprising. Solving the problem of downward compatibility in a general way is extremely difficult since it requires the developer, in essence, to anticipate the future direction of product development. Further, the need to maintain downward compatibility with products already in the field may encourage bad design decisions in new product development, burdening those products with inefficiencies and unneeded complexity. While it's easy to omit entire objects that aren't recognized when loading a DXF file, coping with the subtler differences between releases of AutoCAD such as new bits denoting variants of Polylines, changes to text alignment, and the like cannot be reasonably done at the DXF input level. (The introduction of AUDIT in Release 11 will go a long way toward detecting and discarding undefined information received from later releases of AutoCAD. But that, of course, doesn't help users of releases prior to 11. Further, AUDIT will never be able to translate entities in subsequent releases into their equivalents in the release on which it's running.) With the end approaching for support of various venerable AutoCAD host machines (e.g., the 8086 and 8088 in Release 11), the need arises to allow users of those machines to load, albeit with some loss of information, drawings made by AutoCAD Release 11 and, ideally, releases after that. The fact that information may be lost down-converting a drawing may not be as serious as it appears at first glance--the user of an obsolete machine is, in all likelihood, using it for pretty basic 2D drafting work. Having the polyface meshes and registered application name tables purged from a drawing is unlikely to bother such a user, particularly when the alternatives are either not being able to edit the drawing at all or having to junk his computer. The most straightforward way to provide this compatibility is by translating a DXF file written by a new release of AutoCAD into one that can be loaded by an earlier target system. Prior to the introduction of binary DXF in Release 10, this was a very painful process since DXF files were huge, took forever to read and write, and forced a tradeoff between file size and accuracy of the numbers in the file. With binary DXF, however, nothing is lost in exporting and transferring a drawing via DXF, and the file sizes and I/O times are comparable to regular drawing (.dwg) files. What we need, then, is a DXF converter program--one that reads DXF from Release 11 and emits DXF compatible with Release 10. Ideally, such a product should be configurable so changes made in future releases do not require continual modification of the DXF converter. Indeed, the converter should be *programmable*, so that it can translate DXF from ANY release of AutoCAD into that of any other. Best of all would be a converter that was open, programmable by the user, one that could perform any transformation on a DXF file. DXFIX(tm) (Drawing eXchange File Inter Xlator) is precisely such a tool. Incorporating the FORTH-based Autodesk Threaded Language Application System Toolkit (ATLAST (tm)), DXFIX is an open system that can be used, without speed penalty, for the simple task of converting DXF from Release 11 to Release 10, or programmed by anybody with access to this documentation to perform arbitrary translation of DXF. Running DXFIX ------------- To convert a Release 11 DXF file to Release 10, use the command: dxfix [options] infile outfile where "infile" is the input DXF file name (an extension of .dxf is assumed if no explicit extension is given), and "outfile" is the output DXF file name (.dxf is also added to this name if no extension is present). The input DXF file can be either binary or ASCII format; binary format is much faster to process and maintains full accuracy of drawing objects, but if you supply an ASCII DXF file, DXFIX will process it without difficulty. DXFIX reads binary DXF files written in AutoCAD portable format regardless of the system on which it is running, and ASCII DXF with any of the end of line conventions recognized by AutoCAD. The "outfile" is written, by default, in binary DXF format. When transferring data to AutoCAD Release 10, this format is preferable by far as it is much faster to write, generally smaller, and preserves full accuracy. If you want ASCII DXF output instead (for example, if the DXF is intended for an AutoCAD prior to Release 10 that only supports ASCII DXF, or is being sent to a non-Autodesk DXF processing program that requires ASCII), specify the "-Adigits" option, where "digits" is a number giving the precision, in decimal places, to which numbers should be edited in the ASCII DXF file. For example, to convert a Release 11 DXF file named "house11.dxf" to a Release 10 DXF file named "house10.dxf" (writing "house10.dxf" in binary format), use: dxfix house11 house10 To perform the same translation, but generating an ASCII output file with six decimal places of precision, use: dxfix -a6 house11 house10 The actual translation relies on specifications given in the file "dxfix.dxt", which is assumed to reside in the current directory. Alternatively, you can use the "-Tname" option to specify a different location or name for the translation specifications file. For instance: dxfix -a6 -t\dxfix\11_to_10 house11 house10 tells DXFIX to find its translation specifications in the file "\dxfix\11_to_10.dxt". Note that the extension .dxt is assumed if none is supplied. Release 11 to 10 translations ----------------------------- The following Release 11 to Release 10 translations are specified by the standard "dxfix.dxt" file provided with this version of DXFIX. Deleting new header variables The following drawing header variables, added since Release 10, are deleted. (The names in the DXF file are preceded by dollar signs; we give the base names here, as they are more familiar.) DIMCLRD Dimension line colour DIMCLRE Dimension extension line colour DIMCLRT Dimension text colour DIMGAP Dimension block gap DIMSTYLE Current dimension style DIMTFAC Dimension text vertical position MAXACTVP Maximum active viewports PELEVATION Paper elevation PEXTMAX Paper maximum extents PEXTMIN Paper minimum extents PINSBASE Paper insertion base PLIMCHECK Paper limit checking PLIMMAX Paper maximum limits PLIMMIN Paper minimum limits PUCSNAME Paper UCS name PUCSORG Paper UCS origin PUCsXDIR Paper UCS X direction PUCSYDIR Paper UCS Y direction SHADEDGE SHADE command edge rendering mode SHADEDIF Shading diffuse illumination factor TILEMODE Tiled viewports mode UNITMODE Drawing unit mode bits VISRETAIN Layer/viewport visibility retention Flattening Z extents The drawing extents, output as the $EXTMAX and $EXTMIN header variables, contain Z extents for the first time in Release 11. Earlier releases report an error when encountering an unexpected Z coordinate, so the Z coordinates are removed from these variables. Trapping zero $DIMSCALE In Release 11, the DIMSCALE dimensioning variable can be set to zero for paper space scaling. If a zero is detected, it is changed to 1.0. Deleting new symbol tables The following new symbol tables were added in Release 11. They are deleted in their entirety from the TABLES section. APPID - Registered application table DIMSTYLE - Dimension style table Handling new LAYER table flag bits The 70 (flags) group of the LAYER table entry has a few new bits. The "frozen by default in new viewports" bit has no meaning in Release 10 and is zeroed. If any of the "dependent symbol" bits are set, the entire layer entry is discarded. Deleting new entities All VIEWPORT entities, used by the multiple view facility of Release 11, are deleted whenever encountered in the ENTITIES or BLOCKS section. Deleting new entity fields The following fields, representing features added in Release 11, are deleted from the entities in which they appear. BLOCK entity: The 1 (Xref path name) and 3 (block name) groups are deleted. In addition, all Xref-related bits of the 70 (flags) group are zeroed. If the BLOCK was an Xref, it becomes a dummy BLOCK. DIMENSION entity: The 3 (dimension style), 52 (obliquing angle), and 53 (text rotation angle) groups are deleted. Deleting new common entity fields The following fields, which can occur on any entity in the database, are deleted wherever encountered. Extended entity data: Extended entity data (entity attributes) has group codes in the 1000 to 1100 range. All groups in that range are deleted, stripping all extended entity data from the drawing. Paper/model space indicator A new common property of all entities, the paper/model space indicator, is represented in DXF by a 67 group, where: 0 = model space 1 = paper space All other values are reserved for future use. Since versions of AutoCAD prior to Release 11 don't support paper space, DXFIX deletes all entities whose 67 group is nonzero. If the 67 group is zero, just that group is deleted. Adjusting text vertical alignment In Release 11, general specification of the vertical alignment of Text, Attribute Definition, and Attribute entities was implemented. Earlier releases provided general horizontal alignment, with one special case providing both horizontal and vertical centering. The new-style "MCenter" specification is translated into the near-equivalent "MID" form accepted by versions prior to Release 11. All other instances of vertical alignment cause the text to be reset to left/baseline justification. In such cases, a warning message informs the user of the loss of alignment information, and the justification point (group 11) is deleted. In all cases, the vertical alignment group (73 in a Text entity, 74 in an Attribute or Attribute Definition) is deleted. Exploding polyface meshes In Release 11 a new variant of the Polyline entity, the polyface mesh, was introduced. Consisting of a table of vertices and faces, it is a far more compact representation of tiled surfaces than the 3Dface entities used prior to Release 11. However, any polyface mesh can be represented by a collection of 3Dface, Line, and Point entities. If the mesh is EXPLODEd prior to DXFOUT, the resulting file can be loaded into Release 10 without difficulty. To eliminate this requirement (and to demonstrate by a flashy but convenient tour de force the ATLAST-derived power of DXFIX), polyface meshes present in the DXF input file are automatically transformed into the equivalent collection of individual entities. This is done by saving the vertices in a temporary file, then replacing each polyface entity with the corresponding simple entity. Invisible edge specifications are converted from the negative-vertex form used in polyface meshes to the invisible edge bits used in 3Dfaces. The temporary file, "$pface.$ac", is deleted at the end of DXF file translation. Translation specifications -------------------------- Most customers who use DXFIX to convert Release 11 DXF to Release 10 need know nothing more about the program than the information above: how to run it and what information is lost in the process of converting a drawing. DXFIX is, however, far more than a Release 11 to Release 10 DXF translator. In fact, it knows nothing about the contents of either Release 11 or Release 10 DXF. DXFIX is actually an ATLAST-based, general purpose, open architecture DXF translator. As long as no fundamentally new data types are added to DXF (and the extended entity group codes and binary chunks of Release 11 were the first additions to the format in 5 years and 6 releases), DXFIX will be able, given suitable specifications, to translate DXF from any AutoCAD of the future to any AutoCAD target system of Release 2.0 or later. (Earlier versions of AutoCAD used a totally different form of DXF, and while DXFIX could even be made to generate that format, it would hardly be worth the bother.) The transformation of a DXF file by DXFIX can be controlled at several different levels, provided by the natural layering of an ATLAST application. Each will be discussed in turn. Layer 0: Translation file selection ------------------------------------ The transformations performed by DXFIX are defined in a "translation specification file", which is actually an executable ATLAST program loaded by DXFIX to perform the translation. If no translation specification file is named on the DXFIX command line, the default file named "dxfix.dxt" is used. The version of "dxfix.dxt" furnished with the initial version of DXFIX performs Release 11 to Release 10 translation. It is intended that when DXFIX is shipped with an AutoCAD release, it will normally be accompanied with a "dxfix.dxt" file that converts DXF from that release to the immediately prior one. If you wish to use different translation specifications, specify the "-Tname" switch on the DXFIX command line, where "name" is the translation specification file name. If no extension is specified, ".dxt" is automatically appended. For example, if you had a file named "r11_r9.dxt" that converts Release 11 DXF to input compatible with Release 9, you could translate a DXF file called "chair11.dxf" with the command: dxfix -tr11_r9 chair11 chair9 Since Release 9 cannot read binary DXF, the "r11_r9.dxt" file would automatically select ASCII output format, even though the user specified no "-Adigits" switch on the command line. (We'll see later how the translation specifications can control that and much, much more.) Layer 1: Object processing specifications ----------------------------------------- Since much of the process of translating a DXF file consists of simply deleting material added in later releases, much of a translation specification file consists of statements that specify what is to be deleted, which bits should be turned off in mask fields, and the like. The ability to define new data types and methods in ATLAST is exploited to make these specifications simple and readable. To understand the bulk of the specifications in the "dxfix.dxt" file, you must first understand the hierarchy of a DXF file and the nomenclature DXFIX uses to designate different levels and objects in the structure. The overall structure of a DXF file is as follows: Sections Subsections Objects Groups --------- ----------- ------- ------ HEADER Variables Groups TABLES LTYPE Entries Groups LAYER Entries Groups STYLE Entries Groups ... BLOCKS Entities Groups ENTITIES Entities Groups EOF Processing of different parts of this hierarchy is specified by a "structure name" that selects the portions(s) of the hierarchy to be processed. Each structure name begins with the characters "DXF:" followed by the path through the hierarchy with wild-card selection available by specifying the "*" character at various places. To illustrate the use of structure names, we'll use the most common declaration in a translation specification file, the "remove" statement. All objects that match the structure name are deleted, in their entirety, from the DXF file. Here is how "dxfix.dxt" deletes the paper space extents header variables from Release 11 DXF. remove dxf:header:$pextmax remove dxf:header:$pextmin The structure name "dxf:header:$pextmax" specifies the HEADER variable section, object $pextmax (recall that all header variable names are prefixed with a "$" in DXF files). When this variable is encountered in the DXF file, its action, "remove", is executed, thereby deleting the variable from the output file. To delete additional header variables, just add "remove" statements naming them. To demonstrate the generality of this approach, let's examine how the new Release 11 DIMSTYLE (Dimension Style) table is deleted. The specification that accomplishes this is just: remove dxf:tables:dimstyle Unlike the HEADER variables section, the TABLES section can contain multiple objects in each table--in this case any number of dimension styles. The structure name triggers, however, when it matches to the end of the name specification, so this "remove" statement will be executed for each entry in the DIMSTYLE table. Since each execution has the effect of deleting the current item (and since the table start and table end items are also matched as part of the table), this single statement deletes the entire dimension style symbol table from the output DXF. As part of the multiple view plotting facility in Release 11, a new VIEWPORT entity was added to the database. All of these entities must be removed when converting a DXF file for use with Release 10. Since VIEWPORT entities can appear in either the ENTITIES section or, if part of a Block Definition, in the BLOCKS section, we can remove them from both sections with the statements: remove dxf:blocks:viewport remove dxf:entities:viewport The "remove" declaration isn't the only action we can take when a section name is matched. The "ditchgroup" statement causes the named group (which must be specified to the group level) to be deleted from the current object. For example, three new groups were added to the Dimension entity in Release 11. To remove these, we can use the statements: ditchgroup dxf:*:dimension:3 ditchgroup dxf:*:dimension:52 ditchgroup dxf:*:dimension:53 The first statement matches the 3 group of objects (entities) named DIMENSION appearing in any section of the DXF file (effectively just the BLOCKS and ENTITIES sections, since DIMENSION objects appear only there). The action of "ditchgroup" causes the 3 group to be deleted wherever it appears within a Dimension entity. The statements that follow similarly delete the 52 and 53 groups. Extended entity data (entity attributes) were introduced in Release 11. These are written to DXF files with group codes between 1000 and 1100, and must be removed when translating DXF for earlier versions of AutoCAD. We can remove all extended entity data, wherever present in the DXF file, with the statement: ditchgroup dxf:*:*:1000-1100 In this case we're wild-carding both the section and the object type and taking advantage of the ability to specify a range of group codes to be selected. If we wanted to select the polyface vertex fields in a Vertex entity, we could use "dxf:*:vertex:71-74". If special processing is specified at more than one level (for example, a given group might be selected by a global group specification, as a group of an entity of that type, or as a member of an object of that type), processing is done with the most specific (group) first to the most general (item) last. Layer 2: Translation programming --------------------------------- What appears at first glance to be a data file that specifies the changes to be made to a DXF file is, in actuality, an executable ATLAST program. The program has access to all the usual ATLAST facilities and, in addition, a set of primitives provided by DXFIX that allow easy manipulation of objects in the DXF file. The key to understanding how DXFIX and ATLAST work together is the structure name specifications described in Layer 1 above. Each structure name specification is actually an executable ATLAST definition automatically invoked when a matching component of the DXF file is encountered. The ability to potentially run a user-defined program for every component of the file permits DXFIX to perform arbitrary transformations on the DXF files it processes. The quickness with which DXFIX can determine if special processing is requested for a component of the DXF file (by pre-scanning the ATLAST dictionary for the "DXF:" definitions and building a hierarchically-organized table of processing requests, it is just a matter of a few pointer and integer comparisons), and the inherent speed of executing precompiled ATLAST code in an already-looked-up definition, result in DXFIX being able to perform substantial user-specified processing while running at essentially the same speed as a simple DXF copy program written entirely in C. To understand how DXFIX and ATLAST are woven together into a unified general purpose translation tool, let's start with the DXF processing primitives added to ATLAST by DXFIX. Many of these primitives will rarely be used in any but the most ambitious DXF translation projects (far more complicated than converting Release 11 to Release 10), but they're available if you need them. Key to understanding the DXFIX primitives is the "current item". DXFIX is always working on one item (structure delimiter, header variable, symbol table entry, or entity) from the DXF file at a time. This current item is implicitly referenced by all of the item and group manipulation primitives. Item primitives --------------- The item primitives operate upon entire items (lists of groups forming the fundamental objects in the DXF file). CLEARITEM All groups of the current item are deleted. You'd only use this if you intended to build a new item from scratch using ADDGROUP. The stack is not affected. ITEMPOS The position at which the current item began in the DXF file is placed on the stack. This is the line number of the first group of the item if the input file is ASCII or its byte number if the input is binary. Both lines and bytes are numbered from zero. PRINTITEM All groups of the current item are printed on the output file specified by the top of the stack. You can use the predefined and automatically opened streams STDOUT and STDERR to send output to the user. For example, if an Arc is the current entity, the statement: stdout printitem might generate the following output: 0: "ARC" 8: "0" 10: (3, 2, 0) 40: 1 50: 0 51: 90 READITEM The next item is read from the DXF file and becomes the current item. If the item is read normally, -1 is placed on the stack; if end of file or a read error occurs, 0 is placed on the stack. When the item is read, all special processing declared by structure names that match the item and/or groups within it is automatically performed BEFORE the item is made available to the caller of READITEM. If the previous current item has not been written to the output file with WRITEITEM before READITEM is called to read the next, it will be lost--deleted from the output file. WRITEITEM The current item is written to the output file, unless marked for deletion. If the item is written normally or is to be deleted, -1 is placed on the top of the stack. If an error occurs attempting to write the item to the output file, 0 is returned on the stack top. If the DELITEM (delete item) shared variable is nonzero, the item is not written to the output file, and the DELITEM variable is cleared to zero. If the DELCITEM (delete complex item) shared variable is nonzero, the item is not written to the output file and, if the item is a Sequence End entity, the DELCITEM variable is cleared to zero. Group primitives ---------------- The group primitives provide access to the individual data fields that make up an item. In the following descriptions of primitives, assume that the current item is a Line entity on layer 0, from coordinates (1,1,0) to (2,2,0). This item would be displayed with the sequence "stdout printitem" as: 0: "LINE" 8: "0" 10: (1, 1, 0) 11: (2, 2, 0) Groups within an item can be identified either by group code or by their position within the item. Regular AutoCAD item fields are always unique and may be identified simply by their group codes. Extended entity data, however, uses the same group code for all fields of a given type, so group codes are not necessarily unique. A positive number used to designate a group chooses the first occurrence of that group code in the current item. A negative number of the form -(10000 + n), where "n" specifies the position of the group within the item (with the first group numbered zero), selects the nth group in the chain of groups composing the item and may be used to uniquely specify extended entity groups that appear more than once in an item. PRINTGROUP The group identified by the second item on the stack is printed on the output stream specified by the top of stack. For example: -> 10 stdout printgroup 10: (1, 1, 0) -> -10001 stdout printgroup 8: "0" GROUPCOUNT Places the number of groups in the current item on the top of the stack. -> groupcount . 4 GROUP? If the group with group code given by the top of the stack is present in the item, -1 is placed on the top of the stack. If the group does not appear in the item, 0 is returned. -> 10 group? . -1 -> 40 group? . 0 DELGROUP The group on the top of the stack is deleted from the item, if present. If the specified group is not present, DELGROUP is simply ignored. -> stdout printitem 0: "LINE" 8: "0" 10: (1, 1, 0) 11: (2, 2, 0) -> 8 delgroup -> stdout printitem 0: "LINE" 10: (1, 1, 0) 11: (2, 2, 0) GROUP The value of the specified group, in whatever form is appropriate for it, is placed on the top of the stack. Integers are stored as single stack items; real numbers and angles as pairs of stack items representing their floating point values; coordinates as triples of pairs, each giving a floating coordinate with Z at the top of the stack, Y next, and then X; strings as the address of a temporary string buffer containing the text; and binary chunks as a length, in bytes, on the top of the stack and the address of the chunk data, stored in a temporary string buffer, next on the stack. -> 10 group f. f. f. 0 1 1 ADDGROUP A group with the type given by the top of the stack is added to the end of the item. The value field of the group is cleared to zero, and may be then set with SETGROUP. -> 62 addgroup -> stdout printitem 0: "LINE" 10: (1, 1, 0) 11: (2, 2, 0) 62: 0 SETGROUP Sets the value of the group specified by the top of the stack to the values below it (in the same form as the results returned by GROUP). Removes the group specification and the values from the stack. -> 3 62 setgroup -> 3.0 4.0 5.0 10 setgroup -> stdout printitem 0: "LINE" 10: (3, 4, 5) 11: (2, 2, 0) 62: 3 Diagnostic primitives --------------------- DUMPSPECIAL The hierarchy of the DXF file is printed, including all items named by structure name declarations (whether seen in the DXF file or not) and those actually encountered in processing the input file so far. Those items for which special processing is requested are identified with the legend "(Special)". At the end of the listing, wild-card specifications are listed. Here is the output from DUMPSPECIAL after the first few header variables of a DXF file are processed when using the standard Release 11 to Release 10 "dxfix.dxt" specifications. -> readitem readitem readitem readitem -> dumpspecial START (Special) HEADER $INSBASE $ACADVER $DIMCLRD (Special) $DIMCLRE (Special) $DIMCLRT (Special) $DIMGAP (Special) $DIMSTYLE (Special) $DIMTFAC (Special) $MAXACTVP (Special) $PELEVATION (Special) $PEXTMAX (Special) $PEXTMIN (Special) $PINSBASE (Special) $PLIMCHECK (Special) $PLIMMAX (Special) $PLIMMIN (Special) $PUCSNAME (Special) $PUCSORG (Special) $PUCSXDIR (Special) $PUCSYDIR (Special) $SHADEDGE (Special) $SHADEDIF (Special) $TILEMODE (Special) $UNITMODE (Special) $VISRETAIN (Special) $EXTMAX (Special) $EXTMIN (Special) $DIMSCALE (Special) TABLES APPID (Special) DIMSTYLE (Special) LAYER (Special) ENTITIES VIEWPORT (Special) BLOCKS VIEWPORT (Special) BLOCK (Special) END (Special) Group range actions: DXF:*:DIMENSION:3 DXF:*:DIMENSION:52 DXF:*:DIMENSION:53 DXF:*:*:1000-1100 DXF:*:TEXT:73 DXF:*:ATTDEF:74 DXF:*:ATTRIB:74 DXF:*:*:67 DXF:*:POLYLINE:70 DXF:*:SEQEND:* DXF:*:VERTEX:70 Command line primitives ----------------------- The following primitives provide access to the command line used to invoke DXFIX. They can be used by advanced translation programs to obtain parameters from the command line. The following primitive descriptions assume that DXFIX has been invoked with a command line of: dxfix -i -v11 -y=NO dxin dxout OPTION Tests whether a command line option letter, prefixed by a minus sign, is present. If that option is not specified, 0 is placed on the top of the stack. If the option is present, a pointer to the text that follows the option letter is placed on the top of the stack (the pointer is guaranteed to be nonzero). Note that if no text follows the option letter, the pointer will still be nonzero indicating the option was specified. The pointer will point to a null string, in that case. -> "v" option type cr 11 -> "x" option . cr 0 -> "Y" option type cr =NO ARGCOUNT Places the number of command line arguments, including the zeroth argument that gives the program name itself, on the top of the stack. -> argcount 6 ARGVALUE Given a number from 0 to ARGCOUNT-1 on the top of the stack, returns a pointer to a temporary string buffer containing a copy of that command line argument. If the number is less than 0 or >= ARGCOUNT, 0 is placed on the top of the stack. -> 2 argvalue type cr -v11 -> 6 argvalue . cr 0 -> : targs argcount 0 do i argvalue type :> cr loop ; -> targs dxfix -i -v11 -y=NO dxin dxout Shared variables ---------------- A number of shared variables serve to exchange information between DXFIX and the ATLAST translation specification program. These variables are all integers, and have the following meanings. DELITEM DELITEM causes the current item to be deleted rather than being written to the output DXF file when WRITEITEM is invoked. You can set DELITEM from any structure name definition to cause the item that invoked that definition to be deleted from the output file. DELCITEM DELCITEM causes the current item, and all subsequent items up to and including the next Sequence End entity to be deleted from the output DXF file rather than being written to it by WRITEITEM. A structure name definition may set DELCITEM when processing the header of a complex entity (for example, a polygon mesh, when generating DXF for a version of AutoCAD prior to Release 10) which should be deleted in its entirety. INBINARY If the input file is binary, INBINARY has the value -1. If the input file is ASCII, it's 0. OUTPREC The OUTPREC variable specifies the mode of the output file and, if ASCII, the precision of floating point numbers written to it. If the output file is binary, OUTPREC is set to -1; if this default has been overridden with a "-Adigits" specification on the DXFIX command line, OUTPREC will be set to "digits". If the value of OUTPREC is set by a DXF:START definition in the translation program, the value specified will override the default or the value from the command line. DUMPINPUT If DUMPINPUT is nonzero, each item read from the input DXF file will be dumped to standard output. DUMPOUTPUT If DUMPOUTPUT is nonzero, each item written to the output DXF file will be dumped to standard output. SPECIALDONE When an item matches multiple structure name definitions, each is normally executed in sequence. If a structure name definition performs an action that by its very nature completes the processing of the item (for example, setting DELITEM to delete it from the output file), it may set SPECIALDONE. Once this variable is set nonzero by a structure name definition, no other structure name definition triggered by the item will be processed. This is particularly handy when you wish processing of a certain group of an entity to replace the default handling of entities with that group missing. DXFTRACE If the translation program sets DXFTRACE nonzero, all invocations of structure name definitions are printed on standard output. Here is the output generated by translation of a Release 11 DXF file with DXFTRACE set to 1. Invoking DXF:START Invoking DXF:HEADER:$EXTMIN Invoking DXF:HEADER:$EXTMAX Invoking DXF:HEADER:$DIMSCALE Invoking DXF:HEADER:$DIMSTYLE Invoking DXF:HEADER:$DIMCLRD Invoking DXF:HEADER:$DIMCLRE Invoking DXF:HEADER:$DIMCLRT Invoking DXF:HEADER:$DIMTFAC Invoking DXF:HEADER:$DIMGAP Invoking DXF:HEADER:$PELEVATION Invoking DXF:HEADER:$PUCSNAME Invoking DXF:HEADER:$PUCSORG Invoking DXF:HEADER:$PUCSXDIR Invoking DXF:HEADER:$PUCSYDIR Invoking DXF:HEADER:$SHADEDGE Invoking DXF:HEADER:$SHADEDIF Invoking DXF:HEADER:$TILEMODE Invoking DXF:HEADER:$MAXACTVP Invoking DXF:HEADER:$PINSBASE Invoking DXF:HEADER:$PLIMCHECK Invoking DXF:HEADER:$PEXTMIN Invoking DXF:HEADER:$PEXTMAX Invoking DXF:HEADER:$PLIMMIN Invoking DXF:HEADER:$PLIMMAX Invoking DXF:HEADER:$UNITMODE Invoking DXF:HEADER:$VISRETAIN Invoking DXF:TABLES:LAYER Invoking DXF:TABLES:LAYER Invoking DXF:TABLES:LAYER Invoking DXF:TABLES:APPID Invoking DXF:TABLES:APPID Invoking DXF:TABLES:APPID Invoking DXF:TABLES:DIMSTYLE Invoking DXF:TABLES:DIMSTYLE Invoking DXF:TABLES:DIMSTYLE Invoking DXF:BLOCKS:BLOCK Invoking DXF:BLOCKS:BLOCK Invoking DXF:*:SEQEND Invoking DXF:BLOCKS:BLOCK Invoking DXF:BLOCKS:BLOCK Invoking DXF:*:TEXT:73 Invoking DXF:*:DIMENSION:3 Invoking DXF:*:POLYLINE:70 Invoking DXF:*:SEQEND Invoking DXF:*:SEQEND Invoking DXF:BLOCKS:BLOCK Invoking DXF:BLOCKS:BLOCK Invoking DXF:*:TEXT:73 Invoking DXF:*:*:1000-1100 Invoking DXF:*:*:1000-1100 Invoking DXF:*:*:1000-1100 Invoking DXF:*:*:1000-1100 Invoking DXF:*:*:67 Invoking DXF:ENTITIES:VIEWPORT Invoking DXF:END Missing Z coordinates --------------------- Some versions of DXF files contain two dimensional as well as three dimensional points. Unfortunately, these objects are distinguished only by the absence or presence, respectively, of a 3x group in the DXF file. They cannot be told apart by group code alone once assembled into composite structures with group codes from 10 to 19. To allow translation programs to determine whether a Z coordinate was supplied in input and to control whether a Z coordinate is written when a point group is output, a special floating point variable named MISSING_Z is defined. The variable has a value of -1*(10^308), which should never occur in a valid AutoCAD database. If the Z coordinate of a point group tests equal to MISSING_Z, no Z coordinate was present in the DXF input file. If you set the Z coordinate of a point group to MISSING_Z, only X and Y coordinates will be written for that point when it is output. See the definition of DROP_Z in "dxfix.dxt" for an example of MISSING_Z being used to discard an unwanted Z coordinate. Structure name execution ------------------------ As each item is read from the input DXF file, DXFIX matches it against the structure words (such as DXF:ENTITIES:LINE) defined in the translation program. When a match occurs, that definition is executed, allowing it to perform whatever operations it wishes upon the current item. Two kinds of structure names can be specified. The first contains no wild cards or group number and is triggered by an entire item in the input; it will be executed only once per item. The second form of structure name does contain wild cards and/or a group number or range of group numbers. This form of structure name is triggered at the individual group level; note that a structure name with a wild card and no group range is triggered for EVERY group in the item (if you don't want this to happen, simply specify a group code known to appear only once in the item, or set SPECIALDONE to avert further processing of structure names for the item). The two forms of structure names are executed in different ways as well. The following examples should make the distinctions clear. Item level structure names -------------------------- Examples of item level structure names are DXF:HEADER:$EXTMIN, DXF:TABLES:APPID, and DXF:ENTITIES:VERTEX. Each of these denotes a definition triggered when an object with that sequence of names appears in the DXF hierarchy. The test for the presence of an item level structure name is extremely fast, as DXFIX knows at all times where it is in the hierarchy and does not need to search a list of definitions. When an item just read from the input file triggers a structure name, the definition for that name is run. Nothing is placed on the stack before executing the definition, and the definition should leave no result on the stack. Since the definition can examine and manipulate the current item with the primitives provided for that purpose, there's no need to pass data on the stack. Suppose we want to delete the UNITMODE header variable. This can be accomplished with the following definition: : dxf:header:$unitmode 1 delitem ! ; Now when the $UNITMODE item is encountered in the HEADER section, this definition will be run. It stores 1 into the shared variable DELITEM, which causes DXFIX not to emit the item to the output file, accomplishing the objective of the definition. Another example of an item level structure name is provided by this definition that deletes the Xref information from Release 11 block definitions: : dxf:blocks:block 70 group? if 70 group 3 and 70 setgroup then 1 delgroup 3 delgroup ; When triggered by each BLOCK entity in the BLOCKS section (the only place they appear), this definition obtains its 70 group flags, clears the Xref bits if set, and stores the revised flags using SETGROUP. It then uses DELGROUP twice to delete any 1 and 3 Xref groups present in the BLOCK entity. Since DELGROUP does nothing if the group is absent, the definition need not use GROUP? to test whether those groups exist. Group level structure names --------------------------- Group level structure names contain wild card specifications, group numbers or ranges, or both. Group level structure names are matched and executed at the group level, and are passed the group code that triggered them on the stack when invoked. Examples of group level structure names are DXF:*:*:1000-1100 which is triggered by any group with a code between 1000 and 1100, wherever found in the input file; DXF:TABLES:STYLE:70, executed for the 70 group of each entry in the STYLE table of the TABLES section; and DXF:*:TEXT:73, activated whenever a 73 group is encountered in a TEXT item anywhere in the file (of course, TEXT items appear only in the BLOCKS and ENTITIES sections). Checking for the presence of a group level structure definition is somewhat less efficient than determining if an item level definition exists. You should use group level names only when you need the wild card selection and group identification they provide. In translating Release 11 DXF to Release 10, we want to delete all 52 groups from Dimension entities. These entities can appear in either the BLOCKS or the ENTITIES section. We can accomplish this with: : dxf:*:dimension:52 delgroup ; Since this is a group level structure name definition, the group code that invoked it (in this case 52) is placed on the top of the stack before our definition is executed. Knowing it's there, we need only execute DELGROUP to remove the group from all DIMENSION items. Actually, we want to get rid of both 52 and 53 groups in Dimension entities. This requires only our specifying the range of groups: : dxf:*:dimension:52-53 delgroup ; Again, since the group code that triggered the structure name is placed on the stack, the definition does not need to "know" what group caused it to be run. An extreme example of a group range is provided by a definition that removes all extended entity data from a DXF file. : dxf:*:*:1000-1100 delgroup ; Here we delete all groups between 1000 and 1100 in any section of the file. (We should only find such groups in the BLOCKS and ENTITIES section, but no harm is done by this more general specification.) We can do anything we like with groups, not just delete them. Suppose we wished to guarantee that the 4 bit was never set in the 70 group of a Vertex entity (it's never supposed to be set anyway, but what the heck). The following definition will clear any erroneously set 4 bits. : dxf:*:VERTEX:70 group 4 not and 70 setgroup ; When this definition is activated, we use the group code on the stack to obtain the current value of the field. We then push the constant 4 for the bit we wish to clear and use NOT to complement its bits. AND gives us the value with the 4 bit guaranteed to be off. We then push the group code of 70 (we could have copied the version on the stack originally, but since this definition is only executed for 70 groups, using the constant is simpler and faster) and use SETGROUP to store the masked value back into the group. The only limits on the amount of processing you can do when triggered by a group are your ambition in writing code and your patience waiting for it to execute. Take a gander at the definition of DXF:*:VERTEX:70 actually used in "dxfix.dxt" to translate polyface meshes into simple entities for an idea of where the deep end begins. Layer 3: Structure defining words --------------------------------- "But hey!", you say, "Those definitions you just showed me don't look anything like the 'remove' and 'ditchgroup' statements you told me about that are used in 'dxfix.dxt.'" Right you are. In "dxfix.dxt" we took advantage of one of the most powerful aspects of ATLAST to make the translation specifications compact, readable, and efficient. ATLAST allows you to create "defining words" which can subsequently be used to declare new data types complete with method code to be executed when the data type is referenced. If the motive of ATLAST is "every product programmable", its means are "every token executable". When you say "2 3 +" in an ATLAST program, you're EXECUTING the literal 2 (in the implementation as well as conceptually), which has the effect of placing 2 on the stack. You next EXECUTE the literal 3, placing it on top of the 2 already there. Finally, you EXECUTE the primitive "+", which adds the two numbers and leaves their sum, 5, on the stack. Not only can you add definitions which are executed just like system primitives, you can also define new data types by creating definitions called "defining words" that allocate and initialize storage and specify a runtime method. I took advantage of this facility to create the REMOVE statement, defined as follows: : remove create does> drop 1 delitem ! ; When you specify: remove dxf:entities:viewport you are actually EXECUTING the definition of REMOVE. This definition performs a CREATE, which causes the next token to be defined an a new, executable definition. Following the CREATE one can allocate and initialize storage for the definition (what are called "instance variables" in object-land). This definition requires no data, so CREATE is followed immediately by DOES>, which introduces the method to be executed when the new definition is itself executed. Whenever a definition made with CREATE is executed, the address of its storage area (instance variables) is passed to it on the stack--how can you know what you need to do if you don't know who you are? Since our definition doesn't need this address, we discard it with DROP. That taken care of, we need only set DELITEM to 1, thereby deleting the current item, and we're done. Recall that REMOVE is declaring a new EXECUTABLE word. When we use it to declare a DXF structure name, we're automatically registering the processing defined by its method to be done whenever that word is triggered. Thus the user can simply write "remove statements", oblivious of the underlying mechanism. We can exploit instance variables to create more sophisticated defining words. Here is a definition that allows us to AND arbitrary groups in a DXF file with any mask we wish: : maskfield create ( Define Data Type: ) , ( Compile bitmask ) does> ( Runtime Method: ) over ( Duplicate group index ) group ( Extract value of group ) swap ( Bitmask address to top ) @ ( Get value of bitmask ) and ( Mask value of field ) swap ( Get group code on top ) setgroup ( Update group in item ) ; We can use this definition to clear the 4 bits in Vertex entities, rather than the less readable explicit definition we used before: 4 not maskfield dxf:*:vertex:70 When this is executed we push 4 on the stack, invert its bits to get our AND mask, and then execute MASKFIELD. It performs a CREATE to define the following token (in this case our structure name), and uses the comma primitive to remove the value from the top of the stack (the AND mask) and store it in the next instance variable field of the word we're defining. When DXFIX encounters a 70 group in a Vertex entity in the input file, it will push the group code, 70, then execute DXF:*:VERTEX:70. The definition pushes its instance variable address and runs the method that follows the DOES> in the definition. That method, in turn, grabs the group index from the second item on the stack (OVER), gets its current value (GROUP), moves the instance variable address, where the mask used by this word has been stored to the top of the stack (SWAP), loads the mask from that location (@), logically ANDs the mask with the value from the group, moves the group code to the top (SWAP), and updates the group in the current item (SETGROUP). Once a defining word like this has been created, the user can use it to simply list the masks and field names to which they should be applied without thinking about how it's all really working. You can create defining words with arbitrarily complicated methods. An example is the definition of TEXTVADJ in "dxfix.dxt", which declares the special processing of Release 11 text vertical alignment groups. Since these groups appear in three different kinds of entities (Text, Attributes, and Attribute Definitions), and bear different group codes in Text and the Attributes, this defining word allows concise declaration of where the changes are to be applied. Interposition processing (hooks) -------------------------------- A translation program can, by defining specified words, interpose its own code before processing of the DXF file commences, supplant the standard loop that processes items from the file, and perform closeout actions after processing of the file is complete. DXF:START If DXF:START is defined, it is executed before the first item is read from the input file. The INBINARY and OUTPREC shared variables are set based on the properties of the input file and the command line arguments. The DXF:START definition can override the OUTPREC setting at this point to, for example, force an ASCII output file for input to AutoCAD versions prior to Release 10. DXF:TRANSLATE If DXF:TRANSLATE is defined, it is invoked instead of the standard loop that reads items from the input file and writes them to the output file. A DXF:TRANSLATE definition that performs the same basic functions as the standard loop is as follows: : dxf:translate begin readitem while writeitem drop repeat ; DXF:END If DXF:END is defined, it is called after all items in the DXF file have been processed, just before the input and output DXF files are closed. If the DXF translation program has, for example, created temporary files, this is an excellent opportunity to close and delete them. Interactive operation --------------------- To ease debugging of translation programs, you can run DXFIX in an interactive mode where you're "talking" directly to the ATLAST interpreter. If you specify the "-I" switch on the DXFIX command line, DXFIX will pause and display the prompt "->" after opening the input file, running the DXF:START definition (if any), and opening the output file. At this point you can enter any primitive provided by ATLAST or DXFIX or invoke any definition in the translation program file. When you enter an end of file character (CTRL-Z followed by RETURN on DOS, CTRL-D on Unix), DXFIX runs the DXF:END definition (if any), closes the input and output files, and exits. Summary ------- DXF files have developed a well-deserved reputation for being easy to read but difficult to process. DXF has always permitted one, in theory, to convert all mutually representable objects between any two versions of AutoCAD, but the reality of the process was often much more difficult and time consuming than the easily-uttered, "You can always edit the DXF..." would lead one to suspect. DXFIX meets the immediate need for converting Release 11 DXF so Release 10 can load it. With little or no additional development time over that required to write a dedicated program for that task, DXFIX can, given proper specifications, perform any DXF translation or modification job that can reasonably be done in a serial scan of the file. Inheriting most of its generality and flexibility merely by including ATLAST, DXFIX provides a textbook case of how open architecture, programmability, and data driven organization can expedite the development and reduce the life cycle costs of even a small utility with a modest charter. The implementation of DXFIX provides a worked example of how ATLAST can be applied to more ambitious products and projects. DXFIX Primitives: Alphabetical Reference ---------------------------------------- ADDGROUP gcode -- Add group to item Adds a new group of type "gcode" to the end of the current item. ARGCOUNT -- n Command line argument count Places the number of command line arguments, including the zeroth argument giving the command name, on the stack. ARGVALUE n -- s Command line argument string Places the address of a temporary string buffer "s" containing a copy of command line argument "n" on the stack. If "n" is less than zero or greater than ARGCOUNT-1, 0 is returned. CLEARITEM -- Clear current item All groups of the current item are deleted. DELGROUP group -- Delete group The group selected by "group" is deleted from the current item. DUMPSPECIAL -- Dump structure name table The table of structure names selected for special processing and DXF objects encountered so far in the file is printed on standard output. GROUP group -- value Group value The value of the group in the current item selected by "group" is placed on the top of the stack. The value is stored as an integer, a floating point value, a triple of floating point values for coordinates, the address of a temporary string buffer, or the address of a temporary string buffer with a binary chunk length on the top of the stack depending on the group's data type. GROUP? group -- flag Test group present If the designated "group" is present in the current item -1 is placed on the top of the stack. If no such group appears in the current item, 0 is returned. GROUPCOUNT -- n Number of groups in item The number of groups in the current item is placed on the top of the stack. This number can be used in conjunction with the -(10000 + n) group specification to scan streams of extended entity data groups with identical group codes. ITEMPOS -- n Current item position The location in the input DXF file at which the current item began is placed on the top of the stack. This is a byte number if the input file is binary or a line number if it is ASCII. Both bytes and lines are numbered from zero. OPTION s1 -- s2 Command line option If a command line specification of the form "-xspec" is present, where "x" is the same as the first character of string "s1", compared without regard to letter case, "s2" is returned as a pointer to a temporary string buffer containing the balance of the option specification. If nothing follows the option letter, "s2" will be a pointer to a null string. If the option selected by "s1" is not present, 0 is returned. PRINTGROUP group -- file Print group The value of the specified "group" of the current item is printed on "file". PRINTITEM -- file Print current item All groups of the current item are printed on "file". READITEM -- flag Read next item The next item from the input DXF file is read and becomes the current item and all processing specified by structure name definitions is performed. If the item was read normally, -1 is placed on the top of the stack; if the end of file is reached or an error is encountered reading the item, 0 is returned. SETGROUP value group -- Set group value The selected "group" is set to the "value" that precedes it on the stack. The form of the "value" depends on the group's data type; see the GROUP primitive for details. WRITEITEM -- flag Write item The current item is written to the output DXF file unless marked for deletion by the setting of DELITEM and/or DELCITEM. If the item was written successfully or deleted, -1 is placed on the stack. If an error occurred writing the item, 0 is returned. DELITEM is cleared to zero, and DELCITEM is cleared if the current item was a Sequence End. Known Problems -------------- Under certain memory conditions, DXFIX may freeze while transforming a drawing that contains a polyface mesh. If you experience this problem, EXPLODE the mesh before creating the DXF file. Refer to the section earlier in this document, "Exploding polyface meshes". AutoCAD is a registered trademark of Autodesk, Inc. DXF, DXFIX, and ATLAST are trademarks of Autodesk, Inc.