classic_song_slide.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. """
  2. Copyright © 2022 Noah Vogt <noah@noahvogt.com>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. """
  14. from wand.image import Image
  15. from wand.drawing import Drawing
  16. from wand.font import Font
  17. from wand.color import Color
  18. import config as const
  19. from utils import get_empty_image
  20. from .song_slide import SongSlide
  21. class ClassicSongSlide(SongSlide):
  22. def get_slide(
  23. self,
  24. template_img: Image,
  25. slide_text: str,
  26. song_structure: list,
  27. index: int,
  28. use_arrow: bool,
  29. ) -> Image:
  30. canvas_img, font_size = self.get_text_canvas(slide_text)
  31. verse_or_chorus = song_structure[index]
  32. bg_img = template_img.clone()
  33. if "R" not in verse_or_chorus:
  34. bg_img.composite(
  35. self.get_index(verse_or_chorus, font_size),
  36. top=const.STRUCTURE_ELEMENT_Y,
  37. left=const.STRUCTURE_ELEMENT_X,
  38. )
  39. bg_img.composite(
  40. canvas_img, top=const.TEXT_CANVAS_Y, left=const.TEXT_CANVAS_X
  41. )
  42. if use_arrow:
  43. bg_img.composite(
  44. self.get_arrow(), top=const.ARROW_Y, left=const.ARROW_X
  45. )
  46. bg_img.composite(
  47. self.get_structure_info_display(song_structure, index),
  48. top=const.INFODISPLAY_Y,
  49. left=const.INFODISPLAY_X,
  50. )
  51. return bg_img.clone()
  52. def get_arrow(self) -> Image:
  53. with Drawing() as draw:
  54. draw.stroke_width = 1
  55. draw.stroke_color = Color(const.ARROW_COLOR)
  56. draw.fill_color = Color(const.ARROW_COLOR)
  57. arrow_width = const.ARROW_HEIGHT * 3 // 2
  58. draw.path_start()
  59. draw.path_move(
  60. to=(0, const.ARROW_HEIGHT / 2 - const.ARROW_HEIGHT / 10)
  61. )
  62. draw.path_line(
  63. to=(0, const.ARROW_HEIGHT / 2 + const.ARROW_HEIGHT / 10)
  64. )
  65. draw.path_line(
  66. to=(
  67. arrow_width / 3 * 2,
  68. const.ARROW_HEIGHT / 2 + const.ARROW_HEIGHT / 10,
  69. )
  70. )
  71. draw.path_line(to=(arrow_width / 3 * 2, const.ARROW_HEIGHT))
  72. draw.path_line(to=(arrow_width, const.ARROW_HEIGHT / 2))
  73. draw.path_line(to=(arrow_width / 3 * 2, 0))
  74. draw.path_line(
  75. to=(
  76. arrow_width / 3 * 2,
  77. const.ARROW_HEIGHT / 2 - const.ARROW_HEIGHT / 10,
  78. )
  79. )
  80. draw.path_close()
  81. draw.path_finish()
  82. with Image(
  83. width=arrow_width,
  84. height=const.ARROW_HEIGHT,
  85. background=Color(const.BG_COLOR),
  86. ) as image:
  87. draw(image)
  88. return image.clone()
  89. def get_text_canvas(self, slide_text: str) -> tuple[Image, int]:
  90. font_size = const.MAX_CANVAS_FONT_SIZE
  91. while font_size >= const.MIN_CANVAS_FONT_SIZE:
  92. with Drawing() as draw:
  93. draw.fill_color = Color(const.TEXT_COLOR)
  94. draw.text_interline_spacing = const.INTERLINE_SPACING
  95. draw.font_size = font_size
  96. draw.font = const.FONT
  97. draw.text(0, font_size, slide_text)
  98. with Image(
  99. width=const.WIDTH,
  100. height=const.HEIGHT,
  101. background=Color(const.BG_COLOR),
  102. ) as img:
  103. draw(img)
  104. img.trim()
  105. if (
  106. img.width > const.TEXT_CANVAS_WIDTH
  107. or img.height > const.TEXT_CANVAS_HEIGHT
  108. ):
  109. font_size -= const.CANVAS_FONT_SIZE_STEP
  110. else:
  111. return img.clone(), font_size
  112. return get_empty_image(), 0
  113. def get_structure_info_display(self, structure: list, index: int) -> Image:
  114. with Drawing() as draw:
  115. draw.fill_color = Color(const.TEXT_COLOR)
  116. draw.font_size = const.INFODISPLAY_FONT_SIZE
  117. draw.font = const.FONT
  118. for current_index, item in enumerate(structure):
  119. if current_index == index:
  120. draw.font = const.BOLD_FONT
  121. draw.text(
  122. current_index * const.INFODISPLAY_ITEM_WIDTH,
  123. const.INFODISPLAY_FONT_SIZE,
  124. item,
  125. )
  126. draw.font = const.FONT
  127. else:
  128. draw.text(
  129. current_index * const.INFODISPLAY_ITEM_WIDTH,
  130. const.INFODISPLAY_FONT_SIZE,
  131. item,
  132. )
  133. with Image(
  134. width=const.WIDTH,
  135. height=const.HEIGHT,
  136. background=Color(const.BG_COLOR),
  137. ) as img:
  138. draw(img)
  139. img.trim()
  140. return img.clone()
  141. def get_index(self, verse: str, font_size: int) -> Image:
  142. with Image(
  143. width=const.WIDTH,
  144. height=const.HEIGHT,
  145. background=Color(const.BG_COLOR),
  146. ) as img:
  147. img.caption(
  148. verse + ".",
  149. font=Font(
  150. const.FONT_PATH,
  151. size=font_size,
  152. color=Color(const.TEXT_COLOR),
  153. ),
  154. )
  155. img.trim()
  156. return img.clone()