Using Editing Glyphs

This article will explain how to use custom glyphs to represent HTML tags when the MSHTML Editor is activated. A sample application using custom glyphs and its source code is included.

This article is divided into the following topics:

  • Prerequisites and Requirements
  • Overview of Custom Glyphs
  • The Sample Specifications
  • Editing Glyph Architecture
  • Glyph Table String Format
  • Image Resources in DLLs and EXEs
    • The File Name
    • The Resource Type
    • The Resource ID
  • Related topics

Prerequisites and Requirements

In order to understand and use this tutorial, you will need:

  • A good understanding of C++, the Component Object Model (COM), and the Active Template Library (ATL).
  • Microsoft Internet Explorer 5.5 or later installed.
  • Header files for Internet Explorer 5.5 or later for use in your development environment; in particular, Mshtml.h and Mshtmcid.h.

Overview of Custom Glyphs

Editing glyphs are images that represent the tags formatting a Web page. Editing glyphs give users visual clues about the structure of a page, and enable users to do more precise editing than they could without glyphs. The MSHTML Editor enables you to represent most tags with your own custom images. However, some tags can't have glyph images. For instance, a table tag can be assigned an image to show where the table as a whole is anchored in the document, but images cannot be assigned to the tags that format within the table, like td and th. Custom glyphs can only be used from C++, and work only when IHTMLDocument2::designMode is "On."

The Sample Specifications

The sample for this tutorial shows how to use custom editing glyphs in an application. It demonstrates the most important implementation details to consider:

  • How to use IOleCommandTarget::Exec to specify images to display for specific tags.
  • How to format the string data that IOleCommandTarget::Exec needs.
  • How to use image resources in a DLL or executable program to store the images for the editing glyphs.

The specifications of this behavior are:

  • This sample is a simple browser implementation with an address bar and three buttons.
  • The first button turns design mode on and off.
  • The second button turns the custom editing glyphs on and off.
  • The third button turns the default editing glyphs on and off.

Important  You must download the sample to your own computer to run it.

 

The source code for this sample is included in a Microsoft Visual C++ 6.0 workspace. It uses ATL to provide COM support, standard implementations of some of the standard interfaces, and "smart" interface pointers that handle their own reference counting. The project source code can be downloaded at the Editing Glyphs Sample Source Page.

Many of the interface pointers in the sample are wrapped by the CComQIPtr class. CComQIPtr is a "smart" pointer class. Besides automatic reference counting, it provides overloaded operators to make working with COM easier. CComQIPtr includes an overloaded assignment operator (operator=) which performs an automatic QueryInterface during assignment. For instance, the sample's QueryInterface call to initialize its IOleCommandTarget pointer looks like this:

// m_spWebBrowser is an already initialized pointer to an IWebBrowser2 interface.
// m_spOleCmdTarg is a declared but uninitialized CComQIPtr for an IOleCommandTarget interface.

m_spOleCmdTarg = m_spWebBrowser;

Editing Glyph Architecture

Editing glyphs are controlled using the IOleCommandTarget::Exec method with command identifiers taken from the CGID_MSHTML command group. These commands are defined in mshtmcid.h. You can obtain an IOleCommandTarget interface by calling QueryInterface on either the IWebBrowser2 or IHTMLDocument2 interface.

// Assume pWebBrowser is a valid pointer to the IWebBrowser2 interface
IOleCommandTarget* pCmdTarg;
pWebBrowser->QueryInterface(IID_IOleCommandTarget, (void**)&pCmdTarg);

There are a number of glyph commands available, but the only ones relevant to custom glyphs are IDM_ADDTOGLYPHTABLE, IDM_EMPTYGLYPHTABLE, and IDM_REPLACEGLYPHCONTENTS. The other commands (IDM_SHOWALLTAGS, IDM_SHOWALIGNEDSITETAGS, IDM_SHOWSCRIPTTAGS, IDM_SHOWSTYLETAGS, IDM_SHOWAREATAGS, IDM_SHOWCOMMENTTAGS, IDM_SHOWUNKNOWNTAGS, IDM_SHOWWBRTAGS, and IDM_SHOWMISCTAGS) only work with the default glyphs provided with Windows Internet Explorer. These default glyphs are a legacy from Microsoft Internet Explorer 4.0. Since this article is concerned with custom glyphs, it won't discuss the commands that manipulate default glyphs. The important thing to mention here is that IDM_SHOWALLTAGS overrides all others. It must be previously unset—or be set to false if previously set—for the others to function.

For custom glyphs, the most important command is IDM_ADDTOGLYPHTABLE. This command adds an entry or entries to the glyph table. An entry in the glyph table tells Internet Explorer to display an image for a specific tag and the conditions under which to display it. Add glyph table entries as strings with specific formatting to delimit the fields in each entry. The strings are passed to the glyph table in the fourth argument of IOleCommandTarget::Exec. (See the section Glyph Table String Format for the specifics of writing glyph table string entries.) Here is one way to add two entries to the glyph table that assign the images pbgn.gif and pend.gif to the opening and closing p tags:

// Declare a VARIANT data type and initialize it
VARIANT var;
V_VT(&var) = VT_BSTR; // Sets the type to BSTR

// Load the first glyph table entry string into var
V_BSTR(&var) = L"%%p^^%%http://www.imaginarydomain.com/glyphs/pbgn.gif^^%%0^^%%3^^%%3^^%%4^^%%20^^%%15^^%%20^^%%15^^**";

// Exec IDM_ADDTOGLYPHTABLE to add an image for opening P tag
hr = pCmdTarg->Exec(&CGID_MSHTML, IDM_ADDTOGLYPHTABLE, OLECMDEXECOPT_DODEFAULT, &var, NULL);

// Load the second glyph table entry string into var
V_BSTR(&var) = L"%%p^^%%http://www.imaginarydomain.com/glyphs/pend.gif^^%%1^^%%3^^%%3^^%%4^^%%20^^%%15^^%%20^^%%15^^**";

// Exec IDM_ADDTOGLYPHTABLE to add an image for closing P tag
hr = pCmdTarg->Exec(&CGID_MSHTML, IDM_ADDTOGLYPHTABLE, OLECMDEXECOPT_DODEFAULT, &var, NULL);

This example adds the table entries for each tag one at a time. You can add multiple entries at once by concatenating the individual entry strings and passing them in one call to IOleCommandTarget::Exec. This is how the sample creates its glyph table. It stores its strings as resources in a string table and then adds them all in one call to IOleCommandTarget::Exec. Here's how the sample program fills the glyph table using the CComBSTR class:

CComBSTR bstrTemp;
CComBSTR bstrGlyphTable;

// Build Glyph Table
bstrGlyphTable.LoadString(IDS_PBEG_GLYPHSTRING);
bstrTemp.LoadString(IDS_PEND_GLYPHSTRING);
bstrGlyphTable.AppendBSTR(bstrTemp);
bstrTemp.LoadString(IDS_T_GLYPHSTRING);
bstrGlyphTable.AppendBSTR(bstrTemp);
bstrTemp.LoadString(IDS_BR_GLYPHSTRING);
bstrGlyphTable.AppendBSTR(bstrTemp);
bstrTemp.LoadString(IDS_SCRIPT_GLYPHSTRING);
bstrGlyphTable.AppendBSTR(bstrTemp);
bstrTemp.LoadString(IDS_COMMENT_GLYPHSTRING);
bstrGlyphTable.AppendBSTR(bstrTemp);
bstrTemp.LoadString(IDS_STYLE_GLYPHSTRING);
bstrGlyphTable.AppendBSTR(bstrTemp);
bstrTemp.LoadString(IDS_H3STATIC_GLYPHSTRING);
bstrGlyphTable.AppendBSTR(bstrTemp);
bstrTemp.LoadString(IDS_H3ABS_GLYPHSTRING);
bstrGlyphTable.AppendBSTR(bstrTemp);

//Load Glyph Table into VARIANT
VariantInit(&var);
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstrGlyphTable;

// Exec IDM_ADDTOGLYPHTABLE
hr = m_spOleCmdTarg->Exec(&CGID_MSHTML, IDM_ADDTOGLYPHTABLE, OLECMDEXECOPT_DODEFAULT, &var, NULL);

The images you use for your custom glyphs can be GIF or BMP images. They need to be available through a URL or through a res protocol locator, which references resources contained in a DLL or executable. For more information on using the res protocol with custom glyphs, see the section in this article entitled Image Resources in DLLs and EXEs.

Once a custom glyph is added to the glyph table, it will display immediately. To turn off the glyphs, empty the glyph table with the command IDM_EMPTYGLYPHTABLE.

V_VT(&var) = VT_BOOL;
V_BOOL(&var) = VARIANT_TRUE;

hr = pCmdTarg->Exec(&CGID_MSHTML, IDM_EMPTYGLYPHTABLE, OLECMDEXECOPT_DODEFAULT, &var, NULL);

IDM_EMPTYGLYPHTABLE empties the entire glyph table. It's not possible to remove individual entries. You can use IDM_REPLACEGLYPHCONTENTS to change the glyph table, but you must rebuild the table completely each time IOleCommandTarget::Exec is called with IDM_REPLACEGLYPHCONTENTS. You use this command the same way as IDM_ADDTOGLYPHTABLE: by passing the string for the new glyph table in the fourth parameter to IOleCommandTarget::Exec.

Another way to turn off the glyphs is to issue the command IDM_SHOWALLTAGS with the VARIANT argument set to VARIANT_FALSE. Doing so empties the table just like IDM_EMPTYGLYPHTABLE. Each time you want to display glyphs again, the glyph table needs to be rebuilt.

Leaving design mode also turns off glyphs and empties the glyph table.

Glyph Table String Format

The delimiters for the fields and rows in a glyph table string are:

  • %% begins an entry.
  • ^^ ends an entry.
  • ** ends a row.

Each row consists of ten entries:

  1. Tag ID: You identify the tag for each image by the tag name, e.g. "P" or "table." Tag names are not case sensitive. Use the word "comment" to specify an image for comment tags. Set the tag type field to 0 for a "comment" entry.

    %%comment^^%%res://edglyphs.exe/GIFS/comment.gif^^%%0^^%%3^^%%3^^%%4^^%%20^^%%15^^%%20^^%%15^^**
    
  2. Glyph Location: You can use a URL or a resource identifier to an image in a DLL or EXE.

  3. Tag Type: 0 means the image is for the opening tag only. 1 means the image is for the closing tag only. 2 means the image is for both the opening and closing tags. Single tags like br and comment must be added with the tag type set to 0. Tags that don't normally display, like style and script, can only have a beginning tag; their tag type must be set to 0. Rectangular elements—like a table, an img, or any element with implicit or explicit width or height attributes—can only have a beginning tag to show where they are anchored. You don't need the tag type set to 0 for rectangular elements. In the sample, for instance, H4 elements have an image for both the opening and closing tags, but only a single image displays when the H4 element is rectangular.

  4. Alignment (rectangular elements only): This field indicates that an image is for an element with an alignment attribute. Left = 0, center = 1, right = 2, and undefined = 3. Left, right, or center attributes must be explicitly set on the element.

  5. Positioning information: Determines what Cascading Style Sheets (CSS) positioning value the glyph applies to. Static positioning = 0, absolute positioning = 1, relative positioning = 2, and all = 3. This field enables you to specify one glyph for a tag when it is not positioned and another glyph to show an anchor point when the tag is positioned. See the declarations for H3 in the sample:

    // This entry sets the opening H3 tag image
    %%h3^^%%res://edglyphs.exe/GIFS/h3bgn.gif^^%%0^^%%3^^%%0^^%%4^^%%20^^%%15^^%%20^^%%15^^**
    // This entry sets the closing  H3 tag image
    %%h3^^%%res://edglyphs.exe/GIFS/h3end.gif^^%%1^^%%3^^%%0^^%%4^^%%20^^%%15^^%%20^^%%15^^**
    // This entry sets the image for an absolutely positioned H3 element
    %%h3^^%%res://edglyphs.exe/GIFS/anchor.gif^^%%0^^%%3^^%%2^^%%4^^%%20^^%%15^^%%20^^%%15^^**
    

    In this example, the opening and closing tags of statically positioned H3 elements have images. Elements are statically positioned by default, so these images display when no positioning property has been set. Absolutely positioned H3 elements have an image to show where the H3 is anchored in the document. But relatively positioned H3 elements display the opening H3 tag image, h3bgn.gif, for their anchor point.

  6. Direction: This field specifies an image for a tag based on the reading order of the current language. 0 specifies left to right. 1 specifies right to left. 2 specifies top to bottom. 3 specifies bottom to top. 4 specifies all. Generally speaking, you'll set this field to 4.

  7. Image Width: Set this field to the width of the image in pixels.

  8. Image Height: Set this field to the height of the image in pixels.

  9. Reserved: Set this field to the image width.

  10. Reserved: Set this field to the image height.

Image Resources in DLLs and EXEs

The res protocol is a way of addressing resources (images, strings, HTML pages, and so on) stored in an executable or a DLL. If you use images packaged in an executable or DLL for your custom glyph images, be aware of a few implementation details. A res address consists of three parts after the res:// protocol identifier, each separated by a forward slash:

**res://File Name/[Resource Type (may not be needed)/]**Resource ID

The File Name

The file name can be given as a full path or as a relative path.

Important  When you're giving a path in a glyph table entry string, you must use double backslashes for each backslash, since the backslash is the special character to begin escape sequences (for example, res://c:\\images\\glyphs.dll/GIFS/anchor). Backslashes will occur when your image resources are in a DLL or executable file that's separate from your application. The sample is self-contained, so it has no backslashes in its glyph strings.

 

When given as a relative path, the system will search for the DLL or executable, first relative to the directory from which the application loaded, next relative to the current directory, next relative to the Windows system directory, next relative to the Windows directory, and finally relative to the directories listed in the PATH environment variable.

The Resource Type

Resources are typed in the resource script. There are a number of standard types, like BITMAP, MENU, HTML, CURSOR, and ICON. You can also have custom types to categorize resources. For the purposes of editing glyphs, you will be dealing with GIF or BITMAP resources.

GIF resources are not one of the standard resource types. They can be handled in two ways. You can define a custom type for them, such as "GIFS." The type specification in the res address will then be the custom type name.

res://edglyphs.exe/GIFS/anchor

This is the way the sample handles its GIF resources.

Another option is to classify GIF resources under one of the standard types. The best standard type to use is HTML because the HTML resource type is the default type for res protocol addresses. If you classify your GIFs as HTML resources, you do not need to specify a resource type in your res address:

res://edglyphs.exe/anchor

This address will correspond to the following entry in the .rc file:

ANCHOR                      HTML    DISCARDABLE     "glyphs\\anchor.gif"

If you use bitmaps, you can also classify them under the HTML standard type to eliminate the need for the type information in the res address. If you use the standard type for bitmaps, identify it by number, not by name. Use "#2" for the bitmap resource type:

res://glyphs.dll/#2/anchor

This address will correspond to the following entry in the .rc file:

anchor             BITMAP  DISCARDABLE     "anchor.bmp"

The number 2 corresponds to the standard resource type identifier symbol RT_BITMAP defined in winuser.h and must be preceded by the pound sign (#). You can't use the symbol RT_BITMAP itself in the res address.

The Resource ID

Resources are usually identified by a symbol, like IDB_BITMAP1, which is then given a number value automatically in C++ with a #define statement. The res protocol format in the glyph table will not recognize this symbol as long as it is defined as a number by a #define statement. There are a few options for dealing with this:

Use the number corresponding to the symbol

If, for instance, you had a image whose symbol was IDR_GIFS20, there might be a #define statement like this for it:

#define IDR_GIFS20 251

You could reference this image with the following res address:

res://glyphs.dll/GIFS/#251

The number must be preceded by a pound sign (#).

Remove the #define statement

Without it, the symbol is just a string that can be used directly in the res address:

res://glyphs.dll/GIFS/IDR_GIFS20

Give the image resource your own name

Since strings can be used for IDs, as the second option shows, you can change the standard symbol to anything you like. To edit the resource ID in C++, right click on the resource and choose Properties. Change the ID value to a string enclosed in quotation marks, as shown in the following screen shot.

You could also edit the .rc file directly in a text editor. Notice that there are no quotation marks in the file when edited directly.

/////////////////////////////////////////////////////////////////////////////
//
// GIFS
//

ANCHOR                      GIFS    DISCARDABLE     "glyphs\\anchor.gif"
BR                          GIFS    DISCARDABLE     "glyphs\\br.gif"

Tip: To open an .rc file as text in C++, click the Open command on the File menu, choose the .rc file, and change the Open As option to Text.

The res address for the anchor.gif image will be:

res://glyphs.dll/GIFS/anchor

The symbols are not case-sensitive.

Give the image resource your own number

Edit the resource ID to any number you like. Notice that there are no quotation marks in the dialog box.

Or, when edited directly, the resource ID looks like:

/////////////////////////////////////////////////////////////////////////////
//
// GIFS
//

1                       GIFS    DISCARDABLE     "glyphs\\anchor.gif"
2                       GIFS    DISCARDABLE     "glyphs\\br.gif"
.
.
.

The res address for the anchor.gif image will be:

res://glyphs.dll/GIFS/#1

The number must be preceded by a pound sign (#).

Conceptual

Introduction to MSHTML Editing

Activating the MSHTML Editor

Modifying Documents in Edit Mode

Using the MSHTML Editor's Extra Features

Implementing IHTMLEditHost

About Edit Designers