How do I create a virtual PDF Device Content (PDFDC)?

Q: We are looking for a PDF SDK that will allow us to get a virtual
Device Context that we can specify its resolution and paper dimension.
We want to be able to use our C++ software to use WIN32 calls to write
into the DC to create PDF documents whether there are any printer
drivers installed on the system or not. Will any of your software
packages provide us with these options?
----------
A: We are about to release a new version of PDFNet SDK (v.5.
http://www.pdftron.com/pdfnet) and PDFDC (PDF Device Context) will be
one of the new features.

PDFDC will enable you to write into the DC to create PDF documents
whether there are any printer drivers installed on the system or not.
DotNet developers will also be able to create .NET PDF Graphics object
and use it for PDF generation without having to learn more advanced
PDF creation APIs available in PDFNet.

Attached is the header and some sample code illustrating the use of
the new class. Pre-release version is available to existing clinets.

//-----------------------------------------
/**
* PDFDC can be used to create a PDF compatible GDI Display Context.
*/
class PDFDC
{
public:
  PDFDC();
  ~PDFDC();

  /**
  * Create an empty EMF and returns a the GDI Display Context.
  *
  * @param in_doc pointer to the document which will hold the
converted
  * data including embedded fonts and objects.
  * @return a GDI Handle to Display Context.
  */
  HDC Begin( PDFDoc & in_doc, double dpi = 72);

/**
  * Closes the EMF Display Context, converts to PDF and returns the
resulting Form
  * XObject. The XObject can be placed on a PDF page or any content
stream.
  */
  SDF::Obj End();

private:
  TRN_PDFDC m_pdfdc;
};

//-------------------

#include <windows.h>
#include <math.h>
#include <assert.h>
#include <tchar.h>
#include <strsafe.h>

#include <iostream>
#include <PDF/PDFNet.h>
#include <PDF/PDFDoc.h>
#include <PDF/PDFDC.h>
#include <PDF/ElementBuilder.h>
#include <PDF/ElementWriter.h>
#include <SDF/Obj.h>
#include <iostream>

using namespace pdftron;
using namespace SDF;
using namespace PDF;
using namespace std;

// Relative path to the folder containing test files.
string input_path = "../../TestFiles/";
string outputPath = "../../TestFiles/Output/";

// lfHeight; lfWidth; lfEscapement; lfOrientation; lfWeight; lfItalic;
lfUnderline; lfStrikeOut; lfCharSet;
// lfOutPrecision; lfClipPrecision; lfQuality; lfPitchAndFamily;
// lfFaceName[LF_FACESIZE];
static const LOGFONTW ArialFont =
{
  24,
  0,
  200,
  200,
  FW_NORMAL,
  FALSE,
  FALSE,
  FALSE,
  ANSI_CHARSET,
  0,
  0,
  DEFAULT_QUALITY,
  VARIABLE_PITCH | FF_SWISS,
  L"Arial"
};

void OnDraw(HDC hDC)
{
  LONG xMin = 0;
  LONG yMin = 0;
  LONG xMax = 500;
  LONG yMax = 500;

  SetGraphicsMode( hDC, GM_ADVANCED );
  SetMapMode( hDC, MM_ANISOTROPIC );
  SetViewportExtEx( hDC, xMax, yMax, NULL );
  SetWindowOrgEx( hDC, 0, 0, NULL );
  SetWindowExtEx( hDC, xMax, yMax, NULL );

  SelectObject(hDC, GetStockObject( WHITE_BRUSH ) );
  Rectangle( hDC, 0, 0, xMax, yMax );

  SetBkMode(hDC, TRANSPARENT);
  HBRUSH hBrush = CreateSolidBrush( RGB(128,0,0) );
  SelectObject( hDC, hBrush );
  Ellipse(hDC, xMin, yMin, xMax, yMax);

  SetTextColor( hDC, RGB(255,255,0) );

  HFONT hArialFont;
  hArialFont = CreateFontIndirectW( &ArialFont );
  assert( hArialFont );

  LONG x1_org, y1_org;
  LPCWSTR label;

  x1_org = xMax / 2;
  y1_org = yMax / 2;

  SelectObject(hDC, hArialFont );
  SetTextAlign( hDC, TA_CENTER | TA_TOP );
  label = L"EMF 2000 - The Future is Now";
  ExtTextOutW( hDC, x1_org, y1_org, ETO_OPAQUE, NULL, label, wcslen
(label), NULL );

  DeleteObject( hArialFont );
  DeleteObject( hBrush );
}

int main()
{

  int ret = 0;
  HDC currentHdc;

  PDFNet::Initialize();

  try
  {
    PDFDoc pdfdoc;
    PDFDC pdfDc;
    Page page = pdfdoc.PageCreate();
    ElementWriter writer;
    writer.Begin(page);
    ElementBuilder eb;

    currentHdc = pdfDc.Begin( pdfdoc );

    OnDraw(currentHdc);

    SDF::Obj obj = pdfDc.End();

    Element element = eb.CreateForm(obj);
    Rect cropBox = page.GetCropBox();
    // TODO: assume that the page is in portrait, scale up the FormX
object and center it vertically
    element.GetGState().SetTransform(cropBox.x2, 0.0, 0.0, cropBox.x2,
0, (cropBox.y2 - cropBox.x2) / 2.0);
    writer.WriteElement(element);
    writer.End();
    pdfdoc.PagePushBack(page);

    string outputFile = outputPath + "Emf2PdfTest.pdf";
    pdfdoc.Save(outputFile.c_str(), SDFDoc::e_remove_unused, NULL);

    // An example of using the high-level PDFNet API to read existing
annotations,
    // to edit existing annotations, and to create new annotation from
scratch.

    cout << "Saved " << outputPath.c_str() << "\nDone. " << endl;
  }
  catch(Common::Exception& e)
  {
    cout << e << endl;
    ret = 1;
  }
  catch(...)
  {
    cout << "Unknown Exception" << endl;
    ret = 1;
  }

  PDFNet::Terminate();
  return ret;
}

Q: We want to be able to have our drawing commands to the DC captured
as PDF objects rather than rasterizing. We would want to set the page
size of the DC in inches and then set the DPI, or set the size in
pixels and inches. That would make the document portable. Can that be
done with PDFNet SDK?
------
A: Drawing command would be captured as PDF object and the output PDF
file would remain vector based (i.e. GDI command would not be
converted to raster image).

... the DC in inches and then set the DPI, or set the size in pixels and
inches. That would make the document portable. Can that be done with
your SDK.

You could use standard GDI functions to specify the extents on the
device context (e.g. using SetViewportExtEx or SetWindowExtEx). If
these are not called PDFNet automatically determines the extent of
graphics. The DPI can be specified with PDFDC.SetDPI() method, but it
is usually ne requred because it only applies to device depended
features in GDI (such as bitmap patterns, raster ops, etc). Please
note that this parameter does not affect the size of EMF on PDF not
does it affect the resolution of embedded images.

You can specify the size and position of EMF on PDF page as show in
the following sample (using pdfdc.Begin(page, box)):

PDFNet::Initialize();
try
{
  // Start with a PDFDoc to put the picture into, and a PDFDC to
translate GDI to PDF
  PDFDoc pdfdoc;
  PDFDC pdfDc;

  // Create a page to put the GDI content onto
  Page page = pdfdoc.PageCreate();

  // Begin the translation from GDI to PDF.
  // Provide the page to place the picture onto, and the bounding box
for the content.
  // We're going to scale the GDI content to fill the page while
preserving the aspect
  // ratio.
  // Get back a GDI Device Context
  HDC hDC = pdfDc.Begin( page, page.GetCropBox() );

  // Create GDI resources and draw GDI content
  HFONT hArialFont = CreateFontIndirectW( &ArialFont );
  SelectObject(hDC, hArialFont );

  Ellipse(hDC, -30, -30, 30, 30);
  SetTextAlign(hDC, TA_CENTER | TA_BOTTOM);
  TextOutW(hDC, 0, ArialFont.lfHeight / 2, L"Hello World", 11);
  DeleteObject(hArialFont);

  // Complete the translation
  pdfDc.End();

  // Add the page to the document
  pdfdoc.PagePushBack(page);

  // Save the PDF document
  std::string outputFile = outputPath + "PDFDCTest.pdf";
  pdfdoc.Save(outputFile.c_str(), SDF::SDFDoc::e_remove_unused, NULL);

  std::cout << "Saved " << outputPath.c_str() << "\nDone.\n";
}
catch(Common::Exception& e)
{
  std::cout << e << std::endl;
  ret = 1;
}

-----

You can download a pre-release demo version of PDFNet SDK using the
following link:
  http://www.pdftron.com/zPDFNET-5AP2/PDFNetC.zip

Since you are interested primarily in PDF Device Context you may want
to take a look at PDFDC sample project (located under PDFNet/Samples/
PDFDCTest).