Skip to content

admin/dorfarchiv

PublicMirror

Mirror of https://forge.levinkeller.de/levin/dorfarchiv.git

73 lines · 2.5 KB
1import {visit, SKIP} from 'unist-util-visit';
2import path from 'node:path';
3import fs from 'node:fs';
4import ExifReader from 'exifreader';
5
6/**
7 * Remark plugin to wrap images with figure/figcaption based on EXIF metadata
8 * Works with MDX by creating JSX elements
9 */
10export default function remarkImageFigcaption() {
11 return (tree, file) => {
12 visit(tree, 'image', (node, index, parent) => {
13 try {
14 // Skip if already processed
15 if (node.data?.processed) return;
16
17 const imageSrc = node.url;
18 if (!imageSrc || imageSrc.startsWith('http')) return;
19
20 // Get absolute path to image
21 const fileDir = path.dirname(file.path);
22 let imagePath;
23
24 if (imageSrc.startsWith('/')) {
25 imagePath = path.join(process.cwd(), 'static', imageSrc);
26 } else {
27 imagePath = path.join(fileDir, imageSrc);
28 }
29
30 // Skip if doesn't exist
31 if (!fs.existsSync(imagePath)) return;
32
33 // Read EXIF data
34 const tags = ExifReader.load(fs.readFileSync(imagePath));
35
36 const artist = tags.Artist?.description || tags.Creator?.description;
37 const copyright = tags.Copyright?.description;
38 const rights = tags.rights?.description || tags.Rights?.description || tags['Rights Usage Terms']?.description;
39
40 // Only wrap if we have metadata
41 if (!artist && !copyright && !rights) return;
42
43 // Mark the node as processed to avoid infinite loops
44 node.data = node.data || {};
45 node.data.processed = true;
46
47 // Create an MDX JSX element node to use our React component
48 // Pass the original image as a child so it gets processed correctly by Docusaurus
49 const jsxNode = {
50 type: 'mdxJsxFlowElement',
51 name: 'ImageFigure',
52 attributes: [
53 {type: 'mdxJsxAttribute', name: 'artist', value: artist || ''},
54 {type: 'mdxJsxAttribute', name: 'copyright', value: copyright || ''},
55 {type: 'mdxJsxAttribute', name: 'rights', value: rights || ''}
56 ],
57 children: [node] // Pass the original image node as child
58 };
59
60 // Replace the image node with the JSX component
61 parent.children[index] = jsxNode;
62
63 console.log('[remark-figcaption] Wrapped image:', imageSrc);
64
65 // Skip visiting the newly added node
66 return [SKIP, index];
67
68 } catch (error) {
69 console.warn(`[remark-figcaption] Failed to process image:`, error.message);
70 }
71 });
72 };
73}