Issues

ZF-33: Cannot reliably draw on loaded documents containing rotated pages

Description

If an existing PDF document is loaded which contains a page that is rotated, drawing pretty much doesn't work as the end-user would expect. The original report:

bq. drawText renders text rotated 90 degrees counter-clockwise when using a 5.5(w) x 8.5(h) in. document. You can see an example of this at http://dfinc.net/pdf/nstv.pdf .

The problem Aaron is experiencing is that the page he's trying to draw to has been rotated 270 degrees and cropped in the page dictionary:

10 0 obj<[17 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R]
/Type/Page/Parent 4 0 R/Rotate 270/MediaBox[0 0 612 792]
/CropBox[0.0 396.0 612.0 792.0]/Resources 11 0 R>>
endobj

Zend_Pdf currently does not handle page rotation, so doesn't do anything special with the rotation entry in the page's dictionary.

The issue is that even though the page is "rotated", it is only rotated at display time; the page's coordinate system is unchanged. This is why the $line_1 y coordinate in the example below must be at just over 9 inches to appear on the page.

The illustration zf33illustration.png attached to this issue shows the actual drawing coordinate system for this page:

!zf33illustration.png|thumbnail!

As a workaround, the user can rotate the drawing coordinate system with {{page->rotate($x, $y, $degrees)}} and then adjust the {{$x}} and {{$y}} coordinates appropriately, but this is a pain.

I'm not sure what we should actually do here. It would be convenient for the end-user if we 'automagically' rotated and translated the coordinate system if a rotated page is loaded, but there may be cases where such behavior is not desired.

Comments

This is the example of the issue. All that was used to generate this is the $page->drawText method.

This isn't a problem with drawing the text. The following example works as expected (result attached as reduction1.pdf):


$pdf = new Zend_Pdf();

// 5.5x8.5in document. the extra colon needed in the page definition appears to be a bug...
$pdf->pages[] = ($page = $pdf->newPage('396:612:'));

$page->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_TIMES), 18);
$page->drawText('This is a test', 72, 300);

$pdf->save('/path/to/reduction1.pdf');

The problem is not that the PDF document uses a custom size, it's that the page that is being drawn to is rotated:

10 0 obj 
<[17 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 27 0 R ] /Type /Page /Parent 4 0 R /Rotate 270 /MediaBox [0 0 612 792 ] 
/CropBox [0 396 612 792 ] /Resources 11 0 R >>
endobj

The text appears to be drawing on the page in its original (non-rotated) orientation. Looking to see what needs to be fixed...

In the interim, can you post the actual code you're using to generate this document (as with the sample above)?

Here is the code that is used to generate the pdf. This is extracted from a method in a controller for a MCV app. The view just sets the content type and echoes $data.


        $data = $this->Sale->read();
        
        $pdf = Zend_Pdf::load(ROOT.DS.'pdfs'.DS.'6890.pdf');
        
        $page = $pdf->pages[0];
        $page->setFont(new Zend_Pdf_Font_Standard(Zend_Pdf_Const::FONT_HELVETICA), 12);
        $margin_left = 50;
        
        // Line #1
        $line_1 = 650;
        // Plate Number
        $page->drawText($data['Inventory']['license_number'], $margin_left + 5, $line_1);
        
        $this->set('data', $pdf->render());

Actually I forgot a line of code. The pdf that is imported is two pages so I use ``` to take off the extra page. Not sure if that helps but I thought I'd put it in there.

This is the source template that is used in my code.

Thanks for the code. I'm trying to reproduce your issue here, but Zend_Pdf is throwing an exception when trying to read the nstv.pdf file. (That's another bug.) Can you attach the original 6890.pdf file?

LOL - you beat me to the punch. Taking a look now...

Attached illustration showing the actual drawing coordinate system

Changed title of issue and revised description to reflect root of problem. We'll need to hash out a solution here--it's not going to be a trivial change.

Can you give an example of how you could use $page->rotate($x, $y, $degrees) to fix the text?

You need to do two things:

First, you need to rotate the coordinate system. {{$x}} and {{$y}} specify the rotation point. Positive numbers for {{$degrees}} rotate counter-clockwise; negative numbers rotate clockwise. I find it easiest to rotate about the origin (imagine rotating the a sheet of paper with the lower-left corner pinned). So you'd need something like this: {{$page->rotate(0, 0, deg2rad(-90));}}

Then, you need to adjust the {{$x}} and {{$y}} coordinates for your drawing commands. Right now, you'll have to do this manually. (In addition to {{$page->rotate()}}, we really need {{$page->translate()}} so it becomes automatic. See ZF-64.) If you rotated about the origin, you probably only need an offset for your {{$x}} coordinates: {{$xOffset = -792}}

Note that the rotation origin and the specific offsets you use will be entirely dependent on the page you're working with. For your specific example, the page was rotated 270 degrees clockwise and the bottom half was cropped. Other documents will be different. Trial-and-error and a little bit of sleuthing inside the PDF file itself will help you find the right values to use.

Here's a full example using your document:


$pdf = Zend_Pdf::load('/path/to/6890.pdf');

$page = $pdf->pages[0];
$page->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_TIMES), 18);

// Rotate the coordinate system 90 degrees clockwise
$page->rotate(0, 0, deg2rad(-90));

// Calculate the x and y offsets to "shift the origin."
$xOffset = -792;
$yOffset = 0;

// Make the text blue (so you can see it; I find it easier to adjust positioning this way).
$page->setFillColor(new Zend_Pdf_Color_HTML('blue'));

$page->drawText('This is a test', (24 + $xOffset), (418 + $yOffset));

$pdf->save('/path/to/result.pdf');

Positioned out a few releases, if timing isn't right please set fix version to something more appropriate.

Changing fix version to 0.6.0.

Zend_Pdf needs clear API for rotating.

There are two different rotating types: a) page presentation rotating (/Rotate PDF page property is intended for this and must be multiple of 90) b) drawing coordinate system rotation (also needs translate() implementation)

That's a question, should we automatically translate drawing coordinate system if page presentation is rotated???

It needs to be discussed.

Postponed to post-1.0 period

This doesn't appear to have been fixed in 1.5.0. Please update if this is not correct.

It's clear now how to implement this feature, but it breaks backward compatibility at drawing behavior level. Automatic rotation and shifting coordinate system affects drawing behavior.

So it may come only with ZF 1.8 or 2.0 and must be described in the release notes.