Google QR Codes and Zend_PDF

Now that we can generate QR codes using the Google Chart API, and we have a controller action that serves it up for us with the correct .PNG header (see my previous post), we need to get it where it’s of any use – on paper!

So following on from my last post about Zend_Pdf, we can now do the following to get it in our PDF:

//draw qr code
 $target = 'http://example.co.uk/download/qr?target=http://www.example.co.uk/properties/view/id/'.$prop->getID();
 $img = file_get_contents($target); 
 file_put_contents(APPLICATION_PATH.'/uploads/tmp/'.$prop->getID().'-qr.png',$img);
 $image = Zend_Pdf_Image::imageWithPath(APPLICATION_PATH.'/uploads/tmp/'.$prop->getID().'-qr.png');
 $page->drawImage($image, 13, 13, 94,94);
 unlink(APPLICATION_PATH.'/uploads/tmp/'.$prop->getPid().'-qr.png');

The first line of code is the url of our download controllers QR action.  file_get_contents stores the image in memory, which we then save into a temp folder using file_put_contents. These commands are the equivalent of a lot of fopen etc, so use them if you don’t already!  This example uses my property object to make a dynamic filename, but this isn’t necessary, but I don’t need to tell you that!

We then use Zend_Pdf to load our png, and when drawing an image, the numbers in the argument are x1, y1, x2, y2 being bottom left and top right corners respectively. Finally, ditch the temp file with the unlink command. Have Fun!

Advertisements

Generating QR Codes

If any of you have messed around with the google chart API you may or may not have come across an issue where the image returned from Google has no mime type.  Most browsers are smart enough to not care, but we would like our headers please where at all possible. So I made a QR action in my DownloadController:

public function qrAction()
{
   $target = $this->getRequest()->getParam('target');
   $saveas = $this->getRequest()->getParam('save');
   $dim = $this->getRequest()->getParam('dim');
   if(!isset($dim)){$dim = 200;}

   // disable view and layout 
   Zend_Layout::getMvcInstance()->disableLayout(); 
   $this->_helper->viewRenderer->setNoRender(); 
 
   $url = 'https://chart.googleapis.com/chart?';
   $params = array(
   'cht'=>'qr',
   'chs'=> $dim.'x'.$dim,
   'chl'=>$target
   );

   $url .= http_build_query($params);
   header ("Content-Type: image/png");
   if(isset($saveas))
   {
       header("Content-Disposition: attachment; filename=$saveas.png"); 
   }
   $content = file_get_contents($url); 
   echo $content;
}

As you can see, there are a couple of parameters that are accepted. Target is your target URL, and is the only required argument. A thing to note. Usually in Zend, we would usually access this like so:

http://example.com/download/qr/param/value/param/value

However as we are adding a url as a target the forward slashes mess this up, so we’ll use the old fashioned looking way of doing it:

http://example.com/download/qr?target=http://nasa.gov

This will give us our QR code with PNG headers as required 🙂 we can add dimensions to the url by adding:

http://example.com/download/qr?target=http://nasa.gov&dim=300

This will return a larger QR image. Finally, we can pass a filename to the saveas argument:

http://example.com/download/qr?target=http://nasa.gov&dim=300&saveas=nasa

which would bring up the save as dialog as nasa.png

Have Fun with your phone!

Disable Return key using jQuery – IE Bug

Once again IE stops a developer in his tracks and forces him to rewrite something that works perfectly in every other browser. This time – disabling Return using jQuery.

This works for everyone except Microsofts (quirky) browser. More enamel is lost in a year through developers teeth grinding that I’m sure dental profits are soaring through the roof. Anyway:

$(window).keyup(function(e) 
{    
    if (e.keyCode == 13)
    { 
       e.preventDefault();         
        return false;
    }
});

Whereas this will work

$('#my-form').bind("keypress", function (e) {
    if (e.keyCode == 13) 
    {
        e.preventDefault();
        return false;
    }
});

Irritating. Boycott IE!

Getting a Zend Framework site working with a standard PHP site

I have a standard php site in work, which suddenly was to be joined onto a zend site. Wtf. Anyway.

Create a file zend_init.php and require that in your header.

<?php
// Define path to application directory
defined('APPLICATION_PATH')
 || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application/'));
 //die(APPLICATION_PATH);
// Define application environment
defined('APPLICATION_ENV')
 || define('APPLICATION_ENV', 'development');
 
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
 realpath(APPLICATION_PATH . '/../library'),
 get_include_path(),
)));
/** Zend_Application */
require_once 'Zend/Application.php';
// Create application, bootstrap, and run
$application = new Zend_Application(
 APPLICATION_ENV,
 APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap();

That’s it! Just edit the path to find where your application folder is.

Now without having to do any require_once() or include() commands, we can simply just call a new object like you would in your mvc app!

Coda Slider and dynamic CSS

My shiny new website uses an awesome jquery tool called Coda Slider. It’s really good, but I kind of left the old fashioned people with a 1024×768 resolution behind, as my sites default width (and my slider image widths) are  1200px!

Anyway, I couldn’t have cared less, everyone uses a laptop or widescreen monitor anyway! Until that is the feedback started coming back. Looks like I need to fix this then :-s

First up, here’s the way the coda slider works:

<script src="/js/coda/jquery.coda-slider-3.0.min.js" type="text/javascript"></script>
 <link href="/js/coda/coda-slider.css" rel="stylesheet" />

..in the head, obviously. In your body, you set it out like this:

<div class="coda-slider-wrapper">
 <div class="coda-slider preload" id="coda-slider-1">
   <div class="panel">
     <div class="panel-wrapper">
       <img src="/img/coda/banner1.png" />
     </div>
   </div>
   <div class="panel imagepanel">
     <div class="panel-wrapper" style="text-align: center;">
       <img src="/img/coda/ddale.jpg" />
     </div>
     <div class="panel-wrapper" style="text-align: center;">
       <img src="/img/coda/let-your-property.png" />
     </div>
     <div class="panel-wrapper" style="text-align: center;">
       <img src="/img/coda/mobile.jpg" />
     </div>
   </div>
  </div>
 </div>

And in your <script> :

var slide = $('#coda-slider-1').codaSlider({
 autoHeight : false,
 autoSlide : true,
 dynamicArrows : false
 });

Now we have an awesome slider!

However, the guys with the 1024x768px monitors are crying in pain! So we can hack a quick fix for them:

if (screenwidth <= 1024) 
{
    $('div.panel-wrapper img').css('width','1000');
    $('.coda-slider-wrapper .coda-slider').css('width','1000');
    $('.coda-slider-wrapper .coda-slider').css('height','291');
    $('.coda-slider-wrapper .coda-slider .panel').css('width','1000');
}

That shrinks the image width and panel containers to fit 🙂 A bit hacky, but I still like it. Next time though, I’ll do my CSS in em’s and %ages!

 

Consuming Services with Zend Framework

Just when Zend couldn’t get any better, it already is!!

Today we’re farting about with RSS feeds and Twitter feeds! It’s remarkably simple! Lets get cracking:

In your controller:

    //look for Ron Paul news and twitter (Americans please vote this guy!!!)
    $q = 'Ron%20Paul';
    $this->view->q = $q;

    //get twitter feeds
    $twitter = new Zend_Service_Twitter_Search();
    $this->view->tweets = $twitter->search($q,array('lang' => 'en', 'rpp' => 8, 'show_user' => true));

    // get Google News Atom feed
    $this->view->feeds = array();
    $gnewsFeed = "http://news.google.com/news?hl=en&q=$q&output=atom";
    $this->view->feeds = Zend_Feed_Reader::import($gnewsFeed);

Thats it!!! In your view:

<?php 
  $count = 0;  
  foreach ($this->feeds as $entry)
  { 
?> 
  <p class="post">
    <span class="text"><a href="<?php echo $entry->getLink(); ?>"> <?php echo $entry->getTitle(); ?></a></span>
    <span class="time"><?php echo $entry->getDateModified(); ?></span>
  </p>
<?php 
    $count++; 
  }
?>

And for the tweets:

<?php foreach ($this->tweets['results'] as $tweet) {?>
<p class="tweet"> <span class="image">
<img src="<?php echo $tweet['profile_image_url']; ?>" /> </span>
<span class="user">
<?php echo $tweet['from_user'] . ': '; ?>
</span>
<span class="text">
<?php echo $tweet['text']; ?> </span>
<span class="time">
<?php echo $tweet['created_at']; ?>
</span>
</p>
<?php } ?>

Ok, I mean come on, how much easier does this stuff get?!?!?!? Rock on, amigos!

Zend_PDF with templates

Zend_PDF rocks! It makes drawing a PDF almost child’s play. The main time eater though is the trial and error with the co-ordinates. So I stopped all this nonsense, and used Illustrator.

In adobe Illustrator, set the ruler origin to the bottom left of the page. Now in the transform pallette you can get the XY co-ordinates! Once you have created a mock up of the PDF, save a copy minus all the dynamic stuff, which we’ll drop in from our controller action.

First up, ditch the view and layout:

//disable the layout
 Zend_Layout::getMvcInstance()->disableLayout(); 
 $this->_helper->viewRenderer->setNoRender();

Next we create the Zend_PDF and templates, and set default fonts and colours:

//create a zend pdf
 $template = Zend_Pdf::load(APPLICATION_PATH.'/layouts/pdfs/proplist.pdf');
 $this->_pdf = new Zend_Pdf();
 $extractor = new Zend_Pdf_Resource_Extractor();
//set font
 $font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA);
 $bold = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA_BOLD);
//set colors
 $black = new Zend_Pdf_Color_Html('#000000');
 $red = new Zend_Pdf_Color_Html('#ED1F24');
 $green = new Zend_Pdf_Color_Html('#009444');
 $blue = new Zend_Pdf_Color_Html('#0069B5');

We will have so many records per page, so the next bit goes in a for loop:

//add page
 $page = $extractor->clonePage($template->pages[0]);

 //get properties for this page
 $props = $this->_helper->paginator($rows,6);

We can set colours and fonts like this:

$page->setFillColor($black);
$page->setFont($bold, 11);

Add the page to the pdf like this

$pdf->pages[] = $page;

I created a function for getting the coordinates of each element, which adjusts depending which record on the page we are dealing with. This could be tweaked to suit your own project.

private function getCoordinates($row, $elem)
 {
 $xCoords = array(
 'header' => 21,
 'avail_date' => 419,
 'price' => 541,
 'summary' => 21,
 'hol_dep' => 70,
 'dep_date' => 128,
 'board_up' => 217,
 'keys' => 267,
 'tax_band' => 333,
 'on_web' => 388,
 'ref' => 437,
 'images' => 560,
 'condition' => 59,
 'move_date' => 130,
 'gas_safety' => 221 ,
 'hwsa' => 271 ,
 'let_fee' => 327,
 'man_fee' => 391,
 'landlord' => 452,
 'lha' => 551,
 'comment' => 64
 );

 $yCoords = array(
 'header' => 759,
 'avail_date' => 759,
 'price' => 759,
 'summary' => 739,
 'hol_dep' => 692,
 'dep_date' => 692,
 'board_up' => 692,
 'keys' => 692,
 'tax_band' => 692,
 'on_web' => 692,
 'ref' => 692,
 'images' => 692,
 'condition' => 680,
 'move_date' => 680,
 'gas_safety' => 680,
 'hwsa' => 680,
 'let_fee' => 680,
 'man_fee' => 680,
 'landlord' => 680,
 'lha' => 680,
 'comment' => 667
 );

 $z = array();
 $z['x'] = $xCoords[$elem];

 switch($row)
 {
 case 1:
 default:
 $z['y'] = $yCoords[$elem];
 break;
 case 2:
 $z['y'] = $yCoords[$elem] - 127.5;
 break;
 case 3:
 $z['y'] = $yCoords[$elem] - (127.5 * 2);
 break;
 case 4:
 $z['y'] = $yCoords[$elem] - (127.5 * 3);
 break;
 case 5:
 $z['y'] = $yCoords[$elem] - (127.5 * 4);
 break;
 case 6:
 $z['y'] = $yCoords[$elem] - (127.5 * 5);
 break;
 }
 return $z;
 }

Finally we can draw the text like this:

$text = '?';
 $page->setFillColor($blue);
 $z = $this->getCoordinates($row, 'condition');
 $page->drawText($text, $z['x'], $z['y'],'UTF-8');

Other things include fitting text to a specific width, or making a paragraph of multi lines:

private function widthForStringUsingFontSize($string, $font, $fontSize)
 {
 $drawingString = iconv('UTF-8', 'UTF-16BE//IGNORE', $string);
 //$drawingString = $string;
 $characters = array();
 for ($i = 0; $i < strlen($drawingString); $i++) {
 $characters[] = (ord($drawingString[$i++]) << 8 ) | ord($drawingString[$i]);
 }
 $glyphs = $font->glyphNumbersForCharacters($characters);
 $widths = $font->widthsForGlyphs($glyphs);
 $stringWidth = (array_sum($widths) / $font->getUnitsPerEm()) * $fontSize;
 return $stringWidth;
 }
private function getFontSizeForWidth($text,$font,$desiredSize,$maxWidth)
 {
 do 
 {
 $stringWidth = $this->widthForStringUsingFontSize($text, $font, $desiredSize);
 $desiredSize = $desiredSize - 0.5; 
 }
 while($stringWidth > $maxWidth);
 return $desiredSize + 0.5;
 }
private function getWrappedText($string, $font, $fontsize,$max_width)
 {
 $wrappedText = '' ;
 $lines = explode("\n",$string) ;
 foreach($lines as $line) {
 $words = explode(' ',$line) ;
 $word_count = count($words) ;
 $i = 0 ;
 $wrappedLine = '' ;
 while($i < $word_count)
 {
 /* if adding a new word isn't wider than $max_width,
 we add the word */
 if($this->widthForStringUsingFontSize($wrappedLine.' '.$words[$i]
 ,$font
 , $fontsize) < $max_width) {
 if(!empty($wrappedLine)) {
 $wrappedLine .= ' ' ;
 }
 $wrappedLine .= $words[$i] ;
 } else {
 $wrappedText .= $wrappedLine."\n" ;
 $wrappedLine = $words[$i] ;
 }
 $i++ ;
 }
 $wrappedText .= $wrappedLine."\n" ;
 }
 return $wrappedText ;
 }

To align text to the right, we subtract the width:

$width = $this->widthForStringUsingFontSize($text, $bold, 12);
 $z = $this->getCoordinates($row, 'price');
 $z['x'] = $z['x'] - $width;

And if we need wrapped text:

$text = $this->getWrappedText($text, $font,10, 550);

Awesome! Another what used to be pain in the arse task is now a piece of cake (or zf rather lol)!