forked from brianlow/plotter
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththumbnail.py
More file actions
132 lines (102 loc) · 4.49 KB
/
thumbnail.py
File metadata and controls
132 lines (102 loc) · 4.49 KB
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
import base64
import sys
import re
from pathlib import Path
from PIL import Image
from io import BytesIO
import cairosvg
import os
def usage():
print("This utility script embeds an SVG image into a gcode file and adds a SuperSlicer header")
print("The SVG is first converted into the PNG format, then into Base64 and then inserted at")
print(" two resolutions into the gcode file (32x32 and 400x300).")
print("Usage:")
print(" python " + os.path.basename(__file__) + ".py <svgimagefile> <gcodefile>")
def svg_to_png_bytes(file, width, height, fg_color="#000000", bg_color="#ffffff"):
with open(file, "r", encoding="utf-8") as f:
svg_data = f.read()
if "background-injected" not in svg_data:
svg_opening = re.compile(r'(<svg[^>]*>)', re.IGNORECASE)
background = f'<rect width="100%" height="100%" fill="{bg_color}" id="background-injected"/>'
svg_data = svg_opening.sub(r'\1' + background, svg_data, count=1)
if 'width=' not in svg_data or 'height=' not in svg_data:
svg_data = svg_data.replace("<svg", f'<svg width="{width}px" height="{height}px"', 1)
if 'viewBox=' not in svg_data:
svg_data = svg_data.replace("<svg", f'<svg viewBox="0 0 {width} {height}"', 1)
return cairosvg.svg2png(bytestring=svg_data.encode("utf-8"))
def make_thumbnail_with_border(img, target_width, target_height, zoom=1.0, bg_color=(255, 255, 255, 255)):
# Resize while preserving aspect ratio
img_ratio = img.width / img.height
target_ratio = target_width / target_height
if img_ratio > target_ratio:
# Fit width
new_width = round(target_width * zoom)
new_height = round((target_width / img_ratio) * zoom)
else:
# Fit height
new_height = round(target_height * zoom)
new_width = round((target_height * img_ratio) * zoom)
img_resized = img.resize((new_width, new_height), Image.LANCZOS)
# Create a new canvas with background color
canvas = Image.new("RGBA", (target_width, target_height), bg_color)
# Paste the resized image centered
paste_x = (target_width - new_width) // 2
paste_y = (target_height - new_height) // 2
canvas.paste(img_resized, (paste_x, paste_y), img_resized.convert("RGBA"))
return canvas
def to_thumbnail_format(filepath: str, width: int, height: int, zoom=1.0):
file = Path(filepath)
if not file.exists():
print(f"File not found: {filepath}")
return
raw_bytes = None
if file.suffix.lower() == ".svg":
raw_bytes = svg_to_png_bytes(file, width, height)
img = Image.open(BytesIO(raw_bytes))
else:
img = Image.open(file)
# img = img.resize((width, height), Image.LANCZOS)
# Open and resize the image
img = make_thumbnail_with_border(img, width, height, zoom)
# img = img.convert("RGBA") # ensure consistent format
# img = img.resize((width, height), Image.LANCZOS)
# Save resized image into memory as PNG
buffer = BytesIO()
img.save(buffer, format="PNG")
raw_bytes = buffer.getvalue()
# Encode as base64
encoded = base64.b64encode(raw_bytes).decode("ascii")
# The somenumber is the total length of the unwrapped base64 string
somenumber = len(encoded)
# Wrap the base64 string into 78-character lines
lines = [encoded[i:i+78] for i in range(0, len(encoded), 78)]
output = [f"; thumbnail begin {width}x{height} {somenumber}"]
output += [f"; {line}" for line in lines]
output.append("; thumbnail end")
return "\n".join(output)
def main():
if len(sys.argv) < 3:
usage()
sys.exit(1)
filepath = sys.argv[1]
outfile = sys.argv[2]
with open(outfile, "r", encoding="utf-8") as f:
original_content = f.read()
if "generated by SuperSlicer 0.0.0" in original_content:
print("This file already has the SuperSlicer heading and thumbnails, skipping...")
exit(0)
# Fixed header (can be made dynamic if you want UTC timestamp)
header = "; generated by SuperSlicer 0.0.0 on 2025-01-01 at 12:00:00 UTC"
# Generate both thumbnails
# thumb32 = to_thumbnail_format(filepath, 32, 32, 2.0)
thumb32 = to_thumbnail_format(filepath, 32, 32)
thumb300 = to_thumbnail_format(filepath, 400, 300)
# Write output file
with open(outfile, "w", encoding="utf-8") as f:
f.write(header + "\n")
f.write(thumb32 + "\n")
f.write(thumb300 + "\n")
f.write(original_content)
# print(f"Written thumbnails to {outfile}")
if __name__ == "__main__":
main()