@@ -1,646 +0,0 @@
/**
* Copyright (c) 2021 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { throwIfFalsy } from 'browser/renderer/RendererUtils' ;
interface IBlockVector {
x : number ;
y : number ;
w : number ;
h : number ;
}
export const blockElementDefinitions : { [ index : string ] : IBlockVector [ ] | undefined } = {
// Block elements (0x2580-0x2590)
'▀' : [ { x : 0 , y : 0 , w : 8 , h : 4 } ] , // UPPER HALF BLOCK
'▁' : [ { x : 0 , y : 7 , w : 8 , h : 1 } ] , // LOWER ONE EIGHTH BLOCK
'▂' : [ { x : 0 , y : 6 , w : 8 , h : 2 } ] , // LOWER ONE QUARTER BLOCK
'▃' : [ { x : 0 , y : 5 , w : 8 , h : 3 } ] , // LOWER THREE EIGHTHS BLOCK
'▄' : [ { x : 0 , y : 4 , w : 8 , h : 4 } ] , // LOWER HALF BLOCK
'▅' : [ { x : 0 , y : 3 , w : 8 , h : 5 } ] , // LOWER FIVE EIGHTHS BLOCK
'▆' : [ { x : 0 , y : 2 , w : 8 , h : 6 } ] , // LOWER THREE QUARTERS BLOCK
'▇' : [ { x : 0 , y : 1 , w : 8 , h : 7 } ] , // LOWER SEVEN EIGHTHS BLOCK
'█' : [ { x : 0 , y : 0 , w : 8 , h : 8 } ] , // FULL BLOCK
'▉' : [ { x : 0 , y : 0 , w : 7 , h : 8 } ] , // LEFT SEVEN EIGHTHS BLOCK
'▊' : [ { x : 0 , y : 0 , w : 6 , h : 8 } ] , // LEFT THREE QUARTERS BLOCK
'▋' : [ { x : 0 , y : 0 , w : 5 , h : 8 } ] , // LEFT FIVE EIGHTHS BLOCK
'▌' : [ { x : 0 , y : 0 , w : 4 , h : 8 } ] , // LEFT HALF BLOCK
'▍' : [ { x : 0 , y : 0 , w : 3 , h : 8 } ] , // LEFT THREE EIGHTHS BLOCK
'▎' : [ { x : 0 , y : 0 , w : 2 , h : 8 } ] , // LEFT ONE QUARTER BLOCK
'▏' : [ { x : 0 , y : 0 , w : 1 , h : 8 } ] , // LEFT ONE EIGHTH BLOCK
'▐' : [ { x : 4 , y : 0 , w : 4 , h : 8 } ] , // RIGHT HALF BLOCK
// Block elements (0x2594-0x2595)
'▔' : [ { x : 0 , y : 0 , w : 9 , h : 1 } ] , // UPPER ONE EIGHTH BLOCK
'▕' : [ { x : 7 , y : 0 , w : 1 , h : 8 } ] , // RIGHT ONE EIGHTH BLOCK
// Terminal graphic characters (0x2596-0x259F)
'▖' : [ { x : 0 , y : 4 , w : 4 , h : 4 } ] , // QUADRANT LOWER LEFT
'▗' : [ { x : 4 , y : 4 , w : 4 , h : 4 } ] , // QUADRANT LOWER RIGHT
'▘' : [ { x : 0 , y : 0 , w : 4 , h : 4 } ] , // QUADRANT UPPER LEFT
'▙' : [ { x : 0 , y : 0 , w : 4 , h : 8 } , { x : 0 , y : 4 , w : 8 , h : 4 } ] , // QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT
'▚' : [ { x : 0 , y : 0 , w : 4 , h : 4 } , { x : 4 , y : 4 , w : 4 , h : 4 } ] , // QUADRANT UPPER LEFT AND LOWER RIGHT
'▛' : [ { x : 0 , y : 0 , w : 4 , h : 8 } , { x : 4 , y : 0 , w : 4 , h : 4 } ] , // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT
'▜' : [ { x : 0 , y : 0 , w : 8 , h : 4 } , { x : 4 , y : 0 , w : 4 , h : 8 } ] , // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT
'▝' : [ { x : 4 , y : 0 , w : 4 , h : 4 } ] , // QUADRANT UPPER RIGHT
'▞' : [ { x : 4 , y : 0 , w : 4 , h : 4 } , { x : 0 , y : 4 , w : 4 , h : 4 } ] , // QUADRANT UPPER RIGHT AND LOWER LEFT
'▟' : [ { x : 4 , y : 0 , w : 4 , h : 8 } , { x : 0 , y : 4 , w : 8 , h : 4 } ] , // QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
// VERTICAL ONE EIGHTH BLOCK-2 through VERTICAL ONE EIGHTH BLOCK-7
'\u{1FB70}' : [ { x : 1 , y : 0 , w : 1 , h : 8 } ] ,
'\u{1FB71}' : [ { x : 2 , y : 0 , w : 1 , h : 8 } ] ,
'\u{1FB72}' : [ { x : 3 , y : 0 , w : 1 , h : 8 } ] ,
'\u{1FB73}' : [ { x : 4 , y : 0 , w : 1 , h : 8 } ] ,
'\u{1FB74}' : [ { x : 5 , y : 0 , w : 1 , h : 8 } ] ,
'\u{1FB75}' : [ { x : 6 , y : 0 , w : 1 , h : 8 } ] ,
// HORIZONTAL ONE EIGHTH BLOCK-2 through HORIZONTAL ONE EIGHTH BLOCK-7
'\u{1FB76}' : [ { x : 0 , y : 1 , w : 8 , h : 1 } ] ,
'\u{1FB77}' : [ { x : 0 , y : 2 , w : 8 , h : 1 } ] ,
'\u{1FB78}' : [ { x : 0 , y : 3 , w : 8 , h : 1 } ] ,
'\u{1FB79}' : [ { x : 0 , y : 4 , w : 8 , h : 1 } ] ,
'\u{1FB7A}' : [ { x : 0 , y : 5 , w : 8 , h : 1 } ] ,
'\u{1FB7B}' : [ { x : 0 , y : 6 , w : 8 , h : 1 } ] ,
// LEFT AND LOWER ONE EIGHTH BLOCK
'\u{1FB7C}' : [ { x : 0 , y : 0 , w : 1 , h : 8 } , { x : 0 , y : 7 , w : 8 , h : 1 } ] ,
// LEFT AND UPPER ONE EIGHTH BLOCK
'\u{1FB7D}' : [ { x : 0 , y : 0 , w : 1 , h : 8 } , { x : 0 , y : 0 , w : 8 , h : 1 } ] ,
// RIGHT AND UPPER ONE EIGHTH BLOCK
'\u{1FB7E}' : [ { x : 7 , y : 0 , w : 1 , h : 8 } , { x : 0 , y : 0 , w : 8 , h : 1 } ] ,
// RIGHT AND LOWER ONE EIGHTH BLOCK
'\u{1FB7F}' : [ { x : 7 , y : 0 , w : 1 , h : 8 } , { x : 0 , y : 7 , w : 8 , h : 1 } ] ,
// UPPER AND LOWER ONE EIGHTH BLOCK
'\u{1FB80}' : [ { x : 0 , y : 0 , w : 8 , h : 1 } , { x : 0 , y : 7 , w : 8 , h : 1 } ] ,
// HORIZONTAL ONE EIGHTH BLOCK-1358
'\u{1FB81}' : [ { x : 0 , y : 0 , w : 8 , h : 1 } , { x : 0 , y : 2 , w : 8 , h : 1 } , { x : 0 , y : 4 , w : 8 , h : 1 } , { x : 0 , y : 7 , w : 8 , h : 1 } ] ,
// UPPER ONE QUARTER BLOCK
'\u{1FB82}' : [ { x : 0 , y : 0 , w : 8 , h : 2 } ] ,
// UPPER THREE EIGHTHS BLOCK
'\u{1FB83}' : [ { x : 0 , y : 0 , w : 8 , h : 3 } ] ,
// UPPER FIVE EIGHTHS BLOCK
'\u{1FB84}' : [ { x : 0 , y : 0 , w : 8 , h : 5 } ] ,
// UPPER THREE QUARTERS BLOCK
'\u{1FB85}' : [ { x : 0 , y : 0 , w : 8 , h : 6 } ] ,
// UPPER SEVEN EIGHTHS BLOCK
'\u{1FB86}' : [ { x : 0 , y : 0 , w : 8 , h : 7 } ] ,
// RIGHT ONE QUARTER BLOCK
'\u{1FB87}' : [ { x : 6 , y : 0 , w : 2 , h : 8 } ] ,
// RIGHT THREE EIGHTHS B0OCK
'\u{1FB88}' : [ { x : 5 , y : 0 , w : 3 , h : 8 } ] ,
// RIGHT FIVE EIGHTHS BL0CK
'\u{1FB89}' : [ { x : 3 , y : 0 , w : 5 , h : 8 } ] ,
// RIGHT THREE QUARTERS 0LOCK
'\u{1FB8A}' : [ { x : 2 , y : 0 , w : 6 , h : 8 } ] ,
// RIGHT SEVEN EIGHTHS B0OCK
'\u{1FB8B}' : [ { x : 1 , y : 0 , w : 7 , h : 8 } ] ,
// CHECKER BOARD FILL
'\u{1FB95}' : [
{ x : 0 , y : 0 , w : 2 , h : 2 } , { x : 4 , y : 0 , w : 2 , h : 2 } ,
{ x : 2 , y : 2 , w : 2 , h : 2 } , { x : 6 , y : 2 , w : 2 , h : 2 } ,
{ x : 0 , y : 4 , w : 2 , h : 2 } , { x : 4 , y : 4 , w : 2 , h : 2 } ,
{ x : 2 , y : 6 , w : 2 , h : 2 } , { x : 6 , y : 6 , w : 2 , h : 2 }
] ,
// INVERSE CHECKER BOARD FILL
'\u{1FB96}' : [
{ x : 2 , y : 0 , w : 2 , h : 2 } , { x : 6 , y : 0 , w : 2 , h : 2 } ,
{ x : 0 , y : 2 , w : 2 , h : 2 } , { x : 4 , y : 2 , w : 2 , h : 2 } ,
{ x : 2 , y : 4 , w : 2 , h : 2 } , { x : 6 , y : 4 , w : 2 , h : 2 } ,
{ x : 0 , y : 6 , w : 2 , h : 2 } , { x : 4 , y : 6 , w : 2 , h : 2 }
] ,
// HEAVY HORIZONTAL FILL (upper middle and lower one quarter block)
'\u{1FB97}' : [ { x : 0 , y : 2 , w : 8 , h : 2 } , { x : 0 , y : 6 , w : 8 , h : 2 } ]
} ;
type PatternDefinition = number [ ] [ ] ;
/**
* Defines the repeating pattern used by special characters, the pattern is made up of a 2d array of
* pixel values to be filled (1) or not filled (0).
*/
const patternCharacterDefinitions : { [ key : string ] : PatternDefinition | undefined } = {
// Shade characters (0x2591-0x2593)
'░' : [ // LIGHT SHADE (25%)
[ 1 , 0 , 0 , 0 ] ,
[ 0 , 0 , 0 , 0 ] ,
[ 0 , 0 , 1 , 0 ] ,
[ 0 , 0 , 0 , 0 ]
] ,
'▒' : [ // MEDIUM SHADE (50%)
[ 1 , 0 ] ,
[ 0 , 0 ] ,
[ 0 , 1 ] ,
[ 0 , 0 ]
] ,
'▓' : [ // DARK SHADE (75%)
[ 0 , 1 ] ,
[ 1 , 1 ] ,
[ 1 , 0 ] ,
[ 1 , 1 ]
]
} ;
const enum Shapes {
/** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1' ,
/** ─ */ LEFT_TO_RIGHT = 'M0,.5 L1,.5' ,
/** └ */ TOP_TO_RIGHT = 'M.5,0 L.5,.5 L1,.5' ,
/** ┘ */ TOP_TO_LEFT = 'M.5,0 L.5,.5 L0,.5' ,
/** ┐ */ LEFT_TO_BOTTOM = 'M0,.5 L.5,.5 L.5,1' ,
/** ┌ */ RIGHT_TO_BOTTOM = 'M0.5,1 L.5,.5 L1,.5' ,
/** ╵ */ MIDDLE_TO_TOP = 'M.5,.5 L.5,0' ,
/** ╴ */ MIDDLE_TO_LEFT = 'M.5,.5 L0,.5' ,
/** ╶ */ MIDDLE_TO_RIGHT = 'M.5,.5 L1,.5' ,
/** ╷ */ MIDDLE_TO_BOTTOM = 'M.5,.5 L.5,1' ,
/** ┴ */ T_TOP = 'M0,.5 L1,.5 M.5,.5 L.5,0' ,
/** ┤ */ T_LEFT = 'M.5,0 L.5,1 M.5,.5 L0,.5' ,
/** ├ */ T_RIGHT = 'M.5,0 L.5,1 M.5,.5 L1,.5' ,
/** ┬ */ T_BOTTOM = 'M0,.5 L1,.5 M.5,.5 L.5,1' ,
/** ┼ */ CROSS = 'M0,.5 L1,.5 M.5,0 L.5,1' ,
/** ╌ */ TWO_DASHES_HORIZONTAL = 'M.1,.5 L.4,.5 M.6,.5 L.9,.5' , // .2 empty, .3 filled
/** ┄ */ THREE_DASHES_HORIZONTAL = 'M.0667,.5 L.2667,.5 M.4,.5 L.6,.5 M.7333,.5 L.9333,.5' , // .1333 empty, .2 filled
/** ┉ */ FOUR_DASHES_HORIZONTAL = 'M.05,.5 L.2,.5 M.3,.5 L.45,.5 M.55,.5 L.7,.5 M.8,.5 L.95,.5' , // .1 empty, .15 filled
/** ╎ */ TWO_DASHES_VERTICAL = 'M.5,.1 L.5,.4 M.5,.6 L.5,.9' ,
/** ┆ */ THREE_DASHES_VERTICAL = 'M.5,.0667 L.5,.2667 M.5,.4 L.5,.6 M.5,.7333 L.5,.9333' ,
/** ┊ */ FOUR_DASHES_VERTICAL = 'M.5,.05 L.5,.2 M.5,.3 L.5,.45 L.5,.55 M.5,.7 L.5,.95' ,
}
const enum Style {
NORMAL = 1 ,
BOLD = 3
}
/**
* @param xp The percentage of 15% of the x axis.
* @param yp The percentage of 15% of the x axis on the y axis.
*/
type DrawFunctionDefinition = ( xp : number , yp : number ) = > string ;
/**
* This contains the definitions of all box drawing characters in the format of SVG paths (ie. the
* svg d attribute).
*/
export const boxDrawingDefinitions : { [ character : string ] : { [ fontWeight : number ] : string | DrawFunctionDefinition } | undefined } = {
// Uniform normal and bold
'─' : { [ Style . NORMAL ] : Shapes . LEFT_TO_RIGHT } ,
'━' : { [ Style . BOLD ] : Shapes . LEFT_TO_RIGHT } ,
'│' : { [ Style . NORMAL ] : Shapes . TOP_TO_BOTTOM } ,
'┃' : { [ Style . BOLD ] : Shapes . TOP_TO_BOTTOM } ,
'┌' : { [ Style . NORMAL ] : Shapes . RIGHT_TO_BOTTOM } ,
'┏' : { [ Style . BOLD ] : Shapes . RIGHT_TO_BOTTOM } ,
'┐' : { [ Style . NORMAL ] : Shapes . LEFT_TO_BOTTOM } ,
'┓' : { [ Style . BOLD ] : Shapes . LEFT_TO_BOTTOM } ,
'└' : { [ Style . NORMAL ] : Shapes . TOP_TO_RIGHT } ,
'┗' : { [ Style . BOLD ] : Shapes . TOP_TO_RIGHT } ,
'┘' : { [ Style . NORMAL ] : Shapes . TOP_TO_LEFT } ,
'┛' : { [ Style . BOLD ] : Shapes . TOP_TO_LEFT } ,
'├' : { [ Style . NORMAL ] : Shapes . T_RIGHT } ,
'┣' : { [ Style . BOLD ] : Shapes . T_RIGHT } ,
'┤' : { [ Style . NORMAL ] : Shapes . T_LEFT } ,
'┫' : { [ Style . BOLD ] : Shapes . T_LEFT } ,
'┬' : { [ Style . NORMAL ] : Shapes . T_BOTTOM } ,
'┳' : { [ Style . BOLD ] : Shapes . T_BOTTOM } ,
'┴' : { [ Style . NORMAL ] : Shapes . T_TOP } ,
'┻' : { [ Style . BOLD ] : Shapes . T_TOP } ,
'┼' : { [ Style . NORMAL ] : Shapes . CROSS } ,
'╋' : { [ Style . BOLD ] : Shapes . CROSS } ,
'╴' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_LEFT } ,
'╸' : { [ Style . BOLD ] : Shapes . MIDDLE_TO_LEFT } ,
'╵' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_TOP } ,
'╹' : { [ Style . BOLD ] : Shapes . MIDDLE_TO_TOP } ,
'╶' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_RIGHT } ,
'╺' : { [ Style . BOLD ] : Shapes . MIDDLE_TO_RIGHT } ,
'╷' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_BOTTOM } ,
'╻' : { [ Style . BOLD ] : Shapes . MIDDLE_TO_BOTTOM } ,
// Double border
'═' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0, ${ . 5 - yp } L1, ${ . 5 - yp } M0, ${ . 5 + yp } L1, ${ . 5 + yp } ` } ,
'║' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M ${ . 5 - xp } ,0 L ${ . 5 - xp } ,1 M ${ . 5 + xp } ,0 L ${ . 5 + xp } ,1 ` } ,
'╒' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M.5,1 L.5, ${ . 5 - yp } L1, ${ . 5 - yp } M.5, ${ . 5 + yp } L1, ${ . 5 + yp } ` } ,
'╓' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M ${ . 5 - xp } ,1 L ${ . 5 - xp } ,.5 L1,.5 M ${ . 5 + xp } ,.5 L ${ . 5 + xp } ,1 ` } ,
'╔' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M1, ${ . 5 - yp } L ${ . 5 - xp } , ${ . 5 - yp } L ${ . 5 - xp } ,1 M1, ${ . 5 + yp } L ${ . 5 + xp } , ${ . 5 + yp } L ${ . 5 + xp } ,1 ` } ,
'╕' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0, ${ . 5 - yp } L.5, ${ . 5 - yp } L.5,1 M0, ${ . 5 + yp } L.5, ${ . 5 + yp } ` } ,
'╖' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M ${ . 5 + xp } ,1 L ${ . 5 + xp } ,.5 L0,.5 M ${ . 5 - xp } ,.5 L ${ . 5 - xp } ,1 ` } ,
'╗' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0, ${ . 5 + yp } L ${ . 5 - xp } , ${ . 5 + yp } L ${ . 5 - xp } ,1 M0, ${ . 5 - yp } L ${ . 5 + xp } , ${ . 5 - yp } L ${ . 5 + xp } ,1 ` } ,
'╘' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M.5,0 L.5, ${ . 5 + yp } L1, ${ . 5 + yp } M.5, ${ . 5 - yp } L1, ${ . 5 - yp } ` } ,
'╙' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M1,.5 L ${ . 5 - xp } ,.5 L ${ . 5 - xp } ,0 M ${ . 5 + xp } ,.5 L ${ . 5 + xp } ,0 ` } ,
'╚' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M1, ${ . 5 - yp } L ${ . 5 + xp } , ${ . 5 - yp } L ${ . 5 + xp } ,0 M1, ${ . 5 + yp } L ${ . 5 - xp } , ${ . 5 + yp } L ${ . 5 - xp } ,0 ` } ,
'╛' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0, ${ . 5 + yp } L.5, ${ . 5 + yp } L.5,0 M0, ${ . 5 - yp } L.5, ${ . 5 - yp } ` } ,
'╜' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0,.5 L ${ . 5 + xp } ,.5 L ${ . 5 + xp } ,0 M ${ . 5 - xp } ,.5 L ${ . 5 - xp } ,0 ` } ,
'╝' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0, ${ . 5 - yp } L ${ . 5 - xp } , ${ . 5 - yp } L ${ . 5 - xp } ,0 M0, ${ . 5 + yp } L ${ . 5 + xp } , ${ . 5 + yp } L ${ . 5 + xp } ,0 ` } ,
'╞' : { [ Style . NORMAL ] : ( xp , yp ) = > ` ${ Shapes . TOP_TO_BOTTOM } M.5, ${ . 5 - yp } L1, ${ . 5 - yp } M.5, ${ . 5 + yp } L1, ${ . 5 + yp } ` } ,
'╟' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M ${ . 5 - xp } ,0 L ${ . 5 - xp } ,1 M ${ . 5 + xp } ,0 L ${ . 5 + xp } ,1 M ${ . 5 + xp } ,.5 L1,.5 ` } ,
'╠' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M ${ . 5 - xp } ,0 L ${ . 5 - xp } ,1 M1, ${ . 5 + yp } L ${ . 5 + xp } , ${ . 5 + yp } L ${ . 5 + xp } ,1 M1, ${ . 5 - yp } L ${ . 5 + xp } , ${ . 5 - yp } L ${ . 5 + xp } ,0 ` } ,
'╡' : { [ Style . NORMAL ] : ( xp , yp ) = > ` ${ Shapes . TOP_TO_BOTTOM } M0, ${ . 5 - yp } L.5, ${ . 5 - yp } M0, ${ . 5 + yp } L.5, ${ . 5 + yp } ` } ,
'╢' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0,.5 L ${ . 5 - xp } ,.5 M ${ . 5 - xp } ,0 L ${ . 5 - xp } ,1 M ${ . 5 + xp } ,0 L ${ . 5 + xp } ,1 ` } ,
'╣' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M ${ . 5 + xp } ,0 L ${ . 5 + xp } ,1 M0, ${ . 5 + yp } L ${ . 5 - xp } , ${ . 5 + yp } L ${ . 5 - xp } ,1 M0, ${ . 5 - yp } L ${ . 5 - xp } , ${ . 5 - yp } L ${ . 5 - xp } ,0 ` } ,
'╤' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0, ${ . 5 - yp } L1, ${ . 5 - yp } M0, ${ . 5 + yp } L1, ${ . 5 + yp } M.5, ${ . 5 + yp } L.5,1 ` } ,
'╥' : { [ Style . NORMAL ] : ( xp , yp ) = > ` ${ Shapes . LEFT_TO_RIGHT } M ${ . 5 - xp } ,.5 L ${ . 5 - xp } ,1 M ${ . 5 + xp } ,.5 L ${ . 5 + xp } ,1 ` } ,
'╦' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0, ${ . 5 - yp } L1, ${ . 5 - yp } M0, ${ . 5 + yp } L ${ . 5 - xp } , ${ . 5 + yp } L ${ . 5 - xp } ,1 M1, ${ . 5 + yp } L ${ . 5 + xp } , ${ . 5 + yp } L ${ . 5 + xp } ,1 ` } ,
'╧' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M.5,0 L.5, ${ . 5 - yp } M0, ${ . 5 - yp } L1, ${ . 5 - yp } M0, ${ . 5 + yp } L1, ${ . 5 + yp } ` } ,
'╨' : { [ Style . NORMAL ] : ( xp , yp ) = > ` ${ Shapes . LEFT_TO_RIGHT } M ${ . 5 - xp } ,.5 L ${ . 5 - xp } ,0 M ${ . 5 + xp } ,.5 L ${ . 5 + xp } ,0 ` } ,
'╩' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0, ${ . 5 + yp } L1, ${ . 5 + yp } M0, ${ . 5 - yp } L ${ . 5 - xp } , ${ . 5 - yp } L ${ . 5 - xp } ,0 M1, ${ . 5 - yp } L ${ . 5 + xp } , ${ . 5 - yp } L ${ . 5 + xp } ,0 ` } ,
'╪' : { [ Style . NORMAL ] : ( xp , yp ) = > ` ${ Shapes . TOP_TO_BOTTOM } M0, ${ . 5 - yp } L1, ${ . 5 - yp } M0, ${ . 5 + yp } L1, ${ . 5 + yp } ` } ,
'╫' : { [ Style . NORMAL ] : ( xp , yp ) = > ` ${ Shapes . LEFT_TO_RIGHT } M ${ . 5 - xp } ,0 L ${ . 5 - xp } ,1 M ${ . 5 + xp } ,0 L ${ . 5 + xp } ,1 ` } ,
'╬' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M0, ${ . 5 + yp } L ${ . 5 - xp } , ${ . 5 + yp } L ${ . 5 - xp } ,1 M1, ${ . 5 + yp } L ${ . 5 + xp } , ${ . 5 + yp } L ${ . 5 + xp } ,1 M0, ${ . 5 - yp } L ${ . 5 - xp } , ${ . 5 - yp } L ${ . 5 - xp } ,0 M1, ${ . 5 - yp } L ${ . 5 + xp } , ${ . 5 - yp } L ${ . 5 + xp } ,0 ` } ,
// Diagonal
'╱ ' : { [ Style . NORMAL ] : 'M1,0 L0,1' } ,
'╲' : { [ Style . NORMAL ] : 'M0,0 L1,1' } ,
'╳ ' : { [ Style . NORMAL ] : 'M1,0 L0,1 M0,0 L1,1' } ,
// Mixed weight
'╼' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_LEFT , [ Style . BOLD ] : Shapes . MIDDLE_TO_RIGHT } ,
'╽' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_TOP , [ Style . BOLD ] : Shapes . MIDDLE_TO_BOTTOM } ,
'╾' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_RIGHT , [ Style . BOLD ] : Shapes . MIDDLE_TO_LEFT } ,
'╿' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_BOTTOM , [ Style . BOLD ] : Shapes . MIDDLE_TO_TOP } ,
'┍' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_BOTTOM , [ Style . BOLD ] : Shapes . MIDDLE_TO_RIGHT } ,
'┎' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_RIGHT , [ Style . BOLD ] : Shapes . MIDDLE_TO_BOTTOM } ,
'┑' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_BOTTOM , [ Style . BOLD ] : Shapes . MIDDLE_TO_LEFT } ,
'┒' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_LEFT , [ Style . BOLD ] : Shapes . MIDDLE_TO_BOTTOM } ,
'┕' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_TOP , [ Style . BOLD ] : Shapes . MIDDLE_TO_RIGHT } ,
'┖' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_RIGHT , [ Style . BOLD ] : Shapes . MIDDLE_TO_TOP } ,
'┙' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_TOP , [ Style . BOLD ] : Shapes . MIDDLE_TO_LEFT } ,
'┚' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_LEFT , [ Style . BOLD ] : Shapes . MIDDLE_TO_TOP } ,
'┝' : { [ Style . NORMAL ] : Shapes . TOP_TO_BOTTOM , [ Style . BOLD ] : Shapes . MIDDLE_TO_RIGHT } ,
'┞' : { [ Style . NORMAL ] : Shapes . RIGHT_TO_BOTTOM , [ Style . BOLD ] : Shapes . MIDDLE_TO_TOP } ,
'┟' : { [ Style . NORMAL ] : Shapes . TOP_TO_RIGHT , [ Style . BOLD ] : Shapes . MIDDLE_TO_BOTTOM } ,
'┠' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_RIGHT , [ Style . BOLD ] : Shapes . TOP_TO_BOTTOM } ,
'┡' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_BOTTOM , [ Style . BOLD ] : Shapes . TOP_TO_RIGHT } ,
'┢' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_TOP , [ Style . BOLD ] : Shapes . RIGHT_TO_BOTTOM } ,
'┥' : { [ Style . NORMAL ] : Shapes . TOP_TO_BOTTOM , [ Style . BOLD ] : Shapes . MIDDLE_TO_LEFT } ,
'┦' : { [ Style . NORMAL ] : Shapes . LEFT_TO_BOTTOM , [ Style . BOLD ] : Shapes . MIDDLE_TO_TOP } ,
'┧' : { [ Style . NORMAL ] : Shapes . TOP_TO_LEFT , [ Style . BOLD ] : Shapes . MIDDLE_TO_BOTTOM } ,
'┨' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_LEFT , [ Style . BOLD ] : Shapes . TOP_TO_BOTTOM } ,
'┩' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_BOTTOM , [ Style . BOLD ] : Shapes . TOP_TO_LEFT } ,
'┪' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_TOP , [ Style . BOLD ] : Shapes . LEFT_TO_BOTTOM } ,
'┭' : { [ Style . NORMAL ] : Shapes . RIGHT_TO_BOTTOM , [ Style . BOLD ] : Shapes . MIDDLE_TO_LEFT } ,
'┮' : { [ Style . NORMAL ] : Shapes . LEFT_TO_BOTTOM , [ Style . BOLD ] : Shapes . MIDDLE_TO_RIGHT } ,
'┯' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_BOTTOM , [ Style . BOLD ] : Shapes . LEFT_TO_RIGHT } ,
'┰' : { [ Style . NORMAL ] : Shapes . LEFT_TO_RIGHT , [ Style . BOLD ] : Shapes . MIDDLE_TO_BOTTOM } ,
'┱' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_RIGHT , [ Style . BOLD ] : Shapes . LEFT_TO_BOTTOM } ,
'┲' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_LEFT , [ Style . BOLD ] : Shapes . RIGHT_TO_BOTTOM } ,
'┵' : { [ Style . NORMAL ] : Shapes . TOP_TO_RIGHT , [ Style . BOLD ] : Shapes . MIDDLE_TO_LEFT } ,
'┶' : { [ Style . NORMAL ] : Shapes . TOP_TO_LEFT , [ Style . BOLD ] : Shapes . MIDDLE_TO_RIGHT } ,
'┷' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_TOP , [ Style . BOLD ] : Shapes . LEFT_TO_RIGHT } ,
'┸' : { [ Style . NORMAL ] : Shapes . LEFT_TO_RIGHT , [ Style . BOLD ] : Shapes . MIDDLE_TO_TOP } ,
'┹' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_RIGHT , [ Style . BOLD ] : Shapes . TOP_TO_LEFT } ,
'┺' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_LEFT , [ Style . BOLD ] : Shapes . TOP_TO_RIGHT } ,
'┽' : { [ Style . NORMAL ] : ` ${ Shapes . TOP_TO_BOTTOM } ${ Shapes . MIDDLE_TO_RIGHT } ` , [ Style . BOLD ] : Shapes . MIDDLE_TO_LEFT } ,
'┾' : { [ Style . NORMAL ] : ` ${ Shapes . TOP_TO_BOTTOM } ${ Shapes . MIDDLE_TO_LEFT } ` , [ Style . BOLD ] : Shapes . MIDDLE_TO_RIGHT } ,
'┿' : { [ Style . NORMAL ] : Shapes . TOP_TO_BOTTOM , [ Style . BOLD ] : Shapes . LEFT_TO_RIGHT } ,
'╀' : { [ Style . NORMAL ] : ` ${ Shapes . LEFT_TO_RIGHT } ${ Shapes . MIDDLE_TO_BOTTOM } ` , [ Style . BOLD ] : Shapes . MIDDLE_TO_TOP } ,
'╁' : { [ Style . NORMAL ] : ` ${ Shapes . MIDDLE_TO_TOP } ${ Shapes . LEFT_TO_RIGHT } ` , [ Style . BOLD ] : Shapes . MIDDLE_TO_BOTTOM } ,
'╂' : { [ Style . NORMAL ] : Shapes . LEFT_TO_RIGHT , [ Style . BOLD ] : Shapes . TOP_TO_BOTTOM } ,
'╃' : { [ Style . NORMAL ] : Shapes . RIGHT_TO_BOTTOM , [ Style . BOLD ] : Shapes . TOP_TO_LEFT } ,
'╄' : { [ Style . NORMAL ] : Shapes . LEFT_TO_BOTTOM , [ Style . BOLD ] : Shapes . TOP_TO_RIGHT } ,
'╅' : { [ Style . NORMAL ] : Shapes . TOP_TO_RIGHT , [ Style . BOLD ] : Shapes . LEFT_TO_BOTTOM } ,
'╆' : { [ Style . NORMAL ] : Shapes . TOP_TO_LEFT , [ Style . BOLD ] : Shapes . RIGHT_TO_BOTTOM } ,
'╇' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_BOTTOM , [ Style . BOLD ] : ` ${ Shapes . MIDDLE_TO_TOP } ${ Shapes . LEFT_TO_RIGHT } ` } ,
'╈' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_TOP , [ Style . BOLD ] : ` ${ Shapes . LEFT_TO_RIGHT } ${ Shapes . MIDDLE_TO_BOTTOM } ` } ,
'╉' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_RIGHT , [ Style . BOLD ] : ` ${ Shapes . TOP_TO_BOTTOM } ${ Shapes . MIDDLE_TO_LEFT } ` } ,
'╊' : { [ Style . NORMAL ] : Shapes . MIDDLE_TO_LEFT , [ Style . BOLD ] : ` ${ Shapes . TOP_TO_BOTTOM } ${ Shapes . MIDDLE_TO_RIGHT } ` } ,
// Dashed
'╌' : { [ Style . NORMAL ] : Shapes . TWO_DASHES_HORIZONTAL } ,
'╍' : { [ Style . BOLD ] : Shapes . TWO_DASHES_HORIZONTAL } ,
'┄' : { [ Style . NORMAL ] : Shapes . THREE_DASHES_HORIZONTAL } ,
'┅' : { [ Style . BOLD ] : Shapes . THREE_DASHES_HORIZONTAL } ,
'┈' : { [ Style . NORMAL ] : Shapes . FOUR_DASHES_HORIZONTAL } ,
'┉' : { [ Style . BOLD ] : Shapes . FOUR_DASHES_HORIZONTAL } ,
'╎' : { [ Style . NORMAL ] : Shapes . TWO_DASHES_VERTICAL } ,
'╏' : { [ Style . BOLD ] : Shapes . TWO_DASHES_VERTICAL } ,
'┆' : { [ Style . NORMAL ] : Shapes . THREE_DASHES_VERTICAL } ,
'┇' : { [ Style . BOLD ] : Shapes . THREE_DASHES_VERTICAL } ,
'┊' : { [ Style . NORMAL ] : Shapes . FOUR_DASHES_VERTICAL } ,
'┋' : { [ Style . BOLD ] : Shapes . FOUR_DASHES_VERTICAL } ,
// Curved
'╭' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M.5,1 L.5, ${ . 5 + ( yp / . 15 * . 5 ) } C.5, ${ . 5 + ( yp / . 15 * . 5 ) } ,.5,.5,1,.5 ` } ,
'╮' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M.5,1 L.5, ${ . 5 + ( yp / . 15 * . 5 ) } C.5, ${ . 5 + ( yp / . 15 * . 5 ) } ,.5,.5,0,.5 ` } ,
'╯' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M.5,0 L.5, ${ . 5 - ( yp / . 15 * . 5 ) } C.5, ${ . 5 - ( yp / . 15 * . 5 ) } ,.5,.5,0,.5 ` } ,
'╰' : { [ Style . NORMAL ] : ( xp , yp ) = > ` M.5,0 L.5, ${ . 5 - ( yp / . 15 * . 5 ) } C.5, ${ . 5 - ( yp / . 15 * . 5 ) } ,.5,.5,1,.5 ` }
} ;
interface IVectorShape {
d : string ;
type : VectorType ;
/** Padding to apply to the vector's x axis in CSS pixels. */
horizontalPadding? : number ;
}
const enum VectorType {
FILL ,
STROKE
}
/**
* This contains the definitions of the primarily used box drawing characters as vector shapes. The
* reason these characters are defined specially is to avoid common problems if a user's font has
* not been patched with powerline characters and also to get pixel perfect rendering as rendering
* issues can occur around AA/SPAA.
*
* Original symbols defined in https://github.com/powerline/fontpatcher
*/
export const powerlineDefinitions : { [ index : string ] : IVectorShape } = {
// Right triangle solid
'\u{E0B0}' : { d : 'M0,0 L1,.5 L0,1' , type : VectorType . FILL } ,
// Right triangle line
'\u{E0B1}' : { d : 'M0,0 L1,.5 L0,1' , type : VectorType . STROKE , horizontalPadding : 0.5 } ,
// Left triangle solid
'\u{E0B2}' : { d : 'M1,0 L0,.5 L1,1' , type : VectorType . FILL } ,
// Left triangle line
'\u{E0B3}' : { d : 'M1,0 L0,.5 L1,1' , type : VectorType . STROKE , horizontalPadding : 0.5 } ,
// Right semi-circle solid,
'\u{E0B4}' : { d : 'M0,0 L0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0' , type : VectorType . FILL } ,
// Right semi-circle line,
'\u{E0B5}' : { d : 'M0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0' , type : VectorType . STROKE } ,
// Left semi-circle solid,
'\u{E0B6}' : { d : 'M1,0 L1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0' , type : VectorType . FILL } ,
// Left semi-circle line,
'\u{E0B7}' : { d : 'M1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0' , type : VectorType . STROKE }
} ;
/**
* Try drawing a custom block element or box drawing character, returning whether it was
* successfully drawn.
*/
export function tryDrawCustomChar (
ctx : CanvasRenderingContext2D ,
c : string ,
xOffset : number ,
yOffset : number ,
scaledCellWidth : number ,
scaledCellHeight : number
) : boolean {
const blockElementDefinition = blockElementDefinitions [ c ] ;
if ( blockElementDefinition ) {
drawBlockElementChar ( ctx , blockElementDefinition , xOffset , yOffset , scaledCellWidth , scaledCellHeight ) ;
return true ;
}
const patternDefinition = patternCharacterDefinitions [ c ] ;
if ( patternDefinition ) {
drawPatternChar ( ctx , patternDefinition , xOffset , yOffset , scaledCellWidth , scaledCellHeight ) ;
return true ;
}
const boxDrawingDefinition = boxDrawingDefinitions [ c ] ;
if ( boxDrawingDefinition ) {
drawBoxDrawingChar ( ctx , boxDrawingDefinition , xOffset , yOffset , scaledCellWidth , scaledCellHeight ) ;
return true ;
}
const powerlineDefinition = powerlineDefinitions [ c ] ;
if ( powerlineDefinition ) {
drawPowerlineChar ( ctx , powerlineDefinition , xOffset , yOffset , scaledCellWidth , scaledCellHeight ) ;
return true ;
}
return false ;
}
function drawBlockElementChar (
ctx : CanvasRenderingContext2D ,
charDefinition : IBlockVector [ ] ,
xOffset : number ,
yOffset : number ,
scaledCellWidth : number ,
scaledCellHeight : number
) : void {
for ( let i = 0 ; i < charDefinition . length ; i ++ ) {
const box = charDefinition [ i ] ;
const xEighth = scaledCellWidth / 8 ;
const yEighth = scaledCellHeight / 8 ;
ctx . fillRect (
xOffset + box . x * xEighth ,
yOffset + box . y * yEighth ,
box . w * xEighth ,
box . h * yEighth
) ;
}
}
const cachedPatterns : Map < PatternDefinition , Map < /* fillStyle */ string , CanvasPattern > > = new Map ( ) ;
function drawPatternChar (
ctx : CanvasRenderingContext2D ,
charDefinition : number [ ] [ ] ,
xOffset : number ,
yOffset : number ,
scaledCellWidth : number ,
scaledCellHeight : number
) : void {
let patternSet = cachedPatterns . get ( charDefinition ) ;
if ( ! patternSet ) {
patternSet = new Map ( ) ;
cachedPatterns . set ( charDefinition , patternSet ) ;
}
const fillStyle = ctx . fillStyle ;
if ( typeof fillStyle !== 'string' ) {
throw new Error ( ` Unexpected fillStyle type " ${ fillStyle } " ` ) ;
}
let pattern = patternSet . get ( fillStyle ) ;
if ( ! pattern ) {
const width = charDefinition [ 0 ] . length ;
const height = charDefinition . length ;
const tmpCanvas = document . createElement ( 'canvas' ) ;
tmpCanvas . width = width ;
tmpCanvas . height = height ;
const tmpCtx = throwIfFalsy ( tmpCanvas . getContext ( '2d' ) ) ;
const imageData = new ImageData ( width , height ) ;
// Extract rgba from fillStyle
let r : number ;
let g : number ;
let b : number ;
let a : number ;
if ( fillStyle . startsWith ( '#' ) ) {
r = parseInt ( fillStyle . slice ( 1 , 3 ) , 16 ) ;
g = parseInt ( fillStyle . slice ( 3 , 5 ) , 16 ) ;
b = parseInt ( fillStyle . slice ( 5 , 7 ) , 16 ) ;
a = fillStyle . length > 7 && parseInt ( fillStyle . slice ( 7 , 9 ) , 16 ) || 1 ;
} else if ( fillStyle . startsWith ( 'rgba' ) ) {
( [ r , g , b , a ] = fillStyle . substring ( 5 , fillStyle . length - 1 ) . split ( ',' ) . map ( e = > parseFloat ( e ) ) ) ;
} else {
throw new Error ( ` Unexpected fillStyle color format " ${ fillStyle } " when drawing pattern glyph ` ) ;
}
for ( let y = 0 ; y < height ; y ++ ) {
for ( let x = 0 ; x < width ; x ++ ) {
imageData . data [ ( y * width + x ) * 4 ] = r ;
imageData . data [ ( y * width + x ) * 4 + 1 ] = g ;
imageData . data [ ( y * width + x ) * 4 + 2 ] = b ;
imageData . data [ ( y * width + x ) * 4 + 3 ] = charDefinition [ y ] [ x ] * ( a * 255 ) ;
}
}
tmpCtx . putImageData ( imageData , 0 , 0 ) ;
pattern = throwIfFalsy ( ctx . createPattern ( tmpCanvas , null ) ) ;
patternSet . set ( fillStyle , pattern ) ;
}
ctx . fillStyle = pattern ;
ctx . fillRect ( xOffset , yOffset , scaledCellWidth , scaledCellHeight ) ;
}
/**
* Draws the following box drawing characters by mapping a subset of SVG d attribute instructions to
* canvas draw calls.
*
* Box styles: ┎┰┒┍┯┑╓╥╖╒╤╕ ┏┳┓┌┲┓┌┬┐┏┱┐
* ┌─┬─┐ ┏━┳━┓ ╔═╦═╗ ┠╂┨┝┿┥╟╫╢╞╪╡ ┡╇┩├╊┫┢╈┪┣╉┤
* │ │ │ ┃ ┃ ┃ ║ ║ ║ ┖┸┚┕┷┙╙╨╜╘╧╛ └┴┘└┺┛┗┻┛┗┹┘
* ├─┼─┤ ┣━╋━┫ ╠═╬═╣ ┏┱┐┌┲┓┌┬┐┌┬┐ ┏┳┓┌┮┓┌┬┐┏┭┐
* │ │ │ ┃ ┃ ┃ ║ ║ ║ ┡╃┤├╄┩├╆┪┢╅┤ ┞╀┦├┾┫┟╁┧┣┽┤
* └─┴─┘ ┗━┻━┛ ╚═╩═╝ └┴┘└┴┘└┺┛┗┹┘ └┴┘└┶┛┗┻┛┗┵┘
*
* Other:
* ╭─╮ ╲ ╱ ╷╻╎╏┆┇┊┋ ╺╾╴ ╌╌╌ ┄┄┄ ┈┈┈
* │ │ ╳ ╽╿╎╏┆┇┊┋ ╶╼╸ ╍╍╍ ┅┅┅ ┉┉┉
* ╰─╯ ╱ ╲ ╹╵╎╏┆┇┊┋
*
* All box drawing characters:
* ─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏
* ┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟
* ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯
* ┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿
* ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏
* ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟
* ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯
* ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿
*
* ---
*
* Box drawing alignment tests: █
* ▉
* ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
* ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
* ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
* ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
* ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
* ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
* ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
*
* Source: https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html
*/
function drawBoxDrawingChar (
ctx : CanvasRenderingContext2D ,
charDefinition : { [ fontWeight : number ] : string | ( ( xp : number , yp : number ) = > string ) } ,
xOffset : number ,
yOffset : number ,
scaledCellWidth : number ,
scaledCellHeight : number
) : void {
ctx . strokeStyle = ctx . fillStyle ;
for ( const [ fontWeight , instructions ] of Object . entries ( charDefinition ) ) {
ctx . beginPath ( ) ;
ctx . lineWidth = window . devicePixelRatio * Number . parseInt ( fontWeight ) ;
let actualInstructions : string ;
if ( typeof instructions === 'function' ) {
const xp = . 15 ;
const yp = . 15 / scaledCellHeight * scaledCellWidth ;
actualInstructions = instructions ( xp , yp ) ;
} else {
actualInstructions = instructions ;
}
for ( const instruction of actualInstructions . split ( ' ' ) ) {
const type = instruction [ 0 ] ;
const f = svgToCanvasInstructionMap [ type ] ;
if ( ! f ) {
console . error ( ` Could not find drawing instructions for " ${ type } " ` ) ;
continue ;
}
const args : string [ ] = instruction . substring ( 1 ) . split ( ',' ) ;
if ( ! args [ 0 ] || ! args [ 1 ] ) {
continue ;
}
f ( ctx , translateArgs ( args , scaledCellWidth , scaledCellHeight , xOffset , yOffset ) ) ;
}
ctx . stroke ( ) ;
ctx . closePath ( ) ;
}
}
function drawPowerlineChar (
ctx : CanvasRenderingContext2D ,
charDefinition : IVectorShape ,
xOffset : number ,
yOffset : number ,
scaledCellWidth : number ,
scaledCellHeight : number
) : void {
ctx . beginPath ( ) ;
ctx . lineWidth = window . devicePixelRatio ;
for ( const instruction of charDefinition . d . split ( ' ' ) ) {
const type = instruction [ 0 ] ;
const f = svgToCanvasInstructionMap [ type ] ;
if ( ! f ) {
console . error ( ` Could not find drawing instructions for " ${ type } " ` ) ;
continue ;
}
const args : string [ ] = instruction . substring ( 1 ) . split ( ',' ) ;
if ( ! args [ 0 ] || ! args [ 1 ] ) {
continue ;
}
f ( ctx , translateArgs ( args , scaledCellWidth , scaledCellHeight , xOffset , yOffset , charDefinition . horizontalPadding ) ) ;
}
if ( charDefinition . type === VectorType . STROKE ) {
ctx . strokeStyle = ctx . fillStyle ;
ctx . stroke ( ) ;
} else {
ctx . fill ( ) ;
}
ctx . closePath ( ) ;
}
function clamp ( value : number , max : number , min : number = 0 ) : number {
return Math . max ( Math . min ( value , max ) , min ) ;
}
const svgToCanvasInstructionMap : { [ index : string ] : any } = {
'C' : ( ctx : CanvasRenderingContext2D , args : number [ ] ) = > ctx . bezierCurveTo ( args [ 0 ] , args [ 1 ] , args [ 2 ] , args [ 3 ] , args [ 4 ] , args [ 5 ] ) ,
'L' : ( ctx : CanvasRenderingContext2D , args : number [ ] ) = > ctx . lineTo ( args [ 0 ] , args [ 1 ] ) ,
'M' : ( ctx : CanvasRenderingContext2D , args : number [ ] ) = > ctx . moveTo ( args [ 0 ] , args [ 1 ] )
} ;
function translateArgs ( args : string [ ] , cellWidth : number , cellHeight : number , xOffset : number , yOffset : number , horizontalPadding : number = 0 ) : number [ ] {
const result = args . map ( e = > parseFloat ( e ) || parseInt ( e ) ) ;
if ( result . length < 2 ) {
throw new Error ( 'Too few arguments for instruction' ) ;
}
for ( let x = 0 ; x < result . length ; x += 2 ) {
// Translate from 0-1 to 0-cellWidth
result [ x ] *= cellWidth - ( horizontalPadding * 2 * window . devicePixelRatio ) ;
// Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
// line at 100% devicePixelRatio
if ( result [ x ] !== 0 ) {
result [ x ] = clamp ( Math . round ( result [ x ] + 0.5 ) - 0.5 , cellWidth , 0 ) ;
}
// Apply the cell's offset (ie. x*cellWidth)
result [ x ] += xOffset + ( horizontalPadding * window . devicePixelRatio ) ;
}
for ( let y = 1 ; y < result . length ; y += 2 ) {
// Translate from 0-1 to 0-cellHeight
result [ y ] *= cellHeight ;
// Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
// line at 100% devicePixelRatio
if ( result [ y ] !== 0 ) {
result [ y ] = clamp ( Math . round ( result [ y ] + 0.5 ) - 0.5 , cellHeight , 0 ) ;
}
// Apply the cell's offset (ie. x*cellHeight)
result [ y ] += yOffset ;
}
return result ;
}