@@ -106,9 +106,11 @@ export default class CreateRichMenu extends AbstractTool {
106
106
richMenuImagePath,
107
107
} ) ;
108
108
} catch ( error ) {
109
+ console . error ( "Rich menu creation error:" , error ) ;
109
110
return createErrorResponse (
110
111
JSON . stringify ( {
111
- error,
112
+ error : error instanceof Error ? error . message : String ( error ) ,
113
+ stack : error instanceof Error ? error . stack : undefined ,
112
114
createRichMenuResponse,
113
115
setImageResponse,
114
116
setDefaultResponse,
@@ -128,6 +130,9 @@ async function generateRichMenuImage(
128
130
templateNo : number ,
129
131
actions : messagingApi . Action [ ] ,
130
132
) : Promise < string > {
133
+ console . log (
134
+ `Generating rich menu image for template ${ templateNo } with ${ actions . length } actions` ,
135
+ ) ;
131
136
// Flow:
132
137
// 1. Read the Markdown template
133
138
// 2. Convert Markdown to HTML using Marp
@@ -143,9 +148,12 @@ async function generateRichMenuImage(
143
148
// 1. Read the Markdown template
144
149
const srcPath = path . join (
145
150
serverPath ,
146
- `richmenu-templates /template-0${ templateNo } .md` ,
151
+ `richmenu-template /template-0${ templateNo } .md` ,
147
152
) ;
153
+ console . log ( `Reading template from: ${ srcPath } ` ) ;
154
+ console . log ( `Server path: ${ serverPath } ` ) ;
148
155
let content = await fsp . readFile ( srcPath , "utf8" ) ;
156
+ console . log ( `Template content length: ${ content . length } ` ) ;
149
157
for ( let index = 0 ; index < actions . length ; index ++ ) {
150
158
const pattern = new RegExp ( `<h3>item0${ index + 1 } </h3>` , "g" ) ;
151
159
content = content . replace ( pattern , `<h3>${ actions [ index ] . label } </h3>` ) ;
@@ -155,11 +163,19 @@ async function generateRichMenuImage(
155
163
const marp = new Marp ( ) ;
156
164
const { html, css } = marp . render ( content ) ;
157
165
158
- // 3. Save the HTML as a temporary file
166
+ // 3. Save the HTML as a temporary file with Japanese font support
159
167
const htmlContent = `
160
- <html>
168
+ <!doctype html>
169
+ <html lang="ja">
161
170
<head>
162
- <style>${ css } </style>
171
+ <meta charset="UTF-8">
172
+ <style>
173
+ ${ css }
174
+ * {
175
+ font-family: 'IPAexGothic', 'IPAexMincho', 'Noto Sans CJK JP', 'Noto Sans JP', 'Yu Gothic UI', 'Yu Gothic', 'Meiryo UI', 'Meiryo', 'MS UI Gothic', sans-serif !important;
176
+ }
177
+ html, body { margin: 0; padding: 0; }
178
+ </style>
163
179
</head>
164
180
<body>${ html } </body>
165
181
</html>
@@ -170,19 +186,55 @@ async function generateRichMenuImage(
170
186
) ;
171
187
await fsp . writeFile ( tempHtmlPath , htmlContent , "utf8" ) ;
172
188
173
- // 4. Use puppeteer to convert HTML to PNG
174
- const browser = await puppeteer . launch ( ) ;
189
+ // 4. Use puppeteer to convert HTML to PNG with Docker-compatible settings
190
+ console . log (
191
+ `Launching Puppeteer with executable: ${ process . env . PUPPETEER_EXECUTABLE_PATH || "default" } ` ,
192
+ ) ;
193
+ const browser = await puppeteer . launch ( {
194
+ headless : true ,
195
+ executablePath : process . env . PUPPETEER_EXECUTABLE_PATH || undefined ,
196
+ args : [
197
+ "--no-sandbox" ,
198
+ "--disable-setuid-sandbox" ,
199
+ "--disable-dev-shm-usage" ,
200
+ "--disable-gpu" ,
201
+ "--disable-web-security" ,
202
+ "--disable-features=VizDisplayCompositor" ,
203
+ "--no-first-run" ,
204
+ "--no-default-browser-check" ,
205
+ "--disable-default-apps" ,
206
+ "--disable-extensions" ,
207
+ ] ,
208
+ } ) ;
209
+ console . log ( "Puppeteer browser launched successfully" ) ;
175
210
const page = await browser . newPage ( ) ;
176
211
await page . setViewport ( { width : RICHMENU_WIDTH , height : RICHMENU_HEIGHT } ) ;
177
212
await page . goto ( `file://${ tempHtmlPath } ` , {
178
213
waitUntil : "networkidle0" ,
179
214
} ) ;
215
+
216
+ // Wait for fonts to load
217
+ await page . evaluate ( ( ) => document . fonts . ready ) ;
218
+ await new Promise ( resolve => setTimeout ( resolve , 2000 ) ) ;
219
+
220
+ console . log ( `Taking screenshot to: ${ richMenuImagePath } ` ) ;
180
221
await page . screenshot ( {
181
222
path : richMenuImagePath as `${string } .png`,
182
223
clip : { x : 0 , y : 0 , width : RICHMENU_WIDTH , height : RICHMENU_HEIGHT } ,
183
224
} ) ;
225
+ console . log ( "Screenshot taken successfully" ) ;
184
226
await browser . close ( ) ;
185
227
228
+ // Save image to output directory
229
+ const outputPath = path . join ( "/tmp" , path . basename ( richMenuImagePath ) ) ;
230
+
231
+ try {
232
+ await fsp . copyFile ( richMenuImagePath , outputPath ) ;
233
+ console . log ( `Rich menu image saved to: ${ outputPath } ` ) ;
234
+ } catch ( error ) {
235
+ console . warn ( `Failed to save image to output directory: ${ error } ` ) ;
236
+ }
237
+
186
238
// 5. Delete the temporary HTML file
187
239
await fsp . unlink ( tempHtmlPath ) ;
188
240
0 commit comments