|
11 | 11 | import subprocess |
12 | 12 | import base64 |
13 | 13 | import hashlib |
| 14 | +import io |
| 15 | +from PIL import Image |
14 | 16 |
|
15 | 17 |
|
16 | 18 | logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
|
49 | 51 | intro = f.read() |
50 | 52 |
|
51 | 53 |
|
| 54 | +WEBP_QUALITY = 85 |
| 55 | + |
| 56 | + |
52 | 57 | def image_from_cell(cell): |
| 58 | + """Save the cell's PNG output as WebP and return its path and pixel size. |
| 59 | +
|
| 60 | + The filename stays the MD5 of the base64 PNG, so a plot that hasn't changed |
| 61 | + keeps its URL. The pixel size lets the template declare width/height, which |
| 62 | + reserves layout space for the lazily loaded images. |
| 63 | + """ |
53 | 64 | try: |
54 | 65 | for c in cell['outputs']: |
55 | 66 | if 'data' in c and 'image/png' in c['data']: |
56 | 67 | base64_img = c['data']['image/png'].replace("\n", "").strip() |
57 | 68 | filename = hashlib.md5() |
58 | 69 | filename.update(base64_img.encode('ascii')) |
59 | | - web_path = "/img/plots/{}.png".format(filename.hexdigest()) |
| 70 | + web_path = "/img/plots/{}.webp".format(filename.hexdigest()) |
60 | 71 | full_path = "web" + web_path |
61 | | - with open(full_path, "wb") as fh: |
62 | | - fh.write(base64.b64decode(base64_img)) |
63 | | - return web_path |
| 72 | + image = Image.open(io.BytesIO(base64.b64decode(base64_img))) |
| 73 | + if image.mode not in ("RGB", "RGBA"): |
| 74 | + image = image.convert("RGBA") |
| 75 | + image.save(full_path, "WEBP", quality=WEBP_QUALITY, method=6) |
| 76 | + return {"path": web_path, "width": image.width, "height": image.height} |
64 | 77 | except KeyError as e: |
65 | 78 | logging.error("Can't find image in cell: %s", cell['source']) |
66 | 79 | raise e |
@@ -145,7 +158,9 @@ def extract_cells(path): |
145 | 158 | "cell_num": cell_num, |
146 | 159 | "package": packages.get(tags["package"], tags["package"]), |
147 | 160 | "package-slug": tags['package'], |
148 | | - "image": image, |
| 161 | + "image": image["path"], |
| 162 | + "image-width": image["width"], |
| 163 | + "image-height": image["height"], |
149 | 164 | "content": source, |
150 | 165 | "comment": md.convert(comment) or None, |
151 | 166 | }) |
|
0 commit comments