Index: branches/img_metadata/phase3/includes/Exif.php |
— | — | @@ -95,6 +95,13 @@ |
96 | 96 | * Constructor |
97 | 97 | * |
98 | 98 | * @param $file String: filename. |
| 99 | + * @fixme the following are broke: |
| 100 | + * SubjectArea, ExifVersion, Flashpix Version (probably any Exif:Undefined with non 1 count) |
| 101 | + * SceneType, FileSource, MakerNote (we really don't want makerNote though), |
| 102 | + * ComponenentsConfiguration, and possibly others pending better testing. |
| 103 | + * |
| 104 | + * DigitalZoomRatio = 0/0 is rejected. need to determine if thats valid. |
| 105 | + * possibly should treat 0/0 = 0. need to read exif spec on that. |
99 | 106 | */ |
100 | 107 | function __construct( $file ) { |
101 | 108 | /** |
— | — | @@ -211,7 +218,7 @@ |
212 | 219 | 'DigitalZoomRatio' => Exif::RATIONAL, # Digital zoom ration |
213 | 220 | 'FocalLengthIn35mmFilm' => Exif::SHORT, # Focal length in 35 mm film |
214 | 221 | 'SceneCaptureType' => Exif::SHORT, # Scene capture type #p49 |
215 | | - 'GainControl' => Exif::RATIONAL, # Scene control #p49-50 |
| 222 | + 'GainControl' => Exif::SHORT, # Scene control #p49-50 |
216 | 223 | 'Contrast' => Exif::SHORT, # Contrast #p50 |
217 | 224 | 'Saturation' => Exif::SHORT, # Saturation #p50 |
218 | 225 | 'Sharpness' => Exif::SHORT, # Sharpness #p50 |
— | — | @@ -271,6 +278,7 @@ |
272 | 279 | */ |
273 | 280 | $this->mRawExifData = $data ? $data : array(); |
274 | 281 | $this->makeFilteredData(); |
| 282 | + $this->collapseData(); |
275 | 283 | $this->makeFormattedData(); |
276 | 284 | $this->debugFile( __FUNCTION__, false ); |
277 | 285 | } |
— | — | @@ -279,31 +287,101 @@ |
280 | 288 | * Make $this->mFilteredExifData |
281 | 289 | */ |
282 | 290 | function makeFilteredData() { |
283 | | - $this->mFilteredExifData = $this->mRawExifData; |
| 291 | + $this->mFilteredExifData = Array(); |
284 | 292 | |
285 | | - foreach( array_keys( $this->mFilteredExifData ) as $section ) { |
| 293 | + foreach ( array_keys( $this->mRawExifData ) as $section ) { |
286 | 294 | if ( !in_array( $section, array_keys( $this->mExifTags ) ) ) { |
287 | 295 | $this->debug( $section , __FUNCTION__, "'$section' is not a valid Exif section" ); |
288 | | - unset( $this->mFilteredExifData[$section] ); |
289 | 296 | continue; |
290 | 297 | } |
291 | 298 | |
292 | | - foreach( array_keys( $this->mFilteredExifData[$section] ) as $tag ) { |
| 299 | + foreach ( array_keys( $this->mRawExifData[$section] ) as $tag ) { |
293 | 300 | if ( !in_array( $tag, array_keys( $this->mExifTags[$section] ) ) ) { |
294 | 301 | $this->debug( $tag, __FUNCTION__, "'$tag' is not a valid tag in '$section'" ); |
295 | | - unset( $this->mFilteredExifData[$section][$tag] ); |
296 | 302 | continue; |
297 | 303 | } |
298 | | - $value = $this->mFilteredExifData[$section][$tag]; |
299 | | - if( !$this->validate( $section, $tag, $value ) ) { |
| 304 | + |
| 305 | + $this->mFilteredExifData[$tag] = $this->mRawExifData[$section][$tag]; |
| 306 | + // This is ok, as the tags in the different sections do not conflict. |
| 307 | + // except in computed and thumbnail section, which we don't use. |
| 308 | + |
| 309 | + $value = $this->mRawExifData[$section][$tag]; |
| 310 | + if ( !$this->validate( $section, $tag, $value ) ) { |
300 | 311 | $this->debug( $value, __FUNCTION__, "'$tag' contained invalid data" ); |
301 | | - unset( $this->mFilteredExifData[$section][$tag] ); |
| 312 | + unset( $this->mFilteredExifData[$tag] ); |
302 | 313 | } |
303 | 314 | } |
304 | 315 | } |
305 | 316 | } |
306 | 317 | |
307 | 318 | /** |
| 319 | + * Collapse some fields together. |
| 320 | + * This converts some fields from exif form, to a more friendly form. |
| 321 | + * For example GPS lattitude to a single number. |
| 322 | + * |
| 323 | + * The rationale behind this is that we're storing data, not presenting to the user |
| 324 | + * For example a longitude is a single number describing how far away you are from |
| 325 | + * the prime meridian. Well it might be nice to split it up into minutes and seconds |
| 326 | + * for the user, it doesn't really make sense to split a single number into 4 parts |
| 327 | + * for storage. (degrees, minutes, second, direction vs single floating point number). |
| 328 | + * |
| 329 | + * Other things this might do (not really sure if they make sense or not): |
| 330 | + * Dates -> mediawiki date format. |
| 331 | + * convert values that can be in different units to be in one standardized unit. |
| 332 | + * |
| 333 | + * As an alternative approach, some of this could be done in the validate phase |
| 334 | + * if we make up our own types like Exif::DATE. |
| 335 | + */ |
| 336 | + function collapseData( ) { |
| 337 | + |
| 338 | + $this->exifGPStoNumber( 'GPSLatitude' ); |
| 339 | + $this->exifGPStoNumber( 'GPSDestLatitude' ); |
| 340 | + $this->exifGPStoNumber( 'GPSLongitude' ); |
| 341 | + $this->exifGPStoNumber( 'GPSDestLongitude' ); |
| 342 | + |
| 343 | + if ( isset( $this->mFilteredExifData['GPSAltitude'] ) && isset( $this->mFilteredExifData['GPSAltitudeRef'] ) ) { |
| 344 | + if ( $this->mFilteredExifData['GPSAltitudeRef'] === 1 ) { |
| 345 | + $this->mFilteredExifData['GPSAltitude'] *= - 1; |
| 346 | + } |
| 347 | + unset( $this->mFilteredExifData ); |
| 348 | + } |
| 349 | + |
| 350 | + } |
| 351 | + /** |
| 352 | + * Convert gps in exif form to a single floating point number |
| 353 | + * for example 10 degress 20`40`` S -> -10.34444 |
| 354 | + * @param String $prop a gps coordinate exif tag name (like GPSLongitude) |
| 355 | + */ |
| 356 | + function exifGPStoNumber ( $prop ) { |
| 357 | + $loc =& $this->mFilteredExifData[$prop]; |
| 358 | + $dir =& $this->mFilteredExifData[$prop . 'Ref']; |
| 359 | + $res = false; |
| 360 | + |
| 361 | + if ( isset( $loc ) && isset( $dir ) && ( $dir === 'N' || $dir === 'S' || $dir === 'E' || $dir === 'W' ) ) { |
| 362 | + list( $num, $denom ) = explode( '/', $loc[0] ); |
| 363 | + $res = $num / $denom; |
| 364 | + list( $num, $denom ) = explode( '/', $loc[1] ); |
| 365 | + $res += ( $num / $denom ) * ( 1 / 60 ); |
| 366 | + list( $num, $denom ) = explode( '/', $loc[2] ); |
| 367 | + $res += ( $num / $denom ) * ( 1 / 3600 ); |
| 368 | + |
| 369 | + if ( $dir === 'S' || $dir === 'W' ) { |
| 370 | + $res *= - 1; // make negative |
| 371 | + } |
| 372 | + } |
| 373 | + |
| 374 | + // update the exif records. |
| 375 | + |
| 376 | + if ( $res !== false ) { // using !== as $res could potentially be 0 |
| 377 | + $this->mFilteredExifData[$prop] = $res; |
| 378 | + unset( $this->mFilteredExifData[$prop . 'Ref'] ); |
| 379 | + } else { // if invalid |
| 380 | + unset( $this->mFilteredExifData[$prop] ); |
| 381 | + unset( $this->mFilteredExifData[$prop . 'Ref'] ); |
| 382 | + } |
| 383 | + } |
| 384 | + |
| 385 | + /** |
308 | 386 | * @todo document |
309 | 387 | */ |
310 | 388 | function makeFormattedData( ) { |
— | — | @@ -601,468 +679,546 @@ |
602 | 680 | function getFormattedData() { |
603 | 681 | global $wgLang; |
604 | 682 | |
605 | | - $sections =& $this->mExif; |
| 683 | + $tags =& $this->mExif; |
606 | 684 | |
607 | | - $resolutionunit = !isset( $sections['IFD0']['ResolutionUnit'] ) || $sections['IFD0']['ResolutionUnit'] == 2 ? 2 : 3; |
608 | | - unset( $sections['IFD0']['ResolutionUnit'] ); |
| 685 | + $resolutionunit = !isset( $tags['ResolutionUnit'] ) || $tags['ResolutionUnit'] == 2 ? 2 : 3; |
| 686 | + unset( $tags['ResolutionUnit'] ); |
609 | 687 | |
610 | | - foreach( $sections as $section => &$tags ) { |
611 | | - foreach( $tags as $tag => $val ) { |
612 | | - switch( $tag ) { |
613 | | - case 'Compression': |
614 | | - switch( $val ) { |
615 | | - case 1: case 6: |
616 | | - $tags[$tag] = $this->msg( $tag, $val ); |
617 | | - break; |
618 | | - default: |
619 | | - $tags[$tag] = $val; |
620 | | - break; |
621 | | - } |
622 | | - break; |
| 688 | + foreach ( $tags as $tag => &$vals ) { |
623 | 689 | |
624 | | - case 'PhotometricInterpretation': |
625 | | - switch( $val ) { |
626 | | - case 2: case 6: |
627 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 690 | + // This seems ugly to wrap non-array's in an array just to unwrap again, |
| 691 | + // especially when most of the time it is not an array |
| 692 | + if ( !is_array( $tags[$tag] ) ) { |
| 693 | + $vals = Array( $vals ); |
| 694 | + } |
| 695 | + |
| 696 | + // _type is a special value to say what array type |
| 697 | + if ( isset( $tags[$tag]['_type'] ) ) { |
| 698 | + $type = $tags[$tag]['_type']; |
| 699 | + unset( $vals['_type'] ); |
| 700 | + } else { |
| 701 | + $type = 'ul'; // default unorcdered list. |
| 702 | + } |
| 703 | + |
| 704 | + foreach ( $vals as &$val ) { |
| 705 | + |
| 706 | + switch( $tag ) { |
| 707 | + case 'Compression': |
| 708 | + switch( $val ) { |
| 709 | + case 1: case 6: |
| 710 | + $val = $this->msg( $tag, $val ); |
| 711 | + break; |
| 712 | + default: |
| 713 | + $val = $val; |
| 714 | + break; |
| 715 | + } |
628 | 716 | break; |
629 | | - default: |
630 | | - $tags[$tag] = $val; |
631 | | - break; |
632 | | - } |
633 | | - break; |
634 | 717 | |
635 | | - case 'Orientation': |
636 | | - switch( $val ) { |
637 | | - case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: |
638 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 718 | + case 'PhotometricInterpretation': |
| 719 | + switch( $val ) { |
| 720 | + case 2: case 6: |
| 721 | + $val = $this->msg( $tag, $val ); |
| 722 | + break; |
| 723 | + default: |
| 724 | + $val = $val; |
| 725 | + break; |
| 726 | + } |
639 | 727 | break; |
640 | | - default: |
641 | | - $tags[$tag] = $val; |
642 | | - break; |
643 | | - } |
644 | | - break; |
645 | 728 | |
646 | | - case 'PlanarConfiguration': |
647 | | - switch( $val ) { |
648 | | - case 1: case 2: |
649 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 729 | + case 'Orientation': |
| 730 | + switch( $val ) { |
| 731 | + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: |
| 732 | + $val = $this->msg( $tag, $val ); |
| 733 | + break; |
| 734 | + default: |
| 735 | + $val = $val; |
| 736 | + break; |
| 737 | + } |
650 | 738 | break; |
651 | | - default: |
652 | | - $tags[$tag] = $val; |
653 | | - break; |
654 | | - } |
655 | | - break; |
656 | 739 | |
657 | | - // TODO: YCbCrSubSampling |
658 | | - // TODO: YCbCrPositioning |
659 | | - |
660 | | - case 'XResolution': |
661 | | - case 'YResolution': |
662 | | - switch( $resolutionunit ) { |
663 | | - case 2: |
664 | | - $tags[$tag] = $this->msg( 'XYResolution', 'i', $this->formatNum( $val ) ); |
| 740 | + case 'PlanarConfiguration': |
| 741 | + switch( $val ) { |
| 742 | + case 1: case 2: |
| 743 | + $val = $this->msg( $tag, $val ); |
665 | 744 | break; |
666 | | - case 3: |
667 | | - $this->msg( 'XYResolution', 'c', $this->formatNum( $val ) ); |
668 | | - break; |
669 | 745 | default: |
670 | | - $tags[$tag] = $val; |
| 746 | + $val = $val; |
671 | 747 | break; |
672 | | - } |
673 | | - break; |
| 748 | + } |
| 749 | + break; |
674 | 750 | |
675 | | - // TODO: YCbCrCoefficients #p27 (see annex E) |
676 | | - case 'ExifVersion': case 'FlashPixVersion': |
677 | | - $tags[$tag] = "$val"/100; |
678 | | - break; |
| 751 | + // TODO: YCbCrSubSampling |
| 752 | + // TODO: YCbCrPositioning |
679 | 753 | |
680 | | - case 'ColorSpace': |
681 | | - switch( $val ) { |
682 | | - case 1: case 'FFFF.H': |
683 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 754 | + case 'XResolution': |
| 755 | + case 'YResolution': |
| 756 | + switch( $resolutionunit ) { |
| 757 | + case 2: |
| 758 | + $val = $this->msg( 'XYResolution', 'i', $this->formatNum( $val ) ); |
| 759 | + break; |
| 760 | + case 3: |
| 761 | + $this->msg( 'XYResolution', 'c', $this->formatNum( $val ) ); |
| 762 | + break; |
| 763 | + default: |
| 764 | + $val = $val; |
| 765 | + break; |
| 766 | + } |
684 | 767 | break; |
685 | | - default: |
686 | | - $tags[$tag] = $val; |
| 768 | + |
| 769 | + // TODO: YCbCrCoefficients #p27 (see annex E) |
| 770 | + case 'ExifVersion': case 'FlashpixVersion': |
| 771 | + $val = "$val" / 100; |
687 | 772 | break; |
688 | | - } |
689 | | - break; |
690 | 773 | |
691 | | - case 'ComponentsConfiguration': |
692 | | - switch( $val ) { |
693 | | - case 0: case 1: case 2: case 3: case 4: case 5: case 6: |
694 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 774 | + case 'ColorSpace': |
| 775 | + switch( $val ) { |
| 776 | + case 1: case 'FFFF.H': |
| 777 | + $val = $this->msg( $tag, $val ); |
| 778 | + break; |
| 779 | + default: |
| 780 | + $val = $val; |
| 781 | + break; |
| 782 | + } |
695 | 783 | break; |
696 | | - default: |
697 | | - $tags[$tag] = $val; |
| 784 | + |
| 785 | + case 'ComponentsConfiguration': |
| 786 | + switch( $val ) { |
| 787 | + case 0: case 1: case 2: case 3: case 4: case 5: case 6: |
| 788 | + $val = $this->msg( $tag, $val ); |
| 789 | + break; |
| 790 | + default: |
| 791 | + $val = $val; |
| 792 | + break; |
| 793 | + } |
698 | 794 | break; |
699 | | - } |
700 | | - break; |
701 | 795 | |
702 | | - case 'DateTime': |
703 | | - case 'DateTimeOriginal': |
704 | | - case 'DateTimeDigitized': |
705 | | - if( $val == '0000:00:00 00:00:00' ) { |
706 | | - $tags[$tag] = wfMsg('exif-unknowndate'); |
707 | | - } elseif( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d):(?:\d\d)$/', $val ) ) { |
708 | | - $tags[$tag] = $wgLang->timeanddate( wfTimestamp(TS_MW, $val) ); |
709 | | - } |
710 | | - break; |
| 796 | + case 'DateTime': |
| 797 | + case 'DateTimeOriginal': |
| 798 | + case 'DateTimeDigitized': |
| 799 | + if ( $val == '0000:00:00 00:00:00' ) { |
| 800 | + $val = wfMsg( 'exif-unknowndate' ); |
| 801 | + } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d):(?:\d\d)$/', $val ) ) { |
| 802 | + $val = $wgLang->timeanddate( wfTimestamp( TS_MW, $val ) ); |
| 803 | + } |
| 804 | + break; |
711 | 805 | |
712 | | - case 'ExposureProgram': |
713 | | - switch( $val ) { |
714 | | - case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: |
715 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 806 | + case 'ExposureProgram': |
| 807 | + switch( $val ) { |
| 808 | + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: |
| 809 | + $val = $this->msg( $tag, $val ); |
| 810 | + break; |
| 811 | + default: |
| 812 | + $val = $val; |
| 813 | + break; |
| 814 | + } |
716 | 815 | break; |
717 | | - default: |
718 | | - $tags[$tag] = $val; |
| 816 | + |
| 817 | + case 'SubjectDistance': |
| 818 | + $val = $this->msg( $tag, '', $this->formatNum( $val ) ); |
719 | 819 | break; |
720 | | - } |
721 | | - break; |
722 | 820 | |
723 | | - case 'SubjectDistance': |
724 | | - $tags[$tag] = $this->msg( $tag, '', $this->formatNum( $val ) ); |
725 | | - break; |
| 821 | + case 'MeteringMode': |
| 822 | + switch( $val ) { |
| 823 | + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 255: |
| 824 | + $val = $this->msg( $tag, $val ); |
| 825 | + break; |
| 826 | + default: |
| 827 | + $val = $val; |
| 828 | + break; |
| 829 | + } |
| 830 | + break; |
726 | 831 | |
727 | | - case 'MeteringMode': |
728 | | - switch( $val ) { |
729 | | - case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 255: |
730 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 832 | + case 'LightSource': |
| 833 | + switch( $val ) { |
| 834 | + case 0: case 1: case 2: case 3: case 4: case 9: case 10: case 11: |
| 835 | + case 12: case 13: case 14: case 15: case 17: case 18: case 19: case 20: |
| 836 | + case 21: case 22: case 23: case 24: case 255: |
| 837 | + $val = $this->msg( $tag, $val ); |
| 838 | + break; |
| 839 | + default: |
| 840 | + $val = $val; |
| 841 | + break; |
| 842 | + } |
731 | 843 | break; |
732 | | - default: |
733 | | - $tags[$tag] = $val; |
| 844 | + |
| 845 | + case 'Flash': |
| 846 | + $flashDecode = array( |
| 847 | + 'fired' => $val & bindec( '00000001' ), |
| 848 | + 'return' => ( $val & bindec( '00000110' ) ) >> 1, |
| 849 | + 'mode' => ( $val & bindec( '00011000' ) ) >> 3, |
| 850 | + 'function' => ( $val & bindec( '00100000' ) ) >> 5, |
| 851 | + 'redeye' => ( $val & bindec( '01000000' ) ) >> 6, |
| 852 | +// 'reserved' => ($val & bindec( '10000000' )) >> 7, |
| 853 | + ); |
| 854 | + |
| 855 | + # We do not need to handle unknown values since all are used. |
| 856 | + foreach ( $flashDecode as $subTag => $subValue ) { |
| 857 | + # We do not need any message for zeroed values. |
| 858 | + if ( $subTag != 'fired' && $subValue == 0 ) { |
| 859 | + continue; |
| 860 | + } |
| 861 | + $fullTag = $tag . '-' . $subTag ; |
| 862 | + $flashMsgs[] = $this->msg( $fullTag, $subValue ); |
| 863 | + } |
| 864 | + $val = $wgLang->commaList( $flashMsgs ); |
734 | 865 | break; |
735 | | - } |
736 | | - break; |
737 | 866 | |
738 | | - case 'LightSource': |
739 | | - switch( $val ) { |
740 | | - case 0: case 1: case 2: case 3: case 4: case 9: case 10: case 11: |
741 | | - case 12: case 13: case 14: case 15: case 17: case 18: case 19: case 20: |
742 | | - case 21: case 22: case 23: case 24: case 255: |
743 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 867 | + case 'FocalPlaneResolutionUnit': |
| 868 | + switch( $val ) { |
| 869 | + case 2: |
| 870 | + $val = $this->msg( $tag, $val ); |
| 871 | + break; |
| 872 | + default: |
| 873 | + $val = $val; |
| 874 | + break; |
| 875 | + } |
744 | 876 | break; |
745 | | - default: |
746 | | - $tags[$tag] = $val; |
| 877 | + |
| 878 | + case 'SensingMethod': |
| 879 | + switch( $val ) { |
| 880 | + case 1: case 2: case 3: case 4: case 5: case 7: case 8: |
| 881 | + $val = $this->msg( $tag, $val ); |
| 882 | + break; |
| 883 | + default: |
| 884 | + $val = $val; |
| 885 | + break; |
| 886 | + } |
747 | 887 | break; |
748 | | - } |
749 | | - break; |
750 | 888 | |
751 | | - case 'Flash': |
752 | | - $flashDecode = array( |
753 | | - 'fired' => $val & bindec( '00000001' ), |
754 | | - 'return' => ($val & bindec( '00000110' )) >> 1, |
755 | | - 'mode' => ($val & bindec( '00011000' )) >> 3, |
756 | | - 'function' => ($val & bindec( '00100000' )) >> 5, |
757 | | - 'redeye' => ($val & bindec( '01000000' )) >> 6, |
758 | | -// 'reserved' => ($val & bindec( '10000000' )) >> 7, |
759 | | - ); |
| 889 | + case 'FileSource': |
| 890 | + switch( $val ) { |
| 891 | + case 3: |
| 892 | + $val = $this->msg( $tag, $val ); |
| 893 | + break; |
| 894 | + default: |
| 895 | + $val = $val; |
| 896 | + break; |
| 897 | + } |
| 898 | + break; |
760 | 899 | |
761 | | - # We do not need to handle unknown values since all are used. |
762 | | - foreach( $flashDecode as $subTag => $subValue ) { |
763 | | - # We do not need any message for zeroed values. |
764 | | - if( $subTag != 'fired' && $subValue == 0) { |
765 | | - continue; |
| 900 | + case 'SceneType': |
| 901 | + switch( $val ) { |
| 902 | + case 1: |
| 903 | + $val = $this->msg( $tag, $val ); |
| 904 | + break; |
| 905 | + default: |
| 906 | + $val = $val; |
| 907 | + break; |
766 | 908 | } |
767 | | - $fullTag = $tag . '-' . $subTag ; |
768 | | - $flashMsgs[] = $this->msg( $fullTag, $subValue ); |
769 | | - } |
770 | | - $tags[$tag] = $wgLang->commaList( $flashMsgs ); |
771 | | - break; |
| 909 | + break; |
772 | 910 | |
773 | | - case 'FocalPlaneResolutionUnit': |
774 | | - switch( $val ) { |
775 | | - case 2: |
776 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 911 | + case 'CustomRendered': |
| 912 | + switch( $val ) { |
| 913 | + case 0: case 1: |
| 914 | + $val = $this->msg( $tag, $val ); |
| 915 | + break; |
| 916 | + default: |
| 917 | + $val = $val; |
| 918 | + break; |
| 919 | + } |
777 | 920 | break; |
778 | | - default: |
779 | | - $tags[$tag] = $val; |
| 921 | + |
| 922 | + case 'ExposureMode': |
| 923 | + switch( $val ) { |
| 924 | + case 0: case 1: case 2: |
| 925 | + $val = $this->msg( $tag, $val ); |
| 926 | + break; |
| 927 | + default: |
| 928 | + $val = $val; |
| 929 | + break; |
| 930 | + } |
780 | 931 | break; |
781 | | - } |
782 | | - break; |
783 | 932 | |
784 | | - case 'SensingMethod': |
785 | | - switch( $val ) { |
786 | | - case 1: case 2: case 3: case 4: case 5: case 7: case 8: |
787 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 933 | + case 'WhiteBalance': |
| 934 | + switch( $val ) { |
| 935 | + case 0: case 1: |
| 936 | + $val = $this->msg( $tag, $val ); |
| 937 | + break; |
| 938 | + default: |
| 939 | + $val = $val; |
| 940 | + break; |
| 941 | + } |
788 | 942 | break; |
789 | | - default: |
790 | | - $tags[$tag] = $val; |
| 943 | + |
| 944 | + case 'SceneCaptureType': |
| 945 | + switch( $val ) { |
| 946 | + case 0: case 1: case 2: case 3: |
| 947 | + $val = $this->msg( $tag, $val ); |
| 948 | + break; |
| 949 | + default: |
| 950 | + $val = $val; |
| 951 | + break; |
| 952 | + } |
791 | 953 | break; |
792 | | - } |
793 | | - break; |
794 | 954 | |
795 | | - case 'FileSource': |
796 | | - switch( $val ) { |
797 | | - case 3: |
798 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 955 | + case 'GainControl': |
| 956 | + switch( $val ) { |
| 957 | + case 0: case 1: case 2: case 3: case 4: |
| 958 | + $val = $this->msg( $tag, $val ); |
| 959 | + break; |
| 960 | + default: |
| 961 | + $val = $val; |
| 962 | + break; |
| 963 | + } |
799 | 964 | break; |
800 | | - default: |
801 | | - $tags[$tag] = $val; |
| 965 | + |
| 966 | + case 'Contrast': |
| 967 | + switch( $val ) { |
| 968 | + case 0: case 1: case 2: |
| 969 | + $val = $this->msg( $tag, $val ); |
| 970 | + break; |
| 971 | + default: |
| 972 | + $val = $val; |
| 973 | + break; |
| 974 | + } |
802 | 975 | break; |
803 | | - } |
804 | | - break; |
805 | 976 | |
806 | | - case 'SceneType': |
807 | | - switch( $val ) { |
808 | | - case 1: |
809 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 977 | + case 'Saturation': |
| 978 | + switch( $val ) { |
| 979 | + case 0: case 1: case 2: |
| 980 | + $val = $this->msg( $tag, $val ); |
| 981 | + break; |
| 982 | + default: |
| 983 | + $val = $val; |
| 984 | + break; |
| 985 | + } |
810 | 986 | break; |
811 | | - default: |
812 | | - $tags[$tag] = $val; |
813 | | - break; |
814 | | - } |
815 | | - break; |
816 | 987 | |
817 | | - case 'CustomRendered': |
818 | | - switch( $val ) { |
819 | | - case 0: case 1: |
820 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 988 | + case 'Sharpness': |
| 989 | + switch( $val ) { |
| 990 | + case 0: case 1: case 2: |
| 991 | + $val = $this->msg( $tag, $val ); |
| 992 | + break; |
| 993 | + default: |
| 994 | + $val = $val; |
| 995 | + break; |
| 996 | + } |
821 | 997 | break; |
822 | | - default: |
823 | | - $tags[$tag] = $val; |
824 | | - break; |
825 | | - } |
826 | | - break; |
827 | 998 | |
828 | | - case 'ExposureMode': |
829 | | - switch( $val ) { |
830 | | - case 0: case 1: case 2: |
831 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 999 | + case 'SubjectDistanceRange': |
| 1000 | + switch( $val ) { |
| 1001 | + case 0: case 1: case 2: case 3: |
| 1002 | + $val = $this->msg( $tag, $val ); |
| 1003 | + break; |
| 1004 | + default: |
| 1005 | + $val = $val; |
| 1006 | + break; |
| 1007 | + } |
832 | 1008 | break; |
833 | | - default: |
834 | | - $tags[$tag] = $val; |
835 | | - break; |
836 | | - } |
837 | | - break; |
838 | 1009 | |
839 | | - case 'WhiteBalance': |
840 | | - switch( $val ) { |
841 | | - case 0: case 1: |
842 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 1010 | + //The GPS...Ref values are kept for compatability, probably won't be reached. |
| 1011 | + case 'GPSLatitudeRef': |
| 1012 | + case 'GPSDestLatitudeRef': |
| 1013 | + switch( $val ) { |
| 1014 | + case 'N': case 'S': |
| 1015 | + $val = $this->msg( 'GPSLatitude', $val ); |
| 1016 | + break; |
| 1017 | + default: |
| 1018 | + $val = $val; |
| 1019 | + break; |
| 1020 | + } |
843 | 1021 | break; |
844 | | - default: |
845 | | - $tags[$tag] = $val; |
846 | | - break; |
847 | | - } |
848 | | - break; |
849 | 1022 | |
850 | | - case 'SceneCaptureType': |
851 | | - switch( $val ) { |
852 | | - case 0: case 1: case 2: case 3: |
853 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 1023 | + case 'GPSLongitudeRef': |
| 1024 | + case 'GPSDestLongitudeRef': |
| 1025 | + switch( $val ) { |
| 1026 | + case 'E': case 'W': |
| 1027 | + $val = $this->msg( 'GPSLongitude', $val ); |
| 1028 | + break; |
| 1029 | + default: |
| 1030 | + $val = $val; |
| 1031 | + break; |
| 1032 | + } |
854 | 1033 | break; |
855 | | - default: |
856 | | - $tags[$tag] = $val; |
857 | | - break; |
858 | | - } |
859 | | - break; |
860 | 1034 | |
861 | | - case 'GainControl': |
862 | | - switch( $val ) { |
863 | | - case 0: case 1: case 2: case 3: case 4: |
864 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 1035 | + case 'GPSStatus': |
| 1036 | + switch( $val ) { |
| 1037 | + case 'A': case 'V': |
| 1038 | + $val = $this->msg( $tag, $val ); |
| 1039 | + break; |
| 1040 | + default: |
| 1041 | + $val = $val; |
| 1042 | + break; |
| 1043 | + } |
865 | 1044 | break; |
866 | | - default: |
867 | | - $tags[$tag] = $val; |
868 | | - break; |
869 | | - } |
870 | | - break; |
871 | 1045 | |
872 | | - case 'Contrast': |
873 | | - switch( $val ) { |
874 | | - case 0: case 1: case 2: |
875 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 1046 | + case 'GPSMeasureMode': |
| 1047 | + switch( $val ) { |
| 1048 | + case 2: case 3: |
| 1049 | + $val = $this->msg( $tag, $val ); |
| 1050 | + break; |
| 1051 | + default: |
| 1052 | + $val = $val; |
| 1053 | + break; |
| 1054 | + } |
876 | 1055 | break; |
877 | | - default: |
878 | | - $tags[$tag] = $val; |
879 | | - break; |
880 | | - } |
881 | | - break; |
882 | 1056 | |
883 | | - case 'Saturation': |
884 | | - switch( $val ) { |
885 | | - case 0: case 1: case 2: |
886 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 1057 | + |
| 1058 | + case 'GPSTrackRef': |
| 1059 | + case 'GPSImgDirectionRef': |
| 1060 | + case 'GPSDestBearingRef': |
| 1061 | + switch( $val ) { |
| 1062 | + case 'T': case 'M': |
| 1063 | + $val = $this->msg( 'GPSDirection', $val ); |
| 1064 | + break; |
| 1065 | + default: |
| 1066 | + $val = $val; |
| 1067 | + break; |
| 1068 | + } |
887 | 1069 | break; |
888 | | - default: |
889 | | - $tags[$tag] = $val; |
890 | | - break; |
891 | | - } |
892 | | - break; |
893 | 1070 | |
894 | | - case 'Sharpness': |
895 | | - switch( $val ) { |
896 | | - case 0: case 1: case 2: |
897 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 1071 | + case 'GPSDateStamp': |
| 1072 | + $val = $wgLang->date( substr( $val, 0, 4 ) . substr( $val, 5, 2 ) . substr( $val, 8, 2 ) . '000000' ); |
898 | 1073 | break; |
899 | | - default: |
900 | | - $tags[$tag] = $val; |
901 | | - break; |
902 | | - } |
903 | | - break; |
904 | 1074 | |
905 | | - case 'SubjectDistanceRange': |
906 | | - switch( $val ) { |
907 | | - case 0: case 1: case 2: case 3: |
908 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 1075 | + case 'GPSAltitudeRef': |
| 1076 | + switch( $val ) { |
| 1077 | + case 0: case 1: |
| 1078 | + $tags[$tag] = $this->msg( 'GPSAltitude', $val ); |
| 1079 | + break; |
| 1080 | + default: |
| 1081 | + $tags[$tag] = $val; |
| 1082 | + break; |
| 1083 | + } |
909 | 1084 | break; |
910 | | - default: |
911 | | - $tags[$tag] = $val; |
912 | | - break; |
913 | | - } |
914 | | - break; |
915 | 1085 | |
916 | | - case 'GPSLatitudeRef': |
917 | | - case 'GPSDestLatitudeRef': |
918 | | - switch( $val ) { |
919 | | - case 'N': case 'S': |
920 | | - $tags[$tag] = $this->msg( 'GPSLatitude', $val ); |
| 1086 | + case 'GPSLatitude': |
| 1087 | + case 'GPSDestLatitude': |
| 1088 | + $val = $this->formatCoords( $val, 'latitude' ); |
921 | 1089 | break; |
922 | | - default: |
923 | | - $tags[$tag] = $val; |
| 1090 | + case 'GPSLongitude': |
| 1091 | + case 'GPSDestLongitude': |
| 1092 | + $val = $this->formatCoords( $val, 'longitude' ); |
924 | 1093 | break; |
925 | | - } |
926 | | - break; |
927 | 1094 | |
928 | | - case 'GPSLongitudeRef': |
929 | | - case 'GPSDestLongitudeRef': |
930 | | - switch( $val ) { |
931 | | - case 'E': case 'W': |
932 | | - $tags[$tag] = $this->msg( 'GPSLongitude', $val ); |
| 1095 | + case 'GPSSpeedRef': |
| 1096 | + switch( $val ) { |
| 1097 | + case 'K': case 'M': case 'N': |
| 1098 | + $tags[$tag] = $this->msg( 'GPSSpeed', $val ); |
| 1099 | + break; |
| 1100 | + default: |
| 1101 | + $tags[$tag] = $val; |
| 1102 | + break; |
| 1103 | + } |
933 | 1104 | break; |
934 | | - default: |
935 | | - $tags[$tag] = $val; |
936 | | - break; |
937 | | - } |
938 | | - break; |
939 | 1105 | |
940 | | - case 'GPSAltitudeRef': |
941 | | - switch( $val ) { |
942 | | - case 0: case 1: |
943 | | - $tags[$tag] = $this->msg( 'GPSAltitude', $val ); |
| 1106 | + case 'GPSDestDistanceRef': |
| 1107 | + switch( $val ) { |
| 1108 | + case 'K': case 'M': case 'N': |
| 1109 | + $tags[$tag] = $this->msg( 'GPSDestDistance', $val ); |
| 1110 | + break; |
| 1111 | + default: |
| 1112 | + $tags[$tag] = $val; |
| 1113 | + break; |
| 1114 | + } |
944 | 1115 | break; |
945 | | - default: |
946 | | - $tags[$tag] = $val; |
947 | | - break; |
948 | | - } |
949 | | - break; |
950 | 1116 | |
951 | | - case 'GPSLatitude': |
952 | | - case 'GPSDestLatitude': |
953 | | - case 'GPSLongitude': |
954 | | - case 'GPSDestLongitude': |
955 | | - $tags[$tag] = $this->formatCoords( $val ); |
956 | | - break; |
957 | 1117 | |
958 | | - case 'GPSStatus': |
959 | | - switch( $val ) { |
960 | | - case 'A': case 'V': |
961 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 1118 | + // This is not in the Exif standard, just a special |
| 1119 | + // case for our purposes which enables wikis to wikify |
| 1120 | + // the make, model and software name to link to their articles. |
| 1121 | + case 'Make': |
| 1122 | + case 'Model': |
| 1123 | + case 'Software': |
| 1124 | + $val = $this->msg( $tag, '', $val ); |
962 | 1125 | break; |
963 | | - default: |
964 | | - $tags[$tag] = $val; |
| 1126 | + |
| 1127 | + case 'ExposureTime': |
| 1128 | + // Show the pretty fraction as well as decimal version |
| 1129 | + $val = wfMsg( 'exif-exposuretime-format', |
| 1130 | + $this->formatFraction( $val ), $this->formatNum( $val ) ); |
965 | 1131 | break; |
966 | | - } |
967 | | - break; |
968 | 1132 | |
969 | | - case 'GPSMeasureMode': |
970 | | - switch( $val ) { |
971 | | - case 2: case 3: |
972 | | - $tags[$tag] = $this->msg( $tag, $val ); |
| 1133 | + case 'FNumber': |
| 1134 | + $val = wfMsg( 'exif-fnumber-format', |
| 1135 | + $this->formatNum( $val ) ); |
973 | 1136 | break; |
974 | | - default: |
975 | | - $tags[$tag] = $val; |
976 | | - break; |
977 | | - } |
978 | | - break; |
979 | 1137 | |
980 | | - case 'GPSSpeedRef': |
981 | | - switch( $val ) { |
982 | | - case 'K': case 'M': case 'N': |
983 | | - $tags[$tag] = $this->msg( 'GPSSpeed', $val ); |
| 1138 | + case 'FocalLength': |
| 1139 | + $val = wfMsg( 'exif-focallength-format', |
| 1140 | + $this->formatNum( $val ) ); |
984 | 1141 | break; |
985 | | - default: |
986 | | - $tags[$tag] = $val; |
987 | | - break; |
988 | | - } |
989 | | - break; |
990 | 1142 | |
991 | | - case 'GPSDestDistanceRef': |
992 | | - switch( $val ) { |
993 | | - case 'K': case 'M': case 'N': |
994 | | - $tags[$tag] = $this->msg( 'GPSDestDistance', $val ); |
| 1143 | + // Do not transform fields with pure text. |
| 1144 | + // For some languages the formatNum() conversion results to wrong output like |
| 1145 | + // foo,bar@example,com or foo٫bar@example٫com |
| 1146 | + case 'ImageDescription': |
| 1147 | + case 'Artist': |
| 1148 | + case 'Copyright': |
| 1149 | + $val = htmlspecialchars( $val ); |
995 | 1150 | break; |
996 | | - default: |
997 | | - $tags[$tag] = $val; |
998 | | - break; |
999 | | - } |
1000 | | - break; |
1001 | 1151 | |
1002 | | - case 'GPSTrackRef': |
1003 | | - case 'GPSImgDirectionRef': |
1004 | | - case 'GPSDestBearingRef': |
1005 | | - switch( $val ) { |
1006 | | - case 'T': case 'M': |
1007 | | - $tags[$tag] = $this->msg( 'GPSDirection', $val ); |
| 1152 | + // Here begins the non-exif data |
| 1153 | + case 'JPEGFileComment': |
| 1154 | + $val = htmlspecialchars( $val ); |
1008 | 1155 | break; |
| 1156 | + |
1009 | 1157 | default: |
1010 | | - $tags[$tag] = $val; |
| 1158 | + $val = $this->formatNum( $val ); |
1011 | 1159 | break; |
1012 | 1160 | } |
1013 | | - break; |
| 1161 | + } |
| 1162 | + // End formatting values, start flattening arrays. |
| 1163 | + $vals = $this->flattenArray( $vals, $type ); |
1014 | 1164 | |
1015 | | - case 'GPSDateStamp': |
1016 | | - $tags[$tag] = $wgLang->date( substr( $val, 0, 4 ) . substr( $val, 5, 2 ) . substr( $val, 8, 2 ) . '000000' ); |
1017 | | - break; |
| 1165 | + } |
| 1166 | + return $this->mExif; |
| 1167 | + } |
1018 | 1168 | |
1019 | | - // This is not in the Exif standard, just a special |
1020 | | - // case for our purposes which enables wikis to wikify |
1021 | | - // the make, model and software name to link to their articles. |
1022 | | - case 'Make': |
1023 | | - case 'Model': |
1024 | | - case 'Software': |
1025 | | - $tags[$tag] = $this->msg( $tag, '', $val ); |
1026 | | - break; |
| 1169 | + /** |
| 1170 | + * A function to collapse multivalued tags into a single value. |
| 1171 | + * This turns an array of (for example) authors into a bulleted list. |
| 1172 | + * This might be used outside of this class. |
| 1173 | + * @public |
| 1174 | + * |
| 1175 | + * @param $vals Array array of values |
| 1176 | + * @param $type Type of array (either lang, ul, ol). |
| 1177 | + * lang = language assoc array with keys being the lang code |
| 1178 | + * ul = unorded list, ol = ordered list |
| 1179 | + * type can also come from the '_type' member of $vals. |
| 1180 | + * @return String single value (in wiki-syntax). |
| 1181 | + */ |
| 1182 | + public static function flattenArray( $vals, $type = 'ul' ) { |
1027 | 1183 | |
1028 | | - case 'ExposureTime': |
1029 | | - // Show the pretty fraction as well as decimal version |
1030 | | - $tags[$tag] = wfMsg( 'exif-exposuretime-format', |
1031 | | - $this->formatFraction( $val ), $this->formatNum( $val ) ); |
1032 | | - break; |
| 1184 | + if ( isset( $vals['_type'] ) ) { |
| 1185 | + $type = $vals['_type']; |
| 1186 | + } |
1033 | 1187 | |
1034 | | - case 'FNumber': |
1035 | | - $tags[$tag] = wfMsg( 'exif-fnumber-format', |
1036 | | - $this->formatNum( $val ) ); |
1037 | | - break; |
1038 | | - |
1039 | | - case 'FocalLength': |
1040 | | - $tags[$tag] = wfMsg( 'exif-focallength-format', |
1041 | | - $this->formatNum( $val ) ); |
1042 | | - break; |
1043 | | - |
1044 | | - // Do not transform fields with pure text. |
1045 | | - // For some languages the formatNum() conversion results to wrong output like |
1046 | | - // foo,bar@example,com or foo٫bar@example٫com |
1047 | | - case 'ImageDescription': |
1048 | | - case 'Artist': |
1049 | | - case 'Copyright': |
1050 | | - $tags[$tag] = htmlspecialchars( $val ); |
1051 | | - break; |
1052 | | - //Here begins the non-exif data |
1053 | | - case 'JPEGFileComment': |
1054 | | - $tags[$tag] = htmlspecialchars( $val ); |
1055 | | - break; |
1056 | | - |
| 1188 | + if ( !is_array( $vals ) ) { |
| 1189 | + return $vals; // do nothing if not an array; |
| 1190 | + } |
| 1191 | + elseif ( count( $vals ) === 1 && $type !== 'lang' ) { |
| 1192 | + return $vals[0]; |
| 1193 | + } |
| 1194 | + elseif ( count( $vals ) === 0 ) { |
| 1195 | + return ""; // paranoia. This should never happen |
| 1196 | + wfDebug( __METHOD__ . ' metadata array with 0 elements!' ); |
| 1197 | + } |
| 1198 | + else { |
| 1199 | + switch( $type ) { |
| 1200 | + case 'lang': |
| 1201 | + // fixme incomplete |
| 1202 | + // should place x-default, content language, user language |
| 1203 | + // first. then the others, hidden by defualt. |
| 1204 | + // also should use much better markup. |
| 1205 | + $content = ""; |
| 1206 | + if ( $vals['x-default'] ) { |
| 1207 | + $content .= "\n*" . $vals['x-default']; |
| 1208 | + unset( $vals['x-default'] ); |
| 1209 | + } |
| 1210 | + foreach ( $vals as $lang => $content ) { |
| 1211 | + $content = "\n*<span lang=\"$lang\">" |
| 1212 | + . "'''$lang''' $content</span>"; |
| 1213 | + } |
| 1214 | + return $content; |
| 1215 | + case 'ol': |
| 1216 | + return "\n#" . implode( "\n#", $vals ); |
| 1217 | + case 'ul': |
1057 | 1218 | default: |
1058 | | - $tags[$tag] = $this->formatNum( $val ); |
1059 | | - break; |
| 1219 | + return "\n*" . implode( "\n*", $vals ); |
1060 | 1220 | } |
1061 | 1221 | } |
1062 | | - } |
1063 | | - |
1064 | | - return $this->mExif; |
1065 | 1222 | } |
1066 | | - |
1067 | 1223 | /** |
1068 | 1224 | * Convenience function for getFormattedData() |
1069 | 1225 | * |
— | — | @@ -1162,19 +1318,37 @@ |
1163 | 1319 | * @private |
1164 | 1320 | * |
1165 | 1321 | * @param $coords Array: degrees, minutes and seconds |
1166 | | - * @param $ref String: reference direction (N/S/E/W), optional |
| 1322 | + * @param $type String: latitude or longitude (for if its a NWS or E) |
1167 | 1323 | * @return mixed A floating point number or whatever we were fed |
1168 | 1324 | */ |
1169 | | - function formatCoords( $coords, $ref = null ) { |
1170 | | - list($deg, $min, $sec) = $coords; |
1171 | | - $deg = $this->formatNum($deg); |
1172 | | - $min = $this->formatNum($min); |
1173 | | - $sec = $this->formatNum($sec); |
1174 | | - $out = $deg . "°"; |
1175 | | - if ($min) $out .= " " . $min . "'"; |
1176 | | - if ($sec) $out .= " " . $sec . '"'; |
1177 | | - if ($ref) $out .= " " . $ref; |
1178 | | - return $out; |
| 1325 | + function formatCoords( $coord, $type ) { |
| 1326 | + $ref = ''; |
| 1327 | + if ( $coord < 0 ) { |
| 1328 | + if ( $type === 'latitude' ) { |
| 1329 | + $ref = 'S'; |
| 1330 | + } |
| 1331 | + elseif ( $type === 'longitude' ) { |
| 1332 | + $ref = 'W'; |
| 1333 | + } |
| 1334 | + } |
| 1335 | + else { |
| 1336 | + if ( $type === 'latitude' ) { |
| 1337 | + $ref = 'N'; |
| 1338 | + } |
| 1339 | + elseif ( $type === 'longitude' ) { |
| 1340 | + $ref = 'E'; |
| 1341 | + } |
| 1342 | + } |
| 1343 | + |
| 1344 | + $deg = floor( $coord ); |
| 1345 | + $min = floor( ( $coord - $deg ) * 60.0 ); |
| 1346 | + $sec = round( ( ( $coord - $deg ) - $min / 60 ) * 3600, 2 ); |
| 1347 | + |
| 1348 | + $deg = $this->formatNum( $deg ); |
| 1349 | + $min = $this->formatNum( $min ); |
| 1350 | + $sec = $this->formatNum( $sec ); |
| 1351 | + |
| 1352 | + return wfMsg( 'exif-coordinate-format', $deg, $min, $sec, $ref, $coord ); |
1179 | 1353 | } |
1180 | 1354 | |
1181 | 1355 | } |
Index: branches/img_metadata/phase3/includes/filerepo/File.php |
— | — | @@ -268,6 +268,26 @@ |
269 | 269 | public function getMetadata() { return false; } |
270 | 270 | |
271 | 271 | /** |
| 272 | + * get versioned metadata |
| 273 | + * |
| 274 | + * @param $metadata Mixed Array or String of (serialized) metadata |
| 275 | + * @param $version integer version number. |
| 276 | + * @return Array containing metadata, or what was passed to it on fail (unserializing if not array) |
| 277 | + */ |
| 278 | + public function convertMetadataVersion($metadata, $version) { |
| 279 | + $handler = $this->getHandler(); |
| 280 | + if (!is_array($metadata)) { |
| 281 | + //just to make the return type consistant |
| 282 | + $metadata = unserialize( $metadata ); |
| 283 | + } |
| 284 | + if ( $handler ) { |
| 285 | + return $handler->convertMetadataVersion($metadata, $version); |
| 286 | + } else { |
| 287 | + return $metadata; |
| 288 | + } |
| 289 | + } |
| 290 | + |
| 291 | + /** |
272 | 292 | * Return the bit depth of the file |
273 | 293 | * Overridden by LocalFile |
274 | 294 | * STUB |
Index: branches/img_metadata/phase3/includes/filerepo/ForeignAPIFile.php |
— | — | @@ -20,7 +20,13 @@ |
21 | 21 | $data = $repo->fetchImageQuery( array( |
22 | 22 | 'titles' => 'File:' . $title->getText(), |
23 | 23 | 'iiprop' => 'timestamp|user|comment|url|size|sha1|metadata|mime', |
24 | | - 'prop' => 'imageinfo' ) ); |
| 24 | + 'prop' => 'imageinfo', |
| 25 | + 'iimetadataversion' => Exif::version() |
| 26 | + ) ); |
| 27 | + // Note to self/fixme: Using Exif::version() here is obviously not good, as |
| 28 | + // metadata is handler specific. Original plan was to use handler->getMetadataVersion() |
| 29 | + // but that doesn't work, because we don't know mime type yet, thus don't know handler yet. |
| 30 | + // need to think of a better way of doing this. |
25 | 31 | |
26 | 32 | $info = $repo->getImageInfo( $data ); |
27 | 33 | |
Index: branches/img_metadata/phase3/includes/api/ApiQueryImageInfo.php |
— | — | @@ -122,7 +122,7 @@ |
123 | 123 | { |
124 | 124 | $gotOne = true; |
125 | 125 | $fit = $this->addPageSubItem( $pageId, |
126 | | - self::getInfo( $img, $prop, $result, $scale ) ); |
| 126 | + self::getInfo( $img, $prop, $result, $scale, $params['metadataversion'] ) ); |
127 | 127 | if ( !$fit ) { |
128 | 128 | if ( count( $pageIds[NS_IMAGE] ) == 1 ) { |
129 | 129 | // See the 'the user is screwed' comment above |
— | — | @@ -151,7 +151,7 @@ |
152 | 152 | break; |
153 | 153 | } |
154 | 154 | $fit = $this->addPageSubItem( $pageId, |
155 | | - self::getInfo( $oldie, $prop, $result ) ); |
| 155 | + self::getInfo( $oldie, $prop, $result, null, $params['metadataversion'] ) ); |
156 | 156 | if ( !$fit ) { |
157 | 157 | if ( count( $pageIds[NS_IMAGE] ) == 1 ) { |
158 | 158 | $this->setContinueEnumParameter( 'start', |
— | — | @@ -191,7 +191,7 @@ |
192 | 192 | * @param $scale Array containing 'width' and 'height' items, or null |
193 | 193 | * @return Array: result array |
194 | 194 | */ |
195 | | - static function getInfo( $file, $prop, $result, $scale = null ) { |
| 195 | + static function getInfo( $file, $prop, $result, $scale = null, $version = 0 ) { |
196 | 196 | $vals = array(); |
197 | 197 | if ( isset( $prop['timestamp'] ) ) { |
198 | 198 | $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() ); |
— | — | @@ -239,8 +239,11 @@ |
240 | 240 | $vals['sha1'] = wfBaseConvert( $file->getSha1(), 36, 16, 40 ); |
241 | 241 | } |
242 | 242 | if ( isset( $prop['metadata'] ) ) { |
243 | | - $metadata = $file->getMetadata(); |
244 | | - $vals['metadata'] = $metadata ? self::processMetaData( unserialize( $metadata ), $result ) : null; |
| 243 | + $metadata = unserialize( $file->getMetadata() ); |
| 244 | + if ( $version !== 0 ) { |
| 245 | + $metadata = $file->convertMetadataVersion( $metadata, $version ); |
| 246 | + } |
| 247 | + $vals['metadata'] = $metadata ? self::processMetaData( $metadata, $result ) : null; |
245 | 248 | } |
246 | 249 | if ( isset( $prop['mime'] ) ) { |
247 | 250 | $vals['mime'] = $file->getMimeType(); |
— | — | @@ -307,6 +310,11 @@ |
308 | 311 | ApiBase::PARAM_TYPE => 'integer', |
309 | 312 | ApiBase::PARAM_DFLT => - 1 |
310 | 313 | ), |
| 314 | + 'metadataversion' => array( |
| 315 | + ApiBase::PARAM_TYPE => 'integer', |
| 316 | + ApiBase::PARAM_DFLT => 1, |
| 317 | + ApiBase::PARAM_MIN => 0, |
| 318 | + ), |
311 | 319 | 'continue' => null, |
312 | 320 | ); |
313 | 321 | } |
— | — | @@ -341,6 +349,8 @@ |
342 | 350 | 'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.", |
343 | 351 | 'Only the current version of the image can be scaled' ), |
344 | 352 | 'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth", |
| 353 | + 'metadataversion' => array( "Version of metadata to use. if 0 is specified, use latest version.", |
| 354 | + "Defaults to '1' for bacwards compatability" ), |
345 | 355 | 'continue' => 'When more results are available, use this to continue', |
346 | 356 | ); |
347 | 357 | } |
Index: branches/img_metadata/phase3/includes/media/Bitmap.php |
— | — | @@ -424,16 +424,14 @@ |
425 | 425 | $formatted = $format->getFormattedData(); |
426 | 426 | // Sort fields into visible and collapsed |
427 | 427 | $visibleFields = $this->visibleMetadataFields(); |
428 | | - foreach ( $formatted as $section => $tags ) { |
429 | | - foreach ( $tags as $name => $value ) { |
430 | | - $tag = strtolower( $name ); |
431 | | - self::addMeta( $result, |
432 | | - in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed', |
433 | | - 'exif', |
434 | | - $tag, |
435 | | - $value |
436 | | - ); |
437 | | - } |
| 428 | + foreach ( $formatted as $name => $value ) { |
| 429 | + $tag = strtolower( $name ); |
| 430 | + self::addMeta( $result, |
| 431 | + in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed', |
| 432 | + 'exif', |
| 433 | + $tag, |
| 434 | + $value |
| 435 | + ); |
438 | 436 | } |
439 | 437 | return $result; |
440 | 438 | } |
Index: branches/img_metadata/phase3/includes/media/Generic.php |
— | — | @@ -87,6 +87,33 @@ |
88 | 88 | function getMetadata( $image, $path ) { return ''; } |
89 | 89 | |
90 | 90 | /** |
| 91 | + * Get metadata version. |
| 92 | + * @return integer version |
| 93 | + * @todo Originally this was going to be used by ForeignAPIFile, but currently does nothing. |
| 94 | + */ |
| 95 | + function getMetadataVersion () { return 1; } |
| 96 | + |
| 97 | + /** |
| 98 | + * Convert metadata version. |
| 99 | + * |
| 100 | + * By default just returns $metadata, but can be used to allow |
| 101 | + * media handlers to convert between metadata versions. |
| 102 | + * |
| 103 | + * @param $metadata Mixed String or Array metadata array (serialized if string) |
| 104 | + * @param $version Integer target version |
| 105 | + * @return Array serialized metadata in specified version, or $metadata on fail. |
| 106 | + */ |
| 107 | + function convertMetadataVersion( $metadata, $version = 1 ) { |
| 108 | + if ( !is_array( $metadata ) ) { |
| 109 | + //unserialize to keep return parameter consistent. |
| 110 | + wfSuppressWarnings(); |
| 111 | + return unserialize( $metadata ); |
| 112 | + wfRestoreWarnings(); |
| 113 | + } |
| 114 | + return $metadata; |
| 115 | + } |
| 116 | + |
| 117 | + /** |
91 | 118 | * Get a string describing the type of metadata, for display purposes. |
92 | 119 | */ |
93 | 120 | function getMetadataType( $image ) { return false; } |
Index: branches/img_metadata/phase3/includes/media/IPTC.php |
— | — | @@ -63,8 +63,6 @@ |
64 | 64 | $val = self::convIPTCHelper( $val, $charset ); |
65 | 65 | } |
66 | 66 | |
67 | | - // for now. Probably should keep it as an array perhaps |
68 | | - $data = $wgLang->commaList( $data ); |
69 | 67 | } else { |
70 | 68 | $data = self::convIPTCHelper ( $data, $charset ); |
71 | 69 | } |
Index: branches/img_metadata/phase3/includes/media/Jpeg.php |
— | — | @@ -27,4 +27,27 @@ |
28 | 28 | return '0'; |
29 | 29 | } |
30 | 30 | } |
| 31 | + |
| 32 | + function convertMetadataVersion( $metadata, $version = 1 ) { |
| 33 | + // basically flattens arrays. |
| 34 | + if ( $version != 1 ) { |
| 35 | + return $metadata; |
| 36 | + } |
| 37 | + |
| 38 | + if ( !is_array( $metadata ) ) { |
| 39 | + $metadata = unserialize( $metadata ); |
| 40 | + } |
| 41 | + if ( !isset( $metadata['MEDIAWIKI_EXIF_VERSION'] ) || $metadata['MEDIAWIKI_EXIF_VERSION'] != 2 ) { |
| 42 | + return $metadata; |
| 43 | + } |
| 44 | + |
| 45 | + foreach ( $metadata as &$val ) { |
| 46 | + if ( is_array( $val ) ) { |
| 47 | + $val = formatExif::flattenArray( $val ); |
| 48 | + } |
| 49 | + } |
| 50 | + $metadata['MEDIAWIKI_EXIF_VERSION'] = $version; |
| 51 | + return $metadata; |
| 52 | + } |
| 53 | + function getMetadataVersion () { return Exif::version(); } |
31 | 54 | } |
Index: branches/img_metadata/phase3/languages/messages/MessagesQqq.php |
— | — | @@ -3196,6 +3196,7 @@ |
3197 | 3197 | See also Wikipedia on [http://en.wikipedia.org/wiki/Focal_length#In_photography focal length].', |
3198 | 3198 | 'exif-gpslatitude' => '{{Identical|Latitude}}', |
3199 | 3199 | 'exif-gpslongitude' => '{{Identical|Longitude}}', |
| 3200 | +'exif-coordinate-format' => 'For formatting GPS latitude coordinates. $1 is degrees, $2 is minutes, $3 is seconds (up to two decimal places), $4 is direction (N, S, W, or E), $5 is coordinate as a single positive or negative real number.', |
3200 | 3201 | 'exif-jpegfilecomment' => 'This is not a true exif tag, but the contents of the JPEG COM segment. This often contains a file source, but can potentially contain any comment about the file', |
3201 | 3202 | |
3202 | 3203 | # EXIF attributes |
Index: branches/img_metadata/phase3/languages/messages/MessagesEn.php |
— | — | @@ -3757,6 +3757,7 @@ |
3758 | 3758 | 'exif-gpsareainformation' => 'Name of GPS area', |
3759 | 3759 | 'exif-gpsdatestamp' => 'GPS date', |
3760 | 3760 | 'exif-gpsdifferential' => 'GPS differential correction', |
| 3761 | +'exif-coordinate-format' => '$1° $2′ $3″ $4', |
3761 | 3762 | 'exif-jpegfilecomment' => 'JPEG file comment', |
3762 | 3763 | |
3763 | 3764 | # Make & model, can be wikified in order to link to the camera and model name |