releasebird-javascript-sdk 1.0.61 → 1.0.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -1
- package/backend-image-service/image-service/Dockerfile +8 -0
- package/backend-image-service/image-service/fonts.conf +39 -0
- package/backend-image-service/image-service/server.js +27 -8
- package/backend-image-service/test-emoji-detailed.js +119 -0
- package/backend-image-service/test-emoji-simple.js +68 -0
- package/backend-image-service/test-emoji.html +51 -0
- package/build/index.js +1 -1
- package/emoji-test.png +0 -0
- package/package.json +1 -1
- package/published/1.0.62/index.js +1 -0
- package/published/latest/index.js +1 -1
- package/src/RbirdScreenshotManager.js +142 -21
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
"Bash(node test-java-backend.js:*)",
|
|
34
34
|
"Read(//Users/christianzillmann/IdeaProjects/releasebook-backend/src/main/java/io/releasebook/backend/web/rest/papi/**)",
|
|
35
35
|
"Read(//Users/christianzillmann/IdeaProjects/releasebook-backend/**)",
|
|
36
|
-
"Bash(node test-full-flow.js:*)"
|
|
36
|
+
"Bash(node test-full-flow.js:*)",
|
|
37
|
+
"Bash(node test-emoji-simple.js:*)",
|
|
38
|
+
"Bash(node:*)"
|
|
37
39
|
],
|
|
38
40
|
"deny": [],
|
|
39
41
|
"ask": []
|
|
@@ -12,6 +12,8 @@ RUN apt-get update && apt-get install -y \
|
|
|
12
12
|
fonts-noto-color-emoji \
|
|
13
13
|
fonts-noto-cjk \
|
|
14
14
|
fonts-noto-cjk-extra \
|
|
15
|
+
fonts-dejavu-core \
|
|
16
|
+
fontconfig \
|
|
15
17
|
libasound2 \
|
|
16
18
|
libatk-bridge2.0-0 \
|
|
17
19
|
libatk1.0-0 \
|
|
@@ -44,6 +46,12 @@ RUN npm ci --only=production
|
|
|
44
46
|
# Copy application
|
|
45
47
|
COPY server.js ./
|
|
46
48
|
|
|
49
|
+
# Copy font configuration
|
|
50
|
+
COPY fonts.conf /etc/fonts/local.conf
|
|
51
|
+
|
|
52
|
+
# Rebuild font cache
|
|
53
|
+
RUN fc-cache -fv
|
|
54
|
+
|
|
47
55
|
# Expose port
|
|
48
56
|
EXPOSE 3000
|
|
49
57
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
|
3
|
+
<fontconfig>
|
|
4
|
+
<!-- Enable emoji rendering -->
|
|
5
|
+
<match target="pattern">
|
|
6
|
+
<test qual="any" name="family">
|
|
7
|
+
<string>sans-serif</string>
|
|
8
|
+
</test>
|
|
9
|
+
<edit name="family" mode="append" binding="weak">
|
|
10
|
+
<string>Noto Color Emoji</string>
|
|
11
|
+
</edit>
|
|
12
|
+
</match>
|
|
13
|
+
|
|
14
|
+
<match target="pattern">
|
|
15
|
+
<test qual="any" name="family">
|
|
16
|
+
<string>serif</string>
|
|
17
|
+
</test>
|
|
18
|
+
<edit name="family" mode="append" binding="weak">
|
|
19
|
+
<string>Noto Color Emoji</string>
|
|
20
|
+
</edit>
|
|
21
|
+
</match>
|
|
22
|
+
|
|
23
|
+
<match target="pattern">
|
|
24
|
+
<test qual="any" name="family">
|
|
25
|
+
<string>monospace</string>
|
|
26
|
+
</test>
|
|
27
|
+
<edit name="family" mode="append" binding="weak">
|
|
28
|
+
<string>Noto Color Emoji</string>
|
|
29
|
+
</edit>
|
|
30
|
+
</match>
|
|
31
|
+
|
|
32
|
+
<!-- Always prefer color emoji -->
|
|
33
|
+
<alias>
|
|
34
|
+
<family>emoji</family>
|
|
35
|
+
<prefer>
|
|
36
|
+
<family>Noto Color Emoji</family>
|
|
37
|
+
</prefer>
|
|
38
|
+
</alias>
|
|
39
|
+
</fontconfig>
|
|
@@ -34,7 +34,10 @@ async function initBrowser() {
|
|
|
34
34
|
'--disable-accelerated-2d-canvas',
|
|
35
35
|
'--no-first-run',
|
|
36
36
|
'--no-zygote',
|
|
37
|
-
'--disable-gpu'
|
|
37
|
+
'--disable-gpu',
|
|
38
|
+
'--font-render-hinting=none',
|
|
39
|
+
'--enable-font-antialiasing',
|
|
40
|
+
'--disable-lcd-text'
|
|
38
41
|
]
|
|
39
42
|
});
|
|
40
43
|
console.log('[Puppeteer] Browser launched successfully');
|
|
@@ -108,21 +111,37 @@ app.post('/render', async (req, res) => {
|
|
|
108
111
|
|
|
109
112
|
console.log(`[Screenshot] Viewport set to ${viewportWidth}x${viewportHeight}`);
|
|
110
113
|
|
|
111
|
-
//
|
|
112
|
-
|
|
114
|
+
// Inject emoji font CSS into HTML
|
|
115
|
+
const emojiCSS = `
|
|
116
|
+
<style>
|
|
117
|
+
* {
|
|
118
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif !important;
|
|
119
|
+
}
|
|
120
|
+
</style>
|
|
121
|
+
`;
|
|
122
|
+
|
|
123
|
+
// Insert emoji CSS into HTML
|
|
124
|
+
let enhancedHTML = htmlContent;
|
|
125
|
+
if (htmlContent.includes('<head>')) {
|
|
126
|
+
enhancedHTML = htmlContent.replace('<head>', '<head>' + emojiCSS);
|
|
127
|
+
} else if (htmlContent.includes('<html>')) {
|
|
128
|
+
enhancedHTML = htmlContent.replace('<html>', '<html><head>' + emojiCSS + '</head>');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Set content (using enhanced htmlContent with emoji fonts)
|
|
132
|
+
await page.setContent(enhancedHTML, {
|
|
113
133
|
waitUntil: 'networkidle0', // Wait for network to be idle
|
|
114
134
|
timeout: 30000
|
|
115
135
|
});
|
|
116
136
|
|
|
117
|
-
console.log('[Screenshot] HTML content loaded');
|
|
137
|
+
console.log('[Screenshot] HTML content loaded with emoji font support');
|
|
118
138
|
|
|
119
139
|
// Wait a bit for any animations/transitions
|
|
120
140
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
121
141
|
|
|
122
|
-
// Take screenshot
|
|
142
|
+
// Take screenshot (PNG for better emoji support)
|
|
123
143
|
const screenshot = await page.screenshot({
|
|
124
|
-
type: '
|
|
125
|
-
quality: 90,
|
|
144
|
+
type: 'png',
|
|
126
145
|
fullPage: false, // Only visible viewport
|
|
127
146
|
encoding: 'base64'
|
|
128
147
|
});
|
|
@@ -130,7 +149,7 @@ app.post('/render', async (req, res) => {
|
|
|
130
149
|
console.log('[Screenshot] Screenshot captured successfully');
|
|
131
150
|
|
|
132
151
|
// Return as data URL
|
|
133
|
-
const dataUrl = `data:image/
|
|
152
|
+
const dataUrl = `data:image/png;base64,${screenshot}`;
|
|
134
153
|
|
|
135
154
|
res.json({
|
|
136
155
|
screenshot: dataUrl,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const zlib = require('zlib');
|
|
2
|
+
const https = require('https');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
// Test 1: Simple text emoji
|
|
6
|
+
const test1 = `<!DOCTYPE html>
|
|
7
|
+
<html>
|
|
8
|
+
<head>
|
|
9
|
+
<meta charset="UTF-8">
|
|
10
|
+
</head>
|
|
11
|
+
<body style="font-size: 100px; padding: 50px;">
|
|
12
|
+
😀 🎉 🚀
|
|
13
|
+
</body>
|
|
14
|
+
</html>`;
|
|
15
|
+
|
|
16
|
+
// Test 2: With explicit font-family
|
|
17
|
+
const test2 = `<!DOCTYPE html>
|
|
18
|
+
<html>
|
|
19
|
+
<head>
|
|
20
|
+
<meta charset="UTF-8">
|
|
21
|
+
<style>
|
|
22
|
+
body {
|
|
23
|
+
font-family: "Noto Color Emoji", sans-serif;
|
|
24
|
+
font-size: 100px;
|
|
25
|
+
padding: 50px;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
😀 🎉 🚀
|
|
31
|
+
</body>
|
|
32
|
+
</html>`;
|
|
33
|
+
|
|
34
|
+
// Test 3: Unicode emoji codes
|
|
35
|
+
const test3 = `<!DOCTYPE html>
|
|
36
|
+
<html>
|
|
37
|
+
<head>
|
|
38
|
+
<meta charset="UTF-8">
|
|
39
|
+
</head>
|
|
40
|
+
<body style="font-size: 100px; padding: 50px;">
|
|
41
|
+
😀 🎉 🚀
|
|
42
|
+
</body>
|
|
43
|
+
</html>`;
|
|
44
|
+
|
|
45
|
+
async function testEmoji(html, testName) {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
const compressed = zlib.gzipSync(Buffer.from(html, 'utf-8'));
|
|
48
|
+
const base64 = compressed.toString('base64');
|
|
49
|
+
|
|
50
|
+
const data = JSON.stringify({
|
|
51
|
+
htmlCompressed: base64,
|
|
52
|
+
compressed: true,
|
|
53
|
+
width: 600,
|
|
54
|
+
height: 400
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const options = {
|
|
58
|
+
hostname: 'api.releasebird.com',
|
|
59
|
+
path: '/papi/images/screenshot/render',
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: {
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
'Content-Length': Buffer.byteLength(data)
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
console.log(`\n=== ${testName} ===`);
|
|
68
|
+
|
|
69
|
+
const req = https.request(options, (res) => {
|
|
70
|
+
let result = '';
|
|
71
|
+
res.on('data', (chunk) => { result += chunk; });
|
|
72
|
+
res.on('end', () => {
|
|
73
|
+
try {
|
|
74
|
+
const parsed = JSON.parse(result);
|
|
75
|
+
if (parsed.screenshot) {
|
|
76
|
+
console.log('✅ Screenshot generated');
|
|
77
|
+
console.log('Length:', parsed.screenshot.length);
|
|
78
|
+
|
|
79
|
+
const filename = `emoji-${testName.replace(/\s+/g, '-').toLowerCase()}.png`;
|
|
80
|
+
const base64Data = parsed.screenshot.replace(/^data:image\/\w+;base64,/, '');
|
|
81
|
+
fs.writeFileSync(filename, Buffer.from(base64Data, 'base64'));
|
|
82
|
+
console.log(`Saved to ${filename}`);
|
|
83
|
+
|
|
84
|
+
resolve(true);
|
|
85
|
+
} else {
|
|
86
|
+
console.log('❌ Error:', parsed.error);
|
|
87
|
+
resolve(false);
|
|
88
|
+
}
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.log('❌ Parse error:', e.message);
|
|
91
|
+
resolve(false);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
req.on('error', (err) => {
|
|
97
|
+
console.log('❌ Request failed:', err.message);
|
|
98
|
+
resolve(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
req.write(data);
|
|
102
|
+
req.end();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
(async () => {
|
|
107
|
+
console.log('Testing emoji rendering with different approaches...\n');
|
|
108
|
+
|
|
109
|
+
await testEmoji(test1, 'Test 1 Simple');
|
|
110
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
111
|
+
|
|
112
|
+
await testEmoji(test2, 'Test 2 Font Family');
|
|
113
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
114
|
+
|
|
115
|
+
await testEmoji(test3, 'Test 3 Unicode');
|
|
116
|
+
|
|
117
|
+
console.log('\n\nPlease check the generated PNG files to see if emojis are visible.');
|
|
118
|
+
console.log('If they appear as boxes or missing, the font issue is confirmed.');
|
|
119
|
+
})();
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const zlib = require('zlib');
|
|
2
|
+
const https = require('https');
|
|
3
|
+
|
|
4
|
+
// Very simple HTML with emojis
|
|
5
|
+
const html = `<!DOCTYPE html>
|
|
6
|
+
<html>
|
|
7
|
+
<head>
|
|
8
|
+
<meta charset="UTF-8">
|
|
9
|
+
<style>
|
|
10
|
+
body {
|
|
11
|
+
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
|
|
12
|
+
font-size: 48px;
|
|
13
|
+
padding: 40px;
|
|
14
|
+
}
|
|
15
|
+
</style>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<p>Hello 🎉 World 🚀</p>
|
|
19
|
+
<p>✅ ❌ ⚠️ 💡 🔥</p>
|
|
20
|
+
<p>😀 😃 😄 😁 😆</p>
|
|
21
|
+
</body>
|
|
22
|
+
</html>`;
|
|
23
|
+
|
|
24
|
+
const compressed = zlib.gzipSync(Buffer.from(html, 'utf-8'));
|
|
25
|
+
const base64 = compressed.toString('base64');
|
|
26
|
+
|
|
27
|
+
const data = JSON.stringify({
|
|
28
|
+
htmlCompressed: base64,
|
|
29
|
+
compressed: true,
|
|
30
|
+
width: 800,
|
|
31
|
+
height: 600
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const options = {
|
|
35
|
+
hostname: 'api.releasebird.com',
|
|
36
|
+
path: '/papi/images/screenshot/render',
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
'Content-Length': Buffer.byteLength(data)
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
console.log('Testing emoji rendering with explicit font family...');
|
|
45
|
+
|
|
46
|
+
const req = https.request(options, (res) => {
|
|
47
|
+
let result = '';
|
|
48
|
+
res.on('data', (chunk) => { result += chunk; });
|
|
49
|
+
res.on('end', () => {
|
|
50
|
+
const parsed = JSON.parse(result);
|
|
51
|
+
if (parsed.screenshot) {
|
|
52
|
+
console.log('✅ Screenshot generated');
|
|
53
|
+
console.log('Length:', parsed.screenshot.length);
|
|
54
|
+
|
|
55
|
+
// Save to file for inspection
|
|
56
|
+
const fs = require('fs');
|
|
57
|
+
const base64Data = parsed.screenshot.replace(/^data:image\/\w+;base64,/, '');
|
|
58
|
+
fs.writeFileSync('emoji-test.png', Buffer.from(base64Data, 'base64'));
|
|
59
|
+
console.log('Saved to emoji-test.png - please check if emojis are visible');
|
|
60
|
+
} else {
|
|
61
|
+
console.log('Error:', parsed);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
req.on('error', (err) => console.error('Error:', err.message));
|
|
67
|
+
req.write(data);
|
|
68
|
+
req.end();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<style>
|
|
6
|
+
body {
|
|
7
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
8
|
+
padding: 40px;
|
|
9
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
10
|
+
}
|
|
11
|
+
.emoji-test {
|
|
12
|
+
background: white;
|
|
13
|
+
padding: 30px;
|
|
14
|
+
border-radius: 20px;
|
|
15
|
+
margin: 20px 0;
|
|
16
|
+
}
|
|
17
|
+
h1 { font-size: 48px; color: #333; }
|
|
18
|
+
.emoji-large { font-size: 64px; }
|
|
19
|
+
.emoji-row { margin: 20px 0; font-size: 32px; }
|
|
20
|
+
</style>
|
|
21
|
+
</head>
|
|
22
|
+
<body>
|
|
23
|
+
<div class="emoji-test">
|
|
24
|
+
<h1>🎨 Emoji Test 🚀</h1>
|
|
25
|
+
|
|
26
|
+
<div class="emoji-large">
|
|
27
|
+
😀 😃 😄 😁 😆 😅 🤣 😂
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="emoji-row">
|
|
31
|
+
❤️ 💙 💚 💛 🧡 💜 🖤 🤍
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div class="emoji-row">
|
|
35
|
+
✅ ❌ ⚠️ ℹ️ 🔥 💡 🎉 🎊
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="emoji-row">
|
|
39
|
+
🚀 🛸 ✈️ 🚁 🚂 🚗 🏍️ 🚲
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="emoji-row">
|
|
43
|
+
🍎 🍊 🍋 🍌 🍉 🍇 🍓 🫐
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="emoji-row">
|
|
47
|
+
👍 👎 👏 🙌 🤝 💪 🦾 🫶
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</body>
|
|
51
|
+
</html>
|