| name | acad-images-ole |
| description | Raster images, PDF/DWF/DGN underlays, OLE objects, wipeouts, image clipping |
AutoCAD Images, Underlays, and OLE Objects
Use this skill when inserting or manipulating raster images, PDF/DWF/DGN underlays, OLE embedded objects, or wipeout masking entities in AutoCAD drawings.
Raster Image Definition
Every raster image requires a RasterImageDef stored in the ACAD_IMAGE_DICT dictionary. The definition holds the source file path and resolution; multiple RasterImage entities can reference the same definition.
public static ObjectId GetOrCreateImageDef(Database db, Transaction tr,
string imagePath, string dictKey)
{
ObjectId imageDictId = RasterImageDef.GetImageDictionary(db);
if (imageDictId.IsNull)
imageDictId = RasterImageDef.CreateImageDictionary(db);
DBDictionary imageDict = (DBDictionary)tr.GetObject(
imageDictId, OpenMode.ForWrite);
if (imageDict.Contains(dictKey))
return imageDict.GetAt(dictKey);
RasterImageDef imageDef = new RasterImageDef();
imageDef.SourceFileName = imagePath;
imageDef.Load();
ObjectId defId = imageDict.SetAt(dictKey, imageDef);
tr.AddNewlyCreatedDBObject(imageDef, true);
return defId;
}
RasterImageDefReactor
A RasterImageDefReactor keeps an image definition alive while RasterImage entities reference it. Without a reactor, the definition can be purged prematurely.
RasterImageDefReactor reactor = new RasterImageDefReactor();
reactor.ImageDefId = imageDefId;
btr.AppendEntity(reactor);
tr.AddNewlyCreatedDBObject(reactor, true);
rasterImage.ReactorId = reactor.ObjectId;
Inserting a Raster Image
public static ObjectId InsertRasterImage(Database db, ObjectId imageDefId,
Point3d insertionPt, double widthInDrawingUnits)
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(
db.CurrentSpaceId, OpenMode.ForWrite);
RasterImageDef imageDef = (RasterImageDef)tr.GetObject(
imageDefId, OpenMode.ForRead);
RasterImage image = new RasterImage();
image.SetDatabaseDefaults();
image.ImageDefId = imageDefId;
Vector2d pixelSize = imageDef.Size;
double height = widthInDrawingUnits * (pixelSize.Y / pixelSize.X);
image.Orientation = new CoordinateSystem3d(insertionPt,
Vector3d.XAxis * widthInDrawingUnits,
Vector3d.YAxis * height);
btr.AppendEntity(image);
tr.AddNewlyCreatedDBObject(image, true);
RasterImageDefReactor reactor = new RasterImageDefReactor();
reactor.ImageDefId = imageDefId;
btr.AppendEntity(reactor);
tr.AddNewlyCreatedDBObject(reactor, true);
image.ReactorId = reactor.ObjectId;
image.AssociateRasterDef(imageDef);
tr.Commit();
return image.ObjectId;
}
}
Image Clipping
Clip boundaries are specified in image pixel coordinates (origin at bottom-left corner of the image).
Point2dCollection rectPts = new Point2dCollection();
rectPts.Add(new Point2d(100, 100));
rectPts.Add(new Point2d(400, 300));
rasterImage.SetClipBoundary(ClipBoundaryType.Rectangle, rectPts);
rasterImage.IsClipped = true;
Point2dCollection polyPts = new Point2dCollection();
polyPts.Add(new Point2d(50, 50));
polyPts.Add(new Point2d(300, 50));
polyPts.Add(new Point2d(300, 250));
polyPts.Add(new Point2d(150, 300));
polyPts.Add(new Point2d(50, 50));
rasterImage.SetClipBoundary(ClipBoundaryType.Poly, polyPts);
rasterImage.IsClipped = true;
rasterImage.SetClipBoundaryToWholeImage();
rasterImage.IsClipped = false;
ClipBoundary clip = rasterImage.GetClipBoundary();
Converting Drawing Coordinates to Pixel Coordinates
Matrix3d pixelToModel = rasterImage.PixelToModelTransform;
Matrix3d modelToPixel = pixelToModel.Inverse();
Point3d wcsPoint = new Point3d(50.0, 25.0, 0.0);
Point3d pixelPoint = wcsPoint.TransformBy(modelToPixel);
Point2d clipPt = new Point2d(pixelPoint.X, pixelPoint.Y);
RasterVariables
Drawing-wide image display settings stored in the named object dictionary.
ObjectId rvId = RasterVariables.GetRasterVariables(db);
if (rvId.IsNull)
rvId = RasterVariables.CreateRasterVariables(db);
RasterVariables rv = (RasterVariables)tr.GetObject(rvId, OpenMode.ForWrite);
rv.ImageFrame = ImageFrameType.DisplayedNotPlotted;
rv.ImageQuality = ImageQualityType.High;
rv.UserScale = 1.0;
PDF Underlay
PDF underlays use a definition/reference pair: PdfDefinition + PdfReference. The definition lives in the ACAD_PDFDEFINITIONS named dictionary.
public static ObjectId InsertPdfUnderlay(Database db, string pdfPath,
int pageNumber, Point3d insertionPt, double scale)
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(
db.CurrentSpaceId, OpenMode.ForWrite);
ObjectId pdDictId = UnderlayDefinition.GetDictionaryId(db,
new PdfDefinition().GetType());
if (pdDictId.IsNull)
pdDictId = UnderlayDefinition.CreateDictionary(db,
new PdfDefinition().GetType());
DBDictionary pdfDict = (DBDictionary)tr.GetObject(
pdDictId, OpenMode.ForWrite);
PdfDefinition pdfDef = new PdfDefinition();
pdfDef.SourceFileName = pdfPath;
pdfDef.ItemName = pageNumber.ToString();
ObjectId pdfDefId = pdfDict.SetAt(
"MyPdf_Page" + pageNumber, pdfDef);
tr.AddNewlyCreatedDBObject(pdfDef, true);
PdfReference pdfRef = new PdfReference();
pdfRef.SetDatabaseDefaults();
pdfRef.DefinitionId = pdfDefId;
pdfRef.Position = insertionPt;
pdfRef.ScaleFactors = new Scale3d(scale, scale, scale);
pdfRef.Rotation = 0.0;
btr.AppendEntity(pdfRef);
tr.AddNewlyCreatedDBObject(pdfRef, true);
tr.Commit();
return pdfRef.ObjectId;
}
}
DWF and DGN Underlays
DWF and DGN underlays follow the identical definition/reference pattern with swapped types:
| Format | Definition | Reference | Dictionary | ItemName |
|---|
| PDF | PdfDefinition | PdfReference | ACAD_PDFDEFINITIONS | Page number |
| DWF | DwfDefinition | DwfReference | ACAD_DWFDEFINITIONS | Sheet name |
| DGN | DgnDefinition | DgnReference | ACAD_DGNDEFINITIONS | Model name |
Underlay Clipping and Layer Filtering
Point2dCollection clipPts = new Point2dCollection();
clipPts.Add(new Point2d(0, 0));
clipPts.Add(new Point2d(500, 350));
pdfRef.SetClipBoundary(clipPts);
pdfRef.IsClipped = true;
for (int i = 0; i < pdfRef.UnderlayLayerCount; i++)
{
UnderlayLayer layer = pdfRef.GetUnderlayLayer(i);
string name = layer.Name;
layer.State = UnderlayLayerState.Off;
}
OLE Objects (Ole2Frame)
OLE objects embed or link external documents (Excel, Word, images) in the drawing. The .NET API cannot create new OLE objects programmatically; use COM automation or SendStringToExecute with the INSERTOBJ command instead.
Iterating OLE Objects
foreach (ObjectId entId in btr)
{
Entity ent = (Entity)tr.GetObject(entId, OpenMode.ForRead);
if (ent is Ole2Frame ole)
{
ed.WriteMessage("\nOLE: {0}, Linked={1}", ole.UserType, ole.IsLinked);
if (ole.IsLinked)
ed.WriteMessage(", Path={0}", ole.LinkPath);
ole.UpgradeOpen();
ole.Location = new Point3d(100, 200, 0);
ole.WcsWidth = 50.0;
ole.WcsHeight = 30.0;
}
}
Wipeout Entity
A Wipeout masks entities behind it with the background color. It inherits from RasterImage.
public static ObjectId CreateWipeout(Database db, Point2dCollection boundary)
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(
db.CurrentSpaceId, OpenMode.ForWrite);
Wipeout wipeout = new Wipeout();
wipeout.SetDatabaseDefaults();
wipeout.SetFrom(boundary, Vector3d.ZAxis);
btr.AppendEntity(wipeout);
tr.AddNewlyCreatedDBObject(wipeout, true);
tr.Commit();
return wipeout.ObjectId;
}
}
Point2dCollection pts = new Point2dCollection();
pts.Add(new Point2d(0, 0));
pts.Add(new Point2d(100, 0));
pts.Add(new Point2d(100, 50));
pts.Add(new Point2d(0, 50));
pts.Add(new Point2d(0, 0));
Wipeout Frame and Draw Order
Application.SetSystemVariable("WIPEOUTFRAME", 2);
DrawOrderTable dot = (DrawOrderTable)tr.GetObject(
btr.DrawOrderTableId, OpenMode.ForWrite);
ObjectIdCollection ids = new ObjectIdCollection { wipeoutId };
dot.MoveBelow(ids, entityAboveId);
Gotchas
RasterImageDef.Load() throws if the image file does not exist — verify the path or wrap in try/catch
Orientation on RasterImage uses U and V vectors representing the FULL width and height in drawing units, NOT unit vectors — setting unit vectors produces a 1x1 unit image
RasterImageDefReactor is required to prevent image definitions from being purged — always create and attach one per image entity
RasterImageDef.GetImageDictionary() returns ObjectId.Null if no images exist yet — always check and call CreateImageDictionary() if null
- Image clip boundary coordinates are in pixel space (not drawing units) — use the inverse of
PixelToModelTransform to convert from WCS
ClipBoundaryType.Rectangle requires exactly 2 points (lower-left, upper-right) — ClipBoundaryType.Poly requires 3+ points with first = last to close
- Wipeout boundary must be a closed polygon (first = last point) — if it appears inverted, reverse the point winding order
- PDF underlay
ItemName must be a valid page number string (e.g., "1") — invalid values throw eInvalidValue or eOutOfRange
- Underlay clip coordinates are in the underlay's own coordinate system (relative to insertion and scale), not WCS
Ole2Frame cannot be created programmatically via the .NET API — use COM automation or SendStringToExecute("INSERTOBJ\n") to embed new OLE content
UnderlayDefinition.GetDictionaryId() and CreateDictionary() require the concrete type (e.g., typeof(PdfDefinition)) to locate the correct named dictionary
- Bitonal (1-bit) raster images cannot be adjusted for brightness, contrast, or fade
AssociateRasterDef() must be called AFTER the RasterImage is added to the database — it establishes the reactor connection between image and definition
Related Skills
acad-blocks — images and underlays inserted inside block definitions
acad-layers — layer assignment for image, underlay, wipeout, and OLE entities
acad-geometry — CoordinateSystem3d orientation vectors and Matrix3d transforms for image positioning
acad-xrefs — underlays vs xrefs: underlays are display-only while xrefs bring full DWG geometry