From 962d0a68d9312d785dfabea5d9e4feb83d699538 Mon Sep 17 00:00:00 2001 From: Stefan Niederhauser Date: Fri, 28 Feb 2020 18:18:33 +0100 Subject: [PATCH] support kerning and hyphens --- README.md | 5 ++- .../canvas/__tests__/textarea-layout.ts | 14 ++++++- src/render/canvas/canvas-renderer.ts | 3 +- src/render/canvas/textarea-layout.ts | 41 +++++++++++++------ tests/reftests/text/textarea.html | 13 ++++++ 5 files changed, 61 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 8b63c73..b74f46c 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/src/render/canvas/__tests__/textarea-layout.ts b/src/render/canvas/__tests__/textarea-layout.ts index 2148ba5..9ca3553 100644 --- a/src/render/canvas/__tests__/textarea-layout.ts +++ b/src/render/canvas/__tests__/textarea-layout.ts @@ -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; diff --git a/src/render/canvas/canvas-renderer.ts b/src/render/canvas/canvas-renderer.ts index e9d9a07..9d976b2 100644 --- a/src/render/canvas/canvas-renderer.ts +++ b/src/render/canvas/canvas-renderer.ts @@ -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++) { diff --git a/src/render/canvas/textarea-layout.ts b/src/render/canvas/textarea-layout.ts index a425400..2942af6 100644 --- a/src/render/canvas/textarea-layout.ts +++ b/src/render/canvas/textarea-layout.ts @@ -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; + } } diff --git a/tests/reftests/text/textarea.html b/tests/reftests/text/textarea.html index 341eaaf..dc06c69 100644 --- a/tests/reftests/text/textarea.html +++ b/tests/reftests/text/textarea.html @@ -44,6 +44,19 @@

7. long word

+ +

8. hyphen

+ + +

9. kerning

+ + +

10. kerning with letter spacing

+ + +

11. kerning with letter spacing

+ +