1<?php 2/** 3 * Rubify action plugin (post processing) 4 * 5 * @license GPLv3 (http://www.gnu.org/licenses/gpl.html) 6 * @link http://www.dokuwiki.org/plugin:dpicorrect 7 * @author Mike "Pomax" Kamermans <pomax@nihongoresources.com> 8 */ 9 10if(!defined('DOKU_INC')) die(); 11if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 12require_once(DOKU_PLUGIN.'action.php'); 13 14class action_plugin_dpicorrect extends DokuWiki_Action_Plugin { 15 16 var $NO_RESOLUTION = -1; 17 18 function getInfo() { 19 return array( 20 'author' => 'Mike "Pomax" Kamermans', 21 'email' => 'pomax@nihongoresources.com', 22 'date' => '2010-09-16', 23 'name' => 'DPI correct', 24 'desc' => 'Corrects image size based on their resolution (dpi), giving them CSS width and height values in inches, instead of pixels', 25 'url' => 'http://www.dokuwiki.org/plugin:dpicorrect'); 26 } 27 28 /** 29 * Postprocesses the html that was built from that, to rubify kanji that have associated furigana. 30 */ 31 function register(&$controller) 32 { 33 $controller->register_hook('IMAGE_LINK_POSTPROCESS', 'AFTER', $this, '_dpicorrect'); 34 } 35 36 /** 37 * check if an image was a .jpg or .png image, because these can have custom dpi values. 38 */ 39 function _dpicorrect(&$event, $param) 40 { 41 // reference to data and associated data type 42 $img = $event->data; 43 44 // get image link. We only care about JPG and PNG, because GIF images are an on-screen format, and inherit the screen's dpi 45 preg_match("/media=([^\"]+\.(jpg|png))\"/",$img,$matches); 46 $link = str_replace(":","/",$matches[1]); 47 48 $file = 'data/media/'.$link; 49 50 // Not unimportant: only run if the link is valid. 51 if(file_exists($file)) 52 { 53 if(strpos($link,".jpg")!==false) { 54 $jpg = ImageCreateFromJPEG($file); 55 $imgdpi = $this->get_dpi_for_jpeg($file); 56 // if there is no explicit dpi value, leave this image alone 57 if($imgdpi == $this->NO_RESOLUTION) { return; } 58 // if it's not, compute the corrected image dimensions 59 $width = ImageSX($jpg) / $imgdpi["x"]; 60 $height = ImageSY($jpg) / $imgdpi["y"]; } 61 62 else if (strpos($link,".png")!==false) { 63 $png = imagecreatefrompng($file); 64 $imgdpi = $this->get_dpi_for_png($file); 65 // if there is no explicit dpi value, leave this image alone 66 if($imgdpi == $this->NO_RESOLUTION) { return; } 67 // if it's not, compute the corrected image dimensions 68 $width = ImageSX($png) / $imgdpi["x"]; 69 $height = ImageSY($png) / $imgdpi["y"]; } 70 71 // retrofit the dimensions as explicit width/height attributes now 72 $event->data = str_replace('class="media"','class="media" style="width: '.$width.'in; height: '.$height.'in;"',$img); 73 } 74 } 75 76 /** 77 * Mostly from http://www.w3.org/Graphics/JPEG/jfif3.pdf, with with a quick peek 78 * for the JFIF mark, because it's not guaranteeds to be at byte position 2! 79 */ 80 function get_dpi_for_jpeg($filename) 81 { 82 // open the file and read first 20 bytes. 83 $a = fopen($filename,'r'); 84 $string = fread($a,20); 85 fclose($a); 86 $pos = strpos($string, "JFIF"); 87 // if the image claims it's jpg but has no JFIF header, we can't do anything with it. 88 if($pos===false) { return $this->NO_RESOLUTION; } 89 // JFIF is followed by a zero byte, then a 2 byte version number, then 1 byte 'unit', and 2x2 byte x/y dimensions 90 $pos += 5 + 2; 91 $unit = ord(substr($string, $pos, 1)); 92 $x = (ord(substr($string, $pos+1, 1)) << 8) + ord(substr($string, $pos+2, 1)); 93 $y = (ord(substr($string, $pos+3, 1)) << 8) + ord(substr($string, $pos+4, 1)); 94 // if unit is 0, there is no unit. if unit is 1, the dimensions are in pixels per inch, and if the unit is 2, dimensions are in pixels per cm. 95 // note that if the dimensions are 0 pixels per unit, no sensible resolution can be determined. 96 if($unit == 0 || $xdim==0 || $ydim==0) { return $this->NO_RESOLUTION; } 97 if($unit==2) { $x *= 2.54; $y *= 2.54; } 98 // finally, return 99 return array("x"=>$x, "y"=>$y); 100 } 101 102 /** 103 * PNG file layout: cycle through chunks until we find "pHYs" chunk. This chunk consists of 4 + 4 + 1 bytes: 104 * - xdim in pixels per unit 105 * - ydim in pixels per unit 106 * - unit (0=unspecified==useless, 1=meters) 107 * see http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html 108 */ 109 function get_dpi_for_png($filename) 110 { 111 // open the file and start reading chunks 112 $a = fopen($filename,'r'); 113 fread($a,8); // dummy data: 0x137 0x80 0x78 0x71 0x13 0x10 0x26 0x10 114 $skip = 0; 115 $chunk = ""; 116 while($chunk != "pHYs" && $chunk != "IDAT") { 117 // skip ahead to next chunk, making sure to skip the 4 byte crc, too 118 if($skip>0) { fread($a, $skip+4);} 119 // get size of chunk (in terms of bytes that it takes up after the chunk name) 120 $b1 = ord(fread($a,1)) << 24; $b2 = ord(fread($a,1)) << 16; $b3 = ord(fread($a,1)) << 8; $b4 = ord(fread($a,1)); 121 $skip = $b1 + $b2 + $b3 + $b4; 122 // get name of chunk 123 $chunk = fread($a,4); } 124 // no physical dimensions chunk exists: assume screen resolution (dpi=72) 125 if($chunk == "IDAT") { fclose($a); return $this->NO_RESOLUTION; } 126 127 // pHYs chunk found, get xdim/ydim/unit values 128 $b1 = ord(fread($a,1)) << 24; $b2 = ord(fread($a,1)) << 16; $b3 = ord(fread($a,1)) << 8; $b4 = ord(fread($a,1)); 129 $xdim = $b1 + $b2 + $b3 + $b4; 130 $b1 = ord(fread($a,1)) << 24; $b2 = ord(fread($a,1)) << 16; $b3 = ord(fread($a,1)) << 8; $b4 = ord(fread($a,1)); 131 $ydim = $b1 + $b2 + $b3 + $b4; 132 $unit = ord(fread($a,1)); 133 fclose($a); 134 135 // if the unit is unspecified, it means we can't tell the physical dimensions. Same for if there are 0 pixels per unit. 136 if($unit != 1 || $xdim==0 || $ydim==0) { return $this->NO_RESOLUTION; } 137 138 // For PNG, dimensions are written as pixels per meter, so we need to conver to pixels per inch 139 return array("x"=>$xdim * 0.0254, "y"=>$ydim * 0.0254); 140 } 141} 142