Dokumentation
PDFs erstellen
PDFs in Vue
PDFs in Nuxt
PDFs in React
PDFs in Angular
SVG und Canvas
PDF ist ein Vektorformat. Ein als PNG eingefügtes Logo wirft das weg: Es wird unscharf, wenn jemand hineinzoomt, fett im Druck, und es trägt Pixel in ein Dokument, das archiviert werden soll. jasy zeichnet stattdessen Vektoren - entweder aus einer SVG-Datei, die Sie schon haben, oder mit einem Pen, den Sie selbst führen.
Ein SVG-Logo
Image erkennt eine SVG-Quelle von selbst, ein Logo braucht also keine Sonderbehandlung:
import { Document, Page, Image, Svg, renderToBytes } from "@jasy/pdf";
Image("logo.svg", { width: 120 }); // a path (Node), bytes, or markup
Svg(markup, { width: 120, alt: "Acme" }); // the same thing, named explicitly
Ohne Größe zeichnet es in seiner eigenen Größe geändert in beta.5 - width/height aus der Datei,
sonst ihre viewBox, sonst 300 x 150 px, wie im Browser. Eine User Unit ist ein CSS-Pixel, 1/96 Zoll, nach
derselben Regel wie bei einem Raster-Image, und pt, pc, in, cm, mm und q
werden umgerechnet, width="100mm" sind also 100 mm. Gibt die Datei eine Seite an, folgt die andere der
Aspect Ratio der viewBox; eine Größe in % zählt als nicht angegeben. Vor beta.5 war eine User Unit ein
Punkt, und Einheiten wurden ignoriert.
Legen Sie eine Achse fest, folgt die andere der Aspect Ratio, genau wie bei einem <img> ohne Größe;
ist es breiter als der angebotene Platz, wird es verkleinert, nie vergrößert. Die Zeichnung wird
gleichmäßig skaliert und in ihrer Box zentriert, das ist der eigene Standard von SVG für
preserveAspectRatio.
Die Datei wird gelesen und geparst, wenn Sie sie nennen, nicht beim Rendern, ein falscher Pfad oder eine fehlerhafte Datei scheitert also in dieser Zeile und nicht drei Aufrufebenen tief in einem Rendervorgang.
Was es zeichnet
| Wird gezeichnet | Wird mit Namen gemeldet, nie übersprungen |
|---|---|
path (Bögen, Kurzformen, relative Koordinaten), rect, circle, ellipse, line, polyline, polygon | <text> - wandeln Sie es in Pfade um |
g, defs, viewBox, jede transform-Funktion | <use>, <symbol> |
Presentation Attributes, style="" und <style>-Blöcke mit flachen Selektoren | ein CSS-Combinator oder eine Pseudo-Class |
fill, stroke (Width, Cap, Join, Dash), fill-rule, Opacity | filter, <mask> - Pixeloperationen |
<clipPath>, <linearGradient>, <radialGradient>, href-Ketten | ein Gradient auf einem Stroke |
SVG ist ein Dokumentformat, kein Grafikformat. Es bringt CSS, Text-Layout, Filter und Animation mit, und Filter und Masken sind Pixeloperationen, die kein Vektor-Backend ausdrücken kann - deshalb hat alles, was SVG vollständig rendert, einen Rasterizer, und deshalb liefert jede PDF-Bibliothek ein Subset. Unsere steht oben.
Der Unterschied liegt darin, was an ihrem Rand passiert: Ein Element, das jasy nicht zeichnen kann, ist ein
benannter Fehler mit Lösung, nie ein stilles Überspringen. Ein weggelassenes Element hinterlässt ein
Logo, das sich rendern lässt und unbemerkt falsch ist, und niemand merkt es. (Gemessen: Die
<style>-Verarbeitung von react-pdf verliert jede Füllung eines Illustrator-Exports mit dessen
Standardoption „Internes CSS“.)
@jasy/pdf: the SVG element <text> is not supported yet.
Convert the text to outlines in your editor - a PDF cannot lay out SVG text.
Das Subset wurde an 10.819 echten SVG-Dateien von einem Arbeitsrechner geprüft: 96,4 % werden
gerendert. Die Korrektheit wird gegen Headless-Chrome geprüft, nicht gegen unsere eigenen Überlegungen - so
haben wir herausgefunden, dass das Root-<svg> auf sich selbst zuschneidet und dass sein eigenes
fill="none" (der Standard beim Export aus Figma, in 778 dieser Dateien) gelesen werden muss, sonst wird
jede Form ohne Füllung schwarz.
Selbst zeichnen
Canvas ist der Ausweg für alles, was keine Komponente abdeckt: eine Sparkline, ein Siegel, ein Diagramm,
ein Schnörkel unter der Unterschrift. Sie bekommen eine Box im Layout und einen typisierten Pen.
import { Canvas } from "@jasy/pdf";
Canvas({ height: 46 }, (c, { width, height }) => {
c.move(0, height).line(20, 12).line(60, 22).line(width, 4);
c.stroke("#1450aa", { width: 1.5, cap: "round", join: "round" });
c.circle(width - 4, 4, 2.5).fill("#1450aa");
});
Die Funktion bekommt die Größe, die das Layout ermittelt hat, eine Zeichnung, die gegen size.width
geschrieben ist, funktioniert also in jeder Breite - setzen Sie dieselbe Funktion in eine Box mit 300pt und
eine mit 160pt, und sie passt in beide.
Der Pen
| Einen Pfad bauen | Ihn malen |
|---|---|
move line curve quad close | fill(paint?, { rule }) |
rect circle ellipse polygon | stroke(color?, { width, cap, join, miterLimit, dash, dashOffset }) |
path(d) - SVG-Path-Daten, durch denselben Parser, den Svg() verwendet | fillAndStroke(fill, stroke, opts) |
Malen schließt den Pfad ab und beginnt einen neuen, eine Form kann also nie in die nächste hineinlaufen.
fill nimmt jede Farbform, die auch der Rest der API nimmt - und einen Gradient, aufgelöst gegen die eigene
Bounding Box der Form, so wie objectBoundingBox in SVG.
Scopes statt save/restore
Eine Transformation oder ein Clip ist ein Scope, der sich selbst schließt. Es gibt kein Paar
save()/restore(), das man vergessen kann, und kein lineWidth(2), das ein späteres stroke() zufällig
übernimmt:
Canvas({ width: 90, height: 90 }, (c, { width }) => {
for (let i = 0; i < 12; i++) {
c.group({ translate: [width / 2, width / 2], rotate: i * 30 }, (g) => {
g.rect(-1, -width / 2 + 6, 2, i % 3 === 0 ? 10 : 5).fill("#0a2348");
});
}
c.clipped(
(clip) => clip.circle(width / 2, width / 2, 20),
(draw) => draw.rect(0, 0, width, width).fill("#f3dc29"),
);
});
Koordinaten beginnen oben links und sind in Punkt - dasselbe System wie bei jedem anderen Element und
dasselbe wie in SVG. 0,0 ist die eigene Ecke der Canvas-Box.
Ein Block im Layout, kein Weg daran vorbei
Eine Canvas wird auf ihre eigene Box zugeschnitten, bricht als eine unteilbare Einheit um und kann mit
alt in einem getaggten PDF zu einer Figure werden. Was sie absichtlich nicht hat, ist Textsatz:
Beschriftungen gehören in Text, in den Baum, wo sie markierbar, durchsuchbar und getaggt bleiben.
Row({ gap: 8, align: "stretch" }, [
Column(
{ justify: "between", width: 26 },
ticks.map((t) => Text(String(t), { size: 7 })),
),
Expanded(
{ flex: 1 },
Column({ gap: 4 }, [
Canvas({ height: 170, alt: "Monthly revenue" }, barChart),
Row(
{ justify: "between" },
months.map((m) => Text(m, { size: 7 })),
),
]),
),
]);
Das ist ein echtes Diagramm: Die Zeichnung ist eine Canvas, die Achsen sind Text im Layout.
Beides gibt es auch als Komponenten in Vue, React und Nuxt - siehe die Komponenten für Vue und React.