Insert OLE Objects with Raster Data

Yuri Moskovsky

April 01, 2021

The INSERTRASTER cross-platform command (see the _InsertRaster_func function) is used to insert files of different raster image formats into .dwg drawings. It is used for pseudo-opening of raster files in ODADrawingsExplorer, where the result can be saved as a .dwg file.

The _InsertRaster_func function is included in the ExCommands.tx module and its source code can be found in the following path: Drawing/Examples/ExCommands/ExInserRaster.cpp.

First the command prompts for the path to the source raster image file.

OdDbUserIOPtr pIO = pCmdCtx->userIO();
OdString path = pIO->getFilePath(L"Select file to insert", OdEd::kGfpForOpen,
                                 L"Select file to insert", L"bmp", L"",
                                 L"Bitmap files (*.bmp)|*.bmp|Jpeg files (*.jpg)|*.jpg|"
                                 L"PNG files (*.png)|*.png|All files (*.*)|*.*||");

Then the command loads the specified raster image.

OdRxRasterServicesPtr pRasSvcs = ::odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME);
OdGiRasterImagePtr pImage = pRasSvcs->loadRasterImage(path);

Then the command transforms the loaded raster image as needed so that the height and width do not exceed 2,048 pixels. This is required for the resulting .dwg drawing containing the image to successfully render in both AutodeskĀ® AutoCADĀ® and ODA Software.

If this step is skipped, raster images larger than 2,048 pixels in height or width are rendered successfully only in ODA Software and only using the OdOleSsItemHandlerModule.tx module.

::odrxDynamicLinker()->loadModule(OdOleSsItemHandlerModuleName);

The ItemHandler module must be loaded before creating an Ole2Frame object. Typically, the application executing the command initially loads the module. In this case the following can be skipped.

::odrxDynamicLinker()->loadModule(OdOlePlatformItemHandlerModuleName);

Then the command creates an Ole2Frame object and inserts the loaded raster image into it.

OdDbDatabase* pDb = OdDbDatabase::cast(pCmdCtx->baseDatabase()).get();
OdDbOle2FramePtr pOleFrame = OdDbOle2Frame::createObject();
pOleFrame->setDatabaseDefaults(pDb);
OdOleItemHandler* pItem = pOleFrame->getItemHandler();
pItem->embedRaster(pImage, pDb);
pOleFrame->setOutputQuality(OdDbOle2Frame::kHighGraphics);
pOleFrame->unhandled_setHimetricSize(OdUInt16(pImage->pixelWidth()),
                                     OdUInt16(pImage->pixelHeight()));

A special tracker is used to set the position of the frame.

struct OleSsInsertTracker : OdEdRealTracker
{
  OdSmartPtr<OdDbOle2Frame> _pOleFrame;
  OdGePoint3d _orig;
  OdGeVector2d _size;
  double _dist;

  void setValue(double dist)
  {
    _dist = dist;
    OdRectangle3d r3d;
    OdGeVector2d size = _size.normal() * _dist;
    r3d.lowLeft .set(_orig.x,          _orig.y,          _orig.z);
    r3d.upLeft  .set(_orig.x,          _orig.y + size.y, _orig.z);
    r3d.upRight .set(_orig.x + size.x, _orig.y + size.y, _orig.z);
    r3d.lowRight.set(_orig.x + size.x, _orig.y,          _orig.z);
    _pOleFrame->setPosition(r3d);
  }
  int addDrawables(OdGsView* pView) { pView->add(_pOleFrame, 0); return 1; }
  void removeDrawables(OdGsView* pView) { pView->erase(_pOleFrame); }
};
OdStaticRxObject<OleSsInsertTracker> tracker;
tracker._pOleFrame = pOleFrame;
OdGeVector2d size(pImage->pixelWidth(), pImage->pixelHeight());
OdDbUserIOPtr pIO = pCmdCtx->userIO();
tracker._orig = pIO->getPoint(L"Specify insertion point <0,0>:",
                              OdEd::kGptDefault, &OdGePoint3d::kOrigin);

// Set initial OLE frame position to origin, and size in world units.
// Convert MM_HIMETRIC (0.01 millimeter) to space units.

double s = pDb->getMEASUREMENT()==OdDb::kEnglish ? 0.01 / 25.4 : 0.01;
tracker._size.set(double(size.x) * s, double(size.y) * s);
OdString sPmt;
sPmt.format(OD_T("Specify size <%g>:"), tracker._size.length());
double dist = pIO->getDist(sPmt, OdEd::kGdsFromLastPoint | OdEd::kGptRubberBand, 0.,
                           OdString::kEmpty, &tracker));
tracker.setValue(dist);

If the r3d rectangle is already known, it is enough to make the following call instead of the above:

pOleFrame->setPosition(r3d);

And finally, the command adds a finished frame to the active layout of the database.

OdDbBlockTableRecordPtr pSpace = pDb->getActiveLayoutBTRId().safeOpenObject(OdDb::kForWrite);
pSpace->appendOdDbEntity(pOleFrame);