Recent

Author Topic: [Solved] Convert PDF to bitmap using Ghostscript in cmd and read output pipe  (Read 6169 times)

danielthoss

  • New member
  • *
  • Posts: 6
Hi Forum,

I want to convert a PDF to a bitmap. I use the program Ghostscript to do this. Actually I let Ghostscript write a *.bmp-File and create a TBitmap from it. This works!!

But now I want to use the output pipe to read the bitmap without the detour over the *.bmp-File - maybe this way saves a lot of time because the files are bigger then 100MB.

I used this tutorial to do this: http://forum.codecall.net/topic/72472-execute-a-console-program-and-capture-its-output/

Question 1: In the tutorial he first wait for the end of the process and then reads the output pipe. If I'm doing this, the process never exit. But why?

Question 2: If I'm using just a small buffer size like 2048 it works, but if I increase it the image if broken and sometimes the process never exit. But why?

Have anyone an idea whats my mistake?


Start converting by a button press
Code: Pascal  [Select][+][-]
  1. procedure TfrmPDF.Button1Click(Sender: TObject);
  2. var
  3.   MemStream: TMemoryStream;
  4.   sGS, cmd: String;
  5.   pic: TPicture;
  6. begin
  7.   self.timePreview:=GetTickCount();
  8.   sGS      := pdfUtil.getFileTmp();
  9.   cmd      := pdfUtil.getGhostCmdPipe(30, 1, 'C:\Users\thoss\Desktop\KAMA_8UP_final.pdf');
  10.  
  11.   logging.log('TEST', cmd);
  12.  
  13.   // Run Ghostscipt and read the bitmap from output pipe
  14.   MemStream := TMemoryStream.Create;
  15.   pdfUtil.RunCmdAsAnsiStringAndReadInMemStream(cmd, MemStream);
  16.  
  17.   // Create the bitmap from memory stream
  18.   pic := TPicture.Create();
  19.   pic.LoadFromStream(MemStream);
  20.  
  21.   // Show the image
  22.   self.timePreview:=GetTickCount()-self.timePreview;
  23.   self.Caption:= IntToStr(self.timePreview);
  24.   imgOriginal.Picture := pic;
  25.  
  26.   FreeAndNil(MemStream);
  27. end;    

Call ghostscript and read from output pipe
Code: Pascal  [Select][+][-]
  1. function TFDFUtil.RunCmdAsAnsiStringAndReadInMemStream(cmd: AnsiString; memStream: TMemoryStream): DWORD ;
  2. const
  3.   ReadBufferCONST = 2048;
  4. var
  5.   security : TSecurityAttributes;
  6.   readPipe, WritePipe : THandle;
  7.   start : TStartUpInfo;
  8.   processInfo : TProcessInformation;
  9.   bytesRead, bytesReadTotal : DWord;
  10.   apprunning, exitCode : DWord;
  11.   i: Integer;
  12. begin
  13.   BytesRead := 0;
  14.   bytesReadTotal := 0;
  15.   Security.nlength := SizeOf(TSecurityAttributes);
  16.   Security.binherithandle := true;
  17.   Security.lpsecuritydescriptor := nil;
  18.  
  19.   // Create Pipes
  20.   if Createpipe (ReadPipe, WritePipe, @Security, 0) then
  21.   begin
  22.     FillChar(Start,Sizeof(Start),#0);
  23.  
  24.     start.cb := SizeOf(start);
  25.     start.hStdOutput := WritePipe;
  26.     start.hStdInput := ReadPipe;
  27.     start.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
  28.     start.wShowWindow := SW_HIDE;
  29.  
  30.     // Create process
  31.     if CreateProcess(nil, PChar(cmd), @Security, @Security, true, NORMAL_PRIORITY_CLASS, nil, nil, start, ProcessInfo) then
  32.     begin
  33.       repeat
  34.         // make sure we have room
  35.         MemStream.SetSize(bytesReadTotal + ReadBufferCONST);
  36.  
  37.         ReadFile(ReadPipe, (MemStream.Memory + bytesReadTotal)^, ReadBufferCONST, BytesRead, nil);
  38.         bytesReadTotal := bytesReadTotal + BytesRead;
  39.  
  40.         if (BytesRead < ReadBufferCONST) then
  41.         begin
  42.           logging.log('RunCmdAsAnsiStringAndReadInMemStream', 'Readed in total: ' + IntToStr(bytesReadTotal + BytesRead));
  43.           break;
  44.         end;
  45.  
  46.         Apprunning := WaitForSingleObject(ProcessInfo.hProcess, 1);
  47.       until (Apprunning <> WAIT_TIMEOUT);
  48.  
  49.       if (apprunning = WAIT_FAILED) then
  50.       begin
  51.         exitCode := GetLastError();
  52.         logging.log('oFoilUtil | RunCmdAsAnsiString', 'System Error Codes: ' + IntToStr(exitCode));
  53.       end;
  54.     end;
  55.  
  56.     // Check return value
  57.     GetExitCodeProcess(ProcessInfo.hProcess, &exitCode);
  58.  
  59.     if (exitCode <> 0) then
  60.     begin
  61.       ShowMessage('Error: ' + cmd + ' failed with ' + IntToStr(exitCode));
  62.       logging.log('oFoilUtil | RunCmdAsAnsiString', 'Error: ' + cmd + ' failed with ' + IntToStr(exitCode));
  63.     end;
  64.  
  65.     // Free everything
  66.     CloseHandle(ProcessInfo.hProcess);
  67.     CloseHandle(ProcessInfo.hThread);
  68.     CloseHandle(ReadPipe);
  69.     CloseHandle(WritePipe);
  70.  
  71.     result := exitCode;
  72.  end;
  73. end;    
« Last Edit: February 26, 2016, 12:42:59 pm by danielthoss »

danielthoss

  • New member
  • *
  • Posts: 6
I forgot to post the command to run ghostscript - if anyone whats to test the problem :)

Quote
cmd := C:\Program Files\gs\gs9.16\bin\gswin64c.exe -dUseCIEColor -dNumRenderingThreads=8 -sDEVICE=bmp16m -r30 -dNOPAUSE -dFirstPage=1 -dLastPage=1 -sOutputFile="C:\Temp\BMPTmp.bmp" "C:\Users\thoss\Desktop\ART1512326_HP30000.pdf" -c quit

GetMem

  • Hero Member
  • *****
  • Posts: 3757
@danielthoss
1. Try TProcess with large output: http://wiki.freepascal.org/Executing_External_Programs#Reading_large_output
2. Before loading the picture, always set the position of the MemoryStream to 0
Code: Pascal  [Select][+][-]
  1.   pic := TPicture.Create();
  2.   MemStream.Position := 0;
  3.   pic.LoadFromStream(MemStream);
  4.  
3. You forget to free pic

PS: I did some tests, you won't gain much speed. Loading a 100Mb bmp from file into a MemoryStream is almost instantaneous, assigning the stream to a picture is the bottleneck:
Code: Pascal  [Select][+][-]
  1.  MemStream := TMemoryStream.Create;
  2.  try
  3.    MemStream.LoadFromFile('C:\Temp\BMPTmp.bmp');
  4.    MemStream.Position := 0;
  5.    imgOriginal.Picture.LoadFromStream(MemStream); //<--this is the bottleneck
  6.  finally
  7.    MemStream.Free;
  8.  end;
             
So keep it simple, let gswin64c.exe create the bmp, then load with a memorystream.
« Last Edit: February 26, 2016, 09:00:31 am by GetMem »

danielthoss

  • New member
  • *
  • Posts: 6
@GetMem

Thx for your hints and time!!

I changed the execution of Ghostscript to TProcess and now it works  :)

GetMem

  • Hero Member
  • *****
  • Posts: 3757
@danielthoss

You're welcome!
Ok it works, but is it faster then loading directly from file?

danielthoss

  • New member
  • *
  • Posts: 6
@GetMem

Sorry for the long delay.

On my laptop it's not really faster - I think because of the build in SSD hard disk. On an old hard disk it whould be really faster and also if I increase the DPI value (GhostScript parameter -r) to 600 and higher to get a much better quality (resolution) of the image.

 

TinyPortal © 2005-2018