tkrkt / text2png

text-to-png generator for Node.js
MIT License
187 stars 51 forks source link

Rendered font is smaller than declared font-size #2

Open robbytx opened 7 years ago

robbytx commented 7 years ago

Using https://github.com/image-size/image-size:

> require('image-size')(require('text2png')('Line 1', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 31, height: 8, type: 'png' }
> require('image-size')(require('text2png')('Line 1\nLine 2', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 33, height: 17, type: 'png' }

It appears that the problem is the use of actualBoundingBoxAscent/Descent rather than emHeightAscent/Descent, since if I switch the code to use those references, I get:

> require('image-size')(require('./text2png-using-emheight')('Line 1', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 31, height: 12, type: 'png' }
> require('image-size')(require('./text2png-using-emheight')('Line 1\nLine 2', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 33, height: 24, type: 'png' }

Furthermore, because the canvas is properly sized, the text is rendered much clearer.

https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics has a description of the difference of the metrics.

robbytx commented 7 years ago

The above behavior was reproduced on:

$ npm ls
└─┬ text2png@1.0.1
  └─┬ canvas@1.6.5
    ├── nan@2.6.2
    ├─┬ parse-css-font@2.0.2
    │ ├── css-font-size-keywords@1.0.0
    │ ├── css-font-stretch-keywords@1.0.1
    │ ├── css-font-style-keywords@1.0.1
    │ ├── css-font-weight-keywords@1.0.0
    │ ├── css-global-keywords@1.0.1
    │ ├─┬ css-list-helpers@1.0.1
    │ │ └── tcomb@2.7.0 deduped
    │ ├── css-system-font-keywords@1.0.0
    │ ├── tcomb@2.7.0
    │ └── unquote@1.1.0
    └─┬ units-css@0.4.0
      ├── isnumeric@0.2.0
      └── viewport-dimensions@0.2.0

$ brew info cairo
cairo: stable 1.14.10 (bottled), HEAD
Vector graphics library with cross-device output support
https://cairographics.org/
/usr/local/Cellar/cairo/1.14.10 (118 files, 5.9MB) *
  Poured from bottle on 2017-06-22 at 14:37:54
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/cairo.rb
robbytx commented 7 years ago

With cairo 1.12.14 on Amazon Linux, it behaves quite differently:

$ rpm -q cairo cairo-devel
cairo-1.12.14-6.8.amzn1.x86_64
cairo-devel-1.12.14-6.8.amzn1.x86_64

$ node
> require('image-size')(require('text2png')('Line 1', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 35, height: 10, type: 'png' }
> require('image-size')(require('text2png')('Line 1\nLine 2', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 36, height: 20, type: 'png' }
# versus:
> require('image-size')(require('./text2png-using-emheight')('Line 1', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 35, height: 15, type: 'png' }
> require('image-size')(require('./text2png-using-emheight')('Line 1\nLine 2', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 36, height: 30, type: 'png' }
robbytx commented 7 years ago

One difference on Mac was that I didn't have "pango" installed. Once I installed that, text2png behaves similarly across the two platforms. However, using emHeightAscent/Descent still differs:

$ brew info pango
pango: stable 1.40.7 (bottled), HEAD
Framework for layout and rendering of i18n text
http://www.pango.org/
/usr/local/Cellar/pango/1.40.7 (105 files, 4.4MB) *
  Poured from bottle on 2017-08-03 at 18:59:23
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/pango.rb

$ node
> require('image-size')(require('text2png')('Line 1', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 33, height: 10, type: 'png' }
> require('image-size')(require('text2png')('Line 1\nLine 2', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 33, height: 20, type: 'png' }
> require('image-size')(require('./text2png-using-emheight')('Line 1', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 33, height: 12, type: 'png' }
> require('image-size')(require('./text2png-using-emheight')('Line 1\nLine 2', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 33, height: 24, type: 'png' }

It still seems that using emHeightAscent/Descent is the correct value, but I'm not sure why it returns 15px for a 12px font on Amazon Linux.

robbytx commented 7 years ago

Hmm... well after installing Arial from msttcorefonts and installing Helvetica via fondu Helvetica.dfont using fondu, I got the Linux metrics closer to correct:

$ node
> require('image-size')(require('text2png')('Line 1', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 35, height: 10, type: 'png' }
> require('image-size')(require('text2png')('Line 1', {font: '12px Arial', padding: 0, lineSpacing: 0}))
{ width: 34, height: 8, type: 'png' }
> require('image-size')(require('text2png')('Line 1', {font: '12px Helvetica', padding: 0, lineSpacing: 0}))
{ width: 33, height: 9, type: 'png' }

> require('image-size')(require('./text2png-using-emheight')('Line 1', {font: '12px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 35, height: 15, type: 'png' }
> require('image-size')(require('./text2png-using-emheight')('Line 1', {font: '12px Arial', padding: 0, lineSpacing: 0}))
{ width: 34, height: 14, type: 'png' }
> require('image-size')(require('./text2png-using-emheight')('Line 1', {font: '12px Helvetica', padding: 0, lineSpacing: 0}))
{ width: 33, height: 13, type: 'png' }

I continue to believe that emHeightAscent/Descent is more correct, given the differences when using larger font sizes (31px is a lot closer to 30px than 22px is):

> require('image-size')(require('text2png')('Line 1', {font: '30px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 94, height: 23, type: 'png' }
> require('image-size')(require('text2png')('Line 1', {font: '30px Arial', padding: 0, lineSpacing: 0}))
{ width: 83, height: 22, type: 'png' }
> require('image-size')(require('text2png')('Line 1', {font: '30px Helvetica', padding: 0, lineSpacing: 0}))
{ width: 83, height: 22, type: 'png' }

> require('image-size')(require('./text2png-using-emheight')('Line 1', {font: '30px sans-serif', padding: 0, lineSpacing: 0}))
{ width: 94, height: 36, type: 'png' }
> require('image-size')(require('./text2png-using-emheight')('Line 1', {font: '30px Arial', padding: 0, lineSpacing: 0}))
{ width: 83, height: 35, type: 'png' }
> require('image-size')(require('./text2png-using-emheight')('Line 1', {font: '30px Helvetica', padding: 0, lineSpacing: 0}))
{ width: 83, height: 31, type: 'png' }
tkrkt commented 7 years ago

Thank you for reporting! I have confirmed this font size problem, but I am not sure yet...

The difference of actualBoundingBox and emHeight does not affect font-size but line-spacing. I think it is not related to this problem.

example:

fs.writeFileSync('hoge.png', text2png('Line1\nLine2', {
  font: '24px sans-serif',
  bgColor: 'green',
  textColor: 'white',
  padding: 0,
  lineSpacing: 0
}));

results: actualBoundingBox actualboundingbox emHeight emheight

same font-size (and both look small)

robbytx commented 7 years ago

In my tests, at least for single-line messages, it seemed that the rendered font was shrunk because the canvas was smaller than expected. When the canvas was the proper size, it seemed that the font was rendered at the expected size, and therefore was rendered more clearly (at least on Mac OS X).