scitex 2.7.0__py3-none-any.whl → 2.8.1__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 (355) 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/diagram/README.md +197 -0
  79. scitex/diagram/__init__.py +48 -0
  80. scitex/diagram/_compile.py +312 -0
  81. scitex/diagram/_diagram.py +355 -0
  82. scitex/diagram/_presets.py +173 -0
  83. scitex/diagram/_schema.py +182 -0
  84. scitex/diagram/_split.py +278 -0
  85. scitex/fig/__init__.py +352 -0
  86. scitex/{vis → fig}/backend/_parser.py +1 -1
  87. scitex/{vis → fig}/canvas.py +1 -1
  88. scitex/{vis → fig}/editor/__init__.py +5 -2
  89. scitex/{vis → fig}/editor/_dearpygui_editor.py +1 -1
  90. scitex/{vis → fig}/editor/_defaults.py +70 -5
  91. scitex/{vis → fig}/editor/_mpl_editor.py +1 -1
  92. scitex/{vis → fig}/editor/_qt_editor.py +182 -2
  93. scitex/{vis → fig}/editor/_tkinter_editor.py +1 -1
  94. scitex/fig/editor/edit/__init__.py +50 -0
  95. scitex/fig/editor/edit/backend_detector.py +109 -0
  96. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  97. scitex/fig/editor/edit/editor_launcher.py +239 -0
  98. scitex/fig/editor/edit/manual_handler.py +53 -0
  99. scitex/fig/editor/edit/panel_loader.py +232 -0
  100. scitex/fig/editor/edit/path_resolver.py +67 -0
  101. scitex/fig/editor/flask_editor/_bbox.py +1299 -0
  102. scitex/fig/editor/flask_editor/_core.py +1429 -0
  103. scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
  104. scitex/fig/editor/flask_editor/_renderer.py +813 -0
  105. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  106. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  107. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  108. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  109. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  110. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  111. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  112. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  113. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  114. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  115. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  116. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  117. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  118. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  119. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  120. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  121. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  122. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  123. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  124. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  125. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  126. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  127. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  128. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  129. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  130. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  131. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  132. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  133. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  134. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  135. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  136. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  137. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  138. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  139. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  140. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  141. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  142. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  143. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  144. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  145. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  146. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  147. scitex/fig/editor/flask_editor/templates/__init__.py +123 -0
  148. scitex/fig/editor/flask_editor/templates/_html.py +852 -0
  149. scitex/fig/editor/flask_editor/templates/_scripts.py +4933 -0
  150. scitex/fig/editor/flask_editor/templates/_styles.py +1658 -0
  151. scitex/{vis → fig}/io/__init__.py +13 -1
  152. scitex/fig/io/_bundle.py +1058 -0
  153. scitex/{vis → fig}/io/_canvas.py +1 -1
  154. scitex/{vis → fig}/io/_data.py +1 -1
  155. scitex/{vis → fig}/io/_export.py +1 -1
  156. scitex/{vis → fig}/io/_load.py +1 -1
  157. scitex/{vis → fig}/io/_panel.py +1 -1
  158. scitex/{vis → fig}/io/_save.py +1 -1
  159. scitex/{vis → fig}/model/__init__.py +1 -1
  160. scitex/{vis → fig}/model/_annotations.py +1 -1
  161. scitex/{vis → fig}/model/_axes.py +1 -1
  162. scitex/{vis → fig}/model/_figure.py +1 -1
  163. scitex/{vis → fig}/model/_guides.py +1 -1
  164. scitex/{vis → fig}/model/_plot.py +1 -1
  165. scitex/{vis → fig}/model/_styles.py +1 -1
  166. scitex/{vis → fig}/utils/__init__.py +1 -1
  167. scitex/io/__init__.py +22 -26
  168. scitex/io/_bundle.py +493 -0
  169. scitex/io/_flush.py +5 -2
  170. scitex/io/_load.py +98 -0
  171. scitex/io/_load_modules/_H5Explorer.py +5 -2
  172. scitex/io/_load_modules/_canvas.py +2 -2
  173. scitex/io/_load_modules/_image.py +3 -4
  174. scitex/io/_load_modules/_txt.py +4 -2
  175. scitex/io/_metadata.py +34 -324
  176. scitex/io/_metadata_modules/__init__.py +46 -0
  177. scitex/io/_metadata_modules/_embed.py +70 -0
  178. scitex/io/_metadata_modules/_read.py +64 -0
  179. scitex/io/_metadata_modules/_utils.py +79 -0
  180. scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
  181. scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
  182. scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
  183. scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
  184. scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
  185. scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
  186. scitex/io/_metadata_modules/read_metadata_png.py +39 -0
  187. scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
  188. scitex/io/_qr_utils.py +5 -3
  189. scitex/io/_save.py +548 -30
  190. scitex/io/_save_modules/_canvas.py +3 -3
  191. scitex/io/_save_modules/_image.py +5 -9
  192. scitex/io/_save_modules/_tex.py +7 -4
  193. scitex/io/_zip_bundle.py +439 -0
  194. scitex/io/utils/h5_to_zarr.py +11 -9
  195. scitex/msword/__init__.py +255 -0
  196. scitex/msword/profiles.py +357 -0
  197. scitex/msword/reader.py +753 -0
  198. scitex/msword/utils.py +289 -0
  199. scitex/msword/writer.py +362 -0
  200. scitex/plt/__init__.py +5 -2
  201. scitex/plt/_subplots/_AxesWrapper.py +6 -6
  202. scitex/plt/_subplots/_AxisWrapper.py +15 -9
  203. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
  204. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
  205. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
  206. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
  207. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
  208. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
  209. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
  210. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
  211. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
  212. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
  213. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
  214. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
  215. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
  216. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
  217. scitex/plt/_subplots/_FigWrapper.py +6 -6
  218. scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
  219. scitex/plt/_subplots/_export_as_csv.py +35 -5
  220. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
  221. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
  222. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
  223. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
  224. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
  225. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
  226. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
  227. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
  228. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
  229. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
  230. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
  231. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
  232. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
  233. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
  234. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
  235. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
  236. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
  237. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
  238. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
  239. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
  240. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
  241. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
  242. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
  243. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
  244. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
  245. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
  246. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
  247. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
  248. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
  249. scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
  250. scitex/plt/gallery/_generate.py +421 -14
  251. scitex/plt/io/__init__.py +53 -0
  252. scitex/plt/io/_bundle.py +490 -0
  253. scitex/plt/io/_layered_bundle.py +1343 -0
  254. scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
  255. scitex/plt/styles/__init__.py +14 -0
  256. scitex/plt/styles/presets.py +78 -0
  257. scitex/plt/utils/__init__.py +13 -1
  258. scitex/plt/utils/_collect_figure_metadata.py +10 -14
  259. scitex/plt/utils/_configure_mpl.py +6 -18
  260. scitex/plt/utils/_crop.py +32 -14
  261. scitex/plt/utils/_csv_column_naming.py +54 -0
  262. scitex/plt/utils/_figure_mm.py +116 -1
  263. scitex/plt/utils/_hitmap.py +1643 -0
  264. scitex/plt/utils/metadata/__init__.py +25 -0
  265. scitex/plt/utils/metadata/_core.py +9 -10
  266. scitex/plt/utils/metadata/_dimensions.py +6 -3
  267. scitex/plt/utils/metadata/_editable_export.py +405 -0
  268. scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
  269. scitex/schema/__init__.py +109 -16
  270. scitex/schema/_canvas.py +1 -1
  271. scitex/schema/_plot.py +1015 -0
  272. scitex/schema/_stats.py +2 -2
  273. scitex/stats/__init__.py +117 -0
  274. scitex/stats/io/__init__.py +29 -0
  275. scitex/stats/io/_bundle.py +156 -0
  276. scitex/tex/__init__.py +4 -0
  277. scitex/tex/_export.py +890 -0
  278. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/METADATA +11 -1
  279. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/RECORD +294 -170
  280. scitex/io/memo.md +0 -2827
  281. scitex/plt/REQUESTS.md +0 -191
  282. scitex/plt/_subplots/TODO.md +0 -53
  283. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
  284. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
  285. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
  286. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
  287. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
  288. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
  289. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
  290. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
  291. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
  292. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
  293. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
  294. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
  295. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
  296. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
  297. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
  298. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
  299. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
  300. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
  301. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
  302. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
  303. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
  304. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
  305. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
  306. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
  307. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
  308. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
  309. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
  310. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
  311. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
  312. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
  313. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
  314. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
  315. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
  316. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
  317. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
  318. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
  319. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
  320. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
  321. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
  322. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
  323. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
  324. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
  325. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
  326. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
  327. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
  328. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
  329. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
  330. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
  331. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
  332. scitex/vis/__init__.py +0 -177
  333. scitex/vis/editor/_edit.py +0 -390
  334. scitex/vis/editor/flask_editor/_bbox.py +0 -529
  335. scitex/vis/editor/flask_editor/_core.py +0 -168
  336. scitex/vis/editor/flask_editor/_renderer.py +0 -393
  337. scitex/vis/editor/flask_editor/templates/__init__.py +0 -33
  338. scitex/vis/editor/flask_editor/templates/_html.py +0 -513
  339. scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
  340. scitex/vis/editor/flask_editor/templates/_styles.py +0 -739
  341. /scitex/{vis → fig}/README.md +0 -0
  342. /scitex/{vis → fig}/backend/__init__.py +0 -0
  343. /scitex/{vis → fig}/backend/_export.py +0 -0
  344. /scitex/{vis → fig}/backend/_render.py +0 -0
  345. /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
  346. /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
  347. /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
  348. /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
  349. /scitex/{vis → fig}/io/_directory.py +0 -0
  350. /scitex/{vis → fig}/model/_plot_types.py +0 -0
  351. /scitex/{vis → fig}/utils/_defaults.py +0 -0
  352. /scitex/{vis → fig}/utils/_validate.py +0 -0
  353. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
  354. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
  355. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -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
@@ -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