Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion src/wp-admin/includes/image.php
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ function wp_exif_date2ts( $str ) {
* created_timestamp, focal_length, shutter_speed, and title.
*
* The IPTC metadata that is retrieved is APP13, credit, byline, created date
* and time, caption, copyright, and title. Also includes FNumber, Model,
* and time, caption, copyright, alt, and title. Also includes FNumber, Model,
* DateTimeDigitized, FocalLength, ISOSpeedRatings, and ExposureTime.
*
* @todo Try other exif libraries if available.
Expand Down Expand Up @@ -854,6 +854,7 @@ function wp_read_image_metadata( $file ) {
'title' => '',
'orientation' => 0,
'keywords' => array(),
'alt' => '',
);

$iptc = array();
Expand Down Expand Up @@ -926,6 +927,8 @@ function wp_read_image_metadata( $file ) {
}
}

$meta['alt'] = wp_get_image_alttext( $file );

$exif = array();

/**
Expand Down Expand Up @@ -1074,6 +1077,72 @@ function wp_read_image_metadata( $file ) {
return apply_filters( 'wp_read_image_metadata', $meta, $file, $image_type, $iptc, $exif );
}

/**
* Get the alt text from image meta data.
*
* @since x.x.x
*
* @param string $file File path to the image.
* @return string Embedded alternative text.
*/
function wp_get_image_alttext( $file ) {

$img_contents = file_get_contents( $file );
// Find the start and end positions of the XMP metadata.
$xmp_start = strpos( $img_contents, '<x:xmpmeta' );
$xmp_end = strpos( $img_contents, '</x:xmpmeta>' );

if ( ! $xmp_start || ! $xmp_end ) {
// No XMP metadata found.
return '';
}

// Extract the XMP metadata from the JPEG contents
$xmp_data = substr( $img_contents, $xmp_start, $xmp_end - $xmp_start + 12 );

// Parse the XMP metadata using DOMDocument.
$doc = new DOMDocument();
if ( false === $doc->loadXML( $xmp_data ) ) {
// Invalid XML in metadata.
return '';
}

// Instantiate an XPath object, used to extract portions of the XMP.
$xpath = new DOMXPath( $doc );

// Register the relevant XML namespaces.
$xpath->registerNamespace( 'x', 'adobe:ns:meta/' );
$xpath->registerNamespace( 'rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' );
$xpath->registerNamespace( 'Iptc4xmpCore', 'http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/' );

$node_list = $xpath->query( '/x:xmpmeta/rdf:RDF/rdf:Description/Iptc4xmpCore:AltTextAccessibility' );
if ( $node_list && $node_list->count() ) {

$node = $node_list->item( 0 );

// Get the site's locale.
$locale = get_locale();

// Get the alt text accessibility alternative most appropriate for the site language.
// There are 3 possibilities:
//
// 1. there is an rdf:li with an exact match on the site locale.
// 2. there is an rdf:li with a partial match on the site locale (e.g., site locale is en_US and rdf:li has @xml:lang="en").
// 3. there is an rdf:li with an "x-default" lang.
//
// Evaluate in that order, stopping when we have a match.
$value = $xpath->evaluate( "string( rdf:Alt/rdf:li[ @xml:lang = '{$locale}' ] )", $node );
if ( ! $value ) {
$value = $xpath->evaluate( 'string( rdf:Alt/rdf:li[ @xml:lang = "' . substr( $locale, 0, 2 ) . '" ] )', $node );
if ( ! $value ) {
$value = $xpath->evaluate( 'string( rdf:Alt/rdf:li[ @xml:lang = "x-default" ] )', $node );
}
}
}

return $value;
}

/**
* Validates that file is an image.
*
Expand Down
9 changes: 9 additions & 0 deletions src/wp-admin/includes/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ function media_handle_upload( $file_id, $post_id, $post_data = array(), $overrid
$title = sanitize_text_field( $name );
$content = '';
$excerpt = '';
$alt = '';

if ( preg_match( '#^audio#', $type ) ) {
$meta = wp_read_audio_metadata( $file );
Expand Down Expand Up @@ -399,6 +400,10 @@ function media_handle_upload( $file_id, $post_id, $post_data = array(), $overrid
if ( trim( $image_meta['caption'] ) ) {
$excerpt = $image_meta['caption'];
}

if ( trim( $image_meta['alt'] ) ) {
$alt = $image_meta['alt'];
}
}
}

Expand All @@ -421,6 +426,10 @@ function media_handle_upload( $file_id, $post_id, $post_data = array(), $overrid
// Save the data.
$attachment_id = wp_insert_attachment( $attachment, $file, $post_id, true );

if ( trim( $alt ) ) {
update_post_meta( $attachment_id, '_wp_attachment_image_alt', $alt );
}

if ( ! is_wp_error( $attachment_id ) ) {
/*
* Set a custom header with the attachment_id.
Expand Down
Loading