scitex 2.7.0__py3-none-any.whl → 2.7.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scitex/__init__.py +6 -2
- scitex/__version__.py +1 -1
- scitex/audio/README.md +52 -0
- scitex/audio/__init__.py +384 -0
- scitex/audio/__main__.py +129 -0
- scitex/audio/_tts.py +334 -0
- scitex/audio/engines/__init__.py +44 -0
- scitex/audio/engines/base.py +275 -0
- scitex/audio/engines/elevenlabs_engine.py +143 -0
- scitex/audio/engines/gtts_engine.py +162 -0
- scitex/audio/engines/pyttsx3_engine.py +131 -0
- scitex/audio/mcp_server.py +757 -0
- scitex/bridge/_helpers.py +1 -1
- scitex/bridge/_plt_vis.py +1 -1
- scitex/bridge/_stats_vis.py +1 -1
- scitex/dev/plt/__init__.py +272 -0
- scitex/dev/plt/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/plot_mpl_bar.py +29 -0
- scitex/dev/plt/plot_mpl_barh.py +29 -0
- scitex/dev/plt/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/plot_mpl_contour.py +31 -0
- scitex/dev/plt/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/plot_mpl_fill.py +30 -0
- scitex/dev/plt/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/plot_mpl_hist.py +28 -0
- scitex/dev/plt/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/plot_mpl_pie.py +29 -0
- scitex/dev/plt/plot_mpl_plot.py +29 -0
- scitex/dev/plt/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/plot_mpl_stem.py +29 -0
- scitex/dev/plt/plot_mpl_step.py +29 -0
- scitex/dev/plt/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/plot_sns_barplot.py +29 -0
- scitex/dev/plt/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/plot_sns_histplot.py +29 -0
- scitex/dev/plt/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/plot_stx_bar.py +29 -0
- scitex/dev/plt/plot_stx_barh.py +29 -0
- scitex/dev/plt/plot_stx_box.py +28 -0
- scitex/dev/plt/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/plot_stx_contour.py +31 -0
- scitex/dev/plt/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/plot_stx_fillv.py +28 -0
- scitex/dev/plt/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/plot_stx_image.py +28 -0
- scitex/dev/plt/plot_stx_imshow.py +28 -0
- scitex/dev/plt/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/plot_stx_kde.py +28 -0
- scitex/dev/plt/plot_stx_line.py +28 -0
- scitex/dev/plt/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/plot_stx_raster.py +28 -0
- scitex/dev/plt/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/plot_stx_scatter.py +29 -0
- scitex/dev/plt/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/plot_stx_violin.py +28 -0
- scitex/dev/plt/plot_stx_violinplot.py +28 -0
- scitex/fig/__init__.py +352 -0
- scitex/{vis → fig}/backend/_parser.py +1 -1
- scitex/{vis → fig}/canvas.py +1 -1
- scitex/{vis → fig}/editor/_defaults.py +70 -5
- scitex/fig/editor/_edit.py +751 -0
- scitex/{vis → fig}/editor/_qt_editor.py +181 -1
- scitex/fig/editor/flask_editor/_bbox.py +1276 -0
- scitex/fig/editor/flask_editor/_core.py +624 -0
- scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
- scitex/fig/editor/flask_editor/_renderer.py +739 -0
- scitex/{vis → fig}/editor/flask_editor/templates/__init__.py +1 -1
- scitex/fig/editor/flask_editor/templates/_html.py +834 -0
- scitex/fig/editor/flask_editor/templates/_scripts.py +3136 -0
- scitex/{vis → fig}/editor/flask_editor/templates/_styles.py +625 -18
- scitex/{vis → fig}/io/__init__.py +13 -1
- scitex/fig/io/_bundle.py +973 -0
- scitex/{vis → fig}/io/_canvas.py +1 -1
- scitex/{vis → fig}/io/_data.py +1 -1
- scitex/{vis → fig}/io/_export.py +1 -1
- scitex/{vis → fig}/io/_load.py +1 -1
- scitex/{vis → fig}/io/_panel.py +1 -1
- scitex/{vis → fig}/io/_save.py +1 -1
- scitex/{vis → fig}/model/__init__.py +1 -1
- scitex/{vis → fig}/model/_annotations.py +1 -1
- scitex/{vis → fig}/model/_axes.py +1 -1
- scitex/{vis → fig}/model/_figure.py +1 -1
- scitex/{vis → fig}/model/_guides.py +1 -1
- scitex/{vis → fig}/model/_plot.py +1 -1
- scitex/{vis → fig}/model/_styles.py +1 -1
- scitex/{vis → fig}/utils/__init__.py +1 -1
- scitex/io/__init__.py +10 -26
- scitex/io/_bundle.py +434 -0
- scitex/io/_flush.py +5 -2
- scitex/io/_load.py +98 -0
- scitex/io/_load_modules/_H5Explorer.py +5 -2
- scitex/io/_load_modules/_canvas.py +2 -2
- scitex/io/_load_modules/_image.py +3 -4
- scitex/io/_load_modules/_txt.py +4 -2
- scitex/io/_metadata.py +34 -324
- scitex/io/_metadata_modules/__init__.py +46 -0
- scitex/io/_metadata_modules/_embed.py +70 -0
- scitex/io/_metadata_modules/_read.py +64 -0
- scitex/io/_metadata_modules/_utils.py +79 -0
- scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
- scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
- scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
- scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
- scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
- scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
- scitex/io/_metadata_modules/read_metadata_png.py +39 -0
- scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
- scitex/io/_qr_utils.py +5 -3
- scitex/io/_save.py +548 -30
- scitex/io/_save_modules/_canvas.py +3 -3
- scitex/io/_save_modules/_image.py +5 -9
- scitex/io/_save_modules/_tex.py +7 -4
- scitex/io/utils/h5_to_zarr.py +11 -9
- scitex/msword/__init__.py +255 -0
- scitex/msword/profiles.py +357 -0
- scitex/msword/reader.py +753 -0
- scitex/msword/utils.py +289 -0
- scitex/msword/writer.py +362 -0
- scitex/plt/__init__.py +5 -2
- scitex/plt/_subplots/_AxesWrapper.py +6 -6
- scitex/plt/_subplots/_AxisWrapper.py +15 -9
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
- scitex/plt/_subplots/_FigWrapper.py +6 -6
- scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
- scitex/plt/_subplots/_export_as_csv.py +35 -5
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
- scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
- scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
- scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
- scitex/plt/gallery/_generate.py +421 -14
- scitex/plt/io/__init__.py +53 -0
- scitex/plt/io/_bundle.py +490 -0
- scitex/plt/io/_layered_bundle.py +1343 -0
- scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
- scitex/plt/styles/__init__.py +14 -0
- scitex/plt/styles/presets.py +78 -0
- scitex/plt/utils/__init__.py +13 -1
- scitex/plt/utils/_collect_figure_metadata.py +10 -14
- scitex/plt/utils/_configure_mpl.py +6 -18
- scitex/plt/utils/_crop.py +32 -14
- scitex/plt/utils/_csv_column_naming.py +54 -0
- scitex/plt/utils/_figure_mm.py +116 -1
- scitex/plt/utils/_hitmap.py +1643 -0
- scitex/plt/utils/metadata/__init__.py +25 -0
- scitex/plt/utils/metadata/_core.py +9 -10
- scitex/plt/utils/metadata/_dimensions.py +6 -3
- scitex/plt/utils/metadata/_editable_export.py +405 -0
- scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
- scitex/schema/__init__.py +109 -16
- scitex/schema/_canvas.py +1 -1
- scitex/schema/_plot.py +1015 -0
- scitex/schema/_stats.py +2 -2
- scitex/stats/__init__.py +117 -0
- scitex/stats/io/__init__.py +29 -0
- scitex/stats/io/_bundle.py +156 -0
- scitex/tex/__init__.py +4 -0
- scitex/tex/_export.py +890 -0
- {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/METADATA +11 -1
- {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/RECORD +238 -170
- scitex/io/memo.md +0 -2827
- scitex/plt/REQUESTS.md +0 -191
- scitex/plt/_subplots/TODO.md +0 -53
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
- scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
- scitex/vis/__init__.py +0 -177
- scitex/vis/editor/_edit.py +0 -390
- scitex/vis/editor/flask_editor/_bbox.py +0 -529
- scitex/vis/editor/flask_editor/_core.py +0 -168
- scitex/vis/editor/flask_editor/_renderer.py +0 -393
- scitex/vis/editor/flask_editor/templates/_html.py +0 -513
- scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
- /scitex/{vis → fig}/README.md +0 -0
- /scitex/{vis → fig}/backend/__init__.py +0 -0
- /scitex/{vis → fig}/backend/_export.py +0 -0
- /scitex/{vis → fig}/backend/_render.py +0 -0
- /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
- /scitex/{vis → fig}/editor/__init__.py +0 -0
- /scitex/{vis → fig}/editor/_dearpygui_editor.py +0 -0
- /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
- /scitex/{vis → fig}/editor/_mpl_editor.py +0 -0
- /scitex/{vis → fig}/editor/_tkinter_editor.py +0 -0
- /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
- /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
- /scitex/{vis → fig}/io/_directory.py +0 -0
- /scitex/{vis → fig}/model/_plot_types.py +0 -0
- /scitex/{vis → fig}/utils/_defaults.py +0 -0
- /scitex/{vis → fig}/utils/_validate.py +0 -0
- {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/WHEEL +0 -0
- {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/entry_points.txt +0 -0
- {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata_modules/_read.py
|
|
4
|
+
|
|
5
|
+
"""Main read_metadata dispatcher."""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def read_metadata(image_path: str) -> Optional[Dict[str, Any]]:
|
|
12
|
+
"""
|
|
13
|
+
Read metadata from an image or PDF file.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
image_path: Path to the file (PNG, JPEG, SVG, or PDF)
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Dictionary containing metadata, or None if no metadata found
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
FileNotFoundError: If file doesn't exist
|
|
23
|
+
ValueError: If file format is not supported
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
>>> metadata = read_metadata('result.png')
|
|
27
|
+
>>> print(metadata['experiment'])
|
|
28
|
+
'seizure_prediction_001'
|
|
29
|
+
>>> metadata = read_metadata('result.pdf')
|
|
30
|
+
"""
|
|
31
|
+
if not os.path.exists(image_path):
|
|
32
|
+
raise FileNotFoundError(f"File not found: {image_path}")
|
|
33
|
+
|
|
34
|
+
path_lower = image_path.lower()
|
|
35
|
+
|
|
36
|
+
# Dispatch to format-specific handlers
|
|
37
|
+
if path_lower.endswith(".png"):
|
|
38
|
+
from .read_metadata_png import read_metadata_png
|
|
39
|
+
|
|
40
|
+
return read_metadata_png(image_path)
|
|
41
|
+
|
|
42
|
+
elif path_lower.endswith((".jpg", ".jpeg")):
|
|
43
|
+
from .read_metadata_jpeg import read_metadata_jpeg
|
|
44
|
+
|
|
45
|
+
return read_metadata_jpeg(image_path)
|
|
46
|
+
|
|
47
|
+
elif path_lower.endswith(".svg"):
|
|
48
|
+
from .read_metadata_svg import read_metadata_svg
|
|
49
|
+
|
|
50
|
+
return read_metadata_svg(image_path)
|
|
51
|
+
|
|
52
|
+
elif path_lower.endswith(".pdf"):
|
|
53
|
+
from .read_metadata_pdf import read_metadata_pdf
|
|
54
|
+
|
|
55
|
+
return read_metadata_pdf(image_path)
|
|
56
|
+
|
|
57
|
+
else:
|
|
58
|
+
raise ValueError(
|
|
59
|
+
f"Unsupported file format: {image_path}. "
|
|
60
|
+
"Only PNG, JPEG, SVG, and PDF formats are supported."
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# EOF
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata/_utils.py
|
|
4
|
+
|
|
5
|
+
"""Shared utilities for metadata handling."""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def convert_for_json(obj: Any) -> Any:
|
|
12
|
+
"""
|
|
13
|
+
Convert non-JSON-serializable objects to serializable format.
|
|
14
|
+
|
|
15
|
+
Handles FixedFloat objects from figure metadata by converting
|
|
16
|
+
them to regular floats.
|
|
17
|
+
"""
|
|
18
|
+
# Handle FixedFloat (from _collect_figure_metadata)
|
|
19
|
+
if (
|
|
20
|
+
hasattr(obj, "value")
|
|
21
|
+
and hasattr(obj, "precision")
|
|
22
|
+
and hasattr(obj, "__class__")
|
|
23
|
+
and obj.__class__.__name__ == "FixedFloat"
|
|
24
|
+
):
|
|
25
|
+
return obj.value
|
|
26
|
+
|
|
27
|
+
# Handle dict recursively
|
|
28
|
+
if isinstance(obj, dict):
|
|
29
|
+
return {key: convert_for_json(value) for key, value in obj.items()}
|
|
30
|
+
|
|
31
|
+
# Handle list recursively
|
|
32
|
+
if isinstance(obj, list):
|
|
33
|
+
return [convert_for_json(item) for item in obj]
|
|
34
|
+
|
|
35
|
+
# Handle tuple
|
|
36
|
+
if isinstance(obj, tuple):
|
|
37
|
+
return [convert_for_json(item) for item in obj]
|
|
38
|
+
|
|
39
|
+
# Handle numpy arrays
|
|
40
|
+
if hasattr(obj, "tolist"):
|
|
41
|
+
return obj.tolist()
|
|
42
|
+
|
|
43
|
+
# Default: return as-is
|
|
44
|
+
return obj
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def serialize_metadata(metadata: Dict[str, Any]) -> str:
|
|
48
|
+
"""Serialize metadata to JSON string."""
|
|
49
|
+
metadata = convert_for_json(metadata)
|
|
50
|
+
try:
|
|
51
|
+
return json.dumps(metadata, ensure_ascii=False, indent=2)
|
|
52
|
+
except (TypeError, ValueError) as e:
|
|
53
|
+
raise ValueError(f"Metadata must be JSON serializable: {e}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def has_metadata(image_path: str) -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Check if an image file has embedded metadata.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
image_path: Path to the image file
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
True if metadata exists, False otherwise
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> if has_metadata('result.png'):
|
|
68
|
+
... print(read_metadata('result.png'))
|
|
69
|
+
"""
|
|
70
|
+
from ._read import read_metadata
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
metadata = read_metadata(image_path)
|
|
74
|
+
return metadata is not None
|
|
75
|
+
except:
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# EOF
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata/embed_metadata_jpeg.py
|
|
4
|
+
|
|
5
|
+
"""JPEG metadata embedding using EXIF ImageDescription field."""
|
|
6
|
+
|
|
7
|
+
from PIL import Image
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def embed_metadata_jpeg(image_path: str, metadata_json: str) -> None:
|
|
11
|
+
"""
|
|
12
|
+
Embed metadata into a JPEG file using EXIF ImageDescription field.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
image_path: Path to the JPEG file.
|
|
16
|
+
metadata_json: JSON string of metadata to embed.
|
|
17
|
+
|
|
18
|
+
Raises:
|
|
19
|
+
ImportError: If piexif is not installed.
|
|
20
|
+
"""
|
|
21
|
+
try:
|
|
22
|
+
import piexif
|
|
23
|
+
except ImportError:
|
|
24
|
+
raise ImportError(
|
|
25
|
+
"piexif is required for JPEG metadata support. "
|
|
26
|
+
"Install with: pip install piexif"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
img = Image.open(image_path)
|
|
30
|
+
|
|
31
|
+
# Convert to RGB if necessary (JPEG doesn't support RGBA)
|
|
32
|
+
if img.mode in ("RGBA", "LA", "P"):
|
|
33
|
+
rgb_img = Image.new("RGB", img.size, (255, 255, 255))
|
|
34
|
+
if img.mode == "P":
|
|
35
|
+
img = img.convert("RGBA")
|
|
36
|
+
if img.mode in ("RGBA", "LA"):
|
|
37
|
+
rgb_img.paste(img, mask=img.split()[-1])
|
|
38
|
+
else:
|
|
39
|
+
rgb_img.paste(img)
|
|
40
|
+
img = rgb_img
|
|
41
|
+
|
|
42
|
+
# Create EXIF dict with metadata in ImageDescription field
|
|
43
|
+
exif_dict = {
|
|
44
|
+
"0th": {piexif.ImageIFD.ImageDescription: metadata_json.encode("utf-8")},
|
|
45
|
+
"Exif": {},
|
|
46
|
+
"GPS": {},
|
|
47
|
+
"1st": {},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Try to preserve existing EXIF data
|
|
51
|
+
try:
|
|
52
|
+
existing_exif = piexif.load(img.info.get("exif", b""))
|
|
53
|
+
# Merge with new metadata (prioritize new metadata)
|
|
54
|
+
for ifd in ["Exif", "GPS", "1st"]:
|
|
55
|
+
if ifd in existing_exif:
|
|
56
|
+
exif_dict[ifd].update(existing_exif[ifd])
|
|
57
|
+
except:
|
|
58
|
+
pass # If existing EXIF is corrupted, just use new metadata
|
|
59
|
+
|
|
60
|
+
exif_bytes = piexif.dump(exif_dict)
|
|
61
|
+
|
|
62
|
+
# Save with EXIF metadata (quality=100 for maximum quality)
|
|
63
|
+
img.save(
|
|
64
|
+
image_path,
|
|
65
|
+
"JPEG",
|
|
66
|
+
quality=100,
|
|
67
|
+
subsampling=0,
|
|
68
|
+
optimize=False,
|
|
69
|
+
exif=exif_bytes,
|
|
70
|
+
)
|
|
71
|
+
img.close()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# EOF
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata_modules/embed_metadata_pdf.py
|
|
4
|
+
|
|
5
|
+
"""PDF metadata embedding using PDF Info Dictionary."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def embed_metadata_pdf(image_path: str, metadata_json: str, metadata: dict) -> None:
|
|
9
|
+
"""
|
|
10
|
+
Embed metadata into a PDF file using PDF Info Dictionary.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
image_path: Path to the PDF file.
|
|
14
|
+
metadata_json: JSON string of metadata to embed.
|
|
15
|
+
metadata: Original metadata dict for extracting title/author.
|
|
16
|
+
|
|
17
|
+
Raises:
|
|
18
|
+
ImportError: If pypdf is not installed.
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
from pypdf import PdfReader, PdfWriter
|
|
22
|
+
except ImportError:
|
|
23
|
+
raise ImportError(
|
|
24
|
+
"pypdf is required for PDF metadata support. "
|
|
25
|
+
"Install with: pip install pypdf"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Read existing PDF
|
|
29
|
+
reader = PdfReader(image_path)
|
|
30
|
+
writer = PdfWriter()
|
|
31
|
+
|
|
32
|
+
# Copy all pages
|
|
33
|
+
for page in reader.pages:
|
|
34
|
+
writer.add_page(page)
|
|
35
|
+
|
|
36
|
+
# Prepare metadata for PDF Info Dictionary
|
|
37
|
+
pdf_metadata = {
|
|
38
|
+
"/Title": metadata.get("title", ""),
|
|
39
|
+
"/Author": metadata.get("author", ""),
|
|
40
|
+
"/Subject": metadata_json, # Store full JSON in Subject field
|
|
41
|
+
"/Creator": "SciTeX",
|
|
42
|
+
"/Producer": "SciTeX",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Add metadata
|
|
46
|
+
writer.add_metadata(pdf_metadata)
|
|
47
|
+
|
|
48
|
+
# Write back to file
|
|
49
|
+
with open(image_path, "wb") as output_file:
|
|
50
|
+
writer.write(output_file)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# EOF
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata/embed_metadata_png.py
|
|
4
|
+
|
|
5
|
+
"""PNG metadata embedding using tEXt chunks."""
|
|
6
|
+
|
|
7
|
+
from PIL import Image
|
|
8
|
+
from PIL.PngImagePlugin import PngInfo
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def embed_metadata_png(image_path: str, metadata_json: str) -> None:
|
|
12
|
+
"""
|
|
13
|
+
Embed metadata into a PNG file using tEXt chunks.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
image_path: Path to the PNG file.
|
|
17
|
+
metadata_json: JSON string of metadata to embed.
|
|
18
|
+
"""
|
|
19
|
+
img = Image.open(image_path)
|
|
20
|
+
pnginfo = PngInfo()
|
|
21
|
+
pnginfo.add_text("scitex_metadata", metadata_json)
|
|
22
|
+
img.save(image_path, "PNG", pnginfo=pnginfo)
|
|
23
|
+
img.close()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# EOF
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata_modules/embed_metadata_svg.py
|
|
4
|
+
|
|
5
|
+
"""SVG metadata embedding using <metadata> element."""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def embed_metadata_svg(image_path: str, metadata_json: str) -> None:
|
|
11
|
+
"""
|
|
12
|
+
Embed metadata into an SVG file using <metadata> element.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
image_path: Path to the SVG file.
|
|
16
|
+
metadata_json: JSON string of metadata to embed.
|
|
17
|
+
|
|
18
|
+
Raises:
|
|
19
|
+
ValueError: If the SVG file is invalid.
|
|
20
|
+
"""
|
|
21
|
+
with open(image_path, "r", encoding="utf-8") as f:
|
|
22
|
+
svg_content = f.read()
|
|
23
|
+
|
|
24
|
+
# Remove existing scitex metadata if present
|
|
25
|
+
svg_content = re.sub(
|
|
26
|
+
r'<metadata[^>]*id="scitex_metadata"[^>]*>.*?</metadata>',
|
|
27
|
+
"",
|
|
28
|
+
svg_content,
|
|
29
|
+
flags=re.DOTALL,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Find the opening <svg> tag and insert metadata after it
|
|
33
|
+
svg_match = re.search(r"(<svg[^>]*>)", svg_content)
|
|
34
|
+
if svg_match:
|
|
35
|
+
svg_tag_end = svg_match.end()
|
|
36
|
+
# Create metadata element with scitex data
|
|
37
|
+
metadata_element = (
|
|
38
|
+
f'\n<metadata id="scitex_metadata">'
|
|
39
|
+
f"<scitex:data>{metadata_json}</scitex:data>"
|
|
40
|
+
f"</metadata>\n"
|
|
41
|
+
)
|
|
42
|
+
svg_content = (
|
|
43
|
+
svg_content[:svg_tag_end]
|
|
44
|
+
+ metadata_element
|
|
45
|
+
+ svg_content[svg_tag_end:]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Ensure scitex namespace is declared in svg tag if not present
|
|
49
|
+
if "xmlns:scitex" not in svg_content:
|
|
50
|
+
svg_content = svg_content.replace(
|
|
51
|
+
"<svg",
|
|
52
|
+
'<svg xmlns:scitex="http://scitex.io/metadata"',
|
|
53
|
+
1,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
with open(image_path, "w", encoding="utf-8") as f:
|
|
57
|
+
f.write(svg_content)
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError(f"Invalid SVG file: {image_path}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# EOF
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata_modules/read_metadata_jpeg.py
|
|
4
|
+
|
|
5
|
+
"""JPEG metadata reading from EXIF ImageDescription field."""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from PIL import Image
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def read_metadata_jpeg(image_path: str) -> Optional[Dict[str, Any]]:
|
|
14
|
+
"""
|
|
15
|
+
Read metadata from a JPEG file.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
image_path: Path to the JPEG file.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Dictionary containing metadata, or None if no metadata found.
|
|
22
|
+
"""
|
|
23
|
+
metadata = None
|
|
24
|
+
img = Image.open(image_path)
|
|
25
|
+
try:
|
|
26
|
+
try:
|
|
27
|
+
import piexif
|
|
28
|
+
|
|
29
|
+
# Load EXIF data
|
|
30
|
+
if "exif" in img.info:
|
|
31
|
+
exif_dict = piexif.load(img.info["exif"])
|
|
32
|
+
|
|
33
|
+
# Try to read ImageDescription field
|
|
34
|
+
if piexif.ImageIFD.ImageDescription in exif_dict.get("0th", {}):
|
|
35
|
+
description = exif_dict["0th"][piexif.ImageIFD.ImageDescription]
|
|
36
|
+
|
|
37
|
+
# Decode bytes to string
|
|
38
|
+
if isinstance(description, bytes):
|
|
39
|
+
description = description.decode("utf-8", errors="ignore")
|
|
40
|
+
|
|
41
|
+
# Try to parse as JSON
|
|
42
|
+
try:
|
|
43
|
+
metadata = json.loads(description)
|
|
44
|
+
except json.JSONDecodeError:
|
|
45
|
+
# If not JSON, return as raw text
|
|
46
|
+
metadata = {"raw": description}
|
|
47
|
+
except ImportError:
|
|
48
|
+
pass # piexif not available, return None
|
|
49
|
+
except Exception:
|
|
50
|
+
pass # EXIF data corrupted or not readable
|
|
51
|
+
finally:
|
|
52
|
+
img.close()
|
|
53
|
+
|
|
54
|
+
return metadata
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# EOF
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata_modules/read_metadata_pdf.py
|
|
4
|
+
|
|
5
|
+
"""PDF metadata reading from PDF Info Dictionary."""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def read_metadata_pdf(image_path: str) -> Optional[Dict[str, Any]]:
|
|
12
|
+
"""
|
|
13
|
+
Read metadata from a PDF file.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
image_path: Path to the PDF file.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Dictionary containing metadata, or None if no metadata found.
|
|
20
|
+
"""
|
|
21
|
+
metadata = None
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from pypdf import PdfReader
|
|
25
|
+
|
|
26
|
+
reader = PdfReader(image_path)
|
|
27
|
+
|
|
28
|
+
# Try to read metadata from PDF Info Dictionary
|
|
29
|
+
if reader.metadata:
|
|
30
|
+
# Check Subject field for JSON metadata
|
|
31
|
+
if "/Subject" in reader.metadata:
|
|
32
|
+
subject = reader.metadata["/Subject"]
|
|
33
|
+
try:
|
|
34
|
+
metadata = json.loads(subject)
|
|
35
|
+
except json.JSONDecodeError:
|
|
36
|
+
# If not JSON, create metadata dict from available fields
|
|
37
|
+
metadata = {
|
|
38
|
+
"title": reader.metadata.get("/Title", ""),
|
|
39
|
+
"author": reader.metadata.get("/Author", ""),
|
|
40
|
+
"subject": subject,
|
|
41
|
+
"creator": reader.metadata.get("/Creator", ""),
|
|
42
|
+
}
|
|
43
|
+
except ImportError:
|
|
44
|
+
pass # pypdf not available, return None
|
|
45
|
+
except Exception:
|
|
46
|
+
pass # PDF metadata corrupted or not readable
|
|
47
|
+
|
|
48
|
+
return metadata
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# EOF
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata_modules/read_metadata_png.py
|
|
4
|
+
|
|
5
|
+
"""PNG metadata reading from tEXt chunks."""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from PIL import Image
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def read_metadata_png(image_path: str) -> Optional[Dict[str, Any]]:
|
|
14
|
+
"""
|
|
15
|
+
Read metadata from a PNG file.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
image_path: Path to the PNG file.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Dictionary containing metadata, or None if no metadata found.
|
|
22
|
+
"""
|
|
23
|
+
metadata = None
|
|
24
|
+
img = Image.open(image_path)
|
|
25
|
+
try:
|
|
26
|
+
if hasattr(img, "info") and "scitex_metadata" in img.info:
|
|
27
|
+
metadata_json = img.info["scitex_metadata"]
|
|
28
|
+
try:
|
|
29
|
+
metadata = json.loads(metadata_json)
|
|
30
|
+
except json.JSONDecodeError:
|
|
31
|
+
# Metadata exists but is not valid JSON
|
|
32
|
+
metadata = {"raw": metadata_json}
|
|
33
|
+
finally:
|
|
34
|
+
img.close()
|
|
35
|
+
|
|
36
|
+
return metadata
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# EOF
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata_modules/read_metadata_svg.py
|
|
4
|
+
|
|
5
|
+
"""SVG metadata reading from <metadata> element."""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def read_metadata_svg(image_path: str) -> Optional[Dict[str, Any]]:
|
|
13
|
+
"""
|
|
14
|
+
Read metadata from an SVG file.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
image_path: Path to the SVG file.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Dictionary containing metadata, or None if no metadata found.
|
|
21
|
+
"""
|
|
22
|
+
metadata = None
|
|
23
|
+
|
|
24
|
+
with open(image_path, "r", encoding="utf-8") as f:
|
|
25
|
+
svg_content = f.read()
|
|
26
|
+
|
|
27
|
+
# Look for scitex metadata element
|
|
28
|
+
match = re.search(
|
|
29
|
+
r'<metadata[^>]*id="scitex_metadata"[^>]*>.*?'
|
|
30
|
+
r"<scitex:data>(.*?)</scitex:data>.*?</metadata>",
|
|
31
|
+
svg_content,
|
|
32
|
+
flags=re.DOTALL,
|
|
33
|
+
)
|
|
34
|
+
if match:
|
|
35
|
+
metadata_json = match.group(1)
|
|
36
|
+
try:
|
|
37
|
+
metadata = json.loads(metadata_json)
|
|
38
|
+
except json.JSONDecodeError:
|
|
39
|
+
metadata = {"raw": metadata_json}
|
|
40
|
+
|
|
41
|
+
return metadata
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# EOF
|
scitex/io/_qr_utils.py
CHANGED
|
@@ -9,9 +9,13 @@ import json
|
|
|
9
9
|
import os
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
+
from scitex import logging
|
|
13
|
+
|
|
12
14
|
__FILE__ = __file__
|
|
13
15
|
__DIR__ = os.path.dirname(__FILE__)
|
|
14
16
|
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
def add_qr_to_figure(fig, metadata, position="bottom-right", size=0.08):
|
|
17
21
|
"""
|
|
@@ -30,9 +34,7 @@ def add_qr_to_figure(fig, metadata, position="bottom-right", size=0.08):
|
|
|
30
34
|
import qrcode
|
|
31
35
|
from PIL import Image as PILImage
|
|
32
36
|
except ImportError:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
warnings.warn(
|
|
37
|
+
logger.warning(
|
|
36
38
|
"qrcode library not available. Install with: pip install qrcode[pil]"
|
|
37
39
|
)
|
|
38
40
|
return fig
|