@@ -807,149 +807,212 @@ var platformLibrary =
807807
808808 var fontName = UTF8ToString ( _fontName ) ;
809809 var a = fontName . split ( '/' ) ;
810- var fontName = a [ a . length - 1 ] ; // filename
810+ fontName = a [ a . length - 1 ] ; // filename
811811
812- var a = fontName . split ( '.' ) ;
812+ a = fontName . split ( '.' ) ;
813813 fontName = a [ 0 ] ;
814814 var ext = a [ 1 ] ;
815815
816816 var canva = document . createElement ( 'canvas' ) ;
817- canva . width = canvas . width ;
818- canva . height = canvas . height ;
819- canva . style . position = "absolute" ;
820817 var ctx = canva . getContext ( "2d" ) ;
821818
822819 if ( Module . isSafari ) {
823820 ctx . fillStyle = 'red' ;
824821 }
825822
826823 // check if font exists
827- // the text whose final pixel size I want to measure
828824 var testtext = "ABCM|abcdefghijklmnopqrstuvwxyz0123456789" ;
829825
830- // specifying the baseline font
831826 ctx . font = "72px monospace" ;
832-
833- // checking the size of the baseline text
834827 var baselineSize = ctx . measureText ( testtext ) . width ;
835828
836- // specifying the font whose existence we want to check
837829 ctx . font = "72px '" + fontName + "', monospace" ;
838-
839- // checking the size of the font we want to check
840830 var newSize = ctx . measureText ( testtext ) . width ;
841831
842- // If the size of the two text instances is the same, the font does not exist because it is being rendered
843- fontExist = newSize != baselineSize ;
832+ var fontExist = newSize != baselineSize ;
844833
845834 if ( fontName === '' || fontExist == false ) {
846- // console.log(fontName + ' not found, using sans-serif');
847- fontName = 'sans-serif' ; // Default value
835+ // console.log(fontName + ' not found, using sans-serif');
836+ fontName = 'sans-serif' ;
848837 }
849- ctx . font = String ( fontSize ) + 'px ' + fontName ;
850838
851- ctx . textBaseline = 'top' ;
839+ // ensure font is loaded (prevents Firefox fallback metrics)
840+ if ( document . fonts && document . fonts . load ) {
841+ try {
842+ document . fonts . load ( fontSize + 'px "' + fontName + '"' ) ;
843+ } catch ( e ) {
844+ // ignore
845+ }
846+ }
847+
848+ ctx . font = String ( fontSize ) + 'px ' + fontName ;
852849 ctx . textAlign = alignment ;
850+ ctx . textBaseline = 'alphabetic' ; // safer baseline across browsers
851+
852+ // measure proper font metrics for vertical alignment
853+ var testMetrics = ctx . measureText ( "Mg'" ) ;
854+ var ascent = testMetrics . actualBoundingBoxAscent || fontSize * 0.75 ;
855+ var descent = testMetrics . actualBoundingBoxDescent || fontSize * 0.25 ;
856+ var lineHeight = ascent + descent ;
857+
858+ // Extra space for punctuation that extends above normal ascent
859+ var topExtraPadding = fontSize * 0.2 ;
853860
854- var a = measureText ( testtext , false , fontName , fontSize ) ;
855- var lineHeight = a [ 1 ] ;
861+ // Anti-aliasing padding
862+ var horizontalPadding = Math . ceil ( fontSize * 0.15 ) ;
856863
864+ // calculate actual content dimensions first
865+ var maxWidth = w ;
866+ var useFixedWidth = ( w > 0 ) ;
867+
857868 if ( w == 0 ) {
858- // calc width
869+ // calc width dynamically
870+ maxWidth = 0 ;
859871 var line = '' ;
860872 for ( var i = 0 ; i < text . length ; i ++ ) {
861873 if ( text . charAt ( i ) == '\n' ) {
862874 line = '' ;
863- }
864- else {
875+ } else {
865876 line += text . charAt ( i ) ;
866877 var metrics = ctx . measureText ( line ) ;
867- if ( metrics . width > w ) {
868- w = metrics . width ;
878+ if ( metrics . width > maxWidth ) {
879+ maxWidth = metrics . width ;
869880 }
870881 }
871882 }
872- // last line
873883 var metrics = ctx . measureText ( line ) ;
874- w = Math . max ( w , metrics . width ) ;
884+ maxWidth = Math . max ( maxWidth , metrics . width ) ;
875885 }
876886
877- var x = 0 ;
878- var y = 0 ;
879- if ( alignment === 'right' ) {
880- x = w ;
881- }
882- else
883- if ( alignment === 'center' ) {
884- x = w / 2 ;
887+ // count lines to calculate height
888+ var lineCount = 1 ;
889+ var line = '' ;
890+ var effectiveMaxWidth = useFixedWidth ? ( w - ( horizontalPadding * 2 ) ) : maxWidth ;
891+
892+ for ( var i = 0 ; i < text . length ; i ++ ) {
893+ if ( text . charAt ( i ) == '\n' ) {
894+ lineCount ++ ;
895+ line = '' ;
896+ } else {
897+ var testLine = line + text . charAt ( i ) ;
898+ var metrics = ctx . measureText ( testLine ) ;
899+ if ( metrics . width > effectiveMaxWidth ) {
900+ lineCount ++ ;
901+ if ( text . charAt ( i ) === ' ' ) {
902+ line = '' ;
903+ } else {
904+ var a = line . split ( ' ' ) ;
905+ if ( a . length > 1 ) {
906+ line = a [ a . length - 1 ] + text . charAt ( i ) ;
907+ } else {
908+ line = text . charAt ( i ) ;
909+ }
910+ }
911+ } else {
912+ line = testLine ;
913+ }
885914 }
915+ }
916+
917+ // calculate required dimensions with proper padding
918+ var topPadding = topExtraPadding ; // padding to prevent clipping
919+ var calculatedHeight = ( lineCount * lineHeight ) + topPadding ;
920+ var calculatedWidth = maxWidth + ( horizontalPadding * 2 ) ;
921+
922+ // set canvas to exact required size
923+ canva . width = useFixedWidth ? w : Math . ceil ( calculatedWidth ) ;
924+ canva . height = h > 0 ? h : Math . ceil ( calculatedHeight ) ;
925+ canva . style . position = "absolute" ;
926+
927+ // re-apply font and styles after canvas resize (canvas resets context)
928+ ctx . font = String ( fontSize ) + 'px ' + fontName ;
929+ ctx . textAlign = alignment ;
930+ ctx . textBaseline = 'alphabetic' ;
931+
932+ if ( Module . isSafari ) {
933+ ctx . fillStyle = 'red' ;
934+ }
886935
887- // wrap text
936+ var y = ascent + topPadding ;
937+ var x = 0 ;
938+
939+ if ( alignment === 'left' ) {
940+ x = horizontalPadding ;
941+ } else if ( alignment === 'right' ) {
942+ x = canva . width - horizontalPadding ;
943+ } else if ( alignment === 'center' ) {
944+ x = canva . width / 2 ;
945+ }
888946
889- var ww = 0 ;
890- var hh = 0 ;
947+ // render text
891948 var line = '' ;
892949 for ( var i = 0 ; i < text . length ; i ++ ) {
893950 if ( text . charAt ( i ) == '\n' ) {
894951 ctx . fillText ( line , x , y ) ;
895952 line = '' ;
896953 y += lineHeight ;
897- }
898- else {
954+ } else {
899955 var testLine = line + text . charAt ( i ) ;
900956 var metrics = ctx . measureText ( testLine ) ;
901- if ( metrics . width > w ) {
957+ if ( metrics . width > effectiveMaxWidth ) {
902958 if ( text . charAt ( i ) === ' ' ) {
903959 // ignore last space
904960 ctx . fillText ( line , x , y ) ;
905961 line = '' ;
906- }
907- else {
962+ } else {
908963 // delete last uncomplete word if space exists
909964 var a = line . split ( ' ' ) ;
910- if ( a . length > 1 ) {
911- var line = a [ a . length - 1 ] + text . charAt ( i ) ; // the beginning of next line
912- a . pop ( ) ; // remove last
965+ if ( a . length > 1 ) {
966+ line = a [ a . length - 1 ] + text . charAt ( i ) ; // beginning of next line
967+ a . pop ( ) ;
913968 var s = a . join ( ' ' ) ;
914969 ctx . fillText ( s , x , y ) ;
915- }
916- else {
917- // no words, draw line as is
970+ } else {
971+ // no words, draw line as is
918972 ctx . fillText ( line , x , y ) ;
919- line = text . charAt ( i ) ; // the beginning of next line
973+ line = text . charAt ( i ) ;
920974 }
921975 }
922976 y += lineHeight ;
923- }
924- else {
977+ } else {
925978 line = testLine ;
926979 }
927980 }
928981 }
929982
930983 // last line
931- ctx . fillText ( line , x , y ) ;
984+ ctx . fillText ( line , Math . round ( x ) , Math . round ( y ) ) ;
932985
933- hh = h > 0 ? h : y + lineHeight ;
934- ww = w > 0 ? w : 1 ;
986+ var ww = canva . width ;
987+ var hh = canva . height ;
935988
936- // it's needs for corona ?
989+ // make width 4-byte aligned
937990 if ( ( ww & 0x3 ) != 0 ) {
938991 ww = ( ww + 3 ) & - 4 ;
939992 }
940993
941- //console.log('render: ', metrics , text, w, h, ww, hh, alignment, fontName, fontSize);
994+ // console.log('render: ', text, w, h, ww, hh, alignment, fontName, fontSize);
942995
943- var myImageData = ctx . getImageData ( 0 , 0 , ww , hh ) ;
996+ var myImageData = ctx . getImageData ( 0 , 0 , canva . width , canva . height ) ;
944997 var img = Module . jarray2carray ( myImageData . data ) ;
945- _jsEmscriptenBitmapSaveImage ( thiz , myImageData . data . length , img , myImageData . width , myImageData . height , Module . isSafari ) ;
998+ _jsEmscriptenBitmapSaveImage (
999+ thiz ,
1000+ myImageData . data . length ,
1001+ img ,
1002+ myImageData . width ,
1003+ myImageData . height ,
1004+ Module . isSafari
1005+ ) ;
9461006 _free ( img ) ;
9471007
948- //var body = document.getElementsByTagName("body")[0];
949- //body.appendChild(canva);
950- //canva.remove();
1008+ // optional debug
1009+ // var body = document.getElementsByTagName("body")[0];
1010+ // body.appendChild(canva);
1011+ // canva.remove();
9511012 } ,
9521013
1014+
1015+
9531016 jsContextSetClearColor : function ( r , g , b , a )
9541017 {
9551018 var rgba = 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')' ;
0 commit comments