Hello Sergey,
i give up. How can I transfer text to a TRichViewEdit with the following requirements:
In the target RV there is a certain formatting, e.g. Courier New 12.
In Word or somewhere else, I select text that is formatted in, say, USA light, and put it on the clipboard. This text has underlined and bold items, i.e. different TextStyles.
Now I want the text in my target RV to be formatted in Courier New as well, but the italic and bold items are taken over the same, but in Courier New, that the pasted text matches the existing one.
It is important that only the styles are taken from the other text and nothing else.
I have tried all sorts of things, even using a TRichEdit as a bridge, of course using another TRVEdit and transferring via stream or copy&paste, also paste directly, but nothing works cleanly.
Also here in the forum I have found nothing that has helped me.
Do you have any idea how I can solve this?
Regards
Martin
Paste text with target font but keep styles
-
- Site Admin
- Posts: 17565
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
Re: Paste text with target font but keep styles
1) The best solution is using StyleTemplates, they are analogs of styles in Microsoft Word.
If StyleTemplates are used, formatting of inserted RTF and DocX (and HTML) files are processed according to StyleTemplateInsertMode property. By default, formatting of inserted document is changed to formatting of the target document,
So, if font "USA Light" is defined in style of inserted document, and the target document has a style of the same name but formatted with "Courier New" font, "USA Light" will be changed to "Courier New" when inserting.
However, this solution works good only if formatting is made using styles. If formatting is made directly in document (inline formatting), this conversion will not be performed.
2) Another option is to assign rv.RTFReadProperties.ParaStyleMode = rv.RTFReadProperties.TextStyleMode = rvrsUseClosest.
In this case, when inserting (or loading) RTF or DocX, formatting of inserted/loaded document is mapped to the most similar existing formatting.
Please note that list markers are not loaded in this case.
3) If the solutions above are not ok for you, the only way is processing OnPaste event.
In this event, if the Clipboard contains RTF (CanPasteRTF method),
- paste this RTF in an invisible TRichViewEdit (let its name rveHidden)
- change properties rveHidden.Style.TextStyles[], replace FontName and maybe other properties
- save rveHiddent to TMemoryStream (rveHidden.SaveRVFToStream(Stream, False))
- insert this stream in the main editor (InsertRVFFromStreamEd)
- free Stream
- assign DoDefault = False (parameter of OnPaste)
If StyleTemplates are used, formatting of inserted RTF and DocX (and HTML) files are processed according to StyleTemplateInsertMode property. By default, formatting of inserted document is changed to formatting of the target document,
So, if font "USA Light" is defined in style of inserted document, and the target document has a style of the same name but formatted with "Courier New" font, "USA Light" will be changed to "Courier New" when inserting.
However, this solution works good only if formatting is made using styles. If formatting is made directly in document (inline formatting), this conversion will not be performed.
2) Another option is to assign rv.RTFReadProperties.ParaStyleMode = rv.RTFReadProperties.TextStyleMode = rvrsUseClosest.
In this case, when inserting (or loading) RTF or DocX, formatting of inserted/loaded document is mapped to the most similar existing formatting.
Please note that list markers are not loaded in this case.
3) If the solutions above are not ok for you, the only way is processing OnPaste event.
In this event, if the Clipboard contains RTF (CanPasteRTF method),
- paste this RTF in an invisible TRichViewEdit (let its name rveHidden)
- change properties rveHidden.Style.TextStyles[], replace FontName and maybe other properties
- save rveHiddent to TMemoryStream (rveHidden.SaveRVFToStream(Stream, False))
- insert this stream in the main editor (InsertRVFFromStreamEd)
- free Stream
- assign DoDefault = False (parameter of OnPaste)
Re: Paste text with target font but keep styles
Okay, I did find a working solution, but it's not straightforward. I'm posting it here in case someone also has this problem (CurrRV is the current TRichViewEdit). Maybe Sergey has a simpler solution?
1. Paste manually:
2. Paste routine:
3. Paragraph formatting
4. Text formatting
1. Paste manually:
Code: Select all
procedure TfMain.rvPaste(Sender: TCustomRichViewEdit; var DoDefault: Boolean);
begin
if Clipboard.HasFormat(CF_TEXT) then
begin
ConvertRichtext;
DoDefault := False;
end;
CurrRV.StartLiveSpelling;
end;
Code: Select all
procedure TfMain.ConvertRichtext;
var I: Integer;
OldCount, Diff: Integer;
ItS, OfS, ItE, OfE: Integer;
VS: Integer;
begin
FStylNo := CurrRV.GetItemStyle(CurrRV.CurItemNo);
FParaNo := CurrRV.GetItemPara(CurrRV.CurItemNo);
VS := rv.VScrollPos;
CurrRV.BeginUndoGroup(rvutInsert);
CurrRV.SetUndoGroupMode(True);
try
if Clipboard.HasFormat(CF_TEXT) then
begin
OldCount := CurrRV.ItemCount;
CurrRV.GetSelectionBounds(ItS, OfS, ItE, OfE, False);
CurrRV.PasteRTF;
Diff := CurrRV.ItemCount-OldCount;
// Paragraph formatting
CurrRV.SetSelectionBounds(ItS, CurrRV.GetOffsBeforeItem(ItS), ItS+Diff+1, CurrRV.GetOffsAfterItem(ItS+Diff+1));
CurrRV.ApplyParaStyleConversion(700);
// Text formatting
for I := ItS+1 to CurrRV.ItemCount - OldCount + 2 do
begin
FFoSt := CurrRV.Style.TextStyles[CurrRV.GetItemStyle(I)].Style;
CurrRV.SetSelectionBounds(I, CurrRV.GetOffsBeforeItem(I), I, CurrRV.GetOffsAfterItem(I));
CurrRV.ApplyStyleConversion(700);
end;
// Cleanup - Correct several thing as double spaces
CleanUp(CurrRV);
rv.VScrollPos := VS;
rv.SetSelectionBounds(ItS+Diff-1, CurrRV.GetOffsAfterItem(ItS+Diff-1), ItS+Diff-1, CurrRV.GetOffsAfterItem(ItS+Diff-1));
end;
finally
CurrRV.SetUndoGroupMode(False);
end;
end;
Code: Select all
procedure TfMain.rvParaStyleConversion(Sender: TCustomRichViewEdit; StyleNo, UserData: Integer; AppliedToText: Boolean; var NewStyleNo: Integer);
var PI: TParaInfo;
begin
PI := TParaInfo.Create(nil);
try
PI.Assign(rvs.ParaStyles[StyleNo]);
case UserData of
. . .
// Insert, keep paragraph style
700: begin
PI.Assign(Sender.Style.ParaStyles[FParaNo]);
end;
end;
NewStyleNo := rvs.FindParaStyle(PI);
finally
PI.Free;
end;
end;
Code: Select all
procedure TfMain.rvStyleConversion(Sender: TCustomRichViewEdit; StyleNo, UserData: Integer; AppliedToText: Boolean; var NewStyleNo: Integer);
var FI: TFontInfo;
begin
FI := TFontInfo.Create(nil);
try
FI.Assign(R.Style.TextStyles[StyleNo]);
case UserData of
. . .
// Paste: keep text change style
700: begin
FI.Assign(R.Style.TextStyles[FStylNo]);
FI.Style := FFoSt;
end;
end;
NewStyleNo := R.Style.FindTextStyle(FI);
finally
FI.Free;
end;
end;
end;
Last edited by Jim Knopf on Sat Oct 21, 2023 9:50 am, edited 2 times in total.
Re: Paste text with target font but keep styles
Oh - same moment posted
Thank you, I'll try out your tips.
Thank you, I'll try out your tips.
-
- Site Admin
- Posts: 17565
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
Re: Paste text with target font but keep styles
Thank you for the code.
Some notes:
1. You check for the presence of a plain text in the Clipboard, but you need to check for RTF.
The simplest way is rve.CanPasteRTF
2. The solution of pasting in the main editor and then applying conversion to the selection is not so simple. It's not necessary that all new content will be added as separate items. Inserted text items may be merged with existing text at the place of insertion. So, the solution with a hidden editor (see my previous reply) is more reliable. Additionally, it is simpler, because instead of ApplyStyleConversion you can simply modify properties of text and paragraph styles.
3. BeginUndoGroup and SetUndoGoupMode must always be called for TopLevelEditor: CurrRV.TopLevelEditor.BeginUndoGroup(rvutInsert) and so on.
Some notes:
1. You check for the presence of a plain text in the Clipboard, but you need to check for RTF.
The simplest way is rve.CanPasteRTF
2. The solution of pasting in the main editor and then applying conversion to the selection is not so simple. It's not necessary that all new content will be added as separate items. Inserted text items may be merged with existing text at the place of insertion. So, the solution with a hidden editor (see my previous reply) is more reliable. Additionally, it is simpler, because instead of ApplyStyleConversion you can simply modify properties of text and paragraph styles.
3. BeginUndoGroup and SetUndoGoupMode must always be called for TopLevelEditor: CurrRV.TopLevelEditor.BeginUndoGroup(rvutInsert) and so on.
Re: Paste text with target font but keep styles
Hello Sergey,
thanks for your additions, I will make it more stable this way.
Have a nice weekend
Martin
thanks for your additions, I will make it more stable this way.
Have a nice weekend
Martin
Re: Paste text with target font but keep styles
Hello Sergey,
thanks again for your tips!
Indeed, your solution with the invisible RVEdit works perfectly.
Here again the complete code, if someone else is also interested in this topic. The clean transfer of differently formatted text has always been an annoying problem in MS Word, too, which is why it is important to me to better support our users with this issue. Until now only plain text was allowed, but this is much more elegant.
1. OnPaste
2. Pasting and Converting
Regards
Martin
thanks again for your tips!
Indeed, your solution with the invisible RVEdit works perfectly.
Here again the complete code, if someone else is also interested in this topic. The clean transfer of differently formatted text has always been an annoying problem in MS Word, too, which is why it is important to me to better support our users with this issue. Until now only plain text was allowed, but this is much more elegant.
1. OnPaste
Code: Select all
procedure TfMain.rvPaste(Sender: TCustomRichViewEdit; var DoDefault: Boolean);
begin
ConvertRichtext;
DoDefault := False;
FPasted := True;
CurrRV.StartLiveSpelling;
end;
2. Pasting and Converting
Code: Select all
procedure TfMain.ConvertRichtext;
var I: Integer;
OldCount, Diff: Integer;
ItS, OfS, ItE, OfE: Integer;
VS: Integer;
StyleNo, ParaNo: Integer;
Stream: TMemoryStream;
begin
StyleNo := CurrRV.GetItemStyle(CurrRV.CurItemNo);
ParaNo := CurrRV.GetItemPara(CurrRV.CurItemNo);
VS := CurrRV.VScrollPos;
OldCount := CurrRV.ItemCount;
CurrRV.GetSelectionBounds(ItS, OfS, ItE, OfE, False);
CurrRV.TopLevelEditor.BeginUndoGroup(rvutInsert);
CurrRV.TopLevelEditor.SetUndoGroupMode(True);
try
if rvH.CanPasteRTF then
begin
Stream := TMemoryStream.Create;
try
// Invisible edit
rvH.PasteRTF;
for I := 0 to rvsH.TextStyles.Count-1 do
begin
rvsH.TextStyles[I].FontName := CurrRV.Style.TextStyles[StyleNo].FontName;
rvsH.TextStyles[I].Size := CurrRV.Style.TextStyles[StyleNo].Size;
end;
for I := 0 to rvsH.ParaStyles.Count-1 do
begin
rvsH.ParaStyles[I].LeftIndent := CurrRV.Style.ParaStyles[ParaNo].LeftIndent;
rvsH.ParaStyles[I].RightIndent := CurrRV.Style.ParaStyles[ParaNo].RightIndent;
rvsH.ParaStyles[I].SpaceBefore := CurrRV.Style.ParaStyles[ParaNo].SpaceBefore;
rvsH.ParaStyles[I].SpaceAfter := CurrRV.Style.ParaStyles[ParaNo].SpaceAfter;
rvsH.ParaStyles[I].LineSpacing := CurrRV.Style.ParaStyles[ParaNo].LineSpacing;
end;
// Copy to text
rvH.SaveRVFToStream(Stream, False);
Stream.Position := 0;
CurrRV.InsertRVFFromStreamEd(Stream);
Diff := CurrRV.ItemCount-OldCount;
// Cleanup (Change quotation marks, correct punctuation errors)
for I := 0 to CurrRV.ItemCount-1 do
PurgeText(CurrRV, I);
CleanUp(CurrRV);
CurrRV.Format;
CurrRV.VScrollPos := VS;
CurrRV.SetSelectionBounds(ItS+Diff-1, CurrRV.GetOffsAfterItem(ItS+Diff-1),
ItS+Diff-1, CurrRV.GetOffsAfterItem(ItS+Diff-1));
finally
Stream.Free;
end;
end;
finally
CurrRV.TopLevelEditor.SetUndoGroupMode(False);
end;
end;
Martin