* Introduced media handler modules for file-type specific operations: thumbnailing...
[lhc/web/wiklou.git] / includes / media / DjVu.php
1 <?php
2
3 class DjVuHandler extends ImageHandler {
4 function isEnabled() {
5 global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML;
6 if ( !$wgDjvuRenderer || ( !$wgDjvuDump && !$wgDjvuToXML ) ) {
7 wfDebug( "DjVu is disabled, please set \$wgDjvuRenderer and \$wgDjvuDump\n" );
8 return false;
9 } else {
10 return true;
11 }
12 }
13
14 function mustRender() { return true; }
15 function isMultiPage() { return true; }
16
17 function validateParam( $name, $value ) {
18 if ( in_array( $name, array( 'width', 'height', 'page' ) ) ) {
19 if ( $value <= 0 ) {
20 return false;
21 } else {
22 return true;
23 }
24 } else {
25 return false;
26 }
27 }
28
29 function makeParamString( $params ) {
30 $page = isset( $params['page'] ) ? $params['page'] : 1;
31 if ( !isset( $params['width'] ) ) {
32 return false;
33 }
34 return "{$params['width']}px-page{$page}";
35 }
36
37 function parseParamString( $str ) {
38 $m = false;
39 if ( preg_match( '/^(\d+)px-page(\d+)$/', $str, $m ) ) {
40 return array( 'width' => $m[1], 'page' => $m[2] );
41 } else {
42 return false;
43 }
44 }
45
46 function getScriptParams( $params ) {
47 return array(
48 'width' => $params['width'],
49 'page' => $params['page'],
50 );
51 }
52
53 function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
54 global $wgDjvuRenderer, $wgDjvuPostProcessor;
55
56 // Fetch XML and check it, to give a more informative error message than the one which
57 // normaliseParams will inevitably give.
58 $xml = $image->getMetadata();
59 if ( !$xml ) {
60 return new MediaTransformError( 'thumbnail_error', @$params['width'], @$params['height'],
61 wfMsg( 'djvu_no_xml' ) );
62 }
63
64 if ( !$this->normaliseParams( $image, $params ) ) {
65 return new TransformParameterError( $params );
66 }
67 $width = $params['width'];
68 $height = $params['height'];
69 $srcPath = $image->getImagePath();
70 $page = $params['page'];
71 $pageCount = $this->pageCount( $image );
72 if ( $page > $this->pageCount( $image ) ) {
73 return new MediaTransformError( 'thumbnail_error', $width, $height, wfMsg( 'djvu_page_error' ) );
74 }
75
76 if ( $flags & self::TRANSFORM_LATER ) {
77 return new ThumbnailImage( $dstUrl, $width, $height, $dstPath );
78 }
79
80 if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
81 return new MediaTransformError( 'thumbnail_error', $width, $height, wfMsg( 'thumbnail_dest_directory' ) );
82 }
83
84 # Use a subshell (brackets) to aggregate stderr from both pipeline commands
85 # before redirecting it to the overall stdout. This works in both Linux and Windows XP.
86 $cmd = '(' . wfEscapeShellArg( $wgDjvuRenderer ) . " -format=ppm -page={$page} -size={$width}x{$height} " .
87 wfEscapeShellArg( $srcPath );
88 if ( $wgDjvuPostProcessor ) {
89 $cmd .= " | {$wgDjvuPostProcessor}";
90 }
91 $cmd .= ' > ' . wfEscapeShellArg($dstPath) . ') 2>&1';
92 wfProfileIn( 'ddjvu' );
93 wfDebug( __METHOD__.": $cmd\n" );
94 $err = wfShellExec( $cmd, $retval );
95 wfProfileOut( 'ddjvu' );
96
97 $removed = $this->removeBadFile( $dstPath, $retval );
98 if ( $retval != 0 || $removed ) {
99 wfDebugLog( 'thumbnail',
100 sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
101 wfHostname(), $retval, trim($err), $cmd ) );
102 return new MediaTransformError( 'thumbnail_error', $width, $height, $err );
103 } else {
104 return new ThumbnailImage( $dstUrl, $width, $height, $dstPath );
105 }
106 }
107
108 /**
109 * Cache an instance of DjVuImage in an Image object, return that instance
110 */
111 function getDjVuImage( $image, $path ) {
112 if ( !$image ) {
113 $deja = new DjVuImage( $path );
114 } elseif ( !isset( $image->dejaImage ) ) {
115 $deja = $image->dejaImage = new DjVuImage( $path );
116 } else {
117 $deja = $image->dejaImage;
118 }
119 return $deja;
120 }
121
122 /**
123 * Cache a document tree for the DjVu XML metadata
124 */
125 function getMetaTree( $image ) {
126 if ( isset( $image->dejaMetaTree ) ) {
127 return $image->dejaMetaTree;
128 }
129
130 $metadata = $image->getMetadata();
131 if ( !$this->isMetadataValid( $image, $metadata ) ) {
132 wfDebug( "DjVu XML metadata is invalid or missing, should have been fixed in upgradeRow\n" );
133 return false;
134 }
135 wfProfileIn( __METHOD__ );
136
137 wfSuppressWarnings();
138 try {
139 $image->dejaMetaTree = new SimpleXMLElement( $metadata );
140 } catch( Exception $e ) {
141 wfDebug( "Bogus multipage XML metadata on '$image->name'\n" );
142 // Set to false rather than null to avoid further attempts
143 $image->dejaMetaTree = false;
144 }
145 wfRestoreWarnings();
146 wfProfileOut( __METHOD__ );
147 return $image->dejaMetaTree;
148 }
149
150 function getImageSize( $image, $path ) {
151 return $this->getDjVuImage( $image, $path )->getImageSize();
152 }
153
154 function getThumbType( $ext, $mime ) {
155 global $wgDjvuOutputExtension;
156 static $mime;
157 if ( !isset( $mime ) ) {
158 $magic = MimeMagic::singleton();
159 $mime = $magic->guessTypesForExtension( $wgDjvuOutputExtension );
160 }
161 return array( $wgDjvuOutputExtension, $mime );
162 }
163
164 function getMetadata( $image, $path ) {
165 wfDebug( "Getting DjVu metadata for $path\n" );
166 return $this->getDjVuImage( $image, $path )->retrieveMetaData();
167 }
168
169 function getMetadataType( $image ) {
170 return 'djvuxml';
171 }
172
173 function isMetadataValid( $image, $metadata ) {
174 return !empty( $metadata ) && $metadata != serialize(array());
175 }
176
177 function pageCount( $image ) {
178 $tree = $this->getMetaTree( $image );
179 if ( !$tree ) {
180 return false;
181 }
182 return count( $tree->xpath( '//OBJECT' ) );
183 }
184
185 function getPageDimensions( $image, $page ) {
186 $tree = $this->getMetaTree( $image );
187 if ( !$tree ) {
188 return false;
189 }
190
191 $o = $tree->BODY[0]->OBJECT[$page-1];
192 if ( $o ) {
193 return array(
194 'width' => intval( $o['width'] ),
195 'height' => intval( $o['height'] )
196 );
197 } else {
198 return false;
199 }
200 }
201 }
202
203 ?>