100 Commits
Author SHA1 Message Date
Nico Weber 6dfb6940a6 jbig2-from-json+Tests: Add jbig2 test with symbol dict with many entries
Some-real world files have JBIG2 files with many entries in a
symbol dictionary. For example,
https://www.sciencemadness.org/library/books/Mellor_ACTITC_11.pdf
has 173,132 symbols in the global jbig2 stream's symbol dictionary.

This adds a test that has many entries in the symbol dictionary.

Since putting hundreds of thousands of symbols in the json file is a bit
wordy, this adds a new mode "all_symbols_of_size" to
"symbol_dictionary". This mode takes width and height, and then adds all
possible bitmaps with these dimensions to the symbol dictionary. For
width x height, it will add 2 ** (width * height) symbols.

It also adds a new "strips_for_all_symbols" mode to the "text_region"
segments. This gets width and height and a target bitmap. It then tiles
that target bitmap and picks the symbol dict entry for the pattern at
each tile.

It then adds an arithmetically coded and a huffman coded file using this
construct to add a all_symbols_of_size 9x2 symbol dict. In other words,
it has 2 ** 18 == 256K entries.

(All symbols have height 2, and in huffman mode, all symbols with the
same height get concatenated into a single bitmap, so this internally
creates a 9 * 2 ** 18 ~= 2.3 M wide bitmap.  This is a bit artificial;
real-life files will have more than one height class. But this doesn't
seem to be a problem for other viewers, so maybe it's fine.)

That's the gist of it. To make the text region symbol IDs a bit more
interesting, we write bitmap-template0-all-ones.bmp instead of
bitmap.bmp in the text region segment and then xor bitmap-xor.bmp on top
of it to recover bitmap.bmp. This is because otherwise most tiles end up
with symbol id 0.  bitmap-template0-all-ones.bmp is roughly noise, and
bitmap-xor.bmp is the same bitmap with bitmap.bmp xor'd on top.

In addition to testing symbol dicts with many symbols, it's also a bit
of a perf test as this is slow with the huffman implementation in the
spec (which we currently use, and decoding the huffman version of this
file needs over 0.5s to decode at the moment).
2026-09-13 21:12:19 -04:00
Nico Weber bd7be1ae51 LibGfx/JBIG2Writer: Support symbol dicts with more than 32K entries
...as long as fewer than 32K entries are referenced in a single
text region segment.

Having more than 32K entries in a symbol dictionary happens in some
real-world PDFs where the "global" JBIG2 stream that's shared over
all pages has lots of letters. But the text region segment on every
single page just has a couple thousand letters (160 characters per line
at 100 lines and a couple thousand dust speckles is well under 32K
symbol references per page).

I'd like to add a test that's similar to such a file, so we need to
support writing text regions referring to symbol dictionaries with
more than 32K symbols.
2026-09-13 21:12:19 -04:00
Nico Weber f98de99d00 LibGfx: Increase maximum Bitmap dimensions a bit
A test jbig2 image I want to create will have a height class bitmap
that's 9 * 2 ** 18 = 2,359,296 pixels wide (and 2 high).

So bump up our limit on bitmap width and height from 64 ki to 4 Mi.
2026-09-13 21:12:19 -04:00
Nico Weber 8e4e88f540 jbig2-from-json: Use set() more
I missed this one earlier.

No behavior change.
2026-09-12 08:05:58 -04:00
Nico Weber c781514c36 jbig2-from-json: Add set_bits() overload taking non-ErrorOr value
No behavior change.
2026-09-12 08:05:58 -04:00
Nico Weber 9750fdb77a jbig2-from-json: Add set() overload taking non-ErrorOr value and use it
No behavior change.
2026-09-12 08:05:58 -04:00
Nico Weber 03fb4db42f jbig2-from-json: Make combination operator parsing use set_bits()
No behavior change.
2026-09-12 08:05:58 -04:00
Nico Weber 153bffdefc jbig2-from-json: Introduce is_string_literal() function
No behavior change.
2026-09-12 08:05:58 -04:00
Nico Weber afcade316d jbig2-from-json: Introduce set_bits() function
Like set(), but with |= instead of =, and with a built-in left shift.

No behavior change.
2026-09-12 08:05:58 -04:00
Nico Weber 810972c80d jbig2-from-json: Use set() more 2026-09-11 19:03:18 -04:00
Nico Weber adf73f1167 jbig2-from-json: Introduce parse_string() function
ErrorOr<> can't contain a const reference, so this comees with
some line noise in form of `*` deref operators.

No behavior change.
2026-09-11 19:03:18 -04:00
Nico Weber 1bd79a991e jbig2-from-json: Introduce parse_object() function
ErrorOr<> can't contain a const reference, so this comees with
some line noise in form of `*` deref operators.

No behavior change.
2026-09-11 19:03:18 -04:00
Nico Weber 4a67e6ab05 jbig2-from-json: Introduce parse_array() function
ErrorOr<> can't contain a const reference, so this comees with
some line noise in form of `*` deref operators.

No behavior change.
2026-09-11 19:03:18 -04:00
Nico Weber 4b279c46d1 jbig2-from-json: Introduce generic set() function
That way, we don't need all the specialized ones.

Pointless golfing, or exciting independent rediscovery of a subset
of monads? You decide!

No behavior change.
2026-09-11 19:03:18 -04:00
Nico Weber 2b0cb08f22 jbig2-from-json: Introduce parse_u32_in_set() helper
Generally, the design of jbig2-from-json is that it leaves a
roughly correct jbig2 file and then relies on parsing it to do
detailed validation of fields. That means just using
parse_u32_in_range(..., 0, 3, ...) would be good enough for most
uses in this file. But seeing that next to the error message was
confusing, so in this case we do slightly too detailed validation
in jbig2-from-json.cpp directly.

No behavior change.
2026-09-11 19:03:18 -04:00
Nico Weber 91ca72d1a0 jbig2-from-json: Add parse_bit() helper
Technically not needed as parse_bool + implicit conversion from
bool to int does the same thing, but relying on implicit conversion
from bool feels a bit yucky.

No behavior change.
2026-09-11 19:03:18 -04:00
Nico Weber ed1c7467f3 jbig2-from-json: Dedup adaptive template pixel array conversion code
No behavior change.
2026-09-11 21:38:14 +02:00
Nico Weber 15dca529f6 jbig2-from-json: Add parse_i32_in_range() / set_i32_in_range() helpers
set_i32_in_range() is templated for consistency with the other
set_ functions.

No behavior change.
2026-09-11 21:38:14 +02:00
Nico Weber b9d14c9809 jbig2-from-json: Add parse_u32_in_range() / set_u32_in_range() helpers
set_u32_in_range() is templated to set u8 and u16 variables.

No behavior change.
2026-09-11 21:38:14 +02:00
Nico Weber dc17bdb6c1 jbig2-from-image: Deduplicate color parsing code
No behavior change.
2026-09-11 21:38:14 +02:00
Nico Weber 2c3c9d2d51 jbig2-from-image: Deduplicate combination operator parsing code
No behavior change.
2026-09-11 21:38:14 +02:00
Nico Weber a938219f3c jbig2-from-image: Tweak jbig2_adaptive_template_pixels_from_json()
Convert it to the new parse_ pattern to make callers a bit shorter.
No behavior change.
2026-09-11 21:38:14 +02:00
Nico Weber 5790c7c14b jbig2-from-json: Add set_jbig2_trailing_7fff_handling_from_json()
No behavior change.
2026-09-10 14:22:06 -04:00
Nico Weber e8505157c8 jbig2-from-json: Add parse_u32() / set_u32() helpers
set_u32() is templated to set Optional<u32>s.

No behavior change.
2026-09-10 14:22:06 -04:00
Nico Weber 26c1fa1568 jbig2-from-json: Add parse_i32() / set_i32() helpers
All client code only calls set_i32(), but implement set_i32() in
terms of parse_i32() for symmetry with parse_bool() / set_bool().

set_i32() is templated to set Optional<i32>s.

No behavior change.
2026-09-10 14:22:06 -04:00
Nico Weber 499afd2651 jbig2-from-json: Add set_bool() helper to cut down on code duplication
Use set_bool() when parse_bool() was used to initialize a variable.

No behavior change.
2026-09-10 14:22:06 -04:00
Nico Weber 694d3b0fb1 jbig2-from-json: Add parse_bool() helper to cut down on code duplication
No behavior change.
2026-09-10 14:22:06 -04:00
Nico Weber fed29b0c20 LibPDF: Implement support for text rendering modes
If the current text rendering mode isn't a solid-color fill
(or a solid-color fill with a non-scaling matrix), request
vector glyphs, and use regular path rendering.

Like in regular path rendering, for fill-and-stroke, we're supposed
to not draw the stroke on top of the fill. We don't get that right
yet, see the FIXME in Renderer::fill_and_stroke_path().
2026-08-25 08:29:35 -04:00
Nico Weber 962ed5e4bc LibPDF: Implement filling text with a pattern
Detect this case, request vector glyphs for it, and call into the
regular path pattern handling for it.
2026-08-25 08:29:35 -04:00
Nico Weber 2ea15d37f2 LibPDF: Let renderer decide if the current text should use vectors
Removes some duplication from SimpleFont and Type0Font, and makes
it easier to make self-consistent decisions about this with
patterns and text rendering modes.
2026-08-25 08:29:35 -04:00
Nico Weber f5286e9cd9 LibPDF: Add renderer methods for filling and stroking arbitrary paths
...instead of just the current path.

No behavior change.
2026-08-25 08:29:35 -04:00
Nico Weber d79349b49a LibPDF: Let fonts call into renderer to paint final vector paths
This will make it possible to implement text rendering mode support
in a single place.

Both SimpleFont and Type0Font will have to know to call the path
variant if the text rendering modes isn't `Fill`.

Needs a pretty sus const_cast since draw_string() receives
a Renderer const& but a non-const painter (and we need the
renderer's painter in Renderer::paint_text_glyphs(). We could
pass in the painter to paint_text_glyphs(), but it'd be the
same painter really, so that'd be silly too.)

Another option would be to give PDFFont a method to return a
Path and have Renderer decide if it wants to call this. That's
a bit janky with Type3Font though, which cannot return a vector
path, but it does support rotation already. But maybe we want
to reorganize things some more later.

No behavior change.
2026-08-25 08:29:35 -04:00
Nico Weber 7a3ce0060f LibPDF: Move code to disable type0 vertical font vector glyphs around
No behavior change.
2026-08-25 08:29:35 -04:00
Nico Weber 26f766a165 LibPDF: Implement CIDFontType2::append_glyph_path
With this, we can draw rotated or skewed Type0 fonts with
subtype CIDFontType2. These use TrueType data, so this is
similar to TrueTypePainter::append_glyph_path().

We can now draw rotated or skewed fonts for all font types :^)
2026-08-23 17:59:27 -04:00
Nico Weber f453df2b5f LibPDF: Implement CIDFontType0::append_glyph_path
With this, we can draw rotated or skewed Type0 fonts with
subtype CIDFontType0 (using CIDFontType0C in the descriptor's
subtype). These use CFF data, so the implementation here
is very similar to the Type1 implementation in
0ac7f5228f from #26954.
2026-08-23 17:59:27 -04:00
Nico Weber f3616828b1 LibPDF: Implement Type0Font::draw_transformed_glyphs
It delegates to CIDFontType::append_glyph_path(), which no
CIDFontType subclass implements yet, so no behavior change yet.

Type0Font::draw_transformed_glyphs() is virtually identical to
the SimpleFont version, so we'll want to unify code here in the future.

This seems to do the right thing for horizontal text, but not for
vertical text. So the vertical text doesn't use the append_glyph_path()
codepath yet; it's for a future PR.
2026-08-23 17:59:27 -04:00
Nico Weber 863f13c06e LibPDF: Tweak Type0 code to prepare for path-based "slow path"
This does for Type0 what a subset of commits 2 and 3 of #26292
(1892b09b9f / 4bd3d4bf95) did for SimpleFont:

The core logic of Type0Font::draw_string() moves into a new
method Type0Font::for_each_glyph_position(), and the existing
code calls that from a new `Type0Font::draw_axis_aligned_glyphs()`.

for_each_glyph_position() no longer works in prescaled units,
since SimpleFont no longer does that after #26292 either.
(SimpleFont had to keep `font_scale` for fallback widths or
built-in fonts, but it rescaled units to limit that to a small
corner case, and that corner case does not exist in Type0 fonts,
so we get to simplify more.)

Type0Font::draw_transformed_glyph() is called but doesn't do anything
yet. That will be in the next commit. (Since this policy code is
duplicated in SimpleFont and Type0Font, we might want to revert
parts of 4bd3d4bf95 again in the future.)

No behavior change.
2026-08-23 17:59:27 -04:00
Nico Weber 1943bf75e5 LibPDF: Remove unused Renderer argument from append_glyph_path()
This argument has been unused since it was added.

No behavior change.
2026-08-23 17:59:27 -04:00
Nico Weber 0ac7f5228f LibPDF: Implement Type1Font::append_glyph_path()
With this, we can draw rotated type1 text.

The `glyph_width` multiplication in SimpleFont::append_text_path
corresponds to the same in SimpleFont::draw_axis_aligned_glyphs.

Type1FontProgram::append_glyph_to() uses AppendRelativeToLastPoint::Yes
like ScaledFont::append_glyph_path_to().

Type1FontProgram::append_glyph_to() is like build_char() in the same
class, except it does no subpixel translation, and it doesn't move
the origin to the top left corner.
2026-08-19 18:59:30 -04:00
Nico Weber 8dd45d8e09 LibPDF: Plumb width to append_glyph_path()
No behavior change.
2026-08-19 18:59:30 -04:00
Nico Weber ca08ac270f LibPDF: Extract Type1Font::char_name_for_char_code()
No behavior change.
2026-08-19 18:59:30 -04:00
Nico Weber d0ddbf3ccd LibPDF: Rename Type1Font::m_glyph_cache to m_cached_glyph_bitmaps
No behavior change.
2026-08-19 18:59:30 -04:00
Nico Weber bf1bc327be LibPDF: Move Type1Font::append_glyph_path() out of line
No behavior change.
2026-08-19 18:59:30 -04:00
Nico Weber 3f28a3d1db LibPDF: Add spec comments to TilingPattern::create() and tweak a bit
We now require required keys.

Also rename types and names to match the spec better.

(Eventually, we'll want to add "required_foo()" methods that check that
they actually exist instead of VERIFY()ing ne day.)
2026-08-16 16:27:29 -04:00
Nico Weber 074404efaf LibPDF: Rename straggler "style" symbols to "color"
No behavior change.
2026-08-16 15:44:33 -04:00
Nico Weber c794be919e LibPDF: Rename GraphicsState::paint_style to paint_color
For consistency with the previous commit.

No behavior change.
2026-08-16 15:44:33 -04:00
Nico Weber 41d96d0d9a LibPDF: Rename GraphicsState::stroke_style to stroke_color
Makes sense now that it's a Variant<Color, Pattern>. Also,
Gfx::StrokeStyle is something else already (style for stroking paths),
and Renderer::stroke_style() returned that. So this makes things
less confusing.

No behavior change.
2026-08-16 15:44:33 -04:00
Nico Weber 93da1fbf86 LibPDF: Rename ColorSpace::style() back to ColorSpace::color()
Done with "Rename Symbol" in VSCode.

No behavior change.
2026-08-16 15:44:33 -04:00
Nico Weber 5b3f45af3f LibPDF: Make ColorSpace::style() return just Gfx::Color again
This is possible after the recent Pattern changes.

No behavior change.
2026-08-16 15:44:33 -04:00
Nico Weber 389c8d4911 LibGfx: Remove now-unused RepeatingBitmapPaintStyle, OffsetPaintStyle 2026-08-16 14:01:15 -04:00
Nico Weber b39877b00b Tests/LibPDF: Add a file with rotated and skewed tiled patterns
Tweaked from pattern-type1.pdf, and offsets fixed up with
`mutool clean` as usual.
2026-08-16 14:01:15 -04:00
Nico Weber 318464bb7c LibPDF: Paint tiled patterns using explicit bitmap blit loop
This is a bit faster.

This also adds support for rotated and skewed tiled patterns.
2026-08-16 14:01:15 -04:00
Nico Weber de6371c327 LibPDF: Bake tiling pattern matrix into transform
No behavior change.
2026-08-16 14:01:15 -04:00
Nico Weber be5d823f84 LibPDF: Make tiled patterns to work like gradient patterns
This moves tiled pattern painting to work like gradient
patterns work now:

- PatternColorSpace::style() is no longer called

- Pattern::style() is split into TilingPattern::create() and
  TilingPattern::draw(). This is as close to a pure code move
  for now as possible. (OffsetPaintStyle moves from Renderer
  into TilingPattern::draw() for this. This is a slight
  progression in that filing a path with a translation from
  the CTM now works, see upper rect in patter-type1.pdf.)

Other than that, this is a pure reorganization that retains bugs such as
having no pattern rotation support, no support for /PainType 2, and so
on.

One way to look at this commit is that previously, ColorSpace returned a
ColorOrStyle (a Variant<Color, PaintStyle>). That's now no longer used
(a future commit will make ColorSpace return just a Color isntead), but
instead GraphicsState now has a Variant<Color, Pattern>. Pattern has an
explicit painting API instead of the implicit PaintStyle API, as that
works better for some of the gradients.

(Maybe we can even remove PatternColorSpace in the future by making
GraphicState's color spaces a Variant<ColorSpace, PatternTag>. That
way, areas in the code that don't support pattern color spaces will
have an easier time.)

Since this now uses get_resource(), it also makes files work that use
indirect object references for the pattern's resource dictionary
(e.g. 0000450.pdf p5).

Since we now paint tiling patterns by drawing paths to alpha and the
pattern to the path's bounding box, this approach is a bit slower (not
much, and we'll get some of that back when we make TilingPattern::draw()
no longer use a PaintStyle internally). But it'll allow us to handle
gradient and tiling patterns uniformly, and makes it easier to
eventually fill text and masks with patterns.
2026-08-16 14:01:15 -04:00
Nico Weber e22ce7b39a LibPDF: Add a few includes to Page.h
The file uses these includes, and having them helps clangd process it.
2026-08-16 14:01:15 -04:00
Nico Weber 70f15f95d8 LibPDF: Support /Background for pattern type 2 2026-08-15 21:42:41 -04:00
Nico Weber 91861ab927 Tests/LibPDF: Add a file using a type 2 pattern
It's based on pattern-type1.pdf, but with type 2 patterns instead
of type 1 patterns. The shading object inside the pattern is
from group.pdf and slightly tweaked.
2026-08-15 21:42:41 -04:00
Nico Weber 469b9f9014 LibPDF: Implement filling and stroking paths with type 2 patterns
Stroking is implemented by converting the stroke to a path and
filling it, as usual.

Filling a path with a type 2 pattern is implemented by applying
the path as clip, and then drawing the shading inside the type 2
pattern.
2026-08-15 21:42:41 -04:00
Nico Weber f7e32c3689 LibPDF: Scaffolding to prepare for rendering type 2 patterns
state().stroke_style and state().paint_style are now a
`Variant<ColorOrStyle, NonnullRefPtr<Pattern>>` insteaad of just
a ColorOrStyle. Update all use sites to fail with an error if
it's a NonnullRefPtr<Pattern>.

In the future, we can hopefully move type 1 patterns to this design
as well, and then change ColorOrStyle to just Color. Then
ColorSpace will no longer have to know about patterns (or Renderer).
In the meantime, this is a bit awkward.

Future commits will fill in code to allow actually painting
objects with pattern fills or clips, starting with paths, and
eventually image masks.

(Text depends on some more text-to-path code that we also need
for implementing more text rendering modes.)
2026-08-15 21:42:41 -04:00
Nico Weber 0018613b13 LibPDF: Extract a get_resource() method
Less code, and we no longer assert on documents that use a shading but
don't have a /Shading entry in the resource dict, nor on documents that
load a graphics context and either don't have a /ExtGState entry in the
resources dict, or an entry for the name of the loaded graphics context.
2026-08-15 21:42:41 -04:00
Nico Weber 79001c5b94 LibPDF: Make path fill and stroke methods in Renderer fallible
No behavior change.

This will be used to implement filling and stroking paths with
type 2 pattern color spaces.
2026-08-15 21:42:41 -04:00
Nico Weber a11b120911 LibPDF: Add some scaffolding for type 2 patterns
The current design of PatternColorSpace returning a PaintStyle
doesn't neatly work for type 2 patterns: PaintStyle has to return
a Color for each coordinate, and shading types 4-7 do not have a
neat implicit representation.

Instead, type 2 shadings will handle their painting outside the
PDF::ColorSpace system, painting the shading to a bitmap and clipping
it to the path to be filled (or the stroke-to-path for stroking),
similar to how the `sh` operator is already implemented.
2026-08-15 21:42:41 -04:00
Nico Weber e5c070db8f LibPDF: Use CommonNames for BBox string in Pattern.cpp
No behavior change.
2026-08-15 21:42:41 -04:00
Nico Weber 95b0add504 LibPDF: Extract common_entries() in Pattern.cpp
No behavior change.
2026-08-15 21:42:41 -04:00
Nico Weber dddae788ab LibPDF: Move pattern handling type to new class Pattern
This is a pure code move at this point, the only change is
s/m_renderer/renderer/ in the new location.

Later, the API here will change a bit, and the internal
organization of Pattern.cpp will too.

No behavior change.
2026-08-15 21:42:41 -04:00
Nico Weber caef7fdd48 LibPDF: Add a spec comment to PatternColorSpace::style()
No behavior change.
2026-08-15 21:42:41 -04:00
Nico Weber 17ec0acc01 Tests/LibPDF: Use pattern for stroking too in pattern-type1.pdf 2026-08-15 21:42:41 -04:00
Nico Weber 893db07f40 Tests/LibPDF: Make pattern-type1.pdf more spec-compliant
This file was based on Example 4.23 in the PDF 1.7 spec, but
a few changes were made that broke it:

- ZapfDingbats was changed to Helvevtica (a misspelling of Helvetica)
- But it still used an /Encoding array for ZapfDingbats
- The operator stream *was* updated to draw glyphs a, b, c, d
- And pattern had no /Resources dict even though it's required per spec

Make it more like the example from the spec:

- Change the font to ZapfDingbats, and draw \001, \002, \003, \004
  in the operator stream, to match the /Encoding entry
- Give the pattern a /Resources entry. Like in the example, make it
  an indirect object reference to object 16, and add an object 16
- Update file offsets with `mutool clean` as usual

With this, the file shows card suits instead of a, b, c, d, and it
now displays fine in Preview.app. And it'll keep displaying fine
if we decide to make LibPDF stricter and more spec-compliant by
requiring /Resources too, like Preview.app seems to do.
2026-08-15 21:42:41 -04:00
Nico Weber 490626e656 Tests/LibPDF: Rename pattern.pdf to pattern-type1.pdf 2026-08-15 21:42:41 -04:00
Nico Weber 3ed2ecca29 LibPDF: Support tiling matrices with negative scale factors
Previously, we would emit a "bitmap empty backing store" if a tiling's
/Matrix had a negative scale factor.

Happens e.g. for all four tiling patterns in 2025-Laurenz-Thesis.pdf
--page 34.
2026-08-11 23:45:49 -04:00
Nico Weber 2ccde42476 LibPDF: Support setting pattern color spaces via a named resource
`/CS0 cs` in a form where the form has
`/Resources<</ColorSpace<</CS0 [/Pattern]>>>>` now works.

This happens e.g. in 2025-Laurenz-Thesis.pdf --page 34
(which has page number 29 written on it).
2026-08-11 23:45:49 -04:00
Nico Weber 9e646867dc LibPDF+pdf: Add a --strict option
Real-life PDFs don't adhere to the spec, so we need to be lax about
this by default. But it's nice to optionally be more strict.

The main motivation is that we'll have to relax more things, and
this way they're easy to find.

For now, this adds just a handful checks. There will be more later.
2026-08-05 09:38:24 +02:00
Nico Weber 3ef7fb18dc LibPDF: Clear arguments of unknown operators
This is a follow-up to #26905: If an unknown operator had arguments,
we used to not clear them, causing the next known operator to
receive them.

To test, call an unknown operator with a non-number arg,
followed by `re`.
2026-07-29 20:47:02 -04:00
Nico Weber d8c9bc03bb Tests/LibPDF: Check that invalid-operator.pdf renders without error
This is true since b3090a36c8.
2026-07-29 20:47:02 -04:00
Nico Weber 6ec789e749 LibPDF: Do not crash on unknown named encodings 2026-07-29 19:07:52 -04:00
Nico Weber 299bb04485 LibPDF: Stop crashes on invalid args to several drawing operators
We used to crash /Tf with an nonexistent font name,
on /CS / /cs with a nonexistent colorspace name,
on color operators with the wrong number of operators,
and on color operators with the wrong type of operators.

(We already handle /Do with nonexistent image or form name fine,
but slightly reorder lines there to match font and colorspace
handling)

Add a test for this, hand-written and fixed up by `mutool clean`.
2026-07-29 09:11:03 -04:00
Nico Weber b3090a36c8 LibPDF: Ignore unknown page drawing operators
Lets us successfully render page 2 of
BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf

This is a separate commit from the previous one as it's technically
not strictly spec compliant.
2026-07-29 09:11:03 -04:00
Nico Weber b58497421f LibPDF: Do not crash on invalid page drawing operators
Lets us no longer crash rendering page 2 of
BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf.

Test file hand-written (based on text.pdf) and offsets fixed up
with `mutool clean` as usual.
2026-07-29 09:11:03 -04:00
Nico Weber 19e8f5ea00 MacVideoPlayer: Draw video at integer rect coordinates 2026-07-26 10:13:47 -04:00
Nico Weber f03adae364 MacPDF: Draw pages at their aspect ration instead of stretching them
MacPDF is just for hacking on LibPDF so I didn't put this in so far,
but I've spent enough time manually right-sizing PDFs :^)
2026-07-26 10:13:47 -04:00
Nico Weber 44d5e571df Everywhere: Use Rect::scaled_to_fit_within() and Rect::scaled_to_cover()
This is a slight behavior change in some places. Some places did

    (r.size().to_type<float>() * max(scale_x, scale_y)).to_type<int>();

After this commit, the semantics are now like `(...).to_rounded<int>()`,
that is we no longer sometimes omit a column or row in the fitted
direction.
2026-07-26 10:13:47 -04:00
Nico Weber 35d63ed139 LibGfx: Add Rect::scaled_to_fit_within() and Rect::scaled_to_cover()
scaled_to_fit_within() is like CSS's `object-fit: contain`: It returns
the largest rect that completely fits into the given rect, with an exact
fit in one dimension, and centered in the other dimension.

scaled_to_cover() is like `object-fit: cover`: It returns the smallest
rect that completely covers the given rect, with an exact fit in one
dimension, and centered in the other dimension.
2026-07-26 10:13:47 -04:00
Nico Weber dc4f01f580 LibGfx/JPEG2000/IDWT: Combine steps 1 and 2 for 9-7 _1D_FILTR
Takes `Build/lagom/bin/image -o out.tiff ~/Downloads/balloon.jpf` from
531.4 ms ± 5.4 ms to 515.8 ms ± 4.7 ms ms under hyperfine on my system
(2.7% less). When run under `/usr/bin/time -l`, the changes are:

Before:

         13202665108  instructions retired
          2281080551  cycles elapsed

After:

         12821908788  instructions retired
          2213264986  cycles elapsed

Only affects lossy files.
2026-07-26 10:12:58 -04:00
Nico Weber fd743c7011 LibGfx/JPEG2000/IDWT: Name loop boundaries in _1D_FILTR
No performance or behavior change.
2026-07-26 10:12:58 -04:00
Nico Weber 32a71fa3aa LibGfx/JPEG2000/IDWT: Use raw pointers instead of op[] in _1D_EXTR
Takes `Build/lagom/bin/image -o out.tiff ~/Downloads/balloon.jpf` from
542.3 ms ± 7.0 ms to 527.2 ms ± 4.7 ms under hyperfine on my system
(2.7% less). When run under `/usr/bin/time -l`, the changes are:

Before:

         13956342230  instructions retired
          2322276362  cycles elapsed

After:

         13251294364  instructions retired
          2270195534  cycles elapsed

The absolute win on the lossless file is again a bit smaller
(due to smaller image dimensions), and the relative win is tiny
there (due to it spending much more time in bitplane decoding and
less in IDWT).
2026-07-26 10:12:58 -04:00
Nico Weber 83a0675316 LibGfx/JPEG2000/IDWT: Call PSE only when needed in _1D_EXTR
Takes `Build/lagom/bin/image -o out.tiff ~/Downloads/balloon.jpf` from
586.0 ms ± 5.6 ms to 547.7 ms ± 5.1 ms under hyperfine on my system
(6.6% less). When run under `/usr/bin/time -l`, the changes are:

Before:

         14557698316  instructions retired
          2500654210  cycles elapsed

After:

         13958498350  instructions retired
          2318667968  cycles elapsed

The lossless files goes from 1.025 s to 1.000 s (likely smaller win
because it's half the dimensions).

(Note that the "from" hyperfine number is pretty different from the
previous commit, probably due to my system being under different load,
at different battery level, or what. But the "instructions retired"
are very comparable, so that's probably a better number to look at.)
2026-07-26 10:12:58 -04:00
Nico Weber 23925266f3 LibGfx/JPEG2000/IDWT: Use raw pointers instead of op[] in _1D_FILTR
Slight subtlety: Forming the pointer
`data() + (buffers.scanline_start - i0)` is usually technically invalid
in C++: scanline_start is usually 4 and i0 can be up to the current
rect's left or top, so it'll usually point to before the image.

I haven't seen this cause actual problems in practice, but make the
buffers max(left,top) longe, even if that leading data ine is never
touched.

Takes `Build/lagom/bin/image -o out.tiff ~/Downloads/balloon.jpf` from
704.3 ms ± 5.4 ms to 613.2 ms ± 3.9 ms under hyperfine on my system
(12.9% less). When run under `/usr/bin/time -l`, the changes are:

Before:

         18035577126  instructions retired
          2862272914  cycles elapsed

After:

         14521350138  instructions retired
          2492032301  cycles elapsed

balloon.jps uses the lossy 9-7 transform. For lossless files, this helps
too, but less, as the 5-3 transform uses fewer loops in this function.
Also, lossless files spend relatively much more time in bitplane
decoding and less in IDWT, so the relative win for them is smaller too:
For ballon_rev_half.jp2, which is balloon.jpf resized to 50% and
reencoded in lossless, from 1.058 s to 1.035 s.
2026-07-26 10:12:58 -04:00
Nico Weber c6bf4ceda2 LibGfx/JPEG2000: Copy JPEG2000InverseDiscreteWaveletTransformReference.h
I'd like to make changes to our JPEG2000 IDWT implementation to speed
it up. At the moment, it's very close to the spec, which seems useful
for correctness and understandability.

So copy the existing file, put the code in there in a Reference
namespace, and in JPEG2000_DEBUG builds, compare the output of the
two implementations. (At the moment, both implementations are
identical, but that will change more and more over time.)

That way, we can run `Meta/test_pdf.py` with a large test corpus with
the comparison turned on to make sure IDWT optimizations aren't
completely broken.

Run `git show` with -C / --find-copies on this commit to make it display
the new file as copy of the old one, along with changes I made to it.
(Namely: I removed the input structs from the reference file as they're
shared with the production file, and I qualified a few calls with
`Reference::`.)

No behavior change.
2026-07-26 10:12:58 -04:00
Nico Weber a38bd0624c Meta: Disable "readability-math-missing-parentheses" clang-tidy check
It's very noisy and we usually don't intend to follow this check.
2026-07-26 10:00:12 +02:00
Nico Weber b9d5d5386f jbig2-from-json: Prevent some unnecessary copies
Mostly to appease clang-tidy. No behavior change.
2026-07-25 18:45:52 +02:00
Nico Weber 40903d6bc0 jbig2-from-json: Rename struct with accidentally truncated name
It was supposed to be RegionSegmentInformationJSON, not
RegionSegmentInformat.

No behavior change.
2026-07-25 18:45:52 +02:00
Nico Weber 6b838637dc jbig2-from-json: Remove an unused local variable
No behavior change.
2026-07-25 18:45:52 +02:00
Nico Weber 92d38f02af jbig2-from-json: Improve a few diagnostics
Fixes a few copy-pastos, makes some diagnostics more detailed,
and makes the help text a bit more precise.
2026-07-25 18:45:52 +02:00
Nico Weber f261084476 LibGfx/JPEG2000: Pull dequantization step size computation out of loop
step_size is identical for all coefficients. No behavior change.

Takes

    hyperfine \
        'Build/lagom/bin/image --no-output ~/Downloads/balloon.jpf'

from 741.7 ms ± 8.0 ms to 648.0 ms ± 1.9 ms on my system,
a 12.5% reduction.
2026-07-22 08:53:37 -04:00
Nico Weber ada0a3c5fc Tests/LibPDF: Add a pdf covering rotation of different font types
File created by copying fonts.pdf, and then manually slightly
reformatting the content stream, adding a 30 degree rotation matrix
at the top, and fixing the file up with `mutool clean` as usual.
2026-07-22 06:16:01 -04:00
Nico Weber 85e2b03721 Tests/LibPDF: Add a pdf covering the different font types
We handle them all correctly, but we didn't have a checked-in test.
Now we do. See the PR adding this file for how it was created.

Uses SerenitySans transcoded into various formats for the vector fonts,
and KaticaRegular for the type 3 font :^)
2026-07-22 06:16:01 -04:00
Nico Weber f888d2ecfb LibPDF: Remove an incorrect dbgln()
This reverts the 2nd commit from #23265. It fires for example for
(valid!) CFF files that use a built-in encoding, such as the file
mentioned in #21469.
2026-07-22 06:16:01 -04:00
Nico Weber c5af100c9e Everywhere: Use NaN<T> instead of __builtin_nan{f,,l}("")
No behavior change.
2026-06-26 13:15:59 +02:00
Nico Weber c793c2e626 Everywhere: Use Infinity<double> instead of __builtin_huge_val()
No behavior change.
2026-06-26 13:15:59 +02:00
Nico Weber 71199c1449 Everywhere: Use Infinity<float> instead of __builtin_huge_valf()
No behavior change.
2026-06-26 13:15:59 +02:00