@@ -383,6 +383,7 @@ class QuoteGenerate {
383383
384384 const fallbackEmojiImageJson = emojiImageByBrand [ fallbackEmojiBrand ]
385385
386+ // Pre-calculate text dimensions to avoid creating oversized canvas
386387 const canvas = createCanvas ( maxWidth + fontSize , maxHeight + fontSize )
387388 const canvasCtx = canvas . getContext ( '2d' )
388389
@@ -396,6 +397,44 @@ class QuoteGenerate {
396397
397398 const emojis = emojiDb . searchFromText ( { input : text , fixCodePoints : true } )
398399
400+ // Pre-load all emojis for better performance
401+ const emojiCache = new Map ( )
402+ const emojiLoadPromises = [ ]
403+
404+ for ( let emojiIndex = 0 ; emojiIndex < emojis . length ; emojiIndex ++ ) {
405+ const emoji = emojis [ emojiIndex ]
406+ if ( ! emojiCache . has ( emoji . found ) ) {
407+ emojiLoadPromises . push (
408+ ( async ( ) => {
409+ const emojiImageBase = emojiImageJson [ emoji . found ]
410+ if ( emojiImageBase ) {
411+ try {
412+ const image = await loadImage ( Buffer . from ( emojiImageBase , 'base64' ) )
413+ emojiCache . set ( emoji . found , image )
414+ } catch {
415+ try {
416+ const fallbackImage = await loadImage ( Buffer . from ( fallbackEmojiImageJson [ emoji . found ] , 'base64' ) )
417+ emojiCache . set ( emoji . found , fallbackImage )
418+ } catch {
419+ // Skip if both fail
420+ }
421+ }
422+ } else {
423+ try {
424+ const fallbackImage = await loadImage ( Buffer . from ( fallbackEmojiImageJson [ emoji . found ] , 'base64' ) )
425+ emojiCache . set ( emoji . found , fallbackImage )
426+ } catch {
427+ // Skip if fails
428+ }
429+ }
430+ } ) ( )
431+ )
432+ }
433+ }
434+
435+ // Wait for all emojis to load
436+ await Promise . all ( emojiLoadPromises )
437+
399438 for ( let charIndex = 0 ; charIndex < chars . length ; charIndex ++ ) {
400439 const char = chars [ charIndex ]
401440
@@ -534,6 +573,11 @@ class QuoteGenerate {
534573
535574 let breakWrite = false
536575 let lineDirection = this . getLineDirection ( styledWords , 0 )
576+
577+ // Pre-set font to avoid repeated font changes
578+ let currentFont = null
579+ let currentFillStyle = null
580+
537581 for ( let index = 0 ; index < styledWords . length ; index ++ ) {
538582 const styledWord = styledWords [ index ]
539583
@@ -543,17 +587,8 @@ class QuoteGenerate {
543587 if ( styledWord . customEmojiId && customEmojiStickers [ styledWord . customEmojiId ] ) {
544588 emojiImage = customEmojiStickers [ styledWord . customEmojiId ]
545589 } else {
546- const emojiImageBase = emojiImageJson [ styledWord . emoji . code ]
547- if ( emojiImageBase ) {
548- emojiImage = await loadImage (
549- Buffer . from ( emojiImageBase , 'base64' )
550- ) . catch ( ( ) => { } )
551- }
552- if ( ! emojiImage ) {
553- emojiImage = await loadImage (
554- Buffer . from ( fallbackEmojiImageJson [ styledWord . emoji . code ] , 'base64' )
555- ) . catch ( ( ) => { } )
556- }
590+ // Use pre-loaded emoji from cache
591+ emojiImage = emojiCache . get ( styledWord . emoji . code )
557592 }
558593 }
559594
@@ -578,20 +613,37 @@ class QuoteGenerate {
578613 const rbaColor = this . hexToRgb ( this . normalizeColor ( fontColor ) )
579614 fillStyle = `rgba(${ rbaColor [ 0 ] } , ${ rbaColor [ 1 ] } , ${ rbaColor [ 2 ] } , 0.15)`
580615 }
581- // else {
582- // canvasCtx.font = `${fontSize}px OpenSans`
583- // canvasCtx.fillStyle = fontColor
584- // }
585-
586- canvasCtx . font = `${ fontType } ${ fontSize } px ${ fontName } `
587- canvasCtx . fillStyle = fillStyle
588-
589- if ( canvasCtx . measureText ( styledWord . word ) . width > maxWidth - fontSize * 3 ) {
590- while ( canvasCtx . measureText ( styledWord . word ) . width > maxWidth - fontSize * 3 ) {
591- styledWord . word = styledWord . word . substr ( 0 , styledWord . word . length - 1 )
592- if ( styledWord . word . length <= 0 ) break
616+
617+ const newFont = `${ fontType } ${ fontSize } px ${ fontName } `
618+
619+ // Only change font if different from current
620+ if ( currentFont !== newFont ) {
621+ canvasCtx . font = newFont
622+ currentFont = newFont
623+ }
624+
625+ // Only change fill style if different from current
626+ if ( currentFillStyle !== fillStyle ) {
627+ canvasCtx . fillStyle = fillStyle
628+ currentFillStyle = fillStyle
629+ }
630+
631+ // Pre-truncate long words before measurement
632+ let wordToMeasure = styledWord . word
633+ const maxWordWidth = maxWidth - fontSize * 3
634+
635+ if ( wordToMeasure . length > 50 ) { // Quick length check before expensive measurement
636+ while ( canvasCtx . measureText ( wordToMeasure ) . width > maxWordWidth && wordToMeasure . length > 0 ) {
637+ wordToMeasure = wordToMeasure . substr ( 0 , wordToMeasure . length - 1 )
638+ }
639+ if ( wordToMeasure . length < styledWord . word . length ) {
640+ styledWord . word = wordToMeasure + '…'
641+ }
642+ } else if ( canvasCtx . measureText ( wordToMeasure ) . width > maxWordWidth ) {
643+ while ( canvasCtx . measureText ( wordToMeasure ) . width > maxWordWidth && wordToMeasure . length > 0 ) {
644+ wordToMeasure = wordToMeasure . substr ( 0 , wordToMeasure . length - 1 )
593645 }
594- styledWord . word += '…'
646+ styledWord . word = wordToMeasure + '…'
595647 }
596648
597649 let lineWidth
@@ -631,7 +683,7 @@ class QuoteGenerate {
631683 if ( lineWidth > textWidth ) textWidth = lineWidth
632684 if ( textWidth > maxWidth ) textWidth = maxWidth
633685
634- let wordX = ( lineDirection == 'rtl' ) ? maxWidth - lineX - wordlWidth - fontSize * 2 : lineX
686+ let wordX = ( lineDirection === 'rtl' ) ? maxWidth - lineX - wordlWidth - fontSize * 2 : lineX
635687
636688 if ( emojiImage ) {
637689 canvasCtx . drawImage ( emojiImage , wordX , lineY - fontSize + ( fontSize * 0.15 ) , fontSize + ( fontSize * 0.22 ) , fontSize + ( fontSize * 0.22 ) )
0 commit comments