classic_song_slide.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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 .song_slide import SongSlide
  20. class ClassicSongSlide(SongSlide):
  21. def get_slide(
  22. self,
  23. template_img: Image,
  24. slide_text: str,
  25. song_structure: list,
  26. index: int,
  27. use_arrow: bool,
  28. ) -> Image:
  29. canvas_img, font_size = self.get_text_canvas(slide_text)
  30. verse_or_chorus = song_structure[index]
  31. bg_img = template_img.clone()
  32. if "R" not in verse_or_chorus:
  33. bg_img.composite(
  34. self.get_index(verse_or_chorus, font_size),
  35. top=const.STRUCTURE_ELEMENT_Y,
  36. left=const.STRUCTURE_ELEMENT_X,
  37. )
  38. bg_img.composite(
  39. canvas_img, top=const.TEXT_CANVAS_Y, left=const.TEXT_CANVAS_X
  40. )
  41. if use_arrow:
  42. bg_img.composite(
  43. self.get_arrow(), top=const.ARROW_Y, left=const.ARROW_X
  44. )
  45. bg_img.composite(
  46. self.get_structure_info_display(song_structure, index),
  47. top=const.STRUCTURE_Y,
  48. left=const.STRUCTURE_X,
  49. )
  50. return bg_img.clone()
  51. def get_arrow(self) -> Image:
  52. with Drawing() as draw:
  53. draw.stroke_width = 1
  54. draw.stroke_color = Color(const.ARROW_COLOR)
  55. draw.fill_color = Color(const.ARROW_COLOR)
  56. arrow_width = const.ARROW_HEIGHT * 3 // 2
  57. draw.path_start()
  58. draw.path_move(
  59. to=(0, const.ARROW_HEIGHT / 2 - const.ARROW_HEIGHT / 10)
  60. )
  61. draw.path_line(
  62. to=(0, const.ARROW_HEIGHT / 2 + const.ARROW_HEIGHT / 10)
  63. )
  64. draw.path_line(
  65. to=(
  66. arrow_width / 3 * 2,
  67. const.ARROW_HEIGHT / 2 + const.ARROW_HEIGHT / 10,
  68. )
  69. )
  70. draw.path_line(to=(arrow_width / 3 * 2, const.ARROW_HEIGHT))
  71. draw.path_line(to=(arrow_width, const.ARROW_HEIGHT / 2))
  72. draw.path_line(to=(arrow_width / 3 * 2, 0))
  73. draw.path_line(
  74. to=(
  75. arrow_width / 3 * 2,
  76. const.ARROW_HEIGHT / 2 - const.ARROW_HEIGHT / 10,
  77. )
  78. )
  79. draw.path_close()
  80. draw.path_finish()
  81. with Image(
  82. width=arrow_width,
  83. height=const.ARROW_HEIGHT,
  84. background=Color(const.BG_COLOR),
  85. ) as image:
  86. draw(image)
  87. return image.clone()
  88. def get_text_canvas(self, slide_text: str) -> tuple[Image, int]:
  89. font_size = const.MAX_CANVAS_FONT_SIZE
  90. while font_size >= const.MIN_CANVAS_FONT_SIZE:
  91. with Drawing() as draw:
  92. draw.fill_color = Color(const.TEXT_COLOR)
  93. draw.text_interline_spacing = const.INTERLINE_SPACING
  94. draw.font_size = font_size
  95. draw.font = const.FONT
  96. draw.text(0, font_size, slide_text)
  97. with Image(
  98. width=const.WIDTH,
  99. height=const.HEIGHT,
  100. background=Color(const.BG_COLOR),
  101. ) as img:
  102. draw(img)
  103. img.trim()
  104. if (
  105. img.width > const.TEXT_CANVAS_WIDTH
  106. or img.height > const.TEXT_CANVAS_HEIGHT
  107. ):
  108. font_size -= const.CANVAS_FONT_SIZE_STEP
  109. else:
  110. return img.clone(), font_size
  111. return get_empty_image(), 0
  112. def get_structure_info_display(self, structure: list, index: int) -> Image:
  113. with Drawing() as draw:
  114. draw.fill_color = Color(const.TEXT_COLOR)
  115. draw.font_size = const.INFODISPLAY_FONT_SIZE
  116. draw.font = const.FONT
  117. for current_index, item in enumerate(structure):
  118. if current_index == index:
  119. draw.font = const.BOLD_FONT
  120. draw.text(
  121. current_index * const.INFODISPLAY_ITEM_WIDTH,
  122. const.INFODISPLAY_FONT_SIZE,
  123. item,
  124. )
  125. draw.font = const.FONT
  126. else:
  127. draw.text(
  128. current_index * const.INFODISPLAY_ITEM_WIDTH,
  129. const.INFODISPLAY_FONT_SIZE,
  130. item,
  131. )
  132. with Image(
  133. width=const.WIDTH,
  134. height=const.HEIGHT,
  135. background=Color(const.BG_COLOR),
  136. ) as img:
  137. draw(img)
  138. img.trim()
  139. return img.clone()
  140. def get_index(self, verse: str, font_size: int) -> Image:
  141. with Image(
  142. width=const.WIDTH,
  143. height=const.HEIGHT,
  144. background=Color(const.BG_COLOR),
  145. ) as img:
  146. img.caption(
  147. verse + ".",
  148. font=Font(
  149. const.FONT_PATH,
  150. size=font_size,
  151. color=Color(const.TEXT_COLOR),
  152. ),
  153. )
  154. img.trim()
  155. return img.clone()