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

text-wrapper.js 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. var smartquotes = require("smartquotes").string;
  2. module.exports = function(theme) {
  3. // Do some typechecking
  4. var left = ifNumeric(theme.captionLeft, 0),
  5. right = ifNumeric(theme.captionRight, theme.width),
  6. bottom = ifNumeric(theme.captionBottom, null),
  7. top = ifNumeric(theme.captionTop, null),
  8. citationLeft = ifNumeric(theme.citationLeft, 0),
  9. citationRight = ifNumeric(theme.citationRight, theme.width),
  10. citationBottom = ifNumeric(theme.citationBottom, null),
  11. citationTop = ifNumeric(theme.citationTop, null),
  12. labelLeft = ifNumeric(theme.labelLeft, 0),
  13. labelRight = ifNumeric(theme.labelRight, theme.width),
  14. labelBottom = ifNumeric(theme.labelBottom, null),
  15. labelTop = ifNumeric(theme.labelTop, null),
  16. renderfunctions ;
  17. if (bottom === null && top === null) {
  18. top = 0;
  19. }
  20. if (labelBottom === null && labelTop === null) {
  21. labelTop = 0;
  22. }
  23. var captionWidth = right - left,
  24. citationWidth = citationRight - citationLeft;
  25. labelWidth = labelRight - labelLeft;
  26. return function(context, caption, type) {
  27. if (!caption) {
  28. return;
  29. }
  30. if (type === 'caption') {
  31. var lines = [[]],
  32. maxWidth = 0,
  33. words = smartquotes(caption + "").trim().replace(/\s\s+/g, " \n").split(/ /g);
  34. context.font = theme.captionFont;
  35. context.textBaseline = "top";
  36. context.textAlign = theme.captionAlign || "center";
  37. // Check whether each word exceeds the width limit
  38. // Wrap onto next line as needed
  39. words.forEach(function(word,i){
  40. var width = context.measureText(lines[lines.length - 1].concat([word]).join(" ")).width;
  41. if (word[0] === "\n" || (lines[lines.length - 1].length && width > captionWidth)) {
  42. word = word.trim();
  43. lines.push([word]);
  44. width = context.measureText(word).width;
  45. } else {
  46. lines[lines.length - 1].push(word);
  47. }
  48. maxWidth = Math.max(maxWidth,width);
  49. });
  50. var totalHeight = lines.length * theme.captionLineHeight + (lines.length - 1) * theme.captionLineSpacing;
  51. // horizontal alignment
  52. var x = theme.captionAlign === "left" ? left : theme.captionAlign === "right" ? right : (left + right) / 2;
  53. // Vertical alignment
  54. var y;
  55. if (top !== null && bottom !== null) {
  56. // Vertical center
  57. y = (bottom + top - totalHeight) / 2;
  58. } else if (bottom !== null) {
  59. // Vertical align bottom
  60. y = bottom - totalHeight;
  61. } else {
  62. // Vertical align top
  63. y = top;
  64. }
  65. // draw caption
  66. context.fillStyle = theme.captionColor;
  67. lines.forEach(function(line, i){
  68. // negative indentation for opening quotes
  69. var indented_x = (x + 28);
  70. if (i === 0 && /^“/.test(line[0])) {
  71. context.fillText(line.join(" "), x, y + i * (theme.captionLineHeight + theme.captionLineSpacing));
  72. }
  73. else {
  74. context.fillText(line.join(" "), indented_x, y + i * (theme.captionLineHeight + theme.captionLineSpacing));
  75. }
  76. });
  77. } // end if caption
  78. if (type === 'citation') {
  79. var lines = [[]],
  80. maxWidth = 0,
  81. words = smartquotes(caption + "").trim().replace(/\s\s+/g, " \n").split(/ /g);
  82. context.font = theme.citationFont;
  83. context.textBaseline = "top";
  84. context.textAlign = theme.citationAlign || "center";
  85. // Check whether each word exceeds the width limit
  86. // Wrap onto next line as needed
  87. words.forEach(function(word,i){
  88. var width = context.measureText(lines[lines.length - 1].concat([word]).join(" ")).width;
  89. if (word[0] === "\n" || (lines[lines.length - 1].length && width > citationWidth)) {
  90. word = word.trim();
  91. lines.push([word]);
  92. width = context.measureText(word).width;
  93. } else {
  94. word = (i === 0) ? '— ' + word : word;
  95. lines[lines.length - 1].push(word);
  96. }
  97. maxWidth = Math.max(maxWidth,width);
  98. });
  99. var totalHeight = lines.length * theme.citationLineHeight + (lines.length - 1) * theme.citationLineSpacing;
  100. // horizontal alignment
  101. var x = theme.citationAlign === "left" ? left : theme.citationAlign === "right" ? right : (left + right) / 2;
  102. // Vertical alignment
  103. var y;
  104. if (citationTop !== null && citationBottom !== null) {
  105. // Vertical center
  106. y = (citationBottom + citationTop - totalHeight) / 2;
  107. } else if (citationBottom !== null) {
  108. // Vertical align bottom
  109. y = citationBottom - totalHeight;
  110. } else {
  111. // Vertical align top
  112. y = citationTop;
  113. }
  114. // draw citation
  115. context.fillStyle = theme.citationColor;
  116. lines.forEach(function(line, i){
  117. // negative indentation for opening em dash
  118. var indented_x = (x + 50);
  119. if (i === 0 && /^—/.test(line[0])) {
  120. context.fillText(line.join(" "), x, y + i * (theme.citationLineHeight + theme.citationLineSpacing));
  121. }
  122. else {
  123. context.fillText(line.join(" "), indented_x, y + i * (theme.citationLineHeight + theme.citationLineSpacing));
  124. }
  125. });
  126. } // end if citation
  127. if (type === 'label' && caption != 'None') {
  128. var lines = [[]],
  129. maxWidth = 0,
  130. words = smartquotes(caption + "").trim().replace(/\s\s+/g, " \n").split(/ /g);
  131. context.font = theme.labelFont;
  132. context.textBaseline = "top";
  133. context.textAlign = theme.labelAlign || "center";
  134. // Check whether each word exceeds the width limit
  135. // Wrap onto next line as needed
  136. words.forEach(function(word,i){
  137. var width = context.measureText(lines[lines.length - 1].concat([word]).join(" ")).width;
  138. if (word[0] === "\n" || (lines[lines.length - 1].length && width > labelWidth)) {
  139. word = word.trim();
  140. lines.push([word]);
  141. width = context.measureText(word).width;
  142. } else {
  143. lines[lines.length - 1].push(word);
  144. }
  145. maxWidth = Math.max(maxWidth,width);
  146. });
  147. var totalHeight = lines.length * theme.labelLineHeight + (lines.length - 1) * theme.labelLineSpacing;
  148. // horizontal alignment
  149. var x = theme.labelAlign === "left" ? labelLeft : theme.labelAlign === "right" ? labelRight : (labelLeft + labelRight) / 2;
  150. // Vertical alignment
  151. var y;
  152. if (labelTop !== null && labelBottom !== null) {
  153. // Vertical center
  154. y = (labelBottom + labelTop - totalHeight) / 2;
  155. } else if (labelBottom !== null) {
  156. // Vertical align bottom
  157. y = labelBottom - totalHeight;
  158. } else {
  159. // Vertical align top
  160. y = labelTop;
  161. }
  162. // draw label
  163. context.fillStyle = theme.labelColor;
  164. lines.forEach(function(line, i){
  165. context.fillText(line.join(" "), x, y + i * (theme.labelLineHeight + theme.labelLineSpacing));
  166. });
  167. }
  168. };
  169. }
  170. function ifNumeric(val, alt) {
  171. return (typeof val === "number" && !isNaN(val)) ? val : alt;
  172. }