"""Make the test pictures, and measure where a printed picture really is. wonderink-picture-measure.py make DIRECTORY wonderink-picture-measure.py measure PDF WIDTHxHEIGHT SIZE POSITION PAPER FILL wonderink-picture-measure.py darkness PDF Run inside the lab by tools/lab/wonderink-print-check. The pictures are white with a black border drawn on their own edges and a pink mark near one corner, so the ink on a printed page says exactly where the picture landed — and any other dark ink on the page is something that was added, which is what the black bars were. The page is rendered at 72 dpi, so one pixel is one PostScript point, and compared against classroom_printing.picture_rect: the same arithmetic Wonder Ink lays the page out with, asked independently of the preview. """ import json import subprocess import sys sys.path.insert(0, "/usr/local/lib/ekklesia-classroom") import classroom_printing # noqa: E402 MARGIN_POINTS = 10 / 25.4 * 72 def make(directory): from PyQt6.QtCore import QRectF, Qt from PyQt6.QtGui import QColor, QFont, QImage, QPainter, QPen from PyQt6.QtWidgets import QApplication app = QApplication(sys.argv[:1]) # noqa: F841 — kept, or Qt has no application def art(width, height, name, label, transparent=False): image = QImage(width, height, QImage.Format.Format_ARGB32) image.fill(QColor(0, 0, 0, 0) if transparent else QColor("white")) painter = QPainter(image) painter.setPen(QPen(QColor("black"), 8)) painter.drawRect(QRectF(4, 4, width - 8, height - 8)) painter.setPen(QPen(QColor("black"), 6)) painter.drawEllipse(QRectF(width * 0.2, height * 0.25, width * 0.6, height * 0.5)) painter.setFont(QFont("DejaVu Sans", int(min(width, height) * 0.05))) painter.drawText(QRectF(0, height * 0.05, width, height * 0.15), Qt.AlignmentFlag.AlignCenter, label) painter.fillRect(QRectF(width * 0.05, height * 0.8, width * 0.1, height * 0.1), QColor("#e0006c")) painter.end() image.save(f"{directory}/{name}") art(1600, 1000, "ark-wide.png", "Colour the Ark") art(900, 1400, "ark-tall.png", "Noah") art(1600, 1000, "ark-wide.jpg", "Colour the Ark") # Transparent around the drawing: on paper that has to be white, not # black, which is what a careless composite would make of it. art(1200, 1200, "ark-clear.png", "Clear", transparent=True) def page_pixels(pdf): ppm = subprocess.run(["pdftoppm", "-r", "72", "-f", "1", "-l", "1", "-singlefile", pdf], capture_output=True, check=True).stdout # P6 header: magic, width, height, maxval, then raw RGB. fields, index = [], 0 while len(fields) < 4: while ppm[index:index + 1].isspace(): index += 1 start = index while not ppm[index:index + 1].isspace(): index += 1 fields.append(ppm[start:index]) width, height = int(fields[1]), int(fields[2]) return width, height, ppm[index + 1:] def measure(pdf, picture, size, position, paper, fill): width, height, rgb = page_pixels(pdf) picture_w, picture_h = (int(n) for n in picture.split("x")) landscape = classroom_printing.picture_landscape(paper, picture_w, picture_h) x, y, w, h = classroom_printing.picture_rect( (width, height), MARGIN_POINTS, (picture_w, picture_h), int(size), position.replace("-", " "), fill == "fill") # The expected ink, clipped to the printable area as the page is. left = max(x, MARGIN_POINTS) top = max(y, MARGIN_POINTS) right = min(x + w, width - MARGIN_POINTS) bottom = min(y + h, height - MARGIN_POINTS) xs, ys, outside, pink = [], [], 0, 0 for row in range(height): for column in range(width): i = (row * width + column) * 3 r, g, b = rgb[i], rgb[i + 1], rgb[i + 2] dark = r + g + b < 3 * 110 magenta = r > 150 and g < 90 and b > 60 if dark or magenta: xs.append(column) ys.append(row) if not (left - 2 <= column <= right + 2 and top - 2 <= row <= bottom + 2): outside += 1 if magenta: pink += 1 found = (min(xs), min(ys), max(xs) + 1, max(ys) + 1) if xs else None expected = (round(left), round(top), round(right), round(bottom)) off = max(abs(a - b) for a, b in zip(found, expected)) if found else None print(json.dumps({ "page": [width, height], "page_landscape": width > height, "wanted_landscape": landscape, "found": found, "expected": expected, "off_by_points": off, "ink_outside_picture": outside, "pink_pixels": pink, })) def darkness(pdf): """How much of the first page is dark, 0 to 1.""" width, height, rgb = page_pixels(pdf) sums = [rgb[i] + rgb[i + 1] + rgb[i + 2] for i in range(0, len(rgb) - 2, 3)] dark = sum(1 for total in sums if total < 3 * 110) # Anything printed at all. Blink darkens light text when a page's # background is not printed, so white-on-black lesson text comes out # grey on white rather than vanishing; this is what says it did. ink = sum(1 for total in sums if total < 3 * 200) print(json.dumps({"dark_fraction": round(dark / (width * height), 4), "ink_fraction": round(ink / (width * height), 4)})) if __name__ == "__main__": if sys.argv[1] == "make": make(sys.argv[2]) elif sys.argv[1] == "darkness": darkness(sys.argv[2]) else: measure(*sys.argv[2:])