eienmojiki commited on
Commit
eaac59e
1 Parent(s): d70bd9a

Upload 2 files

Browse files
Files changed (2) hide show
  1. package.json +19 -0
  2. server.js +280 -0
package.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "quote-gen",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC",
12
+ "dependencies": {
13
+ "canvas": "^2.10.2",
14
+ "cors": "^2.8.5",
15
+ "express": "^4.21.2",
16
+ "fs": "^0.0.1-security",
17
+ "path": "^0.12.7"
18
+ }
19
+ }
server.js ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require("express");
2
+ const cors = require("cors");
3
+ const { createCanvas, registerFont } = require("canvas");
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+ const app = express();
7
+
8
+ app.use(cors());
9
+ app.use(express.json());
10
+ app.use(express.static("public"));
11
+
12
+ // Create fonts directory if it doesn't exist
13
+ const fontsDir = path.join(__dirname, "fonts");
14
+ if (!fs.existsSync(fontsDir)) {
15
+ fs.mkdirSync(fontsDir);
16
+ }
17
+
18
+ // Store available fonts and their variations
19
+ const availableFonts = new Map();
20
+
21
+ function initializeFonts() {
22
+ if (!fs.existsSync(fontsDir)) {
23
+ console.log(
24
+ "Created fonts directory. Please add font files (.ttf or .otf) to the fonts folder."
25
+ );
26
+ return;
27
+ }
28
+
29
+ const fontFiles = fs
30
+ .readdirSync(fontsDir)
31
+ .filter(
32
+ (file) =>
33
+ file.toLowerCase().endsWith(".ttf") ||
34
+ file.toLowerCase().endsWith(".otf")
35
+ );
36
+
37
+ fontFiles.forEach((file) => {
38
+ const fontPath = path.join(fontsDir, file);
39
+ const fontName = file.replace(/\.(ttf|otf)$/i, "");
40
+
41
+ // Parse font variations (Regular, Bold, Italic, etc.)
42
+ let weight = "normal";
43
+ let style = "normal";
44
+
45
+ const lowerFontName = fontName.toLowerCase();
46
+ if (lowerFontName.includes("bold")) weight = "bold";
47
+ if (lowerFontName.includes("light")) weight = "light";
48
+ if (lowerFontName.includes("medium")) weight = "medium";
49
+ if (lowerFontName.includes("italic")) style = "italic";
50
+
51
+ // Register font with canvas
52
+ registerFont(fontPath, {
53
+ family: fontName.split("-")[0], // Get base font name
54
+ weight,
55
+ style,
56
+ });
57
+
58
+ // Store font info
59
+ const familyName = fontName.split("-")[0];
60
+ if (!availableFonts.has(familyName)) {
61
+ availableFonts.set(familyName, []);
62
+ }
63
+ availableFonts.get(familyName).push({
64
+ fullName: fontName,
65
+ weight,
66
+ style,
67
+ });
68
+ });
69
+
70
+ console.log("Available font families:", Array.from(availableFonts.keys()));
71
+ }
72
+
73
+ // Initialize fonts
74
+ initializeFonts();
75
+
76
+ // Store requests history
77
+ let requestsHistory = [];
78
+
79
+ function generateQuoteImage(ctx, canvas, data) {
80
+ const {
81
+ text,
82
+ author,
83
+ bgColor,
84
+ barColor,
85
+ textColor,
86
+ authorColor,
87
+ quoteFontFamily,
88
+ quoteFontWeight,
89
+ quoteFontStyle,
90
+ authorFontFamily,
91
+ authorFontWeight,
92
+ authorFontStyle,
93
+ barWidth = 4,
94
+ } = data;
95
+
96
+ // Constants for layout
97
+ const margin = 80;
98
+ const quoteMarkSize = 120;
99
+ const padding = 30;
100
+ const lineHeight = 50;
101
+
102
+ // Clear canvas
103
+ ctx.fillStyle = bgColor;
104
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
105
+
106
+ // Draw bars
107
+ ctx.fillStyle = barColor;
108
+ ctx.fillRect(margin, margin, barWidth, canvas.height - margin * 2);
109
+ ctx.fillRect(
110
+ canvas.width - margin - barWidth,
111
+ margin,
112
+ barWidth,
113
+ canvas.height - margin * 2
114
+ );
115
+
116
+ // Set up quote font
117
+ ctx.fillStyle = barColor;
118
+ const quoteMarkFont = [];
119
+ if (quoteFontStyle === "italic") quoteMarkFont.push("italic");
120
+ quoteMarkFont.push(quoteFontWeight);
121
+ quoteMarkFont.push(`${quoteMarkSize}px`);
122
+ quoteMarkFont.push(`"${quoteFontFamily}"`);
123
+ ctx.font = quoteMarkFont.join(" ");
124
+ ctx.textBaseline = "top";
125
+
126
+ // Calculate quote mark metrics
127
+ const quoteMarkWidth = ctx.measureText('"').width;
128
+ const textStartX = margin + barWidth + padding + quoteMarkWidth + padding;
129
+ const textEndX = canvas.width - margin - barWidth - padding * 2;
130
+ const maxWidth = textEndX - textStartX;
131
+
132
+ // Set up quote text font
133
+ ctx.fillStyle = textColor;
134
+ const quoteFont = [];
135
+ if (quoteFontStyle === "italic") quoteFont.push("italic");
136
+ quoteFont.push(quoteFontWeight);
137
+ quoteFont.push("36px");
138
+ quoteFont.push(`"${quoteFontFamily}"`);
139
+ ctx.font = quoteFont.join(" ");
140
+
141
+ // Word wrap text
142
+ const words = text.split(" ");
143
+ const lines = [];
144
+ let currentLine = "";
145
+
146
+ for (let word of words) {
147
+ const testLine = currentLine + (currentLine ? " " : "") + word;
148
+ const metrics = ctx.measureText(testLine);
149
+
150
+ if (metrics.width > maxWidth) {
151
+ if (currentLine) {
152
+ lines.push(currentLine);
153
+ currentLine = word;
154
+ } else {
155
+ currentLine = word;
156
+ }
157
+ } else {
158
+ currentLine = testLine;
159
+ }
160
+ }
161
+ if (currentLine) {
162
+ lines.push(currentLine);
163
+ }
164
+
165
+ // Calculate total height of text block
166
+ const totalTextHeight = lines.length * lineHeight;
167
+ const authorHeight = 60; // Space reserved for author
168
+ const availableHeight = canvas.height - margin * 2;
169
+
170
+ // Calculate starting Y position to center text block
171
+ let startY =
172
+ margin + (availableHeight - (totalTextHeight + authorHeight)) / 2;
173
+
174
+ // Draw quote mark at the same vertical position as first line
175
+ ctx.fillStyle = barColor;
176
+ ctx.font = quoteMarkFont.join(" ");
177
+ ctx.fillText('"', margin + barWidth + padding, startY);
178
+
179
+ // Draw quote lines
180
+ ctx.fillStyle = textColor;
181
+ ctx.font = quoteFont.join(" ");
182
+ lines.forEach((line, index) => {
183
+ ctx.fillText(line.trim(), textStartX, startY + index * lineHeight);
184
+ });
185
+
186
+ // Draw author below the quote
187
+ ctx.fillStyle = authorColor;
188
+ const authorFont = [];
189
+ if (authorFontStyle === "italic") authorFont.push("italic");
190
+ authorFont.push(authorFontWeight);
191
+ authorFont.push("28px");
192
+ authorFont.push(`"${authorFontFamily}"`);
193
+ ctx.font = authorFont.join(" ");
194
+
195
+ // Ensure author doesn't overflow
196
+ let authorText = `- ${author}`;
197
+ let authorMetrics = ctx.measureText(authorText);
198
+ if (authorMetrics.width > maxWidth) {
199
+ const ellipsis = "...";
200
+ while (authorMetrics.width > maxWidth && author.length > 0) {
201
+ author = author.slice(0, -1);
202
+ authorText = `- ${author}${ellipsis}`;
203
+ authorMetrics = ctx.measureText(authorText);
204
+ }
205
+ }
206
+
207
+ // Position author text below quote with spacing
208
+ const authorY = startY + totalTextHeight + 40;
209
+ ctx.fillText(authorText, textStartX, authorY);
210
+ }
211
+
212
+ // API Endpoints
213
+ app.get("/api/fonts", (req, res) => {
214
+ const fontDetails = Array.from(availableFonts.entries()).map(
215
+ ([family, variations]) => ({
216
+ family,
217
+ variations: variations.map((v) => ({
218
+ weight: v.weight,
219
+ style: v.style,
220
+ fullName: v.fullName,
221
+ })),
222
+ })
223
+ );
224
+ res.json(fontDetails);
225
+ });
226
+
227
+ app.post("/api/generate-quote", (req, res) => {
228
+ try {
229
+ const data = req.body;
230
+
231
+ // Validate fonts exist
232
+ if (!availableFonts.has(data.quoteFontFamily)) {
233
+ throw new Error("Selected quote font is not available");
234
+ }
235
+ if (!availableFonts.has(data.authorFontFamily)) {
236
+ throw new Error("Selected author font is not available");
237
+ }
238
+
239
+ // Store request
240
+ requestsHistory.unshift({
241
+ timestamp: new Date(),
242
+ request: data,
243
+ });
244
+
245
+ // Keep only last 10 requests
246
+ requestsHistory = requestsHistory.slice(0, 10);
247
+
248
+ // Create canvas
249
+ const canvas = createCanvas(1200, 675); // 16:9 ratio
250
+ const ctx = canvas.getContext("2d");
251
+
252
+ // Generate quote image
253
+ generateQuoteImage(ctx, canvas, data);
254
+
255
+ // Send response
256
+ res.json({
257
+ success: true,
258
+ imageUrl: canvas.toDataURL(),
259
+ timestamp: new Date().toISOString(),
260
+ });
261
+ } catch (error) {
262
+ console.error("Error generating quote:", error);
263
+ res.status(500).json({
264
+ success: false,
265
+ error: error.message,
266
+ });
267
+ }
268
+ });
269
+
270
+ app.get("/api/requests-history", (req, res) => {
271
+ res.json(requestsHistory);
272
+ });
273
+
274
+ // Start server
275
+ const PORT = process.env.PORT || 3000;
276
+ app.listen(PORT, () => {
277
+ console.log(`Server running on port ${PORT}`);
278
+ console.log(`View the application at http://localhost:${PORT}`);
279
+ console.log("Available fonts:", Array.from(availableFonts.keys()));
280
+ });