Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Read a .pptx file: JSON outline, notes, or export embedded images.
|
||||
|
||||
Modes:
|
||||
--outline JSON with per-slide layout, texts, tables, notes,
|
||||
chart data, and image inventory (default mode).
|
||||
--notes JSON list of speaker notes per slide.
|
||||
--images DIR Export every embedded picture to DIR as files.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pptx import Presentation
|
||||
from pptx.enum.shapes import MSO_SHAPE_TYPE
|
||||
from pptx.util import Emu
|
||||
|
||||
|
||||
def iter_shapes(shapes):
|
||||
"""Yield shapes, descending into groups."""
|
||||
for shape in shapes:
|
||||
if shape.shape_type == MSO_SHAPE_TYPE.GROUP:
|
||||
yield from iter_shapes(shape.shapes)
|
||||
else:
|
||||
yield shape
|
||||
|
||||
|
||||
def chart_info(chart):
|
||||
info = {"type": str(chart.chart_type),
|
||||
"categories": [str(c) for c in chart.plots[0].categories],
|
||||
"series": []}
|
||||
for plot in chart.plots:
|
||||
for series in plot.series:
|
||||
try:
|
||||
name = series.name
|
||||
except (AttributeError, KeyError):
|
||||
name = None
|
||||
info["series"].append({"name": name,
|
||||
"values": list(series.values)})
|
||||
return info
|
||||
|
||||
|
||||
def slide_record(index, slide):
|
||||
rec = {"index": index, "layout": slide.slide_layout.name,
|
||||
"texts": [], "tables": [], "images": [], "charts": [],
|
||||
"notes": None}
|
||||
for shape in iter_shapes(slide.shapes):
|
||||
if shape.has_text_frame and shape.text_frame.text.strip():
|
||||
rec["texts"].append(shape.text_frame.text)
|
||||
if shape.has_table:
|
||||
rec["tables"].append(
|
||||
[[cell.text for cell in row.cells]
|
||||
for row in shape.table.rows])
|
||||
if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
|
||||
try:
|
||||
img = shape.image
|
||||
rec["images"].append({"filename": img.filename,
|
||||
"ext": img.ext,
|
||||
"size_bytes": len(img.blob)})
|
||||
except (KeyError, ValueError):
|
||||
rec["images"].append({"filename": None, "ext": None,
|
||||
"size_bytes": None,
|
||||
"note": "linked or unreadable"})
|
||||
if shape.has_chart:
|
||||
rec["charts"].append(chart_info(shape.chart))
|
||||
if slide.has_notes_slide:
|
||||
rec["notes"] = slide.notes_slide.notes_text_frame.text
|
||||
return rec
|
||||
|
||||
|
||||
def export_images(prs, out_dir):
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
written = []
|
||||
for i, slide in enumerate(prs.slides):
|
||||
for j, shape in enumerate(iter_shapes(slide.shapes)):
|
||||
if shape.shape_type != MSO_SHAPE_TYPE.PICTURE:
|
||||
continue
|
||||
try:
|
||||
img = shape.image
|
||||
except (KeyError, ValueError):
|
||||
continue
|
||||
path = os.path.join(out_dir, f"slide{i}_img{j}.{img.ext}")
|
||||
with open(path, "wb") as fh:
|
||||
fh.write(img.blob)
|
||||
written.append(path)
|
||||
return written
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Read a .pptx: outline/notes as JSON, export images.")
|
||||
parser.add_argument("pptx", help="path to the .pptx file")
|
||||
parser.add_argument("--outline", action="store_true",
|
||||
help="print full JSON outline (default)")
|
||||
parser.add_argument("--notes", action="store_true",
|
||||
help="print speaker notes only")
|
||||
parser.add_argument("--images", metavar="DIR",
|
||||
help="export embedded images into DIR")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
prs = Presentation(args.pptx)
|
||||
|
||||
if args.images:
|
||||
written = export_images(prs, args.images)
|
||||
print(json.dumps({"ok": True, "exported": written}, indent=2))
|
||||
return 0
|
||||
if args.notes:
|
||||
notes = [slide.notes_slide.notes_text_frame.text
|
||||
if slide.has_notes_slide else None
|
||||
for slide in prs.slides]
|
||||
print(json.dumps({"ok": True, "notes": notes},
|
||||
indent=2, ensure_ascii=True))
|
||||
return 0
|
||||
|
||||
outline = {
|
||||
"ok": True,
|
||||
"slide_size_inches": [round(Emu(prs.slide_width).inches, 3),
|
||||
round(Emu(prs.slide_height).inches, 3)],
|
||||
"slide_count": len(prs.slides._sldIdLst),
|
||||
"layouts_available": [lay.name for lay in prs.slide_layouts],
|
||||
"slides": [slide_record(i, s) for i, s in enumerate(prs.slides)],
|
||||
}
|
||||
print(json.dumps(outline, indent=2, ensure_ascii=True))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user