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.
Files changed (297) hide show
  1. scitex/__init__.py +6 -2
  2. scitex/__version__.py +1 -1
  3. scitex/audio/README.md +52 -0
  4. scitex/audio/__init__.py +384 -0
  5. scitex/audio/__main__.py +129 -0
  6. scitex/audio/_tts.py +334 -0
  7. scitex/audio/engines/__init__.py +44 -0
  8. scitex/audio/engines/base.py +275 -0
  9. scitex/audio/engines/elevenlabs_engine.py +143 -0
  10. scitex/audio/engines/gtts_engine.py +162 -0
  11. scitex/audio/engines/pyttsx3_engine.py +131 -0
  12. scitex/audio/mcp_server.py +757 -0
  13. scitex/bridge/_helpers.py +1 -1
  14. scitex/bridge/_plt_vis.py +1 -1
  15. scitex/bridge/_stats_vis.py +1 -1
  16. scitex/dev/plt/__init__.py +272 -0
  17. scitex/dev/plt/plot_mpl_axhline.py +28 -0
  18. scitex/dev/plt/plot_mpl_axhspan.py +28 -0
  19. scitex/dev/plt/plot_mpl_axvline.py +28 -0
  20. scitex/dev/plt/plot_mpl_axvspan.py +28 -0
  21. scitex/dev/plt/plot_mpl_bar.py +29 -0
  22. scitex/dev/plt/plot_mpl_barh.py +29 -0
  23. scitex/dev/plt/plot_mpl_boxplot.py +28 -0
  24. scitex/dev/plt/plot_mpl_contour.py +31 -0
  25. scitex/dev/plt/plot_mpl_contourf.py +31 -0
  26. scitex/dev/plt/plot_mpl_errorbar.py +30 -0
  27. scitex/dev/plt/plot_mpl_eventplot.py +28 -0
  28. scitex/dev/plt/plot_mpl_fill.py +30 -0
  29. scitex/dev/plt/plot_mpl_fill_between.py +31 -0
  30. scitex/dev/plt/plot_mpl_hexbin.py +28 -0
  31. scitex/dev/plt/plot_mpl_hist.py +28 -0
  32. scitex/dev/plt/plot_mpl_hist2d.py +28 -0
  33. scitex/dev/plt/plot_mpl_imshow.py +29 -0
  34. scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
  35. scitex/dev/plt/plot_mpl_pie.py +29 -0
  36. scitex/dev/plt/plot_mpl_plot.py +29 -0
  37. scitex/dev/plt/plot_mpl_quiver.py +31 -0
  38. scitex/dev/plt/plot_mpl_scatter.py +28 -0
  39. scitex/dev/plt/plot_mpl_stackplot.py +31 -0
  40. scitex/dev/plt/plot_mpl_stem.py +29 -0
  41. scitex/dev/plt/plot_mpl_step.py +29 -0
  42. scitex/dev/plt/plot_mpl_violinplot.py +28 -0
  43. scitex/dev/plt/plot_sns_barplot.py +29 -0
  44. scitex/dev/plt/plot_sns_boxplot.py +29 -0
  45. scitex/dev/plt/plot_sns_heatmap.py +28 -0
  46. scitex/dev/plt/plot_sns_histplot.py +29 -0
  47. scitex/dev/plt/plot_sns_kdeplot.py +29 -0
  48. scitex/dev/plt/plot_sns_lineplot.py +31 -0
  49. scitex/dev/plt/plot_sns_scatterplot.py +29 -0
  50. scitex/dev/plt/plot_sns_stripplot.py +29 -0
  51. scitex/dev/plt/plot_sns_swarmplot.py +29 -0
  52. scitex/dev/plt/plot_sns_violinplot.py +29 -0
  53. scitex/dev/plt/plot_stx_bar.py +29 -0
  54. scitex/dev/plt/plot_stx_barh.py +29 -0
  55. scitex/dev/plt/plot_stx_box.py +28 -0
  56. scitex/dev/plt/plot_stx_boxplot.py +28 -0
  57. scitex/dev/plt/plot_stx_conf_mat.py +28 -0
  58. scitex/dev/plt/plot_stx_contour.py +31 -0
  59. scitex/dev/plt/plot_stx_ecdf.py +28 -0
  60. scitex/dev/plt/plot_stx_errorbar.py +30 -0
  61. scitex/dev/plt/plot_stx_fill_between.py +31 -0
  62. scitex/dev/plt/plot_stx_fillv.py +28 -0
  63. scitex/dev/plt/plot_stx_heatmap.py +28 -0
  64. scitex/dev/plt/plot_stx_image.py +28 -0
  65. scitex/dev/plt/plot_stx_imshow.py +28 -0
  66. scitex/dev/plt/plot_stx_joyplot.py +28 -0
  67. scitex/dev/plt/plot_stx_kde.py +28 -0
  68. scitex/dev/plt/plot_stx_line.py +28 -0
  69. scitex/dev/plt/plot_stx_mean_ci.py +28 -0
  70. scitex/dev/plt/plot_stx_mean_std.py +28 -0
  71. scitex/dev/plt/plot_stx_median_iqr.py +28 -0
  72. scitex/dev/plt/plot_stx_raster.py +28 -0
  73. scitex/dev/plt/plot_stx_rectangle.py +28 -0
  74. scitex/dev/plt/plot_stx_scatter.py +29 -0
  75. scitex/dev/plt/plot_stx_shaded_line.py +29 -0
  76. scitex/dev/plt/plot_stx_violin.py +28 -0
  77. scitex/dev/plt/plot_stx_violinplot.py +28 -0
  78. scitex/fig/__init__.py +352 -0
  79. scitex/{vis → fig}/backend/_parser.py +1 -1
  80. scitex/{vis → fig}/canvas.py +1 -1
  81. scitex/{vis → fig}/editor/_defaults.py +70 -5
  82. scitex/fig/editor/_edit.py +751 -0
  83. scitex/{vis → fig}/editor/_qt_editor.py +181 -1
  84. scitex/fig/editor/flask_editor/_bbox.py +1276 -0
  85. scitex/fig/editor/flask_editor/_core.py +624 -0
  86. scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
  87. scitex/fig/editor/flask_editor/_renderer.py +739 -0
  88. scitex/{vis → fig}/editor/flask_editor/templates/__init__.py +1 -1
  89. scitex/fig/editor/flask_editor/templates/_html.py +834 -0
  90. scitex/fig/editor/flask_editor/templates/_scripts.py +3136 -0
  91. scitex/{vis → fig}/editor/flask_editor/templates/_styles.py +625 -18
  92. scitex/{vis → fig}/io/__init__.py +13 -1
  93. scitex/fig/io/_bundle.py +973 -0
  94. scitex/{vis → fig}/io/_canvas.py +1 -1
  95. scitex/{vis → fig}/io/_data.py +1 -1
  96. scitex/{vis → fig}/io/_export.py +1 -1
  97. scitex/{vis → fig}/io/_load.py +1 -1
  98. scitex/{vis → fig}/io/_panel.py +1 -1
  99. scitex/{vis → fig}/io/_save.py +1 -1
  100. scitex/{vis → fig}/model/__init__.py +1 -1
  101. scitex/{vis → fig}/model/_annotations.py +1 -1
  102. scitex/{vis → fig}/model/_axes.py +1 -1
  103. scitex/{vis → fig}/model/_figure.py +1 -1
  104. scitex/{vis → fig}/model/_guides.py +1 -1
  105. scitex/{vis → fig}/model/_plot.py +1 -1
  106. scitex/{vis → fig}/model/_styles.py +1 -1
  107. scitex/{vis → fig}/utils/__init__.py +1 -1
  108. scitex/io/__init__.py +10 -26
  109. scitex/io/_bundle.py +434 -0
  110. scitex/io/_flush.py +5 -2
  111. scitex/io/_load.py +98 -0
  112. scitex/io/_load_modules/_H5Explorer.py +5 -2
  113. scitex/io/_load_modules/_canvas.py +2 -2
  114. scitex/io/_load_modules/_image.py +3 -4
  115. scitex/io/_load_modules/_txt.py +4 -2
  116. scitex/io/_metadata.py +34 -324
  117. scitex/io/_metadata_modules/__init__.py +46 -0
  118. scitex/io/_metadata_modules/_embed.py +70 -0
  119. scitex/io/_metadata_modules/_read.py +64 -0
  120. scitex/io/_metadata_modules/_utils.py +79 -0
  121. scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
  122. scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
  123. scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
  124. scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
  125. scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
  126. scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
  127. scitex/io/_metadata_modules/read_metadata_png.py +39 -0
  128. scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
  129. scitex/io/_qr_utils.py +5 -3
  130. scitex/io/_save.py +548 -30
  131. scitex/io/_save_modules/_canvas.py +3 -3
  132. scitex/io/_save_modules/_image.py +5 -9
  133. scitex/io/_save_modules/_tex.py +7 -4
  134. scitex/io/utils/h5_to_zarr.py +11 -9
  135. scitex/msword/__init__.py +255 -0
  136. scitex/msword/profiles.py +357 -0
  137. scitex/msword/reader.py +753 -0
  138. scitex/msword/utils.py +289 -0
  139. scitex/msword/writer.py +362 -0
  140. scitex/plt/__init__.py +5 -2
  141. scitex/plt/_subplots/_AxesWrapper.py +6 -6
  142. scitex/plt/_subplots/_AxisWrapper.py +15 -9
  143. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
  144. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
  145. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
  146. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
  147. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
  148. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
  149. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
  150. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
  151. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
  152. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
  153. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
  154. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
  155. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
  156. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
  157. scitex/plt/_subplots/_FigWrapper.py +6 -6
  158. scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
  159. scitex/plt/_subplots/_export_as_csv.py +35 -5
  160. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
  161. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
  162. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
  163. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
  164. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
  165. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
  166. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
  167. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
  168. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
  169. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
  170. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
  171. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
  172. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
  173. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
  174. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
  175. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
  176. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
  177. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
  178. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
  179. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
  180. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
  181. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
  182. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
  183. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
  184. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
  185. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
  186. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
  187. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
  188. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
  189. scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
  190. scitex/plt/gallery/_generate.py +421 -14
  191. scitex/plt/io/__init__.py +53 -0
  192. scitex/plt/io/_bundle.py +490 -0
  193. scitex/plt/io/_layered_bundle.py +1343 -0
  194. scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
  195. scitex/plt/styles/__init__.py +14 -0
  196. scitex/plt/styles/presets.py +78 -0
  197. scitex/plt/utils/__init__.py +13 -1
  198. scitex/plt/utils/_collect_figure_metadata.py +10 -14
  199. scitex/plt/utils/_configure_mpl.py +6 -18
  200. scitex/plt/utils/_crop.py +32 -14
  201. scitex/plt/utils/_csv_column_naming.py +54 -0
  202. scitex/plt/utils/_figure_mm.py +116 -1
  203. scitex/plt/utils/_hitmap.py +1643 -0
  204. scitex/plt/utils/metadata/__init__.py +25 -0
  205. scitex/plt/utils/metadata/_core.py +9 -10
  206. scitex/plt/utils/metadata/_dimensions.py +6 -3
  207. scitex/plt/utils/metadata/_editable_export.py +405 -0
  208. scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
  209. scitex/schema/__init__.py +109 -16
  210. scitex/schema/_canvas.py +1 -1
  211. scitex/schema/_plot.py +1015 -0
  212. scitex/schema/_stats.py +2 -2
  213. scitex/stats/__init__.py +117 -0
  214. scitex/stats/io/__init__.py +29 -0
  215. scitex/stats/io/_bundle.py +156 -0
  216. scitex/tex/__init__.py +4 -0
  217. scitex/tex/_export.py +890 -0
  218. {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/METADATA +11 -1
  219. {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/RECORD +238 -170
  220. scitex/io/memo.md +0 -2827
  221. scitex/plt/REQUESTS.md +0 -191
  222. scitex/plt/_subplots/TODO.md +0 -53
  223. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
  224. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
  225. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
  226. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
  227. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
  228. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
  229. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
  230. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
  231. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
  232. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
  233. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
  234. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
  235. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
  236. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
  237. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
  238. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
  239. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
  240. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
  241. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
  242. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
  243. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
  244. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
  245. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
  246. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
  247. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
  248. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
  249. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
  250. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
  251. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
  252. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
  253. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
  254. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
  255. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
  256. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
  257. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
  258. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
  259. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
  260. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
  261. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
  262. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
  263. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
  264. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
  265. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
  266. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
  267. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
  268. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
  269. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
  270. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
  271. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
  272. scitex/vis/__init__.py +0 -177
  273. scitex/vis/editor/_edit.py +0 -390
  274. scitex/vis/editor/flask_editor/_bbox.py +0 -529
  275. scitex/vis/editor/flask_editor/_core.py +0 -168
  276. scitex/vis/editor/flask_editor/_renderer.py +0 -393
  277. scitex/vis/editor/flask_editor/templates/_html.py +0 -513
  278. scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
  279. /scitex/{vis → fig}/README.md +0 -0
  280. /scitex/{vis → fig}/backend/__init__.py +0 -0
  281. /scitex/{vis → fig}/backend/_export.py +0 -0
  282. /scitex/{vis → fig}/backend/_render.py +0 -0
  283. /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
  284. /scitex/{vis → fig}/editor/__init__.py +0 -0
  285. /scitex/{vis → fig}/editor/_dearpygui_editor.py +0 -0
  286. /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
  287. /scitex/{vis → fig}/editor/_mpl_editor.py +0 -0
  288. /scitex/{vis → fig}/editor/_tkinter_editor.py +0 -0
  289. /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
  290. /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
  291. /scitex/{vis → fig}/io/_directory.py +0 -0
  292. /scitex/{vis → fig}/model/_plot_types.py +0 -0
  293. /scitex/{vis → fig}/utils/_defaults.py +0 -0
  294. /scitex/{vis → fig}/utils/_validate.py +0 -0
  295. {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/WHEEL +0 -0
  296. {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/entry_points.txt +0 -0
  297. {scitex-2.7.0.dist-info → scitex-2.7.3.dist-info}/licenses/LICENSE +0 -0
scitex/io/_load.py CHANGED
@@ -48,6 +48,98 @@ from ._load_modules._zarr import _load_zarr
48
48
  from ._load_modules._canvas import _load_canvas
49
49
 
50
50
 
51
+ def _load_bundle(lpath, verbose=False, **kwargs):
52
+ """Load a .pltz, .figz, or .statsz bundle.
53
+
54
+ Parameters
55
+ ----------
56
+ lpath : str or Path
57
+ Path to the bundle (directory or ZIP).
58
+ verbose : bool
59
+ If True, print verbose output.
60
+ **kwargs
61
+ Additional arguments.
62
+
63
+ Returns
64
+ -------
65
+ For .pltz bundles:
66
+ tuple: (fig, ax, data) where fig is reconstructed figure,
67
+ ax is the axes, data is DataFrame or None.
68
+ For .figz bundles:
69
+ dict: Figure data with 'spec' and 'panels'.
70
+ For .statsz bundles:
71
+ dict: Stats data with 'spec' and 'comparisons'.
72
+ """
73
+ from ._bundle import load_bundle, BundleType
74
+
75
+ bundle = load_bundle(lpath)
76
+ bundle_type = bundle.get('type')
77
+
78
+ if bundle_type == BundleType.PLTZ:
79
+ # Return (fig, ax, data) tuple for .pltz bundles
80
+ # Note: We return the spec and data, not a reconstructed figure
81
+ # as matplotlib figures cannot be perfectly serialized/deserialized
82
+ import matplotlib.pyplot as plt
83
+ from pathlib import Path
84
+
85
+ p = Path(lpath)
86
+ bundle_dir = p
87
+
88
+ # Handle ZIP extraction
89
+ if not p.is_dir():
90
+ import tempfile
91
+ import zipfile
92
+ temp_dir = Path(tempfile.mkdtemp())
93
+ with zipfile.ZipFile(p, 'r') as zf:
94
+ zf.extractall(temp_dir)
95
+ bundle_dir = temp_dir
96
+
97
+ # Find PNG file - layered format stores in exports/
98
+ basename = bundle.get('basename', 'plot')
99
+ png_path = bundle_dir / "exports" / f"{basename}.png"
100
+ if not png_path.exists():
101
+ # Fallback to root level (legacy format)
102
+ png_path = bundle_dir / f"{basename}.png"
103
+
104
+ # Load the PNG as a figure
105
+ if png_path.exists():
106
+ img = plt.imread(str(png_path))
107
+ fig, ax = plt.subplots()
108
+ ax.imshow(img)
109
+ ax.axis('off')
110
+
111
+ # Attach metadata from spec
112
+ spec = bundle.get('spec', {})
113
+ if spec:
114
+ # Handle both layered and legacy spec formats
115
+ axes_list = spec.get('axes', [])
116
+ if axes_list and isinstance(axes_list, list):
117
+ for key, val in axes_list[0].items():
118
+ setattr(ax, f'_scitex_{key}', val)
119
+ # Theme from style (layered) or spec (legacy)
120
+ style = bundle.get('style', {})
121
+ theme = style.get('theme', {}) if style else spec.get('theme', {})
122
+ if theme:
123
+ fig._scitex_theme = theme.get('mode')
124
+
125
+ # Data from bundle (merged in load_layered_pltz_bundle)
126
+ data = bundle.get('data')
127
+ return fig, ax, data
128
+ else:
129
+ # No PNG, return spec and data
130
+ return bundle.get('spec'), None, bundle.get('data')
131
+
132
+ elif bundle_type == BundleType.FIGZ:
133
+ # Return figure dict for .figz bundles
134
+ return bundle
135
+
136
+ elif bundle_type == BundleType.STATSZ:
137
+ # Return stats dict for .statsz bundles
138
+ return bundle
139
+
140
+ return bundle
141
+
142
+
51
143
  def load(
52
144
  lpath: Union[str, Path],
53
145
  ext: str = None,
@@ -157,6 +249,12 @@ def load(
157
249
  if lpath.endswith(".canvas"):
158
250
  return _load_canvas(lpath, verbose=verbose, **kwargs)
159
251
 
252
+ # Handle bundle formats (.pltz, .figz, .statsz and their .d variants)
253
+ bundle_extensions = (".figz", ".pltz", ".statsz")
254
+ for bext in bundle_extensions:
255
+ if lpath.endswith(bext) or lpath.endswith(f"{bext}.d"):
256
+ return _load_bundle(lpath, verbose=verbose, **kwargs)
257
+
160
258
  # Check if it's a glob pattern
161
259
  if "*" in lpath or "?" in lpath or "[" in lpath:
162
260
  # Handle glob pattern
@@ -11,7 +11,6 @@ __DIR__ = os.path.dirname(__FILE__)
11
11
 
12
12
  import random
13
13
  import time
14
- import warnings
15
14
 
16
15
  # Time-stamp: "2025-06-13 21:00:00 (ywatanabe)"
17
16
 
@@ -22,6 +21,10 @@ from typing import Any, Dict, List, Optional
22
21
  import h5py
23
22
  import numpy as np
24
23
 
24
+ from scitex import logging
25
+
26
+ logger = logging.getLogger(__name__)
27
+
25
28
 
26
29
  class H5Explorer:
27
30
  """Interactive HDF5 file explorer.
@@ -262,7 +265,7 @@ def explore_h5(filepath: str) -> None:
262
265
  explorer.explore()
263
266
  explorer.close()
264
267
  else:
265
- warnings.warn(f"Warning: File does not exist: {filepath}")
268
+ logger.warning(f"File does not exist: {filepath}")
266
269
 
267
270
 
268
271
  def has_h5_key(h5_path, key, max_retries=3, action_on_corrupted="delete"):
@@ -3,7 +3,7 @@
3
3
  # Timestamp: 2025-12-08
4
4
  # File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_load_modules/_canvas.py
5
5
  """
6
- Load canvas directory (.canvas) for scitex.vis.
6
+ Load canvas directory (.canvas) for scitex.fig.
7
7
 
8
8
  Canvas directories are portable figure bundles containing:
9
9
  - canvas.json: Layout, panels, composition settings
@@ -100,7 +100,7 @@ def _load_canvas(
100
100
  # Return Canvas object by default
101
101
  if not as_dict:
102
102
  try:
103
- from scitex.vis.canvas import Canvas
103
+ from scitex.fig.canvas import Canvas
104
104
 
105
105
  canvas_obj = Canvas.from_dict(canvas_dict)
106
106
  # Store reference to original directory for copying
@@ -3,11 +3,12 @@
3
3
  # Time-stamp: "2024-11-14 07:55:38 (ywatanabe)"
4
4
  # File: ./scitex_repo/src/scitex/io/_load_modules/_image.py
5
5
 
6
- import logging
7
6
  from typing import Any, Dict, Optional, Tuple, Union
8
7
 
9
8
  from PIL import Image
10
9
 
10
+ from scitex import logging
11
+
11
12
  logger = logging.getLogger(__name__)
12
13
 
13
14
 
@@ -58,9 +59,7 @@ def _load_image(
58
59
 
59
60
  except Exception as e:
60
61
  # If metadata reading fails, return None
61
- import warnings
62
-
63
- warnings.warn(f"Failed to read metadata: {e}")
62
+ logger.warning(f"Failed to read metadata: {e}")
64
63
  return img, None
65
64
 
66
65
 
@@ -9,7 +9,9 @@ __FILE__ = __file__
9
9
  __DIR__ = os.path.dirname(__FILE__)
10
10
  # ----------------------------------------
11
11
 
12
- import warnings
12
+ from scitex import logging
13
+
14
+ logger = logging.getLogger(__name__)
13
15
 
14
16
  # # UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8a in position 30173: invalid start byte
15
17
  # def _load_txt(lpath, **kwargs):
@@ -93,7 +95,7 @@ def _load_txt(lpath, strip=True, as_lines=True):
93
95
  lpath = str(lpath)
94
96
 
95
97
  if not lpath.endswith((".txt", ".log", ".event", ".py", ".sh", ".tex", ".bib")):
96
- warnings.warn(f"Unexpected extension for file: {lpath}")
98
+ logger.warning(f"Unexpected extension for file: {lpath}")
97
99
 
98
100
  try:
99
101
  with open(lpath, "r", encoding="utf-8") as file:
scitex/io/_metadata.py CHANGED
@@ -1,337 +1,47 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
- # Timestamp: "2025-11-14 (ywatanabe)"
4
3
  # File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata.py
5
- # ----------------------------------------
4
+
6
5
  """
7
6
  Image and PDF metadata embedding and extraction for research reproducibility.
8
7
 
9
- This module provides functions to embed and extract metadata from image and PDF files.
10
- Metadata is stored using standard formats:
8
+ This module re-exports from _metadata_modules for backwards compatibility.
9
+ See _metadata_modules/ for format-specific implementations:
11
10
  - PNG: tEXt chunks
12
11
  - JPEG: EXIF ImageDescription field
12
+ - SVG: <metadata> element with scitex namespace
13
13
  - PDF: XMP metadata (industry standard)
14
-
15
- The metadata is stored as JSON strings, allowing flexible dictionary structures.
16
14
  """
17
15
 
18
- import json
19
- import os
20
- from typing import Any, Dict, Optional
21
-
22
- from PIL import Image
23
- from PIL.PngImagePlugin import PngInfo
24
-
25
- __FILE__ = __file__
26
- __DIR__ = os.path.dirname(__FILE__)
27
-
28
-
29
- def _convert_for_json(obj: Any) -> Any:
30
- """
31
- Convert non-JSON-serializable objects to serializable format.
32
-
33
- Handles FixedFloat objects from figure metadata by converting
34
- them to regular floats.
35
- """
36
- # Handle FixedFloat (from _collect_figure_metadata)
37
- if hasattr(obj, 'value') and hasattr(obj, 'precision') and hasattr(obj, '__class__') and obj.__class__.__name__ == 'FixedFloat':
38
- return obj.value
39
-
40
- # Handle dict recursively
41
- if isinstance(obj, dict):
42
- return {key: _convert_for_json(value) for key, value in obj.items()}
43
-
44
- # Handle list recursively
45
- if isinstance(obj, list):
46
- return [_convert_for_json(item) for item in obj]
47
-
48
- # Handle tuple
49
- if isinstance(obj, tuple):
50
- return [_convert_for_json(item) for item in obj]
51
-
52
- # Handle numpy arrays
53
- if hasattr(obj, "tolist"):
54
- return obj.tolist()
55
-
56
- # Default: return as-is
57
- return obj
58
- # ----------------------------------------
59
-
60
-
61
- def embed_metadata(image_path: str, metadata: Dict[str, Any]) -> None:
62
- """
63
- Embed metadata into an existing image or PDF file.
64
-
65
- Args:
66
- image_path: Path to the image/PDF file (PNG, JPEG, or PDF)
67
- metadata: Dictionary containing metadata (must be JSON serializable)
68
-
69
- Raises:
70
- ValueError: If file format is not supported or metadata is not JSON serializable
71
- FileNotFoundError: If file doesn't exist
72
-
73
- Example:
74
- >>> metadata = {
75
- ... 'experiment': 'seizure_prediction_001',
76
- ... 'session': '2024-11-14',
77
- ... 'analysis': 'PAC'
78
- ... }
79
- >>> embed_metadata('result.png', metadata)
80
- >>> embed_metadata('result.pdf', metadata)
81
- """
82
- if not os.path.exists(image_path):
83
- raise FileNotFoundError(f"File not found: {image_path}")
84
-
85
- # Convert non-serializable objects (e.g., FixedFloat) to serializable format
86
- metadata = _convert_for_json(metadata)
87
-
88
- # Serialize metadata to JSON
89
- try:
90
- metadata_json = json.dumps(metadata, ensure_ascii=False, indent=2)
91
- except (TypeError, ValueError) as e:
92
- raise ValueError(f"Metadata must be JSON serializable: {e}")
93
-
94
- # Handle PNG format
95
- if image_path.lower().endswith(".png"):
96
- # Open the image
97
- img = Image.open(image_path)
98
- # Create new PNG info with metadata
99
- pnginfo = PngInfo()
100
- pnginfo.add_text("scitex_metadata", metadata_json)
101
-
102
- # Save with metadata
103
- img.save(image_path, "PNG", pnginfo=pnginfo)
104
-
105
- # Handle JPEG format
106
- elif image_path.lower().endswith((".jpg", ".jpeg")):
107
- # Open the image
108
- img = Image.open(image_path)
109
-
110
- try:
111
- import piexif
112
- except ImportError:
113
- raise ImportError(
114
- "piexif is required for JPEG metadata support. "
115
- "Install with: pip install piexif"
116
- )
117
-
118
- # Convert to RGB if necessary (JPEG doesn't support RGBA)
119
- if img.mode in ("RGBA", "LA", "P"):
120
- rgb_img = Image.new("RGB", img.size, (255, 255, 255))
121
- if img.mode == "P":
122
- img = img.convert("RGBA")
123
- if img.mode in ("RGBA", "LA"):
124
- rgb_img.paste(img, mask=img.split()[-1])
125
- else:
126
- rgb_img.paste(img)
127
- img = rgb_img
128
-
129
- # Create EXIF dict with metadata in ImageDescription field
130
- exif_dict = {
131
- "0th": {piexif.ImageIFD.ImageDescription: metadata_json.encode("utf-8")},
132
- "Exif": {},
133
- "GPS": {},
134
- "1st": {},
135
- }
136
-
137
- # Try to preserve existing EXIF data
138
- try:
139
- existing_exif = piexif.load(img.info.get("exif", b""))
140
- # Merge with new metadata (prioritize new metadata)
141
- for ifd in ["Exif", "GPS", "1st"]:
142
- if ifd in existing_exif:
143
- exif_dict[ifd].update(existing_exif[ifd])
144
- except:
145
- pass # If existing EXIF is corrupted, just use new metadata
146
-
147
- exif_bytes = piexif.dump(exif_dict)
148
-
149
- # Save with EXIF metadata (quality=100 for maximum quality)
150
- img.save(
151
- image_path,
152
- "JPEG",
153
- quality=100,
154
- subsampling=0,
155
- optimize=False,
156
- exif=exif_bytes,
157
- )
158
-
159
- # Handle PDF format
160
- elif image_path.lower().endswith(".pdf"):
161
- try:
162
- from pypdf import PdfReader, PdfWriter
163
- except ImportError:
164
- raise ImportError(
165
- "pypdf is required for PDF metadata support. "
166
- "Install with: pip install pypdf"
167
- )
168
-
169
- # Read existing PDF
170
- reader = PdfReader(image_path)
171
- writer = PdfWriter()
172
-
173
- # Copy all pages
174
- for page in reader.pages:
175
- writer.add_page(page)
176
-
177
- # Prepare metadata for PDF Info Dictionary
178
- pdf_metadata = {
179
- "/Title": metadata.get("title", ""),
180
- "/Author": metadata.get("author", ""),
181
- "/Subject": metadata_json, # Store full JSON in Subject field
182
- "/Creator": "SciTeX",
183
- "/Producer": "SciTeX",
184
- }
185
-
186
- # Add metadata
187
- writer.add_metadata(pdf_metadata)
188
-
189
- # Write back to file
190
- with open(image_path, "wb") as output_file:
191
- writer.write(output_file)
192
-
193
- else:
194
- raise ValueError(
195
- f"Unsupported file format: {image_path}. "
196
- "Only PNG, JPEG, and PDF formats are supported."
197
- )
198
-
199
- # Close image if it was opened (not for PDF)
200
- if image_path.lower().endswith((".png", ".jpg", ".jpeg")):
201
- img.close()
202
-
203
-
204
- def read_metadata(image_path: str) -> Optional[Dict[str, Any]]:
205
- """
206
- Read metadata from an image or PDF file.
207
-
208
- Args:
209
- image_path: Path to the file (PNG, JPEG, or PDF)
210
-
211
- Returns:
212
- Dictionary containing metadata, or None if no metadata found
213
-
214
- Raises:
215
- FileNotFoundError: If file doesn't exist
216
- ValueError: If file format is not supported
217
-
218
- Example:
219
- >>> metadata = read_metadata('result.png')
220
- >>> print(metadata['experiment'])
221
- 'seizure_prediction_001'
222
- >>> metadata = read_metadata('result.pdf')
223
- """
224
- if not os.path.exists(image_path):
225
- raise FileNotFoundError(f"File not found: {image_path}")
226
-
227
- # Don't open PDF files with PIL
228
- if not image_path.lower().endswith(".pdf"):
229
- img = Image.open(image_path)
230
- metadata = None
231
-
232
- try:
233
- # Handle PNG format
234
- if image_path.lower().endswith(".png"):
235
- # Check for scitex_metadata in PNG info
236
- if hasattr(img, "info") and "scitex_metadata" in img.info:
237
- metadata_json = img.info["scitex_metadata"]
238
- try:
239
- metadata = json.loads(metadata_json)
240
- except json.JSONDecodeError:
241
- # Metadata exists but is not valid JSON
242
- metadata = {"raw": metadata_json}
243
-
244
- # Handle JPEG format
245
- elif image_path.lower().endswith((".jpg", ".jpeg")):
246
- try:
247
- import piexif
248
-
249
- # Load EXIF data
250
- if "exif" in img.info:
251
- exif_dict = piexif.load(img.info["exif"])
252
-
253
- # Try to read ImageDescription field
254
- if piexif.ImageIFD.ImageDescription in exif_dict.get("0th", {}):
255
- description = exif_dict["0th"][piexif.ImageIFD.ImageDescription]
256
-
257
- # Decode bytes to string
258
- if isinstance(description, bytes):
259
- description = description.decode("utf-8", errors="ignore")
260
-
261
- # Try to parse as JSON
262
- try:
263
- metadata = json.loads(description)
264
- except json.JSONDecodeError:
265
- # If not JSON, return as raw text
266
- metadata = {"raw": description}
267
- except ImportError:
268
- pass # piexif not available, return None
269
- except Exception:
270
- pass # EXIF data corrupted or not readable
271
-
272
- # Handle PDF format
273
- elif image_path.lower().endswith(".pdf"):
274
- try:
275
- from pypdf import PdfReader
276
-
277
- reader = PdfReader(image_path)
278
-
279
- # Try to read metadata from PDF Info Dictionary
280
- if reader.metadata:
281
- # Check Subject field for JSON metadata
282
- if "/Subject" in reader.metadata:
283
- subject = reader.metadata["/Subject"]
284
- try:
285
- metadata = json.loads(subject)
286
- except json.JSONDecodeError:
287
- # If not JSON, create metadata dict from available fields
288
- metadata = {
289
- "title": reader.metadata.get("/Title", ""),
290
- "author": reader.metadata.get("/Author", ""),
291
- "subject": subject,
292
- "creator": reader.metadata.get("/Creator", ""),
293
- }
294
- except ImportError:
295
- pass # pypdf not available, return None
296
- except Exception:
297
- pass # PDF metadata corrupted or not readable
298
- finally:
299
- # No need to close anything for PDF
300
- pass
301
-
302
- else:
303
- raise ValueError(
304
- f"Unsupported file format: {image_path}. "
305
- "Only PNG, JPEG, and PDF formats are supported."
306
- )
307
-
308
- finally:
309
- # Only close if img was opened (not for PDF)
310
- if not image_path.lower().endswith(".pdf"):
311
- img.close()
312
-
313
- return metadata
314
-
315
-
316
- def has_metadata(image_path: str) -> bool:
317
- """
318
- Check if an image file has embedded metadata.
319
-
320
- Args:
321
- image_path: Path to the image file
322
-
323
- Returns:
324
- True if metadata exists, False otherwise
325
-
326
- Example:
327
- >>> if has_metadata('result.png'):
328
- ... print(read_metadata('result.png'))
329
- """
330
- try:
331
- metadata = read_metadata(image_path)
332
- return metadata is not None
333
- except:
334
- return False
335
-
16
+ from ._metadata_modules import (
17
+ embed_metadata,
18
+ read_metadata,
19
+ has_metadata,
20
+ embed_metadata_png,
21
+ embed_metadata_jpeg,
22
+ embed_metadata_svg,
23
+ embed_metadata_pdf,
24
+ read_metadata_png,
25
+ read_metadata_jpeg,
26
+ read_metadata_svg,
27
+ read_metadata_pdf,
28
+ )
29
+
30
+ # Backwards compatibility alias
31
+ _convert_for_json = None # Removed - use _metadata_modules._utils.convert_for_json
32
+
33
+ __all__ = [
34
+ "embed_metadata",
35
+ "read_metadata",
36
+ "has_metadata",
37
+ "embed_metadata_png",
38
+ "embed_metadata_jpeg",
39
+ "embed_metadata_svg",
40
+ "embed_metadata_pdf",
41
+ "read_metadata_png",
42
+ "read_metadata_jpeg",
43
+ "read_metadata_svg",
44
+ "read_metadata_pdf",
45
+ ]
336
46
 
337
47
  # EOF
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata_modules/__init__.py
4
+
5
+ """
6
+ Image and PDF metadata embedding and extraction for research reproducibility.
7
+
8
+ This module provides functions to embed and extract metadata from image and PDF files.
9
+ Metadata is stored using standard formats:
10
+ - PNG: tEXt chunks
11
+ - JPEG: EXIF ImageDescription field
12
+ - SVG: <metadata> element with scitex namespace
13
+ - PDF: XMP metadata (industry standard)
14
+
15
+ The metadata is stored as JSON strings, allowing flexible dictionary structures.
16
+ """
17
+
18
+ from ._embed import embed_metadata
19
+ from ._read import read_metadata
20
+ from ._utils import has_metadata
21
+
22
+ # Format-specific modules (for direct access if needed)
23
+ from .embed_metadata_png import embed_metadata_png
24
+ from .embed_metadata_jpeg import embed_metadata_jpeg
25
+ from .embed_metadata_svg import embed_metadata_svg
26
+ from .embed_metadata_pdf import embed_metadata_pdf
27
+ from .read_metadata_png import read_metadata_png
28
+ from .read_metadata_jpeg import read_metadata_jpeg
29
+ from .read_metadata_svg import read_metadata_svg
30
+ from .read_metadata_pdf import read_metadata_pdf
31
+
32
+ __all__ = [
33
+ "embed_metadata",
34
+ "read_metadata",
35
+ "has_metadata",
36
+ "embed_metadata_png",
37
+ "embed_metadata_jpeg",
38
+ "embed_metadata_svg",
39
+ "embed_metadata_pdf",
40
+ "read_metadata_png",
41
+ "read_metadata_jpeg",
42
+ "read_metadata_svg",
43
+ "read_metadata_pdf",
44
+ ]
45
+
46
+ # EOF
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_metadata_modules/_embed.py
4
+
5
+ """Main embed_metadata dispatcher."""
6
+
7
+ import os
8
+ from typing import Any, Dict
9
+
10
+ from ._utils import serialize_metadata
11
+
12
+
13
+ def embed_metadata(image_path: str, metadata: Dict[str, Any]) -> None:
14
+ """
15
+ Embed metadata into an existing image or PDF file.
16
+
17
+ Args:
18
+ image_path: Path to the image/PDF file (PNG, JPEG, SVG, or PDF)
19
+ metadata: Dictionary containing metadata (must be JSON serializable)
20
+
21
+ Raises:
22
+ ValueError: If file format is not supported or metadata is not JSON serializable
23
+ FileNotFoundError: If file doesn't exist
24
+
25
+ Example:
26
+ >>> metadata = {
27
+ ... 'experiment': 'seizure_prediction_001',
28
+ ... 'session': '2024-11-14',
29
+ ... 'analysis': 'PAC'
30
+ ... }
31
+ >>> embed_metadata('result.png', metadata)
32
+ >>> embed_metadata('result.pdf', metadata)
33
+ """
34
+ if not os.path.exists(image_path):
35
+ raise FileNotFoundError(f"File not found: {image_path}")
36
+
37
+ # Serialize metadata to JSON
38
+ metadata_json = serialize_metadata(metadata)
39
+
40
+ path_lower = image_path.lower()
41
+
42
+ # Dispatch to format-specific handlers
43
+ if path_lower.endswith(".png"):
44
+ from .embed_metadata_png import embed_metadata_png
45
+
46
+ embed_metadata_png(image_path, metadata_json)
47
+
48
+ elif path_lower.endswith((".jpg", ".jpeg")):
49
+ from .embed_metadata_jpeg import embed_metadata_jpeg
50
+
51
+ embed_metadata_jpeg(image_path, metadata_json)
52
+
53
+ elif path_lower.endswith(".svg"):
54
+ from .embed_metadata_svg import embed_metadata_svg
55
+
56
+ embed_metadata_svg(image_path, metadata_json)
57
+
58
+ elif path_lower.endswith(".pdf"):
59
+ from .embed_metadata_pdf import embed_metadata_pdf
60
+
61
+ embed_metadata_pdf(image_path, metadata_json, metadata)
62
+
63
+ else:
64
+ raise ValueError(
65
+ f"Unsupported file format: {image_path}. "
66
+ "Only PNG, JPEG, SVG, and PDF formats are supported."
67
+ )
68
+
69
+
70
+ # EOF