Creating and Rendering PDF Attachments for .dgn Files

Sergey Sherstnev

August 15, 2019

The ODA Platform works with PDF in many ways. One common feature is creating and rendering PDF attachments for drawings using the RxPdfToRaster services module. The process is similar for both .dgn and .dwg files, and this article describes how it works with .dgn drawings.

First create an empty database:

OdDgDatabasePtr pDb = app->createDatabase();

Then prepare the PDF converter module (there will be no conversion, but we need to get some information from the .pdf file):

OdRxPdfToRasterServicesPtr pSvcs = odrxDynamicLinker()->loadApp(RX_PDF_TO_RASTER_SERVICES_APPNAME);
pConverter = pSvcs->createConverter();

Load the .pdf file and get the necessary information:

pConverter->loadPdf(L"file.pdf");
OdUInt16 count = pConverter->getPagesCount();
pConverter->setActivePage(2); //first page is active by default
OdPdfLayerArray layers;
pConverter->getLayers(layers); //we can enable/disable necessary layers in attachment with set layers[index].is_on
OdGsDCRect pageSize;
pConverter->getPageSize(pageSize); //page size measured in 1/72 inches

Prepare the raster attachment for .dgn:

OdDgModelPtr pModel = pDb->getActiveModelId().openObject(OdDg::kForWrite);
OdDgRasterFramePtr pRasterFrame = OdDgRasterFrame::createObject();
OdDgRasterAttachmentHeaderPtr raster = OdDgRasterAttachmentHeader::createObject();

pModel->addElement(raster);
pModel->addElement(pRasterFrame);

raster->setFilename(L"file.pdf");

Put the necessary settings to the attachment and save the attachment to the .dgn drawing:

//calculate size in meters
double width = (double)pageSize.m_max.x * .0254 / 72.;
double height = (double)pageSize.m_max.y  * .0254 / 72.;

OdDgModel::StorageUnitDescription description;
pModel->getStorageUnit(description);

//calculate size in master units
width = width * description.m_numerator / description.m_denominator;
height = height * description.m_numerator / description.m_denominator;

raster->setExtent(OdGePoint2d(width, height));
int iDefaultDpi = 96; //96 is default system resolution
#if defined (ODA_WINDOWS)
  DISPLAY_DEVICE dd;
  dd.cb = sizeof(dd);
  if (EnumDisplayDevices(NULL, 0, &dd, 0) != FALSE)
  {
    HDC hDC = CreateDC(dd.DeviceName, dd.DeviceName, NULL, NULL);
    int iDpiX = GetDeviceCaps(hDC, LOGPIXELSX);
    int iDpiY = GetDeviceCaps(hDC, LOGPIXELSY);
    DeleteDC(hDC);
    iDefaultDpi = iDpiX > iDpiY ? iDpiX : iDpiY;
  }
#endif
pRasterFrame->setScanningResolution(OdGePoint2d(iDefaultDpi, iDefaultDpi));
pRasterFrame->setRasterReferenceId(raster->id());
pRasterFrame->setPrintableFlag(true);
pRasterFrame->setPageNumber(2);
for (OdUInt16 i = 0; i < layers.size(); i++)
  pRasterFrame->setPdfLayerVisibility(layers[i].layer_name, layers[i].is_on);

And redraw it:

pModel->fitToView();

To render the attachments, no specific actions are required if the compiler you’re using supports C++11 (vc14/15, gcc 4.8 and higher, etc).