I'm trying to apply some changes on jsPDF library to be able to justify text.
I'm having trouble to find the right value for Tw (word spacing).
In jspdf.js (L:1413) I have added this code:
if (align) {
...
else if (align === 'justify') {
left = x;
}
else {
throw new Error('Unrecognized alignment option, use "center" or "right".');
}
prevX = x;
text = '(' + da[0];
let pdfPageWidth = this.internal.pageSize.width;
let wordSpacing;
if( align === 'justify' ) {
let fontSize = this.internal.getFontSize();
let nWords = da[0].trim().split(/\s+/).length;
let textWidth = this.getStringUnitWidth(da[0].replace(/\s+/g, '')) / this.internal.scaleFactor;
wordSpacing = (Math.max(0, (pdfPageWidth - textWidth) / Math.max(1, nWords - 1));
wordSpacing += ' Tw\n';
text = wordSpacing + text;
}
...
}
The idea was to extract the space width by doing (pageWidth - textWidth) / numberOfWords -1. I can't get the correct word space.
Output example
BT
/F1 16 Tf
18.4 TL
0 g
28.35 756.85 Td
19.00357142857142 Tw
(And a little bit it adélkfjalké.) Tj
ET
Is there an encoding problem?
Thanks for your help.
I'm trying to apply some changes on jsPDF library to be able to justify text.
I'm having trouble to find the right value for Tw (word spacing).
In jspdf.js (L:1413) I have added this code:
if (align) {
...
else if (align === 'justify') {
left = x;
}
else {
throw new Error('Unrecognized alignment option, use "center" or "right".');
}
prevX = x;
text = '(' + da[0];
let pdfPageWidth = this.internal.pageSize.width;
let wordSpacing;
if( align === 'justify' ) {
let fontSize = this.internal.getFontSize();
let nWords = da[0].trim().split(/\s+/).length;
let textWidth = this.getStringUnitWidth(da[0].replace(/\s+/g, '')) / this.internal.scaleFactor;
wordSpacing = (Math.max(0, (pdfPageWidth - textWidth) / Math.max(1, nWords - 1));
wordSpacing += ' Tw\n';
text = wordSpacing + text;
}
...
}
The idea was to extract the space width by doing (pageWidth - textWidth) / numberOfWords -1. I can't get the correct word space.
Output example
BT
/F1 16 Tf
18.4 TL
0 g
28.35 756.85 Td
19.00357142857142 Tw
(And a little bit it adélkfjalké.) Tj
ET
Is there an encoding problem?
Thanks for your help.
Share Improve this question edited Sep 19, 2017 at 12:39 Pietro asked Sep 12, 2017 at 16:07 PietroPietro 1,8352 gold badges29 silver badges64 bronze badges 5- 1 Which font encoding is used? According to the specification "Word spacing shall be applied to every occurrence of the single-byte character code 32 in a string when using a simple font or a composite font that defines code 32 as a single-byte code. It shall not apply to occurrences of the byte value 32 in multiple-byte codes." If you have an encoding which does not encode the space glyph as a single byte 0x20, word spacing won't be applied as desired. – mkl Commented Sep 12, 2017 at 17:05
- I have found this comment also on the pdfkit library. I'm using utf-8 but I have no clue how to make it code 32. – Pietro Commented Sep 12, 2017 at 17:06
- 2 UTF-8 is not a proper PDF font encoding. You should check the encoding of the current PDF font. If it is e.g. a pure two-byte encoding (e.g. Identity-H), you cannot use word spacing. – mkl Commented Sep 12, 2017 at 17:12
- Sorry, I don't know much about PDF, this is what is specified in jsPDF: "Because of Adobe's focus on "you subset your fonts!" you are not supposed to have a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode code page. There, however, all characters in the stream are treated as GIDs, including BOM, which is the reason we need to skip BOM in content text (i.e. that that is tied to a font)." – Pietro Commented Sep 12, 2017 at 17:21
- Identity-H is a pure 16-bit encoding. Thus, word spacing is not applied. – mkl Commented Sep 12, 2017 at 19:06
3 Answers
Reset to default 9 +25I answered you on github I wrote my own justification code into my refactored jsPDF according to your suggestion.
Check it out...
https://github.com/MrRio/jsPDF/issues/1016#issuecomment-329957940
Edited:
Oh wait, I can probably help with a direct answer. I think you just have to change it like this:
var wordSpacing = (Math.max(0, (pdfPageWidth - textWidth) / Math.max(1, nWords - 1)) * this.internal.scaleFactor);
if (align) {
...
else if (align === 'justify') {
var wordSpacingPerLine = [];
for (var i = 0, len = da.length; i < len; i++) {
wordSpacingPerLine.push(((this.internal.pageSize.width - lineWidths[i]) / (da[i].split(" ").length - 1) * k).toFixed(2));
}
}
else {
throw new Error('Unrecognized alignment option, use "center" or "right".');
}
prevX = x;
text = '(' + da[0];
let pdfPageWidth = this.internal.pageSize.width;
let wordSpacing;
if( align === 'justify' ) {
text = wordSpacingPerLine[0] + ' Tw\n' + text;
}
...
}
You have to do add the specific WordSpacing for every line except the last line. Last line is then left aligned.
Here an example:
BT
/F1 16 Tf
18.4 TL
0 g
4.869500000212597 Tw
28.35 813.54 Td
(Lorem ipsum dolor sit amet, consetetur abore et dolore) Tj
3.5284444446334158 Tw
0.00 -18.40 Td
(magna aliquyam erat, sed diam voluptua. At vero eos et) Tj
2.0876000001700574 Tw
0.00 -18.40 Td
(accusam et justo duo dolores et ea rebum. Stet clita kasd) Tj
1.329500000212585 Tw
0.00 -18.40 Td
(gubergren, no sea takimata sanctus est Lorem ipsum dolor) Tj
1.329500000212585 Tw
0.00 -18.40 Td
(sit amet.) Tj
ET
After many tries I have found the working code solution :D. It works also with array of text as param.
} else if (align === 'justify') {
left = x;
}
else {
throw new Error(
'Unrecognized alignment option, use "center" or "right".'
);
}
prevX = x;
text = '(' + da[0];
var pdfPageWidth = this.internal.pageSize.width;
var wordSpacing;
var fontSize = this.internal.getFontSize();
if( align === 'justify' ) {
var nWords = da[0].trim().split(/\s+/).length;
var textWidth = this.getStringUnitWidth(da[0]) * fontSize / k;
wordSpacing = (Math.max(0, ((pdfPageWidth - x - marginRight) - textWidth) / Math.max(1, nWords - 1))) * k;
// Do not justify if wordSpacing is too high
wordSpacing = ( wordSpacing > 50 ? 0 : wordSpacing ) + ' Tw\n';
text = wordSpacing + text;
}
for (var i = 1, len = da.length; i < len; i++) {
var delta = maxLineLength - lineWidths[i];
if (align === "center") delta /= 2;
if (align === "justify") { // TODO: improve code duplication
delta = 0;
var nWords = da[i].trim().split(/\s+/).length;
var textWidth = this.getStringUnitWidth(da[i]) * fontSize / k;
wordSpacing = (Math.max(0, ((pdfPageWidth - x - marginRight) - textWidth) / Math.max(1, nWords - 1))) * k;
// Do not justify if wordSpacing is too high
wordSpacing = ( wordSpacing > 50 ? 0 : wordSpacing ) + ' Tw\n';
text += ") Tj\n" + ((left - prevX) + delta) + " -" + leading + " Td\n" + wordSpacing + "(" + da[i];
} else {
// T* = x-offset leading Td ( text )
text += ") Tj\n" + ((left - prevX) + delta) + " -" + leading + " Td (" + da[i];
}
prevX = left + delta;
}
} else {
text = ' 0 Tw\n (' + da.join(") Tj\nT* (");
}
Here is my PR
When using a custom font (ttf etc) Justify text can be done with a javascript to place all words on the correct position. See stackoverflow justify text with custom font