Skip to content

stet 0.5.0 — 64-bit integers, Type 3 font fixes, --page

Choose a tag to compare

@AndyCappDev AndyCappDev released this 25 Aug 23:25
· 55 commits to main since this release

Minor release. PostScript integers are now 64-bit, which fixes the standard
LCG idiom that programs use for pseudo-randomness and is the reason this is a
breaking release rather than a patch. Three Type 3 font defects and a
clippath coordinate-space bug are also fixed, and the CLI gains --page.

Breaking

Downstream Rust code that reads PostScript integers needs attention; nothing
in the PostScript language surface changed incompatibly.

  • PsValue::Int now carries i64 instead of i32. A
    match obj.value { PsValue::Int(v) => … } binds an i64, so any use site
    that needs an i32 no longer compiles. DictKey::Int and Token::Int
    widened with it, as did Context::rand_seed.
  • PsObject::as_i32() now range-checks. It returns None for a value
    outside i32, where before it always returned Some for an integer. This
    is the one change with no compiler error behind it — audit call sites that
    treat None as "not an integer". Use the new as_i64() for the full
    range; keep as_i32() where the value is genuinely bounded (array and
    string indices, character codes), since a too-large value should fail those
    callers' range checks rather than wrap into a valid-looking index.
  • PsObject::int() takes impl Into<i64>. Calls are unaffected; only
    code coercing it to a fn(i32) -> PsObject pointer breaks.

Fixed

  • Type 3 fonts supplying only BuildGlyph raised invalidfont. The show
    path required BuildChar unconditionally and pushed the character code.
    PLRM 5.7 lists BuildGlyph as preferred and makes BuildChar required only
    "for LanguageLevel 1 or if BuildGlyph is absent", so such a font is
    well-formed and must be handed the character name from Encoding.
    Ghostscript renders these; stet refused them. xshow/yshow/xyshow had
    the identical defect.
  • stringwidth raised invalidfont on every Type 3 font, BuildChar
    ones included. It branched for font types 2, 0 and 42 and then fell through
    to the Type 1 path, which looks for CharStrings — a Type 3 font has none.
    There is no width table to consult: the width is whatever the build
    procedure hands setcachedevice/setcharwidth, so the procedure now runs
    inside a gsave/grestore with its marks drained, and measuring paints
    nothing.
  • glyphshow raised invalidfont on every Type 3 font. It read
    FontType but never branched on 3, going straight to the CharStrings
    lookup. Per the PLRM it now invokes BuildGlyph with the name directly —
    bypassing Encoding, which is what lets glyphshow reach glyphs no
    character code maps to — or, with only BuildChar, reverse-searches
    Encoding for the name and pushes the array index, retrying with
    /.notdef and raising invalidfont only when neither is encoded.
  • PostScript integers are now 64-bit, matching Ghostscript, which fixes the
    standard LCG idiom PostScript programs use for pseudo-randomness:
    /seed seed 1103515245 mul 12345 add 2147483648 mod def. On 32-bit integers
    the product overflowed, promoted to a real, and mod — which is
    integer-only — raised typecheck. Widening only the real fallback would not
    have fixed it: the product needs 55 bits and a real carries 53, so the seed
    would have come out one too high and every later draw would have diverged
    silently from what other interpreters produce. PLRM Appendix B's 32-bit
    range is listed under "Typical Limits" for interpreters "running on 32-bit
    machines" which "do not necessarily apply to all PostScript
    implementations", so this is not a conformance change. Overflow past the
    64-bit range still promotes to a real. bitshift is correspondingly 64-bit
    wide, and cvi accepts the wider range.
  • clippath returned the page in device space instead of current user
    space
    , so a program that had transformed its coordinate system got a clip
    rectangle dragged along with the transform. The clippath fill idiom for
    painting a background then filled an offset region and left part of the page
    bare — visible in the tiger EPS, whose grey backdrop was displaced by its
    %%BoundingBox origin. The default clip is a fixed region of the device, so
    it is now derived with the default CTM; pathbbox and fill map it back
    through the current CTM, which is what puts it in user space for the caller.
    Ghostscript's values now match exactly under translate, scale and rotate.
  • cvi on a long integer-valued string was off by one. The string scanner
    returned f64, so (22358003463039195) cvi came back as ...196 after the
    round trip through a 53-bit mantissa. Integer literals now stay integral.

Added

  • The CLI reports a page that was painted but never shown. A program that
    paints marks and then ends without a matching showpage leaves them on a
    page the device is never asked to emit, and the page is discarded. That is
    correct — it is what the PLRM specifies and what Ghostscript's file devices
    do — but it was indistinguishable from a broken renderer: no file appeared
    and nothing said why. The warning distinguishes a program that produced no
    output at all from one that lost only its trailing page.
  • Interpreter::warnings() surfaces the same diagnostic to library
    callers, where the silence was worse: render() returned Ok(vec![]), an
    empty page list that reads as a legitimate result. New public types
    ExecWarning and ExecWarningKind in stet::diagnostics; the CLI shares
    the detector, so the two cannot drift. Programs that install nulldevice
    are exempt — that is the PLRM-sanctioned way to ask for no output, so marks
    left unemitted are the point rather than a mistake.
  • --page sets the page size for PostScript/EPS input — a named size
    (letter, legal, tabloid, ledger, executive, a0-a6, b4, b5)
    or WIDTHxHEIGHT in points, with an optional -landscape / -portrait
    suffix that swaps the dimensions. There was previously no way to render a
    plain %!PS program whose artwork is larger than the default page:
    %%BoundingBox sets the page only for EPS — for a non-EPS document DSC
    makes it a description of the artwork's extent, not a page-size request, so
    both stet and Ghostscript fall back to US Letter and clip. --page
    overrides an EPS %%BoundingBox when both apply, and is rejected for PDF
    input, whose pages carry their own size.
  • GlyphCache::by_type3_name, a name-keyed Type 3 glyph cache. glyphshow
    can name a glyph that no character code maps to, which leaves nothing for
    the existing code-keyed cache to key on.

WebAssembly

  • stet-wasm 0.2.0. Its JavaScript API is unchanged, but the browser
    build inherits everything above, so rendering output moves: clippath
    backgrounds fill the page, Type 3 fonts that previously raised
    invalidfont render, and PostScript programs using the standard LCG for
    pseudo-randomness run instead of failing. A minor rather than a patch
    because the pixels change, not because anything you call does.