Introduction to Rich Text
Domino and Notes use rich text fields to store a variety of objects, including text, tables, document links, bitmaps, and OLE links. Rich text fields have several advantages over other types of fields:
- Paragraphs in rich text can have mixed attributes, such as indenting, justification, and spacing
- Text in rich text can have mixed attributes such as font face, color, and point size
- A single rich text field can hold several megabytes of data
This chapter introduces the structure of rich text and explains how to access the individual CD records that constitute a rich text field. Later chapters explain the details of document links, OLE links, and other objects.
Creating Rich Text
The IBM C API for Domino and Notes provides high-level and low-level ways to create rich text. The high-level way is simple but limited. The low-level way is complex but provides access to advanced features such as tables, pop-ups, and OLE links.
High-Level Access to Rich Text
The CompoundTextxxx family of C API functions provide a high-level way to create rich text. These functions implement an abstraction for rich text that allows C API code to treat a rich text field as an object. We refer to this rich text object as a "compound text context." Use the functions below to create, delete, and manipulate compound text as an object:
Use these high-level functions to create rich-text fields that contain text and document links. The text can have any type face or paragraph style. One advantage of these high-level functions is that C API programs that use them do not need to perform host/canonical conversion to be portable to platforms such as UNIX.
For more information on these functions, see the Reference.For example usage, see the sample program EASYRICH.
Low-Level Access to Rich Text
To take advantage of the advanced features of rich text, such as tables, pop-ups, and OLE links, C API programs must access the individual CD records that constitute a rich text field.
Low-Level Structure of Rich Text
A rich text field in a note consists of one or more items of data type TYPE_COMPOSITE. A single rich text field may consist of multiple items of type TYPE_COMPOSITE, so long as all the items have the same name.
The data in an item of type TYPE_COMPOSITE consists of a series of records called CD records. ("CD" stands for "Compound Document" or "Composite Data.")
To add an item of TYPE_COMPOSITE to a note, prepare a buffer that consists of a series of CD records, and then use NSFItemAppend to append this buffer to the note. The data in the buffer must be in Domino canonical format.
NOTE: Some C API programs do not perform host/canonical conversion when accessing low-level structures in rich text. Canonical conversion is not strictly necessary for programs that run only on Intel-architecture platforms such as Windows. However, source code that does not perform host/canonical conversion will not run on platforms such as UNIX. Source code that does perform host/canonical conversion will run on any platform supported by Domino and Notes. For more information on canonical format requirements, read the "Domino Canonical Format" chapter in this guide.
To prepare the buffer of CD records, initialize each CD structure in turn and then use ODSWriteMemory to convert each CD structure to Domino canonical format. Store the canonical format result in the buffer as the next CD record.
Domino and Notes define many different types of CD records. The C API header file editods.h contains the type definitions for each CD structure.
Every CD record begins with a header, which starts with a signature byte. The signature byte identifies the type of the header and the type of the CD record that follows.
The three types of headers, defined in file ods.h, are BSIG, WSIG, and LSIG. Each header includes a length member, which specifies the entire length of the CD record, including the header. Code can use this length to offset from the start of one record to the start of the next record.
NOTE: In a Composite Data buffer, every CD record must begin on an even byte boundary. If the length member of a given CD record is odd, the next CD record begins length+1 bytes after the start of the given record.
Also note that you initialize the length member of a CD record header with the ODSLength of the CD record, not the "sizeof" the data structure. The length member must specify the length of the record in Domino canonical format.
Rich text must satisfy a set of size limits. First, the total size of any CD record is limited by the size of the length field in the header. For a record with a byte signature (BSIG), the total size is limited to 254 bytes. For a record with a word signature, the total size is limited to approximately 64k. The actual limit is specified by the constant MAXONESEGSIZE.
Second, the size of any rich text item must be less than MAXONESEGSIZE. If a rich text item exceeds this size, it must be stored as separate items with the same name. Domino or Notes will assemble these items in order when the records are read from the file.
Third, the total size of a paragraph of rich text is also limited to MAXONESEGSIZE. However, large elements such as bitmaps, metafiles, and file attachments are not included in the stored size of a paragraph, and are not counted in this limit. When creating rich text, CDPARAGRAPH records must be inserted to ensure that the paragraph data such as CDTEXT records does not exceed this limit.
Finally, the size of a rich text record stored on disk may be different from the storage required when Domino or Notes manipulates the records internally. Additional storage may be used for information that only applies to the Notes user interface. To allow for this expansion, a practical limit on the size of a rich text item or a paragraph is approximately 40k.
The C API provides the function EnumCompositeBuffer to simplify parsing a buffer of CD records.
The data in a simple rich-text field containing text consist of a series of four CD records.
A CDPABDEFINITION structure defines the "style" of a paragraph. This structure contains fields in which to specify margins, line justification, tab stops, and other style attributes. Each CDPABDEFINITION has a unique ID. Subsequent paragraphs in the rich text field use this ID to identify the CDPABDEFINITION that defines its style. Each CDPABDEFINITION structure may be used by many paragraphs in the rich text field.
A CDPARAGRAPH structure marks the start of each new paragraph. Rich-text fields are composed of one or more paragraphs.
CDPABREFERENCE structures specify which paragraph style is to be used in the current paragraph. If a CDPABREFERENCE is not specified for a paragraph, the style of the previous paragraph is used.
NOTE: CDPABREFERENCE structures only refer to styles that have already been defined. Forward references to style definitions are not allowed. While it is not required, we recommend that you define all styles at the beginning of the buffer so you can reference the styles as needed.
A CDTEXT structure defines the start of a run of text. The FontID member of CDTEXT specifies the color, size, and font of this run of text. The actual text string is appended to the buffer immediately following the CDTEXT structure.
Writing A Rich Text Field in a Document
This section examines the sample program DYNAMIC. DYNAMIC creates a document in a database and appends several fields to the document, including a rich text field.
The code fragments below create a rich text field in a document by setting up a buffer that contains four CD records. It creates each CD record by initializing a data structure and then converting the structure to Domino canonical format. It stores the converted canonical data in the buffer. After preparing the buffer, it calls NSFItemAppend to append the buffer to the document as the data value of the rich text field.
The diagram below depicts the data in the buffer created by the code fragments. The diagram is not drawn to scale. The sizes of the structures in a rich-text field vary considerably.
|Paragraph Definition - CDPABDEFINITION|
|Paragraph Header - CDPARAGRAPH|
|Paragraph Reference - CDPABREFERENCE |
|Text Header - CDTEXT|
|"Hello World... "|
WORD wBuffLen; /* required buffer length */
|Creating a CDPABDEFINITION Structure in Sample Program DYNAMIC|
BYTE *rt_field; /* allocated rich-text field */
BYTE *buff_ptr; /* position in allocated memory */
CDPABDEFINITION pabdef; /* rich-text paragraph style */
/* ... steps missing ... */
rt_field = (BYTE *) malloc ( wBuffLen );
if( rt_field == (BYTE *)NULL )
/* Keep a pointer to our current position in the buffer. */
buff_ptr = rt_field;
/* Initialize a CDPABDEFINITION structure.We use all defaults,
except for centered justification.
pabdef.Header.Signature = SIG_CD_PABDEFINITION;
pabdef.Header.Length = ODSLength( _CDPABDEFINITION );
pabdef.PABID = PARA_STYLE_ID;
pabdef.JustifyMode = JUSTIFY_CENTER;
pabdef.LineSpacing = DEFAULT_LINE_SPACING;
pabdef.ParagraphSpacingBefore = DEFAULT_ABOVE_PAR_SPACING;
pabdef.ParagraphSpacingAfter = DEFAULT_BELOW_PAR_SPACING;
pabdef.LeftMargin = DEFAULT_LEFT_MARGIN;
pabdef.RightMargin = DEFAULT_RIGHT_MARGIN;
pabdef.FirstLineLeftMargin = DEFAULT_FIRST_LEFT_MARGIN;
pabdef.Tabs = DEFAULT_TABS;
pabdef.Tab = DEFAULT_TAB_INTERVAL;
pabdef.Flags = 0;
/* Call ODSWriteMemory to convert the CDPABDEFINITION structure to
Domino canonical format and write the converted structure into
the buffer at location buff_ptr. This advances buff_ptr to the
next byte in the buffer after the canonical format strucure.
ODSWriteMemory( &buff_ptr, _CDPABDEFINITION, &pabdef, 1 );
This code dynamically allocates a buffer in which to build the rich-text item. It then fills in all the fields of the CDPABDEFINITION structure. The PABID member is the unique identifier for this style definition. In the above code, this parameter is set to 1. If other CDPABDEFINITION structures are defined in the same rich-text field, each one must have its PABID member set to a unique value.
After setting all members of the structure, the code calls ODSWriteMemory to convert the CDPABDEFINITION structure to Domino canonical format and store the result in the allocated buffer. ODSWriteMemory advances the buff_ptr to point to the next byte in the allocated buffer after the converted CDPABDEFINITION record.
CDPARAGRAPH para; /* rich-text paragraph header */
|Creating a CDPARAGRAPH Structure in Sample Program DYNAMIC |
/* Put a paragraph header in the field. */
para.Header.Signature = SIG_CD_PARAGRAPH;
para.Header.Length = (BYTE) ODSLength( _CDPARAGRAPH );
ODSWriteMemory( &buff_ptr, _CDPARAGRAPH, ¶, 1 );
This code initializes the CDPARAGRAPH structure and then converts it to Domino canonical format, storing the result in the buffer. ODSWriteMemory advances the buffer pointer to the next available byte in the buffer.
|Creating a CDPABREFERENCE Structure in Sample Program DYNAMIC |
/* Put a paragraph reference block in the field. Specify
PARA_STYLE_ID so that this paragraph uses the style
ref.Header.Signature = SIG_CD_PABREFERENCE;
ref.Header.Length = (BYTE) ODSLength( _CDPABREFERENCE );
ref.PABID = PARA_STYLE_ID;
ODSWriteMemory( &buff_ptr, _CDPABREFERENCE, &ref, 1 );
This code initializes the CDPABREFERENCE structure and then converts it to Domino canonical format, storing the result in the buffer. ODSWriteMemory advances the buffer pointer to the next available byte.
CDTEXT cdtext; /* rich-text text header */
|Creating Two CDTEXT Structures in Sample Program DYNAMIC |
char szString1 = "Hello world... ";
WORD wString1Len = strlen( szString1 );
FONTIDFIELDS *pFontID; /* font definitions in text header */
/* Add the CDTEXT record to the field. A CDTEXT record consists
of a CDTEXT structure followed by a run of text. Initialize the
CDTEXT structure by filling in the signature and the length.
The CDTEXT structure also contains the font information that
controls how Domino or Notes displays this first run of text.
cdtext.Header.Signature = SIG_CD_TEXT;
cdtext.Header.Length = ODSLength( _CDTEXT ) + wString1Len ;
pFontID = (FONTIDFIELDS *) &(cdtext.FontID);
pFontID->Face = FONT_FACE_SWISS;
pFontID->Attrib = ISBOLD;
pFontID->Color = NOTES_COLOR_BLUE;
pFontID->PointSize = 24;
ODSWriteMemory( &buff_ptr, _CDTEXT, &cdtext, 1 );
/* Write the actual characters of this first text run to the buffer.
Since the run of text may contain embedded null characters, use
memcpy, not strcpy. No need to terminate the run of text with a
null because the Header.Length member of the CDTEXT structure
specifies the length explicitly.
memcpy( (char *)buff_ptr, szString1, wString1Len );
buff_ptr += wString1Len;
This code fragment adds a CDTEXT record to the buffer. A CDTEXT record consists of a CDTEXT structure followed by a run of text characters. The CDTEXT portion of the record defines certain attributes of the text, including the font face, color, and point size and must be converted to Domino canonical format. Immediately following this structure, we append the run of text characters that will be displayed with the specified attributes. The run of characters is not converted to canonical format.
DWORD rt_size; /* size of rich-text field */
|Appending the Rich Text Buffer to the Note in Sample Program DYNAMIC|
STATUS error; /* return code from API calls */
/* We are done filling the buffer with CD records. Now append the
buffer to the note as a rich text field. First find the size of
the buffer. Then add the rich-text field to the note by calling
NSFItemAppend. NSFItemAppend copies the data out of the buffer
specified by rt_field. Therefore, after calling NSFItemAppend, we
can free the buffer.
rt_size = (DWORD)(buff_ptr - rt_field);
error = NSFItemAppend( note_handle,
rt_field, rt_size );
free( rt_field );
This code fragment gets the total length of the data in the buffer and calls NSFItemAppend.
NSFItemAppend adds an item to the note specified by note_handle. The name of the field in the note is "RICH_TEXT." The data type parameter, TYPE_COMPOSITE, specifies that this is a rich text field. The pointer rt_field specifies the buffer of data, which must be in Domino canonical format. The rt_size parameter specifies how much data is in the buffer.
Not shown above are the subsequent call to NSFNoteUpdate to save the new note to disk and the code that closes the note and the database.