Dokumentation

Komponenten

Jede Komponente entspricht einem Element der Engine, und jeder Prop-Typ IST der Optionstyp der Engine: Eine neue Option der Engine ist am Tag ihres Erscheinens ein typisierter Prop, und ein falscher Wert wird in Ihrem Editor rot. Boolesche Props haben die übliche JSX-Kurzform: <Text bold> ist <Text bold={true}>.

Alle Komponenten:

Document · Page · Column · Row · Box · Padding · Expanded · Spacer · Divider · Image · Svg · Canvas · Text · Paragraph · Span · Table / TableRow / TableCell · Positioned · DefaultTextStyle · Link · Anchor · Bookmark · Rotated · RotatedBox · PageBreak · KeepTogether · PageNumber · PageCount · SubPageNumber · SubPageCount · PageBuilder · SplitView · Flow · Region · ColumnBreak · TextField · Checkbox · RadioGroup · Dropdown · ListBox · PushButton · SignatureField.

Wo React einen eigenen Weg hat, wird er verwendet: Text und Labels sind Children, sich wiederholende Bereiche sind Props, und was pro Seite gebaut wird, ist eine Funktion.

Struktur

Document

Die Wurzel jedes PDF. Ihre Text-Props setzen die Standards für das ganze Dokument, die alles darin erbt.

PropTyp
size, color, font, bold, italicStandard-Textstil - jeder vererbbare Textstil
align, lineHeightStandard-Absatzstil
meta{ title?, author? }
fontseigene Fonts, siehe Daten und Assets
emojieine Quelle für Farb-Emoji, siehe Emoji

Page

Eine Seitenvorlage. Sie ordnet ihre Kinder als Column an, gap, justify und align funktionieren also auch hier.

PropTyp
size"A4", "Letter", ... oder eine eigene Größe
orientation"portrait" | "landscape"
margineine Zahl, oder Insets pro Seite
gapAbstand zwischen den Kindern
justify"start" | "center" | "end" | "between" | "around"
align"start" | "center" | "end" | "stretch"
header, footerein beliebiges Element, auf jeder Seite wiederholt

header und footer werden einmal angeordnet und auf jeder physischen Seite wiederholt, über die der Inhalt läuft, mit dem Platz, den sie brauchen, reserviert - der Inhalt läuft nie darunter:

<Document>
  <Page
    size="A4"
    margin={48}
    header={
      <Text size={9} color="#94a3b8">
        ACME GmbH - Invoice
      </Text>
    }
    footer={
      <Row justify="between">
        <Text size={9} color="#94a3b8">
          acme.example
        </Text>
        <PageNumber size={9} color="#94a3b8" />
      </Row>
    }
  >
    <Text>... long content that paginates across pages ...</Text>
  </Page>
</Document>

Layout

Column und Row

Stapeln die Kinder senkrecht (Column) oder waagerecht (Row). Beide nehmen die Optionen der Stacks der Engine: gap, justify, align, wrap, reverse, alignContent, eine width und height in Punkt oder "%", und die Page-Break-Props unten.

<Row gap={12} justify="between" align="center">
  <Text bold>Item</Text>
  <Text>Value</Text>
</Row>

Jedes Kind kann mit alignSelf aus der Ausrichtung seiner Zeile ausscheren, sich mit order verschieben und minWidth, maxWidth, minHeight, maxHeight und aspectRatio nehmen - siehe Layout.

Box

Ein Container mit Größe und Gestaltung: Hintergrund, Border, Padding, abgerundete Ecken.

PropTyp
bgeine Farbe, oder ein Gradient
bordereine Farbe
borderTop / borderRight / borderBottom / borderLeft, borderWidthBorder pro Seite
paddingeine Zahl, oder Insets pro Seite
width, heightPunkt, oder ein Prozentwert wie "50%"
radiuseine Zahl, oder Radien pro Ecke
relativemacht sie zum Frame für die Positionierung
overflow"hidden" schneidet die Kinder auf die Box zu

Padding, Spacer, Expanded, Divider

  • <Padding insets={16}> - umgibt ein Kind mit Abstand (eine Zahl oder Insets pro Seite).
  • <Spacer flex={1} /> - dehnbarer leerer Raum, der Geschwister auseinanderschiebt.
  • <Expanded flex={1}> - macht sein Kind zum Flex-Item, das seinen Anteil am restlichen Platz nimmt; flexBasis und flexShrink wie in CSS.
  • <Divider color="#e2e8f0" thickness={1} /> - eine Linie.

Text

Text, Paragraph und Span

Text und Paragraph zeichnen ihre Children als Text; Span gestaltet ein Stück darin. Total: {sum} ist ein Stück, gemessen und gekernt als eines.

PropTyp
size, font, colordas Grundlegende; font nimmt auch eine Fallback-Liste
bold, italicboolesche Kurzformen
align"left" | "center" | "right" | "justify"
lineHeightFaktor für die Line-Height
letterSpacing, wordSpacingin Punkt; negativ verengt
textIndentEinzug der ersten Zeile, in Punkt
textTransform"uppercase" | "lowercase" | "capitalize" | "none"
underline, strikethrough, skipInkLinien; skipInk führt sie um Descender herum
direction"ltr" | "rtl" - Hebräisch und Arabisch in richtiger Reihenfolge
ligaturesfalse schaltet fi/fl/ffi ab, die standardmäßig an sind
breakWord, hyphenatewas mit einem Wort geschieht, das zu lang für seine Box ist
orphans, widowsMindestzahl an Zeilen, die an einem Page Break zusammenbleiben
maxLines, overflowoverflow ist "clip" | "ellipsis"
role"h1".."h6" | "p" für ein getaggtes, barrierefreies PDF
hrefden Text mit einer externen URL verlinken
toden Text mit einem <Anchor> verlinken

<Span> nimmt die Stile eines Textstücks - size, font, color, bold, italic, die Linien, letterSpacing, wordSpacing, textTransform, ligatures, breakWord, hyphenate - plus href / to und verticalAlign ("super" | "sub"), das die Baseline eines Stücks verschiebt, ohne seine Größe zu ändern. Was zum ganzen Absatz gehört, bleibt an <Text>: align, lineHeight, textIndent, direction, maxLines, overflow, orphans, widows und role.

<Paragraph>
  Built with{" "}
  <Span href="https://jasy.dev" bold color="#1450aa">
    jasy
  </Span>
  , and this part is not a link.
</Paragraph>

Leerzeichen zwischen Spans schreiben Sie selbst, als {" "} - JSX verwirft die Zeilenumbrüche um ein Tag. <DefaultTextStyle> belegt den Textstil für einen ganzen Teilbaum neu vor, das Gegenstück pro Abschnitt zu den Standards von <Document>.

Image

<Image src={bytes} width={120} height={120} fit="cover" align="top" radius={8} alt="Logo" />

src nimmt Bild-Bytes (ein Uint8Array), eine URL oder - auf dem Server - einen Pfad; siehe Daten und Assets. fit ist "fill" (der Standard), "contain", "cover" oder "none", und align sagt, wo das Bild in seiner Box sitzt. Ohne Größe hat das Bild seine eigene Größe, passend verkleinert. Die Props sind die der Factory Image. Eine SVG-Quelle wird von selbst erkannt und bleibt ein Vektor.

Svg

<Svg src={logoMarkup} width={120} alt="Acme" />

src nimmt SVG-Markup, die Bytes der Datei oder - auf dem Server - einen Pfad. Ohne Größe zeichnet es in seiner eigenen Größe; legen Sie eine Achse fest, folgt die andere der viewBox. Mit alt wird es in einem getaggten PDF zu einer Figure. Was das Subset abdeckt, steht unter SVG und Canvas.

Canvas

Für ein Diagramm, eine Sparkline oder ein Siegel reicht <Canvas> Ihrer Funktion einen typisierten Pen und die Größe, die das Layout ermittelt hat:

import type { CanvasPaint } from "@jasy/pdf";

const months = [12, 19, 14, 27, 22, 31];

const sparkline: CanvasPaint = (c, { width, height }) => {
  const max = Math.max(...months);
  c.move(0, height - (months[0] / max) * height);
  months
    .slice(1)
    .forEach((v, i) =>
      c.line(((i + 1) / (months.length - 1)) * width, height - (v / max) * height),
    );
  c.stroke("#1450aa", { width: 1.5, cap: "round" });
};

<Canvas height={46} paint={sparkline} />;

Dieselbe Funktion funktioniert in jeder Breite, eine Definition dient also einer breiten Karte und einer schmalen Seitenleiste.

Table

<Table columns> enthält <TableRow>s aus <TableCell>s. Markieren Sie eine Zeile mit header, um sie auf jeder Seite zu wiederholen, über die die Tabelle läuft. Eine Zahl ist eine feste Breite in Punkt, "1fr" ein flexibler Anteil, "auto" die breiteste Zelle.

<Table columns={["1fr", 120]} cellBorder="#e2e8f0" cellPadding={9}>
  <TableRow header>
    <TableCell>
      <Text bold>Description</Text>
    </TableCell>
    <TableCell>
      <Text bold>Amount</Text>
    </TableCell>
  </TableRow>
  {lines.map((line) => (
    <TableRow key={line.id}>
      <TableCell>{line.name}</TableCell>
      <TableCell>{`${line.total} EUR`}</TableCell>
    </TableRow>
  ))}
</Table>

Eine Zelle enthält einen String oder beliebige Komponenten. Table nimmt außerdem gap / rowGap / colGap, cellPadding, cellBorder und eine Farbe für rule.

Positioned

Platzierung außerhalb des Flows, verankert am nächsten <Box relative> (oder an der Seite). Geben Sie ihm Kanten (top / right / bottom / left) oder einen Anchor (h / v) plus einen Versatz x / y. zIndex legt die Zeichenreihenfolge fest - siehe Positionierung.

<Box relative height={200}>
  <Positioned top={8} right={8}>
    <Text size={9} color="#94a3b8">
      draft
    </Text>
  </Positioned>
</Box>

<Link> macht sein Kind anklickbar. href öffnet eine URL, to springt zu einem <Anchor> im selben Dokument - genau eines von beiden, und die Typen sagen das.

KomponenteProps
<Link>href | to
<Anchor>name (Pflicht)
<Bookmark>title (Pflicht), level

Eine Kapitelüberschrift will einen Eintrag in der Seitenleiste und ein Sprungziel. Verschachteln Sie sie:

<Bookmark title="2. Installation" level={1}>
  <Anchor name="install">
    <Text size={22} bold>
      2. Installation
    </Text>
  </Anchor>
</Bookmark>

Wie Links, Anchors und Bookmarks darunter funktionieren, steht unter Navigation.

Rotated und RotatedBox

<Rotated angle> dreht sein Kind beim Zeichnen: Die Layout-Box bleibt ungedreht, Geschwister ordnen sich also nicht neu an - ein Stempel oder ein Watermark. <RotatedBox turns> macht Quarter-Turns, die das Layout berücksichtigt: Eine Drehung um 90 oder 270 Grad tauscht Breite und Höhe, ein senkrechtes Label reserviert also genau den Streifen, den es einnimmt.

<Row gap={12}>
  <RotatedBox turns={3}>
    <Text bold>SECTION A</Text>
  </RotatedBox>
  <Rotated angle={-18}>
    <Box bg="#ffe9e9" border="#d33" padding={10}>
      <Text size={26} bold color="#d33">
        PAID
      </Text>
    </Box>
  </Rotated>
</Row>

Page Breaks

<PageBreak /> unterbricht den Flow: Alles danach beginnt auf einer neuen Seite. <KeepTogether> lässt seine Kinder nicht teilen und schiebt die ganze Gruppe auf die nächste Seite, wenn sie nicht passt. Beide gibt es auch als Props (breakBefore, breakAfter, keepTogether) an <Box>, <Column> und <Row>.

<Page>
  <Text size={18} bold>
    Terms
  </Text>
  <PageBreak />
  <Box keepTogether border="#d0d4da" radius={8} padding={14}>
    <Text bold>Signed</Text>
    <Text size={9}>This box is never torn across two pages.</Text>
  </Box>
</Page>

Seitenzahlen

<PageNumber> und <PageCount> drucken die aktuelle Seite und die Gesamtzahl des Dokuments; <SubPageNumber> und <SubPageCount> zählen innerhalb einer <Page>, für ein Dokument aus Abschnitten. Sie nehmen die Props von <Text> plus offset, das zur Zahl addiert wird (-1, wenn ein Deckblatt nicht mitzählen soll).

Für alles andere, was von der Seite abhängt, nimmt <PageBuilder> eine Funktion als Child und baut sie für jede Seite, auf der es landet. Geben Sie null zurück, um auf einer Seite nichts zu zeigen:

<Page
  header={
    <PageBuilder>
      {({ pageNumber }) =>
        pageNumber === 1 ? null : <Text size={9}>Annual Report, continued</Text>
      }
    </PageBuilder>
  }
  footer={
    <PageBuilder>
      {({ pageNumber, pageCount }) => (
        <Text size={9} align="center">
          Page {pageNumber} of {pageCount}
        </Text>
      )}
    </PageBuilder>
  }
>
  {/* body */}
</Page>

Die Funktion bekommt pageNumber, pageCount, pageSize, subPageNumber und subPageTotalPages. Was sie baut, wird als Ganzes auf ihre Seite gesetzt. Damit die Gesamtzahl stimmt, paginiert jasy das ganze Dokument, bevor es irgendetwas zeichnet.

Spalten

<SplitView> setzt seinen Inhalt in Zeitungsspalten, die auf der nächsten Seite weitergehen und sich auf der letzten ausgleichen. <ColumnBreak /> beginnt die nächste Spalte.

<Page gap={12}>
  <Text size={28} bold color="#1450aa">
    The Valley Courier
  </Text>
  <SplitView columns={3} colGap={16} rowGap={8}>
    {paragraphs.map((p) => (
      <Text key={p.id}>{p.text}</Text>
    ))}
  </SplitView>
  <Text italic>This line follows the columns, right where they end.</Text>
</Page>

Für ein eigenes Layout nimmt <Flow> es als Render-Prop page: eine Seite des Layouts, mit einer <Region /> überall dort, wo der Inhalt hin soll. Sie bekommt { index, pageNumber }, die erste Seite kann also anders aussehen als die übrigen.

<Flow
  gap={8}
  page={({ index }) => (
    <Column gap={12}>
      {index === 0 ? (
        <Text size={22} bold color="#1450aa">
          Annual Report
        </Text>
      ) : (
        <Text size={9} italic>
          Annual Report, continued
        </Text>
      )}
      <Row gap={16}>
        <Expanded>
          <Region />
        </Expanded>
        <Box bg="#f3f5f9" padding={8}>
          <Region width={150} />
        </Box>
      </Row>
    </Column>
  )}
>
  {report.map((p) => (
    <Text key={p.id}>{p.text}</Text>
  ))}
</Flow>

page und die Funktion eines PageBuilder werden an Ort und Stelle gerendert, in Ihrem Baum, während das PDF angeordnet wird - Hooks und Context funktionieren darin wie überall sonst. Die Optionen und die benannten Fehler stehen auf der Seite Spalten.

Formularfelder

Ein Formularfeld ist ein Layout-Element wie jedes andere: Es reserviert eine Box, fließt mit seinen Nachbarn und erzeugt ein Widget, in das man im Reader tippen kann. Sieben Arten:

KomponenteWofür
TextFieldeine Zeile oder, mit multiline, mehrere; password, maxLength
Checkboxein einzelnes Häkchen; onValue ist das, was gespeichert wird
RadioGroupeines von mehreren, für Sie angeordnet; options + value
Dropdowneine geschlossene Liste, oder editable für freien Text
ListBoxeine sichtbare Liste; multiSelect für mehrere gleichzeitig
PushButtoneine Aktion - "reset", { open: url } oder { submit: url }
SignatureFieldein leeres Feld, in dem der Leser unterschreibt

Jedes Feld nimmt name (der Schlüssel, unter dem der Wert gespeichert wird), required, hidden, print und die Appearance-Props border, background und color. Die einzelnen Widgets ergänzen tooltip, readOnly und borderWidth; die mit Box (TextField, Dropdown, ListBox, PushButton, SignatureField) nehmen width und height, eine Checkbox nimmt size. Eine RadioGroup besteht aus mehreren Buttons und hat deshalb keines dieser drei: Sie nimmt size, gap und die Label-Gestaltung ihrer Optionen. Das Label einer Checkbox, eines PushButton oder eines SignatureField sind seine Children:

<Column gap={10}>
  <TextField name="fullName" width={220} height={22} border="#aab3c2" background="#fbfcfe" />
  <RadioGroup
    name="plan"
    gap={6}
    options={[
      { value: "basic", label: "Basic" },
      { value: "pro", label: "Pro" },
    ]}
  />
  <Dropdown name="country" width={110} height={22} options={["DE", "AT", "CH"]} />
  <Checkbox name="agree">I have read the statutes</Checkbox>
  <PushButton name="clear" action="reset" width={92} height={26}>
    Reset form
  </PushButton>
</Column>

Es gibt kein onChange: Ein erzeugtes PDF schreibt nichts zurück, value ist also der Wert, mit dem das Feld beginnt. Ein ausgefülltes Formular zurückzulesen liegt im Core: Formulare.

Eine eigene Komponente

Ihre eigenen Komponenten sind React-Komponenten - setzen Sie sie aus den Komponenten oben zusammen, mit Props, Hooks und Context:

function Callout({ children }: { children: string }) {
  return (
    <Box bg="#eef1f5" border="#1450aa" radius={6} padding={12}>
      <Text color="#1450aa">{children}</Text>
    </Box>
  );
}