From 576b04d58d79d4021cf88c5eaa25663af5d905cc Mon Sep 17 00:00:00 2001 From: Justin Georgi Date: Fri, 8 May 2026 18:52:24 -0700 Subject: [PATCH] Derive from GlModelViewer Signed-off-by: Justin Georgi --- composer.json | 5 ++ extension.json | 21 +++++ includes/AnatImageHandler.php | 63 +++++++++++++ includes/AnatImageHooks.php | 77 ++++++++++++++++ includes/AnatImageTransformOutput.php | 124 ++++++++++++++++++++++++++ 5 files changed, 290 insertions(+) create mode 100644 composer.json create mode 100644 extension.json create mode 100644 includes/AnatImageHandler.php create mode 100644 includes/AnatImageHooks.php create mode 100644 includes/AnatImageTransformOutput.php diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..677166a --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "devium/toml": "*" + } +} diff --git a/extension.json b/extension.json new file mode 100644 index 0000000..b051000 --- /dev/null +++ b/extension.json @@ -0,0 +1,21 @@ +{ + "name": "AnatImageViewer", + "author": "Justin Georgi", + "url": "https://gitea.azgeorgis.net/jgeorgi/AnatImageViewer", + "description": "This extension allows the creation of .an pages in the Files namespace which will be annotated images.", + "version": "0.0.1", + "license-name": "MIT", + "type": "media", + "manifest_version": 1, + "AutoloadNamespaces": { + "MediaWiki\\Extension\\AnatImageViewer\\": "includes/" + }, + "MediaHandlers": { + "image/annotation": "MediaWiki\\Extension\\AnatImageViewer\\AnatImageHandler" + }, + "Hooks": { + "MimeMagicInit": "MediaWiki\\Extension\\AnatImageViewer\\AnatImageHooks::onMimeMagicInit", + "MimeMagicImproveFromExtension": "MediaWiki\\Extension\\AnatImageViewer\\AnatImageHooks::onMimeMagicImproveFromExtension", + "ParserFirstCallInit": "MediaWiki\\Extension\\AnatImageViewer\\AnatImageHooks::onParserFirstCallInit" + } +} \ No newline at end of file diff --git a/includes/AnatImageHandler.php b/includes/AnatImageHandler.php new file mode 100644 index 0000000..6c9eb32 --- /dev/null +++ b/includes/AnatImageHandler.php @@ -0,0 +1,63 @@ +validateParam() + * @param int $flags A bitfield, may contain self::TRANSFORM_LATER + * @return MediaTransformOutput + */ + public function doTransform($image, $dstPath, $dstUrl, $params, $flags = 0) { + return new AnatImageTransformOutput($image, $params); + } + + /** + * Small helper function to display information on the browser console + * + * Usage: + * echo ''; + * + * @param $data information to display + * @param bool $add_script_tags true to put information is inside complete script tag + */ + public static function console_log($data, $add_script_tags = false) { + $command = 'console.log('. json_encode($data, JSON_HEX_TAG).');'; + if ($add_script_tags) { + $command = ''; + } + echo $command; + } +} \ No newline at end of file diff --git a/includes/AnatImageHooks.php b/includes/AnatImageHooks.php new file mode 100644 index 0000000..58a5197 --- /dev/null +++ b/includes/AnatImageHooks.php @@ -0,0 +1,77 @@ +addExtraTypes('image/annotation an'); + $mime->addExtraInfo('image/annotation [METAIMAGE]'); + } + + /** + * MWHook: Make sure that an files (fake) get the proper mime assignement on upload + * + * @see https://www.mediawiki.org/wiki/Manual:Hooks/MimeMagicImproveFromExtension + * + * @param MimeAnalyzer $mimeAnalyzer Instance of MW MimeAnalyzer object + * @param string $ext extention of upload file + * @param string &$mime current assigned mime type + */ + public static function onMimeMagicImproveFromExtension( $mimeAnalyzer, $ext, &$mime ) { + if ( $mime !== 'image/annotation' && in_array( $ext, ['an'] ) ) { + $mime = 'image/annotation'; + } + } + + /** + * MWHook: Called when the parser initializes for the first time + * + * @param Parser $parser: Parser object being initialized + */ + static function onParserFirstCallInit( $parser ) { + $parser->setHook('anconfig', array( __CLASS__, 'renderConfigTag')); + } + + /** + * Render the config toml in a
 tag
+     * 
+     * @param $input The text inside the custom tag
+     * @param array $args Any attributes given in the tag
+     * @param Parser $parser The parser object
+     * @param PPFrame $frame The parent frame calling the parser
+     * @return string HTML string of output
+     */
+    static function renderConfigTag( $input, array $args, $parser, $frame ) {
+		return '
' . $input . '
'; + } + + /** + * Small helper function to display information on the browser console + * + * Usage: + * echo ''; + * + * @param $data information to display + * @param bool $add_script_tags true to put information is inside complete script tag + */ + public static function console_log($data, $add_script_tags = false) { + $command = 'console.log('. json_encode($data, JSON_HEX_TAG).');'; + if ($add_script_tags) { + $command = ''; + } + echo $command; + } +} \ No newline at end of file diff --git a/includes/AnatImageTransformOutput.php b/includes/AnatImageTransformOutput.php new file mode 100644 index 0000000..42ad3c4 --- /dev/null +++ b/includes/AnatImageTransformOutput.php @@ -0,0 +1,124 @@ +file = $file; + $this->parameters = $parameters; + } + + /** + * Fetch HTML for this transform output + * + * @access public + * @param array $options Associative array of options. Boolean options + * should be indicated with a value of true for true, and false or + * absent for false. + * alt Alternate text or caption + * desc-link Boolean, show a description link + * file-link Boolean, show a file download link + * custom-url-link Custom URL to link to + * custom-title-link Custom Title object to link to + * valign vertical-align property, if the output is an inline element + * img-class Class applied to the "" tag, if there is such a tag + * preview Boolean, render is being called from a preview page + * + * @return string HTML + */ + public function toHtml($options = []) { + $descriptText = $this->file->getDescriptionText(); + preg_match('/
([\S\s]*?)<\/pre>/',$descriptText,$annotDescript);
+        $metadata = toml_decode($annotDescript[1], true);
+
+        if (isset($metadata['baseImage'])) {
+            $baseImage = MediaWikiServices::getInstance()->getRepoGroup()->findFile(`File:` . $metadata['baseImage']);
+        } else {
+            $baseImage = false;
+        }
+
+        return self::buildSvg($metadata,$baseImage);
+	}
+
+    /**
+     * Build annotated SVG from TOML metadata
+     * 
+     * This takes in the metadata text from the file page (or the current editor)
+     * and produces the html string for the svg with base image and annotations.
+     * 
+     * @param string $metadata The metadata object parsed from the text
+     * @param string $baseImageUrl The full url pointing to the base image to annotate
+     * @param array $frameParams The additional user defined parameters for the viewer such as hotspot and view classes
+     * @return string Html string of the complete model-viewer element inside a div container
+     */
+    private function buildSvg($metadata, $baseImage) {
+        //Gather basic data
+        $elBaseImg = '';
+        $vbHeight = 100;
+        $vbWidth = 100;
+        if ($baseImage) {
+            $baseImageUrl = $baseImage->getFullUrl();
+            $baseHeight = $baseImage->getHeight();
+            $baseWidth = $baseImage->getWidth();
+            $baseAspect = $baseHeight / $baseWidth;
+            $vbWidth = 100 / sqrt($baseAspect);
+            $vbHeight = $baseAspect * $vbWidth;
+            $attrBase = array(
+                'class' => 'annot-base',
+                'preserveAspectRatio' => 'none',
+                'width' => $vbWidth,
+                'height' => $vbHeight,
+                'href' => $baseImageUrl
+            );
+            $elBaseImg = Html::rawElement('image', $attrBase);
+            }
+        $mainConfig = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+        $context = RequestContext::getMain();
+
+        //Render and return svg
+        $attrSvg = array(
+            'class' => 'annot-svg',
+            'version' => '1.1',
+            'viewBox' => "0 0 {$vbWidth} {$vbHeight}",
+            'xml:space' => 'preserve',
+            'xmlns' => 'http://www.w3.org/2000/svg'
+        );
+
+        return Html::rawElement('svg', $attrSvg, $elBaseImg);
+    }
+
+    /**
+     * Small helper function to display information on the browser console
+     * 
+     * Usage:
+     * echo '';
+     * 
+     * @param $data information to display
+     * @param bool $add_script_tags true to put information is inside complete script tag
+     */
+    public static function console_log($data, $add_script_tags = false) {
+        $command = 'console.log('. json_encode($data, JSON_HEX_TAG).');';
+        if ($add_script_tags) {
+            $command = '';
+        }
+        echo $command;
+    }
+}
\ No newline at end of file