export class Font { constructor ( public fontFamily = "Arial" , public fontSize = 12 , public fontWeight = "normal" , public fontStyle = "normal" ) { } hash ( ) { return "F" + this . fontFamily + "," + this . fontSize + "," + this . fontWeight + "," + this . fontStyle ; } setFont ( c ) { c . font = `${ this . fontStyle } ${ this . fontWeight } ${ this . fontSize }px ${ JSON . stringify ( this . fontFamily ) }` ; } } export interface TextCacheEntry { x : number ; y : number ; x_offset : number ; baseline_offset : number ; bbox_width : number ; bbox_height : number ; } export class TextCache { entries : { [ name ] : TextCacheEntry ; } ; current_x : number ; current_y : number ; current_height : number ; width : number ; height : number ; scaling : number ; canvas : HTMLCanvasElement ; context : CanvasRenderingContext2D ; updated : boolean ; constructor ( width = 1024 , height = 1024 , scaling = 2 ) { this . entries = { } ; this . current_x = 0 ; this . current_y = 0 ; this . current_height = 0 ; this . width = width ; this . height = height ; this . canvas = document . createElement ( "canvas" ) ; this . context = this . canvas . getContext ( "2d" ) ; this . canvas . width = this . width * scaling ; this . canvas . height = this . height * scaling ; this . scaling = scaling ; this . context . scale ( scaling , scaling ) ; this . context . fillStyle = "#000000" ; } addText ( text , font ) { var hash = JSON . stringify ( text ) + font . hash ( ) ; if ( this . entries [ hash ] ) { return this . _layout2TextRect ( this . entries [ hash ] ) ; } font . setFont ( this . context ) ; var width = this . context . measureText ( text ) . width ; var height = font . fontSize ; var bbox_width = Math . ceil ( width + 4 ) ; var bbox_height = Math . ceil ( height + 2 ) ; var x_offset = 2 ; var baseline_offset = height - 2 ; if ( bbox_width > this . width ) { throw "E_FIT" ; } if ( this . current_x + bbox_width > this . width ) { if ( this . current_y + this . current_height + bbox_height > this . height ) { throw "E_FIT" ; } this . current_x = 0 ; this . current_y += this . current_height ; this . current_height = bbox_height ; } var layout = { x : this . current_x , y : this . current_y , x_offset : x_offset , baseline_offset : baseline_offset , bbox_width : bbox_width , bbox_height : bbox_height } ; this . current_x += bbox_width ; this . current_height = Math . max ( this . current_height , bbox_height ) ; this . entries [ hash ] = layout ; var draw_x = layout . x + x_offset ; var draw_y = layout . y + baseline_offset ; this . context . fillText ( text , draw_x , draw_y ) ; this . updated = true ; return this . _layout2TextRect ( layout ) ; } ; _layout2TextRect ( layout ) { return { x : layout . x * this . scaling , y : layout . y * this . scaling , w : layout . bbox_width * this . scaling , h : layout . bbox_height * this . scaling , scaling : this . scaling , x_offset : layout . x_offset * this . scaling , baseline_offset : layout . baseline_offset * this . scaling } ; } getTextRect ( text , font ) { var hash = JSON . stringify ( text ) + font . hash ( ) ; var layout = this . entries [ hash ] ; if ( ! layout ) return null ; return this . _layout2TextRect ( layout ) ; } clear ( ) { this . entries = { } ; this . current_x = 0 ; this . current_y = 0 ; this . current_height = 0 ; this . context . clearRect ( 0 , 0 , this . canvas . width , this . canvas . height ) ; this . updated = true ; } }