|
|
|
@@ -297,9 +297,9 @@ async function nachbearbeiten(
|
|
|
|
|
const textbreite = Math.max(
|
|
|
|
|
...zeilen.map((zeile) => schrift.widthOfTextAtSize(zeile, groesse))
|
|
|
|
|
);
|
|
|
|
|
let unten = eintrag.y - 2;
|
|
|
|
|
let unten = eintrag.y - UNTERSCHRIFT.textY;
|
|
|
|
|
for (const zeile of zeilen) {
|
|
|
|
|
unten -= groesse * 1.15;
|
|
|
|
|
unten -= groesse * UNTERSCHRIFT.zeile;
|
|
|
|
|
seite.drawText(zeile, {
|
|
|
|
|
x: eintrag.x,
|
|
|
|
|
y: unten,
|
|
|
|
@@ -312,9 +312,9 @@ async function nachbearbeiten(
|
|
|
|
|
// erkennbar zu bleiben: ein halber Punkt in hellem Grau ging zwischen
|
|
|
|
|
// den Tabellenlinien schlicht unter.
|
|
|
|
|
seite.drawLine({
|
|
|
|
|
start: { x: eintrag.x, y: eintrag.y - 1.5 },
|
|
|
|
|
end: { x: eintrag.x + textbreite, y: eintrag.y - 1.5 },
|
|
|
|
|
thickness: 0.9,
|
|
|
|
|
start: { x: eintrag.x, y: eintrag.y - UNTERSCHRIFT.strichY },
|
|
|
|
|
end: { x: eintrag.x + textbreite, y: eintrag.y - UNTERSCHRIFT.strichY },
|
|
|
|
|
thickness: UNTERSCHRIFT.strich,
|
|
|
|
|
color: PDFLib.rgb(0.15, 0.15, 0.15),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@@ -342,12 +342,77 @@ async function nachbearbeiten(
|
|
|
|
|
return dokument.save();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Masse des Textblocks unter einer Unterschrift -- in PDF-Punkten.
|
|
|
|
|
*
|
|
|
|
|
* Die Vorschau rechnet dieselben Zahlen in Pixel um. Standen sie zweimal im
|
|
|
|
|
* Code, liefen sie auseinander: die Marke haengt mit ihrer **Unterkante** am
|
|
|
|
|
* Ankerpunkt, das PDF setzt dort die **Unterkante des Bildes** und schreibt
|
|
|
|
|
* den Text darunter. Beim Setzen sass der Block deshalb um die Texthoehe
|
|
|
|
|
* hoeher als in der gespeicherten Datei.
|
|
|
|
|
*/
|
|
|
|
|
const UNTERSCHRIFT = {
|
|
|
|
|
//: Mitte des Strichs unter der Bildkante.
|
|
|
|
|
strichY: 1.5,
|
|
|
|
|
//: Staerke des Strichs.
|
|
|
|
|
strich: 0.9,
|
|
|
|
|
//: Bildkante bis zur ersten Grundlinie, vor dem Zeilenabstand.
|
|
|
|
|
textY: 2,
|
|
|
|
|
//: Zeilenabstand als Vielfaches der Schriftgroesse.
|
|
|
|
|
zeile: 1.15,
|
|
|
|
|
//: Oberkante der Zeilenbox bis zur Grundlinie, in "em" -- nur ein
|
|
|
|
|
//: Rueckfall: gewoehnlich misst "grundlinie()" das am echten Text nach.
|
|
|
|
|
//: (Helvetica/Arial: Ascender 0,905 plus halber Durchschuss.)
|
|
|
|
|
grundlinie: 0.9215,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//: Gemessene Grundlinien, je dargestellter Schriftgroesse in Pixeln.
|
|
|
|
|
const grundlinien = new Map();
|
|
|
|
|
|
|
|
|
|
/** Abstand von der Oberkante der Zeilenbox zur Grundlinie, in "em".
|
|
|
|
|
*
|
|
|
|
|
* Gemessen statt geraten, und bei genau der Pixelgroesse, die auch auf dem
|
|
|
|
|
* Schirm steht. Zwei Gruende: welche Datei hinter "Helvetica" steckt,
|
|
|
|
|
* entscheidet das Geraet -- auf einem iPad eine andere als auf einem
|
|
|
|
|
* Windows-Rechner, mit anderen Oberlaengen. Und der Browser rundet die
|
|
|
|
|
* Schriftmasse auf ganze Pixel: bei acht Punkt Schrift verschiebt das die
|
|
|
|
|
* Zeile um einen halben Pixel gegenueber der Rechnung. pdf-lib rundet nicht.
|
|
|
|
|
*
|
|
|
|
|
* Eine leere Inline-Box sitzt auf der Grundlinie -- ihre Lage verraet also,
|
|
|
|
|
* wo der Text steht.
|
|
|
|
|
*/
|
|
|
|
|
function grundlinie(px) {
|
|
|
|
|
const schluessel = px.toFixed(1);
|
|
|
|
|
const gemerkt = grundlinien.get(schluessel);
|
|
|
|
|
if (gemerkt !== undefined) return gemerkt;
|
|
|
|
|
let anteil = UNTERSCHRIFT.grundlinie;
|
|
|
|
|
try {
|
|
|
|
|
const probe = document.createElement("div");
|
|
|
|
|
probe.className = "viewer__unterschrifttext";
|
|
|
|
|
probe.style.cssText =
|
|
|
|
|
"position:absolute;left:-9999px;top:0;visibility:hidden;border:0;padding:0;";
|
|
|
|
|
probe.style.fontSize = `${px}px`;
|
|
|
|
|
probe.style.lineHeight = String(UNTERSCHRIFT.zeile);
|
|
|
|
|
const lot = document.createElement("span");
|
|
|
|
|
lot.style.cssText = "display:inline-block;width:0;height:0;";
|
|
|
|
|
probe.append(document.createTextNode("Hg"), lot);
|
|
|
|
|
document.body.append(probe);
|
|
|
|
|
const oben = probe.getBoundingClientRect().top;
|
|
|
|
|
const auf = lot.getBoundingClientRect().top;
|
|
|
|
|
probe.remove();
|
|
|
|
|
if (auf > oben) anteil = (auf - oben) / px;
|
|
|
|
|
} catch (fehler) {
|
|
|
|
|
console.warn("Grundlinie nicht messbar -- Rueckfall auf Helvetica", fehler);
|
|
|
|
|
}
|
|
|
|
|
grundlinien.set(schluessel, anteil);
|
|
|
|
|
return anteil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Schriftgroesse des Namenszugs unter einer Unterschrift dieser Breite.
|
|
|
|
|
*
|
|
|
|
|
* Ein Achtzehntel: bei den 150 Punkt der vollen Breite sind das gut acht
|
|
|
|
|
* Punkt -- die uebliche Groesse einer Unterschriftszeile auf Papier. Dieselbe
|
|
|
|
|
* Rechnung benutzt die Vorschau (als ``cqw``), damit auf dem Schirm steht,
|
|
|
|
|
* was spaeter im PDF steht.
|
|
|
|
|
* Rechnung benutzt die Vorschau -- in Pixeln umgerechnet --, damit auf dem
|
|
|
|
|
* Schirm steht, was spaeter im PDF steht.
|
|
|
|
|
*/
|
|
|
|
|
function schriftgroesse(breite) {
|
|
|
|
|
return Math.max(5, Math.min(10, breite / 18));
|
|
|
|
@@ -637,6 +702,8 @@ async function pdfBetrachter(behaelter, seiten, url, basis, { formulare = true }
|
|
|
|
|
const MAX_BREITE = 150;
|
|
|
|
|
const MAX_HOEHE = 50;
|
|
|
|
|
const unterschriften = [];
|
|
|
|
|
//: Je gesetzte Unterschrift eine Funktion, die ihre Marke neu vermisst.
|
|
|
|
|
const neuzeichnen = new Set();
|
|
|
|
|
let wartendeUnterschrift = null;
|
|
|
|
|
let zeugenzeile = () => "";
|
|
|
|
|
|
|
|
|
@@ -714,19 +781,52 @@ async function pdfBetrachter(behaelter, seiten, url, basis, { formulare = true }
|
|
|
|
|
seitenEl.append(marke);
|
|
|
|
|
|
|
|
|
|
const zeichnen = () => {
|
|
|
|
|
// Die Marke ist genau die Zeichnung: ihre Unterkante liegt auf dem
|
|
|
|
|
// Ankerpunkt, so wie im PDF. Der Text haengt darunter (siehe
|
|
|
|
|
// Gestaltung: absolut, "top: 100%") und zaehlt nicht zur Hoehe --
|
|
|
|
|
// sonst saesse der Block in der Vorschau um die Texthoehe zu hoch.
|
|
|
|
|
marke.style.left = `${(eintrag.x / blick.width) * 100}%`;
|
|
|
|
|
marke.style.bottom = `${(eintrag.y / blick.height) * 100}%`;
|
|
|
|
|
marke.style.width = `${(eintrag.breite / blick.width) * 100}%`;
|
|
|
|
|
// Dieselbe Rechnung wie beim Stempeln, samt ihrer Ober- und Untergrenze
|
|
|
|
|
// -- in "cqw" (Prozent der Blockbreite), damit sie beim Zoomen mitgeht.
|
|
|
|
|
text.style.fontSize = `${(schriftgroesse(eintrag.breite) / eintrag.breite) * 100}cqw`;
|
|
|
|
|
// Alles Weitere wird gerechnet, nicht vom Stylesheet abgeleitet:
|
|
|
|
|
// Punkte mal Pixel-je-Punkt der gerade dargestellten Seite. Ueber
|
|
|
|
|
// Container-Einheiten ("cqw") ging das auch -- aber nur dort, wo der
|
|
|
|
|
// Browser sie kennt; wo nicht, erbte der Block die Schriftgroesse der
|
|
|
|
|
// Seite, und der Text war um die Haelfte zu breit.
|
|
|
|
|
const proPunkt = seitenEl.getBoundingClientRect().width / blick.width;
|
|
|
|
|
const groesse = schriftgroesse(eintrag.breite);
|
|
|
|
|
const inPixeln = groesse * proPunkt;
|
|
|
|
|
const oben = UNTERSCHRIFT.strichY - UNTERSCHRIFT.strich / 2;
|
|
|
|
|
text.style.fontSize = `${inPixeln}px`;
|
|
|
|
|
text.style.lineHeight = String(UNTERSCHRIFT.zeile);
|
|
|
|
|
text.style.marginTop = `${oben * proPunkt}px`;
|
|
|
|
|
text.style.borderTopWidth = `${UNTERSCHRIFT.strich * proPunkt}px`;
|
|
|
|
|
// Rahmenbreiten rundet der Browser auf ganze Geraetepixel: aus 1,21
|
|
|
|
|
// wird 1. Mit dem gerechneten Wert sass die erste Zeile deshalb einen
|
|
|
|
|
// Fuenftel Punkt zu hoch -- also mit dem tatsaechlich benutzten rechnen.
|
|
|
|
|
const strich =
|
|
|
|
|
(parseFloat(getComputedStyle(text).borderTopWidth) ||
|
|
|
|
|
UNTERSCHRIFT.strich * proPunkt) / proPunkt;
|
|
|
|
|
// Oberkante des Strichs, die Strichstaerke, dann so viel Luft, dass die
|
|
|
|
|
// erste Grundlinie dort sitzt, wo pdf-lib sie setzt: textY plus einen
|
|
|
|
|
// Zeilenabstand unter der Bildkante.
|
|
|
|
|
const innen =
|
|
|
|
|
UNTERSCHRIFT.textY +
|
|
|
|
|
UNTERSCHRIFT.zeile * groesse -
|
|
|
|
|
oben -
|
|
|
|
|
strich -
|
|
|
|
|
grundlinie(inPixeln) * groesse;
|
|
|
|
|
text.style.paddingTop = `${Math.max(0, innen) * proPunkt}px`;
|
|
|
|
|
};
|
|
|
|
|
// Beim Zoomen aendert sich Pixel-je-Punkt -- dann neu rechnen.
|
|
|
|
|
neuzeichnen.add(zeichnen);
|
|
|
|
|
zeichnen();
|
|
|
|
|
|
|
|
|
|
weg.addEventListener("click", (event) => {
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
const stelle = unterschriften.indexOf(eintrag);
|
|
|
|
|
if (stelle >= 0) unterschriften.splice(stelle, 1);
|
|
|
|
|
neuzeichnen.delete(zeichnen);
|
|
|
|
|
marke.remove();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
@@ -754,8 +854,8 @@ async function pdfBetrachter(behaelter, seiten, url, basis, { formulare = true }
|
|
|
|
|
} else {
|
|
|
|
|
eintrag.x = Math.max(2, start.x + (zug.clientX - startX) / proPunkt);
|
|
|
|
|
eintrag.y = Math.max(
|
|
|
|
|
// Platz fuer die Textzeilen unter der Zeichnung.
|
|
|
|
|
schriftgroesse(eintrag.breite) * 2.5,
|
|
|
|
|
// Platz fuer die beiden Textzeilen unter der Zeichnung.
|
|
|
|
|
UNTERSCHRIFT.textY + 2 * UNTERSCHRIFT.zeile * schriftgroesse(eintrag.breite),
|
|
|
|
|
start.y - (zug.clientY - startY) / proPunkt
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
@@ -778,6 +878,18 @@ async function pdfBetrachter(behaelter, seiten, url, basis, { formulare = true }
|
|
|
|
|
platzieren(event).catch((fehler) => console.error("Unterschrift", fehler));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Nach dem Zoomen stehen die Marken anteilig richtig, aber die Schrift
|
|
|
|
|
// haengt an der Pixelgroesse der Seite -- die ist jetzt eine andere.
|
|
|
|
|
eventBus.on("scalechanging", () => {
|
|
|
|
|
for (const zeichnen of neuzeichnen) {
|
|
|
|
|
try {
|
|
|
|
|
zeichnen();
|
|
|
|
|
} catch (fehler) {
|
|
|
|
|
console.error("Unterschrift neu vermessen", fehler);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
hinein: () => {
|
|
|
|
|
viewer.currentScale = Math.min(MAX_ZOOM, viewer.currentScale * 1.2);
|
|
|
|
|