fix: a picture prints as the picture, where and as large as the teacher asks
The black bars above and below a printed picture were neither in the picture nor in the layout. A PNG or JPEG in a tab of its own is shown by the engine on a viewer page whose background is rgb(14, 14, 14), and Qt prints page backgrounds by default; 0.2.6.1 printed that page. A picture is now taken as itself — drawn onto white, capped at 300 dpi across Letter — and laid out on the printer's paper by the preview, with Size (as large as fits down to 25%), Position (nine squares), Paper (automatic, upright, sideways) and Fill the whole page, which says it cuts the edges off and is never the default. The preview is the PDF that prints, laid out again after each change. Worksheet PDFs and lesson pages get no picture controls and are unchanged. Page backgrounds are no longer printed for lesson pages either, as in Chrome; Blink darkens light text when it does that, so a white-on-black page prints readable grey on white. "Colour" is "Color" in the print interface and the manual. Lab: 135 print checks, now including eight picture layouts measured on the job file CUPS kept — byte-identical to the preview, within 1 point of where asked, no ink anywhere else. Printed from the real session with the pointer: 50% top left, Elmer-66, the same. Not yet on paper. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6946bae455
commit
940f63e0e5
@@ -0,0 +1,144 @@
|
||||
"""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:])
|
||||
Reference in New Issue
Block a user