How to programmatically add and get rich text using a TdxRichEditControl? - delphi

DevExpress doesn't provide docs about "How to programmatically add and get rich text using a TdxRichEditControl".
Or Any suggestion to Embedded Full text Editor in my program? I am on Delphi Seattle.
My Situation:
I am creating a program on which the user need rich text editing (like Bold, Italic, Underlining, Font Size, Font family, Paragraph Aligning). So I've putted a DBRichEdit that load a model of text on which the user will make changes.
So I need to take this model text to the TdxRichEditControl to apply the changes and back it again to the DBRichEdit to save it in the database.

If you want to just insert portions of text into the control, this is some code I used to use for copying from one DevExpress RichEdit to another.. You might be able to adapt it to copy from another source (warning - it's a few years old):
procedure AppendToRichEdit(const Source, Dest: TcxRichEdit) ;
var
RTFStream: TEditStream;
Stream : TMemoryStream;
function EditStreamReader(dwCookie: DWORD; pBuff: Pointer; cb: LongInt; pcb: PLongInt): DWORD; stdcall;
begin
Result := $0000;
try
pcb^ := TStream(dwCookie).Read(pBuff^, cb);
except
Result := $FFFF;
end;
end;
begin
Stream := TMemoryStream.Create;
try
Source.Lines.SaveToStream(Stream) ;
Stream.Position := 0;
RTFStream.dwCookie := DWORD(Stream) ;
RTFStream.dwError := $0000;
RTFStream.pfnCallback := #EditStreamReader;
Dest.InnerControl.Perform(EM_STREAMIN, SFF_SELECTION or SF_RTF or SFF_PLAINRTF, LPARAM(#RTFStream)) ;
if RTFStream.dwError <> $0000 then
raise Exception.Create('Error appending RTF data.') ;
finally
Stream.Free;
end;
end;

Related

TPDFImage.LoadFromFile makes printer screen show up and cuts off the rest of the function's execution

I am aiming to convert a PDF to JPEG, so I can show it in an image viewer that is embedded in my Delphi program. Here is the code for my function:
function TAnexo.loadFilePdf(AFile:String): Boolean;
Var FPicture:TPDFImage;
FStream: TMemoryStream;
PdfExist: Boolean;
jpeg: TJPEGImage;
begin
result := false;
PdfExist := fileExists(AFile);
if PdfExist then
begin
fResultFileNameBmp := AFile + '.jpg';
FPicture:=TPDFImage.Create;
Try
//set resolution and page before load so the first requested page is loaded
FPicture.Resolution:=300;
FPicture.PixelFormat := pf24bit;
FPicture.CurrentPage:=1;
FPicture.LoadFromFile(AFile);
jpeg := TJPEGImage.Create;
try
jpeg.CompressionQuality := 100;
jpeg.PixelFormat := jf24Bit;
jpeg.Scale := jsFullSize;
jpeg.Performance := jpBestQuality;
jpeg.GrayScale := False; {Apenas tons de cinza}
jpeg.ProgressiveDisplay := False; {Display Progressivo}
jpeg.Assign(FPicture);
jpeg.SaveToFile(fResultFileNameBmp);
finally
jpeg.Free;
end;
self.loadFileToBinary(fResultFileNameBmp);
finally
FPicture.Free;
DeleteFile(fResultFileNameBmp);
DeleteFile(AFile);
end;
end;
end;
But when the function reaches the line "FPicture.LoadFromFile(AFile);", it shows a screen requesting that I choose between the available printers to print the PDF and, no matter what I choose, the rest of the function will not execute.
printer screen
What could be the solution? My intention is not to print the PDF, but only to make de PDF-JPEG conversion behind the scenes so I can visualize it in the image viewer whenever I would like.
Edit: Aparently there's another line responsible for making the printer dialog show up in my program. I have an instance of TChromium calling the PrintToPdf() function before my custom function executes, but I still don't know how to make my instance of TChromium save the PDF without bringing up the printer dialog. Here's the code where it executes:
procedure TfrmDarfOnline.actConfirmarExecute(Sender: TObject);
begin
Self.fDarfAvulso.FilePdf := ArquivoPDF{my PDF file's path};
Self.fDarfAvulso.Chrome.PrintToPDF(Self.fDarfAvulso.FilePdf,'DARF',Self.Chrome.Browser.MainFrame.GetURL);
sleep(2000);
Chrome.ClearCache;
Chrome.DeleteCookies('http://www31.receita.fazenda.gov.br');
inherited;
ModalResult := mrOk;
end;

Reading HTML content from Clipboard in Delphi

I have a webpage which has various tables on it. These tables are Javascript components, not just pure HTML tables. I need to process the text of this webpage (somewhat similar to screen scraping) with a Delphi program (Delphi 10.3).
I do a Ctrl-A/Ctrl-C to select all the webpage and copy everything to the clipboard. If I paste this into a TMemo component in my program, I am only getting text outside the table. If I paste into MS Word, I can see all the content, including the text inside the table.
I can paste this properly into TAdvRichEditor (3rd party), but it takes forever, and I often run out of memory. This leads me to believe that I need to directly read the clipboard with an HTML clipboard format.
I set up a clipboard HTML format. When I inspect the clipboard contents, I get what looks like all Kanji characters.
How do I read the contents of the clipboard when the contents are HTML?
In a perfect world, I would like ONLY the text, not the HTML itself, but I can strip that out later. Here is what I am doing now...
On initialization.. (where CF_HTML is a global variable)
CF_HTML := RegisterClipboardFormat('HTML Format');
then my routine is...
function TMain.ClipboardAsHTML: String;
var
Data: THandle;
Ptr: PChar;
begin
Result := '';
with Clipboard do
begin
Open;
try
Data := GetAsHandle(CF_HTML);
if Data <> 0 then
begin
Ptr := PChar(GlobalLock(Data));
if Ptr <> nil then
try
Result := Ptr;
finally
GlobalUnlock(Data);
end;
end;
finally
Close;
end;
end;
end;
** ADDITIONAL INFO - When I copy from the webpage... I can then inspect the contents of the Clipboard buffer using a free tool called InsideClipBoard. It shows that the clipboard contains 1 entry, with 5 formats: CT_TEXT, CF_OEMTEXT, CF_UNICODETEXT, CF_LOCALE, and 'HTML Format' (with Format ID of 49409). Only 'HTML Format' contains what I am looking for.... and that is what I am trying to access with the code that I have shown.
The HTML format is documented here. It is placed on the clipboard as UTF-8 encoded text, and you can extract it like this.
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Winapi.Windows,
Vcl.Clipbrd;
procedure Main;
var
CF_HTML: Word;
Data: THandle;
Ptr: Pointer;
Error: DWORD;
Size: NativeUInt;
utf8: UTF8String;
Html: string;
begin
CF_HTML := RegisterClipboardFormat('HTML Format');
Clipboard.Open;
try
Data := Clipboard.GetAsHandle(CF_HTML);
if Data=0 then begin
Writeln('HTML data not found on clipboard');
Exit;
end;
Ptr := GlobalLock(Data);
if not Assigned(Ptr) then begin
Error := GetLastError;
Writeln('GlobalLock failed: ' + SysErrorMessage(Error));
Exit;
end;
try
Size := GlobalSize(Data);
if Size=0 then begin
Error := GetLastError;
Writeln('GlobalSize failed: ' + SysErrorMessage(Error));
Exit;
end;
SetString(utf8, PAnsiChar(Ptr), Size - 1);
Html := string(utf8);
Writeln(Html);
finally
GlobalUnlock(Data);
end;
finally
Clipboard.Close;
end;
end;
begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.

In Delphi, use SaveToStream to save ClientDataSets plus other material to a file?

I would like to use SaveToStream to save a ClientDataSet ALONG WITH OTHER MATERIAL. Here is a short sample:
filename := ChangeFileExt(Application.ExeName, '.dat');
FS := TFileStream.Create(filename, fmCreate);
CDS.SaveToStream(FS);
ShowMessage('After save, position is ' + IntToStr(FS.Position));
{now write a longint}
L := 1234;
siz := SizeOf(L);
Write(L, siz);
FS.Free;
But when I try to load this back in using LoadFromStream, and I again display the position after the ClientDataSet has been loaded, I see that the position is now 4 bytes AFTER the clientdataset was originally saved. It seems that CDS.LoadFromStream just plows ahead and consumes whatever follows it. As a result, when I then try to read the longint, I get an end of file error.
It is not sufficient to just use the CDS.SaveToStream at the end of creating a file, because what I'd really like to do is to save TWO clientdatasets to the file, one after the other, plus other material.
Ideas? Thanks.
[NB, this solution is essentially doubling up the work that (TLama's suggestion) "ReadDataPacket/WriteDataPacket" already does internally. I would use TLama's approach i.e. sub-class TClientDataSet to expose the above protected methods, and use the WriteSize parameter.]
Save the datasets to a temporary stream and then copy that to your destination stream with size information:
procedure InternalSaveToStream(AStream: TStream);
var
ATempStream: TMemoryStream;
ASize: Int64;
begin
ATempStream := TMemoryStream.Create;
// Save first dataset:
DataSet1.SaveToStream(ATempStream, dfBinary);
ASize := ATempStream.Size;
AStream.WriteData(ASize);
ATempStream.Position := 0;
AStream.CopyFrom(ATempStream, ALength);
ATempStream.Clear;
// Save second dataset:
DataSet2.SaveToStream(ATempStream, dfBinary);
ASize := ATempStream.Size;
AStream.WriteData(ASize);
ATempStream.Position := 0;
AStream.CopyFrom(ATempStream, ALength);
ATempStream.Clear;
FreeAndNil(ATempStream);
end;
To read back, first read the size and then copy that section of your source to a temporary stream again and load your dataset from that:
procedure InternalLoadFromStream(AStream: TStream);
var
ATempStream: TMemoryStream;
ASize: Int64;
begin
ATempStream := TMemoryStream.Create;
// Load first datset:
AStream.Read(ASize,SizeOf(ASize));
ASize := ATempStream.Size;
ATempStream.CopyFrom(AStream,ASize);
ATempStream.Position := 0;
DataSet1.LoadFromStream(ATempStream);
//...etc.
end;

How to open .pas file from another app in already open Delphi IDE and position to line#

Assuming I have the Delphi IDE open, how can I open a .pas file selected in another app and open it in the Delphi IDE, as well as positioning it to a specific line number?
I've seen some editing tools do this.
I'm not sure if it's just an option to a normal file open (eg., using default file association), or a command-line option, or you need DDE or COM or something entirely different.
Note that I don't want to close the project and reopen a new or fake project.
Also, I don't want the file added to the project. I just want to open it.
For example, When you <ctrl>-click on a varible or type, the IDE will open the file containing that symbol and go to the line where that symbol is declared. That's all I want to do -- but from an external app. (I'm not looking for a symbol, just a line.)
I'm using Delphi XE5 at the moment, so I'm interested in newer Delphi versions, not pre-XE2 or so.
(Part of the question is, how do I ensure that if the IDE is already open, the the file is opened in anew tab inside of the current IDE rather than in another instance of the IDE?)
The code below (for D7) shows how this can be done by way of an IDE add-in .Dpk compiled
into a Bpl. It started as just a "proof of concept", but it does actually work.
It comprises a "sender" application which uses WM_COPYDATA to send the FileName, LineNo & Column to a receiver hosted in the .Bpl file.
The sender sends the receiver a string like
Filename=d:\aaad7\ota\dskfilesu.pas
Line=8
Col=12
Comment=(* some comment or other*)
The Comment line is optional.
In the .Bpl, the receiver uses OTA services to open the requested file and positions the editor caret, then inserts the comment, if any.
The trickiest thing was to find out how to handle one particular complication, the case where the named file to be opened is one with an associated form. If so, in D7 (and, I assume, other IDE versions with the floating designer option enabled) when the IDE
opens the .Pas file, it also opens the .Dfm, and left to its own devices, that would leave the form editor in front of the code editor. Calling the IOTASourceEditor.Show for the .Pas file at least puts the IDE code editor in front of the .Dfm form, but that didn't satisfy me, because by now my curiosity was piqued - how do you get a form the IDE is displaying off the screen?
I spent a lot of time exploring various blind alleys, because the OTA + NTA services don't seem to provide any way to explicitly close an IOTAEditor or any of its descendants. In the end it turned out that the thing to do is simply get a reference to the form and just send it a WM_CLOSE(!) - see comments in the code.
Fwiw, being a novice at OTA, at first (before I found out how IOTAModules work) I found that far and away the most difficult part of this was discovering how to get hold of the IEditView interface needed to set the editor caret position, but as usual with these interfacey things, once you get the "magic spell" exactly right, it all works.
Good luck! And thanks for the fascinating challenge!
unit Receiveru;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ToolsAPI;
type
TOTAEditPosnForm = class(TForm)
Memo1: TMemo;
private
FEdLine: Integer;
FEdCol: Integer;
FEditorFileName: String;
FEditorInsert: String;
procedure WMCopyData(var Msg : TWMCopyData); message WM_COPYDATA;
procedure HandleCopyDataString(CopyDataStruct : PCopyDataStruct);
procedure OpenInIDEEditor;
property EditorFileName : String read FEditorFileName write FEditorFileName;
property EdLine : Integer read FEdLine write FEdLine;
property EdCol : Integer read FEdCol write FEdCol;
property EditorInsert : String read FEditorInsert write FEditorInsert;
end;
var
OTAEditPosnForm: TOTAEditPosnForm;
procedure Register;
implementation
{$R *.dfm}
procedure MonitorFiles;
begin
OTAEditPosnForm := TOTAEditPosnForm.Create(Nil);
OTAEditPosnForm.Show;
end;
procedure Register;
begin
MonitorFiles;
end;
procedure TOTAEditPosnForm.OpenInIDEEditor;
var
IServices : IOTAServices;
IActionServices : IOTAActionServices;
IModuleServices : IOTAModuleServices;
IEditorServices : IOTAEditorServices60;
IModule : IOTAModule;
i : Integer;
IEditor : IOTAEditor;
ISourceEditor : IOTASourceEditor;
IFormEditor : IOTAFormEditor;
IComponent : IOTAComponent;
INTAComp : INTAComponent;
AForm : TForm;
IEditView : IOTAEditView;
CursorPos : TOTAEditPos;
IEditWriter : IOTAEditWriter;
CharPos : TOTACharPos;
InsertPos : Longint;
FileName : String;
begin
IServices := BorlandIDEServices as IOTAServices;
Assert(Assigned(IServices), 'IOTAServices not available');
IServices.QueryInterface(IOTAACtionServices, IActionServices);
if IActionServices <> Nil then begin
IServices.QueryInterface(IOTAModuleServices, IModuleServices);
Assert(IModuleServices <> Nil);
// Close all files open in the IDE
IModuleServices.CloseAll;
if IActionServices.OpenFile(EditorFileName) then begin
// At this point, if the named file has an associated .DFM and
// we stopped here, the form designer would be in front of the
// code editor.
IModule := IModuleServices.Modules[0];
// IModule is the one holding our .Pas file and its .Dfm, if any
// So, iterate the IModule's editors until we find the one
// for the .Pas file and then call .Show on it. This will
// bring the code editor in front of the form editor.
ISourceEditor := Nil;
for i := 0 to IModule.ModuleFileCount - 1 do begin
IEditor := IModule.ModuleFileEditors[i];
FileName := IEditor.FileName;
Memo1.Lines.Add(Format('%d %s', [i, FileName]));
if CompareText(ExtractFileExt(IEditor.FileName), '.Pas') = 0 then begin
if ISourceEditor = Nil then begin
IEditor.QueryInterface(IOTASourceEditor, ISourceEditor);
IEditor.Show;
end
end
else begin
// Maybe the editor is a Form Editor. If it is
// close the form (the counterpart to the .Pas, that is}
IEditor.QueryInterface(IOTAFormEditor, IFormEditor);
if IFormEditor <> Nil then begin
IComponent := IFormEditor.GetRootComponent;
IComponent.QueryInterface(INTAComponent, INTAComp);
AForm := TForm(INTAComp.GetComponent);
//AForm.Close; < this does NOT close the on-screen form
// IActionServices.CloseFile(IEditor.FileName); <- neither does this
SendMessage(AForm.Handle, WM_Close, 0, 0); // But this does !
end;
end;
end;
// Next, place the editor caret where we want it ...
IServices.QueryInterface(IOTAEditorServices, IEditorServices);
Assert(IEditorServices <> Nil);
IEditView := IEditorServices.TopView;
Assert(IEditView <> Nil);
CursorPos.Line := edLine;
CursorPos.Col := edCol;
IEditView.SetCursorPos(CursorPos);
// and scroll the IEditView to the caret
IEditView.MoveViewToCursor;
// Finally, insert the comment, if any
if EditorInsert <> '' then begin
Assert(ISourceEditor <> Nil);
IEditView.ConvertPos(True, CursorPos, CharPos);
InsertPos := IEditView.CharPosToPos(CharPos);
IEditWriter := ISourceEditor.CreateUndoableWriter;
Assert(IEditWriter <> Nil, 'IEditWriter');
IEditWriter.CopyTo(InsertPos);
IEditWriter.Insert(PChar(EditorInsert));
IEditWriter := Nil;
end;
end;
end;
end;
procedure TOTAEditPosnForm.HandleCopyDataString(
CopyDataStruct: PCopyDataStruct);
begin
Memo1.Lines.Text := PChar(CopyDataStruct.lpData);
EditorFileName := Memo1.Lines.Values['FileName'];
edLine := StrToInt(Memo1.Lines.Values['Line']);
edCol := StrToInt(Memo1.Lines.Values['Col']);
EditorInsert := Trim(Memo1.Lines.Values['Comment']);
if EditorFileName <> '' then
OpenInIDEEditor;
end;
procedure TOTAEditPosnForm.WMCopyData(var Msg: TWMCopyData);
begin
HandleCopyDataString(Msg.CopyDataStruct);
msg.Result := Length(Memo1.Lines.Text);
end;
initialization
finalization
if Assigned(OTAEditPosnForm) then begin
OTAEditPosnForm.Close;
FreeAndNil(OTAEditPosnForm);
end;
end.
Code for sender:
procedure TSenderMainForm.btnSendClick(Sender: TObject);
begin
SendMemo;
end;
procedure TSenderMainForm.SendData(
CopyDataStruct: TCopyDataStruct);
var
HReceiver : THandle;
Res : integer;
begin
HReceiver := FindWindow(PChar('TOTAEditPosnForm'),PChar('OTAEditPosnForm'));
if HReceiver = 0 then begin
Caption := 'CopyData Receiver NOT found!';
end
else begin
Res := SendMessage(HReceiver, WM_COPYDATA, Integer(Handle), Integer(#CopyDataStruct));
if Res > 0 then
Caption := Format('Received %d characters', [Res]);
end;
end;
procedure TSenderMainForm.SendMemo;
var
MS : TMemoryStream;
CopyDataStruct : TCopyDataStruct;
S : String;
begin
MS := TMemoryStream.Create;
try
S := Memo1.Lines.Text + #0;
MS.Write(S[1], Length(S));
CopyDataStruct.dwData := 1;
CopyDataStruct.cbData := MS.Size;
CopyDataStruct.lpData := MS.Memory;
SendData(CopyDataStruct);
finally
MS.Free;
end;
end;

Get richtext from a richedit in Delphi

Is there a way to get the RTF data from a richedit without using savetostream as in
strStream := TStringStream.Create('') ;
try
RichEdit.Lines.SaveToStream(strStream);
Text := strStream.DataString;
strStream.CleanupInstance;
finally
strStream.Free
Tim the only way to get the RTF data from an RichEdit control is using a Stream because the windows message (EM_STREAMOUT) wich retrieve the RTF Data require a EditStreamCallback structure, this is the way used by windows to transfer rtf data into or out of a richedit control.
So you can use your own sample code, or implement the call to the windows message EM_STREAMOUT.
function RichTextToStr(red : TRichEdit) : string;
var ss : TStringStream;
begin
ss := TStringStream.Create('');
try
red.Lines.SaveToStream(ss);
Result := ss.DataString;
finally
ss.Free;
end;
end;
procedure CopyRTF(redFrom,redTo : TRichEdit);
var s : TMemoryStream;
begin
s := TMemoryStream.Create;
try
redFrom.Lines.SaveToStream(s);
s.Position := 0;
redTo.Lines.LoadFromStream(s);
finally
s.Free;
end;
end;
I can attest deviation from the pattern results in frustration....

Resources