Turn audio into a shareable video. forked from nypublicradio/audiogram

text-wrapper.js 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. var smartquotes = require("smartquotes").string;
  2. function wrapSubtitleText(theme, context, subtitle) {
  3. var left = ifNumeric(theme.subtitleLeft, 0),
  4. right = ifNumeric(theme.subtitleRight, theme.width),
  5. bottom = ifNumeric(theme.subtitleBottom, null),
  6. top = ifNumeric(theme.subtitleTop, null);
  7. if (bottom === null && top === null) {
  8. top = 0;
  9. }
  10. var subtitleWidth = right - left;
  11. if (!subtitle) {
  12. return;
  13. }
  14. var lines = [[]],
  15. maxWidth = 0,
  16. words = smartquotes(subtitle + "").trim().replace(/\s\s+/g, " \n").split(/ /g);
  17. context.font = theme.subtitleFont;
  18. context.textBaseline = "top";
  19. context.textAlign = theme.subtitleAlign || "right";
  20. // Check whether each word exceeds the width limit
  21. // Wrap onto next line as needed
  22. words.forEach(function(word,i){
  23. var width = context.measureText(lines[lines.length - 1].concat([word]).join(" ")).width;
  24. if (word[0] === "\n" || (lines[lines.length - 1].length && width > subtitleWidth)) {
  25. word = word.trim();
  26. lines.push([word]);
  27. width = context.measureText(word).width;
  28. } else {
  29. lines[lines.length - 1].push(word);
  30. }
  31. maxWidth = Math.max(maxWidth,width);
  32. });
  33. var totalHeight = lines.length * theme.subtitleLineHeight + (lines.length - 1) * theme.subtitleLineSpacing;
  34. // horizontal alignment
  35. var x = theme.subtitleAlign === "left" ? left : theme.subtitleAlign === "right" ? right : (left + right) / 2;
  36. // Vertical alignment
  37. var y;
  38. if (top !== null && bottom !== null) {
  39. // Vertical center
  40. y = (bottom + top - totalHeight) / 2;
  41. } else if (bottom !== null) {
  42. // Vertical align bottom
  43. y = bottom - totalHeight;
  44. } else {
  45. // Vertical align top
  46. y = top;
  47. }
  48. context.fillStyle = theme.subtitleColor;
  49. lines.forEach(function(line, i){
  50. context.fillText(line.join(" "), x, y + i * (theme.subtitleLineHeight + theme.subtitleLineSpacing));
  51. });
  52. }
  53. module.exports = function(theme) {
  54. // Do some typechecking
  55. var left = ifNumeric(theme.captionLeft, 0),
  56. right = ifNumeric(theme.captionRight, theme.width),
  57. bottom = ifNumeric(theme.captionBottom, null),
  58. top = ifNumeric(theme.captionTop, null);
  59. if (bottom === null && top === null) {
  60. top = 0;
  61. }
  62. var captionWidth = right - left;
  63. const self = this;
  64. self.theme = theme;
  65. return function(context, caption, subtitle) {
  66. if (subtitle) {
  67. return wrapSubtitleText(self.theme, context, subtitle);
  68. }
  69. if (!caption) {
  70. return;
  71. }
  72. var lines = [[]],
  73. maxWidth = 0,
  74. words = smartquotes(caption + "").trim().replace(/\s\s+/g, " \n").split(/ /g);
  75. context.font = theme.captionFont;
  76. context.textBaseline = "top";
  77. context.textAlign = theme.captionAlign || "center";
  78. // Check whether each word exceeds the width limit
  79. // Wrap onto next line as needed
  80. words.forEach(function(word,i){
  81. var width = context.measureText(lines[lines.length - 1].concat([word]).join(" ")).width;
  82. if (word[0] === "\n" || (lines[lines.length - 1].length && width > captionWidth)) {
  83. word = word.trim();
  84. lines.push([word]);
  85. width = context.measureText(word).width;
  86. } else {
  87. lines[lines.length - 1].push(word);
  88. }
  89. maxWidth = Math.max(maxWidth,width);
  90. });
  91. var totalHeight = lines.length * theme.captionLineHeight + (lines.length - 1) * theme.captionLineSpacing;
  92. // horizontal alignment
  93. var x = theme.captionAlign === "left" ? left : theme.captionAlign === "right" ? right : (left + right) / 2;
  94. // Vertical alignment
  95. var y;
  96. if (top !== null && bottom !== null) {
  97. // Vertical center
  98. y = (bottom + top - totalHeight) / 2;
  99. } else if (bottom !== null) {
  100. // Vertical align bottom
  101. y = bottom - totalHeight;
  102. } else {
  103. // Vertical align top
  104. y = top;
  105. }
  106. context.fillStyle = theme.captionColor;
  107. lines.forEach(function(line, i){
  108. context.fillText(line.join(" "), x, y + i * (theme.captionLineHeight + theme.captionLineSpacing));
  109. });
  110. };
  111. }
  112. function ifNumeric(val, alt) {
  113. return (typeof val === "number" && !isNaN(val)) ? val : alt;
  114. }