svg_embed.module in SVG Embed 7
SVG Embed. Provides a filter for text formats that includes and on the fly translates SVG files into text fields.
File
svg_embed.moduleView source
<?php
/**
* @file
* SVG Embed. Provides a filter for text formats that includes and on the
* fly translates SVG files into text fields.
*/
/**
* Implements hook_filter_info().
*
* @return array
* filters to add
*/
function svg_embed_filter_info() {
$filters['filter_svg_embed'] = array(
'title' => t('SVG Embed Filter'),
'description' => t('Embed SVG files into content and translate them on the fly.'),
'process callback' => 'svg_embed_filter_svg_embed_process',
'tips callback' => 'svg_embed_filter_svg_embed_tips',
'cache' => FALSE,
);
return $filters;
}
/**
* Implements hook_locale().
*
* @param string $op
* the operation
*
* @return array
* groups to add
*/
function svg_embed_locale($op = 'groups') {
switch ($op) {
case 'groups':
return array(
'svg_embed' => t('Embedded SVG files'),
);
}
return array();
}
/**
* Implements hook_menu().
*/
function svg_embed_menu() {
$items['admin/config/regional/translate/svgextract'] = array(
'title' => 'Embedded SVG',
'description' => 'Extracts strings from embedded SVG files into po files for translation and re-imports them into the proper text group.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'svg_embed_manage_po_files',
),
'access arguments' => array(
'translate interface',
),
'type' => MENU_LOCAL_TASK,
'weight' => 99,
);
$items['svg-embed/%/%file/fallback.png'] = array(
'title' => 'Fallback PNG',
'page callback' => 'svg_embed_fallback_png',
'page arguments' => array(
1,
2,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_filter_FILTER_process().
*/
function svg_embed_filter_svg_embed_process($text, $filter, $format, $langcode, $cache, $cache_id) {
if (empty($text)) {
return '';
}
if ($langcode == LANGUAGE_NONE) {
global $user;
$language = user_preferred_language($user);
$langcode = $language->language;
}
if (preg_match_all("/\\[svg:([^\\:;\\?\\*\\{\\}\\/]+)\\]/", $text, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
if (is_numeric($match[1])) {
$field = 'fid';
}
else {
$field = 'filename';
}
$file = db_select('file_managed', 'f')
->fields('f', array(
'fid',
'uri',
))
->condition('f.' . $field, $match[1])
->execute()
->fetch();
if (!empty($file)) {
$text = str_replace('[svg:' . $match[1] . ']', _svg_embed_get_svg($file, $langcode), $text);
}
}
}
return $text;
}
/**
* Implements hook_filter_FILTER_tips().
*/
function svg_embed_filter_svg_embed_tips($filter, $format, $long) {
if ($long) {
return t('To insert an SVG graphic include [svg:FID] into the text. The file should already be uploaded to your Drupal site and you should then replace FID with either the file id (if known) or with the filename.');
}
else {
return t('To insert an SVG graphic include [svg:FID] into the text.');
}
}
/**
* Implements hook_file_download().
*/
function svg_embed_file_download($uri) {
global $user;
if (strpos(file_uri_target($uri), 'svg_embed/') === 0) {
if (user_access('translate interface')) {
return array(
'Pragma' => 'public',
'Expires' => 0,
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
'Content-Type' => 'application/force-download',
);
}
else {
return -1;
}
}
return NULL;
}
/**
* Form to download and upload PO files for embedded SVG files.
*
* @param array $form
* form definition
* @param array $form_state
* form state
*
* @return array
* renderable form
*/
function svg_embed_manage_po_files($form, $form_state) {
$strings = array();
$files = db_select('file_managed', 'f')
->fields('f', array(
'uri',
))
->condition('f.filemime', 'image/svg+xml')
->execute()
->fetchAll();
foreach ($files as $file) {
$svg = file_get_contents($file->uri);
if (!empty($svg)) {
$xml = new SimpleXMLElement($svg);
if (!empty($xml)) {
_svg_embed_extract($xml, $strings);
}
}
}
$directory = 'temporary://svg_embed';
file_prepare_directory($directory, FILE_CREATE_DIRECTORY + FILE_MODIFY_PERMISSIONS);
$all_languages = language_list('enabled');
$languages = array_shift($all_languages);
$output = '';
foreach ($languages as $langcode => $item) {
$po = 'msgid ""' . PHP_EOL;
$po .= 'msgstr "PO-Revision-Date: 2012-03-10 14:34+0100\\nMIME-Version: 1.0\\nContent-Type: text/plain; charset=utf-8\\nContent-Transfer-Encoding: 8bit\\n\\n"' . PHP_EOL;
foreach ($strings as $string => $lng) {
$po .= PHP_EOL;
$po .= 'msgid "' . str_replace('"', '\\"', $string) . '"' . PHP_EOL;
$po .= 'msgstr "' . str_replace('"', '\\"', $lng[$langcode]) . '"' . PHP_EOL;
}
$filename = $directory . '/' . $langcode . '.po';
file_put_contents($filename, $po);
$output .= '<div>' . l($langcode, 'system/temporary/svg_embed/' . $langcode . '.po') . '</div>';
}
$form['download'] = array(
'#type' => 'fieldset',
'#title' => t('Download PO-files'),
);
$form['download']['links'] = array(
'#markup' => $output,
);
// Get all languages, except English.
drupal_static_reset('language_list');
$names = locale_language_list('name');
unset($names['en']);
if (!count($names)) {
$languages = _locale_prepare_predefined_list();
$default = key($languages);
}
else {
$languages = array(
t('Already added languages') => $names,
t('Languages not yet added') => _locale_prepare_predefined_list(),
);
$default = key($names);
}
$form['upload'] = array(
'#type' => 'fieldset',
'#title' => t('Upload PO file'),
);
$form['upload']['langcode'] = array(
'#type' => 'select',
'#title' => t('Import into'),
'#options' => $languages,
'#default_value' => $default,
'#description' => t('Choose the language you want to add strings into. If you choose a language which is not yet set up, it will be added.'),
);
$form['upload']['file'] = array(
'#type' => 'file',
'#title' => t('Language file'),
'#size' => 50,
'#description' => t('A Gettext Portable Object (<em>.po</em>) file.'),
);
$form['upload']['mode'] = array(
'#type' => 'radios',
'#title' => t('Mode'),
'#default_value' => LOCALE_IMPORT_KEEP,
'#options' => array(
LOCALE_IMPORT_OVERWRITE => t('Strings in the uploaded file replace existing ones, new ones are added. The plural format is updated.'),
LOCALE_IMPORT_KEEP => t('Existing strings and the plural format are kept, only new strings are added.'),
),
);
$form['upload']['submit'] = array(
'#type' => 'submit',
'#value' => t('Import'),
);
return $form;
}
/**
* Process the import form submission.
*/
function svg_embed_manage_po_files_submit($form, &$form_state) {
$validators = array(
'file_validate_extensions' => array(
'po',
),
);
// Ensure we have the file uploaded.
if ($file = file_save_upload('file', $validators)) {
// Now import strings into the language.
if ($return = _locale_import_po($file, $form_state['values']['langcode'], $form_state['values']['mode'], 'svg_embed') == FALSE) {
$variables = array(
'%filename' => $file->filename,
);
drupal_set_message(t('The translation import of %filename failed.', $variables), 'error');
watchdog('locale', 'The translation import of %filename failed.', $variables, WATCHDOG_ERROR);
}
}
else {
drupal_set_message(t('File to import not found.'), 'error');
}
$form_state['redirect'] = 'admin/config/regional/translate/svgextract';
}
/**
* Menu callback to localise and convert the SVG to PNG which will then be
* delivered to the requesting browser.
*
* @param string $langcode
* @param stdClass $file
*/
function svg_embed_fallback_png($langcode, $file) {
if (!class_exists('Imagick')) {
drupal_not_found();
exit;
}
$svg = _svg_embed_get_svg($file, $langcode, FALSE);
$im = new Imagick();
$im
->readImageBlob($svg);
$im
->setImageFormat("png24");
header("Content-Type: image/png");
print $im
->getImagesBlob();
exit;
}
/**
* Helper function to read the SCG, localise it and prepare it for output.
*
* @param stdClass $file
* @param string $langcode
* @param bool $include_fallback
* @return string
*/
function _svg_embed_get_svg($file, $langcode, $include_fallback = TRUE) {
$svg = file_get_contents($file->uri);
if (!empty($svg)) {
$xml = new SimpleXMLElement($svg);
if (!empty($xml)) {
if ($langcode != LANGUAGE_NONE) {
_svg_embed_translate($xml, $langcode);
}
if ($include_fallback && class_exists('Imagick')) {
$fallback = $xml
->addChild('image');
$fallback
->addAttribute('src', url('svg-embed/' . $langcode . '/' . $file->fid . '/fallback.png'));
}
$svg = $xml
->asXML();
$svg_tag = strpos($svg, '<svg');
return $include_fallback ? substr($svg, $svg_tag) : $svg;
}
}
return '';
}
/**
* Helper function called recursively to translate all strings in an SVG file.
*
* @param SimpleXMLElement $xml
* the SVG graphic code
* @param string $langcode
* the language code to which we need to translate
*/
function _svg_embed_translate($xml, $langcode) {
foreach ($xml as $child) {
_svg_embed_translate($child, $langcode);
if (isset($child->text) || isset($child->tspan)) {
if (isset($child->text->tspan)) {
$text = $child->text->tspan;
}
elseif (isset($child->tspan)) {
$text = $child->tspan;
}
else {
$text = $child->text;
}
$i = 0;
while (TRUE) {
$string = (string) $text[$i];
if (empty($string)) {
break;
}
$string = trim($string);
if (!empty($string)) {
$query = db_select('locales_source', 's');
$query
->leftJoin('locales_target', 't', 's.lid = t.lid');
$translation = $query
->fields('t', array(
'translation',
))
->condition('s.source', $string)
->condition('s.textgroup', 'svg_embed')
->condition('t.language', $langcode)
->execute()
->fetchField();
$text[$i][0] = empty($translation) ? $string : $translation;
}
$i++;
}
}
}
}
/**
* Helper function to find all strings and all their existing translations, prepared to export all to a po file.
*
* @param SimpleXMLElement $xml
* the svg graphic code
* @param array $strings
* array containing all strings and translations
*/
function _svg_embed_extract($xml, &$strings) {
$all_languages = language_list('enabled');
$languages = array_shift($all_languages);
foreach ($xml as $child) {
_svg_embed_extract($child, $strings);
if (isset($child->text) || isset($child->tspan)) {
if (isset($child->text->tspan)) {
$text = $child->text->tspan;
}
elseif (isset($child->tspan)) {
$text = $child->tspan;
}
else {
$text = $child->text;
}
$i = 0;
while (TRUE) {
$string = (string) $text[$i];
if (empty($string)) {
break;
}
$string = trim($string);
if (!empty($string)) {
if (empty($strings[$string])) {
$strings[$string] = array();
foreach ($languages as $langcode => $language) {
$strings[$string][$langcode] = '';
}
$query = db_select('locales_source', 's');
$query
->leftJoin('locales_target', 't', 's.lid = t.lid');
$translations = $query
->fields('t', array(
'translation',
'language',
))
->condition('s.source', $string)
->condition('s.textgroup', 'svg_embed')
->condition('t.language', 'en', '<>')
->execute()
->fetchAll();
foreach ($translations as $translation) {
$strings[$string][$translation->language] = $translation->translation;
}
}
}
$i++;
}
}
}
}
Functions
Name | Description |
---|---|
svg_embed_fallback_png | Menu callback to localise and convert the SVG to PNG which will then be delivered to the requesting browser. |
svg_embed_file_download | Implements hook_file_download(). |
svg_embed_filter_info | Implements hook_filter_info(). |
svg_embed_filter_svg_embed_process | Implements hook_filter_FILTER_process(). |
svg_embed_filter_svg_embed_tips | Implements hook_filter_FILTER_tips(). |
svg_embed_locale | Implements hook_locale(). |
svg_embed_manage_po_files | Form to download and upload PO files for embedded SVG files. |
svg_embed_manage_po_files_submit | Process the import form submission. |
svg_embed_menu | Implements hook_menu(). |
_svg_embed_extract | Helper function to find all strings and all their existing translations, prepared to export all to a po file. |
_svg_embed_get_svg | Helper function to read the SCG, localise it and prepare it for output. |
_svg_embed_translate | Helper function called recursively to translate all strings in an SVG file. |