Publisher 2003: Creating and Managing Linked Text Boxes

(Note: This is the third in a series of entries that aim to introduce experienced programmers to the Publisher object model.

The first entry covered creating Web pages programmatically. You can read it here.

The second entry covered working with wizards and templates. You can read it here.)

While the way you work with text in the Publisher object model is very similar to how you work with text in other word processing applications, such as Word, in does differ in one very important aspect: because Publisher is a desktop design and publishing application, it provides you to ability to include multiple text flows in a single publication, and programmatically control how those flows are laid out and formatted.

For example, suppose you needed to create a newsletter. You would probably want to include several different articles, each with their own distinct content and formatting. In addition, a given article might continue from one page to another; those pages might not be contiguous. The final result might look something like the three-page newsletter in Figure 1.

 

Figure 1. Lay out of a sample newsletter.

In Publisher, each distinct flow of text is referred to as a story. A given story may span one or more textboxes, on one or more pages. Those pages need not be contiguous. Consider the sample newsletter in Figure 1. The first story is contained in a single text box on the first page. The second story starts in a text box on page one, then concludes in another textbox on page three. Story three is contained in three text boxes: two on page two, and a single text box on page three.

Publisher lets you link multiple text boxes together to contain a story, and automatically manages how text flows from one text box to the next. If there isn't room to display all the story text in the first text box, the text flows into the second, and so on. If there's too much text to display in the final text box, Publisher stores the remaining text in an overflow buffer. Publisher automatically adjusts the amount of text contained in each text box as you resize the textboxes, format the text, etc.

Using the Story Object to Manage Text

When working with story text through the Publisher object model, it is important to distinguish between the story itself, and the individual text boxes that contain it. Each story in a given publication is represented by a Story object contained in the Document.Stories collection. Operations performed on a Story object affect the entire story, regardless of the textboxes in which it is contained. For example, the following code sets the font size for a story to 12 points, for all the text boxes that contain that story.

Activedocument.Stories(2).TextRange.Font.Size=12

The following example, on the other hand, set the font size for only the story text contained in the specific textbox:

Activedocument.Pages(1).Shapes("Story2TextBox1") _

.TextFrame.TextRange.Font.Size=12

You can not create or delete stories through the Stories collection. Rather, when you add a textbox to the publication, you are also adding a new story to the publication. For example, the following code adds a textbox, and therefore a story, to the active publication, even though no text has been specified for the text box.

ActiveDocument.Pages(1).Shapes.AddTextbox _

  Orientation:=pbTextOrientationHorizontal, _

  Left:=72, Top:=72, Width:=200, Height:=200

If you then deleted the text box, or linked it to an existing text box, the number of stories in the publication would decrease by one. Use the Stories.Count property to return the number of stories in a publication at a given time.

To delete a story, you must delete each text box that contains the story text.

Use the TextFrame property to access the text frame of the first text box of a story. Use the TextRange property to return the full text of the story.

A story may also be placed within a table, in which case the story would be contained in one or more Cell objects rather than TextFrame objects. In such a case, use the Cell.TextRange property to access the text in a specific table cell. Use the HasTextFrame property to determine if a story is contained in one or more TextFrame objects.

The only objects to which you can link a text box are:

· An empty text box that is not already part of a chain of connected text boxes.

· A drawing shape that has a text frame. To determine if a shape has a text frame, use the Shape.HasTextFrame property.

Using the TextFrame Object to Manage Story Content

Each text box contains a TextFrame object, which contains the text in shape, as well as the properties that control the margins and orientation of the text frame. The TextFrame object includes several members that enable you to determine if a text box is part of a linked story, and to set or sever the connections between text boxes.

To determine if a text box is connected to a preceding or following text box, use the HasPreviousLink and HasNextLink properties, respectively. To access the text frames of those connected shapes, use the PreviousLinkedTextFrame and NextLinkedTextFrame properties. To connect one text box to the text box you want to follow it, use the NextLinkedTextFrame property as well.

To break the forward link for a specified text frame, use the BreakForwardLink method. Applying this method to a shape in the middle of a chain of shapes with linked text frames will break the chain, leaving two sets of linked shapes. All of the text, however, will remain in the first series of linked shapes.

The following example illustrates the relationship between a story and the connected text boxes that contain it. The example adds three text boxes to the active publication, and adds text to the first text box. At this point, with the text boxes not connected, three stories have been added to the publication's Stories collection. Next, the example links the three text boxes together by setting the NextLinkedTextFrame property of the first two text boxes. By doing this, two stories have been removed from the Stories collection. Note that the code calls the ValidLinkTarget method to determine if each text frame is a legitimate target to which to link.

Finally, the third text box is disconnected by calling the BreakForwardLink method for the second text box. The story text is now stored only in the first two text boxes, and the overflow buffer if necessary. In addition, text box three now represents a new, empty story.

Sub BreakTextLink()

  Dim tb1 As Shape

  Dim tb2 As Shape

  Dim tb3 As Shape

 

  Set tb1 = ActiveDocument.Pages(1).Shapes.AddTextbox _

      (Orientation:=msoTextOrientationHorizontal, _

  Left:=72, Top:=36, Width:=72, Height:=36)

  tb1.TextFrame.TextRange = "This is some text. " _

      & "This is some more text. This is even more text. " _

      & "And this is some more text and even more text."

 

  Set tb2 = ActiveDocument.Pages(1).Shapes.AddTextbox _

      (Orientation:=msoTextOrientationHorizontal, _

      Left:=72, Top:=108, Width:=72, Height:=36)

 

  Set tb3 = ActiveDocument.Pages(1).Shapes.AddTextbox _

      (Orientation:=msoTextOrientationHorizontal, _

      Left:=72, Top:=180, Width:=72, Height:=36)

 

  ShowStoryCount

   

  If tb1.TextFrame.ValidLinkTarget(tb2) Then

    tb1.TextFrame.NextLinkedTextFrame = tb2.TextFrame

  End If

 

  If tb2.TextFrame.ValidLinkTarget(tb3) Then

    tb2.TextFrame.NextLinkedTextFrame = tb3.TextFrame

  End If

 

  ShowStoryCount

     

  tb2.TextFrame.BreakForwardLink

 

  ShowStoryCount

End Sub

 

Function ShowStoryCount()

  MsgBox "There are currently " & _

    ActiveDocument.Stories.Count & _

    " stories in this publication.", , "Story Count"

End Function

There is no object in the Publisher object model that represents the overflow buffer for a specific story. Each TextFrame has an Overflowing property, however, that indicates whether text is overflowing from that text box into the overflow buffer. For linked text frames, only the final text frame can be overflowing.

For a given story, the text in the overflow buffer is the difference between the End property of the final linked TextFrame in the story, and the End property of the Story object itself.

For example, the following procedures retrieves the text in the overflow buffer. First, the code determines if the selected text box has overflow text. If it does, the procedure retrieves the text contained in the overflow buffer by using the Characters method. This method returns a TextRange object that contains the characters from the end of the text box TextRange object to the end of the Story object. The code then uses the Text property of the resulting TextRange object to return a string representing the overflow text, which it then displays in a message box.

Sub GetOverflowText()

  Dim ot As String

  With Selection.ShapeRange(1).TextFrame

    If .Overflowing Then

      With .TextRange

        ot = .Characters(.End, .Story.TextRange.End).Text

        MsgBox prompt:=ot, Title:="Overflow Text"

      End With

    End If

  End With

End Sub

Also, each TextFrame object also has an AutoFitText property, which lets you set how you want Publisher to deal with overflowing text:

· Allow the text to overflow

· Reduce the text size so that the it fits in the text frame

· Reduce or enlarge the text size so it fills the text frame

The following text boxes cannot be part of a chain of connected text boxes: headers or footers, navigation bars, inline objects, personal information text boxes, text boxes already containing text, or text boxes set to automatically reduce text size.

Finally, each TextFrame and TextRange object has a Story property, which enables you to access the Story object associated with a specific text frame or text range.

The Publisher Story Object Model

Figure 2 illustrates the structure of the Publisher object model surrounding the Story object. It also includes the TextFrame properties concerned with manipulating the text frames of a specific story.

 

Figure 2. The Story Object Model Structure