Spaces:
Sleeping
Sleeping
import io | |
from pptx import Presentation | |
from pptx.enum.shapes import MSO_SHAPE_TYPE | |
from pptx.enum.text import PP_ALIGN | |
from PIL import Image, ImageDraw, ImageFont | |
import skia | |
def transfer_textbox_content_in_group(group_shape): | |
"""Edit the content of text boxes within a group shape.""" | |
group_shape_item = {} | |
for l, shape in enumerate(group_shape.shapes): | |
shape_item = {} | |
if shape.has_text_frame: | |
shape_item['type'] = "text" | |
shape_item['location'] = (shape.left, shape.top) | |
text_frame = shape.text_frame | |
for r, paragraph in enumerate(text_frame.paragraphs): | |
original_run = paragraph.runs[0] | |
paragraph_item = {} | |
paragraph_item['text'] = paragraph.text | |
paragraph_item['align'] = paragraph.alignment | |
font_item = {} | |
font_item['name'] = original_run.font.name | |
font_item['size'] = original_run.font.size | |
font_item['bold'] = original_run.font.bold | |
font_item['italic'] = original_run.font.italic | |
font_item['underline'] = original_run.font.underline | |
font_item['color'] = original_run.font.color.rgb | |
font_item['language_id'] = original_run.font.language_id | |
paragraph_item['font'] = font_item | |
shape_item[f'paragraph_{r}'] = paragraph_item | |
group_shape_item[f"shape_{l}"] = shape_item | |
return group_shape_item | |
def pptx_to_images(pptx_path): | |
# Load the PowerPoint presentation | |
prs = Presentation(pptx_path) | |
# List to hold the images | |
images = [] | |
# Conversion factor from EMUs to pixels | |
EMU_TO_PIXELS = 914400 / 96 | |
# Conversion factor from EMUs to points | |
EMU_TO_POINTS = 12700 | |
# Load a default font | |
default_font = ImageFont.load_default() | |
# Iterate through each slide in the presentation | |
for slide in prs.slides: | |
# Convert slide dimensions from EMUs to pixels | |
slide_width = int(prs.slide_width / EMU_TO_PIXELS) | |
slide_height = int(prs.slide_height / EMU_TO_PIXELS) | |
# Create a blank image with the same dimensions as the slide | |
img = Image.new('RGB', (slide_width, slide_height), color='white') | |
draw = ImageDraw.Draw(img) | |
# Draw the slide content onto the image | |
for shape in slide.shapes: | |
if shape.has_text_frame: | |
# Convert shape position from EMUs to pixels | |
shape_left = int(shape.left / EMU_TO_PIXELS) | |
shape_top = int(shape.top / EMU_TO_PIXELS) | |
text_frame = shape.text_frame | |
for paragraph in text_frame.paragraphs: | |
original_run = paragraph.runs[0] | |
font = ImageFont.truetype(original_run.font.name, original_run.font.size) | |
draw.text((shape_left, shape_top), paragraph.text, fill=original_run.font.color.rgb, font=font) | |
elif isinstance(shape, GroupShape): | |
group_content = transfer_textbox_content_in_group(shape) | |
for shape_key, shape_item in group_content.items(): | |
if shape_item['type'] == "text": | |
text_location = shape_item['location'] | |
text_left = int(text_location[0] / EMU_TO_PIXELS) | |
text_top = int(text_location[1] / EMU_TO_PIXELS) | |
for paragraph_key, paragraph_item in shape_item.items(): | |
if paragraph_key.startswith('paragraph_'): | |
font_info = paragraph_item['font'] | |
# font = ImageFont.load_default() | |
print(font_info['size']) | |
font_size = int(font_info['size'] / EMU_TO_POINTS) # Convert EMUs to points | |
font = ImageFont.truetype("fonts/FXZK-FANXMLT.ttf", font_size) | |
# font = ImageFont.truetype(font_info['name'], font_info['size']) | |
draw.text((text_left, text_top), paragraph_item['text'], fill=font_info['color'], font=font) | |
elif shape.shape_type == MSO_SHAPE_TYPE.PICTURE: | |
# Convert shape position and size from EMUs to pixels | |
picture_left = int(shape.left / EMU_TO_PIXELS) | |
picture_top = int(shape.top / EMU_TO_PIXELS) | |
picture_width = int(shape.width / EMU_TO_PIXELS) | |
picture_height = int(shape.height / EMU_TO_PIXELS) | |
# Load the picture and resize it to the correct dimensions | |
picture = shape.image.blob | |
picture_img = Image.open(io.BytesIO(picture)) | |
picture_img = picture_img.resize((picture_width, picture_height), Image.Resampling.LANCZOS) | |
# Paste the picture onto the image | |
img.paste(picture_img, (picture_left, picture_top)) | |
# Convert the PIL image to a byte stream | |
img_byte_arr = io.BytesIO() | |
img.save(img_byte_arr, format='PNG') | |
img_byte_arr.seek(0) | |
# Append the image byte stream to the list | |
images.append(img_byte_arr) | |
return images, (slide_width, slide_height) | |
def render_images_with_skia(images, slide_dimensions, output_dir): | |
# Create the output directory if it doesn't exist | |
import os | |
if not os.path.exists(output_dir): | |
os.makedirs(output_dir) | |
# Create a Skia surface | |
slide_width, slide_height = slide_dimensions | |
surface = skia.Surface(slide_width, slide_height) | |
with surface as canvas: | |
for i, img_byte_arr in enumerate(images): | |
# Load the image into a Skia image | |
skia_image = skia.Image.MakeFromEncoded(img_byte_arr.getvalue()) | |
# Draw the image onto the canvas | |
canvas.drawImage(skia_image, 0, 0) | |
# Save the canvas to a file | |
output_path = os.path.join(output_dir, f'slide_{i+1}.png') | |
surface.makeImageSnapshot().save(output_path, skia.kPNG) | |
if __name__ == "__main__": | |
pptx_path = 'templates_from_gaoding/ppt/1.pptx' # Replace with your .pptx file path | |
output_dir = 'output_slides' # Directory to save the rendered images | |
images, slide_dimensions = pptx_to_images(pptx_path) | |
render_images_with_skia(images, slide_dimensions, output_dir) |