Hash value of a report
Hi,
Probably a weird question to ask, but I was wondering if anyone has any
suggestions on how I could go about checking / verifying when a report
is printed to see if it matches exactly to a previous one that has been
printed?
Is there a way to perform a hash tag on the report contents as such, or
what would be the best method to compare one report to another to see if
they are the same.
I'm mainly just wanting to verify text fields, so I'm happy if it only
checks text - but am equally as happy if the process also checks images
/ lines shapes / etc.
Thanks & regards
Adam
Probably a weird question to ask, but I was wondering if anyone has any
suggestions on how I could go about checking / verifying when a report
is printed to see if it matches exactly to a previous one that has been
printed?
Is there a way to perform a hash tag on the report contents as such, or
what would be the best method to compare one report to another to see if
they are the same.
I'm mainly just wanting to verify text fields, so I'm happy if it only
checks text - but am equally as happy if the process also checks images
/ lines shapes / etc.
Thanks & regards
Adam
This discussion has been closed.
Comments
When a report is printed, a TppPage object is created for each page.
Each TppPage object is essentially a list of DrawCommand objects
containing instructions how to render each report component.
If you have an archive of a previously printed report, you can easily
print that report with the archive reader's Publisher.CachePages
property set to True to obtain a list of the Page objects generated.
Then with the new report's CachePages set to true, you would do the same
thing with your newly generated report. From there you can access each
list of page objects (Publisher.Pages[]) and compare them as you like.
I suggest taking a look at the TppPage class located in the ppDevice.pas
file to become familiar with its properties.
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com
Thanks for the suggestion. However, I'm not wanting to save the entire
report (that would require a large bit of storage for the amount of
reports that will be generated in the end )
Rather, I would like to generate a hash tag from the report when
printing, and compare it to a previous one to see if they match.
I was wondering if it was possible to do something such as printing the
report to a stream, and then hashing that stream?
I've tried (using your suggestion) to generate a hash using the cache
with the following code, but I get an access violation when calling
GetPageFromCache.
I was just wondering if you might have any suggestions?
(The report is set to twopass:)
Thanks & Regards
Adam.
-----------------------
procedure TForm1.Button1Click(Sender: TObject);
begin
ppreport1.print;
end;
function idbytestohex(b1 : TidBytes) : Widestring;
var
i : integer;
s : string;
begin
s := '';
for i := 0 to length(b1) do
s := s + bytetohex(b1[i]);
result := s;
end;
Function HashReport(Report: TppReport) : TIdBytes;
var
i : integer;
j : integer;
ms : TMemoryStream;
Hash : TIDHash;
LongHash : WideString;
b1 : TidBytes;
EndHash : Tidbytes;
begin
ms := tmemorystream.create;
LongHash := '';
// First go through each page and generate a has for it, adding all the
hashes together
for i := 0 to Report.CacheManager.CachePageCount - 1 do
for j := 0 to Report.Publisher.GetPageFromCache(i).DrawCommandCount - 1 do
begin
Report.Publisher.GetPageFromCache(i).DrawCommands[j].AsMetaFile.SaveToStream(MS);
b1 := hash.hashstream(ms);
LongHash := LongHash + idbytestohex(b1);
end;
Endhash := hash.HashString(LongHash);
result := endhash;
end;
procedure TForm1.ppReport1StartSecondPass(Sender: TObject);
var
hash : TIdBytes;
begin
hash := HashReport(Tppreport(sender));
showmessage(idbytestohex(hash));
end;
The solution I presented was a general one. It is not a built-in
feature of ReportBuilder.
The basic idea is to extract the page objects from each report and
compare the two. How you access the page objects and what you do with
them is completely up to you.
Some Notes:
1. In your code, it is not possible to access the page cache after the
first pass of a report.
2. Take a look at the following example and article. There is code that
prints a report to a generic device to cache the pages for later use.
http://www.digital-metaphors.com/rbWiki/Delphi_Code/Layouts/How_To...Create_a_Spread_Sheet_Style_Report
3. It is also possible to access the page objects as they are generated
by implementing the Device.OnPageReceive event.
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com
Thanks for your reply. It looks as though this might be a bit more
difficult than I first thought.
I'll check out the example and see what I can find.
Thanks & Regards
Adam
I've been trying to use the example that you've provided on your link to
implement into my code, but I'm still experiencing problems.
The problem I face is that I am still unable to access the cached pages.
lPublisher.GetPageFromCache(i) is returning nil.
Here is the modified code that I am now using. I was wondering if you
could please advise where I've gone wrong?
Thanks & Regards
Adam.
---------------
function idbytestohex(b1 : TidBytes) : Widestring;
var
i : integer;
s : string;
begin
s := '';
for i := 0 to length(b1) do
s := s + bytetohex(b1[i]);
result := s;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
lDevice: TppDevice;
lPages : TList;
i, j : integer;
lpublisher : TppPublisher;
lpage : tppPage;
lclonepage : TppPage;
ms : TMemoryStream;
Hash : TIDHash;
LongHash : WideString;
b1 : TidBytes;
EndHash : Tidbytes;
begin
lDevice := TppDevice.Create(Self);
lDevice.PageSetting := psLastPage;
lDevice.Publisher := ppReport1.Publisher;
ppReport1.CachePages := True;
ppReport1.PrintToDevices;
lPages := TList.Create;
lPublisher := ppReport1.Publisher;
MS := TMemoryStream.create;
// work through all pages
for i := 0 to lPublisher.PageCount-1 do
for j := 0 to lPublisher.Pages[i].DrawCommandCount - 1 do
begin
{******FAILS HERE. GetPageFromCache is Nil******}
lPublisher.GetPageFromCache(i).DrawCommands[j].AsMetaFile.SaveToStream(MS);
b1 := hash.hashstream(ms);
LongHash := LongHash + idbytestohex(b1);
end;
lDevice.Free;
Endhash := hash.HashString(LongHash);
showmessage(idbytestohex(endhash));
end;
1. GetPageFromCache expects a page number as its parameter. You are
starting with page 0 which does not exist. If you take a closer look at
the example, it uses Publisher.Pages[] property to access a 0-based page
list.
2. Not all drawcommands implement the AsMetaFile routine. You are going
to want to check if this returns nil or you may run into more issues.
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com