How to reliably detect RICHTEXT format on clipboard? - delphi

Embarcadero RAD Studio VCL has the TClipboard.HasFormat Method, with a usage e.g. Clipboard.HasFormat(CF_TEXT) or Clipboard.HasFormat(CF_BITMAP) etc..
But I did not find any supported CF_RTF or CF_RICHTEXT format-descriptor which indicates a rich-text format in the clipboard.
So I created some formatted text in Microsoft WordPad and copied it to the clipboard. Then I used a clipboard-spy program to inspect the formats on the clipboard:
This lists 3 RichText formats with the format-descriptors C078, C16B and C1A5.
Are these format-descriptors universal or dependent from the individual system or from the current situation? I.e., can I generally use Clipboard.HasFormat($C078) to detect any RichText format on the clipboard? Or is there another method?

Can I generally use Clipboard.HasFormat($C078) to detect any
RichText format on the clipboard?
No, You need to register the RTF clipboard format via RegisterClipboardFormat function. The returned value is generated by the system and may vary.
Registers a new clipboard format. This format can then be used as a
valid clipboard format.
If a registered format with the specified name already exists, a new
format is not registered and the return value identifies the existing
format. This enables more than one application to copy and paste data
using the same registered clipboard format.
var
CF_RTF: UINT;
...
initialization
CF_RTF := RegisterClipboardFormat('Rich Text Format');
Then check for:
if Clipboard.HasFormat(CF_RTF) then ...
{ or // if Windows.IsClipboardFormatAvailable(CF_RTF) then ... }
Edit: After reading the documentation: How to Use Rich Edit Clipboard Operations
The constant CF_RTF is already declared in RichEdit unit as:
CF_RTF = 'Rich Text Format';
CF_RTFNOOBJS = 'Rich Text Format Without Objects';
CF_RETEXTOBJ = 'RichEdit Text and Objects';
So it might be a better idea to use other naming for the returned value of RegisterClipboardFormat. e.g.
uses RichEdit;
...
var
CF_RICHTEXT: UINT;
...
initialization
CF_RICHTEXT := RegisterClipboardFormat(RichEdit.CF_RTF);
And:
if Clipboard.HasFormat(CF_RICHTEXT) then ...
Note: There are already a few reserved system clipboard formats such as CF_TEXT (=1), CF_BITMAP(=2) etc ... but the "CF_RTF" or "CF_RICHTEXT" is not one of them. it is a custom format used by the RICHEDIT common control, and registered via RegisterClipboardFormat as already mentioned.

Related

Elctron clipboard.writeBuffer() doesn't write anything to the clipboard

I understand that you can't write multiple custom formats to the clipboard using clipboard.writeBuffer(),
But when I try to use it write a special format (not text, html, image or rtf), nothing is written to the clipboard:
const buffer = Buffer.from('slide format data');
clipboard.writeBuffer("application/special-format", buffer);
and consequently, nothing is being pasted when
webcontent.paste() is called.
That being said, I am able to read the buffer from the clipboard using clipboard.readBuffer(),
but clipboard.availbaleFormtas() returns an empty array.
Are some formats not supported at all?

Error loading txt files to synedit

I'm trying to load the text from a text file to a synmemo by using
procedure TForm1.btn7Click(Sender: TObject);
begin
if dlgOpen1.Execute then
synm1.Lines.LoadFromFile(dlgOpen1.Files.Text);
end;
But as soon as I select a file i get this error:
Cannot open file "C:\Users\adria\Desktop\New Text Document.txt
". The filename, directory name, or volume label syntax is incorrect.
Component: https://github.com/TurboPack/SynEdit
The problem is in the use of the Files property of the dialog to access the selected filename.
The Files property is a list of strings intended for use when you have enabled multiple selection in the dialog and need to process more than one filename selected by the user.
The Text property of string list returns a formatted representation of all entries in that list with each entry delimited by an EOL character (or characters).
You might expect that where only a single file is involved that this Textproperty would contain only the name of that file. But in fact it also includes an EOL character. i.e. the filename you are trying to open by using this technique is actually:
'C:\Users\adria\Desktop\New Text Document.txt'#13#10
There was actually a clue to this in the way that the message was being displayed, with the closing quotes on a separate line as a result of that EOL.
The correct way to work with the selected filename depends on whether you are supporting multiple selection or single.
In the case of single selection (your case here) the simplest approach is to use the Filename property of the dialog:
if dlgOpen1.Execute then
synm1.Lines.LoadFromFile(dlgOpen1.Filename);
For multiple selection you would use the Files property, but access each filename by index in the list:
if dlgOpen1.Execute then
for i := 0 to Pred(dlgOpen1.Files.Count) do
begin
// Do something with each dlgOpen1.Files[i] ...
end;

How do I work with Word Documents without using COM Automation?

I have read multiple posts on the issue, and none seem to come to a decent conclusion to my question. (Perhaps I'm trying to see if anything has popped up lately.)
I have a small charity app that handles pledges. In doing so, it needs to work with and print documents.
Thing is, if Word is open in the background, the app thread will hang and won't respond to the closure of Word, and I have to roll back manually and close word. Sure, that all works fine, but I simply cannot guarantee that the end user will close Word, even if I put the instruction in a user manual.
I'm not too fussed about speed, but I guess that if it can be enhanced, it would be a nice little bonus.
Have any libraries been released for Delphi that will allow me to open, edit, print, and save documents? If not, is there a way to use Word Automation in such a way that it will not conflict with another open handle of Word when opened?
If you use GetActiveOleObject, you will get the running instance of Word.
By using CreateOleObject, you will get a new instance and shouldn't be troubled by other running instances.
In case you use the TWordApplication, wrapper you can set ConnectKind to ckNewInstance to accomplish this. By default, TWordApplication will try to connect with a running instance.
If you want to open edit and print Word documents and you don't mind using RTF format for what you're doing, investigate TRichView.
It will generate rich documents that are in RTF format, which is one of the formats MS word supports. I don't think it directly reads .DOC files but you can convert .DOC and .DOCX into RTF, for most simple files, but certain advanced formatting features would be lost in the conversion.
It has the advantage of working without any need for even any copy of MS Word to be installed on the machine that is going to do the document processing. For production of receipts and other simple documents, this would be the most reliable technique; Don't use Word directly, at all.
procedure PrintViaWord (const filename: string);
const
wdUserTemplatesPath = 2;
var
wrdApp, wrdDoc, wrdSel: variant;
begin
wrdApp:= CreateOleObject ('Word.Application'); // create new instance
sleep (5000); // this fixes a bug in Word 2010 to do with opening templates
wrdDoc:= wrdApp.documents.add (
wrdApp.Options.DefaultFilePath[wdUserTemplatesPath] + '\mytemplate.dot');
wrdDoc.Select;
wrdSel:= wrdApp.selection;
wrdApp.Options.CheckSpellingAsYouType:= 0;
wrdSel.paragraphformat.alignment:= 1;
wrdSel.typetext ('This is a program demonstrating how to open Word in the background'
+ ' and add some text, print it, save it and exit Word');
wrdDoc.SaveAs (filename + '.doc');
wrdApp.ActivePrinter:= 'Konica Minolta 211';
wrdApp.PrintOut;
WrdDoc.SaveAs (filename + '.doc');
wrdApp.quit;
wrdSel:= unassigned;
wrdDoc:= unassigned;
wrdApp:= unassigned
end;

Loading the output from TOleContainer.SaveAsDocument

I have an existing database with blobs contain OLE compound files. I have a requirement to read these OLE compound files and open them in the Delphi 7 TOleContainer control.
Note that I don't have the source of the app that reads and write to the database. The database remains in active use, so my solution will be used on an ongoing basis, not just for a one-off data extraction.
TOleContainer has a SaveAsDocument method, and by experimentation I have found that, for a given file, this method produces OLE compound files which are identical to those created in the database when that file is added.
However, TOleContainer does NOT have a corresponding LoadFromDocument method. It has other Load* and Create* methods, but none seem capable or suitable for loading the output from SaveAsDocument.
The delphi 7 implementation of SaveAsDocument is this, from the OleCtnrs.pas module:
procedure TOleContainer.SaveAsDocument(const FileName: string);
var
TempStorage: IStorage;
PersistStorage: IPersistStorage;
begin
CheckObject;
if FModSinceSave then SaveObject;
FOleObject.QueryInterface(IPersistStorage, PersistStorage);
if PersistStorage <> nil then
begin
OleCheck(StgCreateDocFile(PWideChar(WideString(Filename)), STGM_READWRITE
or STGM_SHARE_EXCLUSIVE or STGM_CREATE, 0, TempStorage));
OleCheck(OleSave(PersistStorage, TempStorage, False));
PersistStorage.SaveCompleted(nil);
end;
end;
Please provide an implementation of LoadFromDocument which is capable of loading the output from SaveToDocument, and which I can use to patch OleCtnrs.pas. Or else point me to an existing solution.
Thanks!
You have to load the file by using TOleContainer.CreateObjectFromFile. Do not use TOleContainer.LoadFromStream/File, that only works with files that are saved with TOleContainer.SaveToStream/File. Files saved that way get a Delphi specific header containing a four byte code (BDOC) and size (and maybe something more).
According to the documentation for Delphi 2007 (should be the same for ), you can use 'TOleContainer.LoadFromStream'. From the Delphi 7 help file (emphasis mine):
Call LoadFromStream to load an OLE object from a stream. If OldStreamFormat is true, LoadFromStream loads OLE objects saved by a TOleContainer object as well as OLE objects saved using the current format; if OldStreamFormat is false, LoadFromStream will not load OLE objects saved by the library. If there's already an OLE object in the container, it is destroyed and any changes the user made to it are discarded.

Send an e-mail with rtf text in delphi

I would like to perform the following task: converting a TRichEdit content (an rtf text) into a not-plain-text e-mail message body.
MAPI doesn't support rtf, but is there a way to do it maybe with Indy?
The problem is that rtf is rtf and emails are plain text or HTML.
Can someone suggest a trick? Is it possible to convert rtf to text using TWebBrowser?
Basically the scenario is:
1) User writes email in a delphi form,
2) The email is then sent with MAPI to the default mail client (so a new email window is generated, and the message body is the same I had in delphi form)
3) User sends the email from mail client
Anyway MAPI accepts only plain text.
UPDATE:
Trying with Indy I wrote this but still it doesn't work, as I send a mail it to my gmail account I recieve a message with empty body and NONAME fake attachment.
uses IdMessageBuilder;
procedure SendMail;
var
MBuilder: TIdMessageBuilderRtf;
MyMemoryStream: TMemoryStream;
begin
try
MBuilder := TIdMessageBuilderRtf.Create;
MyMemoryStream := TMemoryStream.Create;
MBuilder.RtfType := idMsgBldrRtfRichtext;
// RichEdit1 has PlainText set to False
// at design time I pasted some formatted text onto it
RichEdit1.Lines.SaveToStream(MyMemoryStream);
MBuilder.Rtf.LoadFromStream(MyMemoryStream);
MBuilder.FillMessage(IdMessage1);
IdSMTP1.Connect;
IdSMTP1.Send(IdMessage1);
IdSMTP1.Disconnect;
finally
MyMemoryStream.Free;
MBuilder.Free;
end;
end;
Indy supports sending RTF emails. Send an email the same way you would send an HTML email, just use the "text/rtf", "text/enriched", or "text/richtext" Context-Type, and send the raw RTF codes that are generated by TRichEdit when its PlainText property is set to false. Also, if you are using Indy 10, look at its TIdMessageBuilderRtf class to set up the TIdMessage structure correctly.
Apparently (googled this myself, as I mail some from Delphi, but not in this case), the PR_RTF_COMPRESSED option of (extended) MAPI may help you here. Also look at this.
Is it possible to convert rtf to text using TWebBrowser?
You can convert RTF to Text using TRichEdit - but obviously this loses all formatting:
Using TRichEdit at runtime without defining a parent
2: You can find a number of RTF to HTML converters online - some free, some not. Like these:
http://wiki.delphi-jedi.org/wiki/JVCL_Help:TJvRichEditToHtml
https://www.habarisoft.com/scroogexhtml_delphi.html
http://www.easybyte.com/products/rtf2html.html
http://www.sautinsoft.com/products/rtf-to-html/index.php
3: You can rename the noname attachment as NONAME.MHT and it should then be openable with internet explorer.
However you might want to consider using an HTML edit control, rather than a TRichEdit. Then you have no conversion to worry about. Something like this question:-
WYSIWYG HTML Editor Component for Delphi

Resources