-
Notifications
You must be signed in to change notification settings - Fork 5
/
render.py
206 lines (165 loc) · 7.22 KB
/
render.py
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import configparser
import enum
import io
import re
from PIL import Image, ImageDraw, ImageFont
from werkzeug.utils import secure_filename
from cam import debug
from routes.get_image_id import determine_path
class ObjectTypes(enum.Enum):
IMAGE = "1"
TEXT = "2"
# 3 goes unused.
BACKGROUND = "4"
def parse_coords(coordinates: str) -> (int, int):
x, y = coordinates.split(",")
return int(x), int(y)
def parse_rgb(color: str) -> (int, int, int):
r, g, b = color.split(",")
return int(r), int(g), int(b)
def render(order_schema: str, order_id: str) -> int:
s_config = order_schema.encode().decode("utf-8-sig")
buf = io.StringIO(s_config)
config = configparser.ConfigParser()
config.read_file(buf)
# Python's ConfigParser takes strings specified with quotes literally.
# Before any usage, we want to find values matching this criteria
# and proactively strip quotes.
for section in config.sections():
for key, value in config.items(section):
config[section][key] = value.strip('"')
base_info = config["BaseInfo"]
page_count = int(base_info["PageCount"])
service_type = int(base_info["ServiceType"])
service_types = {1: "Notebook", 2: "Photo Book", 3: "Business Card"}
if debug:
print("Print Type: {}\n".format(service_types[service_type]))
# We're often given multiple pages.
# Iterate through all.
for page_num in range(1, page_count + 1):
handle_page(page_num, config, order_id)
return service_type
def handle_page(page_num: int, config: configparser.ConfigParser, order_id: str):
padded_page_num = f"{page_num:02}"
page_info = config[f"Page{padded_page_num}Info"]
# This field can contain "255,255,255" or a filename for a template.
background_filename = page_info["BackGroundFileName"]
if "," not in background_filename:
# White ("255,255,255") is the only color sent by the client.
background_color = (255, 255, 255)
else:
# If we're not given a color, default to black.
background_color = (0, 0, 0)
# This image represents one for the entire page.
print_size_width = int(page_info["PrintSizeWidth"])
print_size_height = int(page_info["PrintSizeHeight"])
page_img = Image.new(
mode="RGB",
size=(print_size_width, print_size_height),
color=background_color,
)
# If we were given a true filename, paste it over our background.
if ".bmp" in background_filename:
bg_frame_id = "templates/templates/{}".format(
background_filename.replace(".bmp", ".png")
)
background = Image.open(bg_frame_id, "r").convert("RGBA")
page_img.paste(background, (0, 0), background)
# Within PageXXInfo, we're given a format similar to 'Layer01="Object01,1"'.
# This would presumably state a layer order with a respective object and object type.
# However, item type 1 (a business card) lacks this layer information for seemingly no reason.
#
# Historically, we iterated through all of these to determine the order.
# As the client ensures layers are the same index as objects, we will just loop.
page_objects_count = int(page_info["PageObjectsCount"])
for object_num in range(1, page_objects_count + 1):
# Pad to two zeros where possible.
object_num = f"{object_num:02}"
object_section = config[f"Page{padded_page_num}Object{object_num}"]
object_type = ObjectTypes(object_section["ObjectType"])
# Object is a JPEG.
if object_type == ObjectTypes.IMAGE:
zoom = float(object_section["Zoom"]) * 0.01
rect_used = object_section["RectUsed"].split(",")
center_point_x, center_point_y = parse_coords(object_section["CenterPoint"])
frame_width = int(object_section["EffectFrameWidth"])
frame_height = int(object_section["EffectFrameHeight"])
object_file_name = secure_filename(object_section["FileName"])
object_file_path = determine_path(order_id, object_file_name)
picture = Image.open(object_file_path, "r")
picture_width, picture_height = picture.size
# Resize the image with our specified zoom.
picture_resized = picture.resize(
(int(picture_width * zoom), int(picture_height * zoom))
)
# Create a mask with the client's specified background color.
mask_im = Image.new(
mode="RGB",
size=(int(picture_width * zoom), frame_height),
color=background_color,
)
# Paste our resized image within the mask.
rect_x, rect_y = rect_used[0], rect_used[1]
mask_im.paste(
picture_resized,
(
int(int(rect_x) * (zoom * -1)),
int(int(rect_y) * (zoom * -1)),
),
)
# Paste centered within our directed frame location.
page_img.paste(
mask_im,
(
center_point_x - (frame_width // 2),
center_point_y - (frame_height // 2),
),
)
# Object is text.
elif object_type == ObjectTypes.TEXT:
font_r, font_g, font_b = parse_rgb(object_section["FontColor"])
start_position_x, start_position_y = parse_coords(
object_section["StartPosition"]
)
character_width = int(float(object_section["Ch_Width_Size"]))
character_height = int(float(object_section["Ch_Height_Size"]))
text = " ".join(object_section["Text"].split())
# When possible, we want to localize.
if text == "W i i 番 号":
text = "Wii Number:"
elif text == "電話番号":
text = "Phone Number:"
try:
number = int(text.replace(" ", ""))
if len(str(number)) == 16:
# Remove extra spaces
text = " ".join(re.findall("....", text.replace(" ", "")))
if start_position_x == 358:
start_position_x = 585
elif start_position_x == 388:
start_position_x = 715
except ValueError:
pass
draw = ImageDraw.Draw(page_img)
font = ImageFont.truetype(
"templates/fonts/FOT-RodinNTLGPro-DB.otf", character_height
)
draw.text(
(start_position_x, start_position_y),
text,
(font_r, font_g, font_b),
font,
)
# Object is a background.
elif object_type == ObjectTypes.BACKGROUND:
bg_frame_id = "templates/templates/{}".format(
object_section["BGFrameID"].replace(".bmp", ".png")
)
background = Image.open(bg_frame_id, "r").convert("RGBA")
page_img.paste(background, (0, 0), background)
page_filename = f"Page {page_num}.jpg"
page_save_path = determine_path(order_id, page_filename)
page_img.convert("RGB")
page_img.save(f"{page_save_path}", optimize=True)
if debug:
print(f"Processed {page_filename}")