Compare commits

...

8 Commits

15 changed files with 1214 additions and 850 deletions

View File

@ -1,33 +1,37 @@
### Changelog ###
#### v1.0.0-alpha6 - 28.12.2017 ####
#### v1.0.0-alpha.7 - 31.12.2017 ####
* Fix form input rendering (#1338)
* Improve word line breaking algorithm
#### v1.0.0-alpha.6 - 28.12.2017 ####
* Fix list-style: none (#1340)
* Extend supported values for pseudo element content
#### v1.0.0-alpha5 - 21.12.2017 ####
#### v1.0.0-alpha.5 - 21.12.2017 ####
* Fix underline positioning
* Fix canvas rendering on Chrome
* Fix overflow: auto
* Added support for rendering list-style
#### v1.0.0-alpha4 - 12.12.2017 ####
#### v1.0.0-alpha.4 - 12.12.2017 ####
* Fix rendering with multiple fonts defined (Fix #796)
* Add support for radial-gradients
* Fix logging option (#1302)
* Add support for rendering webgl canvas content (#646)
* Fix external SVG loading with proxies (#802)
#### v1.0.0-alpha3 - 9.12.2017 ####
#### v1.0.0-alpha.3 - 9.12.2017 ####
* Disable `foreignObjectRendering` by default (#1295)
* Fix background-size when using background-origin and background-size: cover/contain (#1299)
* Added support for background-origin: content-box (#1299)
#### v1.0.0-alpha2 - 7.12.2017 ####
#### v1.0.0-alpha.2 - 7.12.2017 ####
* Fix scroll positions for CanvasRenderer (#1259)
* Fix `data-html2canvas-ignore` attribute (#1253)
* Fix decimal `letter-spacing` values (#1293)
#### v1.0.0-alpha1 - 5.12.2017 ####
#### v1.0.0-alpha.1 - 5.12.2017 ####
* Complete rewrite of library
##### Breaking Changes #####
* Remove deprecated onrendered callback, calling `html2canvas` returns a `Promise<HTMLCanvasElement>`

View File

@ -36,6 +36,7 @@ Below is a list of all the supported CSS properties and values.
- height
- left
- letter-spacing
- line-break
- list-style
- list-style-image
- list-style-position
@ -47,6 +48,7 @@ Below is a list of all the supported CSS properties and values.
- min-width
- opacity
- overflow
- overflow-wrap
- padding
- position
- right
@ -62,7 +64,9 @@ Below is a list of all the supported CSS properties and values.
- visibility
- white-space
- width
- word-break
- word-spacing
- word-wrap
- z-index
## Unsupported CSS properties
@ -76,8 +80,6 @@ These CSS properties are **NOT** currently supported
- [mix-blend-mode](https://github.com/niklasvh/html2canvas/issues/580)
- [object-fit](https://github.com/niklasvh/html2canvas/issues/1064)
- [repeating-linear-gradient()](https://github.com/niklasvh/html2canvas/issues/1162)
- word-break
- [word-wrap](https://github.com/niklasvh/html2canvas/issues/664)
- [writing-mode](https://github.com/niklasvh/html2canvas/issues/1258)
- [zoom](https://github.com/niklasvh/html2canvas/issues/732)

1685
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
"name": "html2canvas",
"description": "Screenshots with JavaScript",
"main": "dist/npm/index.js",
"version": "1.0.0-alpha.6",
"version": "1.0.0-alpha.7",
"author": {
"name": "Niklas von Hertzen",
"email": "niklasvh@gmail.com",
@ -77,6 +77,6 @@
"homepage": "https://html2canvas.hertzen.com",
"license": "MIT",
"dependencies": {
"punycode": "2.1.0"
"css-line-break": "1.0.1"
}
}

View File

@ -133,7 +133,7 @@ const inlineFormElement = (
if (value.length > 0 && body) {
const wrapper = node.ownerDocument.createElement('html2canvaswrapper');
copyCSSStyles(node.ownerDocument.defaultView.getComputedStyle(node, null), wrapper);
wrapper.style.position = 'fixed';
wrapper.style.position = 'absolute';
wrapper.style.left = `${container.bounds.left}px`;
wrapper.style.top = `${container.bounds.top}px`;
if (!allowLinebreak) {

View File

@ -7,9 +7,11 @@ import type {BorderRadius} from './parsing/borderRadius';
import type {DisplayBit} from './parsing/display';
import type {Float} from './parsing/float';
import type {Font} from './parsing/font';
import type {LineBreak} from './parsing/lineBreak';
import type {ListStyle} from './parsing/listStyle';
import type {Margin} from './parsing/margin';
import type {Overflow} from './parsing/overflow';
import type {OverflowWrap} from './parsing/overflowWrap';
import type {Padding} from './parsing/padding';
import type {Position} from './parsing/position';
import type {TextShadow} from './parsing/textShadow';
@ -17,6 +19,7 @@ import type {TextTransform} from './parsing/textTransform';
import type {TextDecoration} from './parsing/textDecoration';
import type {Transform} from './parsing/transform';
import type {Visibility} from './parsing/visibility';
import type {WordBreak} from './parsing/word-break';
import type {zIndex} from './parsing/zIndex';
import type {Bounds, BoundCurves} from './Bounds';
@ -34,9 +37,11 @@ import {parseDisplay, DISPLAY} from './parsing/display';
import {parseCSSFloat, FLOAT} from './parsing/float';
import {parseFont} from './parsing/font';
import {parseLetterSpacing} from './parsing/letterSpacing';
import {parseLineBreak} from './parsing/lineBreak';
import {parseListStyle} from './parsing/listStyle';
import {parseMargin} from './parsing/margin';
import {parseOverflow, OVERFLOW} from './parsing/overflow';
import {parseOverflowWrap} from './parsing/overflowWrap';
import {parsePadding} from './parsing/padding';
import {parsePosition, POSITION} from './parsing/position';
import {parseTextDecoration} from './parsing/textDecoration';
@ -44,6 +49,7 @@ import {parseTextShadow} from './parsing/textShadow';
import {parseTextTransform} from './parsing/textTransform';
import {parseTransform} from './parsing/transform';
import {parseVisibility, VISIBILITY} from './parsing/visibility';
import {parseWordBreak} from './parsing/word-break';
import {parseZIndex} from './parsing/zIndex';
import {parseBounds, parseBoundCurves, calculatePaddingBoxPath} from './Bounds';
@ -65,10 +71,12 @@ type StyleDeclaration = {
float: Float,
font: Font,
letterSpacing: number,
lineBreak: LineBreak,
listStyle: ListStyle | null,
margin: Margin,
opacity: number,
overflow: Overflow,
overflowWrap: OverflowWrap,
padding: Padding,
position: Position,
textDecoration: TextDecoration | null,
@ -76,6 +84,7 @@ type StyleDeclaration = {
textTransform: TextTransform,
transform: Transform,
visibility: Visibility,
wordBreak: WordBreak,
zIndex: zIndex
};
@ -134,12 +143,16 @@ export default class NodeContainer {
font: parseFont(style),
letterSpacing: parseLetterSpacing(style.letterSpacing),
listStyle: display === DISPLAY.LIST_ITEM ? parseListStyle(style) : null,
lineBreak: parseLineBreak(style.lineBreak),
margin: parseMargin(style),
opacity: parseFloat(style.opacity),
overflow:
INPUT_TAGS.indexOf(node.tagName) === -1
? parseOverflow(style.overflow)
: OVERFLOW.HIDDEN,
overflowWrap: parseOverflowWrap(
style.overflowWrap ? style.overflowWrap : style.wordWrap
),
padding: parsePadding(style),
position: position,
textDecoration: parseTextDecoration(style),
@ -147,6 +160,7 @@ export default class NodeContainer {
textTransform: parseTextTransform(style.textTransform),
transform: parseTransform(style),
visibility: parseVisibility(style.visibility),
wordBreak: parseWordBreak(style.wordBreak),
zIndex: parseZIndex(position !== POSITION.STATIC ? style.zIndex : 'auto')
};

View File

@ -1,18 +1,12 @@
/* @flow */
'use strict';
import {ucs2} from 'punycode';
import type NodeContainer from './NodeContainer';
import {Bounds, parseBounds} from './Bounds';
import {TEXT_DECORATION} from './parsing/textDecoration';
import FEATURES from './Feature';
const UNICODE = /[^\u0000-\u00ff]/;
const hasUnicodeCharacters = (text: string): boolean => UNICODE.test(text);
const encodeCodePoint = (codePoint: number): string => ucs2.encode([codePoint]);
import {breakWords, toCodePoints, fromCodePoint} from './Unicode';
export class TextBounds {
text: string;
@ -29,9 +23,10 @@ export const parseTextBounds = (
parent: NodeContainer,
node: Text
): Array<TextBounds> => {
const codePoints = ucs2.decode(value);
const letterRendering = parent.style.letterSpacing !== 0 || hasUnicodeCharacters(value);
const textList = letterRendering ? codePoints.map(encodeCodePoint) : splitWords(codePoints);
const letterRendering = parent.style.letterSpacing !== 0;
const textList = letterRendering
? toCodePoints(value).map(i => fromCodePoint(i))
: breakWords(value, parent);
const length = textList.length;
const defaultView = node.parentNode ? node.parentNode.ownerDocument.defaultView : null;
const scrollX = defaultView ? defaultView.pageXOffset : 0;
@ -88,42 +83,3 @@ const getRangeBounds = (
range.setEnd(node, offset + length);
return Bounds.fromClientRect(range.getBoundingClientRect(), scrollX, scrollY);
};
const splitWords = (codePoints: Array<number>): Array<string> => {
const words = [];
let i = 0;
let onWordBoundary = false;
let word;
while (codePoints.length) {
if (isWordBoundary(codePoints[i]) === onWordBoundary) {
word = codePoints.splice(0, i);
if (word.length) {
words.push(ucs2.encode(word));
}
onWordBoundary = !onWordBoundary;
i = 0;
} else {
i++;
}
if (i >= codePoints.length) {
word = codePoints.splice(0, i);
if (word.length) {
words.push(ucs2.encode(word));
}
}
}
return words;
};
const isWordBoundary = (characterCode: number): boolean => {
return (
[
32, // <space>
13, // \r
10, // \n
9, // \t
45 // -
].indexOf(characterCode) !== -1
);
};

View File

@ -1,32 +1,27 @@
/* @flow */
'use strict';
export const fromCodePoint = (...codePoints: Array<number>): string => {
if (String.fromCodePoint) {
return String.fromCodePoint(...codePoints);
import NodeContainer from './NodeContainer';
import {LineBreaker, fromCodePoint, toCodePoints} from 'css-line-break';
import {OVERFLOW_WRAP} from './parsing/overflowWrap';
export {toCodePoints, fromCodePoint} from 'css-line-break';
export const breakWords = (str: string, parent: NodeContainer): Array<string> => {
const breaker = LineBreaker(str, {
lineBreak: parent.style.lineBreak,
wordBreak:
parent.style.overflowWrap === OVERFLOW_WRAP.BREAK_WORD
? 'break-word'
: parent.style.wordBreak
});
const words = [];
let bk;
while (!(bk = breaker.next()).done) {
words.push(bk.value.slice());
}
const length = codePoints.length;
if (!length) {
return '';
}
const codeUnits = [];
let index = -1;
let result = '';
while (++index < length) {
let codePoint = codePoints[index];
if (codePoint <= 0xffff) {
codeUnits.push(codePoint);
} else {
codePoint -= 0x10000;
codeUnits.push((codePoint >> 10) + 0xd800, codePoint % 0x400 + 0xdc00);
}
if (index + 1 === length || codeUnits.length > 0x4000) {
result += String.fromCharCode(...codeUnits);
codeUnits.length = 0;
}
}
return result;
return words;
};

19
src/parsing/lineBreak.js Normal file
View File

@ -0,0 +1,19 @@
/* @flow */
'use strict';
export const LINE_BREAK = {
NORMAL: 'normal',
STRICT: 'strict'
};
export type LineBreak = $Values<typeof LINE_BREAK>;
export const parseLineBreak = (wordBreak: string): LineBreak => {
switch (wordBreak) {
case 'strict':
return LINE_BREAK.STRICT;
case 'normal':
default:
return LINE_BREAK.NORMAL;
}
};

View File

@ -0,0 +1,19 @@
/* @flow */
'use strict';
export const OVERFLOW_WRAP = {
NORMAL: 0,
BREAK_WORD: 1
};
export type OverflowWrap = $Values<typeof OVERFLOW_WRAP>;
export const parseOverflowWrap = (overflow: string): OverflowWrap => {
switch (overflow) {
case 'break-word':
return OVERFLOW_WRAP.BREAK_WORD;
case 'normal':
default:
return OVERFLOW_WRAP.NORMAL;
}
};

22
src/parsing/word-break.js Normal file
View File

@ -0,0 +1,22 @@
/* @flow */
'use strict';
export const WORD_BREAK = {
NORMAL: 'normal',
BREAK_ALL: 'break-all',
KEEP_ALL: 'keep-all'
};
export type WordBreak = $Values<typeof WORD_BREAK>;
export const parseWordBreak = (wordBreak: string): WordBreak => {
switch (wordBreak) {
case 'break-all':
return WORD_BREAK.BREAK_ALL;
case 'keep-all':
return WORD_BREAK.KEEP_ALL;
case 'normal':
default:
return WORD_BREAK.NORMAL;
}
};

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<title>word-break</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
body {
font-family: Arial;
}
.test span {
line-break: normal;
}
.strict span {
line-break: strict;
}
p.test{
border: 1px solid gray;
color: blue;
width: 6em;
}
</style>
</head>
<body>
<!-- iteration marks -->
<p class="test" lang="ja">
<span>サンプルぁルぁルぁルぁルぁルぁルぁぁぁぁ文ンプル–文々サンプル文</span>
</p>
<p class="test strict" lang="ja">
<span>サンプルぁルぁルぁルぁルぁルぁルぁぁぁぁ文文文文文‐–〜゠サンプル文々サンプル文</span>
</p>
<hr />
</body>
</html>

View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<title>word-break</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
body {
font-family: Arial;
}
.normal {
width: 13em;
background: gold;
overflow-wrap: normal;
}
.break-word {
width: 13em;
background: lime;
overflow-wrap: break-word;
}
.word-normal {
width: 13em;
background: gold;
word-wrap: normal;
}
.word-break-word {
width: 13em;
background: lime;
word-wrap: break-word;
}
</style>
</head>
<body>
<div>
<p>1. <code>overflow-wrap: normal</code></p>
<p class="normal">FStrPrivFinÄndG (Gesetz zur Änderung des
Fernstraßenbauprivatfinanzierungsgesetzes
und straßenverkehrsrechtlicher Vorschriften)</p>
<p>2. <code>overflow-wrap: break-word</code></p>
<p class="break-word">FStrPrivFinÄndG (Gesetz zur Änderung des
Fernstraßenbauprivatfinanzierungsgesetzes
und straßenverkehrsrechtlicher Vorschriften)</p>
<p>3. <code>word-wrap: normal</code></p>
<p class="word-normal">FStrPrivFinÄndG (Gesetz zur Änderung des
Fernstraßenbauprivatfinanzierungsgesetzes
und straßenverkehrsrechtlicher Vorschriften)</p>
<p>4. <code>word-wrap: break-word</code></p>
<p class="word-break-word">FStrPrivFinÄndG (Gesetz zur Änderung des
Fernstraßenbauprivatfinanzierungsgesetzes
und straßenverkehrsrechtlicher Vorschriften)</p>
</div>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<title>Thai text</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
.text-block {
width: 500px;
font-family: serif;
float:left;
text-align:justify;
}
</style>
</head>
<body>
<div id="text" class="text-block">
<p>.....</p>
<p>ทดสอบ แบบกำหนดรูปแบบ ทำ ที นี่ นู่น นั่น นี้ มี หรือ ไม่ </p>
<p>ภาษาไทย เป็นภาษาราชการของประเทศไทย และภาษาแม่ของชาวไทย และชนเชื้อสายอื่นในประเทศไทย ภาษาไทยเป็นภาษาในกลุ่มภาษาไท ซึ่งเป็นกลุ่มย่อยของตระกูลภาษาไท-กะได สันนิษฐานว่า ภาษาในตระกูลนี้มีถิ่นกำเนิดจากทางตอนใต้ของประเทศจีน และนักภาษาศาสตร์บางส่วนเสนอว่า ภาษาไทยน่าจะมีความเชื่อมโยงกับตระกูลภาษาออสโตร-เอเชียติก ตระกูลภาษาออสโตรนีเซียน และตระกูลภาษาจีน-ทิเบต</p>
<p>ภาษาไทยเป็นภาษาที่มีระดับเสียงของคำแน่นอนหรือวรรณยุกต์เช่นเดียวกับภาษาจีน และออกเสียงแยกคำต่อคำ ทำให้เป็นที่ลำบากของชาวต่างชาติเนื่องจากการออกเสียงวรรณยุกต์ที่เป็นเอกลักษณ์ของแต่ละคำ และการสะกดคำที่ซับซ้อน</p>
<p>คำว่า ไทย หมายความว่า อิสรภาพ เสรีภาพ หรืออีกความหมายหนึ่งคือ ใหญ่ ยิ่งใหญ่ เพราะการจะเป็นอิสระได้จะต้องมีกำลังที่มากกว่า แข็งแกร่งกว่า เพื่อป้องกันการรุกรานจากข้าศึก คำนี้เป็นคำไทยแท้ที่เกิดจากการสร้างคำที่เรียก "การลากคำเข้าวัด" ซึ่งเป็นการลากความวิธีหนึ่ง ตามหลักคติชนวิทยา คนไทยเป็นชนชาติที่นับถือกันว่า ภาษาบาลี ซึ่งเป็นภาษาที่บันทึกพระธรรมคำสอนของพระพุทธเจ้าเป็นภาษาอันศักดิ์สิทธิ์และเป็นมงคล เมื่อคนไทยต้องการตั้งชื่อประเทศว่า ไท ซึ่งเป็นคำไทยแท้ จึงเติมตัว ย เข้าไปข้างท้าย เพื่อให้มีลักษณะคล้ายคำในภาษาบาลี - สันสกฤตเพื่อความเป็นมงคลตามความเชื่อของตน ภาษาไทยจึงหมายถึงภาษาของชนชาติไทยผู้เป็นไทนั่นเอง</p>
</div>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<title>word-break</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
body {
font-family: Arial;
}
.narrow {
padding: 5px;
border: 1px solid;
width: 8em;
}
.normal {
word-break: normal;
}
.breakAll {
word-break: break-all;
}
.keep {
word-break: keep-all;
}
</style>
</head>
<body>
<div>
<p>1. <code>word-break: normal</code></p>
<p class="normal narrow">This is a long and
Supercalifragilisticexpialidocious sentence.
次の単語グレートブリテンおよび北アイルランド連合王国で本当に大きな言葉</p>
<p>2. <code>word-break: break-all</code></p>
<p class="breakAll narrow">This is a long and
Supercalifragilisticexpialidocious sentence.
次の単語グレートブリテンおよび北アイルランド連合王国で本当に大きな言葉</p>
<p>3. <code>word-break: keep-all</code></p>
<p class="keep narrow">This is a long and
Supercalifragilisticexpialidocious sentence.
次の単語グレートブリテンおよび北アイルランド連合王国で本当に大きな言葉</p>
</div>
</body>
</html>