(args: {
binary: Buffer
ext: string
workspaceId: string
})
| 31 | * Throws on a sandbox/infra failure or when the doc renders to zero pages. |
| 32 | */ |
| 33 | export async function renderDocToGrid(args: { |
| 34 | binary: Buffer |
| 35 | ext: string |
| 36 | workspaceId: string |
| 37 | }): Promise<DocRender> { |
| 38 | const ext = args.ext.toLowerCase() |
| 39 | if (!isRenderableDocExt(ext)) { |
| 40 | throw new Error(`Cannot render .${ext} to images (supported: pptx, docx, pdf)`) |
| 41 | } |
| 42 | |
| 43 | const script = ` |
| 44 | import subprocess, glob, base64, json |
| 45 | from PIL import Image |
| 46 | |
| 47 | ext = ${JSON.stringify(ext)} |
| 48 | inp = f"/home/user/input.{ext}" |
| 49 | pdf = inp if ext == "pdf" else "/home/user/input.pdf" |
| 50 | |
| 51 | if ext != "pdf": |
| 52 | subprocess.run( |
| 53 | ["soffice", "--headless", "--convert-to", "pdf", "--outdir", "/home/user", inp], |
| 54 | check=True, timeout=120, capture_output=True, |
| 55 | ) |
| 56 | |
| 57 | subprocess.run( |
| 58 | ["pdftoppm", "-jpeg", "-r", "${RENDER_DPI}", "-l", "${MAX_RENDER_PAGES}", pdf, "/home/user/page"], |
| 59 | check=True, timeout=120, capture_output=True, |
| 60 | ) |
| 61 | |
| 62 | paths = sorted(glob.glob("/home/user/page*.jpg"))[:${MAX_RENDER_PAGES}] |
| 63 | imgs = [Image.open(p).convert("RGB") for p in paths] |
| 64 | n = len(imgs) |
| 65 | if n == 0: |
| 66 | print("__SIM_RESULT__=" + json.dumps({"grid": None, "pageCount": 0})) |
| 67 | else: |
| 68 | cols = 1 if n == 1 else (2 if n <= 6 else 3) |
| 69 | rows = (n + cols - 1) // cols |
| 70 | cell_w = max(i.width for i in imgs) |
| 71 | cell_h = max(i.height for i in imgs) |
| 72 | pad = 12 |
| 73 | grid = Image.new("RGB", (cols * cell_w + (cols + 1) * pad, rows * cell_h + (rows + 1) * pad), (240, 240, 240)) |
| 74 | for idx, im in enumerate(imgs): |
| 75 | r, c = divmod(idx, cols) |
| 76 | grid.paste(im, (pad + c * (cell_w + pad), pad + r * (cell_h + pad))) |
| 77 | # Cap the grid's longest edge so the JPEG stays a reasonable vision input. |
| 78 | max_edge = 2200 |
| 79 | if max(grid.size) > max_edge: |
| 80 | scale = max_edge / max(grid.size) |
| 81 | grid = grid.resize((int(grid.width * scale), int(grid.height * scale))) |
| 82 | grid.save("/home/user/grid.jpg", "JPEG", quality=80) |
| 83 | with open("/home/user/grid.jpg", "rb") as f: |
| 84 | print("__SIM_RESULT__=" + json.dumps({"grid": base64.b64encode(f.read()).decode(), "pageCount": n})) |
| 85 | `.trim() |
| 86 | |
| 87 | const result = await executeInE2B({ |
| 88 | code: script, |
| 89 | language: CodeLanguage.Python, |
| 90 | timeoutMs: RENDER_TIMEOUT_MS, |
no test coverage detected