RTF Format and Open Design Drawings product

Andrew Markovich

August 09, 2018

RTF (Rich Text Format) is a document file format widely supported by most text processing software (Microsoft® Word®, Apache OpenOffice™ and so on). It is able to store all core text properties (size, font, color, style, justification, paragraph and so on). This format is also supported natively by the Microsoft Windows® clipboard, so client applications are able to access RTF content from other applications.

Teigha Drawings has an OdDbMText entity that can parse RTF content and set up the multiline text format using a single call from the OdDbMText class method:

int setContentsRTF(const OdString& text);

Working with RTF content

There are two main ways to access existing RTF content:

  • from the Windows clipboard
  • from an .rtf file

Getting RTF content from the Windows clipboard

This example demonstrates how to get existing RTF data from the Windows clipboard. This code works only on the Microsoft Windows operating system. For other platforms, clipboard methods can differ.

OdString rtfContents;
{ // Extract RTF contents from Windows clipboard
  UINT formatId = ::RegisterClipboardFormat(CF_RTF);
  // Check that RTF format available in clipboard
  if (::IsClipboardFormatAvailable(formatId))
  { // Read RTF contents from clipboard
    if (::OpenClipboard(NULL))
    {
      HGLOBAL hDataPtr = ::GetClipboardData(formatId);
      if (hDataPtr)
      {
        LPSTR pStr = (LPSTR)::GlobalLock(hDataPtr);
        if (pStr)
        {
          rtfContents = pStr;
          ::GlobalUnlock(hDataPtr);
        }
      }
      ::CloseClipboard();
    }
  }
}

This code first checks the availability of RTF content in the Windows clipboard and copies the content into a Teigha OdString using Windows API functions.

Next we can create an MText entity using the available RTF data:

if (!rtfContents.isEmpty())
{ // Create MText finally
  OdDbMTextPtr pMText = OdDbMText::createObject();
  pMText->setDatabaseDefaults(pDb);
  pMText->setContentsRTF(rtfContents);
  OdDbBlockTableRecord::cast(pDb->getActiveLayoutBTRId().safeOpenObject(OdDb::kForWrite))->appendOdDbEntity(pMText);
}

Using this code, a client application can paste the RTF content that was copied to the Windows clipboard by another application.

Here is an example of pasting RTF clipboard content from Microsoft Word into the OdaMfcApp sample application:

image1

Another application that supports RTF clipboard content is Microsoft Visual Studio. You can similarly copy selected parts of text and copy them to the OdaMfcApp sample application (Microsoft Visual Studio uses a different color scheme for viewing Clipboard contents, so the resulting colors can differ):

image2

Getting RTF content from a file

You can also read RTF content directly from a file. The Teigha API provides cross-platform functionality to work with RTF files.

The following example reads an RTF file as an ASCII string for further parsing to an MText entity:

OdString rtfContents;
if (!fileName.isEmpty())
{
  OdStreamBufPtr pBuf = ::odSystemServices()->createFile(fileName);
  if (!pBuf.isNull())
  { // Load ASCII data from file
    OdAnsiCharArray asciiData;
    asciiData.resize(pBuf->length());
    pBuf->getBytes(asciiData.asArrayPtr(), pBuf->length());
    asciiData.push_back(0);
    // Convert to string
    rtfContents = asciiData.getPtr();
  }
}

After, you can create an MText entity using similar code from the previous section in this article.

Full command source code

This is the full source code for the command that creates an OdDbMText entity using RTF content from a file or from the Windows clipboard:

class OdExMTextRtfContentsCmd : public OdEdCommand
{
  public:
    const OdString groupName() const
    {
      return OD_T("Test Commands");
    }
    const OdString globalName() const
    {
      return OD_T("ExMTextRtfContents");
    }
    void execute(OdEdCommandContext* pCmdCtx)
    { // Get required command interfaces
      OdDbCommandContextPtr pDbCmdCtx(pCmdCtx);
      OdDbDatabasePtr pDb = pDbCmdCtx->database();
      OdSmartPtr<OdDbUserIO> pIO = pDbCmdCtx->userIO();
      OdString rtfContents;
      if (pDbCmdCtx->userIO()->getKeyword(OD_T("Extract RTF contents from File or Clipboard [File Clipboard]?"), OD_T("File Clipboard")) == 0)
      { // Extract RTF contents from file
        OdString fileName = pIO->getFilePath(OD_T("Select file to open"), OdEd::kGfpForOpen,
                                             OD_T("Select file to open"), OD_T("rtf"), OD_T(""), "RTF files (*.rtf)|*.rtf");
        if (!fileName.isEmpty())
        {
          OdStreamBufPtr pBuf = ::odSystemServices()->createFile(fileName);
          if (!pBuf.isNull())
          { // Load ASCII data from file
            OdAnsiCharArray asciiData;
            asciiData.resize(pBuf->length());
            pBuf->getBytes(asciiData.asArrayPtr(), pBuf->length());
            asciiData.push_back(0);
            // Convert to string
            rtfContents = asciiData.getPtr();
          }
        }
      }
      else
      { // Extract RTF contents from Windows clipboard
        UINT formatId = ::RegisterClipboardFormat(CF_RTF);
        // Check that RTF format available in clipboard
        if (::IsClipboardFormatAvailable(formatId))
        { // Read RTF contents from clipboard
          if (::OpenClipboard(NULL))
          {
            HGLOBAL hDataPtr = ::GetClipboardData(formatId);
            if (hDataPtr)
            {
              LPSTR pStr = (LPSTR)::GlobalLock(hDataPtr);
              if (pStr)
              {
                rtfContents = pStr;
                ::GlobalUnlock(hDataPtr);
              }
            }
            ::CloseClipboard();
          }
        }
      }
      if (rtfContents.isEmpty())
      {
        pIO->putString(OD_T("Rtf contents doesn't found."));
        return;
      }
      OdGePoint3d insertionPoint = pIO->getPoint(OD_T("Specify MText insertion point:"));
      // Create MText finally
      OdDbMTextPtr pMText = OdDbMText::createObject();
      pMText->setDatabaseDefaults(pDb);
      pMText->setLocation(insertionPoint);
      pMText->setContentsRTF(rtfContents);
      OdDbBlockTableRecord::cast(pDb->getActiveLayoutBTRId().safeOpenObject(OdDb::kForWrite))->appendOdDbEntity(pMText);
    }
};
static OdStaticRxObject<OdExMTextRtfContentsCmd> g_OdExMTextRtfContentsCmd;

Conclusion

Internally, conversion between Rich Text Format and MText isn’t a simple thing (look at the RTF data stored by Microsoft Word, in Notepad for example). Teigha parses all tags from RTF content and represents the data inside an OdDbMText entity using specific MText keys. But this functionality is very simple to use in a client application because all the work is done internally, and client code can access the RTF content using just one simple method call.