foliojs / pdfkit

A JavaScript PDF generation library for Node and the browser
http://pdfkit.org/
MIT License
9.95k stars 1.16k forks source link

Text not positioned like PDF... nor like SVG... #351

Open v-python opened 9 years ago

v-python commented 9 years ago

While PDF coordinates have the origin at the bottom left corner of the page, PDFkit seems to try to be "like SVG" in putting the origin at the top left. This is convenient for doing the same thing to SVG and PDF.

Except, SVG (and PDF) places text by using the coordinates given (or implied) as the baseline of the text, but PDFkit uses the coordinates for the top (of bounding box or something).

I was trying to convert some SVG to PDFkit that has text overlaid on graphics, and the text was "a line below" where it should have been. Not sure if that means cap height, line height, or exactly what, though.

Is there a way to obtain the distance between whatever PDFkit uses as the text placement point and the baseline for the font, so that adjustments could be made?

It would actually be nice if there were a "mode" or "setting" that would alter the way PDFkit places text so that when the mode is turned on, text would be placed with the baseline at the coordinates given. It is only "backward compatibility for other PDFkit users" that makes me not suggest that PDFkit should simply be changed to use the baseline. But using the baseline is what PDF and SVG do... whatever PDFkit is doing is surprising to users that understand either of the others.

This is related to issue #334

v-python commented 9 years ago

Hmm. Poking around a bit, and not knowing what the units are, I see that an adjustment of

( doc._font.ascender + doc._font.capHeight ) / 100

seems to put the characters' baselines on the coordinates, instead of their tops. The random 100 makes me nervous, and maybe that can be found somewhere rather than being hard coded? Better would be a method to return the baseline, or maybe a variety of baselines (HTML has various options for character alignment, for example).

This was for a 20 point font... so I'm not sure if I should be multiplying by font size and dividing by 2000, or just what. 2000 seems like a magic number too!

Well, this worked for one font, but not another. So probably the numbers are in there, but I don't know enough about what they are called or how to combine them.

v-python commented 9 years ago

So this formula seems to work for calculating baselines for several different fonts.

baseT = this.baseY - ( this.doc._font.ascender ) / 1000 * ptSize;

devongovett commented 9 years ago

Yes. I made a mistake when designing the API to have text positions based on the top of the textbox rather than the baseline. It was originally to allow text to be positioned easily inside a box without needing to calculate the line height. I'd like to fix this problem, but I'm not sure how without breaking backwards compatibility for programs that depend on this now. I'll probably add a baseline option to the text method. Eventually it should be the default though...

sprice237 commented 9 years ago

Any idea when the baseline option would make it in?

christiangenco commented 9 years ago

:+1:

jholmes033169 commented 9 years ago

Can anyone give me a hit on what the actual units are? Pixels?

v-python commented 9 years ago

Depends. PDF units are points: 72 points = 1 inch. Font units are "design units", generally 1000 per em square, unless it is something else. And so in a 10 point font, you em square would be 100 design units = 1 point. But in a 20 point font, you'd have only 50 design units = 1 point. SVG gives much user control over what its units are. Sadly there is at least one combination of nested SVG scaling that acts different in different browsers.

acthp commented 8 years ago

What is this?

baseT = this.baseY - ( this.doc._font.ascender ) / 1000 * ptSize;
v-python commented 8 years ago

@acthp This is Javascript source code. Not sure exactly what you are asking. The comment from which you quoted it describes its purpose.

acthp commented 8 years ago

@v-python this, not this. ;) Yes, it's javascript source, referring to an object (this pointer) that is not described.

I believe now it's not relevant, there is no this.baseY. Rather, doc is the pdfkit document object, so this is more like

pdfkitYCoord = svgYCoord - doc._font.ascender / 1000 * ptSize
v-python commented 8 years ago

Been a while since I wrote that code. And indeed, you are correct regarding "this" being a pointer to an object not described. Likely it was an object that was generating a pdf file containing formatted text, having a pdfkit document object as a member. You've correctly described the gist of the code.