Hi.
I am planning to add code that automatically splits long strings into multiple text lines.
Efficient string length calculation is not going to be a problem - will use off-screen DIV to measure length of text, infer chunk lengths and cut appropriately. What is a problem is the obtuseness of DOM manipulation using pure JavaScript (and browser-specific gotchas that come with that). So, naturally will roll with what i know - jQuery, which will make my code jQuery-dependent.
This is fine for us (corporate use, we already have jQuery where that matters), but, understandably could not go well if you want to pull the code into your mainline repo.
Could you mention your suggestions / thoughts on possible addition of line wrapping code to jsPDF?
If you already have some thoughts on that, and would like to have it as part of mainline jsPDF, by all means, chime in so we can try to code in way that helps eventual pull. If you don't really care about auto line wrapping, would be good to know too as it frees our hands.
Cheers.
If you'd like to go ahead with a jQuery implementation - I will assist in getting a succinct non-jQuery dependant version into the mainline branch.
Thanks,
James
The multiline support is coming in two parts:
Part 1 was simple and is done:
https://github.com/willowsystems/jsPDF
The base support for multiline text nodes is in 9ad879092a57a584867f529344f300fa1e740426
Test for that is in 82396214ca9b5177a5b1d26566c82ae3462d3c72
Part 2 is the one that will likely depend on running us within a browser, and on jQuery.
Code is heavily rearranged, but not really changed. Rearranging + minor changes were needed for the other features that will come, like templates... Please, don't take the restyling as an insult.
That's absolutely fine. Looking good! I've also kicked off a project that does a similar thing for DOCX files.. http://github.com/MrRio/DOCX.js
Since ATM jsPDF only uses the standard fonts, would it not make sense to compute the length of a text string directly from the standard metrics?
The patch mentioned here just deals with pre-chopped lines. The line "to-size" chopper code is not published.
My Original direction for the chopper was jQuery because code for measuring text in hidden absolute span is already available. I have that working already (code not published), albeit poorly as the "twin" font in browser has different widths, kerning.
Seeing imperfect results of length approximation, I also decided to add actual widths, kerning + chopper as plugin
My problem with that approach was always size. _All_ of the metrics were coming in at 40+kb. We target this at mobile and things like that matter.
After massaging (throwing away data too close to average size, throwing away tiny kerning values) the metrics, i got it down to 17kb, 4.5kb gzipped. Now that I have the metrics db size dealt with, adding proper chopper.
If anyone needs a quick'n'dirty solution to measuring text server-side (i.e. without a browser engine to perform the layout), the following gist contains the basic Adobe font metrics for the Base 14 fonts:
https://gist.github.com/3152705
It only supports ASCII characters at the moment; I wasn't able to find full font metrics for all Unicode characters.
https://github.com/MrRio/jsPDF/commit/b9f9f86ceea2486bb4a3c32b75e1461dfbed52f5
^ Added font metrics plugin that does not depend on DOM. It adds gliph sizes as well as kerning. See
https://github.com/MrRio/jsPDF/blob/master/jspdf.plugin.standard_fonts_metrics.js
https://github.com/MrRio/jsPDF/blob/master/jspdf.plugin.split_text_to_size.js
(There is a fix coming later today for split_text_to_size. It had "split long word" code unfinished.
Will there be any functions exposed to measure text, or right- and center-align text?
Cheers,
Stu
@stu-smith
RE: "Will there be any functions exposed to measure text"
There are such function exposed. getCharWidthsArray(text, options) and getStringUnitWidth(text, options) - part of jspdf.plugin.split_text_to_size.js.
Both operate on "font unit" sizes, meaning, they all return values as if the font size is 1 point. You would scale it up to actual points width by multiplying by desired font size. Example:
var actualTextWidth = pdf.getStringUnitWidth(text, {fontName:'Times', fontStyle:'Roman'}) * fontSizeInPoints / toYourUnitsScaleRatio
RE: "or right- and center-align text?"
For me this is a problem that rides on top of a bigger problem. The bigger problem is: "how do you communicate FORMATTED text to pdf and just have it render it right?"
By formatted i mean, something like "Markdown" or "HTML", as I, personally, have no desire reinventing "formatted text" spec just for jsPDF. Formatted text has different font styling and padding within a line of text. Just measuring glyphs is not enough. Without first supporting in-line multi-format, I, personally, don't see a point coding in "justify" or other "final touches"
We have a BETA (Functionality, API is unfinished) HTML to PDF converter plugin in WIllow Systems's jsPDF repo - https://github.com/willowsystems/jsPDF/blob/stable/jspdf.plugin.from_html.js
It relies on browser and jQuery for parsing and extraction of formatting from a snippet of HTML. (Yes, Node.js, server-side JavaScript people, I don't care about you :) ) See Examples -> Text Elements -> FromHTML plugin.
We (at Willow Systems) _do_ have a plan to add "justify" and "text-indent" (first line indent) support to it. I did not think about "center" CSS scraping yet. (Well, I did, but it complicates CSS scraping a bit, so I did not think about it further.) If you think you can contribute to the fromHTML plugin some code that does scraping and rendering of centered, right-aligned formatted text, will gladly take it, as our goals are elsewhere right now.
Daniel
Willow Systems
My apologies - I hadn't noticed getStringUnitWidth - that's exactly what I needed.
To put my request in context - I'm writing a diagramming tool of sorts, and it needs PDF output, so jsPDF is perfect as it sits nicely in the node.js backend. I needed the ability to output text aligned to a particular point, so I can now right- and center-align nicely, now that I can calculate the width of the text.
Many thanks for the help - it's all moving along nicely now.
question
What is really "toYourUnitsScaleRatio"?
Dont really understand that one :-)
Multiline Text is not supported with drawText
doc.text(text,left,top,'center') can be used to center text. It can be used with array of lines as well but when it is used with array the center does not work right so I have used it in a loop for every object in the array.
var lMargin=15; //left margin in mm
var rMargin=15; //right margin in mm
var pdfInMM=210; // width of A4 in mm
var pageCenter=pdfInMM/2;
var doc = new jsPDF("p","mm","a4");
var paragraph="Apple's iPhone 7 is officially upon us. After a week of pre-orders, the latest in the iPhone lineup officially launches today.\n\nEager Apple fans will be lining up out the door at Apple and carrier stores around the country to grab up the iPhone 7 and iPhone 7 Plus, while Android owners look on bemusedly.\n\nDuring the Apple Event last week, the tech giant revealed a number of big, positive changes coming to the iPhone 7. It's thinner. The camera is better. And, perhaps best of all, the iPhone 7 is finally water resistant.\n\nStill, while there may be plenty to like about the new iPhone, there's plenty more that's left us disappointed. Enough, at least, to make smartphone shoppers consider waiting until 2017, when Apple is reportedly going to let loose on all cylinders with an all-glass chassis design.";
var lines =doc.splitTextToSize(paragraph, (pdfInMM-lMargin-rMargin));
var dim = doc.getTextDimensions('Text');
var lineHeight = dim.h
for(var i=0;i<lines.length;i++){
lineTop = (lineHeight/2)*i
doc.text(lines[i],pageCenter,20+lineTop,'center'); //see this line
}
doc.save('Generated.pdf');
This is not a multiline solution but a workaround... :(
This is my solution:
this.pageStart = 20;
this.step = 5;
this.line = 20;
var splitTitle = this.doc.splitTextToSize(text, 180);
for (var i = 0; i < splitTitle.length; i++) {
this.doc.text(splitTitle[i], marginX, this.line);
this.addLine(this.step)
}
Where addLine is function for count line and split content by page
addLine(step) {
if (this.line >= 275) {
this.doc.addPage();
this.line = this.pageStart;
}
this.line += step;
}