fix: black and white is shown as it prints, and right-click prints the picture

Printing through Elmer found two things. Black and white came out grey
while the preview stayed in colour: 0.2.6.2 only told the driver. And
right-clicking a picture in a lesson and choosing Print printed the whole
page around it, where some pictures cannot be opened on their own at all.

Black and white is now made before the preview. Ghostscript makes a grey
copy, the preview shows it, and CUPS is given that same file, still with
ColorModel=Grayscale so Elmer's PCL 6 driver prints with pxlmono. Measured
first: Ghostscript's own grey of the colour original and of the copy give
the same greys (Elmer's driver is Ghostscript); poppler's do not. It takes
up to 4.4 s for a lesson page of pictures, so it runs in the background
with a minute to finish, is made once and kept, and a failure is said so
and never printed in colour instead. Ghostscript is now a named dependency;
cups-filters already brought it.

Right-click asks the engine's hit test: on a picture or a canvas it offers
Print image and Print page, elsewhere only Print page. The picture comes
from the engine's own copy image — the only one of five ways measured that
worked for a signed-in picture, a hotlink-protected one, a one-time
address, blob:, data:, another site's, a canvas and WebP — and it fetches
nothing, so the teacher's sign-in is not needed again. It goes through the
clipboard, which is kept and put back; measured under KWin with another
program owning it. A picture that never arrives is said so; the page is
not printed in its place. It then gets the 0.2.6.2 picture layout.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ekklesia
2026-09-26 16:34:38 -05:00
co-authored by Claude Opus 5.5
parent 550c0bf126
commit 594fbd810e
4 changed files with 514 additions and 44 deletions
+56 -1
View File
@@ -230,6 +230,24 @@ check("every position is on a three by three grid",
len(printing.PICTURE_POSITIONS) == 9
and sorted(printing.PICTURE_POSITIONS.values()) == [(c, r) for c in range(3) for r in range(3)])
print()
print("4c. black and white, made before the preview")
command = printing.grayscale_command(work / "in.pdf", work / "out gray.pdf")
check("Ghostscript writes a PDF", command[0] == "gs" and "-sDEVICE=pdfwrite" in command, command)
check("in grey and nothing else",
"-sColorConversionStrategy=Gray" in command and "-dProcessColorModel=/DeviceGray" in command, command)
# pdfwrite otherwise turns a page to match its text; a landscape picture
# on a portrait sheet would come back turned.
check("never turning a page", "-dAutoRotatePages=/None" in command, command)
check("annotations drawn in as they print, not left for a filter to draw in colour",
"-dPreserveAnnots=false" in command and "-dPrinted" in command, command)
check("safely, and without stopping to ask",
"-dSAFER" in command and "-dBATCH" in command and "-dNOPAUSE" in command, command)
check("to the file asked for, even with a space in its name",
f"-sOutputFile={work / 'out gray.pdf'}" in command, command)
check("and the source after --, so no name can be read as an option",
command[-2:] == ["--", str(work / "in.pdf")], command)
print()
print("5. Wonder Ink prints through this, and only this")
ink = (root / "scripts/classroom-wonderink").read_text()
@@ -239,7 +257,10 @@ check("no QPrinter", "QPrinter(" not in code and "QPrintDialog" not in code)
check("no QWebEngineView.print()", not re.search(r"\bview\.print\(", code))
check("a lesson page becomes a PDF by printToPdf", "printToPdf(" in code)
check("which is given up on if the engine never answers",
"PREPARE_TIMEOUT_MS" in code and "self._timer.start(PREPARE_TIMEOUT_MS)" in code)
"def _wait(self, message, too_long, timeout=PREPARE_TIMEOUT_MS):" in code
and "self._timer.start(timeout)" in code)
check("black and white has longer, since a page of photographs is slow, not stuck",
"GRAY_TIMEOUT_MS = 60_000" in code and " GRAY_TIMEOUT_MS,\n" in code)
check("the preview submits with classroom_printing.submit",
"classroom_printing.submit(" in code)
check("there is no file chooser in it", "QFileDialog" not in code)
@@ -255,6 +276,40 @@ check("the print interface says Color, not Colour",
'"Color"' in preview_code and '"Colour"' not in preview_code)
check("an exception in a handler is logged, not fatal",
"sys.excepthook = keep_going" in code)
# 0.2.6.3: black and white is shown as it prints.
check("black and white is made by classroom_printing.grayscale_command, in the background",
"classroom_printing.grayscale_command(" in preview_code and "QProcess(" in preview_code)
check("choosing Color or Black and white lays the preview out again",
"self._colour.currentIndexChanged.connect" in preview_code)
check("a failed conversion is judged by its exit and its pages, never printed in colour",
"process.exitCode() != 0" in preview_code
and "page_count(target) != expected" in preview_code)
check("the driver is still told ColorModel=Grayscale for the grey file",
'gray = self._options[printer]["gray"]' in preview_code)
check("and nothing prints that is not what is shown",
"(self._gray is not None and self._pdf == self._gray[1]) != (gray is not None)" in preview_code)
# 0.2.6.3: right-click says what it prints.
menu_code = code[code.index(" def contextMenuEvent"):code.index("\nclass WonderInk")]
check("right-click offers Print page always",
'"Print page…"' in menu_code and "print_view(self)" in menu_code)
check("and Print image only where the engine's hit test found a picture or a canvas",
'"Print image…"' in menu_code and "MediaTypeImage" in menu_code
and "MediaTypeCanvas" in menu_code and "print_image(self)" in menu_code)
check("the old single Print… is gone from the menu", '"Print…"' not in menu_code)
take_code = code[code.index(" def _take_picture"):code.index(" def _show_preview")]
check("the picture is the engine's own copy, not fetched again",
"WebAction.CopyImageToClipboard" in take_code
and "download(" not in take_code and "QNetwork" not in take_code)
check("what was on the clipboard is kept and put back",
"keep_clipboard(clipboard)" in take_code and "put_back()" in take_code)
check("a picture that never arrives is said so, not printed as the page",
"TAKE_PICTURE_MS" in take_code and "dialog.fail(" in take_code
and "print_view" not in take_code)
check("and it goes to the picture layout, on white",
"dialog.show_picture(printable_picture(image))" in take_code
and "Qt.GlobalColor.white" in code)
check("the module is packaged",
"lib/classroom_printing.py" in (root / "tools/build-release.sh").read_text())