URI: 
       text.go - hugo - [fork] hugo port for 9front
  HTML git clone https://git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
       text.go (3646B)
       ---
            1 // Copyright 2021 The Hugo Authors. All rights reserved.
            2 //
            3 // Licensed under the Apache License, Version 2.0 (the "License");
            4 // you may not use this file except in compliance with the License.
            5 // You may obtain a copy of the License at
            6 // http://www.apache.org/licenses/LICENSE-2.0
            7 //
            8 // Unless required by applicable law or agreed to in writing, software
            9 // distributed under the License is distributed on an "AS IS" BASIS,
           10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           11 // See the License for the specific language governing permissions and
           12 // limitations under the License.
           13 
           14 package images
           15 
           16 import (
           17         "image"
           18         "image/color"
           19         "image/draw"
           20         "io"
           21         "strings"
           22 
           23         "github.com/disintegration/gift"
           24         "github.com/gohugoio/hugo/common/hugio"
           25 
           26         "golang.org/x/image/font"
           27         "golang.org/x/image/font/gofont/goregular"
           28         "golang.org/x/image/font/opentype"
           29         "golang.org/x/image/math/fixed"
           30 )
           31 
           32 var _ gift.Filter = (*textFilter)(nil)
           33 
           34 type textFilter struct {
           35         text        string
           36         color       color.Color
           37         x, y        int
           38         alignx      string
           39         aligny      string
           40         size        float64
           41         linespacing int
           42         fontSource  hugio.ReadSeekCloserProvider
           43 }
           44 
           45 func (f textFilter) Draw(dst draw.Image, src image.Image, options *gift.Options) {
           46         // Load and parse font
           47         ttf := goregular.TTF
           48         if f.fontSource != nil {
           49                 rs, err := f.fontSource.ReadSeekCloser()
           50                 if err != nil {
           51                         panic(err)
           52                 }
           53                 defer rs.Close()
           54                 ttf, err = io.ReadAll(rs)
           55                 if err != nil {
           56                         panic(err)
           57                 }
           58         }
           59         otf, err := opentype.Parse(ttf)
           60         if err != nil {
           61                 panic(err)
           62         }
           63 
           64         // Set font options
           65         face, err := opentype.NewFace(otf, &opentype.FaceOptions{
           66                 Size:    f.size,
           67                 DPI:     72,
           68                 Hinting: font.HintingNone,
           69         })
           70         if err != nil {
           71                 panic(err)
           72         }
           73 
           74         d := font.Drawer{
           75                 Dst:  dst,
           76                 Src:  image.NewUniform(f.color),
           77                 Face: face,
           78         }
           79 
           80         gift.New().Draw(dst, src)
           81 
           82         maxWidth := dst.Bounds().Dx() - 20
           83 
           84         var availableWidth int
           85         switch f.alignx {
           86         case "right":
           87                 availableWidth = f.x
           88         case "center":
           89                 availableWidth = min((maxWidth-f.x), f.x) * 2
           90         case "left":
           91                 availableWidth = maxWidth - f.x
           92         }
           93 
           94         fontHeight := face.Metrics().Ascent.Ceil()
           95 
           96         // Calculate lines, consider and include linebreaks
           97         finalLines := []string{}
           98         f.text = strings.ReplaceAll(f.text, "\r", "")
           99         for _, line := range strings.Split(f.text, "\n") {
          100                 currentLine := ""
          101                 // Break each line at the maximum width.
          102                 for _, str := range strings.Fields(line) {
          103                         fieldStrWidth := font.MeasureString(face, str)
          104                         currentLineStrWidth := font.MeasureString(face, currentLine)
          105 
          106                         if (currentLineStrWidth.Ceil() + fieldStrWidth.Ceil()) >= availableWidth {
          107                                 finalLines = append(finalLines, currentLine)
          108                                 currentLine = ""
          109                         }
          110                         currentLine += str + " "
          111                 }
          112                 finalLines = append(finalLines, currentLine)
          113         }
          114         // Total height of the text from the top of the first line to the baseline of the last line
          115         totalHeight := len(finalLines)*fontHeight + (len(finalLines)-1)*f.linespacing
          116 
          117         // Correct y position based on font and size
          118         y := f.y + fontHeight
          119         switch f.aligny {
          120         case "top":
          121                 // Do nothing
          122         case "center":
          123                 y = y - totalHeight/2
          124         case "bottom":
          125                 y = y - totalHeight
          126         }
          127 
          128         // Draw text line by line
          129         for _, line := range finalLines {
          130                 line = strings.TrimSpace(line)
          131                 strWidth := font.MeasureString(face, line)
          132                 var x int
          133                 switch f.alignx {
          134                 case "right":
          135                         x = f.x - strWidth.Ceil()
          136                 case "center":
          137                         x = f.x - (strWidth.Ceil() / 2)
          138 
          139                 case "left":
          140                         x = f.x
          141                 }
          142                 d.Dot = fixed.P(x, y)
          143                 d.DrawString(line)
          144                 y = y + fontHeight + f.linespacing
          145         }
          146 }
          147 
          148 func (f textFilter) Bounds(srcBounds image.Rectangle) image.Rectangle {
          149         return image.Rect(0, 0, srcBounds.Dx(), srcBounds.Dy())
          150 }