Skip to content

fix(bbb-web): Fallback to Rasterization if SVG has (lots of) Mask Tags - #25

Closed
antobinary wants to merge 3 commits into
v4.0.x-developfrom
fix-svg-mask-rasterization-40
Closed

antobinary wants to merge 3 commits into
v4.0.x-developfrom
fix-svg-mask-rasterization-40

Conversation

@antobinary

Copy link
Copy Markdown
Owner

What

Refined follow-up to bigbluebutton#24393 by @paultrudel, targeting 4.0. Structured as three commits so the refinement is reviewable:

  1. @paultrudel's original commit (cherry-picked, his authorship): count <mask> tags during SVG analysis and rasterize any slide whose generated SVG contains one.
  2. Refinement: replace the any-mask trigger with a configurable maskTagThreshold (wired like imageTagThreshold / useTagThreshold / placementsThreshold), disabled by default (0), plus a ScalaTest spec with a committed soft-masked sample.
  3. E2E test: an @setting-required Playwright test asserting a masked slide is served as rasterized SVG when the feature is enabled.

Background

Certain scanned PDFs contain images whose soft masks pdftocairo 22.02.0 (Ubuntu 22.04 / BBB 3.0) converted into SVG <mask> elements with wrong values, leaving slides partially blank or dark in the browser — the problem bigbluebutton#24393 addressed by rasterizing on any mask.

Addressing @gustavotrott's review of bigbluebutton#24393

The objection was that most PDFs contain <mask> tags, so presence of a mask isn't the problem — pdftocairo was writing masks with wrong values. Empirically confirmed: BBB's own default.pdf title page yields 3 <mask> tags under pdftocairo 24.02.0, so "any mask → rasterize" would rasterize BBB's own default deck (see table). This PR never rasterizes on mere mask presence.

Configuration (follows the bigbluebutton.properties 0-disables convention)

maskTagThreshold (default 0, disabled):

  • 0 — disabled. pdftocairo 24.02.0 (Ubuntu 24.04, BBB 4.0's target OS) generates correct mask values, so no rasterization is needed on a stock install and vector slides are preserved. Default behavior is bit-identical to current 4.0.
  • 1 — rasterize any slide whose generated SVG contains a <mask> (this reproduces the fix(bbb-web): Fallback to Rasterization if SVG has Mask Tags bigbluebutton/bigbluebutton#24393 behavior).
  • N — rasterize only slides with N or more masks.

The mask count is always collected and reported in the potential_problem_with_svg analytics entry for tuning, even when disabled.

Poppler on BBB 4.0

BBB 4.0 targets Ubuntu 24.04 → poppler-utils 24.02.0, the version @gustavotrott identified as fixing the wrong-mask-values bug (verified: 24.02.0 on a 4.0 Noble install vs 22.02.0 on 3.0 Jammy). That's why the default is disabled.

Evidence (bbb 4.0 install, pdftocairo 24.02.0, exact production analysis command)

Input (page 1) image path use mask base 4.0 commit 1 (any-mask) commit 2 default (0) maskTagThreshold=1
sample-with-mask.pdf (soft-masked image) 3 2 4 2 keep vector rasterize keep vector rasterize
default.pdf (ordinary deck) 2 159 444 3 keep vector rasterize (over-broad) keep vector rasterize

Counts are real pdftocairo output; decisions were computed by feeding that stdout into the compiled SvgConversionHandler and evaluating each variant's condition — a decision-logic evaluation, not a full upload run.

Testing

  • Unit (ScalaTest): SvgConversionHandlerTest replicates the grep -oE '<image|<path|<use|<mask' | sort | uniq -c analysis pipeline against a committed sample — a minimal PDF with an /SMask image and the SVG pdftocairo 24.02.0 produced from it — asserting the parsed counts (mask=2, image=3, path=2, use=4) plus zero-mask and malformed-stdout cases. All 3 pass (ScalaTest 3.0.8, JDK 21). Note: bbb-common-web specs aren't currently run in CI, so this documents the parsing/threshold contract rather than acting as a gate today.
  • E2E (Playwright, @setting-required:maskTagThreshold): uploads the same soft-masked sample and asserts the served slide SVG is the rasterized embedded-PNG form produced by createSvgWithEmbeddedPng() (<image href="data:image/png;base64,... and no <path>), fetched via the tl-image asset URL. 4.0 renders slides as tldraw image assets, so a visual snapshot can't distinguish rasterized from vector — inspecting the served SVG isolates the feature. maskTagThreshold is a server-side setting with no client-settings hook, so the test requires bbb-web restarted with maskTagThreshold=1 and is excluded from the default CI gate. Mechanics validated both ways on a live 4.0 server: default config serves vector SVG (assertion rejects); a forceRasterizeSlides=true upload serves the embedded-PNG form (assertion matches).
  • Touched Java compiles cleanly with JDK 21 against the bbb-web runtime classpath; the Playwright suite type-checks and lints with no new findings.

paultrudel and others added 3 commits July 23, 2026 19:05
…d by default

Refines the previous commit: masks are common in ordinary PDFs
(soft-masked/alpha images - the bundled default.pdf title page yields 3),
so rasterizing on the mere presence of a <mask> tag would rasterize most
real-world decks. Replace the any-mask check with a configurable
maskTagThreshold wired like the existing imageTagThreshold,
useTagThreshold and placementsThreshold.

The default is 0 (disabled), following the bigbluebutton.properties
convention where 0 turns a limit off: pdftocairo 24.02.0 shipped with
Ubuntu 24.04 - BBB 4.0's target OS - generates correct mask values, so no
rasterization is needed on a stock install and vector slides are
preserved. Setting maskTagThreshold=1 rasterizes any slide whose
generated SVG contains a mask (the previous commit's behavior); setting
it to N rasterizes slides with N or more masks. The mask count is always
collected and reported in the potential_problem_with_svg analytics log
entry for tuning.

Adds a ScalaTest spec for the SvgConversionHandler tag counting, backed
by a minimal soft-masked sample PDF and the SVG that pdftocairo 24.02.0
actually produced from it (2 <mask>, 3 <image>, 2 <path>, 4 <use>).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ation fallback

Uploads the same soft-masked sample PDF used by the bbb-common-web unit
spec and asserts the served slide SVG is the rasterized embedded-PNG form
produced by SvgImageCreatorImp.createSvgWithEmbeddedPng() - a single
<image href="data:image/png;base64,..."> and no vector <path> elements.
BBB 4.0 renders slides as tldraw image assets, so a visual snapshot
cannot tell a rasterized slide apart from a vector slide of the same
content; inspecting the served SVG file isolates the feature instead.

maskTagThreshold is a server-side bbb-web setting with no client-settings
hook, so nothing applies it automatically: the test requires bbb-web to
be restarted with maskTagThreshold=1 in /etc/bigbluebutton/
bbb-web.properties and is tagged @setting-required:maskTagThreshold,
which keeps it out of the default CI gate.

The mechanics (upload flow, conversion wait, served-SVG fetch via the
tl-image asset URL incl. pageToken/sessionToken query params, and the
embedded-PNG discriminator in both directions) were validated against a
live 4.0 server: a default-config upload serves pdftocairo vector SVG
(no match), and a forceRasterizeSlides=true upload serves the
embedded-PNG form (match).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Automated tests are running...

@antobinary antobinary closed this Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants