SVG computed raw data

Created on 20 March 2023, over 1 year ago

Problem/Motivation

Building on this request I think: https://www.drupal.org/project/svg_image_field/issues/2999815 ✨ Expose custom SVG width/height from Field Formatter to Views & JSON Export Active
For decoupled sites, it would be useful to have raw svg data. Could we have a computed property like "raw_data" that would then get output to jsonapi?

Steps to reproduce

Proposed resolution

I don't know 100% if this is the right way to go about this but I tested this out and it seems to work...

/**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties = parent::propertyDefinitions($field_definition);

    unset($properties['display']);
    unset($properties['description']);

    $properties['alt'] = DataDefinition::create('string')
      ->setLabel(t('Alternative text'))
      ->setDescription(t("Alternative image text, for the image's 'alt' attribute."));

    $properties['title'] = DataDefinition::create('string')
      ->setLabel(t('Title'))
      ->setDescription(t("Image title text, for the image's 'title' attribute."));

    $properties['raw_data'] = DataDefinition::create('string')
      ->setLabel(t('Raw data'))
      ->setDescription(t('The svg raw xml data.'))
      ->setClass('\Drupal\svg_image_field\SvgRawComputed')
      ->setSetting('file source', 'entity')
      ->setComputed(TRUE)
      ->setInternal(FALSE);

    return $properties;
  }

The computed class: SvgRawComputed

<?php

namespace Drupal\svg_image_field;

use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\TypedData;
use Drupal\Core\TypedData\TypedDataInterface;
use enshrined\svgSanitize\Sanitizer;

/**
 * A computed property for generating raw svg data from file.
 * 
 * Required settings (below the definition's 'settings' key) are:
 *  - file source: The entity property containing the file to convert.
 */
class SvgRawComputed extends TypedData {

  /**
   * {@inheritdoc}
   */
  public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
    parent::__construct($definition, $name, $parent);

    if ($definition->getSetting('file source') === NULL) {
      throw new \InvalidArgumentException("The definition's 'file source' key has to specify the name of the text property to be processed.");
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getValue() {
    $item = $this->getParent();

    /** @var \Drupal\file\Entity\File $file */
    $file = $item->{($this->definition->getSetting('file source'))};

    if ($file) {
      $uri = $file->getFileUri();
      $svg_file = file_get_contents($uri);
      $dom = new \DOMDocument();
      libxml_use_internal_errors(TRUE);
      if (!empty($svg_file)) {
        $dom->loadXML($svg_file);
        $svg_data = $dom->saveXML();
        $svgSanitizer = new Sanitizer();
        $svgSanitizer->removeRemoteReferences(TRUE);
        $svgSanitizer->removeXMLTag(TRUE);
        $svg_data = $svgSanitizer->sanitize($svg_data);
        return $svg_data;
      }
    }
    return NULL;
  }

}

Remaining tasks

Feedback/recommendations first... I could provide a patch certainly after that provides the changes.

User interface changes

Provides raw_data property in jsonapi output. Example screenshot

✨ Feature request
Status

Active

Version

2.2

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States apmsooner

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @apmsooner
  • @apmsooner opened merge request.
  • Status changed to Needs review over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States apmsooner
  • πŸ‡ΊπŸ‡ΈUnited States apmsooner
  • Status changed to Needs work about 1 year ago
  • πŸ‡ͺπŸ‡¨Ecuador jwilson3
        $properties['raw_data'] = DataDefinition::create('string')
          ->setLabel(t('Raw data'))
          ->setDescription(t('The svg raw xml data.'))
          ->setClass('\Drupal\svg_image_field\SvgRawComputed')
          ->setSetting('file source', 'entity')
          ->setComputed(TRUE)
          ->setInternal(FALSE);
    

    If the underlying request here is to improve this for JSONAPI, then it seems like we should also provide metadata containing the URL to the SVG file, so that a decoupled frontend consumer can have the choice to render the SVG inline or via an IMG tag, or is that somehow already available?

          if (!empty($svg_file)) {
            $dom->loadXML($svg_file);
            $svg_data = $dom->saveXML();
            $svgSanitizer = new Sanitizer();
            $svgSanitizer->removeRemoteReferences(TRUE);
            $svgSanitizer->removeXMLTag(TRUE);
            $svg_data = $svgSanitizer->sanitize($svg_data);
            return $svg_data;
          }
    

    This section probably needs to respect other existing configurations for inline SVG configurations. Note that we already have SVGImageFieldFormatter::loadInlineSvgData() for this purpose. So the question is whether field formatters are in play for the JSONAPI stuff. I.e., can this code either be modified to load the settings from a configured display formatter, or refactored entirely to just call the loadInlineSvgData() function?

  • πŸ‡ΊπŸ‡ΈUnited States rrrob

    If the underlying request here is to improve this for JSONAPI, then it seems like we should also provide metadata containing the URL to the SVG file, so that a decoupled frontend consumer can have the choice to render the SVG inline or via an IMG tag, or is that somehow already available?

    The path to the SVG file is not removed, so it can still be used as an image like you mentioned. This patch just adds the raw svg code inline.

    There is one issue though. I do see the following in the Drupal log:

    Deprecated function: Creation of dynamic property Drupal\svg_image_field\SvgRawComputed::$value is deprecated in Drupal\Core\TypedData\TypedData->setValue() (line 102 of /var/www/html/web/core/lib/Drupal/Core/TypedData/TypedData.php)
    

    If I add an empty setValue function in the SvgRawComputed class, the deprecation error goes away:

      /**
       * {@inheritdoc}
       */
      public function setValue($value, $notify = TRUE) {
        // Do nothing, as we don't need to set the value directly
      }
    
Production build 0.71.5 2024