Skip to content

Commit 52e5cc2

Browse files
committed
Refactor: og-image generation to use layered approach
This change refactors the OG image generation process to use a layered approach, creating a base image and overlaying text. This improves efficiency and maintainability.
1 parent c5382b9 commit 52e5cc2

File tree

4 files changed

+118
-46
lines changed

4 files changed

+118
-46
lines changed

assets/og-template/template.html renamed to assets/og-template/base-template.html

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html>
33
<head>
44
<meta charset="utf-8">
5-
<title>OG Image</title>
5+
<title>OG Image Base</title>
66
<style>
77
/* Use a wrapper to act as our canvas and positioning context */
88
.canvas {
@@ -78,33 +78,6 @@
7878
white-space: nowrap;
7979
}
8080

81-
.title {
82-
font-size: 50px;
83-
font-weight: 700;
84-
line-height: 1.15;
85-
color: #1f2937;
86-
margin: 0 0 20px 0;
87-
display: -webkit-box;
88-
-webkit-line-clamp: 3;
89-
-webkit-box-orient: vertical;
90-
overflow: hidden;
91-
text-overflow: ellipsis;
92-
text-align: left;
93-
}
94-
.description {
95-
font-size: 32px;
96-
font-weight: 400;
97-
line-height: 1.45;
98-
color: #4b5563;
99-
margin: 0 0 20px 0;
100-
display: -webkit-box;
101-
-webkit-line-clamp: 3;
102-
-webkit-box-orient: vertical;
103-
overflow: hidden;
104-
text-overflow: ellipsis;
105-
text-align: left;
106-
}
107-
10881
.site-name {
10982
font-size: 35px;
11083
font-weight: 500;
@@ -129,8 +102,7 @@
129102
<img src="LOGO_SRC" alt="Site Logo" class="logo">
130103
<div class="logo-text">Open Neuromorphic</div>
131104
</div>
132-
<h1 class="title">PAGE_TITLE</h1>
133-
<p class="description">PAGE_DESCRIPTION</p>
105+
<!-- Title and Description are removed from the base template -->
134106
<div class="site-name">open-neuromorphic.org</div>
135107
</div>
136108
</div>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>OG Image Text Overlay</title>
6+
<style>
7+
body, html {
8+
margin: 0;
9+
padding: 0;
10+
}
11+
.canvas {
12+
position: relative;
13+
width: 1200px;
14+
height: 630px;
15+
background-image: url('BASE_IMAGE_URL');
16+
background-size: 1200px 630px;
17+
background-repeat: no-repeat;
18+
font-family: 'Inter', Arial, sans-serif;
19+
overflow: hidden;
20+
}
21+
.container {
22+
position: absolute;
23+
top: 30px;
24+
left: 30px;
25+
width: 950px;
26+
height: 550px;
27+
}
28+
.main-content {
29+
width: 100%;
30+
height: 100%;
31+
display: flex;
32+
flex-direction: column;
33+
justify-content: center;
34+
padding-top: 110px; /* To account for the header in the base image */
35+
box-sizing: border-box;
36+
}
37+
.title {
38+
font-size: 50px;
39+
font-weight: 700;
40+
line-height: 1.15;
41+
color: #1f2937;
42+
margin: 0 0 20px 0;
43+
display: -webkit-box;
44+
-webkit-line-clamp: 3;
45+
-webkit-box-orient: vertical;
46+
overflow: hidden;
47+
text-overflow: ellipsis;
48+
text-align: left;
49+
}
50+
.description {
51+
font-size: 32px;
52+
font-weight: 400;
53+
line-height: 1.45;
54+
color: #4b5563;
55+
margin: 0 0 20px 0;
56+
display: -webkit-box;
57+
-webkit-line-clamp: 3;
58+
-webkit-box-orient: vertical;
59+
overflow: hidden;
60+
text-overflow: ellipsis;
61+
text-align: left;
62+
}
63+
</style>
64+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
65+
</head>
66+
<body>
67+
<div class="canvas">
68+
<div class="container">
69+
<div class="main-content">
70+
<h1 class="title">PAGE_TITLE</h1>
71+
<p class="description">PAGE_DESCRIPTION</p>
72+
</div>
73+
</div>
74+
</div>
75+
</body>
76+
</html>

scripts/generateImagesFromData.js

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ const { execSync } = require('child_process');
55

66
// --- Configuration ---
77
const PROJECT_ROOT = process.cwd();
8-
const TEMPLATE_PATH = join(PROJECT_ROOT, 'assets', 'og-template', 'template.html');
9-
const INPUT_JSON_PATH = join(PROJECT_ROOT, 'tmp', 'ogImageData.json');
8+
const TMP_DIR = join(PROJECT_ROOT, 'tmp');
9+
const BASE_TEMPLATE_PATH = join(PROJECT_ROOT, 'assets', 'og-template', 'base-template.html');
10+
const OVERLAY_TEMPLATE_PATH = join(PROJECT_ROOT, 'assets', 'og-template', 'text-overlay-template.html');
11+
const INPUT_JSON_PATH = join(TMP_DIR, 'ogImageData.json');
12+
13+
const BASE_IMAGE_TMP_HTML = join(TMP_DIR, 'base-og-temp.html');
14+
const BASE_IMAGE_OUTPUT_PATH = join(TMP_DIR, 'base-og.jpg');
1015
const OUTPUT_FORMAT = 'jpg';
1116
const JPEG_QUALITY = 95;
1217

@@ -15,18 +20,37 @@ async function pathExists(path) {
1520
try { await stat(path); return true; } catch { return false; }
1621
}
1722

23+
async function generateBaseImage(logoUri, backgroundUri) {
24+
console.log('🖼️ Generating base OG image...');
25+
if (!(await pathExists(BASE_TEMPLATE_PATH))) {
26+
throw new Error(`Base template not found: ${BASE_TEMPLATE_PATH}`);
27+
}
28+
const baseTemplateContent = await readFile(BASE_TEMPLATE_PATH, 'utf8');
29+
30+
const htmlContent = baseTemplateContent
31+
.replace('LOGO_SRC', logoUri)
32+
.replace('BACKGROUND_URL', backgroundUri);
33+
34+
await writeFile(BASE_IMAGE_TMP_HTML, htmlContent);
35+
36+
const command = `wkhtmltoimage --enable-local-file-access --quality ${JPEG_QUALITY} --format ${OUTPUT_FORMAT} --width 1200 --height 630 "${BASE_IMAGE_TMP_HTML}" "${BASE_IMAGE_OUTPUT_PATH}"`;
37+
execSync(command, { stdio: 'pipe' });
38+
39+
await unlink(BASE_IMAGE_TMP_HTML); // Clean up temp html
40+
console.log(`✅ Base OG image created at: ${BASE_IMAGE_OUTPUT_PATH}`);
41+
return `file://${BASE_IMAGE_OUTPUT_PATH}`;
42+
}
43+
1844
// --- Main Image Generation Logic ---
1945
async function generateImages() {
20-
console.log(`🖼️ Starting OG image generation...`);
46+
console.log(`🖼️ Starting page-specific OG image generation...`);
2147

2248
// --- Pre-flight checks ---
23-
if (!(await pathExists(TEMPLATE_PATH))) {
24-
console.error(`❌ Template not found: ${TEMPLATE_PATH}`);
25-
process.exit(1);
26-
}
2749
if (!(await pathExists(INPUT_JSON_PATH))) {
28-
console.error(`❌ Data file not found: ${INPUT_JSON_PATH}. Run collect script first.`);
29-
process.exit(1);
50+
throw new Error(`❌ Data file not found: ${INPUT_JSON_PATH}. Run collect script first.`);
51+
}
52+
if (!(await pathExists(OVERLAY_TEMPLATE_PATH))) {
53+
throw new Error(`❌ Overlay template not found: ${OVERLAY_TEMPLATE_PATH}`);
3054
}
3155
try {
3256
execSync('wkhtmltoimage --version', { stdio: 'ignore' });
@@ -36,27 +60,27 @@ async function generateImages() {
3660
}
3761

3862
// --- Read data and template ---
39-
const templateContent = await readFile(TEMPLATE_PATH, 'utf8');
63+
const overlayTemplateContent = await readFile(OVERLAY_TEMPLATE_PATH, 'utf8');
4064
const jsonData = JSON.parse(await readFile(INPUT_JSON_PATH, 'utf8'));
4165

4266
if (!jsonData || !jsonData.pages || jsonData.pages.length === 0) {
4367
console.warn('⚠️ No pages to process.');
4468
return;
4569
}
70+
71+
// --- Generate the single base image first ---
72+
const baseImageFileUrl = await generateBaseImage(jsonData.logoDataUri, jsonData.backgroundDataUri);
4673

4774
let successCount = 0;
4875
let errorCount = 0;
49-
const logoDataUri = jsonData.logoDataUri || '';
50-
const backgroundDataUri = jsonData.backgroundDataUri || '';
5176

52-
// --- Process each page ---
77+
// --- Process each page using the base image ---
5378
for (const pageData of jsonData.pages) {
5479
const { title, description, outputPath, tempHtmlPath } = pageData;
5580

5681
try {
57-
const htmlContent = templateContent
58-
.replace('LOGO_SRC', logoDataUri)
59-
.replace('BACKGROUND_URL', backgroundDataUri) // Using BACKGROUND_URL placeholder now
82+
const htmlContent = overlayTemplateContent
83+
.replace('BASE_IMAGE_URL', baseImageFileUrl)
6084
.replace('PAGE_TITLE', title)
6185
.replace('PAGE_DESCRIPTION', description);
6286

static/images/og-image.jpg

1.75 KB
Loading

0 commit comments

Comments
 (0)