How to Write a Plug-in to Calculate Bounding Boxes for Text

This document describes

Bounding Boxes (bBoxes) for Text

Graphical applications require a great deal of flexibility in the way that they display text labels for design elements. OpenAccess provides three types of objects that support the display of text labels:

These objects are categorized as shapes because they all have characteristics common to shapes such as a layers, purposes, transforms, and bounding boxes (bBoxes). Unique to the text shapes are attributes such as font, text alignment, and height. These attributes determine how the text string appears in graphical applications. OpenAccess also uses these attributes to determine the bBox for the text based on an internal algorithm. This calculated bBox is used by editing applications for object selection as well as by the OpenAccess region query plug-in.

Applications can apply special formatting to text labels. For example, the application might use custom fonts. In such cases, the bBox calculated by OpenAccess might not be appropriate.

OpenAccess provides a plug-in infrastructure that lets an application calculate its own bBoxes for text labels. It is recommended that the application provide this capability with a plug-in component, and this document is tailored to this approach.

It is also possible to provide a text bBox calculator as an in-memory component, as explained in Creating an In-Memory Text bBox Calculator near the end of this document.

bBox Plug-In Infrastructure

A text bBox calculator is responsible for calculating the bounding boxes of oaText, oaTextDisplay, and oaEvalText objects. The following sections describe how OpenAccess, an application built on OpenAccess, and a text bBox calculator plug-in should communicate and transfer data.

The actual tasks involved in creating the plug-in as well as an example follow these sections.

Calculation of bBoxes for oaText Objects

An application starts the process by modifying an oaText object, for example, by calling the oaText::setHeight function. This triggers OpenAccess to call IText::getBBox(), which starts the process of recalculating the bBox. The oaText object is passed through the IText interface to the bBox calculator. The calculator then accesses attributes such as the height and origin of the oaText object in order to calculate the bBox.

The bounding box is returned to OpenAccess through the IText interface. OpenAccess caches and persistently stores this bBox on the object. Note that the plug-in is responsible for calculating bBoxes for all text objects—not only the ones that have been formatted by the application.

Invalidating a bBox

An application might alter a text label in a way that is not understood directly by OpenAccess. For example, the application might use TrueType fonts to visually display text labels. If there are changes to the TrueType display attributes and the application wants these changes reflected in the bBox, the application must invalidate the text bBox, which causes the plug-in to recalculate it. The ITextInvalidate class provides this functionality. When a text bBox calculator plug-in is registered or loaded, it receives a handle to the ITextInvalidate interface through the IText::init function. The plug-in should provide a way to pass this ITextInvalidate object to the application, which can then call the appropriate ITextInvalidate::invalidate function.

Calculation of bBoxes for oaTextDisplay Objects

Graphical applications can use oaTextDisplay objects to display text strings that represent attributes and properties of design objects.

An oaTextDisplay object can be one of the following:

If the application changes the attribute of the object being displayed, the bBox must be recalculated. The flow of information is the same as the flow of information for oaText objects shown in the previous figure. The difference is in the various actions that trigger the flow (represented by in the following figure.)

In another scenario, the application might change the name of the owning net for an oaAttrDisplay object, in which case the bBox must be recalculated. Because oaAttrDisplay objects display name mapped strings, the plug-in must select the appropriate name space to perform the name mapping and calculate the bBox based on the name mapped string.

Note that the application can also invalidate the bBox as described in Invalidating a bBox.

Calculation of bBoxes for oaEvalText Objects

For oaEvalText objects, another plug-in is used for evaluating the text string. This plug-in is accessed whenever getText is called on an oaEvalText object.

An application starts the process by modifying the oaEvalText object. This triggers OpenAccess to call IText::getBBox(). The oaEvalText object is passed through the IText interface to the bBox calculator. The bBox calculator calls getText, which calls the text evaluator plug-in. The evaluator plug-in passes the evaluated text string back to oaEvalText, and then to the bBox plug-in. The bBox is returned to OpenAccess through the IText interface. OpenAccess caches and persistently stores this bBox on the object.

Note that the application can also invalidate the bBox as described in Invalidating a bBox.

Creating a Text bBox Calculator Plug-in

Before starting this project, you might want to read How to Write a Plug-In, which is a more generic document describing the general concept of plug-ins and how they work.

You can create a text bBox calculator plug-in by creating a shared library that is loaded by OpenAccess at runtime. The shared library must do the following:

In addition, you must Create a plug-in registration file.

Creating a Plug-in Factory

OpenAccess provides the oaCommon::Factory<T> templated class for defining a factory object for a plug-in. The factory object is used to create instances of a plug-in. Each plug-in has a classID, which is stored in a factory table. When OpenAccess needs to create an instance of a plug-in, it uses the classID to find the factory for the plug-in. For more information about creating a plug-in factory, see Understanding the Plug-in Factory in How to Write a Plug-in.

Implementing the Entry Point Function

You must also implement the getClassObject entry point function. When called, this function returns a pointer to the plug-in factory. For more information about implementing the getClassObject entry point function, see Implementing the Entry Point Function in How to Write a Plug-in.

Defining a Class Derived from PlugInBase<IText>

To create a text bBox calculator plug-in, you must define a new class derived from the PlugInBase<IText> class. The new class must implement all virtual functions of the IText class. Refer to the API documentation for IText for a detailed description about these virtual functions.

Link Between OpenAccess and the Plug-In

Applications can use the oaTextLink class to register a text bBox calculator plug-in. Only one such plug-in can be registered per session. If an application registers a plug-in, it is recommended that this be done after the call to oaDesignInit, but before a design is opened.

This function registers the text bBox calculator plug-in:

setIText(const oaString &plugInName) 

If an application does not register a plug-in, OpenAccess loads the default bBox calculator plug-in. An application can denote their bBox plug-in as the default plug-in. Refer to Step 7 in the Example for details.

Creating the Plug-in Registration File

The plug-in registration file registers the plug-in shared library. The registration file can be installed in $(OA_HOME)/data/plugins when the plug-in shared library is installed. Alternatively, the OA_PLUGIN_PATH environment variable can be used to reference a .plg file outside of $(OA_HOME)/data/plugins. Refer to Writing the Plug-In Registration File in How to Write a Plug-In for more information.

Example: Creating a Plug-in Text bBox Calculator Named myIText

A plug-in is a shared library that is installed in the OpenAccess installation hierarchy in the $(OA_HOME)/data/plugins directory. Applications load the plug-in at runtime to select a customized implementation. The following describes the steps for creating a plug-in.

  1. Define the entry point function getClassObject():
    extern "C" long getClassObject(const char  *classID,
                                   const Guid  &interfaceID,
                                   void        **ptr);
      
  2. Implement the entry point function as:
    long
    getClassObject(const char  *classID,
                   const Guid  &interfaceID,
                   void        **ptr)
    {
        return FactoryBase::getClassObject(classId, interfaceID, ptr);
    }
    
  3. Define a new class derived from PlugInBase<IText>, and implement the required functions.
    For example:
    class myIText : public PlugInBase<IText> {
      public:
        virtual void getBBox(const oaText  *text,
                             oaBox         &box);
    
        virtual void getBBox(const oaTextDisplay  *textDisplay,
                             oaBox                &box);
    
        virtual void getName(oaString  &name);
    
        virtual void init(ITextInvalidate  *textInvalidate); 
    	.
    	.
    	. 
         
    };
      
  4. Optionally implement an ICompatibility::validate function to verify whether or not the version of OpenAccess is compatible with the plug-in. The validate() function should check the OpenAccess API version and either throw an exception with a description of the requirements or simply return false. REVIEWERS: Need to describe how an app might check if an interface has an ICompatibility component and if so, how to use it.
    virtual bool                validate();
  5. Declare a Factory static member variable (inside the class myIText):

    class myIText : public PlugInBase<IText> {
       .
       .
       .
         private:
           static Factory<myIText> myFactory;
    };


    Note: Factory is a class defined in the oaCommon namespace that resides in the oaCommon library.

  6. Initialize the Factory static member variable (of class myIText):

    Factory<myIText>	 myIText::myFactory("myIText")
  7. Write the plug-in registration file myIText.plg with the following contents:
    <?xml version="1.0" encoding="utf-8" ?>
    <plugIn lib="myAppTextLibrary"/>
    
    The registration file can be installed in $(OA_HOME)/data/plugins when the plug-in shared library is installed. Alternatively, the OA_PLUGIN_PATH environment variable can be used to reference a .plg file outside of $(OA_HOME)/data/plugins. Refer to Writing the Plug-In Registration File in How to Write a Plug-In for more information.

  8. (Optional) Designate that your bBox calculator is the default plug-in by editing $(OA_HOME)/data/oaTextSystem.plg as follows:

    <?xml version="1.0" encoding="utf-8" ?>
    <plugIn treatAs="myAppTextLibrary"/>
    

Creating an In-Memory Text bBox Calculator

To create an in-memory text bBox calculator:

  1. Define a new class derived from PlugInBase<IText>, and implement the required functions.
    For example:
    class myIText : public PlugInBase<IText> {
      public:
        virtual void getBBox(const oaText  *text,
                             oaBox         &box);
    
        virtual void getBBox(const oaTextDisplay  *textDisplay,
                             oaBox                &box);
    
        virtual void getName(oaString  &name);
    
        virtual void init(ITextInvalidate  *textInvalidate);
        virtual bool validate();
    };
  2. Instantiate an object of the class defined in the previous step:
     myIText   *myText = new myIText;

  3. Register your in-memory bBox calculator, as follows:
oaTextLink::setIText(myText);

Copyright © 2001-2010 Cadence Design Systems, Inc.
All rights reserved.