Coverage for ppt_utils.py: 100%
46 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
1"""
2Utilities to process PPTX files.
3"""
5import logging
6import fitz
7import subprocess
8import os
10from pptx import Presentation
12from typing import Optional
13from typing import List
16IMAGE_DPI = 100
19def get_num_slides(
20 pptx_path: str,
21 count_hidden: bool = False
22) -> int:
23 """
24 Get the number of slides in a PPTX file.
25 """
26 presentation = Presentation(pptx_path)
27 num_slides = 0
28 for slide in presentation.slides:
29 is_hidden = slide._element.get("show") == "0"
30 if count_hidden or not is_hidden:
31 num_slides += 1
32 return num_slides
35def pptx_to_images(
36 pptx_path: str,
37 output_path: str,
38 dpi: int = IMAGE_DPI,
39 width: Optional[int] = 1280,
40 height: Optional[int] = 800,
41 logger: Optional[logging.Logger] = None,
42) -> List[str]:
43 """
44 Render PPTX slides to images using libreoffice.
45 """
46 # PPTX to PDF
47 cmd = [
48 "libreoffice",
49 "--headless",
50 "--convert-to", "pdf",
51 "--outdir", output_path,
52 pptx_path
53 ]
54 if logger:
55 logger.debug(f"Rendering PPTX slides to PDF with command: {' '.join(cmd)}")
56 result = subprocess.run(
57 cmd,
58 stdout=subprocess.PIPE,
59 stderr=subprocess.PIPE,
60 text=True)
61 if logger and result.stdout:
62 logger.debug("LibreOffice:\n%s", result.stdout)
63 if logger and result.stderr:
64 logger.warning("LibreOffice:\n%s", result.stderr)
65 if result.returncode != 0:
66 raise RuntimeError(f"Failed to generate images from PPTX. Return code: {result.returncode}")
68 # PDF to PNG
69 image_paths = []
70 pdf_path = pptx_path.replace(".pptx", ".pdf")
72 if not os.path.exists(pdf_path):
73 raise FileNotFoundError(f"Expected PDF file not found: {pdf_path}")
75 doc = fitz.open(pdf_path)
77 doc_rect = doc[0].rect
78 if width and height:
79 matrix_width = width / doc_rect.width
80 matrix_height = height / doc_rect.height
81 matrix = fitz.Matrix(matrix_width, matrix_height)
82 else:
83 matrix = fitz.Matrix(dpi / 72, dpi / 72)
85 for page_number in range(len(doc)):
86 page = doc.load_page(page_number)
87 pix = page.get_pixmap(matrix=matrix)
88 image_path = f"{output_path}/slide_{page_number + 1:03d}.png"
89 pix.save(image_path)
90 image_paths.append(image_path)
91 doc.close()
93 return image_paths