summaryrefslogtreecommitdiffstats
path: root/resources/images
diff options
context:
space:
mode:
authorPaul van Brouwershaven <vanbroup@users.noreply.github.com>2021-12-07 11:29:55 +0100
committerGitHub <noreply@github.com>2021-12-07 11:29:55 +0100
commit283394a4fde14476db576a15b7bcc472d693d76f (patch)
tree6d3ed28edfaecb0ec70fedad4eba397a309a4c0b /resources/images
parent5538507e9053a00faa358b92ad0bb004e8d28daf (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.go39
-rw-r--r--resources/images/text.go94
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())
+}