diff options
author | Paul van Brouwershaven <vanbroup@users.noreply.github.com> | 2021-12-07 11:29:55 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-07 11:29:55 +0100 |
commit | 283394a4fde14476db576a15b7bcc472d693d76f (patch) | |
tree | 6d3ed28edfaecb0ec70fedad4eba397a309a4c0b /resources/images | |
parent | 5538507e9053a00faa358b92ad0bb004e8d28daf (diff) |
images: Text filter that draws text with the given options (#9239)
Fixes #9238
Diffstat (limited to 'resources/images')
-rw-r--r-- | resources/images/filters.go | 39 | ||||
-rw-r--r-- | resources/images/text.go | 94 |
2 files changed, 133 insertions, 0 deletions
diff --git a/resources/images/filters.go b/resources/images/filters.go index 74c50363e..c933e733b 100644 --- a/resources/images/filters.go +++ b/resources/images/filters.go @@ -15,6 +15,8 @@ package images import ( + "github.com/gohugoio/hugo/common/maps" + "github.com/disintegration/gift" "github.com/spf13/cast" ) @@ -33,6 +35,43 @@ func (*Filters) Overlay(src ImageSource, x, y interface{}) gift.Filter { } } +// Text creates a filter that draws text with the given options. +func (*Filters) Text(text string, options ...interface{}) gift.Filter { + tf := textFilter{ + text: text, + color: "#ffffff", + size: 20, + x: 10, + y: 10, + linespacing: 2, + } + + var opt map[string]interface{} + if len(options) > 0 { + opt := maps.MustToParamsAndPrepare(options[0]) + for option, v := range opt { + switch option { + case "color": + tf.color = cast.ToString(v) + case "size": + tf.size = cast.ToFloat64(v) + case "x": + tf.x = cast.ToInt(v) + case "y": + tf.y = cast.ToInt(v) + case "linespacing": + tf.linespacing = cast.ToInt(v) + } + + } + } + + return filter{ + Options: newFilterOpts(text, opt), + Filter: tf, + } +} + // Brightness creates a filter that changes the brightness of an image. // The percentage parameter must be in range (-100, 100). func (*Filters) Brightness(percentage interface{}) gift.Filter { diff --git a/resources/images/text.go b/resources/images/text.go new file mode 100644 index 000000000..a90f25272 --- /dev/null +++ b/resources/images/text.go @@ -0,0 +1,94 @@ +// Copyright 2021 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package images + +import ( + "image" + "image/draw" + "strings" + + "github.com/disintegration/gift" + + "golang.org/x/image/font" + "golang.org/x/image/font/gofont/goregular" + "golang.org/x/image/font/opentype" + "golang.org/x/image/math/fixed" +) + +var _ gift.Filter = (*textFilter)(nil) + +type textFilter struct { + text, color string + x, y int + size float64 + linespacing int +} + +func (f textFilter) Draw(dst draw.Image, src image.Image, options *gift.Options) { + color, err := hexStringToColor(f.color) + if err != nil { + panic(err) + } + + // Load and parse font + ttf := goregular.TTF + otf, err := opentype.Parse(ttf) + if err != nil { + panic(err) + } + + // Set font options + face, err := opentype.NewFace(otf, &opentype.FaceOptions{ + Size: f.size, + DPI: 72, + Hinting: font.HintingNone, + }) + if err != nil { + panic(err) + } + + d := font.Drawer{ + Dst: dst, + Src: image.NewUniform(color), + Face: face, + } + + gift.New().Draw(dst, src) + + // Draw text, consider and include linebreaks + maxWidth := dst.Bounds().Dx() - 20 + fontHeight := face.Metrics().Ascent.Ceil() + + // Correct y position based on font and size + f.y = f.y + fontHeight + + // Start position + y := f.y + d.Dot = fixed.P(f.x, f.y) + + // Draw text and break line at max width + parts := strings.Fields(f.text) + for _, str := range parts { + strWith := font.MeasureString(face, str) + if (d.Dot.X.Ceil() + strWith.Ceil()) >= maxWidth { + y = y + fontHeight + f.linespacing + d.Dot = fixed.P(f.x, y) + } + d.DrawString(str + " ") + } +} + +func (f textFilter) Bounds(srcBounds image.Rectangle) image.Rectangle { + return image.Rect(0, 0, srcBounds.Dx(), srcBounds.Dy()) +} |