vue.jskonvajsreact-konvakonvavue-konva

How to pixel perfect align text-element


I want my vue Konva Text element to completely fill the given height, like i expect of a rectangle. This is issue becomes obvious when pairing with text images, (converting svg text to canvas) that properly match the given dimension

Example image

<v-text :config={ 
 x: 50,
 y: 50,
 width: 1000,
 height: 60,
 fontSize: 60,
 fontStyle: 'bold',
 fontFamily 'Campton Book'
 text: 'WELT'
} 
/>
<v-rect 
  :config="{ x: 50, y: 50, fill: 'black', height: 60, width: 200 }"
/>

Second Part, is there any way to always pixel perfectly align the left side with the border? the x coordinate matches the border

enter image description here

Is this due to font constraints? What am I missing?

I tried to get the height of the text node to fix this positioning but this is the given height passed down as props


Solution

  • Text is defined as having parts above and below the baseline. Above is termed 'ascenders' amd below is 'descenders', which are required for lower case letters like j y g.

    Setting the text fontSize to 60 does not say 'whatever the string, make it fill a space 60px high'. Instead it says 'Make text in a 60px font', which makes space for the descenders because they will generally be required.

    If you know for sure that the text will be all caps, then a solution is to measure the height used and increase the font size by a computed factor so that the font fills the line height.

    To do this you'll need to get the glyph measurements as follows:

    const lineHeight = 60; // following your code
    // make your text shape here...left out for brevity
    const metrics = ctx.measureText('YOUR CAPS TEXT');
          capsHeight = Math.abs(metrics.actualBoundingBoxAscent) 
          fontSize = lineHeight * lineHeight / capsHeight;
    

    If I got that right, your 60px case should give a value around 75. That's based on the convention that ascenders are 80% of the line height. Now you set the font size of your shape to this new value and you should be filling the entire line height.

    Regarding the left-alignment, this relies on what the font gurus call the a-b-c widths. The left gap is the a-space, the b is the character width (where the ink falls) and the c-space is the same as the a-space but on the right hand side.

    Sadly unless someone else can tell me I am wrong, you don't get a-b-c widths in the canvas TextMetric. There is a workaround which is rather convoluted but viable. You would draw the text in black on an off-screen canvas filled with a transparent background. Then get the canvas pixel data and walk horizontal lines from the left of the canvas inspecting pixels and looking for the first colored pixel. Once you find it you have the measurement to offset the text shape horizontally.