@ -18,7 +18,6 @@ import {
DEFAULT_FONT_FAMILY ,
DEFAULT_FONT_SIZE ,
FONT_FAMILY ,
isSafari ,
TEXT_ALIGN ,
VERTICAL_ALIGN ,
} from "../constants" ;
@ -62,7 +61,6 @@ export const redrawTextBoundingBox = (
text : textElement.text ,
width : textElement.width ,
height : textElement.height ,
baseline : textElement.baseline ,
} ;
boundTextUpdates . text = textElement . text ;
@ -83,7 +81,6 @@ export const redrawTextBoundingBox = (
boundTextUpdates . width = metrics . width ;
boundTextUpdates . height = metrics . height ;
boundTextUpdates . baseline = metrics . baseline ;
if ( container ) {
const maxContainerHeight = getBoundTextMaxHeight (
@ -188,7 +185,6 @@ export const handleBindTextResize = (
const maxWidth = getBoundTextMaxWidth ( container , textElement ) ;
const maxHeight = getBoundTextMaxHeight ( container , textElement ) ;
let containerHeight = container . height ;
let nextBaseLine = textElement . baseline ;
if (
shouldMaintainAspectRatio ||
( transformHandleType !== "n" && transformHandleType !== "s" )
@ -207,7 +203,6 @@ export const handleBindTextResize = (
) ;
nextHeight = metrics . height ;
nextWidth = metrics . width ;
nextBaseLine = metrics . baseline ;
}
// increase height in case text element height exceeds
if ( nextHeight > maxHeight ) {
@ -235,7 +230,6 @@ export const handleBindTextResize = (
text ,
width : nextWidth ,
height : nextHeight ,
baseline : nextBaseLine ,
} ) ;
if ( ! isArrowElement ( container ) ) {
@ -285,8 +279,6 @@ export const computeBoundTextPosition = (
return { x , y } ;
} ;
// https://github.com/grassator/canvas-text-editor/blob/master/lib/FontMetrics.js
export const measureText = (
text : string ,
font : FontString ,
@ -301,59 +293,7 @@ export const measureText = (
const fontSize = parseFloat ( font ) ;
const height = getTextHeight ( text , fontSize , lineHeight ) ;
const width = getTextWidth ( text , font ) ;
const baseline = measureBaseline ( text , font , lineHeight ) ;
return { width , height , baseline } ;
} ;
export const measureBaseline = (
text : string ,
font : FontString ,
lineHeight : ExcalidrawTextElement [ "lineHeight" ] ,
wrapInContainer? : boolean ,
) = > {
const container = document . createElement ( "div" ) ;
container . style . position = "absolute" ;
container . style . whiteSpace = "pre" ;
container . style . font = font ;
container . style . minHeight = "1em" ;
if ( wrapInContainer ) {
container . style . overflow = "hidden" ;
container . style . wordBreak = "break-word" ;
container . style . whiteSpace = "pre-wrap" ;
}
container . style . lineHeight = String ( lineHeight ) ;
container . innerText = text ;
// Baseline is important for positioning text on canvas
document . body . appendChild ( container ) ;
const span = document . createElement ( "span" ) ;
span . style . display = "inline-block" ;
span . style . overflow = "hidden" ;
span . style . width = "1px" ;
span . style . height = "1px" ;
container . appendChild ( span ) ;
let baseline = span . offsetTop + span . offsetHeight ;
const height = container . offsetHeight ;
if ( isSafari ) {
const canvasHeight = getTextHeight ( text , parseFloat ( font ) , lineHeight ) ;
const fontSize = parseFloat ( font ) ;
// In Safari the font size gets rounded off when rendering hence calculating the safari height and shifting the baseline if it differs
// from the actual canvas height
const domHeight = getTextHeight ( text , Math . round ( fontSize ) , lineHeight ) ;
if ( canvasHeight > height ) {
baseline += canvasHeight - domHeight ;
}
if ( height > canvasHeight ) {
baseline -= domHeight - canvasHeight ;
}
}
document . body . removeChild ( container ) ;
return baseline ;
return { width , height } ;
} ;
/ * *
@ -378,6 +318,23 @@ export const getLineHeightInPx = (
return fontSize * lineHeight ;
} ;
/ * *
* Calculates vertical offset for a text with alphabetic baseline .
* /
export const getVerticalOffset = (
fontFamily : ExcalidrawTextElement [ "fontFamily" ] ,
fontSize : ExcalidrawTextElement [ "fontSize" ] ,
lineHeightPx : number ,
) = > {
const { unitsPerEm , ascender , descender } = FONT_METRICS [ fontFamily ] ;
const fontSizeEm = fontSize / unitsPerEm ;
const lineGap = lineHeightPx - fontSizeEm * ascender + fontSizeEm * descender ;
const verticalOffset = fontSizeEm * ascender + lineGap ;
return verticalOffset ;
} ;
// FIXME rename to getApproxMinContainerHeight
export const getApproxMinLineHeight = (
fontSize : ExcalidrawTextElement [ "fontSize" ] ,
@ -964,13 +921,40 @@ const DEFAULT_LINE_HEIGHT = {
// ~1.25 is the average for Virgil in WebKit and Blink.
// Gecko (FF) uses ~1.28.
[ FONT_FAMILY . Virgil ] : 1.25 as ExcalidrawTextElement [ "lineHeight" ] ,
// ~1.15 is the average for Virgil in WebKit and Blink.
// Gecko if all over the place.
// ~1.15 is the average for Helvetica in WebKit and Blink.
[ FONT_FAMILY . Helvetica ] : 1.15 as ExcalidrawTextElement [ "lineHeight" ] ,
// ~1.2 is the average for Virgil in WebKit and Blink, and kinda Gecko too
// ~1.2 is the average for Cascadia in WebKit and Blink, and kinda Gecko too
[ FONT_FAMILY . Cascadia ] : 1.2 as ExcalidrawTextElement [ "lineHeight" ] ,
} ;
type FontMetrics = {
unitsPerEm : number ; // head.unitsPerEm
ascender : number ; // sTypoAscender
descender : number ; // sTypoDescender
} ;
/ * *
* Hardcoded metrics for default fonts , read by https : //opentype.js.org/font-inspector.html.
* For custom fonts , read these metrics on load and extend this object .
* /
const FONT_METRICS = {
[ FONT_FAMILY . Virgil ] : {
unitsPerEm : 1000 ,
ascender : 886 ,
descender : - 374 ,
} ,
[ FONT_FAMILY . Helvetica ] : {
unitsPerEm : 2048 ,
ascender : 1577 ,
descender : - 471 ,
} ,
[ FONT_FAMILY . Cascadia ] : {
unitsPerEm : 2048 ,
ascender : 1977 ,
descender : - 480 ,
} ,
} as Record < number , FontMetrics > ;
export const getDefaultLineHeight = ( fontFamily : FontFamilyValues ) = > {
if ( fontFamily in DEFAULT_LINE_HEIGHT ) {
return DEFAULT_LINE_HEIGHT [ fontFamily ] ;