Synthetic Bold/Slant

Synthetic Bold and Slant

When a font family does not include a bold or italic face, the shaper can simulate these styles synthetically. The adjustments are applied during shaping — the resulting glyph positions already include the correct advances and origins.

⚠️
Synthetic bold and slant are approximations. Whenever possible, use a proper bold or italic font face for better typographic quality.

Quick Start

shaper, _ := ot.NewShaper(font)

// Enable synthetic bold before shaping
shaper.SetSyntheticBold(0.02, 0.02, false)

buf := ot.NewBuffer()
buf.AddString("Bold text")
buf.GuessSegmentProperties()
shaper.Shape(buf, nil)
// buf.Pos now contains bold-adjusted positions

Synthetic Bold

SetSyntheticBold

func (s *Shaper) SetSyntheticBold(x, y float32, inPlace bool)

Sets the embolden strength in em-units (fractions of units-per-em). Typical values range from 0.01 (subtle) to 0.05 (heavy).

Parameter Type Description
x float32 Horizontal embolden strength
y float32 Vertical embolden strength
inPlace bool If true, advances and origins stay unchanged

Internally, the em-unit values are converted to font units: xStrength = round(upem * x). For a font with 1000 upem and x = 0.02, the horizontal strength is 20 font units.

Call SetSyntheticBold before Shape().

How Bold Affects Shaping

Horizontal text (inPlace = false): Each non-zero horizontal advance is widened by xStrength.

Vertical text (inPlace = false):

  • Vertical advance (with vmtx): becomes larger (more negative) by yStrength
  • Vertical origin X: recomputed as (hAdvance + xStrength) / 2 + xStrength
  • Vertical origin Y: shifted by yStrength

In-place mode (inPlace = true): Advances and origins are not modified. Only glyph extents and drawing outlines are affected — useful for rendering bolder glyphs without changing text layout.

Choosing Bold Values

Desired Effect Suggested Value Notes
Semibold 0.01 – 0.015 Subtle thickening
Bold 0.02 – 0.03 Standard synthetic bold
Extra-bold 0.04 – 0.05 Heavy, use with care

The x and y values do not have to be identical. Using a smaller y than x can produce a more natural look for some fonts.

SyntheticBold (getter)

func (s *Shaper) SyntheticBold() (x, y float32, inPlace bool)

Synthetic Slant

SetSyntheticSlant

func (s *Shaper) SetSyntheticSlant(slant float32)

Sets the shear factor for synthetic oblique. A value of 0.2 tilts glyphs by approximately 11.3° (atan(0.2)).

ℹ️
Slant has no effect on glyph advances or origins. It only affects glyph extents and drawing/rendering. This matches HarfBuzz behavior.
Desired Effect Suggested Value Approximate Angle
Subtle oblique 0.1 ~5.7°
Standard italic 0.2 ~11.3°
Strong italic 0.3 ~16.7°

The slant value is stored on the shaper for downstream rendering code:

slant := shaper.SyntheticSlant()
// Apply shear transform: [1, slant, 0, 1, 0, 0]

SyntheticSlant (getter)

func (s *Shaper) SyntheticSlant() float32

Combining Bold and Slant

shaper.SetSyntheticBold(0.02, 0.02, false)
shaper.SetSyntheticSlant(0.2)
shaper.Shape(buf, nil)

Both can be reset independently:

shaper.SetSyntheticBold(0, 0, false) // Reset bold
shaper.SetSyntheticSlant(0)          // Reset slant

Settings persist on the shaper between Shape() calls. You can change them between calls to shape normal, bold, italic, and bold-italic text with the same shaper.

HarfBuzz Compatibility

textshape HarfBuzz C
SetSyntheticBold(x, y, inPlace) hb_font_set_synthetic_bold(font, x, y, inPlace)
SetSyntheticSlant(slant) hb_font_set_synthetic_slant(font, slant)