1
+ const { createCanvas, registerFont } = require ( "canvas" ) ;
2
+ const fs = require ( "fs" ) ;
3
+
4
+ module . exports = ( username , comment , upvotes , timestamp = "1h" , width = 1920 , height = 1080 , zoom = 3 , output = "out.png" ) => {
5
+ const canvas = createCanvas ( width , height ) ;
6
+ const ctx = canvas . getContext ( "2d" ) ;
7
+ const separator = "•"
8
+
9
+ registerFont ( "./fonts/rfont.ttf" , { family : "rfont" } ) ;
10
+
11
+ var zoom = 3.8 ;
12
+ var fontSize = 13 * zoom ;
13
+
14
+ // White background
15
+ ctx . fillStyle = "#ffffff" ;
16
+ ctx . fillRect ( 0 , 0 , width , height ) ;
17
+
18
+ // Username
19
+ var leftPadding = 10 * zoom ;
20
+ var topPadding = fontSize + 7 * zoom ;
21
+ ctx . font = fontSize + "px Helvetica" ;
22
+ ctx . fillStyle = "#a5a4a4" ;
23
+ ctx . fillText ( username , leftPadding , topPadding )
24
+
25
+ // Separator
26
+ leftPadding += ctx . measureText ( username ) . width + 4 * zoom ;
27
+ ctx . fillStyle = "#efefed" ;
28
+ ctx . fillText ( separator , leftPadding , topPadding ) ;
29
+
30
+ // Timestamp
31
+ leftPadding += ctx . measureText ( separator ) . width + 4 * zoom ;
32
+ ctx . fillStyle = "#a5a4a4" ;
33
+ ctx . fillText ( timestamp , leftPadding , topPadding ) ;
34
+
35
+ // Comment
36
+ ctx . fillStyle = "#222222" ;
37
+ leftPadding = 10 * zoom ;
38
+ topPadding += 7 * zoom ;
39
+ var lines = getLines ( comment ) ;
40
+ for ( let line of lines ) {
41
+ topPadding += fontSize ;
42
+ ctx . fillText ( line , leftPadding , topPadding ) ;
43
+ }
44
+
45
+ // Downvote
46
+
47
+ ctx . font = 24 * zoom + "px rfont" ;
48
+ ctx . fillStyle = "#ccccca" ;
49
+ topPadding += fontSize + 18 * zoom ;
50
+ leftPadding = width - 8 * zoom - ctx . measureText ( "\uF115" ) . width ;
51
+ ctx . fillText ( "\uF115" , leftPadding , topPadding + 2.3 * zoom ) ; // not sure why 2.3 * zoom is needed but it is
52
+
53
+ // Upvotes
54
+ ctx . font = 14 * zoom + "px rfont" ;
55
+ leftPadding -= ctx . measureText ( upvotes ) . width ;
56
+ ctx . fillText ( upvotes , leftPadding , topPadding - 7 * zoom + 2.3 * zoom ) ; // not sure about why - 7 * zoom is needed here ALSO not sure why 2.3 * zoom is needed but it is
57
+
58
+ // Upvote
59
+ ctx . font = 24 * zoom + "px rfont" ;
60
+ leftPadding -= ctx . measureText ( "\uF16E" ) . width ;
61
+ ctx . fillText ( "\uF16E" , leftPadding , topPadding + 2.3 * zoom ) ; // not sure why 2.3 * zoom is needed but it is
62
+
63
+ // Divider
64
+ leftPadding -= 8 * zoom ;
65
+ ctx . lineWidth = 1 * zoom ;
66
+ ctx . strokeStyle = "#efefed" ;
67
+ ctx . beginPath ( ) ;
68
+ ctx . moveTo ( leftPadding , topPadding - 20 * zoom ) ;
69
+ ctx . lineTo ( leftPadding , topPadding ) ;
70
+ ctx . stroke ( ) ;
71
+
72
+ // More
73
+ leftPadding -= 9 * zoom + ctx . measureText ( "\uF159" ) . width ;
74
+ ctx . font = 20 * zoom + "px rfont" ;
75
+ ctx . fillText ( "\uF159" , leftPadding , topPadding ) ;
76
+
77
+ // Reply
78
+ leftPadding -= 8 * zoom + ctx . measureText ( "\uF151" ) . width ;
79
+ ctx . fillText ( "\uF151" , leftPadding , topPadding ) ;
80
+
81
+ function getLines ( text ) {
82
+ var words = text . split ( " " ) ;
83
+ var lines = [ ] ;
84
+ var line = "" ;
85
+
86
+ for ( var i = 0 ; i < words . length ; i ++ ) {
87
+ var word = words [ i ] ;
88
+ var testLine = line + word + " " ;
89
+ var testWidth = ctx . measureText ( testLine ) . width ;
90
+ if ( testWidth > width - 8 * zoom && i ) {
91
+
92
+ /* This entire isolated chunk is for wrapping when the entire word is longer than the width.
93
+ * There is probably a much more efficient way to do this. */
94
+ if ( line . match ( / / g) . length === 1 ) {
95
+ var characters = line . split ( "" ) ;
96
+ line = "" ;
97
+ for ( let character of characters ) {
98
+ testLine = line + character //+ " ";
99
+ testWidth = ctx . measureText ( testLine ) . width ;
100
+ if ( testWidth > width - 8 * zoom ) {
101
+ lines . push ( line ) ;
102
+ line = character ;
103
+ } else {
104
+ line = testLine ;
105
+ }
106
+ }
107
+ if ( line ) lines . push ( line + " " ) ;
108
+ line = word + " " ;
109
+ } else {
110
+ lines . push ( line ) ;
111
+ line = word + " " ;
112
+ }
113
+
114
+ } else {
115
+ line = testLine ;
116
+ }
117
+ }
118
+ if ( line ) lines . push ( line ) ;
119
+ return lines ;
120
+ }
121
+
122
+ fs . writeFileSync ( output , canvas . toDataURL ( ) . replace ( "data:image/png;base64," , "" ) , "base64" ) ;
123
+ }
0 commit comments