Releases

Changelog

Every version of @ngpdf/core, newest first. Each one is published from GitHub Actions with npm provenance, and its GitHub release carries an SBOM.

1.0.1

Patch Changes

  • #300 ee94df9 Thanks @GabeSilvaDev! - The npm README now describes 1.0 instead of v0.1. It explains when a tall table row or row pdf-view continues onto another page and when it is clipped, and it links to the current roadmap.

1.0.0

Major Changes

  • #298 8863c68 Thanks @GabeSilvaDev! - First stable release. From 1.0 on, the public API follows semantic versioning: breaking changes to the elements, their inputs, the style properties, PdfRenderer and the server entry point only come in a new major version. Nothing breaks in this release by itself; it groups the v1.0 features listed below (pdf-svg, pages that grow with their content, row views that split across sheets, min-presence-ahead, every standard paper size and page sizes with units).

Minor Changes

  • #294 43c733e Thanks @GabeSilvaDev! - pdf-page takes auto as its height, for receipts and other slips with a fixed width: [size]="[226, 'auto']", ['80mm', 'auto'] or size="80mm auto". Each sheet is as tall as its content plus the page's padding and border, with bottom fixed views and the page number right after the content. A page break you ask for starts a new sheet sized to its own content. A sheet grows to at most 14400pt; taller content breaks onto more sheets, with a development-mode warning. auto as the width, or orientation="landscape" with an auto height, throws.

  • #290 a510921 Thanks @GabeSilvaDev! - New style property min-presence-ahead, to keep a section title from sitting alone at the bottom of a sheet. Set it to a length, for example min-presence-ahead: 80pt, and an element that would leave less than that much space below it on its sheet moves to the next one. The space is measured above the page number and fixed footers. An element already at the top of a sheet, the last one on a pdf-page, or one followed by a page break (break-after: page on it or break-before: page on the next element) stays where it is. It works on texts, images, views, tables and table rows (a value on a cell counts for its row); a header row ignores it. Elements joined to a moved element with break-after: avoid move with it. The default 0 changes nothing; a percentage, a keyword or a negative length throws.

  • #291 9a9b5e5 Thanks @GabeSilvaDev! - pdf-page size now takes lengths as well as points. Each side of [width, height] can be a number of points or a length string in pt, px, in, cm or mm, as in styles: [size]="['210mm', '297mm']". One number or length makes a square page ([size]="400", size="400", size="10cm"), and the CSS @page form with two lengths works too: size="210mm 297mm". %, em, auto and other units, a side that is zero, negative or not finite, and a string with more than two lengths fail with ngpdf: unknown page size "<size>" on pdf-page. A pair with more than two values, which was cut to its first two, now fails the same way. orientation="landscape" still always swaps width and height, for these sizes as for names.

  • #289 ef6cd86 Thanks @GabeSilvaDev! - pdf-page size now knows every standard paper size, so a label on A6, a book on B5 or an envelope on C5 no longer needs its points worked out by hand: ISO A0 to A10, B0 to B10, C0 to C10, 2A0 and 4A0, RA0 to RA4 and SRA0 to SRA4, the Japanese B series as JIS-B0 to JIS-B10, and the US sizes Executive, Folio, Tabloid, Ledger and ID1. All of them are portrait; orientation="landscape" turns them. Ledger follows CSS Paged Media and is the same 11 × 17 in sheet as Tabloid, so the traditional landscape ledger is size="Ledger" orientation="landscape". Size names now match in any case at run time, so size="a4" no longer throws; the PageSize type keeps the spellings from the docs table. The existing sizes keep the same points, and an unknown name still fails with ngpdf: unknown page size "<size>" on pdf-page.

  • #292 9121583 Thanks @GabeSilvaDev! - Add pdf-svg, which draws an SVG as vector shapes wherever a pdf-image can go. Its src takes SVG markup, a data:image/svg+xml URL, an http(s) URL (read like an image URL, with maxImageBytes and the server's fetchImage rule) or a Blob. It is sized like an image from the SVG's width, height and viewBox, honours preserveAspectRatio, and is clipped to its box. It draws svg, g, path (the whole d grammar, arcs included), rect, circle, ellipse, line, polyline, polygon, use within the same file, and linear and radial gradients, with fills, strokes, dashes, opacity, visibility, the six transform functions and currentColor, which is the element's own color. Text, images, clip paths, masks, filters, patterns, markers and <style> sheets are left out with one development-mode warning each. The parser is ngpdf's own, so it works the same on the server: it reads no DTD or external entities, runs no scripts, fetches no external references, and throws ngpdf: pdf-svg … past 1 MB of source, 50,000 elements, 64 levels of nesting, 16 levels of use or 1,000,000 path segments, gradient stops, dashes and attributes drawn. Malformed markup throws ngpdf: invalid SVG: <reason> at <offset>.

  • #295 405689e Thanks @GabeSilvaDev! - A row pdf-view (or block pdf-link) taller than a page now splits across sheets instead of being cut at the bottom, so a two-column layout with a long main column runs over as many pages as it needs. It splits the way a tall table row does: each child breaks on its own, a pdf-text between its lines and a column pdf-view between its children, and each piece is as tall as its tallest child piece. The row's background and border are sliced per sheet with no border drawn at a cut, and a child stretched to the row's height, such as a shaded sidebar, fills every piece. A row view that fits on a page still moves to the next one whole, so documents that already fit keep their pages. break-inside: avoid, a fixed height or max-height, a transform or an overflow other than visible keep the old behaviour: one unit, cut at the bottom with the dev-mode warning. A single line of a flex-wrap: wrap row that is taller than a page now splits the same way.

  • #296 277daef Thanks @GabeSilvaDev! - pdf-svg draws symbol, so icon sprites made of <symbol> and <use href="#id"> now show up instead of drawing nothing with a warning. A symbol is drawn only through a use, as a nested svg: its viewBox and preserveAspectRatio apply, it is placed at the use's x and y and clipped to the use's width and height, or else the symbol's own, or else 100% of the drawing around it, skipping a negative size. The limits on use count each symbol drawn: 16 levels deep, 50,000 elements and the drawing budget.

Patch Changes

  • #297 21566d8 Thanks @GabeSilvaDev! - A pdf-svg or pdf-canvas with a huge but finite number no longer crashes the render with pdfkit's bare unsupported number error. Every number a drawing writes now stays within ±1e9 (about 350 km of page). Past that, a pdf-svg fails with ngpdf: pdf-svg <rect> has the number 1e+22, past ±1e9, naming the element, and a pdf-canvas with ngpdf: pdf-canvas fill has the number …, past ±1e9, naming the paint call. This covers path coordinates, shape sizes, stroke widths, dashes, gradient coordinates, transforms and a viewBox fitted at a huge scale. Each number is checked in its own coordinates, so a large drawing scaled down onto a page still renders. The writer checks every drawing operation, page size and gradient again before pdfkit sees it.

0.9.0

Minor Changes

  • #281 e70c53f Thanks @GabeSilvaDev! - dashed and dotted borders are now drawn as dashes and dots instead of solid lines with a warning. Dashes are 3 times the border width long and dots are as wide as the border, about 2 widths apart, with the gaps fitted so each side starts and ends at its corners. A rounded box with four alike dashed or dotted sides is drawn as one outline, so the pattern runs on round the corners. border-style (1 to 4 values) and border-top-style, border-right-style, border-bottom-style and border-left-style set the style per side; before, they were ignored. A side whose style is none or hidden now has a width of 0 whatever order the declarations come in, so border: none; border-width: 2 no longer draws a border. double, groove, ridge, inset and outset are still drawn solid with a warning.

  • #275 0c7ad77 Thanks @GabeSilvaDev! - display: none now hides an element. It leaves the element and everything inside it out of the document: it takes no space, starts no new sheet and draws nothing, and the fixed views, absolute elements, notes, images and bookmarks inside it go with it. An anchor inside a hidden element does not exist, so a pdf-link to it gets the "names no anchor" warning in development mode and is drawn without a link. You can now write [style.display]="shown() ? null : 'none'" where you needed @if.

    display: flex is the default and still accepted. Any other value, such as block or grid, now throws ngpdf: invalid value "block" for display on pdf-view; use flex or none. It used to be ignored without a warning.

  • #287 286273d Thanks @GabeSilvaDev! - Rows can wrap. New flex-wrap (nowrap, wrap, wrap-reverse), the flex-flow shorthand (<direction> <wrap>, either part optional, the missing one reset to its default as in CSS) and align-content (stretch by default, flex-start, center, flex-end, space-between, space-around, space-evenly). These properties used to be ignored silently; a bad value now throws. Each line of a wrapping row is its own unit for pagination, so a long grid of cards or tags breaks between its lines and never through one, and the row gap before a line moved to a new page is dropped. align-content is not redone per page: lines keep their places from the single layout. A column with flex-wrap whose children went into several columns is kept whole. flex-wrap on a pdf-row inside a table is ignored, with the ngpdf: ignored inside tables: pdf-row flex-wrap development-mode warning.

  • #279 526e619 Thanks @GabeSilvaDev! - font-weight now takes any number from 1 to 1000 and the names thin (100), extralight (200), light (300), normal (400), medium (500), semibold (600), bold (700), extrabold (800) and black (900), and bolder and lighter follow the CSS table from the inherited weight. providePdfFonts takes a numeric weight per face, so a family registered with Light, Medium or Black faces can use them; 'normal' and 'bold' still work as 400 and 700. When a family has no face for a weight, ngpdf picks one as CSS font matching does, and the development warning names the face it drew with.

    Two things change for existing documents. An unknown font-weight value such as heavy used to draw normal text and now throws ngpdf: invalid value "heavy" for font-weight on <element>, with the allowed values in the message. The standard fonts are drawn bold above 500 rather than from 600, which only changes numbers between 501 and 599.

  • #282 dc17963 Thanks @GabeSilvaDev! - Text now sits in the middle of its line, as in CSS: the extra space that line-height adds is split in half above and below the text (half-leading), where it used to go all below it. Text in a box with a large line-height moves down; with the default line-height: 1.2, Helvetica moves down by about 0.14 of its font size. A line-height smaller than the font's height moves text up. Line heights, box sizes and page breaks do not change, a link's clickable area still covers the whole line, and underlines and strike-throughs follow the text. In a line with spans of several sizes, the span that reaches highest sets the baseline. The page number is placed the same way.

  • #285 a1e7688 Thanks @GabeSilvaDev! - Every style property that takes keywords now throws on one it doesn't know. Before, font-style drew an unknown value as normal, and break-before, break-after and break-inside ignored theirs; now they throw like the others. font-style takes normal, italic or oblique, break-before takes auto or page, break-after takes auto, page, avoid or avoid-page, and break-inside takes auto, avoid or avoid-page, in any case. So break-before: always, break-after: column, break-inside: avoid-column or font-style: oblique 10deg now fail the render. The message names the value, the property, the element and the keywords the property takes, for example ngpdf: invalid value "middle" for text-align on pdf-text; use left, center, right or justify, and properties that already threw (text-align, display, border-style, white-space, text-overflow, flex-wrap, align-content and the rest) now list their keywords the same way. flex-flow and vertical-align end their message with what they take, for example use baseline, super, sub, a length or a percentage. border-style errors used to read invalid border style "<value>". Unknown properties are still ignored.

  • #277 473b4a5 Thanks @GabeSilvaDev! - A pdf-view with fixed="top" or fixed="bottom" now takes its own width and places itself with auto left and right margins, so width: 200; margin: 0 auto centres a header and margin-left: auto pushes a footer to the right. A fixed view used to be laid out at the full content width less its margins whatever its width, so a document that sets width on one now draws it at that width. In the flow, auto margins already worked through Yoga, and the docs now say so: margin: 0 auto centres a box with a width, margin-left: auto pushes a row item to the end, and a box with margin-top: auto in a column split across sheets keeps the position it had in the unsplit layout.

  • #280 b40fdfd Thanks @GabeSilvaDev! - pdf-page takes a dpi input (72 by default) for documents made for print at a given resolution. An image on that page with no width and no height now takes 72 / dpi points per pixel, so a 600-pixel image on a dpi="300" page is 2in wide. This includes images in fixed views and pdfPerSheet content. Images with a size set keep it, px in styles stays 1pt, and the page keeps its size in points, so viewers show it at its real size. A dpi that is not a finite number above 0 throws ngpdf: invalid dpi "<value>" on pdf-page.

  • #276 b14035d Thanks @GabeSilvaDev! - New row-gap and column-gap properties, and gap now takes two values, gap: <row> <column>. One value still sets both. Gaps take any length unit ngpdf accepts or a percentage of the container's content box (its height for row-gap, its width for column-gap); gap: 10% used to throw. A negative gap throws. On a pdf-row inside a table only the column gap is ignored, and the development-mode warning now names pdf-row column-gap instead of pdf-row gap; a row-gap there no longer warns.

  • #286 d6dffdf Thanks @GabeSilvaDev! - pdf-text takes text-overflow: clip | ellipsis. With ellipsis and a fixed height or max-height on the same pdf-text, it keeps the lines that fit in its content box. When text was cut, the last kept line ends with …, cut the way max-lines cuts it. With max-lines too, the smaller number of lines wins. When not even one line fits, the first line is kept, ending with … when more text follows it. clip is the default and keeps the old behaviour. The property is not inherited, and an overflow: hidden parent's height does not count.

  • #283 109e6d0 Thanks @GabeSilvaDev! - A pdf-span now takes vertical-align: baseline | super | sub | <length> | <percentage> for superscripts and subscripts such as m², H₂O and footnote markers. super raises the span by 0.33em and sub lowers it by 0.2em of the font size around it, and both draw it at 70% of that size unless the span sets its own font-size. A length raises the text by that amount (negative lowers it) and a percentage is of the span's line height; neither changes the size. A nested span moves with the span around it. A line grows when a moved run sticks out of it, and the underline, strike and link area of the span move with its text. vertical-align used to be ignored everywhere; on an element other than a pdf-span it is still ignored, now with a development-mode warning, and a keyword such as middle throws.

  • #284 3638f15 Thanks @GabeSilvaDev! - New white-space property: normal (the default, unchanged), nowrap, pre, pre-line and pre-wrap, inherited and allowed on pdf-span, as in CSS. With pre-line, an address or note bound from data keeps its line breaks: <pdf-text style="white-space: pre-line">{{ address }}</pdf-text>. pre and pre-wrap also keep spaces, and tabs go to stops every 8 spaces. nowrap and pre do not wrap, and a line wider than its box overflows it. white-space was ignored before.

Patch Changes

  • #288 fa88de8 Thanks @GabeSilvaDev! - In a pdf-table with flex-direction: row, where the header rows sit beside the body rows, a fixed row-gap or gap no longer pushes a body row below its header. A row split across sheets now starts each piece beside the repeated header, at the header's top, as a percentage row-gap already did. When the header is taller than the room left on a sheet, the rows beside it move to the next sheet with it, so the header is not cut at the bottom.

0.8.0

Minor Changes

  • #273 654893f Thanks @GabeSilvaDev! - Adds VERSION, the version of @ngpdf/core your app runs, as an Angular Version with full, major, minor and patch (the same shape as VERSION from @angular/core). Import it from @ngpdf/core to put the version in bug reports, support tools or your own producer. The version isn't added to the default producer, which stays ngpdf, so upgrading doesn't change the bytes of a PDF rendered with a fixed creationDate.

  • #269 6674be4 Thanks @GabeSilvaDev! - Adds maxImageBytes, a render option next to inputs that caps the size of images fetched from a URL. A response whose Content-Length is over the cap is refused before its body is read; otherwise the download stops as soon as the bytes read pass the cap, and the render rejects with ngpdf: could not load image "<url>": larger than N bytes. PdfRenderer, renderToBlob, downloadPdf, pdfResource and renderToBuffer take it, PdfDownload takes pdfMaxImageBytes and pdf-viewer takes maxImageBytes. On the server it checks what fetchImage returns. Data URLs and bytes are not counted. There is no cap by default.

  • #268 753b761 Thanks @GabeSilvaDev! - Adds pdfPerSheet, for content in a fixed header or footer that changes from sheet to sheet, such as "Continued on next page" on every sheet but the last and "End of report" on the last. Put <ng-template pdfPerSheet let-sheet let-total="total"> inside a pdf-view with fixed="top" or fixed="bottom"; the template also gets first, last and page, the pdf-page the sheet comes from. It comes with PDF_ELEMENTS. The template renders like the rest of the document, with pipes, inputs and services, once per sheet. The view keeps the height of the template's content for a one-sheet document; content taller than that on a later sheet is cut and warns in development mode, so give the view a height when its content varies. A pdfPerSheet template anywhere but inside a fixed view fails the render.

Patch Changes

  • #259 d0e857f Thanks @GabeSilvaDev! - Absolute elements placed below the sheet that ends a page's content no longer get a sheet each. The first one starts a new sheet at its top, and the others land on that sheet, moved with it, until one's top falls below it and starts the next. Two badges with the same top now share one sheet instead of making two, and two elements one content height apart still land on two sheets.

  • #272 ac7ce22 Thanks @GabeSilvaDev! - A horizontal border line that crosses the bottom of the page in a unit taller than a page is now cut there. Before, its stroke could reach up to half its width below the content area, and a top border that started just above the cut was dropped.

  • #263 906c78c Thanks @GabeSilvaDev! - An accented letter written as a base letter plus a combining mark, such as e followed by U+0301 (as macOS file names and some APIs send it), is now drawn as the composed letter (é) in the standard fonts instead of the letter followed by ?, and no longer triggers the WinAnsi warning. In a font-family list, a font that lacks the letter as written but has its composed form draws the composed form. A custom font that has the combining mark draws the text as before.

  • #257 9308189 Thanks @GabeSilvaDev! - A broken or cut-off PNG in a pdf-image now rejects the render with ngpdf: could not load image "<src>": the image data is corrupt or cut off. Before, a PNG with transparency whose pixel data was damaged made the render hang with an uncaught error. A damaged emoji image is drawn as text instead.

  • #264 1eac6b4 Thanks @GabeSilvaDev! - A line cut by max-lines no longer ends in empty boxes when its last characters come from a font that has no …. The ellipsis is now drawn in the first font of the font-family list that has …, or ... in the first one with . when none has …. A custom font that has … keeps drawing it itself, as before.

  • #258 a7fc03d Thanks @GabeSilvaDev! - Units that fill the content area exactly now stay on the sheet. Yoga's float32 rounding could put their bottom slightly past the page, by more on tall pages and with many units, which moved the last one onto an extra sheet. A unit may now end up to 1/131072 of the content height past the bottom (0.001pt at least). On every named page size a unit that is over by 0.01pt or more still moves.

    A table header that can't go on a page with the table's first row, because that row fits a page only without it and can't split, is now drawn once as ordinary rows, with a warning in development mode. It used to be left off that row's page, so a table with no row that fits under it lost its header entirely.

    A table row that fits a page only without its header no longer has a piece cut at the bottom when no break in it fits below the header. It used to be split anyway, with the first piece drawn past the bottom of the sheet; it now goes whole on a sheet of its own, without the header and with a warning in development mode, and a header that can't go on a page with such a first row is drawn once, as above.

  • #262 f7c7156 Thanks @GabeSilvaDev! - Styles that take a length, a number or an angle now accept exponent form, such as 1e-7, 1.5E3 or 1.2e-15pt. Numeric bindings write very small and very large values this way, so [style.left.pt]="Math.sin(Math.PI) * 10" or [style.opacity]="1e-7" now render instead of throwing. An e with no digit after it is still part of the unit, so 3em still throws unknown unit. A length or angle that is not finite, either as written (1e999) or once its unit is applied (1e308in, rotate(1e308turn)), throws invalid length or invalid angle. A huge but finite gradient angle, such as linear-gradient(1e308deg, red, blue), now renders in the direction of that angle modulo 360.

  • #265 af0e7c9 Thanks @GabeSilvaDev! - A JPEG with a stray byte between two segments now loads at its frame size, as it does in a browser. It used to fail the render with ngpdf: invalid JPEG marker.

  • #261 a375650 Thanks @GabeSilvaDev! - A longhand such as margin-top now overrides an earlier shorthand such as margin, as in a browser. Before, margin-top: 20; margin: 0; margin-top: 5 gave a top margin of 0, and a [style.margin-top] binding lost to a margin in the static style.

  • #260 2d5c378 Thanks @GabeSilvaDev! - break-before: page on a repeated table header row, or on an element inside it, now starts a new page before the table's first row instead of being ignored. A table header that is not repeated because its rows are taller than a page together, or that is left off a page whose first row fits only without it, now warns in development mode.

0.7.0

Minor Changes

  • #149 20d2466 Thanks @GabeSilvaDev! - Support position: relative and position: absolute with top, right, bottom and left, as in CSS. A relative element is drawn moved by its offsets, and the layout around it does not change. An absolute element leaves the flow and is placed against the padding box of its nearest relative, absolute or transformed ancestor, or against the page's content area. With left and right both set and width: auto it stretches between them, and the same goes for the height. Positioned elements are drawn on top of the in-flow ones. An absolute element is never a pagination unit and is never split: it is drawn with its containing block when that is one unit, and otherwise on the sheet where its top lands. position: fixed and sticky throw; use the fixed input of pdf-view for a header or footer on every sheet. position and the offsets are ignored on pdf-page, pdf-row and pdf-cell. Before, all five properties were ignored.

  • #151 0a14e4a Thanks @GabeSilvaDev! - Adds links to places inside the document, and bookmarks. Give a pdf-view, pdf-text, pdf-image, pdf-table, pdf-row or pdf-cell an anchor name, and <pdf-link href="#name"> jumps to it, for example from a table of contents. A bookmark title on the same elements adds an entry to the PDF's bookmarks panel, nested as the elements are nested in the template. pageMode now also takes UseOutlines, which opens that panel when the PDF opens. A link to a name that no element has, or a second element with the same name, draws the content as usual and warns in development mode.

  • #142 254ecc9 Thanks @GabeSilvaDev! - break-after: avoid (or avoid-page) keeps an element on the same sheet as what follows it, so a heading no longer ends a sheet alone in most cases. When the next unit does not fit, the element moves to the next sheet with it. For a split text, only its first lines need to fit. Several elements in a row with avoid move together. An element already at the top of a sheet stays there, and a group that would not fit on an empty sheet is left where it is, so in those cases a heading can still end a sheet.

  • #156 ea8bb74 Thanks @GabeSilvaDev! - Adds pdf-canvas, for charts, signature lines and other shapes. Put <pdf-canvas [paint]="draw" style="height: 120" /> where a pdf-view can go, and draw in draw(ctx, size) with lines, curves, rectangles, circles, ellipses and polygons, filled or stroked, with dashes, line caps and joins, colors, opacity and transforms. Coordinates are in points from the top left of the canvas's content box, and the drawing is clipped to it. The function runs once per render, after layout, with the canvas's final size, and a canvas in a fixed header or a repeated table header is drawn on every sheet. A canvas is never split across pages. A non-finite number, an unknown option, too many calls, an error thrown by the function or a function that returns a promise rejects the render with an ngpdf: pdf-canvas error. The PdfCanvasContext and PdfCanvasPaint types are exported.

  • #157 b44817d Thanks @GabeSilvaDev! - Adds a debug input on pdf-document that draws layout outlines on every page, to find out why a box is not where you expect it. Each box gets a red outline around its border box, a blue dashed outline around its content box when it has padding or a border, and a small grey label with its tag and size in points. The page's content area gets a green dashed outline, and each line of a pdf-text a thin grey one. The outlines are drawn on top of everything, follow transforms and are not cut by overflow: hidden. Turning debug on never changes the layout or where pages break. Each render with it on prints the dev-mode warning ngpdf: debug outlines are on, so a debug PDF is not shipped by mistake.

  • #161 9a54c47 Thanks @GabeSilvaDev! - Adds providePdfEmoji, which draws emoji in color as small inline images. Give it a URL template such as '/emoji/{codepoints}.png', where {codepoints} becomes the emoji's code points in lower-case hex joined by -, without U+FE0F (1f44d-1f3fd for 👍🏽), or a function that returns a PNG or JPEG for each emoji, or null to leave it as text. Each emoji is 1 em square at the text's size, sits on the line like the letters around it, breaks lines as one character, takes letter-spacing and underlines, and works with max-lines and justified text. ZWJ sequences, skin tones, flags and keycaps are drawn as one image. ©, ®, ™ and digits stay text on their own: they become emoji with U+FE0F after them, digits also in a keycap (with or without U+FE0F), and pictographic characters also in a ZWJ sequence or with a skin tone. Each distinct emoji is loaded once per render, and at most 500. An emoji whose image is missing, fails to load or takes more than 10 seconds is drawn as text with a warning in development mode, so it never fails the render. ngpdf ships no emoji images and fetches nothing unless you add the provider. An emoji image is not text, so it cannot be copied or searched, and color emoji fonts are still not supported.

  • #150 44956d8 Thanks @GabeSilvaDev! - Adds a security input on pdf-document to protect a PDF with a password and restrict what a viewer lets the reader do. userPassword is asked for when the file is opened, ownerPassword lifts the restrictions, and permissions limits printing to low resolution ('low') or turns it off ('none'), and turns off copying, modifying, annotating, filling forms, content extraction for accessibility or page assembly. Everything is allowed unless you turn it off. The file is encrypted with AES-256, with no new dependency. Restricting permissions without an owner password uses a random one, so the restrictions cannot be lifted. Permissions are only a request to the viewer and do not protect the content; the user password does. A password written in your app's code is visible to every user, so pass one the user typed or your server sent. A password longer than 127 bytes in UTF-8, as typed or after Unicode NFKC normalization, or a value of the wrong type, throws ngpdf: invalid security <field>.

  • #146 b9e379b Thanks @GabeSilvaDev! - Backgrounds can be gradients. background-image takes one linear-gradient() or radial-gradient(), and background becomes a shorthand for a color, a gradient, or both, with the color drawn under the gradient. Linear gradients take an angle (deg, rad, turn) or to a side or corner; radial ones take circle or ellipse, a size keyword and at <position>. Stops take any supported color and an optional position in % or a length, spread and ordered as in CSS. The gradient fills the border box, follows border-radius, and a view split across pages keeps one gradient for its whole height. repeating-* gradients, conic-gradient(), url() and several backgrounds throw an ngpdf: error.

  • #158 e122b9e Thanks @GabeSilvaDev! - opacity now fades an element and everything inside it as one picture, as in CSS. Before, each piece inside a faded view was faded on its own, so overlapping children showed through each other and a background showed through the text and borders drawn over it. Now a faded card looks like the same card, solid, made see-through as a whole. Content that doesn't overlap looks the same as before. A faded view inside another faded view still composes (0.5 inside 0.5 is 0.25 over the page), a color's own alpha still applies, and opacity: 0 hides the content while its links keep working. Each faded element adds a small object to the PDF; fonts and images inside are not embedded twice.

  • #153 5ed8a06 Thanks @GabeSilvaDev! - Adds hyphenation with the CSS hyphens property: manual (the default), none or auto. It is inherited, and a pdf-span can set it. A soft hyphen (&shy;) in your text now marks a place where a long word may break, with a hyphen drawn at the end of the line; before, soft hyphens were dropped and words never broke there. Where the line does not break, a soft hyphen still draws nothing. With hyphens: auto, words also break where your own hyphenation function allows: register it with providePdfHyphenation, for example on top of a pattern library you load lazily. ngpdf ships no hyphenation patterns.

  • #148 bbf2e24 Thanks @GabeSilvaDev! - Adds pdf-link, which makes text or a box open a URL when a reader clicks it in a PDF viewer. Put <pdf-link [href]="url"> around a block, or use it inside a pdf-text for inline text. Only absolute http:, https: and mailto: URLs become links. Any other URL, a relative or invalid one, or a missing one draws the content without a link and warns in development mode. A link does not change how the content looks. A pdf-link inside another pdf-link throws an error.

  • #155 3d7e4c3 Thanks @GabeSilvaDev! - Adds ng add @ngpdf/core, so you can start with one command instead of setting things up by hand. It installs the package and writes an example document to your application, ready to render. With --demo-route you also get a /pdf-demo page with a Download PDF button, so you can see a PDF in the browser right away. --project picks the application and --path picks the folder. Your existing files are never overwritten.

  • #154 5ee6aa9 Thanks @GabeSilvaDev! - Adds pdf-note, a comment on the page that PDF viewers show as an icon opening a popup, like a sticky note. Put it where a pdf-view can go, with the note's text as its content: <pdf-note author="Ana" icon="help">Is this before tax?</pdf-note>. author is shown as the note's title, icon picks one of the standard icons (comment by default, note, help, key, insert, paragraph, new-paragraph), and open shows the popup when the file opens. The icon is sticky-note yellow unless the note sets its own color. A note takes no space in the layout and is not printed. Its icon sits where the next element starts, or anywhere with position: absolute. A note in the flow lands on the same sheet as the content next to it; an absolute note follows the rules of position: absolute. The text can hold any character, also ones the page fonts can't draw. A note takes plain text only, and a note inside a pdf-text fails the render.

  • #143 7c94088 Thanks @GabeSilvaDev! - Support overflow: hidden. An element's children, text and image are clipped to its padding box, with rounded corners when it has border-radius, so a card can crop an object-fit: cover image or long text. Its own background and border are not clipped. clip, scroll and auto clip like hidden, and the layout does not change. A column pdf-view or a pdf-text that clips, with any of the four keywords, is one pagination unit and is never split across pages. One taller than a page is cut at the bottom of that page, and the rest of it is not drawn on the next one. overflow is ignored on pdf-page and pdf-table. Before, overflow was ignored everywhere.

  • #152 e11393f Thanks @GabeSilvaDev! - Adds pdf-viewer, a live preview of a document, from the new entry point @ngpdf/core/viewer. <pdf-viewer [document]="Invoice" [inputs]="{ invoice }" /> shows the PDF in the browser's own PDF viewer and renders it again, after a 300 ms pause by default, when inputs change. The previous PDF stays on screen while the next one renders. Content marked pdfViewerLoading shows until the first PDF is there, and content marked pdfViewerError shows when a render fails; rendered and failed emit the PDF and the error. Apps that don't import @ngpdf/core/viewer don't bundle it. Chrome on Android shows no PDF inside a page and Safari on iOS shows only the first page, so offer a download next to the preview. If your Content-Security-Policy sets frame-src, allow blob:.

    pdfResource's url() now keeps the previous PDF's URL while a newer render runs, instead of going undefined until it finishes.

  • #160 627d262 Thanks @GabeSilvaDev! - Adds server rendering. renderToBuffer from the new entry point @ngpdf/core/server renders a document to PDF bytes in Node, with no DOM: in a plain Node script, or in an Angular SSR route, where you can pass the app's injector so the document uses its services. It takes the same inputs as renderToBlob, plus providers such as providePdfFonts, and returns a Uint8Array you can send with res.end() or write to a file. Each call is isolated, so renders can run at the same time. renderToBuffer fetches nothing by itself: an http: or https: image or font URL is read only through the fetchImage function you pass, and refused without it. Restrict that function to your own hosts; a URL from user data then cannot make your server request internal addresses. Relative URLs and other schemes, such as file:, are refused on the server and never reach fetchImage. Data URLs and bytes work as before. On the server, downloadPdf and pdfResource given an injector now throw ngpdf: … needs a browser instead of failing later. In an SSR page, pdf-viewer shows its loading content and a pdfResource made in a component stays idle; the browser renders the PDF.

    pdf-image now also takes a Uint8Array as src, so a Node Buffer from readFile works as it is.

  • #159 2dcf3e7 Thanks @GabeSilvaDev! - A table row taller than a page now splits across pages instead of being cut off at the bottom. Each cell breaks on its own, between the lines of its text or between the elements of a column pdf-view inside it, and the header row repeats above every piece. The cells of every piece are as tall as each other, so their borders line up, and backgrounds and borders are sliced with no border drawn at a cut. Content that can't break, such as an image, moves whole to the next page, or is cut at the bottom when it is taller than a page. A split row draws its cells from the top, so vertical alignment inside it is ignored. Rows that fit on a page are placed as before. break-inside: avoid on a row keeps the old behaviour: the row is cut at the bottom of its page, with the development-mode warning.

  • #145 168e1fb Thanks @GabeSilvaDev! - Elements can now be rotated, scaled and moved with transform and transform-origin, as in CSS, without changing the layout around them. A transformed column pdf-view or pdf-text is kept whole on one sheet, and one taller than the sheet's content area is cut at the bottom of that area, with a warning in development mode. transform has no effect on pdf-span, pdf-page or pdf-table, and skew(), matrix() and 3D functions throw. See Transforms for the full syntax.

Patch Changes

  • #254 186c9c5 Thanks @GabeSilvaDev! - A style property that only contains a side name, such as margin-top-x or padding-leftover, is now ignored like any other unknown property instead of setting that side.

  • #244 8c42ff3 Thanks @GabeSilvaDev! - line-height: normal is now matched in any case, like every other keyword: NORMAL or Normal keeps the inherited line height instead of failing the render.

0.6.0

Minor Changes

  • #139 376630b Thanks @GabeSilvaDev! - letter-spacing and text-transform are supported and inherited like the other text styles, also on a pdf-span. letter-spacing adds space after each character and counts in wrapping, alignment and max-lines. text-transform takes uppercase, lowercase or capitalize and is applied before the text is measured.

  • #140 4d6e05d Thanks @GabeSilvaDev! - Borders now follow border-radius. border-radius takes one to four lengths in CSS corner order, and the border-top-left-radius, border-top-right-radius, border-bottom-right-radius and border-bottom-left-radius longhands set one corner each. Radii too big for their box are scaled down together, as in CSS, and a view split across pages keeps square corners where the page cuts it. An elliptical radius (4 / 8), a percentage or a negative radius throws. Before, border-radius took one length and rounded only the background, so square border lines stuck out of it.

0.5.0

Minor Changes

  • #133 76517d0 Thanks @GabeSilvaDev! - breakAfter on pdf-view and pdf-text, and the style break-after: page on any element but a pdf-span, start a new sheet after that element. Like breakBefore, it applies to a whole row when set inside one, and it never adds a blank sheet at the end of a page.

    break-inside: avoid now works on a column pdf-view and a pdf-table too. One that doesn't fit in the space left on a sheet but fits on a whole sheet moves to the next sheet in one piece. One taller than a sheet splits as before.

Patch Changes

  • #132 0a5bad9 Thanks @GabeSilvaDev! - Next line (U+0085) is now drawn as a normal space and collapses with the whitespace around it, like form feed, vertical tab and the line and paragraph separators. Before, a standard font drew it as ?.

  • #134 2d90fd2 Thanks @GabeSilvaDev! - The style attribute is recognised in any case, as in HTML: [attr.STYLE] or a directive that sets Style now styles the element instead of being ignored.

  • #130 08afbf5 Thanks @GabeSilvaDev! - A pdf-text split over several pages no longer leaves a page in the middle with fewer lines than orphans and widows ask for when its lines have very different heights. A page gives lines back to the one before it, or the text starts on the next page, when that keeps the limits. When no layout keeps them, lines are placed one by one as before.

  • #129 dc34713 Thanks @GabeSilvaDev! - Vertical tab, form feed and the line and paragraph separators (U+2028, U+2029) are now drawn as a normal space, and U+FEFF is removed. A standard font draws the ideographic space (U+3000) and the Ogham space mark (U+1680) as a space too, while a custom font that has them still draws its own glyph. Before, a standard font drew all of these as ? in the middle of a line, while the same characters at the ends of a paragraph were trimmed as whitespace.

0.4.0

Minor Changes

  • #119 9844b8c Thanks @GabeSilvaDev! - Add 'auto' table columns: [columns]="['auto', '1fr', 'auto']" sizes a column to the widest content of its cells on one line, so codes, dates and totals fit without wrapping. auto columns come after fixed and percentage columns and before fr ones, and shrink in proportion when they do not fit.

  • #111 b2ac328 Thanks @Aaqibhafeezkhan! - Add optional document language, application attribution and initial page-view preferences to generated PDFs. Language tags and viewer preferences are validated, and the PDF producer metadata now identifies ngpdf instead of PDFKit.

  • #122 59006e9 Thanks @GabeSilvaDev! - Add max-lines to cut a pdf-text to a number of lines. When text was cut, the last kept line ends with …, shortened until it fits the box, and the text is only as tall as the lines it keeps. A font without … gets ... instead.

  • #117 353ff5e Thanks @GabeSilvaDev! - pdf-page-number now works anywhere in a page, not only as a direct child of pdf-page. Put it in a fixed header to show "Page 2 of 5" on every sheet, or anywhere in the content to show the number of the sheet it lands on. A direct child of pdf-page still reserves the bottom of every sheet as before.

  • #121 6fb4354 Thanks @GabeSilvaDev! - Add text-align: justify. Every line of a paragraph but the last is stretched to both edges of its box by widening the gaps between words, across pdf-spans and across a page break. The last line, a line with one word and a word wider than the box stay at the start.

Patch Changes

  • #58 ced90e0 Thanks @arxigen! - Style keywords are matched case-insensitively and trimmed, as in CSS. text-align: CENTER no longer throws, and font-weight: BOLD and font-style: ITALIC are no longer dropped silently. This covers text-align, text-decoration, flex-direction, justify-content, align-items, align-self, object-fit, font-weight, font-style and break-before. Unknown keywords behave as before: they throw for text-align, text-decoration, flex-direction, justify-content, align-items, align-self and object-fit, fall back to normal for font-weight and font-style, and are ignored for break-before.

  • #63 df2b007 Thanks @arxigen! - ngpdf now uses pdfkit 0.20. pdfkit's ES module browser build replaces its old standalone bundle, so the combined PDF engine payload loaded on the first render drops from about 1.5 MB to about 610 KB before compression, and from about 433 KB to about 238 KB gzipped. The initial bundle is unchanged.

    The text and layout of the PDFs you get are unchanged. Their bytes are not: pdfkit 0.20 compresses streams with a different library and writes /UserUnit 1 on every page.

    A JPEG's EXIF orientation is now read the way pdfkit 0.20 reads it: from the first Exif segment before the image data, even when an XMP segment or a comment comes first. The image's laid-out size keeps matching how pdfkit turns it.

    In an Angular CLI app, pdfkit no longer needs to be listed under allowedCommonJsDependencies. Angular may now name fontkit's CommonJS dependencies instead (brotli, clone, dfa, fast-deep-equal and tiny-inflate).

  • #60 82788a8 Thanks @arxigen! - Large images given as percent-encoded data URLs load faster. The decoded image is unchanged.

  • #56 af957f2 Thanks @jay23606! - Support thin, medium and thick border widths, and reject negative border widths.

0.3.0

Minor Changes

  • #116 1c7adcf Thanks @GabeSilvaDev! - Add fixed headers and footers: a pdf-view with fixed="top" or fixed="bottom", placed directly inside a pdf-page, is drawn on every sheet of that page, and the content flows in the space between them. A footer sits above the pdf-page-number.

  • #112 2e89379 Thanks @GabeSilvaDev! - Add the pdf-span element to style part of a text, such as one bold word in a sentence: <pdf-text>Total: <pdf-span style="font-weight: bold">{{ total }}</pdf-span></pdf-text>. A span can change the color, font family, size, weight, style and decoration; the paragraph still wraps as one text, and runs of different sizes share a baseline. Characters drawn in a fallback font now share the baseline of the text around them instead of its top.

  • #114 646d783 Thanks @GabeSilvaDev! - A long pdf-text now breaks between its lines at a page break instead of moving to the next page whole, or being clipped when taller than a page. orphans and widows (default 2) set the fewest lines kept before and after the break. A text keeps the old behaviour, moving whole, when it has break-inside: avoid, a fixed height or a max-height, or when it sits inside a table row or a row pdf-view.

0.2.0

Minor Changes

  • #110 b2762ed Thanks @GabeSilvaDev! - Add custom fonts: register TTF, OTF, WOFF or WOFF2 files with providePdfFonts and name them in font-family. Text in them is not limited to WinAnsi, and each character falls back along the font-family list to the first font that has it. Only the fonts a PDF uses are embedded, as subsets. Documents that name no registered family render exactly as before.

  • #108 318a305 Thanks @GabeSilvaDev! - Add the pdfDownload directive: <button [pdfDownload]="Invoice" pdfFileName="invoice.pdf"> downloads the document when clicked, marks the button busy and ignores clicks while it renders, and reports a failed render through (pdfError) and the app's ErrorHandler.

  • #107 1c4d22a Thanks @GabeSilvaDev! - Add pdfResource to keep a PDF Blob and object URL current as document inputs change. It also exposes loading and error state, revokes replaced URLs, and can debounce bursts of changes.

Patch Changes

  • #59 17706f7 Thanks @arxigen! - A pdf-image with element children now rejects the render with ngpdf: pdf-image cannot have children (found <pdf-text>), naming the first child, before any image is fetched. The error identifies the element you need to remove.

  • #64 7ac8749 Thanks @arxigen! - A ; inside quotes or parentheses in a style attribute no longer ends the declaration, so values such as url(data:image/png;base64,...) or font-family: "A;B" are kept whole.

  • #71 f6cbe03 Thanks @arxigen! - Text is measured once per render instead of again for every layout pass, so documents with a lot of text lay out faster. The output is unchanged.

  • #55 fc028e9 Thanks @arxigen! - An image src written as an upper- or mixed-case data URL (DATA:image/png;base64,...) is now decoded instead of being fetched over the network, as URL schemes are case-insensitive.

0.1.3

Patch Changes

  • #34 3da43bb Thanks @GabeSilvaDev! - break-before: page on an element inside a table row or a row pdf-view, such as a pdf-cell, now starts a new page before that whole row. A breakBefore input on an inner pdf-view or pdf-text does the same. These breaks did nothing before.

  • #37 0326e89 Thanks @GabeSilvaDev! - A JPEG with an EXIF orientation from 5 to 8, as portrait phone photos usually have, is now laid out at the size it is displayed at, with width and height swapped, so it keeps its aspect ratio instead of being squeezed into the box of the unrotated picture.

  • #35 ef0e6ad Thanks @GabeSilvaDev! - Text from the date, currency and number pipes no longer shows ? where the locale uses a thin or narrow no-break space (3:00 PM in en-US, 1 234,50 € in fr): the Unicode spaces U+2000–U+200A and U+205F are drawn as a normal space, and the no-break ones (U+2007, U+202F) as a no-break space, so 3:00 PM stays on one line. A soft hyphen (U+00AD) is no longer drawn as a hyphen in the middle of a word, and a zero-width space (U+200B) is no longer drawn as ?; both are removed.

  • #39 a707598 Thanks @GabeSilvaDev! - A document nested exactly 64 elements deep now renders when its innermost element holds text or an @if/@for block. The limit counted that text as one more level and rejected the document with ngpdf: nesting deeper than 64, which made the real limit 63 for almost every template.

  • #36 11296e1 Thanks @GabeSilvaDev! - A render that fails no longer leaves things behind. If the document component, or a component inside it, throws in its constructor, the services and callbacks of that render's injector are now cleaned up before the error reaches you; before, they stayed alive. Callbacks that a component registered with inject(DestroyRef) before the throw still do not run, because Angular does not hand back the partly created component. download and downloadPdf now remove their temporary link and free the PDF's memory even when the click throws.

  • #33 cb6a54b Thanks @GabeSilvaDev! - padding: auto, and auto on a padding-* side, now throw an ngpdf: error instead of becoming 0, since CSS has no auto padding. A negative font-size or padding now throws as well; negative margins still work, as in CSS. gap on a pdf-row inside a table is now named in the ngpdf: ignored inside tables dev-mode warning.

0.1.2

Patch Changes

  • #10 b30c004 Thanks @GabeSilvaDev! - Rendering a PDF works in an app that hydrated from server-rendered HTML (provideClientHydration() with SSR or prerendering), where it threw TypeError: rNode.getAttribute is not a function.

0.1.1

Patch Changes

  • #7 3c457e9 Thanks @GabeSilvaDev! - rgb() and rgba() now throw an ngpdf: invalid color error on hexadecimal, binary or octal channel values such as 0x10, 0b1 and 0o7, which CSS does not accept. Documents with deeply nested component hosts now render without a stack overflow. Styles on the host of the document component you render do not affect the PDF: style its pdf-document or pdf-page instead.

0.1.0

Minor Changes

  • ba61563: First release. Write a PDF as an Angular component: a document is a normal component whose template uses the pdf-document, pdf-page, pdf-view, pdf-text, pdf-image, pdf-table, pdf-row, pdf-cell and pdf-page-number elements. A lazily loaded engine lays it out with Yoga, breaks it into pages and writes it with pdfkit, all in the browser. Importing @ngpdf/core adds at most 15 KB gzipped to the initial bundle.