Extended Data and Dictionaries
So far, we have created, modified, and read entities with their standard geometric properties: points, radii, layers, colors. But in a real program, you will often need to attach your own information to an entity — a part number, a supplier reference, a link to another entity. AutoLISP offers several mechanisms for this, from the simplest to the most powerful.
Extended Data (xdata)
Extended data (xdata) allows you to attach custom information to any entity in the drawing. This data is invisible to the user — it does not display on screen — but your program can read and modify it.
Registering an Application
Before attaching xdata, you must register an application name with regapp. This name serves as an identifier to group your data:
(regapp "WIIP_PIPING")
regapp returns the application name if registration succeeds, or nil if the name is already registered (which is not an error). In practice, you call regapp at the beginning of your program without worrying about the return value.
Convention: choose a unique application name that clearly identifies your program. A prefix with your company name helps avoid collisions with other developers.
Attaching xdata to an Entity
Xdata is added to the -3 group of an entity's DXF list. Each piece of data has a specific DXF code:
| Code | Data Type | Example |
|---|---|---|
| 1000 | String | (1000 . "DN100") |
| 1001 | Application name (start of an xdata group) | (1001 . "WIIP_PIPING") |
| 1005 | Handle reference (link to another entity) | (1005 . "1A3F") |
| 1010 | 3D point | (1010 0.0 0.0 0.0) |
| 1040 | Real number | (1040 . 114.3) |
| 1070 | 16-bit integer, from -32,768 to 32,767 — typical for color indices and flags | (1070 . 42) |
| 1071 | 32-bit long integer, from -2,147,483,648 to 2,147,483,647 — for large values | (1071 . 100000) |
Here is how to attach xdata to the last entity created:
;; Register the application
(regapp "WIIP_PIPING")
;; Get the last entity
(setq entity (entlast))
(setq data (entget entity))
;; Add the xdata
(setq data
(append data
(list
(list -3
(list "WIIP_PIPING"
(cons 1000 "DN100") ; Nominal diameter
(cons 1040 114.3) ; Outside diameter in mm
(cons 1070 10) ; Nominal pressure (PN)
)
)
)
)
)
;; Apply the modification
(entmod data)
The -3 code tells AutoCAD that what follows is a block of extended data. Inside, the data is grouped by application name.
Reading xdata from an Entity
By default, entget does not return xdata. You must explicitly request the data for a given application:
;; Read the entity WITH its xdata
(setq data (entget (entlast) '("WIIP_PIPING")))
;; Extract the xdata group
(setq xdata (assoc -3 data))
The result of (assoc -3 data) looks like:
(-3 ("WIIP_PIPING"
(1000 . "DN100")
(1040 . 114.3)
(1070 . 10)
)
)
To extract a specific value:
;; Get the application data
(setq app-data (cdadr (assoc -3 data)))
;; Find the nominal diameter
(cdr (assoc 1000 app-data)) ; → "DN100"
;; Find the outside diameter
(cdr (assoc 1040 app-data)) ; → 114.3
Complete Example: Labeling Entities
This program attaches a unique identifier to each selected entity, then allows you to retrieve it:
(defun c:label (/ selection-set index entity data identifier)
(regapp "WIIP_ID")
(setq selection-set (ssget))
(if selection-set
(progn
(setq index 0)
(repeat (sslength selection-set)
(setq entity (ssname selection-set index))
(setq identifier (strcat "ID-" (itoa (1+ index))))
;; Read the entity
(setq data (entget entity))
;; Add the xdata
(setq data
(append data
(list
(list -3
(list "WIIP_ID"
(cons 1000 identifier)
)
)
)
)
)
(entmod data)
(setq index (1+ index))
)
(princ (strcat "\n" (itoa (sslength selection-set)) " entity(ies) labeled."))
)
(princ "\nNo selection.")
)
(princ)
)
(defun c:read-label (/ selection entity data xdata)
(setq selection (entsel "\nSelect an entity: "))
(if selection
(progn
(setq entity (car selection))
(setq data (entget entity '("WIIP_ID")))
(setq xdata (assoc -3 data))
(if xdata
(princ (strcat "\nIdentifier: " (cdr (assoc 1000 (cdadr xdata)))))
(princ "\nThis entity has no identifier.")
)
)
(princ "\nNo selection.")
)
(princ)
)
Extension Dictionaries
Xdata is perfect for attaching data to a specific entity. But what if you want to store information at the drawing level itself — for example, your program's configuration, a global counter, or a lookup table? That is the role of extension dictionaries.
The Named Object Dictionary
Every AutoCAD drawing contains a Named Object Dictionary (NOD). It is the root of the dictionary tree. You access it with the namedobjdict function:
(setq root-dictionary (namedobjdict))
This function returns the entity name of the root dictionary. You can then search in it or add entries to it.
Searching a Dictionary
The dictsearch function looks up an entry in a dictionary by name:
;; Search for a dictionary named "WIIP_CONFIG"
(setq result (dictsearch (namedobjdict) "WIIP_CONFIG"))
If the entry exists, dictsearch returns its DXF list. Otherwise, it returns nil.
Storing Data with Xrecords
An xrecord is a database object that can hold arbitrary data. It is the equivalent of a record in a database. You create it with entmakex (a variant of entmake that returns the entity name):
;; Create an xrecord
(setq xrecord
(entmakex
(list
'(0 . "XRECORD")
'(100 . "AcDbXrecord")
(cons 1 "Industrial piping") ; String
(cons 70 42) ; Integer
(cons 40 3.14) ; Real
)
)
)
Standard DXF codes are used to type the data in an xrecord:
| Code | Type |
|---|---|
| 1 | String |
| 10 | 3D point |
| 40 | Real number |
| 70 | Integer |
| 300-309 | Additional strings |
Adding an Xrecord to the Dictionary
Once the xrecord is created, you attach it to the named object dictionary with dictadd:
(dictadd (namedobjdict) "WIIP_CONFIG" xrecord)
Complete Example: Persistent Configuration
This program stores and reads a configuration in the drawing:
(defun save-configuration (project-name scale / xrecord existing)
;; Check if the configuration already exists
(setq existing (dictsearch (namedobjdict) "WIIP_CONFIG"))
(if existing
;; Remove the old xrecord
(dictremove (namedobjdict) "WIIP_CONFIG")
)
;; Create a new xrecord
(setq xrecord
(entmakex
(list
'(0 . "XRECORD")
'(100 . "AcDbXrecord")
(cons 1 project-name)
(cons 40 scale)
)
)
)
;; Add it to the dictionary
(dictadd (namedobjdict) "WIIP_CONFIG" xrecord)
(princ (strcat "\nConfiguration saved for " project-name))
(princ)
)
(defun read-configuration (/ result)
(setq result (dictsearch (namedobjdict) "WIIP_CONFIG"))
(if result
(progn
(princ (strcat "\nProject: " (cdr (assoc 1 result))))
(princ (strcat "\nScale: " (rtos (cdr (assoc 40 result)) 2 2)))
)
(princ "\nNo configuration found.")
)
(princ)
)
;; Usage
(defun c:save-config (/ name scale)
(setq name (getstring T "\nProject name: "))
(setq scale (getreal "\nScale: "))
(save-configuration name scale)
)
(defun c:read-config ()
(read-configuration)
)
The decisive advantage of dictionaries is that the data is persistent: it is saved with the DWG file and survives across AutoCAD sessions.
Xdata or Dictionary: How to Choose?
| Criterion | Xdata | Extension Dictionary |
|---|---|---|
| Scope | Attached to an entity | At the drawing level |
| Maximum size | 16 KB per application per entity | No practical limit |
| Complexity | Simple | More complex |
| Use case | Custom properties of an entity | Global configuration, data tables |
As a general rule: if the data is tied to a specific entity, use xdata. If it concerns the drawing as a whole, use a dictionary.
Linking Entities Together Through Handles
Every entity in the drawing has a handle — a unique identifier in the form of a hexadecimal string. You find it in DXF code 5:
(setq data (entget (entlast)))
(cdr (assoc 5 data)) ; → "1A3F" (for example)
This handle remains stable: it does not change when you save and reopen the drawing. This is what makes it ideal for creating links between entities.
Creating a Link Between Two Entities
Imagine you want to link an annotation text to the line it describes. The idea is to store the line's handle in the text's xdata:
(defun c:link-annotation (/ line-selection line line-data line-handle
text-selection text text-data)
(regapp "WIIP_LINK")
;; Select the line
(setq line-selection (entsel "\nSelect the line: "))
(if (null line-selection)
(progn (princ "\nNo selection.") (princ) (exit))
)
(setq line (car line-selection))
(setq line-data (entget line))
(setq line-handle (cdr (assoc 5 line-data)))
;; Select the text
(setq text-selection (entsel "\nSelect the text: "))
(if (null text-selection)
(progn (princ "\nNo selection.") (princ) (exit))
)
(setq text (car text-selection))
(setq text-data (entget text))
;; Attach the line's handle to the text via xdata
(setq text-data
(append text-data
(list
(list -3
(list "WIIP_LINK"
(cons 1005 line-handle) ; Code 1005 = handle reference
)
)
)
)
)
(entmod text-data)
(princ (strcat "\nLink created to handle " line-handle))
(princ)
)
The xdata code 1005 is specifically designed to store handle references. This is not trivial — AutoCAD treats it differently from a plain string (code 1000).
Finding the Linked Entity
To find an entity from its handle, use handent:
(defun c:follow-link (/ selection entity data xdata target-handle target)
(setq selection (entsel "\nSelect the annotation: "))
(if (null selection)
(progn (princ "\nNo selection.") (princ) (exit))
)
(setq entity (car selection))
(setq data (entget entity '("WIIP_LINK")))
(setq xdata (assoc -3 data))
(if xdata
(progn
(setq target-handle (cdr (assoc 1005 (cdadr xdata))))
(setq target (handent target-handle))
(if target
(progn
(princ (strcat "\nLinked entity found: " target-handle))
;; Highlight the target entity
(redraw target 3)
)
(princ "\nThe linked entity no longer exists in the drawing.")
)
)
(princ "\nThis entity has no link.")
)
(princ)
)
WBLOCK and Handle Translation
This is a crucial point to understand. When you use WBLOCK (write a block to an external file), AutoCAD translates the handles: entities receive new handles in the destination file. And thanks to code 1005, AutoCAD knows that the stored value is a handle and automatically updates the reference. This is why you should not use code 1000 (ordinary string) to store a handle — AutoCAD would not know that it needs to translate it.
However, during an internal copy (for example with _COPY or (command "_COPY" ...)), there is no handle translation. The copy retains the same xdata values as the original, including handle references. Both entities will therefore point to the same target. This is a behavior to keep in mind: after a copy, you may need to update the links manually.
| Operation | Handle translation (code 1005) |
|---|---|
| WBLOCK (export to external file) | Yes — references are updated |
| INSERT (import from external file) | Yes — references are updated |
Internal COPY (_COPY) |
No — references remain identical |
| COPYCLIP / PASTECLIP | Yes — similar treatment to WBLOCK/INSERT |
Summary
| Concept | Function | Description |
|---|---|---|
| Register an application | regapp |
Declares an application name for xdata |
| Read xdata | (entget ename '("APP")) |
Retrieves the extended data of an entity |
| Root dictionary | (namedobjdict) |
Accesses the named object dictionary |
| Search a dictionary | (dictsearch dict "name") |
Looks up an entry by name |
| Add to dictionary | (dictadd dict "name" xrec) |
Attaches an xrecord to the dictionary |
| Entity handle | (cdr (assoc 5 data)) |
Unique and stable identifier |
| Entity from handle | (handent "1A3F") |
Finds an entity by its handle |
In the next chapter, we will see how to automatically react to drawing modifications using reactors.
Need an AutoCAD (AutoLISP, ObjectARX, .NET, VBA) development? Contact me for a free quote.