support kerning and hyphens

This commit is contained in:
Stefan Niederhauser 2020-02-28 18:18:33 +01:00
parent 95ca5a627a
commit 962d0a68d9
5 changed files with 61 additions and 15 deletions

View File

@ -3,11 +3,14 @@ html2canvas
[Homepage](https://html2canvas.hertzen.com) | [Downloads](https://github.com/niklasvh/html2canvas/releases) | [Questions](http://stackoverflow.com/questions/tagged/html2canvas?sort=newest)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/niklasvh/html2canvas?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/niklasvh/html2canvas?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://dev.azure.com/niklasvh/html2canvas/_apis/build/status/niklasvh.html2canvas?branchName=master)](https://dev.azure.com/niklasvh/html2canvas/_build/latest?definitionId=1&branchName=master)
[![NPM Downloads](https://img.shields.io/npm/dm/html2canvas.svg)](https://www.npmjs.org/package/html2canvas)
[![NPM Version](https://img.shields.io/npm/v/html2canvas.svg)](https://www.npmjs.org/package/html2canvas)
This is a branch of the original and fixes:
- Rendering of textarea (see [#2008](https://github.com/niklasvh/html2canvas/issues/2008))
#### JavaScript HTML renderer ####
The script allows you to take "screenshots" of webpages or parts of it, directly on the users browser. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.

View File

@ -35,10 +35,22 @@ describe('textarea-layout', () => {
layoutEqual('Donaudampfsc x2',
['Donaudampfsc', 'x2']);
});
it('should wrap lines at -', () => {
layoutEqual(' Long text- message with sev lines.',
[' Long text-', 'message with','sev lines.']);
});
it('should wrap lines at - 2', () => {
layoutEqual('Long text- message with sev lines.',
['Long text- ', 'message with','sev lines.']);
});
it('should wrap lines at - 3', () => {
layoutEqual('Long text-message with sev lines.',
['Long text-', 'message with','sev lines.']);
});
});
function layoutEqual(s: string, lines: string[]) {
const pos = layout(toCodePoints(s).map(i => fromCodePoint(i)), 120, _ => 10);
const pos = layout(toCodePoints(s).map(i => fromCodePoint(i)), 120, (_, len) => 10 * len);
let line = '';
let y = 0;
let j = 0;

View File

@ -146,7 +146,8 @@ export class CanvasRenderer {
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
} else {
const chars = toCodePoints(text.text).map(i => fromCodePoint(i));
const pos = layout(chars, text.bounds.width, s => this.ctx.measureText(s).width + letterSpacing);
const pos = layout(chars, text.bounds.width, (s, len) =>
this.ctx.measureText(s).width + letterSpacing * (len - 1));
const dx = text.bounds.left;
const dy = text.bounds.top + lineHeight / 2;
for (let i = 0; i < pos.length; i++) {

View File

@ -1,40 +1,44 @@
export function layout(chars: string[], width: number, measure: (s: string) => number): number[][] {
export function layout(chars: string[], width: number, measure: (s: string, len: number) => number): number[][] {
const pos: number[][] = [];
let line = 0;
let lineWidth = 0;
let lineString = '';
let lineLen = 0;
for (let i = 0; i < chars.length; i++) {
if (chars[i] === '\n') {
pos.push([-1, line]);
line++;
lineWidth = 0;
setLine(0, 0);
} else {
pos.push([lineWidth, line]);
lineWidth += measure(chars[i]);
addChar(i);
const lineWidth = measure(lineString, lineLen);
pos.push([lineWidth - measure(chars[i], 1), line]);
if (lineWidth > width) {
if (chars[i] === ' ') {
if (chars[i] === ' ' || chars[i] === '-') {
let p = i;
while (p > 0 && pos[p][1] === line && chars[p] === ' ') p--;
for (let j = i; j > p; j--) {
pos[j][0] = -1;
}
line++;
lineWidth = 0;
setLine(0, 0);
while (i < chars.length + 1 && chars[i + 1] === ' ') {
pos.push([-1, line]);
i++;
}
} else {
let p = i;
while (p > 0 && pos[p][1] === line && chars[p] !== ' ') p--;
while (p > 0 && pos[p][1] === line && chars[p] !== ' ' && chars[p] !== '-') p--;
line++;
if (chars[p] === ' ') {
lineWidth -= pos[p + 1][0];
if (chars[p] === ' ' || chars[p] === '-') {
setLine(p + 1, i + 1);
for (let j = i; j > p; j--) {
pos[j] = [pos[j][0] - pos[p + 1][0], line];
}
pos[p][0] = -1;
if (chars[p] === ' ') {
pos[p][0] = -1;
}
} else {
lineWidth -= pos[i][0];
setLine(i, i + 1);
pos[i] = [0, line];
}
}
@ -42,4 +46,17 @@ export function layout(chars: string[], width: number, measure: (s: string) => n
}
}
return pos;
function addChar(pos: number) {
lineString += chars[pos];
lineLen++;
}
function setLine(from: number, to: number) {
lineString = '';
for (let i = from; i < to; i++) {
lineString += chars[i];
}
lineLen = to - from;
}
}

View File

@ -44,6 +44,19 @@
<p>7. long word</p>
<textarea>Donaudampfschifffahrtsgesellschaftskapitänskochmütze</textarea>
<p>8. hyphen</p>
<textarea>A text with many-manymany manys.</textarea>
<p>9. kerning</p>
<textarea>AVAVAVAVAVAVAVAV TeTeTeTeTeTeTeTeTe YaYaYaYaYaYaYaYaY</textarea>
<p>10. kerning with letter spacing</p>
<textarea style="letter-spacing: 1px">AVAVAVAVAVAVAV TeTeTeTeTeTeTeTe YaYaYaYaYaYaYaY</textarea>
<p>11. kerning with letter spacing</p>
<textarea style="letter-spacing: 5px">AVAVAVAVAV TeTeTeTeTeT YaYaYaYaYa</textarea>
</div>
</body>