I think I am doing something similar to what you are trying to do. When building a final report, I add chapter headings and topic headings followed by the actual rtf text. Chapter headings, Topic headings, and rtf body text are all pulled from the database separately and then assembled. I automatically insert code to make both the chapter and topic headings show up in the TOC, and will be hyperlinkable if then output to a PDF file using wPDF. Example code snippets that might help follow. I modified Julian's CreateTableOfContents code to fit my needs, including honoring the margins and indents of where the TOC tag was inserted in the master template and providing for indentation of Chapters vs. Topics and unique font, size and color for each.
The snippets might not be complete, but hopefully it will help in some way.
Adding a chapter heading bookmark for TOC creation:
with WPRichText1 do begin TextCursor.GetCharAttr; BookmarkInput( '_Toc_' + TNavFolder(node.Data).ID, true ); ActiveParagraph.ASet(WPAT_ParIsOutline,1); CurrAttr.Size := 1.0; // WPTools 5.20.6 bug "getaround" InputString(nodeDesc); end;
Adding a topic heading bookmark for TOC creation:
with WPRichText1 do begin TextCursor.GetCharAttr; BookmarkInput( '_Toc_' + TNavDocument(node.Data).ID, true ); ActiveParagraph.ASet(WPAT_ParIsOutline,2); CurrAttr.Size := 1.0; // WPTools 5.20.6 bug "getaround" InputString(nodeDesc); end;
Then I call the TOC creation once the report has been assemled:
if WPRichText_Rpt.FieldExists(WPTOC_FIELDNAME) then begin preTOCPageCnt := WPRichText_Rpt.PageCount; MyCreateTableOfContents( '', '_Toc_', [ wptocUseParIsOutline, wptocCreateHyperlinks ], nil, '', nil);
And then of course the long table of contents creation code:
// Based on CreateTableOfContents from WPCTRMemo.pas of WPTools 5.20.5
function TFPrintReport_Main.MyCreateTableOfContents(
const AName: string = '';
const BookMarkId: string = '_Toc';
Mode: TWPCreateTableOfContentsMode = [];
StopPar: PTParagraph = nil;
const TitleText: string = '';
myFirstPar: PTParagraph = nil): Integer; // wptocRecreateAllBookmarks
var ResultPar: TParagraph;
ResultPosInPar: Integer;
bobj, bobje: TWPTextObj;
var s: WideString;
outl, i, iCurrCharAttr: Integer;
tocobje, tocobj, hyperobj, beobj: TWPTextObj;
FNeedCr: Boolean;
sty: TWPTextStyle;
uniqueid: Cardinal;
var
curFontName: TFontName;
curCharSet: Integer;
curFontSize: Single;
curFontStyle: WrtStyle;
curColor, curBkColor: Integer;
curAlignment: TParAlign;
curLeftIndent, curRightIndent: Integer;
procedure Process(par: TParagraph);
procedure AppendParText;
var i: Integer;
c: WideChar;
begin
with WPRichText_Rpt do
begin
for i := 0 to par.CharCount-1 do
begin
c := par.CharItem[i];
if (Integer(c) > 9) and
((Integer(c) > 32) or (ActiveParagraph.CharCount > 0))
then
ActiveParagraph.AppendChar(c, 0);
end;
end;
end;
begin
with WPRichText_Rpt do
begin
if (wptocUseParIsOutline in Mode) and
((par.AGetInherited(WPAT_ParIsOutline, outl)) and (outl > 0)) then
begin
// If the paragraph uses a style we can use the number level
// WPAT_ParIsOutline can come from style OR paragraph!
sty := par.ABaseStyle;
if (sty <nil> 0) then
outl := i;
bobj := BookmarkFirstObjInPar(par);
if (wptocCreateOutlineBookmarks in Mode) and (bobj = nil) then
begin
bobj := par.InsertNewObject(0, wpobjBookmark, true, false, iCurrCharAttr);
bobj.Name := BookMarkId + IntToStr(uniqueid);
inc(uniqueid);
bobje := par.InsertNewObject(par.CharCount, wpobjBookmark, true, true, iCurrCharAttr);
bobje.SetTag(bobj.NewTag);
end;
end else if not (wptocUseParIsOutline in Mode) then
bobj := BookmarkFirstObjInPar(par)
else
bobj := nil;
if (bobj <nil> 0)) then
begin
if FNeedCr then InputString(#13);
// Create Hyperklink START
if wptocCreateHyperlinks in Mode then
begin
hyperobj := InputNewObject(wpobjHyperlink, false, false, iCurrCharAttr);
hyperobj.Source := bobj.Name;
hyperobj.Mode := [wpobjUsedPairwise, wpobjIsOpening];
end else
hyperobj := nil;
if (outl = 1) or (tocIndent = 0) then
ActiveParagraph.ASet( WPAT_IndentLeft, curLeftIndent )
else
ActiveParagraph.ASet( WPAT_IndentLeft, curLeftIndent + ((tocIndentFactor + 1) * 360) );
ActiveParagraph.ADel(WPAT_IndentFirst);
if wptocLocateTextAfterBookmark in Mode then
begin
par := par.NextPar;
AppendParText;
end else
begin
// This is the usual code which uses the embedded text
s := bobj.EmbeddedText;
if outl = 1 then
begin
CurrAttr.Size := tocChapterFontSize;
CurrAttr.SetFontCharset( tocChapterFont, DEFAULT_CHARSET );
CurrAttr.Style := tocChapterFontStyle;
CurrAttr.Color := CurrAttr.ColorToNr( tocChapterFontColor );
CurrAttr.BKColor := CurrAttr.ColorToNr( tocChapterBkColor );
end else
begin
CurrAttr.Size := tocTopicFontSize;
CurrAttr.SetFontCharset( tocTopicFont, DEFAULT_CHARSET );
CurrAttr.Style := tocTopicFontStyle;
CurrAttr.Color := CurrAttr.ColorToNr( tocTopicFontColor );
CurrAttr.BKColor := CurrAttr.ColorToNr( tocTopicBkColor );
end;
InputString(s);
CurrAttr.Size := curFontSize;
CurrAttr.SetFontCharset( curFontName, curCharSet );
CurrAttr.Style := curFontStyle;
CurrAttr.Color := curColor;
CurrAttr.BKColor := curBkColor;
beobj := bobj.EndTag;
if beobj <> nil then
par := beobj.ParentPar;
end;
inc(Result);
// Create hyperlink closing
if hyperobj <> nil then
begin
with InputNewObject(wpobjHyperlink, false, false, iCurrCharAttr) do
begin
hyperobj.CParam := WPHYPER_BOOKMARKLINK;
SetTag(hyperobj.NewTag); // Link together
Mode := [wpobjUsedPairwise, wpobjIsClosing];
end;
hyperobj := nil;
end;
InputString(#9);
// Create Hyperklink START
if (hyperobj = nil) and (wptocCreateHyperlinkNumbers in Mode) then
begin
hyperobj := InputNewObject(wpobjHyperlink, false, false, iCurrCharAttr);
hyperobj.Source := bobj.Name;
hyperobj.Mode := [wpobjUsedPairwise, wpobjIsOpening];
end;
ActiveParagraph.TabstopAdd( Header.PageWidth -
ActivePar.CurrentSection.LeftMargin - ActivePar.CurrentSection.RightMargin -
curRightIndent,// - 360,
tkRight, tkDots );
InputNewObject(wpobjReference, false, false, iCurrCharAttr).Name_Spaces := bobj.Name;
// Create hyperlink closing
if hyperobj <> nil then
begin
with InputNewObject(wpobjHyperlink, false, false, iCurrCharAttr) do
begin
hyperobj.CParam := WPHYPER_BOOKMARKLINK;
SetTag(hyperobj.NewTag); // Link together
Mode := [wpobjUsedPairwise, wpobjIsClosing];
end;
end;
FNeedCr := TRUE;
end;
end;
end;
var
par, rpar, cpar: TParagraph;
begin
with WPRichText_Rpt do
begin
Result := 0;
uniqueid := GetTickCount;
{$IFDEF ALLOWUNDO}
if wpActivateUndo in EditOptions then
begin
Memo.RTFData.UndoStreamStart(wputInsertObject);
Memo.RTFData.UndoDisable;
end;
{$ENDIF}
try
if CodeLocate(ResultPar, ResultPosInPar, WPTOC_FIELDNAME, wpobjMergefield,
[wpobjIsOpening],
[wpCompareName{, wpCompareSource}], AName) then
begin
ActiveParagraph := ResultPar;
ActivePosInPar := ResultPosInPar;
TextCursor.GetCharAttr;
iCurrCharAttr := TextCursor.WritingTextAttr.CharAttr;
tocobj := nil;
end else
begin
HideSelection;
iCurrCharAttr := TextCursor.WritingTextAttr.CharAttr;
tocobj := InputSingleCode(wpobjMergeField, WPTOC_FIELDNAME, AName);
tocobj.Mode := [wpobjUsedPairwise, wpobjIsOpening];
ResultPar := ActiveParagraph;
ResultPosInPar := ActivePosInPar;
end;
tocStartPage := CPPage;
// -------------------------
if (tocobj <> nil) or SelectFieldAtCP(false, true) then
begin
ClearSelection(true);
ResultPar := ActiveParagraph;
ResultPar.TabstopClear;
InputString(TitleText);
FNeedCr := TitleText <> '';
if myFirstPar = nil then
par := FirstPar
else
par := myFirstPar;
curFontName := CurrAttr.FontName;
curCharSet := CurrAttr.CharSet;
curFontSize := CurrAttr.Size;
curFontStyle := CurrAttr.Style;
curColor := CurrAttr.Color;
curBkColor := CurrAttr.BKColor;
curAlignment := CurrAttr.Alignment;
CurrAttr.Alignment := parAlLeft;
curLeftIndent := ActiveParagraph.indentleft;
curRightIndent := ActiveParagraph.indentright;
try
while par <> nil do
begin
if (wptocAlsoProcessTables in Mode) and (par.ParagraphType = wpIsTable) then
begin
rpar := par.ChildPar;
while rpar <> nil do
begin
cpar := rpar.ChildPar;
while cpar <> nil do
begin
Process(cpar);
cpar := cpar.NextPar;
end;
rpar := rpar.NextPar;
end;
end else
Process(par);
if par = StopPar then
break;
par := par.NextPar;
end;
if FNeedCr then
InputString(#13);
if tocobj <> nil then
begin
tocobje := InputSingleCode(wpobjMergeField, WPTOC_FIELDNAME, AName);
tocobje.Mode := [wpobjUsedPairwise, wpobjIsClosing];
tocobje.SetTag(tocobj.NewTag);
end;
finally
CurrAttr.Alignment := curAlignment;
end;
ReformatAll(true);
tocEndPage := CPPage;
end;
finally
{$IFDEF ALLOWUNDO}
if wpActivateUndo in EditOptions then
begin
Memo.RTFData.UndoStreamEnd;
Memo.RTFData.UndoEnable;
end;
{$ENDIF}
end;
end;
end;