Convert WriteFreely quotes to Instagram / PixelFed posts.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

131 Zeilen
3.0 KiB

  1. /*
  2. * Copyright © 2021 A Bunch Tell LLC.
  3. *
  4. * This file is part of text-pic.
  5. *
  6. * text-pic is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License, included
  8. * in the LICENSE file in this source code package.
  9. */
  10. package textpic
  11. import (
  12. "fmt"
  13. "image/color"
  14. "path/filepath"
  15. "github.com/fogleman/gg"
  16. )
  17. func loadFont(dc *gg.Context, bold bool, points float64) error {
  18. fontLoraBoldPath := filepath.Join("fonts", "Lora", "Lora-Bold.ttf")
  19. fontLoraPath := filepath.Join("fonts", "Lora", "Lora-Regular.ttf")
  20. path := fontLoraPath
  21. if bold {
  22. path = fontLoraBoldPath
  23. }
  24. err := dc.LoadFontFace(path, points)
  25. if err != nil {
  26. return fmt.Errorf("load bold font: %s", err)
  27. }
  28. return nil
  29. }
  30. func GenerateImage(opt *ContentOptions, outputFilename string) error {
  31. w := 900
  32. h := 900
  33. wf := float64(w)
  34. hf := float64(h)
  35. dc := gg.NewContext(w, h)
  36. dc.DrawRectangle(0, 0, wf, hf)
  37. dc.SetRGB(1, 1, 1)
  38. dc.Fill()
  39. // Define margins for footer text
  40. footerFontSize := 32.0
  41. footerMargin := 20.0
  42. x := footerMargin
  43. y := footerMargin
  44. footerMarginY := 20.0
  45. // Content parameters
  46. contentFontSize := 48.0
  47. lineSpacing := 1.8
  48. contentBottomMargin := 100.0
  49. contentRightMargin := 50.0
  50. contentTopMargin := 50.0
  51. contentWidth := wf - contentRightMargin - contentRightMargin
  52. // Create bold instance name
  53. err := loadFont(dc, true, footerFontSize)
  54. if err != nil {
  55. return err
  56. }
  57. instance := opt.Instance
  58. baseTextWidth, textHeight := dc.MeasureString(instance)
  59. // Create user path
  60. err = loadFont(dc, false, footerFontSize)
  61. if err != nil {
  62. return err
  63. }
  64. dc.SetColor(color.Black)
  65. var userPath = ""
  66. if opt.Username != "" {
  67. userPath = "/" + opt.Username
  68. }
  69. userTextWidth, _ := dc.MeasureString(userPath)
  70. // x = canvas halfway point - total text width halfway point
  71. x = wf/2 - (baseTextWidth+userTextWidth)/2
  72. y = hf - textHeight - footerMarginY
  73. err = loadFont(dc, true, footerFontSize)
  74. if err != nil {
  75. return err
  76. }
  77. dc.DrawString(instance, x, y)
  78. // x = original x coordinate + base text width
  79. x += baseTextWidth
  80. y = hf - textHeight - footerMarginY
  81. err = loadFont(dc, false, footerFontSize)
  82. if err != nil {
  83. return err
  84. }
  85. dc.DrawString(userPath, x, y)
  86. // Draw the content
  87. err = loadFont(dc, false, contentFontSize)
  88. if err != nil {
  89. return err
  90. }
  91. s := opt.Content
  92. lines := dc.WordWrap(s, contentWidth)
  93. // Center-align text by default for short content
  94. alignment := gg.AlignCenter
  95. linesStr := ""
  96. for i, str := range lines {
  97. linesStr += str
  98. if i != len(lines)-1 {
  99. linesStr += "\n"
  100. if alignment == gg.AlignCenter {
  101. // Since content uses multiple lines, left-align it.
  102. alignment = gg.AlignLeft
  103. }
  104. }
  105. }
  106. _, contentTextHeight := dc.MeasureMultilineString(linesStr, lineSpacing)
  107. x = contentRightMargin
  108. y = contentTopMargin - contentBottomMargin + hf/2 - contentTextHeight/2
  109. dc.DrawStringWrapped(s, x, y, 0, 0, contentWidth, lineSpacing, alignment)
  110. err = dc.SavePNG(outputFilename)
  111. if err != nil {
  112. return fmt.Errorf("save png: %s", err)
  113. }
  114. return nil
  115. }