Exporting and importing image dimensions with focal point

Created on 3 June 2025, about 1 month ago

I think the underlying issue here is with Focal Point β†’ itself, and I've raised an issue here, but this might be relevant to anyone else in a similar situation as mine, using Single Content Sync.

I'm migrating content that is full of media reference fields, the images contained having focal point settings. When I imported the YAMLs, I saw that the focal points were being lost, and tracked it down to this plugin not capturing those dimensions and saving them on export.

It turns out that the dimensions aren't being saved to begin with in the `crop_field_data` table by Focal Point, so even though we are getting `field_original_image_height` and `field_original_image_width` in the export, they're not available at the point in `File::exportBaseValues()` where the crop values get saved.

For now I've worked around it with the patch attached here. I'm not 100% certain of its correctness, of course, but so far it seems to be doing what I need.

✨ Feature request
Status

Active

Version

1.4

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States datawench

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

Comments & Activities

  • Issue created by @datawench
  • πŸ‡ΊπŸ‡ΈUnited States datawench
  • πŸ‡ΊπŸ‡ΈUnited States datawench

    Scratch that. I figured out how to use your event system to avoid hacking. I'm closing this, and showing my code here in case anyone's interested.

    namespace Drupal\my_custom\EventSubscriber;
    
    use Drupal;
    use Drupal\file\Entity\File;
    use Drupal\focal_point\FocalPointManager;
    use Drupal\media\Entity\Media;
    use Drupal\focal_point\FocalPointManagerInterface;
    use Drupal\single_content_sync\Event\ExportEvent;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    class ExportEventSubscriber implements EventSubscriberInterface {
    
      private array $imgMimeTypes = [
        'image/jpeg',
        'image/gif',
        'image/png',
        'image/webp',
      ];
      private FocalPointManagerInterface $focalPointManager;
      private string $cropType;
    
      public function __construct() {
        $this->focalPointManager = new FocalPointManager(Drupal::service('entity_type.manager'));
        $this->cropType          = Drupal::config('focal_point.settings')
                                          ->get('crop_type');
      }
    
      /**
       * @return array[]
       */
      public static function getSubscribedEvents(): array {
        return [
          ExportEvent::class => ['onExport'],
        ];
      }
    
      /**
       * @param \Drupal\single_content_sync\Event\ExportEvent $event
       *
       * @return void
       */
      public function onExport(ExportEvent $event) {
        if ('file' == $event->getEntity()->getEntityTypeId()) {
          /** @var File $file */
          $file    = $event->getEntity();
          $content = $event->getContent();
          if (in_array($file->getMimeType(), $this->imgMimeTypes)) {
            $crop = $this->handleFocalPoint($file);
            if (!empty($crop)) {
              $content['base_fields']['crop'] = $crop;
              $event->setContent($content);
            }
          }
        }
      }
    
      /**
       * Extract the actual focal point dimensions.
       *
       * @param \Drupal\file\Entity\File $file
       *
       * @return array
       */
      private function handleFocalPoint(File $file): array {
        /** @var \Drupal\crop\Entity\Crop $cropEntity */
        $cropEntity = $this->focalPointManager->getCropEntity($file, $this->cropType);
        $fid        = $file->id();
        if (!$cropEntity->isNew() && !$cropEntity->get('x')
                                                 ->isEmpty() && !$cropEntity->get('y')
                                                                            ->isEmpty()) {
          $media_ids = Drupal::entityQuery('media')
                              ->accessCheck(FALSE)
                              ->condition('field_media_image.target_id', $fid)
                              ->execute();
    
          // to populate the crop height and width,
          //   we need to back-reference to the media entity that uses this image,
          //   because the crop entity isn't saving it.
          if (!empty($media_ids)) {
            $media_id           = reset($media_ids);
            $media              = Media::load($media_id);
            $height             = !empty($media->get('field_original_image_height')
                                               ->getValue()) ? $media->get('field_original_image_height')
                                                                     ->getValue()[0]['value'] : 0;
            $width              = !empty($media->get('field_original_image_width')
                                               ->getValue()) ? $media->get('field_original_image_width')
                                                                     ->getValue()[0]['value'] : 0;
            $x                  = !empty($cropEntity->get('x')
                                                    ->getValue()) ? $cropEntity->get('x')
                                                                               ->getValue()[0]['value'] : 0;
            $y                  = !empty($cropEntity->get('y')
                                                    ->getValue()) ? $cropEntity->get('y')
                                                                               ->getValue()[0]['value'] : 0;
            $cropDims           = $this->focalPointManager->absoluteToRelative($x, $y, $width, $height);
            $cropDims['height'] = $height;
            $cropDims['width']  = $width;
    
            return $cropDims;
          }
    
          return [];
        }
    
        return [];
      }
    
    }
    
  • πŸ‡ΊπŸ‡ΈUnited States datawench
Production build 0.71.5 2024