Skip to content

stet 0.8.7

Choose a tag to compare

@github-actions github-actions released this 15 Oct 03:29
· 48 commits to main since this release

Fixed

  • A fill whose path encloses no area is drawn as a thin line, at any
    angle.
    A path that runs out along a line and back along it — a bare
    moveto lineto fill, a rectangle of no height, a curve retraced —
    encloses nothing, and only the level and upright ones were drawn at
    all. PostScript and PDF paint the pixels such a path passes through
    (PLRM 7.5.1), and Ghostscript's zeroline.ps now shows its fans of
    lines. Each line is drawn once, as thin as a stroke of no width: the
    level and upright ones used to be drawn out and back, which made the
    rules of a PDF table drawn this way as heavy as its border. A path of
    several pieces has only its pieces of no area drawn as lines, lines
    laid over each other are drawn once, and the outline of a glyph is
    left alone. The lines now also overprint as the fill would have.
  • Where a path overlaps itself, its edge is no heavier than the edge
    of the shape.
    The fill rule was applied to the total of what every
    edge covered of a pixel, which is wrong wherever two edges share one.
    Under the even-odd rule a hole that shared an edge with its shape left
    a faint line along it — the stray line beside the L-shape in
    Ghostscript's zeroline.ps. Under the non-zero rule the shared edge
    came out fully dark where it should be partly covered, and the outline
    of a stroke overlaps itself at every join, so closely spaced strokes
    could merge at a corner and a thin rule drawn as overlapping pieces
    came out heavy. The rule is now applied to the edges in order, row by
    row. Most pages with strokes change by a few pixels, nearly always
    towards less ink.
  • Text in a multiple master font is drawn, where it came out as
    scribbles.
    The glyphs of such a font carry every master design, to be
    blended by the font's /WeightVector, and the PostScript interpreter
    never passed the weights on. Acrobat's substitute font AdobeSansMM is
    one, and pdftops writes it into the PostScript of any PDF that used
    it. PDF input was not affected. To hold the name it is looked up by,
    NameCache in stet_core::context has a new public field, so code
    that builds one with a struct literal no longer compiles. Reading its
    fields is unaffected.
  • Accented letters are drawn by every text operator. A Type 1 glyph
    made of a letter and an accent (seac) names its two parts, and only
    show and its nearest relatives looked them up: xshow, yshow and
    xyshow — which is what pdftops positions all its text with —
    glyphshow, charpath and the fonts of a composite font drew nothing
    for such a glyph, and a stringwidth beforehand left show with
    nothing to draw either.
  • PDF output embeds a multiple master font as one. It was written as
    an ordinary Type 1 font, so its text was scribbles or missing in every
    reader. The embedded font now carries its weights, its design axes and
    its masters' bounding boxes, which between them are what Ghostscript,
    poppler and stet need to blend it.
  • A PostScript job starts in DeviceGray. Every output device, and
    the library's own page setup, put the graphics state in DeviceRGB
    when the device was installed. So on the first page of a job, and on
    the first page after each setpagedevice, currentcolorspace gave
    DeviceRGB and 0.5 setcolor with no colour space set before it
    raised stackunderflow. showpage put DeviceGray back, so later
    pages were right. The initial colour space is now DeviceGray on
    every device, as PLRM Table 4.1 gives it and as Ghostscript has it.
  • A pattern tile larger than the page is drawn only where it shows.
    A tile that holds a shading, a transparency group or a soft mask is
    drawn once into a buffer of its own and stamped wherever it falls.
    PowerPoint writes a gradient fill as a single such tile several times
    the size of the slide, and every band of the page drew the whole of
    it. When the parts of the tiles that show come to less than one tile,
    each part is now drawn on its own. A slide of this kind at 150 dpi
    went from 5.6 s to 0.25 s on one thread, and on 24 threads from
    1.6 GB of memory to 49 MB; at 600 dpi from 20 s and 4.9 GB to 0.8 s
    and 250 MB. The tile's buffer also stopped at 8,192 pixels, so what a
    larger tile held beyond that was left unpainted; it is now drawn.
  • A soft mask's content is drawn only where the mask can show it, and
    a group with nothing to paint is skipped.
    InDesign gives a feathered
    or glowing object a form whose /BBox is far larger than the page, so
    the bounds it declares bound nothing, and it leaves behind groups that
    hold only a clip. A page of a hundred such objects drew a hundred
    gradients the size of the page, each to show a small part, and
    composited dozens of empty page-sized groups. The content is now kept
    to the mask's own extent wherever the mask is nought outside it, and
    an empty group costs nothing. One such page at 150 dpi went from 7.2 s
    to 0.95 s on one thread and from 1.4 s to 0.45 s on 24. Pixels at
    the very edge of a mask can differ slightly on a page drawn with
    overprint, where the hidden part of the content used to be recorded
    as if it had been painted.
  • Separation and DeviceN images convert faster. Every pixel of such
    an image went through its tint transform's table on its own: sixteen
    table entries blended for a four-colorant pixel. A one-colorant image
    now works out its 256 answers once, an image of up to four colorants
    keeps the answers for the sample values it has lately seen, and the
    table lookup itself passes over the entries that carry no weight. A
    page with a 3,806 × 5,349 four-colorant photograph at 150 dpi went
    from 3.2 s to 1.5 s on one thread. Output is unchanged.
  • A blend drawn as lines side by side is no longer striped. Older
    programs drew a blend as a few hundred stroked lines of slowly changing
    colour, each overlapping the last. Painted one after another with
    smooth edges, each line was laid over a pixel as though its neighbours
    might lie anywhere in it, so a pixel two lines covered between them
    still showed some of the page. Below about 600 dpi, where the lines
    are finer than the pixels, every row showed it by a different amount
    and the blend came out in light and dark stripes, lighter overall than
    its colours. A run of such lines — the same pen, nearly the same
    colour, each a step across from the last and no farther than its own
    width — now covers the page as its outline does. A line alone, and
    lines that are apart, transparent, or of unlike colours, are painted
    as before.
  • Where a gradient mesh patch folds over itself, the part on top is the
    one the spec puts there.
    A Coons or tensor-product patch (shading
    types 6 and 7) that curls back across itself shows the part with the
    larger v, and of parts with equal v the one with the larger u. Its
    control points are named with u first, so the first side of the data
    runs along v, and stet took that side for u: the folds of a patch were
    drawn the other way round, the back of a curled sheet in front of it.
    The order is now the spec's, decided pixel by pixel where a patch folds
    rather than a triangle at a time, so where the two layers lie close
    together the edge between them is a line, not a row of teeth. stet now
    draws these folds as Ghostscript and cairo do.
  • A gradient mesh has a smooth edge. A mesh shading — free-form,
    lattice, Coons or tensor-product (types 4 to 7) — coloured each pixel
    whose centre one of its triangles held, so its outline came out in
    steps where everything else on the page is smoothed. The pixels along
    its edge are now painted at the part of them the mesh covers, as a fill
    of its outline is, and the edge of a patch is its curved sides
    themselves rather than the chords its triangles are cut along. Where
    two meshes meet, as in a gradient drawn as many meshes side by side,
    both stay hard along the line they share: each covering part of the
    pixels there, painted one after the other they would leave the page
    showing between them. With --no-aa every mesh edge is hard, as
    before. A page of many patches takes about 5% longer on one thread.
  • Pages built from layers of gradients and transparency draw faster.
    Several costs came out of them, none changing a pixel. A radial
    gradient worked out a CMYK value through the colour profile for every
    pixel, though only a page drawn with overprint reads it. It searched
    its colour stops from the first for every pixel, where a gradient
    sampled from a function has dozens and neighbouring pixels fall between
    the same two. It solved its equation, a square root and four divisions,
    one pixel at a time; a row is now solved together, in the same
    arithmetic. A transparency group was blended back onto the page through
    the rasteriser's general pipeline, a stage at a time; it is now blended
    a row at a time in one loop, rows that are all transparent are passed
    over, and rows that are all opaque are copied. And content under a soft
    mask was laid over its backdrop one pixel at a time, which is now a row
    at a time as well. The row loops run eight numbers to an instruction on
    a processor with AVX2 and four elsewhere. A slide of ten full-page
    gradients at 300 dpi went from 1.5 s to 0.7 s on one thread, and an
    InDesign page of several hundred groups from 0.97 s to 0.69 s.
  • A function-based shading in a PDF is worked out only where it can
    show, and on several threads.
    Such a shading (type 1) is sampled
    into an image before the page is drawn, one sample per device pixel,
    and every sample runs its function. The whole of its domain was
    sampled, though a shading is drawn inside its clip and on the page. A
    colour wheel drawn as a large square cut to a ring sampled four
    million points to show a fifth of them. The samples now stop at the
    page and at every clip in force when the shading is drawn. That page
    went from 1.9 s to 0.4 s at 300 dpi on one thread, with every pixel
    unchanged. At 600 dpi it went from 2.6 s to 1.5 s, and it is sharper
    there: the image stopped at 2,048 samples across the whole domain, and
    that limit now applies to the part that shows. The samples are also
    worked out on several threads, under the new parallel feature of
    stet-pdf-reader, on by default: on 24 threads the colour wheel takes
    0.18 s, and a shading pattern that fills most of a page went from
    1.3 s to 0.55 s. On one thread nothing changes.
  • A function-based shading is cut to its BBox. The bounding box of
    a shading clips it wherever it is painted (PLRM 3, 4.9.3; ISO 32000-1,
    8.7.4.5.1), and both the PostScript and the PDF interpreter dropped it
    for a function-based shading (type 1), the one type that is sampled
    into an image: the whole domain was painted, with shfill and sh and
    in a shading pattern. The other shading types already kept to theirs.
    In a PDF nothing outside the box is sampled either.

Downloads

stet-<version>-x86_64-unknown-linux-musl.tar.gz is the one most people
want: statically linked, no glibc requirement, no runtime libraries, no GUI.
It is the right choice for servers, CI and containers.

The -gnu Linux build adds the desktop viewer and needs glibc 2.35 or newer
(Debian 12, Ubuntu 22.04 and later) plus X11 or Wayland and OpenGL to open a
window. Rendering to files works without them.

macOS: the binaries are unsigned, so opening a downloaded archive from
Finder trips Gatekeeper ("cannot be opened because the developer cannot be
verified"). Installing with curl avoids it entirely, because the quarantine
attribute is set by the downloading application and curl does not set one:

curl -fL <asset-url> | tar xz

If you already downloaded it in a browser, clear the flag with
xattr -dr com.apple.quarantine stet.

Windows: SmartScreen will warn that the publisher is unknown; choose
More info -> Run anyway. Code signing certificates cost money and stet does
not have one.

Rendering is identical across these artifacts with one caveat: the musl
build's libm differs from glibc's in the last bits, which can shift
antialiasing coverage by a pixel or two on heavily curved PostScript
artwork (measured: 265 of 5.4M pixels on one sample, invisible in practice;
all PDF samples tested were byte-identical). Do not generate rendering
baselines with one build and compare them against another.

Verify a download against SHA256SUMS.