{"id":2388,"date":"2012-06-13T07:56:36","date_gmt":"2012-06-13T13:56:36","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=2388"},"modified":"2012-06-13T07:59:03","modified_gmt":"2012-06-13T13:59:03","slug":"changing-images-in-a-pdf","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2012\/06\/13\/changing-images-in-a-pdf\/","title":{"rendered":"Changing Images in a PDF"},"content":{"rendered":"<p>The <a href=\"http:\/\/bililite.com\/blog\/2012\/06\/06\/adding-javascript-to-pdf-files\/\" title=\"Adding Javascript to PDF Files\">PHP routines to fill in PDF forms<\/a> work great, and now my partner wants to use them too. Changing the forms to make the physician name a fill-in field rather than fixed text is easy, but what to do about the signature? It's not like a check; a pixelated image would be fine (I'm not worried about someone forging a preschool physical exam note). But it's not that easy to insert an image into an existing PDF file. I don't want to parse the entire PDF and rewrite it.<\/p>\r\n<p>Fortunately, <a href=\"http:\/\/bililite.com\/blog\/2011\/04\/01\/creating-pdfs-with-php-part-4\/\" title=\"Creating PDFs with PHP, part 4: Images\">images are stored as individual objects in the PDF file<\/a>, so if there is an image I can identify, I can easily replace it with one of my own choosing. The placement and size of the image on the page is part of the page description, so the new image will be in exactly the same place as the old.<\/p>\r\n<!--more-->\r\n<p>So, for example, I grab a blank check image off the web:<\/p>\r\n<p><a href=\"\/blog\/blogfiles\/pdf\/sig\/blank-check.jpg\"><img decoding=\"async\" src=\"\/blog\/blogfiles\/pdf\/sig\/blank-check.jpg\" alt=\"Blank Check\"\/><\/a><\/p>\r\n<p>And I create a <a href=\"\/blog\/blogfiles\/pdf\/sig\/1px.png\">1 pixel white png image<\/a> to insert into the signature line. Then I use <a href=\"http:\/\/pdfescape.com\">PDFEscape<\/a> to create a <a href=\"\/blog\/blogfiles\/pdf\/sig\/blank check.pdf\">PDF with those images (scaled to the appropriate size)<\/a> and some useful text.<\/p>\r\n<p>Now the plan is to use PHP to replace the white box with a signature image. The problem is programmatically figuring out where in the file is the image.<\/p> \r\n<p>I don't want to have to parse the whole file, so I use a RegExp to look for a stream object with a width of 1, and assume that it is the image I want to replace. Depending on the PDF, it may be necessary to use a more sophisicated test.<\/p>\r\n<p>So to replace the image, I open the PDF file, create <a href=\"http:\/\/bililite.com\/blog\/2011\/04\/01\/creating-pdfs-with-php-part-4\/\" title=\"Creating PDFs with PHP, part 4: Images\">a PDF image object<\/a> from the replacement image, and use <code>str_replace<\/code> to replace it. Simple!.\r\n<pre><code class=\"language-php\">$images = array(\r\n\t'Bill-Atkinson',\r\n\t'Andy-Hertzfeld',\r\n\t'Steve-Jobs',\r\n\t'Jef-Raskin',\r\n\t'Steve-Wozniack'\r\n);\r\n$image = @imagecreatefrompng(__DIR__.'\/'.$images[$_GET['which']].'.png');\r\n$file = file_get_contents(__DIR__.'\/blank check.pdf');\r\n$blank = findBlankImage($file);\r\n\r\nif ($blank !== FALSE && $image !== FALSE){\r\n\t$sig = streamfromimage($image);\r\n\t$file = str_replace ($blank, $sig, $file);\r\n}\r\nheader('Content-Type: application\/pdf');\r\n\/\/header('Content-disposition: attachment; filename=\"big check.pdf\"'); \/\/ Uncomment this to force downloading rather than viewing in browser\r\ndie ($file);\r\n\r\nfunction findBlankImage ($file){\r\n\t\/\/ find a stream with \/Width 1 (not a very sensitive search, but it could be made better)\r\n\t\/\/ Ugly regexp; match '&lt;&lt; \/Width 1 &gt;&gt; stream {stuff} endstream'. Suffix means ungreedy, dot matches newline\r\n\t$result = preg_match ('#(&lt;&lt;[^&gt;]*\/Width\\s+1\\b[^&gt;]*&gt;&gt;\\s*stream\\b.*\\bendstream\\b)#Us', $file, $matches);\r\n\tif ($result == 0) return FALSE;\r\n\treturn $matches[1];\r\n}\r\n\r\nfunction streamfromimage($im){\r\n\t\/\/ from http:\/\/bililite.com\/blog\/2011\/04\/01\/creating-pdfs-with-php-part-4\/\r\n\t$h = imagesy($im);\r\n\t$w = imagesx($im);\r\n\t$stream = '';\r\n\tfor ($row = 0; $row &lt; $h; ++$row) for ($col = 0; $col &lt; $w; ++$col){\r\n\t\t$colorindex = imagecolorat($im, $col, $row);\r\n\t\t$colors = imagecolorsforindex($im, $colorindex);\r\n\t\t$stream .= sprintf('%c%c%c', $colors['red'], $colors['green'], $colors['blue']);\r\n\t}\r\n\t$length = strlen($stream);\r\n\treturn \"&lt;&lt;\/Subtype \/Image \/Width $w \/Height $h \/ColorSpace \/DeviceRGB \/BitsPerComponent 8\r\n\t\/Length $length &gt;&gt; stream\r\n\t$stream\r\n\tendstream\\n\";\r\n}<\/code><\/pre>\r\n<p>For the check example, I need some good signatures. The original Macintosh team <a href=\"http:\/\/www.folklore.org\/StoryView.py?project=Macintosh&story=Signing_Party.txt\">signed the inside of the case<\/a>. We can grab a few famous signatures from there.<\/p>\r\n<form method=\"GET\" action=\"\/blog\/blogfiles\/pdf\/sig\/sigswap.php\">\r\n<label><input type=\"radio\" name=\"which\" value=\"0\" checked=\"checked\"\/> <img decoding=\"async\" src=\"\/blog\/blogfiles\/pdf\/sig\/Bill-Atkinson.png\" style=\"height: 2em;\" alt=\"Bill Atkinson\" title=\"Bill Atkinson\"\/><\/label><br\/>\r\n<label><input type=\"radio\" name=\"which\" value=\"1\"\/> <img decoding=\"async\" src=\"\/blog\/blogfiles\/pdf\/sig\/Andy-Hertzfeld.png\" style=\"height: 2em;\" alt=\"Andy Hertzfeld\" title=\"Andy Hertzfeld\"\/><\/label><br\/>\r\n<label><input type=\"radio\" name=\"which\" value=\"2\"\/> <img decoding=\"async\" src=\"\/blog\/blogfiles\/pdf\/sig\/Steve-Jobs.png\" style=\"height: 2em;\" alt=\"Steve Jobs\" title=\"Steve Jobs\"\/><\/label><br\/>\r\n<label><input type=\"radio\" name=\"which\" value=\"3\"\/> <img decoding=\"async\" src=\"\/blog\/blogfiles\/pdf\/sig\/Jef-Raskin.png\" style=\"height: 2em;\" alt=\"Jef Raskin\" title=\"Jef Raskin\"\/><\/label><br\/>\r\n<label><input type=\"radio\" name=\"which\" value=\"4\"\/> <img decoding=\"async\" src=\"\/blog\/blogfiles\/pdf\/sig\/Steve-Wozniack.png\" style=\"height: 2em;\" alt=\"Steve Wozniack\" title=\"Steve Wozniack\"\/><\/label><br\/>\r\n<input type=\"submit\" value=\"Generate Check\"\/>\r\n<\/form>","protected":false},"excerpt":{"rendered":"The PHP routines to fill in PDF forms work great, and now my partner wants to use them too. Changing the forms to make the physician name a fill-in field rather than fixed text is easy, but what to do about the signature? It's not like a check; a pixelated image would be fine (I'm [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15,9],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2388"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=2388"}],"version-history":[{"count":23,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2388\/revisions"}],"predecessor-version":[{"id":2413,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/2388\/revisions\/2413"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=2388"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=2388"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=2388"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}