|
|
|
@@ -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,6 +342,71 @@ 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
|
|
|
|
@@ -716,17 +781,42 @@ 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}%`;
|
|
|
|
|
// Die Schriftgroesse wird gerechnet, nicht vom Stylesheet abgeleitet:
|
|
|
|
|
// 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. Genau das war der
|
|
|
|
|
// Unterschied zwischen Vorschau und Datei.
|
|
|
|
|
// Seite, und der Text war um die Haelfte zu breit.
|
|
|
|
|
const proPunkt = seitenEl.getBoundingClientRect().width / blick.width;
|
|
|
|
|
text.style.fontSize = `${schriftgroesse(eintrag.breite) * proPunkt}px`;
|
|
|
|
|
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);
|
|
|
|
@@ -764,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
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|