Hello Sergey,
i am still trying to find the best method for creating some complex documents that my app requires and i thought of using rtf.
The fact is that the document is actually a concatenation of many small paragraphs. Each paragraph has some XML tags that denote formatting (for example html tags, etc).
Since the methods so far (use the AddNL and ApplyStyleConversion, i described in previous post) seem complicated, i thought that i could use the LoadRTFFromStream.
This method seems to work also (and might be simpler for my case). What i want to ask is this: can i use an RTF fragment or should i use a complete RTF document?
For example:
it tried adding (with a Memory stream) the following: " This is a {\b bold } word". Unfortunately no bold appeared, although the plain text was there. The minimum code i needed to see the bold was this:
"{\rtf1{\fonttbl{\f0}}This is a \b bold \b0 word}".
Now, i suppose that if i need to also add colors and more fonts, i will need to create a colortbl and proper fonttbl.
My questions are these:
1. Why the first RTF fragment didn't work?
2. Do i need to always supply the rtf headers? For example, say that cf2 is blue. Do i need to have a colortbl for each fragment? Wouldn't be easier to say "{\cf2 This is blue}" and define once (for all fragments) that cf2 is for blue?. Is there a way to do this?
3. Since i suppose that adding always the RTF headers will have a deep impact on parsing time, is there a better solution than this?
4. Should i try to use RVF instead? Will it be faster? Is it easy to convert from RTF to RVF (string conversion)?
5. A last question: when using the LoadRTFFromStream method, a new paragraph is created for each fragment i insert: how can i avoid this? If not, how i could join the lines (even after formatting)?
Thanks for your help,
Costas
Appending RTF
-
- Site Admin
- Posts: 17569
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
Probably because some special actions are performed when the closing '}' of 'rtf1' block is processed.1. Why the first RTF fragment didn't work?
(I am not sure that I understand the question correctly)2. Do i need to always supply the rtf headers? For example, say that cf2 is blue. Do i need to have a colortbl for each fragment? Wouldn't be easier to say "{\cf2 This is blue}" and define once (for all fragments) that cf2 is for blue?. Is there a way to do this?
Each RTF loaded by LoadRTFFromStream must be self-containing, must have all necessary declarations.
If using RTF - no, I am afraid.3. Since i suppose that adding always the RTF headers will have a deep impact on parsing time, is there a better solution than this?
But it's not necessary for RTF headers to be large. Font and color (if you use colors) tables must present, but they may include only fonts and colors which are really used in that RTF.
See the help file, "RVF Specification" for details about RVF.4. Should i try to use RVF instead? Will it be faster? Is it easy to convert from RTF to RVF (string conversion)?
RVF is simple to generate without TRichView is it does not include declarations of text and paragraph styles (a predefined set of styles is used).
But probably the best solution is using RVXML ( http://www.trichview.com/resources/xml/ )
Not possible using documented methods.5. A last question: when using the LoadRTFFromStream method, a new paragraph is created for each fragment i insert: how can i avoid this? If not, how i could join the lines (even after formatting)?
But it's quite simple: set SameAsPrev property of the first paragraph item to True. Besides, all items in the same paragraph must have the same value of ParaNo property (index of paragraph style), so you must correct them when joining two paragraphs of different style (there is a NormalizeRichView procedure doing this work, but making it yourself is faster). Calling Format is required after modifying these properties.
Some examples: http://www.trichview.com/forums/viewtopic.php?t=66
As for joining paragraphs as an editing operation (undoable by user), you can move caret to the beginning of paragraph (SetSelectionBounds) and simulate backspace key (SendMessage WM_KEYDOWN)
The following code works:Not possible using documented methods.5. A last question: when using the LoadRTFFromStream method, a new paragraph is created for each fragment i insert: how can i avoid this? If not, how i could join the lines (even after formatting)?
But it's quite simple: set SameAsPrev property of the first paragraph item to True. Besides, all items in the same paragraph must have the same value of ParaNo property (index of paragraph style), so you must correct them when joining two paragraphs of different style (there is a NormalizeRichView procedure doing this work, but making it yourself is faster). Calling Format is required after modifying these properties.
Some examples: http://www.trichview.com/forums/viewtopic.php?t=66
As for joining paragraphs as an editing operation (undoable by user), you can move caret to the beginning of paragraph (SetSelectionBounds) and simulate backspace key (SendMessage WM_KEYDOWN)
ItemNo := rv.ItemCount-1;
rv.LoadRTFFromStream(ms);
rv.GetItem(ItemNo + 1).SameAsPrev := True;
Does setting the SameAsPrev correct the ParaNo for all items or i do need to do it manually after setting the SameAsPrev? (it is not clear from your previous comment)
Thanks for your answers and immediate support, they are so helpful.
Costas
-
- Site Admin
- Posts: 17569
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
Code: Select all
ItemNo := rv.ItemCount;
rv.LoadRTFFromStream(ms);
if ItemNo = rv.ItemCount then
exit;
rv.GetItem(ItemNo).SameAsPrev := True;
if ItemNo = 0 then
exit;
for i := ItemNo to rv.ItemCount-1 do
begin
if not rv.GetItem(i).SameAsPrev then
break;
rv.GetItem(i).ParaNo := rv.GetItem(ItemNo-1).ParaNo;
end;
Hi Sergey,
OK, i extended the code to try and handle tables:
Now, the above code has a single problem:
when i delete the paragraph i just copied (either with selecting and sending DEL key, or with DeleteParas or with DeleteItems) it seems that somehow an invalid document is produced because when i try to select the text in the cell, i get an AV.
If i select the content with the mouse and delete it, everything is OK. Can you spot the error?
Thanks,
Costas
OK, i extended the code to try and handle tables:
Code: Select all
function MemoryStreamFromString(const s: String): TMemoryStream;
begin
result := TMemoryStream.Create;
result.Write(s[1], Length(s));
result.Position := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
const
RTF_TBL_2COLS = '{\trowd\cellx\cellx{{\intbl %s\cell}{\intbl %s\cell}}\intbl\row}';
var
s, fonttbl: String;
ItemNo, ParaNo: Integer;
ms: TMemoryStream;
i, c, Offs: Integer;
trv: TCustomRVFormattedData;
table: TRVTableItemInfo;
begin
rv.Clear;
rv.AddNL('0. ', 2, 0);
rv.AddNL('1. ', 2, 0);
rv.AddNL('a word here ', 3, -1);
s := Format(RTF_TBL_2COLS, ['Column1', 'Column2']);
s := Format('{\rtf1\ansi\ansicpg1252\deff0\deflang1033%s%s\uc0\fs20 %s}',
[
'{\fonttbl{\f0\fcharset0 Arial;}}', //fonttable
'{\colortbl ;}', //colortable
s //actual content
]);
ms := MemoryStreamFromString(s); //this is obvious...
ItemNo := rv.ItemCount;
rv.LoadRTFFromStream(ms);
rv.Format;
//when loading RTF, a new paragraph is always added. Delete it...
if rv.GetItemStyle(ItemNo)=rvsTable then begin
//if this is a table, we will add all the previous paragraph to the beginning of this one
i := ItemNo - 1;
ParaNo := rv.getItemPara(i);
table := TRVTableItemInfo(rv.GetItem(ItemNo));
table.EditCell(0,0);
c := 0;
while (i>=0) do begin
with rv.TopLevelEditor do
SetSelectionBounds(0, GetOffsBeforeItem(0), 0, GetOffsBeforeItem(0));
rv.TopLevelEditor.CurTextStyleNo := rv.GetItemStyle(i);
rv.InsertText(rv.GetItemText(i));
Inc(c);
if rv.IsParaStart(i) then break;
Dec(i);
end;
Inc(i);
rv.SetSelectionBounds(ItemNo-c, rv.GetOffsBeforeItem(ItemNo-c), ItemNo-1,
rv.GetOffsAfterItem(ItemNo-1));
SendMessage(rv.Handle, WM_KEYDOWN, VK_DELETE,0);
SendMessage(rv.Handle, WM_KEYDOWN, VK_DELETE,0);
//rv.DeleteItems(ItemNo-c, c);
//rv.ClearUndo;
//rv.SetSelectionBounds(0,1,0,1);
//rv.DeleteParas(ItemNo-1, ItemNo-1+c-1);
end
else begin
if ItemNo < rv.ItemCount then rv.GetItem(ItemNo).SameAsPrev := True;
for i := ItemNo to rv.ItemCount-1 do begin
if not rv.GetItem(i).SameAsPrev then break;
rv.GetItem(i).ParaNo := rv.GetItem(ItemNo-1).ParaNo;
end;
end;
ms.Free;
rv.Format;
rv.SetFocus;
end;
when i delete the paragraph i just copied (either with selecting and sending DEL key, or with DeleteParas or with DeleteItems) it seems that somehow an invalid document is produced because when i try to select the text in the cell, i get an AV.
If i select the content with the mouse and delete it, everything is OK. Can you spot the error?
Thanks,
Costas
-
- Site Admin
- Posts: 17569
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
This code is overcomplicated.
The rules are simple: you cannot move to the previous paragraph:
- very first item,
- paragraph list marker,
- full line items (breaks, tables),
- item after full line item.
The rules are simple: you cannot move to the previous paragraph:
- very first item,
- paragraph list marker,
- full line items (breaks, tables),
- item after full line item.
Code: Select all
uses RVItem;
ItemNo := rv.ItemCount;
rv.LoadRTFFromStream(ms);
if ItemNo = rv.ItemCount then
exit;
if (ItemNo>0) and
(rv.GetItemStyle(ItemNo)<>rvsListMarker) and
not rv.GetItem(ItemNo).GetBoolValue(rvbpFullWidth) and
not rv.GetItem(ItemNo-1).GetBoolValue(rvbpFullWidth) then
begin
rv.GetItem(ItemNo).SameAsPrev := True;
if ItemNo = 0 then
exit;
for i := ItemNo to rv.ItemCount-1 do
begin
if not rv.GetItem(i).SameAsPrev then
break;
rv.GetItem(i).ParaNo := rv.GetItem(ItemNo-1).ParaNo;
end;
end;