#1 2015-03-30 12:43:52

matthiasp
Member
Registered: 2015-03-30
Posts: 6

Out of memory - Many images in one pdf

Hi everybody,

I want to save many tif images (486, one file ~3MB) in one pdf. But now I have an "Out of memory"-problem.

var
  i: Integer;
  lPdf: TPdfDocument;
  lPage: TPdfPage;
  lImage :TPdfImage;
  lFiles: TStringList;
begin
  lFiles:= TStringList.Create;
  try
    // Search for tif 
    ScanDir(FImageFolder, '*.tif', True, False, lFiles);
    lPDF:= TPdfDocument.Create();
    lPDF.NewDoc;
    lPDF.DefaultPaperSize:= psA4;
    try
      for i:= 0 to Pred(lFiles.Count) do
      begin
        lPage:= FPDF.AddPage;
        img1.LoadFromFile(lFiles[i], 0);
        lImage := TPdfImage.Create(lPDF, img1.Graphic, true);
        lPDF.AddXObject('image' + IntToStr(i), lImage);
        lPDF.Canvas.DrawXObject(0, 0, lPage.PageWidth, lPage.PageHeight, 'image' + IntToStr(i));
      end;
    finally
      lPDF.SaveToFile(ExtractFilePath(Application.ExeName) + 'output.pdf');
      lPDF.Free;
    end;
  finally
    lFiles.Free;
  end;
end;

After 190 files I get a "Out of memory" Error.
I don't know how I can fix this.

Maybe someone knows a solution to the problem.

Thanks, Matthias.

Offline

#2 2015-03-30 12:57:33

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Out of memory - Many images in one pdf

Take a look at TPdfDocument.SaveToStreamDirectBegin/PageFlush/End methods, able to render all page content directly to the destination stream/file, therefore reducing the memory use to a minimal value for huge content - used e.g. in TPdfDocumentGDI.SaveToStream() and TGDIPages.ExportPDFStream().

See http://synopse.info/files/html/api-1.18 … IRECTBEGIN

Offline

#3 2015-03-30 14:28:47

matthiasp
Member
Registered: 2015-03-30
Posts: 6

Re: Out of memory - Many images in one pdf

Thank you, ab.

I changed my code to:

var
  i: Integer;
  lPage: TPdfPage;
  lPdf: TPdfDocument;
  lImage :TPdfImage;
  lFiles: TStringList;
  fs: TFileStream;
begin
  lFiles:= TStringList.Create;
  try
    ScanDir(FImageFolder, '*.tif', True, False, lFiles);
    if lFiles.Count = 0 then
      Exit;

    lPdf:= TPdfDocument.Create;
    fs:= TFileStream.Create(ExtractFilePath(Application.ExeName) + 'output.pdf', fmCreate);
    lPdf.NewDoc;
    lPdf.DefaultPaperSize:= psA4;
    try
      lPdf.SaveToStreamDirectBegin(fs);
      for i:= 0 to Pred(lFiles.Count) do
      begin       
        lPage:= lPdf.AddPage;
        img1.LoadFromFile(lFiles[i], 0);
        lImage := TPdfImage.Create(lPdf, img1.Graphic, true);
        lPdf.AddXObject('image' + IntToStr(i), lImage);
        lPdf.Canvas.DrawXObject(0, 0, lPage.PageWidth, lPage.PageHeight, 'image' + IntToStr(i));
        lPdf.SaveToStreamDirectPageFlush;
        img1.Clear;
      end;
      lPdf.SaveToStreamDirectEnd;
    finally
      fs.Free;
      lPdf.Free;
    end;
  finally
    lFiles.Free;
  end;
end;

It's little bit better.
Now I can save 391 of 486 images in the pdf before I get the "out of memory" error.
Don't know why, in taskmanager the programsize is around 30MB all the time.

Is it possible to destroy the lImage object after the flush?

Any ideas?

Offline

#4 2015-03-30 18:16:10

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Out of memory - Many images in one pdf

AFAIK, the image should be released when flush to the disk.
See FWriter.fDestStream.Size := 0 in TPdfStream.InternalWriteTo.

Try to use the new FlushCurrentPageNow parameter of TPdfDocument.SaveToStreamDirectPageFlush(), or the new low-level TPdfObject.ForceSaveNow method.
See http://synopse.info/fossil/info/51487ef761

Offline

#5 2015-03-31 07:53:19

matthiasp
Member
Registered: 2015-03-30
Posts: 6

Re: Out of memory - Many images in one pdf

Sorry, same problem. Here is my sourcecode for testing:

var
  i: Integer;
  lPage: TPdfPage;
  lPdf: TPdfDocument;
  lImage :TPdfImage;
  lBmp: TBitmap;
  fs: TFileStream;
begin
  lBmp:= TBitmap.Create;
  lPdf:= TPdfDocument.Create;
  fs:= TFileStream.Create(ExtractFilePath(Application.ExeName) + 'output.pdf', fmCreate);
  try
    lBmp.PixelFormat:= pf8bit;
    lBmp.Width:= 2048;
    lBmp.Height:= 2600;
    lPdf.NewDoc;
    lPdf.DefaultPaperSize:= psA4;
    lPdf.SaveToStreamDirectBegin(fs);
    for i:= 1 to 500 do
    begin
      lPage:= lPdf.AddPage;
      lImage := TPdfImage.Create(lPdf, lBmp, true);
      lPdf.AddXObject('image' + IntToStr(i), lImage);
      lPdf.Canvas.DrawXObject(0, 0, lPage.PageWidth, lPage.PageHeight, 'image' + IntToStr(i));
      lPdf.SaveToStreamDirectPageFlush(True);
    end;
    lPdf.SaveToStreamDirectEnd;
  finally
    fs.Free;
    lPdf.Free;
    lBmp.Free;
  end;
end;

Offline

#6 2015-03-31 07:55:48

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Out of memory - Many images in one pdf

And with lImage.ForceSaveNow before lPdf.SaveToStreamDirectPageFlush ?

Offline

#7 2015-03-31 08:05:26

matthiasp
Member
Registered: 2015-03-30
Posts: 6

Re: Out of memory - Many images in one pdf

I have tried it now. Nothing changed.

...
lPdf.AddXObject('image' + IntToStr(i), lImage);
lPdf.Canvas.DrawXObject(0, 0, lPage.PageWidth, lPage.PageHeight, 'image' + IntToStr(i));
lImage.ForceSaveNow;
lPdf.SaveToStreamDirectPageFlush(True);
...

Offline

#8 2015-03-31 08:07:50

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Out of memory - Many images in one pdf

Could you use the debugger and find out why the FWriter.fDestStream.Size := 0 in TPdfStream.InternalWrite is not releasing the memory.

Offline

#9 2015-03-31 08:49:10

matthiasp
Member
Registered: 2015-03-30
Posts: 6

Re: Out of memory - Many images in one pdf

Seems to be ok. Frees memory in SynCommons. Jumps into:

function THeapMemoryStream.Realloc(var NewCapacity: longint): Pointer;
...
if NewCapacity <> Capacity then begin
  if NewCapacity = 0 then begin
    FreeMem(Memory);
    Result := nil;
  end else begin
    if Capacity = 0 then
      GetMem(Result, NewCapacity) else
    if NewCapacity > Capacity then // only realloc if necessary (grow up)
      ReallocMem(Result, NewCapacity) else
      NewCapacity := Capacity; // same capacity as before
    if Result = nil then
      raise EStreamError.Create('THeapMemoryStream'); // memory allocation bug
  end;
end;
...

If you want to try, increase imagesize. With lBmp.Width:= 20048; lBmp.Height:= 26000; program crashes after 3 images.

Offline

#10 2015-03-31 13:38:17

matthiasp
Member
Registered: 2015-03-30
Posts: 6

Re: Out of memory - Many images in one pdf

Solution: Use FastMM in project and it works!

uses
  FastMM4, 
  ...

@ab: big thank you.

Offline

#11 2015-03-31 15:31:00

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,660
Website

Re: Out of memory - Many images in one pdf

Always use FastMM4 in older versions of Delphi.
smile

Offline

Board footer

Powered by FluxBB