Spaces:
Running
Running
File size: 6,547 Bytes
dccd1b0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
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) |