Repository navigation
stet 0.8.7
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'szeroline.psnow 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'szeroline.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, andpdftopswrites it into the PostScript of any PDF that used
it. PDF input was not affected. To hold the name it is looked up by,
NameCacheinstet_core::contexthas 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
showand its nearest relatives looked them up:xshow,yshowand
xyshow— which is whatpdftopspositions all its text with —
glyphshow,charpathand the fonts of a composite font drew nothing
for such a glyph, and astringwidthbeforehand leftshowwith
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 inDeviceRGB
when the device was installed. So on the first page of a job, and on
the first page after eachsetpagedevice,currentcolorspacegave
DeviceRGBand0.5 setcolorwith no colour space set before it
raisedstackunderflow.showpageputDeviceGrayback, so later
pages were right. The initial colour space is nowDeviceGrayon
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/BBoxis 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-aaevery 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 newparallelfeature 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, withshfillandshand
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.