mirror of
https://github.com/daijro/camoufox.git
synced 2026-09-09 08:01:15 +00:00
fix(stealth): perturb glyph advances only, not positioning offsets (#741)
The font-spacing randomisation in anti-font-fingerprinting.patch fed a
running total into each glyph's x_offset/y_offset as well as its advance:
glyphPositions[i].x_advance += spacing;
glyphPositions[i].x_offset += cumulativeOffset;
cumulativeOffset += spacing;
x_offset is the GPOS positioning offset -- the thing that parks a combining
mark over its base. It does not contribute to the measured width, so it adds
nothing to the metric the randomisation exists to perturb, but layout never
sees it either: gfxTextRun stores advances, while painting honours the
offsets. The running total therefore paints every glyph a further `spacing`
past where layout placed it, and detaches every combining mark from its base.
Latin hides this because Gecko's word cache shapes each space-delimited word
separately, so the accumulator resets constantly. Thai has no inter-word
spaces, so a whole paragraph is a single shaping run and the error grows
without bound. Measured on 152.0.4-beta.29 at the worst-case seed, painted
text overran its layout box by:
Thai 1x run 4.90px Latin 1x run 0.00px
Thai 2x run 9.80px Latin 2x run 0.00px
Thai 4x run 20.60px Latin 4x run 0.00px
which is the garbled Thai in #741 -- and, since the seed is randomised per
launch, why it looks like an intermittent regression rather than a bug.
Perturb advances only, and skip zero-advance glyphs: a combining mark is not
a separate character, and widening it pushes the following base away and
strands the mark. Every advancing glyph still gets +spacing, so the measured
width -- the actual fingerprinting signal -- is perturbed exactly as before
(verified: 4/8/16/32px by run length, unchanged).
After the fix the overrun is 0.00px for Thai at every run length.
This commit is contained in:
@@ -851,7 +851,7 @@ index e41408f51c..ffa0bd4caf 100644
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -1465,6 +1469,36 @@ bool gfxHarfBuzzShaper::ShapeText(const char16_t* aText, uint32_t aOffset,
|
||||
@@ -1465,6 +1469,50 @@ bool gfxHarfBuzzShaper::ShapeText(const char16_t* aText, uint32_t aOffset,
|
||||
|
||||
hb_shape(mHBFont, mBuffer, features.Elements(), features.Length());
|
||||
|
||||
@@ -870,18 +870,32 @@ index e41408f51c..ffa0bd4caf 100644
|
||||
+ hb_glyph_position_t* glyphPositions =
|
||||
+ hb_buffer_get_glyph_positions(mBuffer, &glyphCount);
|
||||
+
|
||||
+ hb_position_t cumulativeOffset = 0;
|
||||
+
|
||||
+ // Apply custom letter spacing
|
||||
+ // Perturb ADVANCES only.
|
||||
+ //
|
||||
+ // x_offset/y_offset are the positioning offsets GPOS uses to place a glyph
|
||||
+ // relative to the pen -- most importantly to park a combining mark over
|
||||
+ // its base. They do not contribute to the measured width, so they add
|
||||
+ // nothing to the metric we are perturbing, but layout never sees them:
|
||||
+ // gfxTextRun stores advances, while painting honours the offsets. Feeding
|
||||
+ // a running total into them paints every glyph a further `spacing` past
|
||||
+ // where layout placed it and detaches every combining mark from its base.
|
||||
+ // Scripts whose marks carry a zero advance -- Thai, Lao, Arabic,
|
||||
+ // Devanagari, Hebrew -- collapse into stacked glyphs, while Latin merely
|
||||
+ // looks slightly loose (daijro/camoufox#741).
|
||||
+ //
|
||||
+ // Zero-advance glyphs are skipped for the same reason: a combining mark is
|
||||
+ // not a separate character, and widening it pushes the following base away
|
||||
+ // and strands the mark. Every advancing glyph still gets +spacing, so the
|
||||
+ // measured width -- the actual fingerprinting signal -- is perturbed
|
||||
+ // exactly as before.
|
||||
+ for (uint32_t i = 0; i < glyphCount; ++i) {
|
||||
+ if (aVertical) {
|
||||
+ glyphPositions[i].y_advance -= spacing;
|
||||
+ glyphPositions[i].y_offset -= cumulativeOffset;
|
||||
+ } else {
|
||||
+ if (glyphPositions[i].y_advance != 0) {
|
||||
+ glyphPositions[i].y_advance -= spacing;
|
||||
+ }
|
||||
+ } else if (glyphPositions[i].x_advance != 0) {
|
||||
+ glyphPositions[i].x_advance += spacing;
|
||||
+ glyphPositions[i].x_offset += cumulativeOffset;
|
||||
+ }
|
||||
+ cumulativeOffset += spacing;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
|
||||
Reference in New Issue
Block a user