I have a Windows Server 2003 box running Apache 2.2.11, PHP 5.2.9, and Drupal 6.13. I'm using Printer, e-mail and PDF version 6.x-1.10 with wkhtmltopdf 0.8.3.

When I click the PDF icon, nothing appears to happen. I've determined that a valid PDF is created in C:\Windows\Temp, but not deleted, and not streamed to the browser.

If I modify print_pdf.pages.inc after:

>
    if (stristr(PHP_OS, 'Win')) {
      // It seems Windows can't use the pipe properly so get the contents from a file and then delete it.

by adding the following line (sans php start and closing naturally):

    sleep(1);

I am prompted to download the streamed PDF and the PDF is deleted from TEMP.

Using sleep() in this way is unreliable and cludgy, so I'm not suggesting it, but I don't have a genuine fix to propose at this time. It appears that wkhtmltopdf has a lock on the file or that the file doesn't exist yet when the app gets to "$pdf = file_get_contents($pdf_output);" (around line 315).

When I var_dump($pdf) after file_get_contents($pdf_output), var_dump($pdf) returns a null string unless I call to sleep() for a second or two first. This indicates to me that "$pdf = file_get_contents($pdf_output);" is happening too soon.

Thanks for a great module,
Mike Hays

Comments

jcnventura’s picture

Status: Active » Postponed (maintainer needs more info)

I take it that you have corrected the 1.10 typo first mentioned in #521776: Windows wkhtmltopdf support, as your description of line 315 doesn't include it.

If the return is a null string, it means that it managed to open the file, but but failed in reading it. Can you make sure that the file exists by doing a var_dump(file_exists($pdf_output)) before the file_get_contents call ?

I guess the way to solve this would be to not use file_get_contents but to replace it with:

$fh = fopen($pdf_output, 'r');
flock($fh, LOCK_EX)
$pdf = fread($fh, filesize($pdf_output));
fclose($fh);

Can you also test the above code and tell me if it works for you?

João

dogbertdp’s picture

var_dump(file_exists($pdf_output)) returnes TRUE. But replacing file_get_contents with the code above returns:

warning: fread() [function.fread]: Length parameter must be greater than 0 in C:\xampp\htdocs\sites\all\modules\print\print_pdf\print_pdf.pages.inc on line 319.

The file is there, but is zero bytes when we get to it here. Slowing it down (by using sleep()) makes things work (albeit in a cludgy way), by giving the data time to populate the file. This applies to both file_get_contents and fopen/flock/fread/fclose.

I confirmed this by doing "var_dump(filesize($pdf_output));" before the fopen. Without sleep it returns "int(0)" with sleep it returns "int(36323)" for my sample node.

Thanks,
Mike Hays

jcnventura’s picture

Status: Postponed (maintainer needs more info) » Active

Well, if Windows needs to sleep, then sleep it shall.

dogbertdp’s picture

Here is how I'm dealing with this issue:

    if (stristr(PHP_OS, 'Win')) {
      //It seems Windows can't use the pipe properly so get the contents from a file and then delete it.
 
      //Make sure wkhtmltopdf is done writing the temporary PDF.
      $file_size_stable = FALSE;
      $file_size_then = -1;
      while (!$file_size_stable)
      {
        $file_size_now = filesize($pdf_output);
        if ($file_size_then < $file_size_now) {
          $file_size_then = $file_size_now;
          sleep(1);
        } else {
          $file_size_stable = TRUE;
        }
      }
      $pdf = file_get_contents($pdf_output);  // Place the contents of the temporary PDF in a variable.
      unlink($pdf_output);  // Delete the temporary PDF.
    }

This works on my site. It generated a valid PDF for a rather large node, as wkhtmltopdf finished before file_get_contents fired. Checking for a stable filesize isn't my preferred approach, but since we don't control wkhtmltopdf it seems to be a good compromise without introducing an arbitrary sleep time that will be too long for short nodes, and potentially too short for long nodes. Also I don't think an infinite loop can occur in my while statement.

Thoughts?

Mike Hays

ghede’s picture

@jcnventura: I can confirm that this works with my site also, but I must disable 'send directly to printer', as you had asked me to do in the other post #521776: Windows wkhtmltopdf support

@dogbertdp: Just wanted to say thanks, and nice work. Makes me wish I could code.

Jim

dogbertdp’s picture

StatusFileSize
new860 bytes

I'm doing some general clean-up on my site and I realized I hadn't rolled a patch for the fix offered in #4. It's attached if you're interested. It's against 6.x-dev (which has the correction to the "$pdf_outputg" typo for anyone else reading this post).

Mike Hays

jcnventura’s picture

Status: Active » Needs review
wvd_vegt’s picture

Hi,

I tried the above patch but with not much luck. The files stayed 0 bytes. So i changed the code slightly so it is now a bit different for windows (but works).

I replaced the $cmd= line with

  //veg: added
  $htm_output = (stristr(PHP_OS, 'Win')) ? tempnam(file_directory_temp(), 'htm') : '-';
  if (stristr(PHP_OS, 'Win')) {
    $fh = fopen($htm_output, 'w') or die("can't open file");
    fwrite($fh, $html);
    fflush($fh);
    fclose($fh);

    //veg: changed,  inserted < %$htm_output
    $cmd = realpath($print_pdf_pdf_tool) ." --page-size $print_pdf_paper_size --orientation $print_pdf_page_orientation --dpi $dpi $print_pdf_wkhtmltopdf_options - $pdf_output < $htm_output";
  } else {
    $cmd = realpath($print_pdf_pdf_tool) ." --page-size $print_pdf_paper_size --orientation $print_pdf_page_orientation --dpi $dpi $print_pdf_wkhtmltopdf_options - $pdf_output";
  }

So windows now used a temp htm file on disk and uses a piping symbol ('<') instead of the pipes directly. And start with a sleep before requesting the filesize(). In my case I had a couple of time that the filesize kept returning 0 when the file already had size (must the the error filesize can return).

Next I untwined the *nix/windows code:

  if (is_resource($process)) {
    //veg: moved to else clause..
    //fwrite($pipes[0], $html);
    //fclose($pipes[0]);

    if (stristr(PHP_OS, 'Win')) {
      // It seems Windows can't use the pipe properly so get the contents from a file and then delete it.
      //$pdf = file_get_contents($pdf_output);

      //veg: Added
      //Make sure wkhtmltopdf is done writing the temporary PDF.      
      
      $file_size_stable = FALSE;
      $file_size_then = -1;
     while (!$file_size_stable && file_exists($pdf_output))
      {
        sleep(1);
        
        $file_size_now = filesize($pdf_output);
        if ($file_size_then < $file_size_now || $file_size_now == 0) {
  	  $file_size_then = $file_size_now;
 	  //sleep(1);
        } else {
	  $file_size_stable = TRUE;
        }
      }      
      $pdf = file_get_contents($pdf_output);

      //veg: added	      
      unlink($pdf_output);
      unlink($htm_output);
    }
    else {
      //veg: moved from above
      fwrite($pipes[0], $html);
      fclose($pipes[0]);
      
      $pdf = stream_get_contents($pipes[1]);
    }
    fclose($pipes[1]);
Mikeq’s picture

I have recently managed to get pdf printing working on Windows, using wkhtmltopdf. In case it helps anyone else, here is what I did.

Using print module 6.x-1.x-dev (2010-Jan-12), Drupal 6.12, Windows server 2003 with Apache and php 5.

download wkhtmltopdf 0.9.5 and install to modules/print/lib. Note that this version of wkhtmltopdf corrects the bug which resulted in no output to stdout, and required much of the OS specific patching above.

Remove Win OS specific conditional processing in print.pdf.pages.inc - $pdf is populated from the pipe regardless of OS, and there is no need for a temporary pdf file.

Remove the --dpi $dpi command line option from the $cmd used to invoke wkhtmltopdf in print.pdf.pages.inc - this does not seem to be a valid option in this version and causes an acrobat "wrong operand type" error and a blank pdf file to be generated.

No temp files, no sleeping, no file size checks, no OS dependencies. Hope this helps.

verta’s picture

Version: 6.x-1.x-dev » 6.x-1.10

I really wanted wkhtmltopdf to work, but I had to give up. I have the latest, and I even tried the tweaks in #9. What happens is, nothing happens; the target page reloads, I get a zero byte file in the server's windows\temp folder and no PDF. It feels like a problem with a parameter, but I just don't have time to figure out what the parameters ARE that the module is sending it that aren't making it happy. If I could figure that out, I could run that command on the server and see what happens (perhaps the stream is not getting closed with control-Z?).

TCPDF worked the first time, Windows server and all.

smk-ka’s picture

Version: 6.x-1.10 » 6.x-1.x-dev
StatusFileSize
new1.57 KB

A patch for what Mikeq said: when using wkhtmltopdf 0.9.5 it'll work right out of the box, no workarounds required for Windows anymore.

verta’s picture

Version: 6.x-1.10 » 6.x-1.x-dev

Patch totally works, thanks!

I am noticing that the output of wkhtmltopdf is fairly different from that of tcpdf on printing the same node. Different enough to possibly need to tweak settings, and I'm actually leaning toward tcpdf as the more attractive of the two, just out of the box.

I have the logo print turned on, and the logo is too small with wkhtmltopdf. Tcpdf adds a nice footer rule line and prints the breadcrumb, although it uses a smaller font and overrides the link color to match the body text color instead of leaving them blue.

rconstantine’s picture

I confirm that the above worked. For those using IIS (not sure about Apache on Win), you may also have to do the following:

1) Browse to where cmd.exe is and grant execute permission to the anonymous user that the web site uses to run things. This eliminates a proc_open error.

2) Not sure if I needed to do this or not, but granted the anonymous user read/write access to C:\windows\temp.

3) Because the wkhtmltopdf is an executable, make sure your site in IIS is set to run both scripts and executables.

I think that is it. The PDF generated looks the same as the printer friendly version. I would imagine that is what it is supposed to look like unless I create different tpl files, right?

verta’s picture

Can someone else please test and hopefully confirm the patch so this can go to "reviewed and tested" and possibly rolled out? That would be cool, thanks.

verta’s picture

I should mention, I was having this problem on a system that did not have wkhtmltopdf, it is using TCPDF, and the patch fixed it right up.

dsy73’s picture

The patch #11 works for me. Thanks.
-Windows 2008 (x86)
-Apache
-wkhtmltopdf 0.9.9

jcnventura’s picture

Status: Needs review » Fixed

I've just committed #11 to CVS.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

WorldFallz’s picture

Status: Closed (fixed) » Active

I seem to be having this problem with the following:

-Windows server 2003 sp2
-Apache/2.2.11
-PHP 5.2.14
-Drupal 6.20
-Print 6.x-1.12
-wkhtmltopdf 0.9.9

wkhtlmltopdf works fine from the command line, there's no watchdog messages, no php log messages, and no apache log messages. Both tcpdf and dompdf work fine (though their output is unsatisfactory).

The browser just spins and spins and never comes back so I'm at a loss as to how to continue troubleshooting-- any suggestions would be greatly appreciated.

verta’s picture

Check for NTFS permissions on the Windows temp folder. Try granting rights for your web server's anonymous user, IUSR_SERVERNAME or the account that the PHP ISAPI application pool is using, possibly IWAM_SERVERNAME.

Look for temp files with zero byte sizes and time stamps that correspond to your print attempts, I had to search the whole server disk for files modified within the past day and sort the result set by date modified to see where it was trying to work.

WorldFallz’s picture

Using 'die' i've narrowed it down to it definitely failing on the line 351 of print_pdf.pages.inc fwrite. But i'm using apache and there is no IUSR_SERVERNAME or IWAM_SERVERNAME and I can't find any 0 byte files either (plus tcpdf and dompdf work fine with the current permissions).

WorldFallz’s picture

ok, after troubleshooting this for hours, I stumbled across the problem-- it doesn't like the "--disable-local-file-access" option-- if I remove that everything works as expected. I couldn't find any documentation about that option so I don't know what it does or what the ramifications of removing it are.

any one know what that option is?

WorldFallz’s picture

and i just confirmed the same problem with print 6.x-1.x-dev -- and commenting out the "--disable-local-file-access" line also fixes it.

verta’s picture

Thanks for the correction, I think Apache runs as 'system' so that's not it.

Possibly related: http://drupal.org/node/892920#comments

If I am reading it correctly, and this is not a given, the gist is, with the latest patch to this module which was for security, if you keep the directive (un-mod the module code), you would now also need to allow anonymous users to access uploaded files in permission administration, since the process making the PDF is accessing the site over http as an anonymous user. Possibly only relevant for private file systems. I have not tested any of this, though.

WorldFallz’s picture

@verta -- thanks for all your help. For some reason I didn't come across that link or the security advisory when I googled '--disable-local-file-access'.

In any case, my site is an intranet site behind a firewall and no one but admins have access to unfiltered html. So, if I understand the issue with "--disable-local-file-access", I should be ok. Besides, if there is a hacking problem in that scenario I have more serious problems than wkhtmltopdf security, lol.

as a side note to anyone who stumbles across this issue-- wkhtmltopdf is by far the best pdf renderer choice. It solved all my output problems and blows tcpdf and dompdf away. well worth the time I spent troubleshooting to get it working.

WorldFallz’s picture

Status: Active » Closed (works as designed)

guess we can consider this closed again.

verta’s picture

I totally agree, way superior output. I'll revisit it, now that this is closed with a workaround.