Plugin Directory

Changeset 3446683


Ignore:
Timestamp:
01/25/2026 09:30:01 PM (2 months ago)
Author:
pablo2
Message:

trunk ver. 2.1.5

Location:
nextgen-gallery-geo/trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • nextgen-gallery-geo/trunk/functions.php

    r3445193 r3446683  
    1616 * @since      2.1.3 Amended functions: geo2_maps_enqueue_scripts(), geo2_maps_handle_proxy_request(), geo2_maps_coordinates(), geo2_maps_exif(), geo2_maps_exif_camera(), geo2_maps_shortcodes().
    1717 * @since      2.1.4 Amended functions: geo2_maps_coordinates(), geo2_maps_exif(), geo2_maps_exif_camera(), geo2_maps_check_content(), geo2_maps_data(), geo2_maps_data_single(), geo2_maps_data_worldmap(), geo2_maps_enqueue_scripts(). New function: geo2_maps_is_webp(), geo2_maps_get_webp_metadata(), geo2_maps_webp_search_xmp(), geo2_maps_webp_search_xmp_gps(), geo2_maps_webp_search_xmp_exif_info(), geo2_maps_eval_fraction(), geo2_maps_is_valid_tiff_blob(), geo2_maps_parse_exif_blob().
     18 * @since      2.1.5 Amended functions: geo2_maps_get_webp_metadata(), geo2_maps_webp_search_xmp(), geo2_maps_webp_search_xmp_gps(), geo2_maps_webp_search_xmp_exif_info(), geo2_maps_parse_exif_blob().
    1819 * @copyright  Copyright (c) 2023, Pawel Block
    1920 * @link       http://www.geo2maps.plus
     
    755756}
    756757
    757 // === Configuration ===
    758 if ( ! defined( 'GEO2_MAPS_MAX_BYTES' ) ) {
    759     define( 'GEO2_MAPS_MAX_BYTES', 32 * 1024 * 1024 );
    760 } // 32MB safety cap
    761 if ( ! defined( 'GEO2_MAPS_CHUNK_SIZE' ) ) {
    762     define( 'GEO2_MAPS_CHUNK_SIZE', 1024 * 1024 );
    763 } // 1MB
    764 
    765758/**
    766759 * Detect if file is WebP by checking file extension and magic bytes
     
    861854 *
    862855 * @since 2.1.4
     856 * @since 2.1.5 Corrected to extract EXIF data if no XMP is found also for GPS.
    863857 *
    864858 * @param string      $file_path The path to the WebP file.
     
    966960        } elseif ( $xmp_streamed !== false && $sections === 'IFD0&EXIF' ) {
    967961            $xmp_search_data = geo2_maps_webp_search_xmp_exif_info( $xmp_streamed );
    968             $xmp_data        = array_replace( $xmp_data, $xmp_search_data );
     962            $xmp_data = array_replace( $xmp_data, $xmp_search_data );
    969963        }
    970964        // Reset for the main scan loop.
     
    982976        }
    983977    }
     978
     979    // Check if XMP data is incomplete for 'GPS' section.
     980    if ( $sections === 'GPS' && ( empty( $xmp_data ) || ! isset( $xmp_data['GPSLatitude'] ) ) ) {
     981        $is_xmp_incomplete = true;
     982    }
     983
    984984    // Scan chunks for EXIF if no XMP or XMP did not contain search data.
    985985    if ( $has_exif && ( ! $xmp_data || $is_xmp_incomplete ) ) {
     
    10131013        $exif_data = array();
    10141014    }
    1015     $result = array_replace( $exif_data, $xmp_data );
     1015
     1016    $result = $exif_data;
     1017    foreach ( $xmp_data as $key => $value ) {
     1018        if ( $value !== null && $value !== '' ) {
     1019            $result[ $key ] = $value;
     1020        }
     1021    }
    10161022    // Cache result (we cache only positive arrays to avoid caching failures).
    10171023    if ( $caching_enabled && $result !== false ) {
     
    10271033}
    10281034
    1029 
    10301035/**
    10311036 * Searches for an XMP metadata block within a file stream.
    10321037 *
    1033  * This function reads a file stream in chunks to efficiently find the
    1034  * start and end tags of an XMP block ('<x:xmpmeta>' and '</x:xmpmeta>').
     1038 * This function parses the RIFF structure of the WebP file to locate
     1039 * the 'XMP ' chunk and extract its content.
    10351040 *
    10361041 * @since 2.1.4
     
    10411046 */
    10421047function geo2_maps_webp_search_xmp( $fp ) {
    1043     if ( ! is_resource( $fp ) ) {
     1048    rewind( $fp );
     1049    $header = fread( $fp, 12 );
     1050    if ( strlen( $header ) < 12 ) {
    10441051        return false;
    10451052    }
    1046     $buffer    = '';
    1047     $read      = 0;
    1048     $max       = GEO2_MAPS_MAX_BYTES;
    1049     $chunk     = GEO2_MAPS_CHUNK_SIZE;
    1050     $tail_keep = 4096;
    1051 
    1052     while ( ! feof( $fp ) && $read < $max ) {
    1053         $data = fread( $fp, $chunk );
    1054         if ( $data === false || $data === '' ) {
     1053
     1054    $riff = substr( $header, 0, 4 );
     1055    $webp = substr( $header, 8, 4 );
     1056
     1057    if ( $riff !== 'RIFF' || $webp !== 'WEBP' ) {
     1058        return false;
     1059    }
     1060
     1061    $xmp_data = '';
     1062
     1063    while ( ! feof( $fp ) ) {
     1064        $chunk_header = fread( $fp, 8 );
     1065        if ( strlen( $chunk_header ) < 8 ) {
    10551066            break;
    10561067        }
    1057         $read   += strlen( $data );
    1058         $buffer .= $data;
    1059 
    1060         $start = strpos( $buffer, '<x:xmpmeta' );
    1061         if ( $start !== false ) {
    1062             $end = strpos( $buffer, '</x:xmpmeta>', $start );
    1063             if ( $end === false ) {
    1064                 // keep from start to avoid unbounded growth.
    1065                 $buffer = substr( $buffer, $start );
    1066                 continue;
    1067             }
    1068             $end += strlen( '</x:xmpmeta>' );
    1069             return substr( $buffer, $start, $end - $start );
    1070         }
    1071 
    1072         // keep only tail to avoid memory growth.
    1073         if ( strlen( $buffer ) > $tail_keep ) {
    1074             $buffer = substr( $buffer, -$tail_keep );
    1075         }
    1076     }
    1077 
    1078     return false;
     1068
     1069        $chunk_tag = substr( $chunk_header, 0, 4 );
     1070        // WebP chunk size is little-endian 32-bit integer.
     1071        $chunk_size = unpack( 'V', substr( $chunk_header, 4, 4 ) )[1];
     1072
     1073        if ( $chunk_tag === 'XMP ' ) {
     1074            // Found XMP chunk, read the full content based on chunk size.
     1075            $read = 0;
     1076            while ( $read < $chunk_size && ! feof( $fp ) ) {
     1077                $buffer = fread( $fp, min( 8192, $chunk_size - $read ) );
     1078                if ( $buffer === false ) {
     1079                    break;
     1080                }
     1081                $xmp_data .= $buffer;
     1082                $read     += strlen( $buffer );
     1083            }
     1084            // Padding byte if size is odd (RIFF standard).
     1085            if ( $chunk_size % 2 !== 0 ) {
     1086                fseek( $fp, 1, SEEK_CUR );
     1087            }
     1088            continue;
     1089        }
     1090
     1091        // Skip chunk data.
     1092        fseek( $fp, $chunk_size, SEEK_CUR );
     1093
     1094        // Padding byte if size is odd (RIFF standard).
     1095        if ( $chunk_size % 2 !== 0 ) {
     1096            fseek( $fp, 1, SEEK_CUR );
     1097        }
     1098    }
     1099
     1100    return ! empty( $xmp_data ) ? $xmp_data : false;
    10791101}
    10801102
     
    10851107 *
    10861108 * @since 2.1.4
     1109 * @since 2.1.5 Added support for WebD files create with older Adobe XMP Core 7.0 ( metadata saved in "" not in <> tags).
    10871110 *
    10881111 * @see geo2_maps_get_webp_metadata()
     
    10971120    $lon_ref = 'E';
    10981121
    1099     if ( preg_match( '/<exif:GPSLatitude>([^<]+)</', $xmp, $m ) ) {
    1100         $lat     = trim( $m[1] );
     1122    $result = array();
     1123
     1124    if ( preg_match( '/(?:<exif:GPSLatitude>([^<]+)<\/|exif:GPSLatitude="([^"]+)")/i', $xmp, $m ) ) {
     1125        $lat     = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
    11011126        $lat_ref = ( stripos( $lat, 'S' ) !== false ) ? 'S' : 'N';
    11021127    }
    1103     if ( preg_match( '/<exif:GPSLongitude>([^<]+)</', $xmp, $m ) ) {
    1104         $lon     = trim( $m[1] );
     1128    if ( preg_match( '/(?:<exif:GPSLongitude>([^<]+)<\/|exif:GPSLongitude="([^"]+)")/i', $xmp, $m ) ) {
     1129        $lon     = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
    11051130        $lon_ref = ( stripos( $lon, 'W' ) !== false ) ? 'W' : 'E';
    11061131    }
     
    11271152    }
    11281153
    1129     return $result ? $result : false;
     1154    return ! empty( $result ) ? $result : false;
    11301155}
    11311156
     
    11371162 *
    11381163 * @since 2.1.4
     1164 * @since 2.1.5 Added support for WebD files create with older Adobe XMP Core 7.0 ( metadata saved in "" not in <> tags).
    11391165 *
    11401166 * @see geo2_maps_get_webp_metadata()
     
    11461172    $data = array();
    11471173
    1148     $data = array();
    1149 
    11501174    // --- 1. TIMESTAMP (created_timestamp) ---
    11511175    $data['created_timestamp'] = '';
    11521176    // Search for common date tags (DateTimeOriginal, CreateDate, DateCreated, ModifyDate).
    1153     if ( preg_match( '/<(?:exif:DateTimeOriginal|xmp:CreateDate|photoshop:DateCreated|xmp:ModifyDate)>([^<]+)<\//i', $xmp_raw, $m ) ) {
    1154         $raw_date = trim( $m[1] );
     1177    if ( preg_match( '/(?:<(?:exif:DateTimeOriginal|xmp:CreateDate|photoshop:DateCreated|xmp:ModifyDate)>([^<]+)<\/|(?:exif:DateTimeOriginal|xmp:CreateDate|photoshop:DateCreated|xmp:ModifyDate)="([^"]+)")/i', $xmp_raw, $m ) ) {
     1178        $raw_date = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
    11551179        // Clean date string.
    11561180        $clean_date                = str_replace( 'T', ' ', $raw_date );
     
    11641188
    11651189    // Extract Make (tiff:Make or exif:Make).
    1166     if ( preg_match( '/<(?:tiff|exif):Make>([^<]+)<\//i', $xmp_raw, $m ) ) {
    1167         $make = trim( $m[1] );
     1190    if ( preg_match( '/(?:<(?:tiff|exif):Make>([^<]+)<\/|(?:tiff|exif):Make="([^"]+)")/i', $xmp_raw, $m ) ) {
     1191        $make = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
    11681192    }
    11691193    // Extract Model (tiff:Model or exif:Model).
    1170     if ( preg_match( '/<(?:tiff|exif):Model>([^<]+)<\//i', $xmp_raw, $m ) ) {
    1171         $model = trim( $m[1] );
     1194    if ( preg_match( '/(?:<(?:tiff|exif):Model>([^<]+)<\/|(?:tiff|exif):Model="([^"]+)")/i', $xmp_raw, $m ) ) {
     1195        $model = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
    11721196    }
    11731197
     
    11781202    // Lens Make: aux:LensMake or exifex:LensMake.
    11791203    $data['lens_make'] = '';
    1180     if ( preg_match( '/<(?:aux|exifex):LensMake>([^<]+)<\//i', $xmp_raw, $m ) ) {
    1181         $data['lens_make'] = trim( $m[1] );
     1204    if ( preg_match( '/(?:<(?:aux|exifex):LensMake>([^<]+)<\/|(?:aux|exifex):LensMake="([^"]+)")/i', $xmp_raw, $m ) ) {
     1205        $data['lens_make'] = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
    11821206    }
    11831207
    11841208    // Lens Model: aux:LensModel, exifex:LensModel, or aux:lens.
    11851209    $data['lens_model'] = '';
    1186     if ( preg_match( '/<(?:aux|exifex):LensModel>([^<]+)<\//i', $xmp_raw, $m ) ) {
    1187         $data['lens_model'] = trim( $m[1] );
    1188     } elseif ( preg_match( '/<aux:lens>([^<]+)<\//i', $xmp_raw, $m ) ) {
     1210    if ( preg_match( '/(?:<(?:aux|exifex):LensModel>([^<]+)<\/|(?:aux|exifex):LensModel="([^"]+)")/i', $xmp_raw, $m ) ) {
     1211        $data['lens_model'] = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
     1212    } elseif ( preg_match( '/(?:<aux:lens>([^<]+)<\/|aux:Lens="([^"]+)")/i', $xmp_raw, $m ) ) {
    11891213        // Specific tag used in your sample XML.
    1190         $data['lens_model'] = trim( $m[1] );
     1214        $data['lens_model'] = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
    11911215    }
    11921216
    11931217    // --- 4. APERTURE (aperture) ---
    11941218    $data['aperture'] = '';
    1195     if ( preg_match( '/<exif:FNumber>([^<]+)<\//i', $xmp_raw, $m ) ) {
    1196         $val = geo2_maps_eval_fraction( $m[1] );
     1219    if ( preg_match( '/(?:<exif:FNumber>([^<]+)<\/|exif:FNumber="([^"]+)")/i', $xmp_raw, $m ) ) {
     1220        $val = geo2_maps_eval_fraction( ! empty( $m[1] ) ? $m[1] : $m[2] );
    11971221        if ( $val > 0 ) {
    11981222            $data['aperture'] = 'f/' . round( $val, 1 );
     
    12021226    // --- 5. FOCAL LENGTH (focal_length) ---
    12031227    $data['focal_length'] = '';
    1204     if ( preg_match( '/<exif:FocalLength>([^<]+)<\//i', $xmp_raw, $m ) ) {
    1205         $val = geo2_maps_eval_fraction( $m[1] );
     1228    if ( preg_match( '/(?:<exif:FocalLength>([^<]+)<\/|exif:FocalLength="([^"]+)")/i', $xmp_raw, $m ) ) {
     1229        $val = geo2_maps_eval_fraction( ! empty( $m[1] ) ? $m[1] : $m[2] );
    12061230        if ( $val > 0 ) {
    12071231            $data['focal_length'] = round( $val, 1 ) . 'mm';
     
    12121236    $data['iso'] = '';
    12131237    // ISO is often nested in an rdf:seq list, this regex extracts the value.
    1214     if ( preg_match( '/<exif:ISOSpeedRatings>.*?<rdf:li>([^<]+)<\/rdf:li>/is', $xmp_raw, $m ) ) {
    1215         $data['iso'] = trim( $m[1] );
     1238    if ( preg_match( '/(?:<exif:ISOSpeedRatings>.*?<rdf:li>([^<]+)<\/rdf:li>|exif:ISOSpeedRatings="([^"]+)")/is', $xmp_raw, $m ) ) {
     1239        $data['iso'] = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
    12161240    }
    12171241
    12181242    // --- 7. SHUTTER SPEED (shutter_speed) ---
    12191243    $data['shutter_speed'] = '';
    1220     if ( preg_match( '/<exif:ExposureTime>([^<]+)<\//i', $xmp_raw, $m ) ) {
    1221         $data['shutter_speed'] = trim( $m[1] );
     1244    if ( preg_match( '/(?:<exif:ExposureTime>([^<]+)<\/|exif:ExposureTime="([^"]+)")/i', $xmp_raw, $m ) ) {
     1245        $data['shutter_speed'] = trim( ! empty( $m[1] ) ? $m[1] : $m[2] );
    12221246    }
    12231247    return $data;
     
    12531277 *
    12541278 * @since 2.1.4
     1279 * @since 2.1.5 Critical logic mistake corrected.
    12551280 *
    12561281 * @see geo2_maps_get_webp_metadata()
     
    12741299    }
    12751300    $magic = substr( $blob, 2, 2 );
    1276     if ( $magic === "\x2A\x00" || $magic === "\x00\x2A" ) {
     1301    if ( $magic !== "\x2A\x00" && $magic !== "\x00\x2A" ) {
    12771302        return false;
    12781303    }
     
    15081533    if ( $sections === 'IFD0&EXIF' ) {
    15091534        $info = array(
    1510             'created_timestamp' => '',
    1511             'camera'            => '',
     1535            'created_timestamp' => 'g',
     1536            'camera'            => 'r',
    15121537            'lens_make'         => '',
    15131538            'lens_model'        => '',
     
    15171542            'shutter_speed'     => '',
    15181543        );
     1544
    15191545        // DateTimeOriginal (0x9003) in EXIF IFD, fallback to DateTime (0x0132) in IFD0.
    15201546        if ( isset( $exif_tags[0x9003] ) && is_string( $exif_tags[0x9003] ) ) {
  • nextgen-gallery-geo/trunk/plugin.php

    r3445206 r3446683  
    2525 * Plugin URI:  https://wordpress.org/plugins/nextgen-gallery-geo/
    2626 * Description: Geo2 Maps Add-on for NextGEN Gallery is a flexible plugin, displaying beautiful maps with your photos by using EXIF data or geocoding.
    27  * Version:     2.1.4
     27 * Version:     2.1.5
    2828
    2929 * Author URI:  http://www.geo2maps.plus
  • nextgen-gallery-geo/trunk/readme.txt

    r3445210 r3446683  
    66Tested up to: 6.9
    77Requires PHP: 7.2.0
    8 Stable tag: 2.1.4
     8Stable tag: 2.1.5
    99License: GPLv2 or later
    1010License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    181181== Changelog ==
    182182
    183 = V2.1.4 - 07.12.2025 =
     183= V2.1.5 - 25.01.2026 =
     184
     185* Update: Support added for WebD files created with an older library Adobe XMP Core 7.0
     186
     187* Bugfix: Mistake corrected which prevented extracting WebD metadata from EXIP chunk.
     188
     189= V2.1.4 - 21.01.2026 =
    184190
    185191* NEW: Added support for reading GPS and EXIF metadata from WebP images.
Note: See TracChangeset for help on using the changeset viewer.