easysvg icon indicating copy to clipboard operation
easysvg copied to clipboard

Vertical Alignment Off

Open brianwozeniak opened this issue 6 years ago • 6 comments

I am trying to vertically align text, but its definitely off. When I look at your textDimensions method I am a little confused on exactly what this calculation does:

$lineHeight = ( $this->font->ascent + $this->font->descent ) * $fontSize * 2;

Is this really getting the line height of the text? The ascent usually comes in as a positive number, and the descent a negative number. 1 + -1 = 0, how is this measuring distance? Could this be related to to the vertical centering issue?

brianwozeniak avatar Feb 15 '19 02:02 brianwozeniak

You are right, these calculations are off ! This was made a long time ago and I am actually surprised it has been used for so long without anyone noticing.

I can't write the code right now so if you want to give it a go, please do.

It should be something along those lines (to include multi line text vertical centering) :

  1. calculate the "real" text height (which is actually calculated by adding ascent and descent)
  2. based on the line height set by the user, calculate the distance between two lines
  3. count the number of lines (by counting the number of \n)
  4. get the total height of the text nb of lines * real text height + (nb of lines - 1) * distance between lines
  5. move the path by (svg height - total text height) / 2

There is probably a check to be made in the code that the bad calculation isn't used somewhere that would mess up the display

Thanks to anyone who would tackle this issue, please send a PR including your own name in a Contributors section of the README (to be created)

kartsims avatar Feb 19 '19 06:02 kartsims

  1. calculate the "real" text height (which is actually calculated by adding [ascent and descent]

I still think you need to subtract these two, or at least take the absolute value of each before adding them. I ran your code through a debugger and the descent is negative which means its decreasing the total height if adding them together.

Your x-center is being calculated correctly, but for my code I am just manually passing in a Y value now instead of 'center'. I know my formula isn't accurate, but for my situation it gets me in the ballpark which is good enough and lets me move on with my project:

$yCenter = ($heightOfCanvas / 2) - ($fontSize / 1.2)

Dividing the font size by 1.2 for my situation solves the problem for my specific case, but I know that is likely an arbitrary value depending on the font. I think the 1.2 represents the distance between the ascent and top, and the descent and bottom since if you don't take that white space area into the calculation it tends to make it look centered to high or too low. I tried numerous font sizes and it is perfectly centering on the y-axis for me. I am using Open Sans.

brianwozeniak avatar Feb 19 '19 16:02 brianwozeniak

Instead of jumping through those hoops to calculate the dimensions, couldn't you just figure that from a <path> generated similarly to what you do in addText(), by reading coordinates and calculating a bounding box? I'm trying to get this to generate text using the Furore font, and it has some extra horizontal space (which seems a problem in the font itself) in addition to the existing issues detailed in this thread, so I wonder if such an approach would solve both issues?

Ambient-Impact avatar Nov 25 '19 14:11 Ambient-Impact

What about the possibility of parsing the path itself? I found this from part of the meyfa/php-svg library: https://github.com/meyfa/php-svg/blob/main/src/Rasterization/Path/PathParser.php

Feed it the 'd' attribute of the 'path' element. I added something like this to it to get maximums, minimums and relative width and height. I'm assuming the min values represent the top left for translations?:

    public function getBounds($description)
    {
        $bounds = [ 'minX' => 0, 'minY' => 0, 'maxX' => 0, 'maxY' => 0 ];

        foreach($this->parse($description) as $p) {
            if(count($p['args']) == 2) {
                [$x,$y] = $p['args'];
                if($x < $bounds['minX']) $bounds['minX'] = $x;
                if($x > $bounds['maxX']) $bounds['maxX'] = $x;
                if($y < $bounds['minY']) $bounds['minY'] = $y;
                if($y > $bounds['maxY']) $bounds['maxY'] = $y;
                $foo = 'bar';
            }
        };
        $bounds['width'] = $bounds['maxX'] - $bounds['minX'];
        $bounds['height'] = $bounds['maxY'] - $bounds['minY'];
        return $bounds;
    }

I'm still toying with it, but you can do things like $easySvg->addAttribute("viewBox", "0 219 110 68") or add a translate:

        $svgData = simplexml_load_string($easySvg->asXML());
        $svgData->path->addAttribute('transform', "translate(0,40));
        return $svgData->asXML();

(such attributes can be passed in to the addText, but you can't parse the path until it's generated and the svg property is protected. but presumably, something similar could be done inside the addText itself after the path is generated)

treii28 avatar Sep 20 '22 18:09 treii28

@treii28 I am afraid this approach wouldn't work because for bezier curves (widely used in fonts) the points coordinates aren't always the max X/Y of the line described

@Ambient-Impact that makes sense but you will be getting the vertical alignment based on the min/max height, and depending on the characters, on a multiline text for instance you'd get weird results.

ie.

hi fi
poo
long

each line would be aligned differently and the end result would look off

kartsims avatar Sep 22 '22 08:09 kartsims

I am very interested in the outcome of this issue or if anyone has any tips about it.

raphaelbastide avatar Jan 15 '23 14:01 raphaelbastide