All projects PokeProxy · Write-up

Millimetres, not pixelsMillimeter, nicht Pixel

PokeProxy prints sheets of Pokémon card proxies for the binder, at 63 × 88 mm. Every position on its pages is set in millimetres, so the preview and the printout are one layout, and the cards come from TCGSL’s catalogue.PokeProxy druckt Bögen mit Platzhaltern für Pokémon-Karten im Sammelordner, in 63 × 88 mm. Jede Position auf seinen Seiten ist in Millimetern gesetzt, so sind Vorschau und Ausdruck ein Layout, und die Karten kommen aus dem Katalog von TCGSL.

pokeproxy.ewolution.cloud Source

Type
Web appWeb-App
Stack
Svelte 5 · Node
Year
2026
Code
Public on GitHubÖffentlich auf GitHub

A sheet for the binder

PokeProxy makes printable sheets of card proxies: stand-ins at the real size for the cards a binder is still missing, until the real ones arrive. The sheet comes first. Pick the paper (A4, A3, A5, Letter or Legal, upright or sideways) and the card size, 63 × 88 mm by default, the size of every standard trading card, or a size of your own. The page shows the grid that fits inside a 5 mm printer margin, centred: three by three on A4, four by four on A3. Space between the cards is optional; with none, one cut separates two cards. Cut marks sit in the margin at every edge, and a 50 mm line under the cards is there to measure after printing, to see the printer kept the size.

Cards print in colour, in greyscale (the default, so a proxy never passes for the real card) or in a lighter grey that saves ink, for the whole sheet or card by card. The slots run on from page to page, so a different paper or card size flows the same cards onto the new pages in their order, and a full page starts the next one.

Millimetres, not pixels

Every position on a page is a number of millimetres times one variable, --mm. On screen that is a scale, so the page fits the window; in print it is exactly 1mm, and the page itself gets an @page size of the chosen paper with no margins. So the preview and the printout are the same layout, and a card comes out at 63 by 88 mm. Only pages with a card on them print.

That was checked by printing to PDF in headless Chromium and measuring the result: the page is A4 to the point, every card is 63.00 by 88.00 mm and sits where the grid says. The same check found the one thing a screen never shows: the sticky header had printed on a page of its own. In the print dialog: scale 100 % and no margins.

63 × 88 mmA card on paper, measured in the printed PDF.
33,445English and Japanese cards in the search.
734 pxThe printed picture’s width, about 300 dpi on a card.

Cards from TCGSL

PokeProxy has no card data of its own. TCGSL, the family’s set list, already keeps a daily copy of every English and Japanese set, and serves each card’s picture resized, up to 734 pixels wide. So the server reads TCGSL’s public snapshot once a day, about 2 MB in all, and keeps every card in memory. A search takes a few milliseconds: every word has to fit the card’s name, its set (by name, code or id) or its number, the best fit comes first, and the newest set first among equals. “umbreon vmax”, “151 6”, “MEW 199/165” and “151 jp 6” all find what you’d expect, and Japanese names can be typed in katakana or hiragana. A click puts the card in the selected slot and moves on to the next empty one.

Links, read carefully

A pasted link turns into a card too. Addresses the server knows need no request at all: TCGSL’s pictures and set pages, the image servers of pokemontcg.io and Scrydex, and Limitless TCG’s card pages, matched by set code and number. Anything else is fetched once: a picture as it is, or a page for the picture it names for link previews, which card sites set to the card. The bytes go back to the page, which keeps them like an upload, so a sheet never depends on another site staying up.

Fetching an address a stranger names is the risky part. The server only connects to public addresses on the usual ports, checks the address it actually connects to and again after every redirect, gives up after ten seconds, reads at most 2 MB of a page and 15 MB of a picture, and accepts only real PNG, JPEG, WebP, GIF or AVIF pictures, judged by their bytes. A link to the NAS itself is refused before any request. Sites behind bot protection don’t answer; copying the picture’s address there works.

Part of the family

Uploads never leave the device: the sheet lives in the browser’s storage and the pictures in IndexedDB. The tokens and components come from Folio, the family’s shared library, the server has no dependencies and sends a strict Content Security Policy, and the visit counter, Census, works without cookies.

Ein Bogen für den Ordner

PokeProxy macht druckbare Bögen mit Platzhalterkarten: Stellvertreter in echter Größe für die Karten, die im Sammelordner noch fehlen, bis die echten da sind. Zuerst kommt der Bogen. Papier wählen (A4, A3, A5, Letter oder Legal, hoch oder quer) und die Kartengröße, voreingestellt 63 × 88 mm, so groß wie jede Standard-Sammelkarte, oder eine eigene. Die Seite zeigt das Raster, das innerhalb von 5 mm Druckrand passt, mittig: drei mal drei auf A4, vier mal vier auf A3. Abstand zwischen den Karten ist wählbar; ohne Abstand trennt ein Schnitt zwei Karten. Schnittmarken sitzen im Rand an jeder Kante, und eine 50-mm-Linie unter den Karten misst man nach dem Druck nach, um zu sehen, ob der Drucker die Größe gehalten hat.

Gedruckt wird in Farbe, in Graustufen (voreingestellt, damit ein Platzhalter nie als echte Karte durchgeht) oder in einem helleren Grau, das Tinte spart, für den ganzen Bogen oder Karte für Karte. Die Plätze laufen von Seite zu Seite weiter, so verteilt ein anderes Papier oder eine andere Kartengröße dieselben Karten in ihrer Reihenfolge auf die neuen Seiten, und eine volle Seite beginnt die nächste.

Millimeter, nicht Pixel

Jede Position auf einer Seite ist eine Zahl von Millimetern mal eine Variable, --mm. Auf dem Bildschirm ist das ein Maßstab, damit die Seite ins Fenster passt; im Druck ist es genau 1mm, und die Seite selbst bekommt mit @page die Größe des gewählten Papiers ohne Ränder. So sind Vorschau und Ausdruck dasselbe Layout, und eine Karte kommt mit 63 mal 88 mm heraus. Gedruckt werden nur Seiten, auf denen eine Karte liegt.

Geprüft wurde das, indem headless Chromium in ein PDF druckte und das Ergebnis vermessen wurde: Die Seite ist auf den Punkt A4, jede Karte 63,00 mal 88,00 mm groß und liegt, wo das Raster es sagt. Dieselbe Prüfung fand das, was ein Bildschirm nie zeigt: Die mitlaufende Kopfzeile war auf einer eigenen Seite mitgedruckt worden. Im Druckdialog: Skalierung 100 % und keine Ränder.

63 × 88 mmEine Karte auf Papier, gemessen im gedruckten PDF.
33.445Englische und japanische Karten in der Suche.
734 pxDie Breite des gedruckten Bildes, etwa 300 dpi auf einer Karte.

Karten von TCGSL

PokeProxy hat keine eigenen Kartendaten. TCGSL, die Set-Liste der Familie, hält schon eine tägliche Kopie jedes englischen und japanischen Sets und liefert das Bild jeder Karte verkleinert aus, bis zu 734 Pixel breit. Also liest der Server einmal am Tag den öffentlichen Datenstand von TCGSL, insgesamt etwa 2 MB, und hält jede Karte im Speicher. Eine Suche dauert ein paar Millisekunden: Jedes Wort muss zum Namen der Karte passen, zu ihrem Set (über Namen, Code oder ID) oder zu ihrer Nummer, die beste Übereinstimmung kommt zuerst und unter gleich guten das neueste Set. „umbreon vmax“, „151 6“, „MEW 199/165“ und „151 jp 6“ finden, was man erwartet, und japanische Namen lassen sich in Katakana oder Hiragana tippen. Ein Klick legt die Karte auf den gewählten Platz und geht zum nächsten freien weiter.

Links, sorgfältig gelesen

Auch ein eingefügter Link wird zur Karte. Adressen, die der Server kennt, brauchen gar keine Anfrage: die Bilder und Set-Seiten von TCGSL, die Bildserver von pokemontcg.io und Scrydex und die Kartenseiten von Limitless TCG, zugeordnet über Set-Code und Nummer. Alles andere wird einmal geholt: ein Bild so, wie es ist, eine Seite wegen des Bildes, das sie für Linkvorschauen nennt, und das ist bei Kartenseiten die Karte. Die Bytes gehen an die Seite zurück, die sie wie einen Upload aufhebt, so hängt ein Bogen nie davon ab, dass eine andere Seite erreichbar bleibt.

Eine Adresse abzurufen, die ein Fremder nennt, ist der heikle Teil. Der Server verbindet sich nur mit öffentlichen Adressen auf den üblichen Ports, prüft die Adresse, mit der er sich tatsächlich verbindet, und nach jeder Weiterleitung noch einmal, gibt nach zehn Sekunden auf, liest höchstens 2 MB einer Seite und 15 MB eines Bildes und nimmt nur echte PNG-, JPEG-, WebP-, GIF- oder AVIF-Bilder an, erkannt an ihren Bytes. Ein Link auf das NAS selbst wird vor jeder Anfrage abgelehnt. Seiten hinter einem Bot-Schutz antworten nicht; dort die Bildadresse zu kopieren, funktioniert.

Teil der Familie

Hochgeladene Bilder verlassen das Gerät nie: Der Bogen liegt im Speicher des Browsers und die Bilder in IndexedDB. Tokens und Komponenten kommen aus Folio, der gemeinsamen Bibliothek der Familie, der Server hat keine Abhängigkeiten und schickt eine strenge Content Security Policy, und der Besuchszähler Census kommt ohne Cookies aus.