#1 Re: PDF Engine » Application terminates » 2012-04-06 18:22:05

In my tests of Result variable is initialized with Boolean=False, Integer=0, String='', Object=nil etc.
But I have not found any official reference about this.
But if my tests is false then it means that i always must start all functions with defining Result, because an exception can happen anywhere in the code.
And that is not the case in mu code at least.

Regards
Roland

ab wrote:

By design, the function is expected to return TRUE on success, and FALSE in case of error.

In this case, any error will be an exception. So exceptions have to be catched, and return false in this case.

#2 Re: PDF Engine » Application terminates » 2012-04-06 12:17:17

Just one further note.
What is the reason to eat an exception here in synpdf.pas ?

function TPdfDocument.SaveToFile(const aFileName: TFileName): boolean;
var FS: TFileStream;
begin
  try
    FS := TFileStream.Create(aFileName,fmCreate);
    try
      SaveToStream(FS);
      result := true;
    finally
      FS.Free;
    end;
  except
    on E: Exception do // error on file creation (opened in reader?)
      result := false;
  end;
end;

I think it could be changed to this ?

function TPdfDocument.SaveToFile(const aFileName: TFileName): boolean;
var FS: TFileStream;
begin
  FS := TFileStream.Create(aFileName,fmCreate);
  try
    SaveToStream(FS);
    result := true;
  finally
    FS.Free;
  end;
end;

Regards
Roland

#3 Re: PDF Engine » Application terminates » 2012-04-06 12:01:19

Yep, it works fine smile
Thanks for good cooperation!

Regards
Roland Bengtsson
Team Attracs

ab wrote:

If w was overriden, this was a stack corruption issue.

I think it should be fixed by now.
See http://synopse.info/fossil/info/3ca7c5c515

#4 Re: PDF Engine » Application terminates » 2012-04-05 20:04:20

Well, normally I have set

fPdfDocument.ForceNoBitmapReuse := True;

as a workaround. But for this test that code is commented out.

Here is the same method again with some additional logging.

function TPdfDocument.CreateOrGetImage(B: TBitmap; DrawAt: PPdfBox): PDFString;
var J: TJpegImage;
    Img: TPdfImage;
    Hash: TPdfImageHash;
    y,w,h,row: integer;
    nPals: cardinal;
    Pals: array of TPaletteEntry;
const PERROW: array[TPixelFormat] of byte = (0,1,4,8,15,16,24,32,0);
procedure DoHash(bits: pointer; size: Integer);
begin // "4 algorithms to rule them all"
  Hash[0] := Hash[0] xor Hash32(bits,size);
  Hash[1] := Hash[1] xor HashOf(bits,size);
  Hash[2] := crc32(Hash[2],bits,size);
  Hash[3] := adler32(Hash[3],bits,size);
end;
begin
  result := '';
  if (self=nil) or (B=nil) then exit;
  w := B.Width;
  h := B.Height;

  TraceLog.Trace(Format('Start %d %d', [w, h]));
  fillchar(Hash,sizeof(Hash),0);
  if not ForceNoBitmapReuse then begin
    row := PERROW[B.PixelFormat];
    if row=0 then begin
      B.PixelFormat := pf24bit;
      row := 24;
    end;
    TraceLog.Trace(Format('row %d %d', [row,w]));
    fillchar(Hash,sizeof(Hash),row);
    if B.Palette<>0 then begin
      nPals := 0;
     TraceLog.Trace(Format('GetObject %d', [w]));
      if (GetObject(B.Palette,sizeof(nPals),@nPals)<>0) and (nPals>0) then begin
        SetLength(Pals,nPals);
       TraceLog.Trace(Format('GetPalette %d', [w]));
        if GetPaletteEntries(B.Palette,0,nPals,Pals)=nPals then
        begin
          TraceLog.Trace(Format('DoHash', []));
          DoHash(pointer(Pals),nPals*sizeof(TPaletteEntry));
        end;
      end;
    end;
    TraceLog.Trace(Format('Scanline1 %d %d', [w, row]));
    row := BytesPerScanline(w,row,32);
    TraceLog.Trace(Format('Scanline2 %d %d', [w, row]));
    for y := 0 to h-1 do
    begin
      TraceLog.Trace('DoHash 1 ' + IntToStr(y));
      DoHash(B.ScanLine[y],row);
      TraceLog.Trace('DoHash 2 ' + IntToStr(y));
    end;

    TraceLog.Trace(Format('GetXObject1 %d %d', [w, h]));
    result := GetXObjectImageName(Hash,w,h);
    TraceLog.Trace(Format('GetXObject2 %d %d', [w, h]));
  end;
  if result='' then begin
     // create new if no existing TPdfImage match
    if ForceJPEGCompression=0 then
      Img := TPdfImage.Create(Canvas.fDoc,B,True) else begin
      J := TJpegImage.Create;
      try
        J.Assign(B);
        Img := TPdfImage.Create(Canvas.fDoc,J,False);
      finally
        J.Free;
      end;
    end;
    Img.fHash := Hash;
    result := 'SynImg'+PDFString(IntToStr(FXObjectList.ItemCount));
    if ForceJPEGCompression=0 then
      AddXObject(result,Img) else
      RegisterXObject(Img, result);
  end;
  // draw bitmap as XObject
  if DrawAt<>nil then
    with DrawAt^ do
      Canvas.DrawXObject(Left,Top,Width,Height,result);
end;

And the output in the log.
20120405 22:58:27 (7800) Start 674 155 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5047)]
20120405 22:58:27 (7800) row 24 674 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5055)]
20120405 22:58:27 (7800) GetObject 674 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5059)]
20120405 22:58:27 (7800) GetPalette 674 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5062)]
20120405 22:58:27 (7800) Scanline1 8388608 32768 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5070)]
20120405 22:58:27 (7800) Scanline2 8388608 0 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5072)]
20120405 22:58:27 (7800) DoHash 1 0 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5075)]

So it seems that w is trashed somehow at the call to GetPaletteEntries.
Hope this information help you.

You said:
The ScanLine[] use sounds quite regular here, don't you think?

I don't understand what you mean here.

Regards
Roland Bengtsson
Team Attracs

#5 Re: PDF Engine » Application terminates » 2012-04-05 06:10:09

Hi!
I downloaded and tested the latest trunk, still the same error.
I also added some logs to try to narrow the error for you.

This code:

function TPdfDocument.CreateOrGetImage(B: TBitmap; DrawAt: PPdfBox): PDFString;
var J: TJpegImage;
    Img: TPdfImage;
    Hash: TPdfImageHash;
    y,w,h,row: integer;
    nPals: cardinal;
    Pals: array of TPaletteEntry;
const PERROW: array[TPixelFormat] of byte = (0,1,4,8,15,16,24,32,0);
procedure DoHash(bits: pointer; size: Integer);
begin // "4 algorithms to rule them all"
  Hash[0] := Hash[0] xor Hash32(bits,size);
  Hash[1] := Hash[1] xor HashOf(bits,size);
  Hash[2] := crc32(Hash[2],bits,size);
  Hash[3] := adler32(Hash[3],bits,size);
end;
begin
  result := '';
  if (self=nil) or (B=nil) then exit;
  w := B.Width;
  h := B.Height;
  fillchar(Hash,sizeof(Hash),0);
  if not ForceNoBitmapReuse then begin
    row := PERROW[B.PixelFormat];
    if row=0 then begin
      B.PixelFormat := pf24bit;
      row := 24;
    end;
    fillchar(Hash,sizeof(Hash),row);
    TraceLog.Trace('Start');
    if B.Palette<>0 then begin
      nPals := 0;
     TraceLog.Trace('GetObject');
      if (GetObject(B.Palette,sizeof(nPals),@nPals)<>0) and (nPals>0) then begin
        SetLength(Pals,nPals);
       TraceLog.Trace('GetPalette');
        if GetPaletteEntries(B.Palette,0,nPals,Pals)=nPals then
        begin
          TraceLog.Trace('DoHash');
          DoHash(pointer(Pals),nPals*sizeof(TPaletteEntry));
        end;
      end;
    end;
    TraceLog.Trace('Scanline 1');
    row := BytesPerScanline(w,row,32);
    TraceLog.Trace('Scanline 2');
    for y := 0 to h-1 do
    begin
      TraceLog.Trace('DoHash 1 ' + IntToStr(y));
      DoHash(B.ScanLine[y],row);
      TraceLog.Trace('DoHash 2 ' + IntToStr(y));
    end;

    TraceLog.Trace('GetXObject1');
    result := GetXObjectImageName(Hash,w,h);
    TraceLog.Trace('GetXObject2');
  end;
  if result='' then begin
     // create new if no existing TPdfImage match
    if ForceJPEGCompression=0 then
      Img := TPdfImage.Create(Canvas.fDoc,B,True) else begin
      J := TJpegImage.Create;
      try
        J.Assign(B);
        Img := TPdfImage.Create(Canvas.fDoc,J,False);
      finally
        J.Free;
      end;
    end;
    Img.fHash := Hash;
    result := 'SynImg'+PDFString(IntToStr(FXObjectList.ItemCount));
    if ForceJPEGCompression=0 then
      AddXObject(result,Img) else
      RegisterXObject(Img, result);
  end;
  // draw bitmap as XObject
  if DrawAt<>nil then
    with DrawAt^ do
      Canvas.DrawXObject(Left,Top,Width,Height,result);
end;

Produced this output in the log
20120405 09:04:50 (6600) Start [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5054)]
20120405 09:04:50 (6600) GetObject [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5057)]
20120405 09:04:50 (6600) GetPalette [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5060)]
20120405 09:04:50 (6600) Scanline 1 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5068)]
20120405 09:04:50 (6600) Scanline 2 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5070)]
20120405 09:04:50 (6600) DoHash 1 0 [SynPdf.TPdfDocument.CreateOrGetImage (SynPdf.pas:5073)]

So it seems to fail for line

DoHash(B.ScanLine[y],row);

Note that this code is never executed

          TraceLog.Trace('DoHash');
          DoHash(pointer(Pals),nPals*sizeof(TPaletteEntry));

Regards
Roland Bengtsson

#6 Re: PDF Engine » Application terminates » 2012-04-04 07:46:35

Hi, I am working with the same application as Daniel. I have found more about the exact sourceline why it silently shut down the whole client when running in Citrix Terminal session from Citrix desktop.
We use version 1.16 of SynPDF. PUREPASCAL is defined.
So I try to describe what happens as exact as possible what happens.

Line 7491 in synpdf.pas

E.DrawBitmap(xSrc,ySrc,cxSrc,cySrc, xDest,yDest,cxDest,cyDest,iUsageSrc,
          pointer(cardinal(R)+offBmiSrc),pointer(cardinal(R)+offBitsSrc));

calls TPdfEnum.DrawBitmap. That procedure calls

Doc.CreateOrGetImage(B,@Box)

on line 7605.
In TPdfDocument.CreateOrGetImage there is a if statement on line 5047

if B.Palette<>0 then begin

so if this is true then it calls

DoHash(pointer(Pals),GetPaletteEntries(B.Palette,0,256,Pals)*sizeof(TPaletteEntry))

and the whole process is shut down silently.
But when it works the statement is false and DoHash is never called.
This is when application is started outside Citrix Desktop.

I have added more logs to the code and comment out the is the lines

//    if B.Palette<>0 then
//    begin
//      SetLength(Pals,256);
//      DoHash(pointer(Pals),GetPaletteEntries(B.Palette,0,256,Pals)*sizeof(TPaletteEntry));
//    end;

And it works smile
But I'm not sure in what cases this is really needed.
Hopefully this is enough information to get it working in all cases.

Regards
Roland Bengtsson
Team Attracs

EDIT:
So I try to investigate why DoHash fails.
I found this comment.

  - fixed a potential GPF issue in function HashOf() in PUREPASCAL mode (used
    to reuse any existing bitmap content within the PDF document)

So there is a risk of General Protection Fault when using PUREPASCAL mode ?
I have add more logs to the code to isolate the problem.
This code in HashOf method generate general Protection Fault in the loops first element (i=0).

   Result := ((Result shl 2) or (Result shr (SizeOf(Result)*8-2))) xor P[i];

My guess is that the array P is somehow broken in the previous method Hash32.
Currently I set ForceNoBitmapReuse := True as a workaround for this problem

ab wrote:

Did you use the latest version from trunk?
http://synopse.info/fossil

The upcoming 1.16 revision have some enhancements to improve consistency.

The case statement implementation sounds pretty valid.
It will just ignore any other pattern than PATCOPY...

Logging the whole PEMRStretchBlt(R)^ content would make sense, anyway.

Board footer

Powered by FluxBB