I am using the following code to export an Open Office document as a pdf file using Delphi:
procedure TOOoWriter.SaveToPDF(FileName: string);
var
wProperties: variant;
begin
if not (fConnected and fDocumentOpened) then
abort;
wProperties := VarArrayCreate([0, 3], varVariant);
if fHTMLSrc then
wProperties[0] := MakePropertyValue('FilterName', 'writer_web_pdf_Export')
else
wProperties[0] := MakePropertyValue('FilterName', 'writer_pdf_Export');
wProperties[1] := MakePropertyValue('CompressionMode', '1');
wProperties[2] := MakePropertyValue('Pages', 'All');
wProperties[3] := MakePropertyValue('Overwrite', TRUE);
fDocument.StoreToURL('file:///'+ StringReplace(FileName, '\', '/', [rfIgnoreCase, rfReplaceAll]), wProperties);
end;
All is working well except:
it insists on opening the resultant pdf file (but not the OO file). This is problematic because I will be writing 100s of files without user interaction.
if the output pdf file does not exist I get an exception, but the file is created properly. The second time it works ok as the file was created the first time.
Are there solutions to these problems?
Related
I am a beginner in Delphi (I use Delphi 2010 because of school) and I am trying to get an output textfile with a list of pastes that a pastebin user has created, but I am not exactly sure how to do it. On Pastebin.com (PastebinAPI), they explain how the API works, but I can't get it working on Delphi.
Here is what I have coded so far (I blurred out my details):
procedure TfrmLogin.imgLoginButtonClick(Sender: TObject);
var
sSource, sAPI_Dev_Key, sAPI_User_Key, sAPI_Results_Limit, sAPI_Option,
sListPasteLink: String;
begin
sSource := 'https://pastebin.com/api/api_post.php/';
sAPI_Dev_Key := 'xxxxxxxxxxxxxxxxxxxxxxxx/';
sAPI_User_Key := 'xxxxxxxxxxxxxxxxxxxxxxx/';
sAPI_Results_Limit := '1000/';
sAPI_Option := 'list';
sListPasteLink := sSource + sAPI_Dev_Key + sAPI_User_Key +
sAPI_Results_Limit + sAPI_Option;
end;
I am not sure what to do after this, how do I POST this generated link in Delphi to get the list of pastes created?
I tried copying the generated link and pasting it in my web browser, but Pastebin says This page has been removed!
Any help would be appreciated, thank you
Here is an example. Drop a TButton, a TMemo, a TIdHTTP and a TIdSSLIOHandlerSocketOpenSSL on a form. You will also need to copy libeay32.dll and ssleay32.dll into your application directory (they are provided somehere in the directory where Delphi was installed).
procedure TForm1.Button1Click(Sender: TObject);
var
Params: TStringList;
begin
Params := TStringList.Create;
Params.Add('api_dev_key=*****');
Params.Add('api_user_key=*****');
Params.Add('api_option=list');
try
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.SSLVersions := [sslvTLSv1_1, sslvTLSv1_2];
IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
Memo1.Text := IdHTTP1.Post('https://pastebin.com/api/api_post.php', Params);
finally
Params.Free;
end;
end;
I have written a routine in Delphi Tokyo which takes multiple files (such as CSV) and merges them together, giving the user the option to ignore the first line on all files except the first one (as CSV files often have header lines/column name lines, when merging the files, I only want one copy of the header). The issue I am having is that even though I am only reading the various input files, if the file is open in another process, (specifically Excel), my app gives an error: "Cannot open file . The process cannot access the file because it is being used by another process."
I am using TStreamReader. How do I tell TStreamReader that it should open the file in read-only...and continue even if the file is open elsewhere?
Code below:
procedure glib_MergeTextFiles(const InFileNames: array of string; const OutFileName: string;
HasHeader: Boolean = True;
KeepHeader: Boolean = True);
var
I: Integer;
InStream: TStreamReader;
OutStream: TStreamWriter;
Line: string;
IsFirstLine: Boolean;
begin
// Create our output stream
OutStream := TStreamWriter.Create(OutFileName, False, TEncoding.UTF8);
try
for I := 0 to high(InFileNames) do
begin
InStream := TStreamReader.Create(InFileNames[I], TEncoding.UTF8);
IsFirstLine := True;
try
while not InStream.EndOfStream do
begin
Line := InStream.ReadLine;
if IsFirstLine then { First Line }
begin
if HasHeader = False then
begin
OutStream.WriteLine(Line);
end
else
begin
// Is First Line, Has Header
if I = 0 then {is first file}
OutStream.WriteLine(Line);
end;
end
else
begin
OutStream.WriteLine(Line);
end;
IsFirstLine := False;
end;
finally
InStream.Free;
end;
end;
finally
OutStream.Free;
end;
end;
The problem is with the sharing mode. By default, the stream reader creates a file stream for reading only, but specifies no sharing mode, so it opens the file for exclusive access. However, to open a file for reading when it is already opened elsewhere, the file must have been previously opened to share reading access using the FILE_SHARE_READ flag:
FILE_SHARE_READ
0x00000001
Enables subsequent open operations on a file or device to request read access.
Otherwise, other processes cannot open the file or device if they request read access.
If this flag is not specified, but the file or device has been opened for read access, the function fails.
You can pass your own file stream to the stream reader, opened with the mode you like:
var
I: Integer;
FileStream: TFileStream;
InStream: TStreamReader;
..
begin
...
FileStream := TFileStream.Create(InFileNames[I], fmOpenRead or fmShareDenyNone);
try
InStream := TStreamReader.Create(FileStream, TEncoding.UTF8);
try
..
Again, this requires Excel doing the same while opening the file, but with my simple test it looks like it does.
Have a set of Word-templates (files *.dot) and a little program, which create new files base on that templates. It's works fine, but the goal is to make all in one exe-file.
I see the solution is to move templates files into program resources. But I don't know, how then I will read them from resources. Tell me, please, how to do this.
Maybe you can advise me another solution.
Now, my code is:
procedure TfmMain.CreateDocument0;
var
TempleateFileName: string;
WordApp, Document: OleVariant;
procedure FillBookmark(BookmarkName, bText: string);
var
Range: OleVariant;
begin
if Document.Bookmarks.Exists(BookmarkName) then
begin
Range := Document.Bookmarks.Item(BookmarkName).Range;
Range.Text := bText;
end;
end;
begin
TempleateFileName := ExtractFilePath(Application.ExeName)+'Templates\0.dot';
try
WordApp := GetActiveOleObject('Word.Application');
except
try
WordApp := CreateOleObject('Word.Application');
except
on E: Exception do
begin
MessageBox(Self.Handle, PChar(E.Message), PChar(fmMain.Caption), MB_OK+MB_ICONERROR);
Exit;
end;
end;
end;
try
Document := WordApp.Documents.Add(TempleateFileName, False);
FillBookmark('ObjectType', edt0ObjectType.Text);
...
WordApp.Visible := True;
WordApp.Activate;
finally
WordApp := Unassigned;
end;
end;
That is, I should change this line:
Document := WordApp.Documents.Add(TempleateFileName, False);
Read not from file, but from program resource.
Word cannot open documents from memory. Not only does it not have such a feature, you must also bear in mind that Word executes in a separate process. It cannot see the memory in your process, even if it were able to open documents from memory.
If you do put the documents into linked resources then you will need to extract them to file before asking Word to open them.
with below example i am counting number of lines in .dfm file and the count is coming wrong because .dfm is saved in binary format.
if i open .dfm file and do right click and set text dfm to checked and the count is coming correctly. Below is the code
function TForm1.FindNumberOfLinesInFile(FileName: String): Integer;
var
contents : TStringList;
filestream : TFileStream;
outStream : TMemoryStream;
begin
try
try
Result := 0;
contents := TStringList.Create;
if edtFileToSearch.Text = '.dfm' then
begin
contents.LoadFromFile(FileName);
//i am binary
if pos('OBJECT', Uppercase(contents[0])) = 0 then // Count is coming wrong with this
begin
contents.Clear;
fileStream := TFileStream.Create(FileName, fmShareDenyNone);
outStream := TMemoryStream.Create;
try
ObjectResourceToText(filestream,outStream);
outStream.Position := 0;
Contents.LoadFromStream(outStream);
finally
FreeAndNil(outStream);
end;
end
else
begin
fileStream := TFileStream.Create(FileName, fmShareDenyNone);
Contents.LoadFromStream(fileStream);
end;
end
else
begin
fileStream := TFileStream.Create(FileName, fmShareDenyNone);
Contents.LoadFromStream(filestream);
end;
Result := contents.Count;
finally
FreeAndNil(fileStream);
FreeAndNil(contents);
end;
except
on e: Exception do Result := -1;
end;
end;
i have 2 questions
1)how to set text dfm value to checked in all dfm files(i have around 1000 dfm files)?
2)how load binary file correctly and count number of lines?
Delphi comes with a command line tool to do this, named convert. Open up a command prompt and ensure that your Delphi bin directory is in the PATH. Then type:
C:\projects\myprocject> convert
The output will be something like this:
Delphi Form Conversion Utility Version 5.0
Copyright (c) 1995,99 Inprise Corporation
Usage: convert.exe [-i] [-s] [-t | -b]
-i Convert files in-place (output overwrites input)
-s Recurse subdirectories
-t Convert to text
-b Convert to binary
So, you should be able to write:
C:\projects\myprocject> convert -i -s -t *.dfm
to effect the change required.
David's answer addresses the first of your questions: You can convert all of your existing binary DFM's to text using the command line tool provided with Delphi.
As well as addressing your immediate problem this is also highly recommended as it will make it much easier (i.e. possible at all!) to visually diff changes to your DFM files in version control.
As for the second part, if for some reason you still want or need to handle binary DFM files in your code is to use the TestStreamFormat() function to determine whether a stream is a valid resource stream and whether it is binary or text format, before calling ObjectResourceToText() function only if required.
This helper function to return the contents of a specified filename (of a DFM) into a supplied TStrings (e.g. a TStringlist) demonstrates this and might simplify things for you:
procedure GetDfmIntoStrings(aFilename: String; aStrings: TStrings);
var
istrm, ostrm: TStream;
begin
ostrm := NIL;
istrm := TFileStream.Create(aFilename, fmOpenRead or fmShareDenyNone);
try
case TestStreamFormat(istrm) of
sofBinary : begin
ostrm := TStringStream.Create('');
ObjectResourceToText(istrm, ostrm)
end;
sofText : ostrm := istrm;
else
raise EFilerError.Create(aFilename + ' is not a valid resource stream (DFM)');
end;
ostrm.Position := 0;
aStrings.LoadFromStream(ostrm);
finally
if ostrm <> istrm then
ostrm.Free;
istrm.Free;
end;
end;
With many thanks to f.i. TLama I could set up a workaround for opening a file, replacing some fields and store the result to pdf using Delphi 2007. I have a document opened in OpenOffice like this:
FileParams := VarArrayCreate([0, 0], varVariant);
FileProperty := StarOffice.Bridge_GetStruct ('com.sun.star.beans.PropertyValue');
FileProperty.Name := 'Hidden';
FileProperty.Value := true;
FileParams[0] := FileProperty;
StarDocument := StarDesktop.LoadComponentFromURL(AFileURL, '_blank', 0, FileParams);
Where AFileURL is the full pathname starting with 'file:///'
Later I replace the fields in this document with the option I discovered elsewhere on this site:
FileReplace := StarDocument.CreateReplaceDescriptor;
FileReplace.SearchCaseSensitive := False;
FileReplace.SetSearchString(zoekstring);
FileReplace.SetReplaceString(vervang);
StarDocument.ReplaceAll(FileReplace);
And then store the document to pdf.
Actually the file is a doc-file simply copied to a file with extension odt.
Everything is working fine if I first load the document in OpenOffice and save as Openoffice odt-document, but I want to do that programmaticly.
So, before being able to replace the WORD-fields (like «11») I want to store the odt-file as a proper OpenOffice document, because like this Open Office does not recognize the fields to replace.
I tried:
if StarDocument.hasLocation then
try
StarDocument.Store();
except
showmessage('unable to save');
end;
Reading OpenOffice documentation gave me the idea that this might work, but it does not.
I also tried:
function CreateProperty(const AName: AnsiString; AValue: Variant): Variant;
begin
Result := StarOffice.Bridge_GetStruct('com.sun.star.beans.PropertyValue');
Result.Name := AName;
Result.Value := AValue;
end;
FilterParams := VarArrayCreate([0, 0], varVariant);
FilterParams[0] := CreateProperty('FilterName', 'Text');
StarDocument.StoreAsURL(AFileURL,FilterParams);
Does not work either. I get a Fatal Error from Open Office. Who knows what I am missing?