Gradient Text
A gradient in the creator is a Color, not a separate drawing operation. Creator.NewLinearGradientColor
and Creator.NewRadialGradientColor return values that satisfy the same Color interface as
creator.ColorBlack, so anywhere a flat color goes a gradient goes as well. Assign one to
chunk.Style.Color and the glyphs are filled with a shading pattern instead of a solid ink.
The same values work as the fill color of a rectangle, ellipse, polygon, curve or border. Text is just the case where the result is most obviously useful.
Two stops and a direction
A gradient is a list of ColorPoint values: a color and its position from 0.0 at the start to
1.0 at the end.
gradient := c.NewLinearGradientColor([]*creator.ColorPoint{
creator.NewColorPoint(creator.ColorRGBFrom8bit(0xE5, 0x3E, 0x3E), 0.0),
creator.NewColorPoint(creator.ColorRGBFrom8bit(0x2B, 0x6C, 0xB0), 1.0),
})
p := c.NewStyledParagraph()
chunk := p.Append("Linear gradient heading")
chunk.Style.FontSize = 32
chunk.Style.Color = gradient
if err := c.Draw(p); err != nil {
return err
}That runs left to right. SetAngle rotates it, in degrees, and AddColorStop appends a stop
after construction if you would rather build the list up.
With exactly two stops the positions are not read - the first color is placed at 0.0 and the
last at 1.0 regardless of what you passed, so NewColorPoint(color, 0.3) in a two-stop list
does nothing. Positions start mattering at three stops, where the intermediate ones become the
bounds of a stitching function.
The radial constructor takes position and radii before the stops:
gradient := c.NewRadialGradientColor(0, 0, 0, -1, []*creator.ColorPoint{
creator.NewColorPoint(creator.ColorRGBFrom8bit(0xF6, 0xC3, 0x43), 0.0),
creator.NewColorPoint(creator.ColorRGBFrom8bit(0xC0, 0x39, 0x2B), 1.0),
})The first two arguments are an offset from an anchor point, not page coordinates. The anchor
defaults to the center of the area being filled, so 0, 0 centers the gradient on the text;
SetAnchor moves it to a corner or an edge. The last two are the inner and outer radius. An
outer radius of -1 sizes the gradient to the filled area. A fixed radius smaller than the text
leaves the glyphs outside the disc unpainted, which is a legitimate effect but easy to hit by
accident.
Extent follows the chunks that use the color
This is the part worth understanding before you start tuning numbers. A shading pattern is a field of color laid out in page space; the glyphs mask it. So what the gradient looks like depends on the box the pattern is given, and that box is derived from the paragraph after layout, not from anything you set.
Vertically the box is always the paragraph’s full height. Horizontally it is the union of the spans of the chunks that share that color value. The practical consequence:
| Where the color is assigned | Resulting extent |
|---|---|
| One chunk | That chunk’s horizontal span. The gradient completes within the chunk. |
| Several chunks, same value | The union of their spans, so the ramp continues across them. |
SetFontColor on the paragraph | The whole paragraph. |
Dedup is by value, so reusing one *LinearShading across chunks registers a single pattern
and produces one continuous ramp. Calling NewLinearGradientColor twice with identical stops
gives two patterns and two independent ramps. Mixed with flat chunks it composes as you would
expect:
flat := p.Append("Flat color, then ")
flat.Style.Color = creator.ColorBlack
graded := p.Append("a gradient chunk")
graded.Style.Color = gradient
trailing := p.Append(", then flat again.")
trailing.Style.Color = creator.ColorBlackThings that catch people out
The bundled image renderer does not paint pattern-filled text. It falls back to solid black,
so converting a page to an image through the render package loses the gradient. The PDF
itself is correct and viewers show it properly. If you are checking output in CI by rendering
to PNG, this looks like a bug in your code and is not.
Anti-aliasing is off by default; SetAntiAlias(true) turns it on. Extends are off too, meaning
the area beyond the first and last stop is left to the background rather than being filled with
the end colors. SetExtends(true, true) is usually what you want for a radial gradient with a
fixed outer radius, and SetBackgroundColor sets what shows through otherwise (white by
default).
SetBoundingBox exists on both shading types but calling it on a gradient used for text has no
effect. The paragraph overwrites the box during drawing.
Run the example
The example draws three paragraphs: a linear gradient heading, a radial one, and a paragraph mixing a gradient chunk between two flat ones.
git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/text
go run pdf_gradient_text.goIf this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.