*/ if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'action.php'); class action_plugin_dpicorrect extends DokuWiki_Action_Plugin { var $NO_RESOLUTION = -1; function getInfo() { return array( 'author' => 'Mike "Pomax" Kamermans', 'email' => 'pomax@nihongoresources.com', 'date' => '2010-09-16', 'name' => 'DPI correct', 'desc' => 'Corrects image size based on their resolution (dpi), giving them CSS width and height values in inches, instead of pixels', 'url' => 'http://www.dokuwiki.org/plugin:dpicorrect'); } /** * Postprocesses the html that was built from that, to rubify kanji that have associated furigana. */ function register(&$controller) { $controller->register_hook('IMAGE_LINK_POSTPROCESS', 'AFTER', $this, '_dpicorrect'); } /** * check if an image was a .jpg or .png image, because these can have custom dpi values. */ function _dpicorrect(&$event, $param) { // reference to data and associated data type $img = $event->data; // get image link. We only care about JPG and PNG, because GIF images are an on-screen format, and inherit the screen's dpi preg_match("/media=([^\"]+\.(jpg|png))\"/",$img,$matches); $link = str_replace(":","/",$matches[1]); $file = 'data/media/'.$link; // Not unimportant: only run if the link is valid. if(file_exists($file)) { if(strpos($link,".jpg")!==false) { $jpg = ImageCreateFromJPEG($file); $imgdpi = $this->get_dpi_for_jpeg($file); // if there is no explicit dpi value, leave this image alone if($imgdpi == $this->NO_RESOLUTION) { return; } // if it's not, compute the corrected image dimensions $width = ImageSX($jpg) / $imgdpi["x"]; $height = ImageSY($jpg) / $imgdpi["y"]; } else if (strpos($link,".png")!==false) { $png = imagecreatefrompng($file); $imgdpi = $this->get_dpi_for_png($file); // if there is no explicit dpi value, leave this image alone if($imgdpi == $this->NO_RESOLUTION) { return; } // if it's not, compute the corrected image dimensions $width = ImageSX($png) / $imgdpi["x"]; $height = ImageSY($png) / $imgdpi["y"]; } // retrofit the dimensions as explicit width/height attributes now $event->data = str_replace('class="media"','class="media" style="width: '.$width.'in; height: '.$height.'in;"',$img); } } /** * Mostly from http://www.w3.org/Graphics/JPEG/jfif3.pdf, with with a quick peek * for the JFIF mark, because it's not guaranteeds to be at byte position 2! */ function get_dpi_for_jpeg($filename) { // open the file and read first 20 bytes. $a = fopen($filename,'r'); $string = fread($a,20); fclose($a); $pos = strpos($string, "JFIF"); // if the image claims it's jpg but has no JFIF header, we can't do anything with it. if($pos===false) { return $this->NO_RESOLUTION; } // JFIF is followed by a zero byte, then a 2 byte version number, then 1 byte 'unit', and 2x2 byte x/y dimensions $pos += 5 + 2; $unit = ord(substr($string, $pos, 1)); $x = (ord(substr($string, $pos+1, 1)) << 8) + ord(substr($string, $pos+2, 1)); $y = (ord(substr($string, $pos+3, 1)) << 8) + ord(substr($string, $pos+4, 1)); // 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. // note that if the dimensions are 0 pixels per unit, no sensible resolution can be determined. if($unit == 0 || $xdim==0 || $ydim==0) { return $this->NO_RESOLUTION; } if($unit==2) { $x *= 2.54; $y *= 2.54; } // finally, return return array("x"=>$x, "y"=>$y); } /** * PNG file layout: cycle through chunks until we find "pHYs" chunk. This chunk consists of 4 + 4 + 1 bytes: * - xdim in pixels per unit * - ydim in pixels per unit * - unit (0=unspecified==useless, 1=meters) * see http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html */ function get_dpi_for_png($filename) { // open the file and start reading chunks $a = fopen($filename,'r'); fread($a,8); // dummy data: 0x137 0x80 0x78 0x71 0x13 0x10 0x26 0x10 $skip = 0; $chunk = ""; while($chunk != "pHYs" && $chunk != "IDAT") { // skip ahead to next chunk, making sure to skip the 4 byte crc, too if($skip>0) { fread($a, $skip+4);} // get size of chunk (in terms of bytes that it takes up after the chunk name) $b1 = ord(fread($a,1)) << 24; $b2 = ord(fread($a,1)) << 16; $b3 = ord(fread($a,1)) << 8; $b4 = ord(fread($a,1)); $skip = $b1 + $b2 + $b3 + $b4; // get name of chunk $chunk = fread($a,4); } // no physical dimensions chunk exists: assume screen resolution (dpi=72) if($chunk == "IDAT") { fclose($a); return $this->NO_RESOLUTION; } // pHYs chunk found, get xdim/ydim/unit values $b1 = ord(fread($a,1)) << 24; $b2 = ord(fread($a,1)) << 16; $b3 = ord(fread($a,1)) << 8; $b4 = ord(fread($a,1)); $xdim = $b1 + $b2 + $b3 + $b4; $b1 = ord(fread($a,1)) << 24; $b2 = ord(fread($a,1)) << 16; $b3 = ord(fread($a,1)) << 8; $b4 = ord(fread($a,1)); $ydim = $b1 + $b2 + $b3 + $b4; $unit = ord(fread($a,1)); fclose($a); // if the unit is unspecified, it means we can't tell the physical dimensions. Same for if there are 0 pixels per unit. if($unit != 1 || $xdim==0 || $ydim==0) { return $this->NO_RESOLUTION; } // For PNG, dimensions are written as pixels per meter, so we need to conver to pixels per inch return array("x"=>$xdim * 0.0254, "y"=>$ydim * 0.0254); } }