classic_song_slide.py 6.0 KB

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