Index: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsScaler.i18n.php |
— | — | @@ -0,0 +1,402 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for extension VipsScaler. |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup Extensions |
| 8 | + */ |
| 9 | + |
| 10 | +$messages = array(); |
| 11 | + |
| 12 | +$messages['en'] = array( |
| 13 | + 'vipstest' => 'VIPS scaling test page', |
| 14 | + |
| 15 | + 'vipsscaler-desc' => 'Create thumbnails using VIPS', |
| 16 | + 'vipsscaler-invalid-file' => 'Could not process requested file. Check that it exists on this wiki.', |
| 17 | + 'vipsscaler-invalid-width' => 'Thumbnail width should be larger than zero and not larger than file width.', |
| 18 | + 'vipsscaler-invalid-sharpen' => 'Sharpening amount should be a number larger than zero and smaller than five.', |
| 19 | + 'vipsscaler-thumb-error' => 'VIPS could not generate a thumbnail with given parameters.', |
| 20 | + |
| 21 | + # Vipscaler test form: |
| 22 | + 'vipsscaler-form-legend' => 'VIPS scaling', |
| 23 | + 'vipsscaler-form-width' => 'Thumbnail width:', |
| 24 | + 'vipsscaler-form-file' => 'File on this wiki:', |
| 25 | + 'vipsscaler-form-sharpen-radius' => 'Amount of sharpening:', |
| 26 | + 'vipsscaler-form-bilinear' => 'Bilinear scaling', |
| 27 | + 'vipsscaler-form-submit' => 'Generate thumbnails', |
| 28 | + |
| 29 | + 'vipsscaler-default-thumb' => 'Thumbnail generated with default scaler', |
| 30 | + 'vipsscaler-vips-thumb' => 'Thumbnail generated with VIPS', |
| 31 | + |
| 32 | + 'vipsscaler-show-both' => 'Show both thumbnails', |
| 33 | + 'vipsscaler-show-default' => 'Show default thumbnail only', |
| 34 | + 'vipsscaler-show-vips' => 'Show VIPS thumbnail only', |
| 35 | + |
| 36 | + # User rights |
| 37 | + 'right-vipsscaler-test' => 'Use the VIPS scaling test interface [[Special:VipsTest]]', |
| 38 | +); |
| 39 | + |
| 40 | +/** Message documentation (Message documentation) |
| 41 | + * @author Purodha |
| 42 | + * @author Umherirrender |
| 43 | + */ |
| 44 | +$messages['qqq'] = array( |
| 45 | + 'vipstest' => 'Title of the Special:VipsTest page', |
| 46 | + 'vipsscaler-desc' => '{{desc}}', |
| 47 | + 'vipsscaler-invalid-file' => 'Error message when SpecialVipsTest was given a non existent or invalid file name', |
| 48 | + 'vipsscaler-invalid-width' => 'Error message when SpecialVipsTest did not get a valid width parameter', |
| 49 | + 'vipsscaler-thumb-error' => 'Error message when VIPS did not manage to generate a thumbnail', |
| 50 | + 'vipsscaler-form-legend' => 'Special:VipsTest form: legend at top of the form', |
| 51 | + 'vipsscaler-form-width' => 'Special:VipsTest form: label for the width input box', |
| 52 | + 'vipsscaler-form-file' => 'Special:VipsTest form: label for the file input box', |
| 53 | + 'vipsscaler-form-sharpen-radius' => 'Special:VipsTest form: label for the sharpening amount input box', |
| 54 | + 'vipsscaler-form-bilinear' => 'Special:VipsTest form: Checkbox label to determine whether to enable bilinear scaling', |
| 55 | + 'vipsscaler-form-submit' => 'Special:VipsTest form: submit button text. The page will then attempt to generate a thumbnail with the given parameters.', |
| 56 | + 'vipsscaler-default-thumb' => 'Special:VipsTest: caption of the default thumbnail', |
| 57 | + 'vipsscaler-vips-thumb' => 'Special:VipsTest: caption of the vips thumbnail', |
| 58 | + 'vipsscaler-show-both' => 'Special:VipsTest: button to show both thumbnails', |
| 59 | + 'vipsscaler-show-default' => 'Special:VipsTest: button to show default thumbnail only', |
| 60 | + 'vipsscaler-show-vips' => 'Special:VipsTest: button to show VIPS thumbnail only', |
| 61 | + 'right-vipsscaler-test' => '{{doc-right|vipsscaler-test}}', |
| 62 | +); |
| 63 | + |
| 64 | +/** Afrikaans (Afrikaans) |
| 65 | + * @author Naudefj |
| 66 | + */ |
| 67 | +$messages['af'] = array( |
| 68 | + 'vipsscaler-desc' => 'Skep duimnaels met behulp van VIPS.', |
| 69 | +); |
| 70 | + |
| 71 | +/** Asturian (Asturianu) |
| 72 | + * @author Xuacu |
| 73 | + */ |
| 74 | +$messages['ast'] = array( |
| 75 | + 'vipsscaler-desc' => 'Crear miniatures usando VIPS', |
| 76 | +); |
| 77 | + |
| 78 | +/** Belarusian (Taraškievica orthography) (Беларуская (тарашкевіца)) |
| 79 | + * @author Wizardist |
| 80 | + */ |
| 81 | +$messages['be-tarask'] = array( |
| 82 | + 'vipsscaler-desc' => 'Стварае мініятуры з дапамогай VIPS', |
| 83 | +); |
| 84 | + |
| 85 | +/** Breton (Brezhoneg) |
| 86 | + * @author Fulup |
| 87 | + */ |
| 88 | +$messages['br'] = array( |
| 89 | + 'vipsscaler-desc' => 'Krouiñ a ra munudoù en ur ober gant VIPS', |
| 90 | + 'vipsscaler-form-file' => 'Restr er wiki-mañ :', |
| 91 | +); |
| 92 | + |
| 93 | +/** Bosnian (Bosanski) |
| 94 | + * @author CERminator |
| 95 | + */ |
| 96 | +$messages['bs'] = array( |
| 97 | + 'vipsscaler-desc' => 'Pravljenje smanjenog pregleda koristeći VIPS', |
| 98 | +); |
| 99 | + |
| 100 | +/** Danish (Dansk) |
| 101 | + * @author Peter Alberti |
| 102 | + */ |
| 103 | +$messages['da'] = array( |
| 104 | + 'vipstest' => 'Testside for skalering vha. VIPS', |
| 105 | + 'vipsscaler-desc' => 'Opret miniaturebilleder ved hjælp af VIPS', |
| 106 | + 'vipsscaler-invalid-file' => 'Ugyldig fil: kunne ikke behandle den angivne fil. Findes den på denne wiki?', |
| 107 | + 'vipsscaler-invalid-width' => 'Du skal angive en bredde (heltal > 0).', |
| 108 | + 'vipsscaler-thumb-error' => 'VIPS kunne ikke oprette et miniaturebillede med de angivne parametre.', |
| 109 | + 'vipsscaler-form-legend' => 'Skalering vha. VIPS', |
| 110 | + 'vipsscaler-form-width' => 'Miniaturebredde:', |
| 111 | + 'vipsscaler-form-file' => 'Fil på denne wiki:', |
| 112 | + 'vipsscaler-form-submit' => 'Opret miniature', |
| 113 | + 'right-vipsscaler-test' => 'Brug brugerfladen til test af skalering ved hjælp af VIPS på [[Special:VipsTest]]', |
| 114 | +); |
| 115 | + |
| 116 | +/** German (Deutsch) |
| 117 | + * @author Kghbln |
| 118 | + */ |
| 119 | +$messages['de'] = array( |
| 120 | + 'vipstest' => 'Testseite zur VIPS-Skalierung', |
| 121 | + 'vipsscaler-desc' => 'Ermöglicht das Erstellen von Miniaturbildern mit VIPS', |
| 122 | + 'vipsscaler-invalid-file' => 'Die angeforderte Datei konnte nicht verarbeitet werden. Bitte überprüfen, ob Sie auf diesem Wiki vorhanden ist.', |
| 123 | + 'vipsscaler-invalid-width' => 'Die Breite des Miniaturbildes sollte größer als Null und nicht größer als die Breite des Bildes sein.', |
| 124 | + 'vipsscaler-invalid-sharpen' => 'Der Wert der Bildschärfe sollte größer als Null und kleiner als Fünf sein.', |
| 125 | + 'vipsscaler-thumb-error' => 'VIPS konnte auf Basis der angegebenen Parameter kein Miniaturbild generieren.', |
| 126 | + 'vipsscaler-form-legend' => 'VIPS-Skalierung', |
| 127 | + 'vipsscaler-form-width' => 'Breite des Miniaturbildes:', |
| 128 | + 'vipsscaler-form-file' => 'Datei in diesem Wiki:', |
| 129 | + 'vipsscaler-form-sharpen-radius' => 'Wert der Bildschärfe:', |
| 130 | + 'vipsscaler-form-bilinear' => 'Bilineare Skalierung', |
| 131 | + 'vipsscaler-form-submit' => 'Miniaturbild generieren', |
| 132 | + 'vipsscaler-default-thumb' => 'Das Miniaturbild wurde mit dem Standardsaklierungsprogramm generiert.', |
| 133 | + 'vipsscaler-vips-thumb' => 'Das Miniaturbild wurde mit VIPS generiert.', |
| 134 | + 'vipsscaler-show-both' => 'Beide Miniaturbilder anzeigen', |
| 135 | + 'vipsscaler-show-default' => 'Nur das Standardminiaturbild anzeigen', |
| 136 | + 'vipsscaler-show-vips' => 'Nur das VIPS-Miniaturbild anzeigen', |
| 137 | + 'right-vipsscaler-test' => 'Nutze das Testinterface zur VIPS-Skalierung [[Special:VipsTest]]', |
| 138 | +); |
| 139 | + |
| 140 | +/** German (formal address) (Deutsch (Sie-Form)) |
| 141 | + * @author Kghbln |
| 142 | + */ |
| 143 | +$messages['de-formal'] = array( |
| 144 | + 'right-vipsscaler-test' => 'Nutzen Sie das Testinterface zur VIPS-Skalierung [[Special:VipsTest]]', |
| 145 | +); |
| 146 | + |
| 147 | +/** French (Français) |
| 148 | + * @author Gomoko |
| 149 | + * @author IAlex |
| 150 | + */ |
| 151 | +$messages['fr'] = array( |
| 152 | + 'vipstest' => "Page de test de la mise à l'échelle de VIPS", |
| 153 | + 'vipsscaler-desc' => "Créer des miniatures à l'aide de VIPS", |
| 154 | + 'vipsscaler-invalid-file' => "Impossible de traiter le fichier demandé. Vérifiez qu'il existe sur ce wiki.", |
| 155 | + 'vipsscaler-invalid-width' => 'La largeur de la vignette doit être supérieure à zéro et pas supérieure à la largeur du fichier.', |
| 156 | + 'vipsscaler-invalid-sharpen' => 'La quantité de netteté doit être un nombre plus grand que zéro et plus petit que cinq.', |
| 157 | + 'vipsscaler-thumb-error' => "VIPS n'a pas pu générer une miniature avec les paramètres fournis.", |
| 158 | + 'vipsscaler-form-legend' => "Mise à l'échelle de VIPS", |
| 159 | + 'vipsscaler-form-width' => 'Largeur de la miniature :', |
| 160 | + 'vipsscaler-form-file' => 'Fichier sur ce wiki :', |
| 161 | + 'vipsscaler-form-sharpen-radius' => 'Montant de netteté :', |
| 162 | + 'vipsscaler-form-bilinear' => "Mise à l'échelle bilinéaire", |
| 163 | + 'vipsscaler-form-submit' => 'Générer la vignette', |
| 164 | + 'vipsscaler-default-thumb' => "Vignette générée avec une mise à l'échelle par défaut", |
| 165 | + 'vipsscaler-vips-thumb' => 'Vignette générée avec VIPS', |
| 166 | + 'vipsscaler-show-both' => 'Afficher les deux vignettes', |
| 167 | + 'vipsscaler-show-default' => 'Afficher uniquement la vignette par défaut', |
| 168 | + 'vipsscaler-show-vips' => 'Afficher uniquement la vignette VIPS', |
| 169 | + 'right-vipsscaler-test' => "Utiliser l'interface de test de mise à l'échelle de VIP [[Special:VipsTest]]", |
| 170 | +); |
| 171 | + |
| 172 | +/** Franco-Provençal (Arpetan) |
| 173 | + * @author ChrisPtDe |
| 174 | + */ |
| 175 | +$messages['frp'] = array( |
| 176 | + 'vipsscaler-desc' => 'Fât des figures avouéc VIPS.', |
| 177 | +); |
| 178 | + |
| 179 | +/** Galician (Galego) |
| 180 | + * @author Toliño |
| 181 | + */ |
| 182 | +$messages['gl'] = array( |
| 183 | + 'vipstest' => 'Páxina de probas da escala de VIPS', |
| 184 | + 'vipsscaler-desc' => 'Crear miniaturas utilizando VIPS', |
| 185 | + 'vipsscaler-invalid-file' => 'Non se puido procesar o ficheiro solicitado. Comprobe que existe neste wiki.', |
| 186 | + 'vipsscaler-invalid-width' => 'O largo da miniatura debe ser maior que cero e non pode superar o largo do ficheiro.', |
| 187 | + 'vipsscaler-invalid-sharpen' => 'A cantidade de agudización debe ser un número maior que cero e menor que cinco.', |
| 188 | + 'vipsscaler-thumb-error' => 'VIPS non pode xerar unha miniatura cos parámetros proporcionados.', |
| 189 | + 'vipsscaler-form-legend' => 'Escala de VIPS', |
| 190 | + 'vipsscaler-form-width' => 'Largo da miniatura:', |
| 191 | + 'vipsscaler-form-file' => 'Ficheiro neste wiki:', |
| 192 | + 'vipsscaler-form-sharpen-radius' => 'Cantidade de agudización:', |
| 193 | + 'vipsscaler-form-bilinear' => 'Escala bilinear', |
| 194 | + 'vipsscaler-form-submit' => 'Xerar a miniatura', |
| 195 | + 'vipsscaler-default-thumb' => 'Miniatura xerada coa escala por defecto', |
| 196 | + 'vipsscaler-vips-thumb' => 'Miniatura xerada con VIPS', |
| 197 | + 'vipsscaler-show-both' => 'Mostrar ambas as miniaturas', |
| 198 | + 'vipsscaler-show-default' => 'Mostrar só a miniatura por defecto', |
| 199 | + 'vipsscaler-show-vips' => 'Mostrar só a miniatura de VIPS', |
| 200 | + 'right-vipsscaler-test' => 'Utilizar a interface de probas de escala de VIPS, [[Special:VipsTest]]', |
| 201 | +); |
| 202 | + |
| 203 | +/** Hebrew (עברית) |
| 204 | + * @author Amire80 |
| 205 | + */ |
| 206 | +$messages['he'] = array( |
| 207 | + 'vipsscaler-desc' => 'יצירות תמונות ממוזערות באמצעות VIPS', |
| 208 | +); |
| 209 | + |
| 210 | +/** Upper Sorbian (Hornjoserbsce) |
| 211 | + * @author Michawiki |
| 212 | + */ |
| 213 | +$messages['hsb'] = array( |
| 214 | + 'vipsscaler-desc' => 'Přehladowe wobrazki z pomocu VIPS wutworić', |
| 215 | +); |
| 216 | + |
| 217 | +/** Interlingua (Interlingua) |
| 218 | + * @author McDutchie |
| 219 | + */ |
| 220 | +$messages['ia'] = array( |
| 221 | + 'vipstest' => 'Pagina de test pro scalation VIPS', |
| 222 | + 'vipsscaler-desc' => 'Crear miniaturas con VIPS', |
| 223 | + 'vipsscaler-invalid-file' => 'Non poteva processar le file requestate. Verifica que illo existe in iste wiki.', |
| 224 | + 'vipsscaler-invalid-width' => 'Le latitude del miniatura debe esser superior a zero e non superior al latitude del file.', |
| 225 | + 'vipsscaler-invalid-sharpen' => 'Le quantitate de acutiamento debe esser un numero superior a zero e inferior a cinque.', |
| 226 | + 'vipsscaler-thumb-error' => 'VIPS non poteva generar un miniatura con le parametros specificate.', |
| 227 | + 'vipsscaler-form-legend' => 'Scalation VIPS', |
| 228 | + 'vipsscaler-form-width' => 'Latitude del miniatura:', |
| 229 | + 'vipsscaler-form-file' => 'File in iste wiki:', |
| 230 | + 'vipsscaler-form-sharpen-radius' => 'Quantitate de acutiamento:', |
| 231 | + 'vipsscaler-form-bilinear' => 'Redimensionamento bilinear', |
| 232 | + 'vipsscaler-form-submit' => 'Generar miniatura', |
| 233 | + 'vipsscaler-default-thumb' => 'Miniatura generate con redimensionator predefinite', |
| 234 | + 'vipsscaler-vips-thumb' => 'Miniatura generate con VIPS', |
| 235 | + 'vipsscaler-show-both' => 'Monstrar ambe miniaturas', |
| 236 | + 'vipsscaler-show-default' => 'Monstrar miniatura predefinite solmente', |
| 237 | + 'vipsscaler-show-vips' => 'Monstrar miniatura VIPS solmente', |
| 238 | + 'right-vipsscaler-test' => 'Usar le interfacie de test pro scalation VIPS [[Special:VipsTest]]', |
| 239 | +); |
| 240 | + |
| 241 | +/** Indonesian (Bahasa Indonesia) |
| 242 | + * @author IvanLanin |
| 243 | + */ |
| 244 | +$messages['id'] = array( |
| 245 | + 'vipsscaler-desc' => 'Membuat gambar mini dengan menggunakan VIPS', |
| 246 | +); |
| 247 | + |
| 248 | +/** Italian (Italiano) |
| 249 | + * @author Beta16 |
| 250 | + */ |
| 251 | +$messages['it'] = array( |
| 252 | + 'vipsscaler-desc' => 'Crea miniature utilizzando VIPS', |
| 253 | +); |
| 254 | + |
| 255 | +/** Japanese (日本語) |
| 256 | + * @author Schu |
| 257 | + */ |
| 258 | +$messages['ja'] = array( |
| 259 | + 'vipsscaler-desc' => 'VIPS を用いてサムネイルを作成します。', |
| 260 | +); |
| 261 | + |
| 262 | +/** Colognian (Ripoarisch) |
| 263 | + * @author Purodha |
| 264 | + */ |
| 265 | +$messages['ksh'] = array( |
| 266 | + 'vipsscaler-desc' => 'Minibeldsche met <i lang="en">VIPS</i> maache.', |
| 267 | +); |
| 268 | + |
| 269 | +/** Luxembourgish (Lëtzebuergesch) |
| 270 | + * @author Robby |
| 271 | + */ |
| 272 | +$messages['lb'] = array( |
| 273 | + 'vipsscaler-desc' => 'Miniaturbiller mat VIPS maachen', |
| 274 | + 'vipsscaler-form-file' => 'Fichier an dëser Wiki:', |
| 275 | +); |
| 276 | + |
| 277 | +/** Macedonian (Македонски) |
| 278 | + * @author Bjankuloski06 |
| 279 | + */ |
| 280 | +$messages['mk'] = array( |
| 281 | + 'vipstest' => 'Проба за менување на размер со VIPS', |
| 282 | + 'vipsscaler-desc' => 'Создавање на минијатури со VIPS', |
| 283 | + 'vipsscaler-invalid-file' => 'Не можев да ја обработам бараната податотека. Проверете дали воопшто постои на ова вики.', |
| 284 | + 'vipsscaler-invalid-width' => 'Минијатурата мора да е поширока од нула, а потесна од изворната ширина на податотеката.', |
| 285 | + 'vipsscaler-invalid-sharpen' => 'Изострувањето треба да е поголемо од нула а помало од пет.', |
| 286 | + 'vipsscaler-thumb-error' => 'VIPS не можеше да создаде минијатура со зададените параметри.', |
| 287 | + 'vipsscaler-form-legend' => 'Менување големина со VIPS', |
| 288 | + 'vipsscaler-form-width' => 'Ширина на минијатурата:', |
| 289 | + 'vipsscaler-form-file' => 'Податотека на ова вики:', |
| 290 | + 'vipsscaler-form-sharpen-radius' => 'Изострување:', |
| 291 | + 'vipsscaler-form-bilinear' => 'Билинеарно размерување', |
| 292 | + 'vipsscaler-form-submit' => 'Создај минијатура', |
| 293 | + 'vipsscaler-default-thumb' => 'Минијатура создадена со основно-зададениот размерител', |
| 294 | + 'vipsscaler-vips-thumb' => 'Минијатура создадена со VIPS', |
| 295 | + 'vipsscaler-show-both' => 'Прикажи ги двете минијатури', |
| 296 | + 'vipsscaler-show-default' => 'Прикажи ја само основната минијатура', |
| 297 | + 'vipsscaler-show-vips' => 'Прикажи ја само минијатурата од VIPS', |
| 298 | + 'right-vipsscaler-test' => 'Употреба на го посредникот [[Special:VipsTest]] за испробување на менување големина со VIPS', |
| 299 | +); |
| 300 | + |
| 301 | +/** Malay (Bahasa Melayu) |
| 302 | + * @author Anakmalaysia |
| 303 | + */ |
| 304 | +$messages['ms'] = array( |
| 305 | + 'vipsscaler-desc' => 'Cipta gambar kenit dengan VIPS', |
| 306 | +); |
| 307 | + |
| 308 | +/** Dutch (Nederlands) |
| 309 | + * @author Saruman |
| 310 | + * @author Siebrand |
| 311 | + */ |
| 312 | +$messages['nl'] = array( |
| 313 | + 'vipstest' => 'Testpagina voor VIPS-transformaties', |
| 314 | + 'vipsscaler-desc' => 'Miniaturen van bestanden aanmaken met VIPS', |
| 315 | + 'vipsscaler-invalid-file' => 'Het was niet mogelijk het gevraagde bestand te verwerken. Controleer of het binnen deze wiki aanwezig is.', |
| 316 | + 'vipsscaler-invalid-width' => 'De breedte van de miniatuur moet groter zijn dan 0 en niet groter dan de breedte van het bestand.', |
| 317 | + 'vipsscaler-invalid-sharpen' => 'De hoeveelheid verscherping moet een getal zijn dat groter is dan nul en kleiner is dan vijf.', |
| 318 | + 'vipsscaler-thumb-error' => 'VIPS kon geen miniatuur genereren met de opgegeven parameters.', |
| 319 | + 'vipsscaler-form-legend' => 'VIPS-transformaties', |
| 320 | + 'vipsscaler-form-width' => 'Breedte miniatuur:', |
| 321 | + 'vipsscaler-form-file' => 'Bestand op deze wiki:', |
| 322 | + 'vipsscaler-form-sharpen-radius' => 'Hoeveelheid verscherping:', |
| 323 | + 'vipsscaler-form-bilinear' => 'Bilineair schalen', |
| 324 | + 'vipsscaler-form-submit' => 'Miniatuur genereren', |
| 325 | + 'vipsscaler-default-thumb' => 'Er is een miniatuurafbeelding aangemaakt met de standaard opschaler', |
| 326 | + 'vipsscaler-vips-thumb' => 'Er is een miniatuurafbeelding aangemaakt met VIPS', |
| 327 | + 'vipsscaler-show-both' => 'Beide miniatuurafbeeldingen weergeven', |
| 328 | + 'vipsscaler-show-default' => 'Alleen de standaard miniatuurafbeelding weergeven', |
| 329 | + 'vipsscaler-show-vips' => 'Alleen de VIPS-miniatuurafbeelding weergeven', |
| 330 | + 'right-vipsscaler-test' => 'Gebruik de [[Special:VipsTest|testinterface voor VIPS-transformaties]]', |
| 331 | +); |
| 332 | + |
| 333 | +/** Norwegian (bokmål) (Norsk (bokmål)) |
| 334 | + * @author Nghtwlkr |
| 335 | + */ |
| 336 | +$messages['no'] = array( |
| 337 | + 'vipsscaler-desc' => 'Opprett miniatyrbilder med VIPS', |
| 338 | +); |
| 339 | + |
| 340 | +/** Polish (Polski) |
| 341 | + * @author Woytecr |
| 342 | + */ |
| 343 | +$messages['pl'] = array( |
| 344 | + 'vipsscaler-desc' => 'Tworzy miniaturki korzystając z VIPS', |
| 345 | +); |
| 346 | + |
| 347 | +/** Piedmontese (Piemontèis) |
| 348 | + * @author Borichèt |
| 349 | + * @author Dragonòt |
| 350 | + */ |
| 351 | +$messages['pms'] = array( |
| 352 | + 'vipsscaler-desc' => 'Creé dle miniadure dovrand VIPS', |
| 353 | +); |
| 354 | + |
| 355 | +/** Portuguese (Português) |
| 356 | + * @author Hamilton Abreu |
| 357 | + */ |
| 358 | +$messages['pt'] = array( |
| 359 | + 'vipsscaler-desc' => 'Criar miniaturas usando VIPS', |
| 360 | +); |
| 361 | + |
| 362 | +/** Tarandíne (Tarandíne) |
| 363 | + * @author Joetaras |
| 364 | + */ |
| 365 | +$messages['roa-tara'] = array( |
| 366 | + 'vipsscaler-desc' => 'Ccreje le miniature ausanne VIPS', |
| 367 | +); |
| 368 | + |
| 369 | +/** Russian (Русский) |
| 370 | + * @author Александр Сигачёв |
| 371 | + */ |
| 372 | +$messages['ru'] = array( |
| 373 | + 'vipsscaler-desc' => 'Создаёт миниатюры с помощью VIPS', |
| 374 | +); |
| 375 | + |
| 376 | +/** Slovenian (Slovenščina) |
| 377 | + * @author Dbc334 |
| 378 | + */ |
| 379 | +$messages['sl'] = array( |
| 380 | + 'vipsscaler-desc' => 'Ustvari sličice z VIPS', |
| 381 | +); |
| 382 | + |
| 383 | +/** Serbian (Cyrillic script) (Српски (ћирилица)) |
| 384 | + * @author Rancher |
| 385 | + */ |
| 386 | +$messages['sr-ec'] = array( |
| 387 | + 'vipsscaler-desc' => 'Прављење умањених приказа слика користећи VIPS', |
| 388 | +); |
| 389 | + |
| 390 | +/** Telugu (తెలుగు) |
| 391 | + * @author Veeven |
| 392 | + */ |
| 393 | +$messages['te'] = array( |
| 394 | + 'vipsscaler-desc' => 'VIPSని ఉపయోగించి నఖచిత్రాలను తయారుచేయండి', |
| 395 | +); |
| 396 | + |
| 397 | +/** Tagalog (Tagalog) |
| 398 | + * @author AnakngAraw |
| 399 | + */ |
| 400 | +$messages['tl'] = array( |
| 401 | + 'vipsscaler-desc' => 'Lumikha ng mga kagyat na ginagamit ang VIPS', |
| 402 | +); |
| 403 | + |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsScaler.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 404 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsScaler.php |
— | — | @@ -0,0 +1,107 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Copyright © Bryan Tong Minh, 2011 |
| 5 | + * |
| 6 | + * This program is free software; you can redistribute it and/or modify |
| 7 | + * it under the terms of the GNU General Public License as published by |
| 8 | + * the Free Software Foundation; either version 2 of the License, or |
| 9 | + * (at your option) any later version. |
| 10 | + * |
| 11 | + * This program is distributed in the hope that it will be useful, |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | + * GNU General Public License for more details. |
| 15 | + * |
| 16 | + * You should have received a copy of the GNU General Public License along |
| 17 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 18 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 19 | + * http://www.gnu.org/copyleft/gpl.html |
| 20 | + * |
| 21 | + * @file |
| 22 | + */ |
| 23 | + |
| 24 | +$wgExtensionCredits['media'][] = array( |
| 25 | + 'path' => __FILE__, |
| 26 | + 'name' => 'VipsScaler', |
| 27 | + 'author' => array( 'Bryan Tong Minh' ), |
| 28 | + 'descriptionmsg' => 'vipsscaler-desc', |
| 29 | + 'url' => 'http://www.mediawiki.org/wiki/VipsScaler', |
| 30 | +); |
| 31 | + |
| 32 | +$dir = dirname( __FILE__ ); |
| 33 | + |
| 34 | +$wgAutoloadClasses['VipsScaler'] = "$dir/VipsScaler_body.php"; |
| 35 | +$wgAutoloadClasses['VipsCommand'] = "$dir/VipsScaler_body.php"; |
| 36 | +$wgAutoloadClasses['VipsConvolution'] = "$dir/VipsScaler_body.php"; |
| 37 | + |
| 38 | +$wgExtensionMessagesFiles['VipsScaler'] = "$dir/VipsScaler.i18n.php"; |
| 39 | + |
| 40 | +$wgHooks['BitmapHandlerTransform'][] = 'VipsScaler::onTransform'; |
| 41 | +$wgHooks['BitmapHandlerCheckImageArea'][] = 'VipsScaler::onBitmapHandlerCheckImageArea'; |
| 42 | + |
| 43 | +# Download vips from http://www.vips.ecs.soton.ac.uk/ |
| 44 | +$wgVipsCommand = 'vips'; |
| 45 | + |
| 46 | +/* Options and conditions for images to be scaled with this scaler. |
| 47 | + * Set to an array of arrays. The inner array contains a condition array, which |
| 48 | + * contains a list of conditions that the image should pass for it to be scaled |
| 49 | + * with vips. Conditions are mimeType, minArea, maxArea, minShrinkFactor, |
| 50 | + * maxShrinkFactor. The other items in the array are options. Options available |
| 51 | + * are: |
| 52 | + * - sharpen: Set to an array with keys 'radius' and 'sigma', which are |
| 53 | + * parameters to gaussian sharpen matrix. |
| 54 | + * - preconvert: Convert the file to a .v file first, which costs some space, |
| 55 | + * but saves memory on the actual downsize |
| 56 | + * - bilinear: Use im_resize_linear instead of im_shrink |
| 57 | + * - convolution: Apply specified convolution matrix |
| 58 | + * - setcomment: Add an exif comment specifying the source of the file. |
| 59 | + * Requires $wgExiv2Command to be set properly. |
| 60 | + */ |
| 61 | +$wgVipsOptions = array( |
| 62 | + # Sharpen jpeg files which are shrunk more than 1.2 |
| 63 | + array( |
| 64 | + 'conditions' => array( |
| 65 | + 'mimeType' => 'image/jpeg', |
| 66 | + 'minShrinkFactor' => 1.2, |
| 67 | + ), |
| 68 | + 'sharpen' => array( 'radius' => 0, 'sigma' => 0.8 ), |
| 69 | + ), |
| 70 | + # Other jpeg files |
| 71 | + array( |
| 72 | + 'conditions' => array( |
| 73 | + 'mimeType' => 'image/jpeg', |
| 74 | + ), |
| 75 | + 'sharpen' => false, |
| 76 | + 'bilinear' => true, |
| 77 | + ), |
| 78 | + # Do a simple shrink for PNGs |
| 79 | + array( |
| 80 | + 'conditions' => array( |
| 81 | + 'mimeType' => 'image/png', |
| 82 | + ), |
| 83 | + ), |
| 84 | +); |
| 85 | + |
| 86 | +# Package vipsScaler material in a resource loader module: |
| 87 | +$wgResourceModules['ext.vipsscaler'] = array( |
| 88 | + 'scripts' => array( 'ext.vipsScaler.js', ), |
| 89 | + 'styles' => array( 'ext.vipsScaler.css' ), |
| 90 | + 'messages' => array( 'vipsscaler-show-both', 'vipsscaler-show-default', 'vipsscaler-show-vips' ), |
| 91 | + 'dependencies' => array( |
| 92 | + 'jquery.ucompare', |
| 93 | + ), |
| 94 | + |
| 95 | + 'localBasePath' => dirname( __FILE__ ) . '/modules/ext.vipsScaler', |
| 96 | + 'remoteExtPath' => 'VipsScaler/modules/ext.vipsScaler', |
| 97 | +); |
| 98 | + |
| 99 | +# Also package upstream jquery.ucompare |
| 100 | +$wgResourceModules['jquery.ucompare'] = array( |
| 101 | + 'scripts' => array( 'js/jquery.ucompare.js', ), |
| 102 | + 'styles' => array( 'css/jquery.ucompare.css' ), |
| 103 | + |
| 104 | + 'localBasePath' => dirname( __FILE__ ) . '/modules/jquery.ucompare', |
| 105 | + 'remoteExtPath' => 'VipsScaler/modules/jquery.ucompare' |
| 106 | +); |
| 107 | + |
| 108 | + |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsScaler.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 109 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsTest.php |
— | — | @@ -0,0 +1,33 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Extension registration file for Special:VipsTest. The VipsScaler extension |
| 6 | + * must be enabled. |
| 7 | + */ |
| 8 | + |
| 9 | +if ( !defined( 'MEDIAWIKI' ) ) exit( 1 ); |
| 10 | + |
| 11 | +/** |
| 12 | + * The host to send the request to when doing the scaling remotely. Set this to |
| 13 | + * null to do the scaling on the same server that receives the request. |
| 14 | + */ |
| 15 | +$wgVipsThumbnailerHost = null; |
| 16 | + |
| 17 | +/** |
| 18 | + * The cache expiry time in seconds to use for images that we stream out. |
| 19 | + */ |
| 20 | +$wgVipsTestExpiry = 3600; |
| 21 | + |
| 22 | +/** Registration */ |
| 23 | +$wgAutoloadClasses['SpecialVipsTest'] = "$dir/SpecialVipsTest.php"; |
| 24 | +$wgExtensionAliasesFiles['VipsTest'] = "$dir/VipsScaler.alias.php"; |
| 25 | +$wgAvailableRights[] = 'vipsscaler-test'; |
| 26 | +$wgGroupPermissions['*']['vipsscaler-test'] = true; |
| 27 | +$wgSpecialPages['VipsTest'] = 'SpecialVipsTest'; |
| 28 | + |
| 29 | +/** |
| 30 | + * Disable VipsScaler for ordinary image scaling so that the test has something |
| 31 | + * to compare against. |
| 32 | + */ |
| 33 | +$wgVipsOptions = array(); |
| 34 | + |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsTest.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 35 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/SpecialVipsTest.php |
— | — | @@ -0,0 +1,363 @@ |
| 2 | +<?php |
| 3 | +/* |
| 4 | + * Copyright © Bryan Tong Minh, 2011 |
| 5 | + * |
| 6 | + * This program is free software; you can redistribute it and/or modify |
| 7 | + * it under the terms of the GNU General Public License as published by |
| 8 | + * the Free Software Foundation; either version 2 of the License, or |
| 9 | + * (at your option) any later version. |
| 10 | + * |
| 11 | + * This program is distributed in the hope that it will be useful, |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | + * GNU General Public License for more details. |
| 15 | + * |
| 16 | + * You should have received a copy of the GNU General Public License along |
| 17 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 18 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 19 | + * http://www.gnu.org/copyleft/gpl.html |
| 20 | + * |
| 21 | + * @file |
| 22 | + */ |
| 23 | + |
| 24 | +/** |
| 25 | + * A Special page intended to test the VipsScaler. |
| 26 | + * @author Bryan Tong Minh |
| 27 | + */ |
| 28 | +class SpecialVipsTest extends SpecialPage { |
| 29 | + public function __construct() { |
| 30 | + parent::__construct( 'VipsTest', 'vipsscaler-test' ); |
| 31 | + } |
| 32 | + |
| 33 | + /** |
| 34 | + * Entry point |
| 35 | + * @param $par Array TODO describe what is expected there |
| 36 | + */ |
| 37 | + public function execute( $par ) { |
| 38 | + $request = $this->getRequest(); |
| 39 | + $this->setHeaders(); |
| 40 | + |
| 41 | + if( !$this->userCanExecute( $this->getUser() ) ) { |
| 42 | + $this->displayRestrictionError(); |
| 43 | + return; |
| 44 | + } |
| 45 | + |
| 46 | + if ( $request->getText( 'thumb' ) ) { |
| 47 | + $this->streamThumbnail(); |
| 48 | + } else { |
| 49 | + $this->showForm(); |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + /** |
| 54 | + */ |
| 55 | + protected function showThumbnails() { |
| 56 | + $request = $this->getRequest(); |
| 57 | + |
| 58 | + # Check if valid file was provided |
| 59 | + $title = Title::makeTitleSafe( NS_FILE, $request->getText( 'file' ) ); |
| 60 | + if ( is_null( $title ) ) { |
| 61 | + $this->getOutput()->addWikiMsg( 'vipsscaler-invalid-file' ); |
| 62 | + return; |
| 63 | + } |
| 64 | + $file = wfFindFile( $title ); |
| 65 | + if ( !$file || !$file->exists() ) { |
| 66 | + $this->getOutput()->addWikiMsg( 'vipsscaler-invalid-file' ); |
| 67 | + return; |
| 68 | + } |
| 69 | + |
| 70 | + # Create options |
| 71 | + $width = $request->getInt( 'width' ); |
| 72 | + if ( !$width ) { |
| 73 | + $this->getOutput()->addWikiMsg( 'vipsscaler-invalid-width' ); |
| 74 | + return; |
| 75 | + } |
| 76 | + $vipsUrlOptions = array( 'thumb' => $file->getName(), 'width' => $width ); |
| 77 | + if ( $request->getVal( 'sharpen' ) ) { |
| 78 | + $vipsUrlOptions['sharpen'] = floatval( $request->getVal( 'sharpen' ) ); |
| 79 | + } |
| 80 | + if ( $request->getCheck( 'bilinear' ) ) { |
| 81 | + $vipsUrlOptions['bilinear'] = 1; |
| 82 | + } |
| 83 | + |
| 84 | + |
| 85 | + # Generate normal thumbnail |
| 86 | + $params = array( 'width' => $width ); |
| 87 | + $thumb = $file->transform( $params ); |
| 88 | + if ( !$thumb || $thumb->isError() ) { |
| 89 | + $this->getOutput()->addWikiMsg( 'vipsscaler-thumb-error' ); |
| 90 | + return; |
| 91 | + } |
| 92 | + |
| 93 | + # Check if we actually scaled the file |
| 94 | + $normalThumbUrl = $thumb->getUrl(); |
| 95 | + if ( wfExpandUrl( $normalThumbUrl ) == $file->getFullUrl() ) { |
| 96 | + // TODO: message |
| 97 | + } |
| 98 | + |
| 99 | + # Make url to the vips thumbnail |
| 100 | + $vipsThumbUrl = $this->getTitle()->getLocalUrl( $vipsUrlOptions ); |
| 101 | + |
| 102 | + # Add to output |
| 103 | + $html = Html::rawElement( 'div', array( 'id' => 'mw-vipstest-thumbnails' ), |
| 104 | + Html::element( 'img', array( |
| 105 | + 'src' => $normalThumbUrl, |
| 106 | + 'alt' => wfMessage( 'vipsscaler-default-thumb' ), |
| 107 | + ) ) . ' ' . |
| 108 | + Html::element( 'img', array( |
| 109 | + 'src' => $vipsThumbUrl, |
| 110 | + 'alt' => wfMessage( 'vipsscaler-vips-thumb' ), |
| 111 | + ) ) |
| 112 | + ); |
| 113 | + $this->getOutput()->addHTML( $html ); |
| 114 | + $this->getOutput()->addModules( array( |
| 115 | + 'ext.vipsscaler', |
| 116 | + 'jquery.ucompare', |
| 117 | + ) ); |
| 118 | + } |
| 119 | + |
| 120 | + /** |
| 121 | + * TODO |
| 122 | + */ |
| 123 | + protected function showForm() { |
| 124 | + $form = new HTMLForm( $this->getFormFields(), $this->getContext() ); |
| 125 | + $form->setWrapperLegend( wfMsg( 'vipsscaler-form-legend' ) ); |
| 126 | + $form->setSubmitText( wfMsg( 'vipsscaler-form-submit' ) ); |
| 127 | + $form->setSubmitCallback( array( __CLASS__, 'processForm' ) ); |
| 128 | + $form->setMethod( 'get' ); |
| 129 | + |
| 130 | + // Looks like HTMLForm does not actually show the form if submission |
| 131 | + // was correct. So we have to show it again. |
| 132 | + // See HTMLForm::show() |
| 133 | + $result = $form->show(); |
| 134 | + if( $result === true or $result instanceof Status && $result->isGood() ) { |
| 135 | + $form->displayForm( $result ); |
| 136 | + $this->showThumbnails(); |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + /** |
| 141 | + * [[Special:VipsTest]] form structure for HTMLForm |
| 142 | + * @return Array A form structure using the HTMLForm system |
| 143 | + */ |
| 144 | + protected function getFormFields() { |
| 145 | + $fields = array( |
| 146 | + 'File' => array( |
| 147 | + 'name' => 'file', |
| 148 | + 'class' => 'HTMLTextField', |
| 149 | + 'required' => true, |
| 150 | + 'size' => '80', |
| 151 | + 'label-message' => 'vipsscaler-form-file', |
| 152 | + 'validation-callback' => array( __CLASS__, 'validateFileInput' ), |
| 153 | + ), |
| 154 | + 'Width' => array( |
| 155 | + 'name' => 'width', |
| 156 | + 'class' => 'HTMLIntField', |
| 157 | + 'default' => '640', |
| 158 | + 'size' => '5', |
| 159 | + 'required' => true, |
| 160 | + 'label-message' => 'vipsscaler-form-width', |
| 161 | + 'validation-callback' => array( __CLASS__, 'validateWidth' ), |
| 162 | + ), |
| 163 | + 'SharpenRadius' => array( |
| 164 | + 'name' => 'sharpen', |
| 165 | + 'class' => 'HTMLFloatField', |
| 166 | + 'default' => '0.0', |
| 167 | + 'size' => '5', |
| 168 | + 'label-message' => 'vipsscaler-form-sharpen-radius', |
| 169 | + 'validation-callback' => array( __CLASS__, 'validateSharpen' ), |
| 170 | + ), |
| 171 | + 'Bilinear' => array( |
| 172 | + 'name' => 'bilinear', |
| 173 | + 'class' => 'HTMLCheckField', |
| 174 | + 'label-message' => 'vipsscaler-form-bilinear', |
| 175 | + ), |
| 176 | + ); |
| 177 | + |
| 178 | + /** |
| 179 | + * Match ImageMagick by default |
| 180 | + */ |
| 181 | + global $wgSharpenParameter; |
| 182 | + if ( preg_match( '/^[0-9.]+x([0-9.]+)$/', $wgSharpenParameter, $m ) ) { |
| 183 | + $fields['SharpenRadius']['default'] = $m[1]; |
| 184 | + } |
| 185 | + return $fields; |
| 186 | + } |
| 187 | + |
| 188 | + public static function validateFileInput( $input, $alldata ) { |
| 189 | + if ( !trim( $input ) ) { |
| 190 | + # Don't show an error if the file is not yet specified, |
| 191 | + # because it is annoying |
| 192 | + return true; |
| 193 | + } |
| 194 | + |
| 195 | + $title = Title::makeTitleSafe( NS_FILE, $input ); |
| 196 | + if( is_null( $title ) ) { |
| 197 | + return wfMsg( 'vipsscaler-invalid-file' ); |
| 198 | + } |
| 199 | + $file = wfFindFile( $title ); # TODO what does it do? |
| 200 | + if ( !$file || !$file->exists() ) { |
| 201 | + return wfMsg( 'vipsscaler-invalid-file' ); |
| 202 | + } |
| 203 | + |
| 204 | + // Looks sane enough. |
| 205 | + return true; |
| 206 | + } |
| 207 | + public static function validateWidth( $input, $allData ) { |
| 208 | + if ( self::validateFileInput( $allData['File'], $allData ) !== true |
| 209 | + || !trim( $allData['File'] ) ) { |
| 210 | + # Invalid file, error will already be shown at file field |
| 211 | + return true; |
| 212 | + } |
| 213 | + $title = Title::makeTitleSafe( NS_FILE, $allData['File'] ); |
| 214 | + $file = wfFindFile( $title ); |
| 215 | + if ( $input <= 0 || $input >= $file->getWidth() ) { |
| 216 | + return wfMsg( 'vipsscaler-invalid-width' ); |
| 217 | + } |
| 218 | + return true; |
| 219 | + } |
| 220 | + public static function validateSharpen( $input, $allData ) { |
| 221 | + if ( $input >= 5.0 || $input < 0.0 ) { |
| 222 | + return wfMsg( 'vipsscaler-invalid-sharpen' ); |
| 223 | + } |
| 224 | + return true; |
| 225 | + |
| 226 | + } |
| 227 | + |
| 228 | + /** |
| 229 | + * Process data submitted by the form. |
| 230 | + */ |
| 231 | + public static function processForm( array $data ) { |
| 232 | + return Status::newGood(); |
| 233 | + } |
| 234 | + |
| 235 | + /** |
| 236 | + * |
| 237 | + */ |
| 238 | + protected function streamThumbnail() { |
| 239 | + global $wgVipsThumbnailerHost, $wgVipsTestExpiry; |
| 240 | + |
| 241 | + $request = $this->getRequest(); |
| 242 | + |
| 243 | + # Validate title and file existance |
| 244 | + $title = Title::makeTitleSafe( NS_FILE, $request->getText( 'thumb' ) ); |
| 245 | + if ( is_null( $title ) ) { |
| 246 | + return $this->streamError( 404 ); |
| 247 | + } |
| 248 | + $file = wfFindFile( $title ); |
| 249 | + if ( !$file || !$file->exists() ) { |
| 250 | + return $this->streamError( 404 ); |
| 251 | + } |
| 252 | + |
| 253 | + # Check if vips can handle this file |
| 254 | + if ( VipsScaler::getVipsHandler( $file ) === false ) { |
| 255 | + return $this->streamError( 500 ); |
| 256 | + } |
| 257 | + |
| 258 | + # Validate param string |
| 259 | + $handler = $file->getHandler(); |
| 260 | + $params = array( 'width' => $request->getInt( 'width' ) ); |
| 261 | + if ( !$handler->normaliseParams( $file, $params ) ) { |
| 262 | + return $this->streamError( 500 ); |
| 263 | + } |
| 264 | + |
| 265 | + |
| 266 | + # Get the thumbnail |
| 267 | + if ( is_null( $wgVipsThumbnailerHost ) || $request->getBool( 'noproxy' ) ) { |
| 268 | + # No remote scaler, need to do it ourselves. |
| 269 | + # Emulate the BitmapHandlerTransform hook |
| 270 | + |
| 271 | + $dstPath = VipsCommand::makeTemp( $file->getExtension() ); |
| 272 | + $dstUrl = ''; |
| 273 | + wfDebug( __METHOD__ . ": Creating vips thumbnail at $dstPath\n" ); |
| 274 | + |
| 275 | + $scalerParams = array( |
| 276 | + # The size to which the image will be resized |
| 277 | + 'physicalWidth' => $params['physicalWidth'], |
| 278 | + 'physicalHeight' => $params['physicalHeight'], |
| 279 | + 'physicalDimensions' => "{$params['physicalWidth']}x{$params['physicalHeight']}", |
| 280 | + # The size of the image on the page |
| 281 | + 'clientWidth' => $params['width'], |
| 282 | + 'clientHeight' => $params['height'], |
| 283 | + # Comment as will be added to the EXIF of the thumbnail |
| 284 | + 'comment' => isset( $params['descriptionUrl'] ) ? |
| 285 | + "File source: {$params['descriptionUrl']}" : '', |
| 286 | + # Properties of the original image |
| 287 | + 'srcWidth' => $file->getWidth(), |
| 288 | + 'srcHeight' => $file->getHeight(), |
| 289 | + 'mimeType' => $file->getMimeType(), |
| 290 | + 'srcPath' => $file->getPath(), |
| 291 | + 'dstPath' => $dstPath, |
| 292 | + 'dstUrl' => $dstUrl, |
| 293 | + ); |
| 294 | + |
| 295 | + $options = array(); |
| 296 | + if ( $request->getBool( 'bilinear' ) ) { |
| 297 | + $options['bilinear'] = true; |
| 298 | + wfDebug( __METHOD__ . ": using bilinear scaling\n" ); |
| 299 | + } |
| 300 | + if ( $request->getVal( 'sharpen' ) && $request->getVal( 'sharpen' ) < 5 ) { |
| 301 | + # Limit sharpen sigma to 5, otherwise we have to write huge convolution matrices |
| 302 | + $options['sharpen'] = array( 'sigma' => floatval( $request->getVal( 'sharpen' ) ) ); |
| 303 | + wfDebug( __METHOD__ . ": sharpening with radius {$options['sharpen']}\n" ); |
| 304 | + } |
| 305 | + |
| 306 | + |
| 307 | + # Call the hook |
| 308 | + $mto = null; |
| 309 | + VipsScaler::doTransform( $handler, $file, $scalerParams, $options, $mto ); |
| 310 | + if ( $mto && !$mto->isError() ) { |
| 311 | + wfDebug( __METHOD__ . ": streaming thumbnail...\n" ); |
| 312 | + $this->getOutput()->disable(); |
| 313 | + StreamFile::stream( $dstPath, array( |
| 314 | + "Cache-Control: public, max-age=$wgVipsTestExpiry, s-maxage=$wgVipsTestExpiry", |
| 315 | + 'Expires: ' . gmdate( 'r ', time() + $wgVipsTestExpiry ) |
| 316 | + ) ); |
| 317 | + } else { |
| 318 | + $this->streamError( 500, $mto->getHtmlMsg() ); |
| 319 | + } |
| 320 | + |
| 321 | + # Cleanup the temporary file |
| 322 | + wfSuppressWarnings(); |
| 323 | + unlink( $dstPath ); |
| 324 | + wfRestoreWarnings(); |
| 325 | + |
| 326 | + } else { |
| 327 | + # Request the thumbnail at a remote scaler |
| 328 | + $url = wfExpandUrl( $request->getRequestURL(), PROTO_INTERNAL ); |
| 329 | + $url = wfAppendQuery( $url, array( 'noproxy' => '1' ) ); |
| 330 | + wfDebug( __METHOD__ . ": Getting vips thumb from remote url $url\n" ); |
| 331 | + |
| 332 | + $options = array( 'method' => 'GET' ); |
| 333 | + |
| 334 | + $req = MWHttpRequest::factory( $url, $options ); |
| 335 | + $status = $req->execute(); |
| 336 | + if ( $status->isOk() ) { |
| 337 | + # Disable output and stream the file |
| 338 | + $this->getOutput()->disable(); |
| 339 | + wfResetOutputBuffers(); |
| 340 | + header( 'Content-Type: ' . $file->getMimeType() ); |
| 341 | + header( 'Content-Length: ' . strlen( $req->getContent() ) ); |
| 342 | + header( "Cache-Control: public, max-age=$wgVipsTestExpiry, s-maxage=$wgVipsTestExpiry" ); |
| 343 | + header( 'Expires: ' . gmdate( 'r ', time() + $wgVipsTestExpiry ) ); |
| 344 | + print $req->getContent(); |
| 345 | + } else { |
| 346 | + return $this->streamError( 500, $req->getContent() ); |
| 347 | + } |
| 348 | + |
| 349 | + } |
| 350 | + } |
| 351 | + |
| 352 | + |
| 353 | + /** |
| 354 | + * Generates a blank page with given HTTP error code |
| 355 | + * |
| 356 | + * @param $code Integer: HTTP error either 404 or 500 |
| 357 | + */ |
| 358 | + protected function streamError( $code, $error = '' ) { |
| 359 | + $this->getOutput()->setStatusCode( $code ); |
| 360 | + $this->getOutput()->setArticleBodyOnly( true ); |
| 361 | + $this->getOutput()->addHTML( $error ); |
| 362 | + } |
| 363 | + |
| 364 | +} |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/SpecialVipsTest.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 365 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/license.gz |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/license.gz |
___________________________________________________________________ |
Added: svn:mime-type |
2 | 366 | + application/octet-stream |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/css/jquery.ucompare.css |
— | — | @@ -0,0 +1,51 @@ |
| 2 | +/*! |
| 3 | + * jQuery uCompare |
| 4 | + * http://www.userdot.net/#!/jquery |
| 5 | + * |
| 6 | + * Copyright 2011, UserDot www.userdot.net |
| 7 | + * Licensed under the GPL Version 3 license. |
| 8 | + * Version 1.0.0 |
| 9 | + * |
| 10 | + */ |
| 11 | + .uc-mask { |
| 12 | + position: absolute; |
| 13 | + top: 0; |
| 14 | + left: 0; |
| 15 | + z-index: 100; |
| 16 | + border-right: 3px solid #333; |
| 17 | + overflow: hidden; |
| 18 | + box-shadow: 3px 5px 5px rgba(0, 0, 0, 0.6); |
| 19 | + box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.6); |
| 20 | + -webkit-box-shadow: 5px 5px 7px rgba(0, 0, 0, 0.6); |
| 21 | + -moz-box-shadow: 5px 0 7px rgba(0, 0, 0, 0.6); |
| 22 | +} |
| 23 | +.uc-bg { |
| 24 | + position: absolute; |
| 25 | + top: 0; |
| 26 | + left: 0; |
| 27 | + z-index: 0; |
| 28 | +} |
| 29 | +.uc-caption { |
| 30 | + position: absolute; |
| 31 | + bottom: 10px; |
| 32 | + left: 10px; |
| 33 | + z-index: 120; |
| 34 | + |
| 35 | + background: #000; |
| 36 | + filter: alpha(opacity=80); |
| 37 | + -moz-opacity:0.8; |
| 38 | + -khtml-opacity: 0.8; |
| 39 | + opacity: 0.8; |
| 40 | + |
| 41 | + color: #fff; |
| 42 | + text-align: center; |
| 43 | + font-size: 12px; |
| 44 | + font-family: arial; |
| 45 | + |
| 46 | + -webkit-border-radius: 5px; |
| 47 | + -moz-border-radius: 5px; |
| 48 | + border-radius: 5px; |
| 49 | + |
| 50 | + padding: 5px; |
| 51 | + display: none; |
| 52 | +} |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/css/jquery.ucompare.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 53 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/css/index.html |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/css/index.html |
___________________________________________________________________ |
Added: svn:eol-style |
2 | 54 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/index.html |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/index.html |
___________________________________________________________________ |
Added: svn:eol-style |
3 | 55 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/js/jquery.ucompare.js |
— | — | @@ -0,0 +1,97 @@ |
| 2 | +/*! |
| 3 | + * jQuery uCompare |
| 4 | + * http://www.userdot.net/#!/jquery |
| 5 | + * |
| 6 | + * Copyright 2011, UserDot www.userdot.net |
| 7 | + * Licensed under the GPL Version 3 license. |
| 8 | + * Version 1.0.0 |
| 9 | + * |
| 10 | + */ |
| 11 | +( function ( $ ) { |
| 12 | + $.fn.extend( { |
| 13 | + // Hook into jquery.ucompage |
| 14 | + ucompare: function ( localConfig ) { |
| 15 | + // Configuration variable |
| 16 | + var config = { |
| 17 | + defaultgap: 50, |
| 18 | + leftgap: 10, |
| 19 | + rightgap: 10, |
| 20 | + caption: false, |
| 21 | + reveal: .5 |
| 22 | + }; |
| 23 | + $.extend( config, localConfig ); |
| 24 | + return this.each( function () { |
| 25 | + /** Initialization function */ |
| 26 | + |
| 27 | + var container = $(this); |
| 28 | + // Extract image attributes |
| 29 | + var imageLeftSource = container.children("img:eq(0)").attr("src"); |
| 30 | + var imageRightSource = container.children("img:eq(1)").attr("src"); |
| 31 | + var caption = container.children("img:eq(0)").attr("alt"); |
| 32 | + var width = container.children("img:eq(0)").width(); |
| 33 | + var height = container.children("img:eq(0)").height(); |
| 34 | + // Hide both images |
| 35 | + container.children("img").hide(); |
| 36 | + container.css({ |
| 37 | + overflow: "hidden", |
| 38 | + position: "relative" |
| 39 | + }); |
| 40 | + |
| 41 | + /** |
| 42 | + * MediaWiki hack: |
| 43 | + * Parent element height can still be 0px after hiding the images |
| 44 | + * so we really want to update its dimensions. |
| 45 | + */ |
| 46 | + container.width(width); container.height(height); |
| 47 | + |
| 48 | + // The left part is the foreground image |
| 49 | + container.append('<div class="uc-mask"></div>'); |
| 50 | + // The right part is the background image |
| 51 | + container.append('<div class="uc-bg"></div>'); |
| 52 | + // Caption |
| 53 | + container.append( $( '<div class="uc-caption" />' ).text( caption ) ); |
| 54 | + // Set the foreground and background image dimensions |
| 55 | + container.children(".uc-mask, .uc-bg").width(width); |
| 56 | + container.children(".uc-mask, .uc-bg").height(height); |
| 57 | + // Fancy initial animation |
| 58 | + container.children(".uc-mask").animate({ |
| 59 | + width: width - config.defaultgap |
| 60 | + }, 1e3); |
| 61 | + // Set the images |
| 62 | + container.children(".uc-mask").css("backgroundImage", "url(" + imageLeftSource + ")"); |
| 63 | + container.children(".uc-bg").css("backgroundImage", "url(" + imageRightSource + ")"); |
| 64 | + if ( config.caption ) { |
| 65 | + container.children(".uc-caption").show() |
| 66 | + } |
| 67 | + }).mousemove(function (event) { |
| 68 | + /** Mouse movent event handler */ |
| 69 | + |
| 70 | + // Create a jQuery object of the container |
| 71 | + var container = $(this); |
| 72 | + |
| 73 | + // Calculate mouse position relative to the left of the image |
| 74 | + var mousePosition = event.pageX - container.children(".uc-mask").offset().left; |
| 75 | + |
| 76 | + // Extract image width |
| 77 | + var imageWidth = container.width(); |
| 78 | + |
| 79 | + // Extract caption |
| 80 | + var captionLeft = container.children("img:eq(0)").attr("alt"); |
| 81 | + var captionRight = container.children("img:eq(1)").attr("alt"); |
| 82 | + |
| 83 | + if ( mousePosition > config.leftgap && |
| 84 | + mousePosition < imageWidth - config.rightgap ) { |
| 85 | + // Set the width of the left image |
| 86 | + container.children(".uc-mask").width( mousePosition ); |
| 87 | + } |
| 88 | + |
| 89 | + // Set caption |
| 90 | + if ( mousePosition < imageWidth * config.reveal ) { |
| 91 | + container.children(".uc-caption").text( captionRight ); |
| 92 | + } else { |
| 93 | + container.children(".uc-caption").text( captionLeft ); |
| 94 | + } |
| 95 | + } ); // End of return statement |
| 96 | + } // End of ucompare function |
| 97 | + } ); |
| 98 | +} )( jQuery ); |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/js/jquery.ucompare.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 99 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/js/index.html |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/js/index.html |
___________________________________________________________________ |
Added: svn:eol-style |
2 | 100 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/README |
— | — | @@ -0,0 +1,9 @@ |
| 2 | +The jQuery uCompare is a jQuery plugin written by Marius Stanciu - Sergiu |
| 3 | +for Userdot, a plugin which gives you the ability to compare two images |
| 4 | +(before and after, etc) via a nice interface. |
| 5 | + |
| 6 | +Source: http://www.userdot.net/files/jquery/jquery.ucompare/ |
| 7 | +Demo: http://www.userdot.net/files/jquery/jquery.ucompare/demo/ |
| 8 | + |
| 9 | +-- |
| 10 | +Copied to MediaWiki subversion repository on 2011-11-15 by hashar |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/jquery.ucompare/README |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 11 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/ext.vipsScaler/ext.vipsScaler.css |
— | — | @@ -0,0 +1,17 @@ |
| 2 | +#mw-vipstest-default-thumb, #mw-vipstest-vips-thumb { |
| 3 | + float: left; |
| 4 | + margin-top: 1em; |
| 5 | + margin-right: 1em; |
| 6 | +} |
| 7 | +.uc-caption { |
| 8 | + /** Overrides ucompare caption position from bottom (bottom:10px) to top */ |
| 9 | + top:10px; |
| 10 | + bottom:auto; |
| 11 | +} |
| 12 | +.uc-mask { |
| 13 | + /* Remove annoying shadow */ |
| 14 | + box-shadow: none; |
| 15 | + box-shadow: none; |
| 16 | + -webkit-box-shadow: none; |
| 17 | + -moz-box-shadow: none; |
| 18 | +} |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/ext.vipsScaler/ext.vipsScaler.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 19 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/ext.vipsScaler/ext.vipsScaler.js |
— | — | @@ -0,0 +1,75 @@ |
| 2 | +jQuery( function( $ ) { |
| 3 | + var container = document.getElementById( 'mw-vipstest-thumbnails' ); |
| 4 | + if ( container ) { |
| 5 | +/* |
| 6 | + $( '<p id="mw-vipstest-buttons">\n' + |
| 7 | + '<button id="mw-vipstest-show-both">' + |
| 8 | + mediaWiki.message( 'vipsscaler-show-both' ).escaped() + |
| 9 | + '</button> ' + |
| 10 | + '<button id="mw-vipstest-show-default">' + |
| 11 | + mediaWiki.message( 'vipsscaler-show-default' ).escaped() + |
| 12 | + '</button> ' + |
| 13 | + '<button id="mw-vipstest-show-vips">' + |
| 14 | + mediaWiki.message( 'vipsscaler-show-vips' ).escaped() + |
| 15 | + '</button> ' + |
| 16 | + '</p>' |
| 17 | + ).prependTo( container ); |
| 18 | +*/ |
| 19 | +/** |
| 20 | + $( '#mw-vipstest-show-both' ).click( function() { |
| 21 | + document.getElementById( 'mw-vipstest-show-default' ).style.display = 'inline'; |
| 22 | + document.getElementById( 'mw-vipstest-show-vips' ).style.display = 'inline'; |
| 23 | + document.getElementById( 'mw-vipstest-default-thumb' ).style.display = 'block'; |
| 24 | + document.getElementById( 'mw-vipstest-vips-thumb' ).style.display = 'block'; |
| 25 | + } ); |
| 26 | + $( '#mw-vipstest-show-default' ).click( function() { |
| 27 | + document.getElementById( 'mw-vipstest-show-default' ).style.display = 'none'; |
| 28 | + document.getElementById( 'mw-vipstest-show-vips' ).style.display = 'inline'; |
| 29 | + document.getElementById( 'mw-vipstest-default-thumb' ).style.display = 'block'; |
| 30 | + document.getElementById( 'mw-vipstest-vips-thumb' ).style.display = 'none'; |
| 31 | + } ); |
| 32 | + $( '#mw-vipstest-show-vips' ).click( function() { |
| 33 | + document.getElementById( 'mw-vipstest-show-default' ).style.display = 'inline'; |
| 34 | + document.getElementById( 'mw-vipstest-show-vips' ).style.display = 'none'; |
| 35 | + document.getElementById( 'mw-vipstest-default-thumb' ).style.display = 'none'; |
| 36 | + document.getElementById( 'mw-vipstest-vips-thumb' ).style.display = 'block'; |
| 37 | + } ); |
| 38 | +**/ |
| 39 | + /** |
| 40 | + * options are detailed in upstream documentation available at |
| 41 | + * http://www.userdot.net/files/jquery/jquery.ucompare/demo/ |
| 42 | + * |
| 43 | + * Copying them here for version 1.0 |
| 44 | + * - caption: toggle the |
| 45 | + * - leftgap: the gap to the left of the image |
| 46 | + * - rightgap: the gap to the right of the image |
| 47 | + * - defaultgap: the default gap shown before any interactions |
| 48 | + */ |
| 49 | + $('#mw-vipstest-thumbnails').ucompare({ |
| 50 | + defaultgap: 50, |
| 51 | + leftgap: 0, |
| 52 | + rightgap: 0, |
| 53 | + caption: true, |
| 54 | + reveal: 0.5 |
| 55 | + }); |
| 56 | + |
| 57 | + /** Also add a click handler to instantly switch beetween pics */ |
| 58 | + $('#mw-vipstest-thumbnails').click( function() { |
| 59 | + var e = $(this) |
| 60 | + var mask = e.children(".uc-mask") |
| 61 | + var caption = e.children(".uc-caption") |
| 62 | + |
| 63 | + width = e.width(); |
| 64 | + maskWidth = mask.width(); |
| 65 | + |
| 66 | + if( maskWidth < width / 2 ) { |
| 67 | + mask.width( width ); |
| 68 | + caption.html( e.children("img:eq(0)").attr("alt") ); |
| 69 | + } else { |
| 70 | + mask.width( 0 ); |
| 71 | + caption.html( e.children("img:eq(1)").attr("alt") ); |
| 72 | + } |
| 73 | + }); |
| 74 | + } |
| 75 | +} |
| 76 | +); |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/modules/ext.vipsScaler/ext.vipsScaler.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 77 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsScaler.alias.php |
— | — | @@ -0,0 +1,20 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Aliases for extension VipsScaler |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup Extensions |
| 8 | + */ |
| 9 | + |
| 10 | +$specialPageAliases = array(); |
| 11 | + |
| 12 | +/** English (English) */ |
| 13 | +$specialPageAliases['en'] = array( |
| 14 | + 'VipsTest' => array( 'VipsTest' ) |
| 15 | +); |
| 16 | + |
| 17 | + |
| 18 | +/** |
| 19 | + * For backwards compatibility with MediaWiki 1.15 and earlier. |
| 20 | + */ |
| 21 | +$aliases =& $specialPageAliases; |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsScaler.alias.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 22 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/README |
— | — | @@ -0,0 +1,27 @@ |
| 2 | +VipsScaler extension for MediaWiki |
| 3 | + |
| 4 | +This extension is a wrapper around VIPS a free image processing software. |
| 5 | + |
| 6 | +== VIPS installation == |
| 7 | + |
| 8 | +http://www.vips.ecs.soton.ac.uk/ |
| 9 | + |
| 10 | +=== Debian / Ubuntu === |
| 11 | + |
| 12 | + $ apt-get install vips |
| 13 | + |
| 14 | +If you want to build from source have a look at upstream documentation: |
| 15 | +http://www.vips.ecs.soton.ac.uk/index.php?title=Build_on_Ubuntu |
| 16 | + |
| 17 | +=== Mac OS X === |
| 18 | + |
| 19 | +; homebrew |
| 20 | +: brew install vips |
| 21 | +: binary symlink is /usr/local/bin/vips |
| 22 | + |
| 23 | +== Configuration == |
| 24 | + |
| 25 | +$wgVipsCommand : path to the vips command (default: vips). |
| 26 | + |
| 27 | +By default everything is scaled using vips. To disable scaling, set |
| 28 | +$wgVipsOptions = array(); |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/README |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 29 | + native |
Index: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsScaler_body.php |
— | — | @@ -0,0 +1,465 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * PHP wrapper class for VIPS under MediaWiki |
| 5 | + * |
| 6 | + * Copyright © Bryan Tong Minh, 2011 |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * You should have received a copy of the GNU General Public License along |
| 19 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 20 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 21 | + * http://www.gnu.org/copyleft/gpl.html |
| 22 | + * @file |
| 23 | + */ |
| 24 | + |
| 25 | +/** |
| 26 | + * Wrapper class for VIPS, a free image processing system good at handling |
| 27 | + * large pictures. |
| 28 | + * |
| 29 | + * http://www.vips.ecs.soton.ac.uk/ |
| 30 | + * |
| 31 | + * @author Bryan Tong Minh |
| 32 | + */ |
| 33 | +class VipsScaler { |
| 34 | + /** |
| 35 | + * Hook to BitmapHandlerTransform. Transforms the file using VIPS if it |
| 36 | + * matches a condition in $wgVipsConditions |
| 37 | + * |
| 38 | + * @param BitmapHandler $handler |
| 39 | + * @param File $file |
| 40 | + * @param array $params |
| 41 | + * @param MediaTransformOutput $mto |
| 42 | + */ |
| 43 | + public static function onTransform( $handler, $file, &$params, &$mto ) { |
| 44 | + # Check $wgVipsConditions |
| 45 | + $options = self::getHandlerOptions( $handler, $file, $params ); |
| 46 | + if ( !$options ) { |
| 47 | + wfDebug( "...\n" ); |
| 48 | + return true; |
| 49 | + } |
| 50 | + |
| 51 | + return self::doTransform( $handler, $file, $params, $options, $mto ); |
| 52 | + } |
| 53 | + |
| 54 | + /** |
| 55 | + * Performs a transform with VIPS |
| 56 | + * |
| 57 | + * @see VipsScaler::onTransform |
| 58 | + * |
| 59 | + * @param BitmapHandler $handler |
| 60 | + * @param File $file |
| 61 | + * @param array $params |
| 62 | + * @param array $options |
| 63 | + * @param MediaTransformOutput &$mto |
| 64 | + * @return bool |
| 65 | + */ |
| 66 | + public static function doTransform( $handler, $file, $params, $options, &$mto ) { |
| 67 | + wfDebug( __METHOD__ . ': scaling ' . $file->getName() . " using vips\n" ); |
| 68 | + |
| 69 | + $vipsCommands = self::makeCommands( $handler, $file, $params, $options ); |
| 70 | + if ( count( $vipsCommands ) == 0 ) { |
| 71 | + return true; |
| 72 | + } |
| 73 | + |
| 74 | + # Execute the commands |
| 75 | + foreach ( $vipsCommands as $i => $command ) { |
| 76 | + # Set input/output files |
| 77 | + if ( $i == 0 && count( $vipsCommands ) == 1 ) { |
| 78 | + # Single command, so output directly to dstPath |
| 79 | + $command->setIO( $params['srcPath'], $params['dstPath'] ); |
| 80 | + } elseif ( $i == 0 ) { |
| 81 | + # First command, input from srcPath, output to temp |
| 82 | + $command->setIO( $params['srcPath'], 'v', VipsCommand::TEMP_OUTPUT ); |
| 83 | + } elseif ( $i + 1 == count( $vipsCommands ) ) { |
| 84 | + # Last command, output to dstPath |
| 85 | + $command->setIO( $vipsCommands[$i - 1], $params['dstPath'] ); |
| 86 | + } else { |
| 87 | + $command->setIO( $vipsCommands[$i - 1], 'v', VipsCommand::TEMP_OUTPUT ); |
| 88 | + } |
| 89 | + |
| 90 | + $retval = $command->execute(); |
| 91 | + if ( $retval != 0 ) { |
| 92 | + wfDebug( __METHOD__ . ": vips command failed!\n" ); |
| 93 | + $mto = $handler->getMediaTransformError( $params, $command->getErrorString() ); |
| 94 | + return false; |
| 95 | + } |
| 96 | + } |
| 97 | + |
| 98 | + # Set comment |
| 99 | + if ( !empty( $options['setcomment'] ) && !empty( $params['comment'] ) ) { |
| 100 | + self::setJpegComment( $params['dstPath'], $params['comment'] ); |
| 101 | + } |
| 102 | + |
| 103 | + # Set the output variable |
| 104 | + $mto = new ThumbnailImage( $file, $params['dstUrl'], |
| 105 | + $params['clientWidth'], $params['clientHeight'], $params['dstPath'] ); |
| 106 | + |
| 107 | + # Stop processing |
| 108 | + return false; |
| 109 | + } |
| 110 | + |
| 111 | + /** |
| 112 | + * @param $handler |
| 113 | + * @param $file |
| 114 | + * @param $params |
| 115 | + * @param $options |
| 116 | + */ |
| 117 | + public static function makeCommands( $handler, $file, $params, $options ) { |
| 118 | + global $wgVipsCommand; |
| 119 | + $commands = array(); |
| 120 | + |
| 121 | + # Get the proper im_XXX2vips handler |
| 122 | + $vipsHandler = self::getVipsHandler( $file ); |
| 123 | + if ( !$vipsHandler ) { |
| 124 | + return array(); |
| 125 | + } |
| 126 | + |
| 127 | + # Check if we need to convert to a .v file first |
| 128 | + if ( !empty( $options['preconvert'] ) ) { |
| 129 | + $commands[] = new VipsCommand( $wgVipsCommand, array( $vipsHandler ) ); |
| 130 | + } |
| 131 | + |
| 132 | + # Do the resizing |
| 133 | + $rotation = 360 - $handler->getRotation( $file ); |
| 134 | + |
| 135 | + wfDebug( __METHOD__ . " rotating '{$file->getName()}' by {$rotation}°\n" ); |
| 136 | + if ( empty( $options['bilinear'] ) ) { |
| 137 | + # Calculate shrink factors. Offsetting by 0.5 pixels is required |
| 138 | + # because of rounding down of the target size by VIPS. See 25990#c7 |
| 139 | + # No need to invert source and physical dimensions. They already got switched if needed. |
| 140 | + $rx = $params['srcWidth'] / ($params['physicalWidth'] + 0.5); |
| 141 | + $ry = $params['srcHeight'] / ($params['physicalHeight'] + 0.5); |
| 142 | + |
| 143 | + wfDebug( sprintf( |
| 144 | + "%s to shrink '%s'. Source: %sx%s, Physical: %sx%s. Shrink factors (rx,ry) = %sx%s.\n", |
| 145 | + __METHOD__, $file->getName(), |
| 146 | + $params['srcWidth'], $params['srcHeight'], |
| 147 | + $params['physicalWidth'], $params['physicalHeight'], |
| 148 | + $rx, $ry |
| 149 | + )); |
| 150 | + |
| 151 | + $commands[] = new VipsCommand( $wgVipsCommand, array( 'im_shrink', $rx, $ry ) ); |
| 152 | + } else { |
| 153 | + if ( $rotation % 180 == 90 ) { |
| 154 | + $dstWidth = $params['physicalHeight']; |
| 155 | + $dstHeight = $params['physicalWidth']; |
| 156 | + } else { |
| 157 | + $dstWidth = $params['physicalWidth']; |
| 158 | + $dstHeight = $params['physicalHeight']; |
| 159 | + } |
| 160 | + wfDebug( sprintf( |
| 161 | + "%s to bilinear resize %s. Source: %sx%s, Physical: %sx%s. Destination: %sx%s\n", |
| 162 | + __METHOD__, $file->getName(), |
| 163 | + $params['srcWidth'], $params['srcHeight'], |
| 164 | + $params['physicalWidth'], $params['physicalHeight'], |
| 165 | + $dstWidth, $dstHeight |
| 166 | + )); |
| 167 | + |
| 168 | + $commands[] = new VipsCommand( $wgVipsCommand, |
| 169 | + array( 'im_resize_linear', $dstWidth, $dstHeight ) ); |
| 170 | + } |
| 171 | + |
| 172 | + if ( !empty( $options['sharpen'] ) ) { |
| 173 | + $options['convolution'] = self::makeSharpenMatrix( $options['sharpen'] ); |
| 174 | + } |
| 175 | + |
| 176 | + if ( !empty( $options['convolution'] ) ) { |
| 177 | + $commands[] = new VipsConvolution( $wgVipsCommand, |
| 178 | + array( 'im_convf', $options['convolution'] ) ); |
| 179 | + } |
| 180 | + |
| 181 | + # Rotation |
| 182 | + if ( $rotation % 360 != 0 && $rotation % 90 == 0 ) { |
| 183 | + $commands[] = new VipsCommand( $wgVipsCommand, array( "im_rot{$rotation}" ) ); |
| 184 | + } |
| 185 | + |
| 186 | + return $commands; |
| 187 | + } |
| 188 | + |
| 189 | + /** |
| 190 | + * Create a sharpening matrix suitable for im_convf. Uses the ImageMagick |
| 191 | + * sharpening algorithm from SharpenImage() in magick/effect.c |
| 192 | + * |
| 193 | + * @param mixed $params |
| 194 | + * @return array |
| 195 | + */ |
| 196 | + public static function makeSharpenMatrix( $params ) { |
| 197 | + $sigma = $params['sigma']; |
| 198 | + $radius = empty( $params['radius'] ) ? |
| 199 | + # After 3 sigma there should be no significant values anymore |
| 200 | + intval( round( $sigma * 3 ) ) : $params['radius']; |
| 201 | + |
| 202 | + $norm = 0; |
| 203 | + $conv = array(); |
| 204 | + |
| 205 | + # Fill the matrix with a negative Gaussian distribution |
| 206 | + $variance = $sigma * $sigma; |
| 207 | + for ( $x = -$radius; $x <= $radius; $x++ ) { |
| 208 | + $row = array(); |
| 209 | + for ( $y = -$radius; $y <= $radius; $y++ ) { |
| 210 | + $z = -exp( -( $x*$x + $y*$y ) / ( 2 * $variance ) ) / |
| 211 | + ( 2 * pi() * $variance ); |
| 212 | + $row[] = $z; |
| 213 | + $norm += $z; |
| 214 | + } |
| 215 | + $conv[] = $row; |
| 216 | + } |
| 217 | + |
| 218 | + # Calculate the scaling parameter to ensure that the mean of the |
| 219 | + # matrix is zero |
| 220 | + $scale = - $conv[$radius][$radius] - $norm; |
| 221 | + # Set the center pixel to obtain a sharpening matrix |
| 222 | + $conv[$radius][$radius] = -$norm * 2; |
| 223 | + # Add the matrix descriptor |
| 224 | + array_unshift( $conv, array( $radius * 2 + 1, $radius * 2 + 1, $scale, 0 ) ); |
| 225 | + return $conv; |
| 226 | + } |
| 227 | + |
| 228 | + |
| 229 | + /** |
| 230 | + * Check the file and params against $wgVipsOptions |
| 231 | + * |
| 232 | + * @param BitmapHandler $handler |
| 233 | + * @param File $file |
| 234 | + * @param array $params |
| 235 | + * @return bool |
| 236 | + */ |
| 237 | + protected static function getHandlerOptions( $handler, $file, $params ) { |
| 238 | + global $wgVipsOptions; |
| 239 | + # Iterate over conditions |
| 240 | + foreach ( $wgVipsOptions as $option ) { |
| 241 | + if ( isset( $option['conditions'] ) ) { |
| 242 | + $condition = $option['conditions']; |
| 243 | + } else { |
| 244 | + # Unconditionally pass |
| 245 | + return $option; |
| 246 | + } |
| 247 | + |
| 248 | + if ( isset( $condition['mimeType'] ) && |
| 249 | + $file->getMimeType() != $condition['mimeType'] ) { |
| 250 | + continue; |
| 251 | + } |
| 252 | + |
| 253 | + $area = $handler->getImageArea( $file ); |
| 254 | + if ( isset( $condition['minArea'] ) && $area < $condition['minArea'] ) { |
| 255 | + continue; |
| 256 | + } |
| 257 | + if ( isset( $condition['maxArea'] ) && $area >= $condition['maxArea'] ) { |
| 258 | + continue; |
| 259 | + } |
| 260 | + |
| 261 | + $shrinkFactor = $file->getWidth() / ( |
| 262 | + ( ( $handler->getRotation( $file ) % 180 ) == 90 ) ? |
| 263 | + $params['physicalHeight'] : $params['physicalWidth'] ); |
| 264 | + if ( isset( $condition['minShrinkFactor'] ) && |
| 265 | + $shrinkFactor < $condition['minShrinkFactor'] ) { |
| 266 | + continue; |
| 267 | + } |
| 268 | + if ( isset( $condition['maxShrinkFactor'] ) && |
| 269 | + $shrinkFactor >= $condition['maxShrinkFactor'] ) { |
| 270 | + continue; |
| 271 | + } |
| 272 | + |
| 273 | + # This condition passed |
| 274 | + return $option; |
| 275 | + } |
| 276 | + # All conditions failed |
| 277 | + return false; |
| 278 | + } |
| 279 | + |
| 280 | + /** |
| 281 | + * Sets the JPEG comment on a file using exiv2. |
| 282 | + * Requires $wgExiv2Command to be setup properly. |
| 283 | + * |
| 284 | + * @todo FIXME need to handle errors such as $wgExiv2Command not available |
| 285 | + * |
| 286 | + * @param string $fileName File where the comment needs to be set |
| 287 | + * @param string $comment The comment |
| 288 | + */ |
| 289 | + public static function setJpegComment( $fileName, $comment ) { |
| 290 | + global $wgExiv2Command; |
| 291 | + |
| 292 | + wfShellExec( wfEscapeShellArg( $wgExiv2Command ) . ' mo -c ' |
| 293 | + . wfEscapeShellArg( $comment ) . ' ' |
| 294 | + . wfEscapeShellArg( $fileName ) |
| 295 | + ); |
| 296 | + } |
| 297 | + |
| 298 | + /** |
| 299 | + * Return the appropriate im_XXX2vips handler for this file |
| 300 | + * @param File $file |
| 301 | + * @return mixed String or false |
| 302 | + */ |
| 303 | + public static function getVipsHandler( $file ) { |
| 304 | + list( $major, $minor ) = File::splitMime( $file->getMimeType() ); |
| 305 | + |
| 306 | + if ( $major == 'image' && in_array( $minor, array( 'jpeg', 'png', 'tiff' ) ) ) { |
| 307 | + return "im_{$minor}2vips"; |
| 308 | + } else { |
| 309 | + return false; |
| 310 | + } |
| 311 | + } |
| 312 | + |
| 313 | + /** |
| 314 | + * Hook to BitmapHandlerCheckImageArea. Will set $result to true if the |
| 315 | + * file will by handled by VipsScaler. |
| 316 | + * |
| 317 | + * @param File $file |
| 318 | + * @param array &$params |
| 319 | + * @param mixed &$result |
| 320 | + * @return bool |
| 321 | + */ |
| 322 | + public static function onBitmapHandlerCheckImageArea( $file, &$params, &$result ) { |
| 323 | + global $wgMaxImageArea; |
| 324 | + if ( self::getHandlerOptions( $file->getHandler(), $file, $params ) !== false ) { |
| 325 | + wfDebug( __METHOD__ . ": Overriding $wgMaxImageArea\n" ); |
| 326 | + $result = true; |
| 327 | + return false; |
| 328 | + } |
| 329 | + return true; |
| 330 | + } |
| 331 | +} |
| 332 | + |
| 333 | +/** |
| 334 | + * Wrapper class around the vips command, useful to chain multiple commands |
| 335 | + * with intermediate .v files |
| 336 | + */ |
| 337 | +class VipsCommand { |
| 338 | + |
| 339 | + /** Flag to indicate that the output file should be a temporary .v file */ |
| 340 | + const TEMP_OUTPUT = true; |
| 341 | + /** |
| 342 | + * Constructor |
| 343 | + * |
| 344 | + * @param string $vips Path to binary |
| 345 | + * @param array $args Array or arguments |
| 346 | + */ |
| 347 | + public function __construct( $vips, $args ) { |
| 348 | + $this->vips = $vips; |
| 349 | + $this->args = $args; |
| 350 | + } |
| 351 | + /** |
| 352 | + * Set the input and output file of this command |
| 353 | + * |
| 354 | + * @param mixed $input Input file name or an VipsCommand object to use the |
| 355 | + * output of that command |
| 356 | + * @param string $output Output file name or extension of the temporary file |
| 357 | + * @param bool $tempOutput Output to a temporary file |
| 358 | + */ |
| 359 | + public function setIO( $input, $output, $tempOutput = false ) { |
| 360 | + if ( $input instanceof VipsCommand ) { |
| 361 | + $this->input = $input->getOutput(); |
| 362 | + $this->removeInput = true; |
| 363 | + } else { |
| 364 | + $this->input = $input; |
| 365 | + $this->removeInput = false; |
| 366 | + } |
| 367 | + if ( $tempOutput ) { |
| 368 | + $this->output = self::makeTemp( $output ); |
| 369 | + } else { |
| 370 | + $this->output = $output; |
| 371 | + } |
| 372 | + } |
| 373 | + /** |
| 374 | + * Returns the output filename |
| 375 | + * @return string |
| 376 | + */ |
| 377 | + public function getOutput() { |
| 378 | + return $this->output; |
| 379 | + } |
| 380 | + /** |
| 381 | + * Return the output of the command |
| 382 | + * @return string |
| 383 | + */ |
| 384 | + public function getErrorString() { |
| 385 | + return $this->err; |
| 386 | + } |
| 387 | + |
| 388 | + /** |
| 389 | + * Call the vips binary with varargs and returns the return value. |
| 390 | + * |
| 391 | + * @return int Return value |
| 392 | + */ |
| 393 | + public function execute() { |
| 394 | + # Build and escape the command string |
| 395 | + $cmd = wfEscapeShellArg( $this->vips, |
| 396 | + array_shift( $this->args ), |
| 397 | + $this->input, $this->output ); |
| 398 | + |
| 399 | + foreach ( $this->args as $arg ) { |
| 400 | + $cmd .= ' ' . wfEscapeShellArg( $arg ); |
| 401 | + } |
| 402 | + |
| 403 | + $cmd .= ' 2>&1'; |
| 404 | + |
| 405 | + # Execute |
| 406 | + $retval = 0; |
| 407 | + $this->err = wfShellExec( $cmd, $retval ); |
| 408 | + |
| 409 | + # Cleanup temp file |
| 410 | + if ( $this->removeInput ) { |
| 411 | + unlink( $this->input ); |
| 412 | + } |
| 413 | + |
| 414 | + return $retval; |
| 415 | + } |
| 416 | + |
| 417 | + /** |
| 418 | + * Generate a random, non-existent temporary file with a specified |
| 419 | + * extension. |
| 420 | + * |
| 421 | + * @param string Extension |
| 422 | + * @return string |
| 423 | + */ |
| 424 | + public static function makeTemp( $extension ) { |
| 425 | + do { |
| 426 | + # Generate a random file |
| 427 | + $fileName = wfTempDir() . DIRECTORY_SEPARATOR . |
| 428 | + dechex( mt_rand() ) . dechex( mt_rand() ) . |
| 429 | + '.' . $extension; |
| 430 | + } while ( file_exists( $fileName ) ); |
| 431 | + # Create the file |
| 432 | + touch( $fileName ); |
| 433 | + |
| 434 | + return $fileName; |
| 435 | + } |
| 436 | + |
| 437 | +} |
| 438 | + |
| 439 | +/** |
| 440 | + * A wrapper class around im_conv because that command expects a a convolution |
| 441 | + * matrix file as its last argument |
| 442 | + */ |
| 443 | +class VipsConvolution extends VipsCommand { |
| 444 | + public function execute() { |
| 445 | + # Convert a 2D array into a space/newline separated matrix |
| 446 | + $convolutionMatrix = array_pop( $this->args ); |
| 447 | + $convolutionString = ''; |
| 448 | + foreach ( $convolutionMatrix as $i=>$row ) { |
| 449 | + $convolutionString .= implode( ' ', $row ) . "\n"; |
| 450 | + } |
| 451 | + # Save the matrix in a tempfile |
| 452 | + $convolutionFile = self::makeTemp( 'conv' ); |
| 453 | + file_put_contents( $convolutionFile, $convolutionString ); |
| 454 | + array_push( $this->args, $convolutionFile ); |
| 455 | + |
| 456 | + wfDebug( __METHOD__ . ": Convolving image [\n" . $convolutionString . "] \n" ); |
| 457 | + |
| 458 | + # Call the parent to actually execute the command |
| 459 | + $retval = parent::execute(); |
| 460 | + |
| 461 | + # Remove the temporary matrix file |
| 462 | + unlink( $convolutionFile ); |
| 463 | + |
| 464 | + return $retval; |
| 465 | + } |
| 466 | +} |
Property changes on: branches/wmf/1.18wmf1/extensions/VipsScaler/VipsScaler_body.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 467 | + native |