SVG computed raw data

Created on 20 March 2023, over 1 year ago
Updated 13 May 2024, about 2 months ago

Problem/Motivation

Building on this request I think: ✨ 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

Needs work

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 9 months 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?

Production build 0.69.0 2024