mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Compare commits
12 Commits
v1.0.0-rc.
...
v1.0.0-rc.
Author | SHA1 | Date | |
---|---|---|---|
99f105cb66 | |||
7d3456b78c | |||
00555cf1ef | |||
eedb81ef9e | |||
d4b58960dc | |||
ee3ca35636 | |||
9a63797aa7 | |||
81dcf7b6be | |||
61f4819e02 | |||
86a650bfd5 | |||
cbaecdca28 | |||
cae44a6f0a |
30
CHANGELOG.md
30
CHANGELOG.md
@ -2,6 +2,36 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
# [1.0.0-rc.4](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.3...v1.0.0-rc.4) (2019-09-22)
|
||||||
|
|
||||||
|
|
||||||
|
### docs
|
||||||
|
|
||||||
|
* fix typo (#1864) ([9a63797aa7fb81454008745d2a1c069ca24339a4](https://github.com/niklasvh/html2canvas/commit/9a63797aa7fb81454008745d2a1c069ca24339a4)), closes [#1864](https://github.com/niklasvh/html2canvas/issues/1864)
|
||||||
|
|
||||||
|
### feat
|
||||||
|
|
||||||
|
* ignore unsupported image functions (#1873) ([61f4819e02102b112513d57b16ec7d37e989af20](https://github.com/niklasvh/html2canvas/commit/61f4819e02102b112513d57b16ec7d37e989af20)), closes [#1873](https://github.com/niklasvh/html2canvas/issues/1873)
|
||||||
|
|
||||||
|
### fix
|
||||||
|
|
||||||
|
* correctly render partial borders (fix #1920) (#2010) ([eedb81ef9e114366a7e286e975659360cf9d0983](https://github.com/niklasvh/html2canvas/commit/eedb81ef9e114366a7e286e975659360cf9d0983)), closes [#1920](https://github.com/niklasvh/html2canvas/issues/1920) [#2010](https://github.com/niklasvh/html2canvas/issues/2010)
|
||||||
|
* nested z-index ordering (#2011) ([00555cf1efddfed5877811d8a03a326f9943ab06](https://github.com/niklasvh/html2canvas/commit/00555cf1efddfed5877811d8a03a326f9943ab06)), closes [#2011](https://github.com/niklasvh/html2canvas/issues/2011) [#1978](https://github.com/niklasvh/html2canvas/issues/1978)
|
||||||
|
* null backgroundColor option as transparent (#2012) ([7d3456b78c37e7333db087601805b32ec7ca0253](https://github.com/niklasvh/html2canvas/commit/7d3456b78c37e7333db087601805b32ec7ca0253)), closes [#2012](https://github.com/niklasvh/html2canvas/issues/2012)
|
||||||
|
* zero size iframe rendering (#1863) ([81dcf7b6be66920260a60908aa4b86e7530f6e17](https://github.com/niklasvh/html2canvas/commit/81dcf7b6be66920260a60908aa4b86e7530f6e17)), closes [#1863](https://github.com/niklasvh/html2canvas/issues/1863)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [1.0.0-rc.3](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.2...v1.0.0-rc.3) (2019-05-30)
|
||||||
|
|
||||||
|
|
||||||
|
### fix
|
||||||
|
|
||||||
|
* stack exceeding for css tokenizer (#1862) ([cbaecdca28cfaf9bd854e1b0c005cc8058208b36](https://github.com/niklasvh/html2canvas/commit/cbaecdca28cfaf9bd854e1b0c005cc8058208b36)), closes [#1862](https://github.com/niklasvh/html2canvas/issues/1862)
|
||||||
|
* typescript options type definition (#1861) ([cae44a6f0a6649bd8a7c4250a20792bb5c2e5b42](https://github.com/niklasvh/html2canvas/commit/cae44a6f0a6649bd8a7c4250a20792bb5c2e5b42)), closes [#1861](https://github.com/niklasvh/html2canvas/issues/1861)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-rc.2](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.1...v1.0.0-rc.2) (2019-05-29)
|
# [1.0.0-rc.2](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.1...v1.0.0-rc.2) (2019-05-29)
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ support [older browsers](http://caniuse.com/#search=promise) that do not nativel
|
|||||||
To render an `element` with html2canvas, simply call:
|
To render an `element` with html2canvas, simply call:
|
||||||
` html2canvas(element[, options]);`
|
` html2canvas(element[, options]);`
|
||||||
|
|
||||||
The function returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) containing the `<canvas>` element. Simply add a promise fullfillment handler to the promise using `then`:
|
The function returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) containing the `<canvas>` element. Simply add a promise fulfillment handler to the promise using `then`:
|
||||||
|
|
||||||
html2canvas(document.body).then(function(canvas) {
|
html2canvas(document.body).then(function(canvas) {
|
||||||
document.body.appendChild(canvas);
|
document.body.appendChild(canvas);
|
||||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "html2canvas",
|
"name": "html2canvas",
|
||||||
"version": "1.0.0-rc.2",
|
"version": "1.0.0-rc.4",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"module": "dist/html2canvas.esm.js",
|
"module": "dist/html2canvas.esm.js",
|
||||||
"typings": "dist/types/index.d.ts",
|
"typings": "dist/types/index.d.ts",
|
||||||
"browser": "dist/html2canvas.js",
|
"browser": "dist/html2canvas.js",
|
||||||
"version": "1.0.0-rc.2",
|
"version": "1.0.0-rc.4",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Niklas von Hertzen",
|
"name": "Niklas von Hertzen",
|
||||||
"email": "niklasvh@gmail.com",
|
"email": "niklasvh@gmail.com",
|
||||||
|
@ -61,7 +61,7 @@ export class CacheStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ResourceOptions {
|
export interface ResourceOptions {
|
||||||
imageTimeout: number;
|
imageTimeout: number;
|
||||||
useCORS: boolean;
|
useCORS: boolean;
|
||||||
allowTaint: boolean;
|
allowTaint: boolean;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {TokenType} from '../syntax/tokenizer';
|
import {TokenType} from '../syntax/tokenizer';
|
||||||
import {ICSSImage, image} from '../types/image';
|
import {ICSSImage, image, isSupportedImage} from '../types/image';
|
||||||
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
|
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
|
||||||
import {CSSValue, nonFunctionArgSeperator} from '../syntax/parser';
|
import {CSSValue, nonFunctionArgSeparator} from '../syntax/parser';
|
||||||
|
|
||||||
export const backgroundImage: IPropertyListDescriptor<ICSSImage[]> = {
|
export const backgroundImage: IPropertyListDescriptor<ICSSImage[]> = {
|
||||||
name: 'background-image',
|
name: 'background-image',
|
||||||
@ -19,6 +19,6 @@ export const backgroundImage: IPropertyListDescriptor<ICSSImage[]> = {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokens.filter(nonFunctionArgSeperator).map(image.parse);
|
return tokens.filter(value => nonFunctionArgSeparator(value) && isSupportedImage(value)).map(image.parse);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -65,6 +65,7 @@ const parseDisplayValue = (display: string): Display => {
|
|||||||
case '-webkit-flex':
|
case '-webkit-flex':
|
||||||
return DISPLAY.FLEX;
|
return DISPLAY.FLEX;
|
||||||
case 'grid':
|
case 'grid':
|
||||||
|
case '-ms-grid':
|
||||||
return DISPLAY.GRID;
|
return DISPLAY.GRID;
|
||||||
case 'ruby':
|
case 'ruby':
|
||||||
return DISPLAY.RUBY;
|
return DISPLAY.RUBY;
|
||||||
|
@ -149,7 +149,7 @@ export const isIdentWithValue = (token: CSSValue, value: string): boolean =>
|
|||||||
isIdentToken(token) && token.value === value;
|
isIdentToken(token) && token.value === value;
|
||||||
|
|
||||||
export const nonWhiteSpace = (token: CSSValue) => token.type !== TokenType.WHITESPACE_TOKEN;
|
export const nonWhiteSpace = (token: CSSValue) => token.type !== TokenType.WHITESPACE_TOKEN;
|
||||||
export const nonFunctionArgSeperator = (token: CSSValue) =>
|
export const nonFunctionArgSeparator = (token: CSSValue) =>
|
||||||
token.type !== TokenType.WHITESPACE_TOKEN && token.type !== TokenType.COMMA_TOKEN;
|
token.type !== TokenType.WHITESPACE_TOKEN && token.type !== TokenType.COMMA_TOKEN;
|
||||||
|
|
||||||
export const parseFunctionArgs = (tokens: CSSValue[]): CSSValue[][] => {
|
export const parseFunctionArgs = (tokens: CSSValue[]): CSSValue[][] => {
|
||||||
|
@ -316,7 +316,7 @@ export class Tokenizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
write(chunk: string) {
|
write(chunk: string) {
|
||||||
this._value.push(...toCodePoints(chunk));
|
this._value = this._value.concat(toCodePoints(chunk));
|
||||||
}
|
}
|
||||||
|
|
||||||
read(): CSSToken[] {
|
read(): CSSToken[] {
|
||||||
@ -370,7 +370,6 @@ export class Tokenizer {
|
|||||||
return this.consumeNumericToken();
|
return this.consumeNumericToken();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
break;
|
|
||||||
case COMMA:
|
case COMMA:
|
||||||
return COMMA_TOKEN;
|
return COMMA_TOKEN;
|
||||||
case HYPHEN_MINUS:
|
case HYPHEN_MINUS:
|
||||||
@ -472,7 +471,6 @@ export class Tokenizer {
|
|||||||
}
|
}
|
||||||
this.reconsumeCodePoint(codePoint);
|
this.reconsumeCodePoint(codePoint);
|
||||||
return this.consumeIdentLikeToken();
|
return this.consumeIdentLikeToken();
|
||||||
break;
|
|
||||||
case VERTICAL_LINE:
|
case VERTICAL_LINE:
|
||||||
if (this.peekCodePoint(0) === EQUALS_SIGN) {
|
if (this.peekCodePoint(0) === EQUALS_SIGN) {
|
||||||
this.consumeCodePoint();
|
this.consumeCodePoint();
|
||||||
@ -655,32 +653,51 @@ export class Tokenizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private consumeStringSlice(count: number): string {
|
||||||
|
const SLICE_STACK_SIZE = 60000;
|
||||||
|
let value = '';
|
||||||
|
while (count > 0) {
|
||||||
|
const amount = Math.min(SLICE_STACK_SIZE, count);
|
||||||
|
value += fromCodePoint(...this._value.splice(0, amount));
|
||||||
|
count -= amount;
|
||||||
|
}
|
||||||
|
this._value.shift();
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private consumeStringToken(endingCodePoint: number): StringValueToken | Token {
|
private consumeStringToken(endingCodePoint: number): StringValueToken | Token {
|
||||||
let value = '';
|
let value = '';
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const codePoint = this.consumeCodePoint();
|
const codePoint = this._value[i];
|
||||||
if (codePoint === EOF || codePoint === endingCodePoint) {
|
if (codePoint === EOF || codePoint === undefined || codePoint === endingCodePoint) {
|
||||||
|
value += this.consumeStringSlice(i);
|
||||||
return {type: TokenType.STRING_TOKEN, value};
|
return {type: TokenType.STRING_TOKEN, value};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codePoint === LINE_FEED) {
|
if (codePoint === LINE_FEED) {
|
||||||
this.reconsumeCodePoint(codePoint);
|
this._value.splice(0, i);
|
||||||
return BAD_STRING_TOKEN;
|
return BAD_STRING_TOKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codePoint === REVERSE_SOLIDUS) {
|
if (codePoint === REVERSE_SOLIDUS) {
|
||||||
const next = this.peekCodePoint(0);
|
const next = this._value[i + 1];
|
||||||
if (next !== EOF) {
|
if (next !== EOF && next !== undefined) {
|
||||||
if (next === LINE_FEED) {
|
if (next === LINE_FEED) {
|
||||||
this.consumeCodePoint();
|
value += this.consumeStringSlice(i);
|
||||||
|
i = -1;
|
||||||
|
this._value.shift();
|
||||||
} else if (isValidEscape(codePoint, next)) {
|
} else if (isValidEscape(codePoint, next)) {
|
||||||
|
value += this.consumeStringSlice(i);
|
||||||
value += fromCodePoint(this.consumeEscapedCodePoint());
|
value += fromCodePoint(this.consumeEscapedCodePoint());
|
||||||
|
i = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
value += fromCodePoint(codePoint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
} while (true);
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {CSSValue, nonFunctionArgSeperator} from '../syntax/parser';
|
import {CSSValue, nonFunctionArgSeparator} from '../syntax/parser';
|
||||||
import {TokenType} from '../syntax/tokenizer';
|
import {TokenType} from '../syntax/tokenizer';
|
||||||
import {ITypeDescriptor} from '../ITypeDescriptor';
|
import {ITypeDescriptor} from '../ITypeDescriptor';
|
||||||
import {angle, deg} from './angle';
|
import {angle, deg} from './angle';
|
||||||
@ -86,7 +86,7 @@ const getTokenColorValue = (token: CSSValue, i: number): number => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const rgb = (args: CSSValue[]): number => {
|
const rgb = (args: CSSValue[]): number => {
|
||||||
const tokens = args.filter(nonFunctionArgSeperator);
|
const tokens = args.filter(nonFunctionArgSeparator);
|
||||||
|
|
||||||
if (tokens.length === 3) {
|
if (tokens.length === 3) {
|
||||||
const [r, g, b] = tokens.map(getTokenColorValue);
|
const [r, g, b] = tokens.map(getTokenColorValue);
|
||||||
@ -121,7 +121,7 @@ function hue2rgb(t1: number, t2: number, hue: number): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hsl = (args: CSSValue[]): number => {
|
const hsl = (args: CSSValue[]): number => {
|
||||||
const tokens = args.filter(nonFunctionArgSeperator);
|
const tokens = args.filter(nonFunctionArgSeparator);
|
||||||
const [hue, saturation, lightness, alpha] = tokens;
|
const [hue, saturation, lightness, alpha] = tokens;
|
||||||
|
|
||||||
const h = (hue.type === TokenType.NUMBER_TOKEN ? deg(hue.number) : angle.parse(hue)) / (Math.PI * 2);
|
const h = (hue.type === TokenType.NUMBER_TOKEN ? deg(hue.number) : angle.parse(hue)) / (Math.PI * 2);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {CSSValue, isIdentToken, isNumberToken, nonFunctionArgSeperator, parseFunctionArgs} from '../../syntax/parser';
|
import {CSSValue, isIdentToken, isNumberToken, nonFunctionArgSeparator, parseFunctionArgs} from '../../syntax/parser';
|
||||||
import {
|
import {
|
||||||
CSSImageType,
|
CSSImageType,
|
||||||
CSSLinearGradientImage,
|
CSSLinearGradientImage,
|
||||||
@ -40,7 +40,7 @@ export const webkitGradient = (tokens: CSSValue[]): CSSLinearGradientImage | CSS
|
|||||||
const color = colorType.parse(firstToken.values[0]);
|
const color = colorType.parse(firstToken.values[0]);
|
||||||
stops.push({stop: HUNDRED_PERCENT, color});
|
stops.push({stop: HUNDRED_PERCENT, color});
|
||||||
} else if (firstToken.name === 'color-stop') {
|
} else if (firstToken.name === 'color-stop') {
|
||||||
const values = firstToken.values.filter(nonFunctionArgSeperator);
|
const values = firstToken.values.filter(nonFunctionArgSeparator);
|
||||||
if (values.length === 2) {
|
if (values.length === 2) {
|
||||||
const color = colorType.parse(values[1]);
|
const color = colorType.parse(values[1]);
|
||||||
const stop = values[0];
|
const stop = values[0];
|
||||||
|
@ -98,9 +98,11 @@ export const image: ITypeDescriptor<ICSSImage> = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const SUPPORTED_IMAGE_FUNCTIONS: {
|
export function isSupportedImage(value: CSSValue) {
|
||||||
[key: string]: (args: CSSValue[]) => ICSSImage;
|
return value.type !== TokenType.FUNCTION || SUPPORTED_IMAGE_FUNCTIONS[value.name];
|
||||||
} = {
|
}
|
||||||
|
|
||||||
|
const SUPPORTED_IMAGE_FUNCTIONS: Record<string, (args: CSSValue[]) => ICSSImage> = {
|
||||||
'linear-gradient': linearGradient,
|
'linear-gradient': linearGradient,
|
||||||
'-moz-linear-gradient': prefixLinearGradient,
|
'-moz-linear-gradient': prefixLinearGradient,
|
||||||
'-ms-linear-gradient': prefixLinearGradient,
|
'-ms-linear-gradient': prefixLinearGradient,
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
isTextNode
|
isTextNode
|
||||||
} from './node-parser';
|
} from './node-parser';
|
||||||
import {Logger} from '../core/logger';
|
import {Logger} from '../core/logger';
|
||||||
import {isIdentToken, nonFunctionArgSeperator} from '../css/syntax/parser';
|
import {isIdentToken, nonFunctionArgSeparator} from '../css/syntax/parser';
|
||||||
import {TokenType} from '../css/syntax/tokenizer';
|
import {TokenType} from '../css/syntax/tokenizer';
|
||||||
import {CounterState, createCounterText} from '../css/types/functions/counter';
|
import {CounterState, createCounterText} from '../css/types/functions/counter';
|
||||||
import {LIST_STYLE_TYPE, listStyleType} from '../css/property-descriptors/list-style-type';
|
import {LIST_STYLE_TYPE, listStyleType} from '../css/property-descriptors/list-style-type';
|
||||||
@ -355,7 +355,7 @@ export class DocumentCloner {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (token.name === 'counter') {
|
} else if (token.name === 'counter') {
|
||||||
const [counter, counterStyle] = token.values.filter(nonFunctionArgSeperator);
|
const [counter, counterStyle] = token.values.filter(nonFunctionArgSeparator);
|
||||||
if (counter && isIdentToken(counter)) {
|
if (counter && isIdentToken(counter)) {
|
||||||
const counterState = this.counters.getCounterValue(counter.value);
|
const counterState = this.counters.getCounterValue(counter.value);
|
||||||
const counterType =
|
const counterType =
|
||||||
@ -368,7 +368,7 @@ export class DocumentCloner {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (token.name === 'counters') {
|
} else if (token.name === 'counters') {
|
||||||
const [counter, delim, counterStyle] = token.values.filter(nonFunctionArgSeperator);
|
const [counter, delim, counterStyle] = token.values.filter(nonFunctionArgSeparator);
|
||||||
if (counter && isIdentToken(counter)) {
|
if (counter && isIdentToken(counter)) {
|
||||||
const counterStates = this.counters.getCounterValues(counter.value);
|
const counterStates = this.counters.getCounterValues(counter.value);
|
||||||
const counterType =
|
const counterType =
|
||||||
|
@ -15,8 +15,8 @@ export class IFrameElementContainer extends ElementContainer {
|
|||||||
constructor(iframe: HTMLIFrameElement) {
|
constructor(iframe: HTMLIFrameElement) {
|
||||||
super(iframe);
|
super(iframe);
|
||||||
this.src = iframe.src;
|
this.src = iframe.src;
|
||||||
this.width = parseInt(iframe.width, 10);
|
this.width = parseInt(iframe.width, 10) || 0;
|
||||||
this.height = parseInt(iframe.height, 10);
|
this.height = parseInt(iframe.height, 10) || 0;
|
||||||
this.backgroundColor = this.styles.backgroundColor;
|
this.backgroundColor = this.styles.backgroundColor;
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
|
99
src/index.ts
99
src/index.ts
@ -4,25 +4,22 @@ import {Parser} from './css/syntax/parser';
|
|||||||
import {CloneOptions, DocumentCloner} from './dom/document-cloner';
|
import {CloneOptions, DocumentCloner} from './dom/document-cloner';
|
||||||
import {isBodyElement, isHTMLElement, parseTree} from './dom/node-parser';
|
import {isBodyElement, isHTMLElement, parseTree} from './dom/node-parser';
|
||||||
import {Logger} from './core/logger';
|
import {Logger} from './core/logger';
|
||||||
import {CacheStorage} from './core/cache-storage';
|
import {CacheStorage, ResourceOptions} from './core/cache-storage';
|
||||||
import {CanvasRenderer, RenderOptions} from './render/canvas/canvas-renderer';
|
import {CanvasRenderer, RenderOptions} from './render/canvas/canvas-renderer';
|
||||||
import {ForeignObjectRenderer} from './render/canvas/foreignobject-renderer';
|
import {ForeignObjectRenderer} from './render/canvas/foreignobject-renderer';
|
||||||
|
|
||||||
export type Options = CloneOptions &
|
export type Options = CloneOptions &
|
||||||
RenderOptions & {
|
RenderOptions &
|
||||||
allowTaint: boolean;
|
ResourceOptions & {
|
||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
foreignObjectRendering: boolean;
|
foreignObjectRendering: boolean;
|
||||||
imageTimeout: number;
|
|
||||||
logging: boolean;
|
logging: boolean;
|
||||||
proxy?: string;
|
|
||||||
removeContainer?: boolean;
|
removeContainer?: boolean;
|
||||||
useCORS: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseColor = (value: string): Color => color.parse(Parser.create(value).parseComponentValue());
|
const parseColor = (value: string): Color => color.parse(Parser.create(value).parseComponentValue());
|
||||||
|
|
||||||
const html2canvas = (element: HTMLElement, options: Options): Promise<HTMLCanvasElement> => {
|
const html2canvas = (element: HTMLElement, options: Partial<Options> = {}): Promise<HTMLCanvasElement> => {
|
||||||
return renderElement(element, options);
|
return renderElement(element, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,7 +27,7 @@ export default html2canvas;
|
|||||||
|
|
||||||
CacheStorage.setContext(window);
|
CacheStorage.setContext(window);
|
||||||
|
|
||||||
const renderElement = async (element: HTMLElement, opts: Options): Promise<HTMLCanvasElement> => {
|
const renderElement = async (element: HTMLElement, opts: Partial<Options>): Promise<HTMLCanvasElement> => {
|
||||||
const ownerDocument = element.ownerDocument;
|
const ownerDocument = element.ownerDocument;
|
||||||
|
|
||||||
if (!ownerDocument) {
|
if (!ownerDocument) {
|
||||||
@ -43,50 +40,43 @@ const renderElement = async (element: HTMLElement, opts: Options): Promise<HTMLC
|
|||||||
throw new Error(`Document is not attached to a Window`);
|
throw new Error(`Document is not attached to a Window`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions = {
|
const instanceName = (Math.round(Math.random() * 1000) + Date.now()).toString(16);
|
||||||
|
|
||||||
|
const {width, height, left, top} =
|
||||||
|
isBodyElement(element) || isHTMLElement(element) ? parseDocumentSize(ownerDocument) : parseBounds(element);
|
||||||
|
|
||||||
|
const defaultResourceOptions = {
|
||||||
allowTaint: false,
|
allowTaint: false,
|
||||||
backgroundColor: '#ffffff',
|
|
||||||
imageTimeout: 15000,
|
imageTimeout: 15000,
|
||||||
logging: true,
|
|
||||||
proxy: undefined,
|
proxy: undefined,
|
||||||
|
useCORS: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const resourceOptions: ResourceOptions = {...defaultResourceOptions, ...opts};
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
cache: opts.cache ? opts.cache : CacheStorage.create(instanceName, resourceOptions),
|
||||||
|
logging: true,
|
||||||
removeContainer: true,
|
removeContainer: true,
|
||||||
foreignObjectRendering: false,
|
foreignObjectRendering: false,
|
||||||
scale: defaultView.devicePixelRatio || 1,
|
scale: defaultView.devicePixelRatio || 1,
|
||||||
useCORS: false,
|
|
||||||
windowWidth: defaultView.innerWidth,
|
windowWidth: defaultView.innerWidth,
|
||||||
windowHeight: defaultView.innerHeight,
|
windowHeight: defaultView.innerHeight,
|
||||||
scrollX: defaultView.pageXOffset,
|
scrollX: defaultView.pageXOffset,
|
||||||
scrollY: defaultView.pageYOffset
|
scrollY: defaultView.pageYOffset,
|
||||||
|
x: left,
|
||||||
|
y: top,
|
||||||
|
width: Math.ceil(width),
|
||||||
|
height: Math.ceil(height),
|
||||||
|
id: instanceName
|
||||||
};
|
};
|
||||||
|
|
||||||
const options: Options = {...defaultOptions, ...opts};
|
const options: Options = {...defaultOptions, ...resourceOptions, ...opts};
|
||||||
|
|
||||||
const windowBounds = new Bounds(options.scrollX, options.scrollY, options.windowWidth, options.windowHeight);
|
const windowBounds = new Bounds(options.scrollX, options.scrollY, options.windowWidth, options.windowHeight);
|
||||||
|
|
||||||
// http://www.w3.org/TR/css3-background/#special-backgrounds
|
|
||||||
const documentBackgroundColor = ownerDocument.documentElement
|
|
||||||
? parseColor(getComputedStyle(ownerDocument.documentElement).backgroundColor as string)
|
|
||||||
: COLORS.TRANSPARENT;
|
|
||||||
const bodyBackgroundColor = ownerDocument.body
|
|
||||||
? parseColor(getComputedStyle(ownerDocument.body).backgroundColor as string)
|
|
||||||
: COLORS.TRANSPARENT;
|
|
||||||
|
|
||||||
const backgroundColor =
|
|
||||||
element === ownerDocument.documentElement
|
|
||||||
? isTransparent(documentBackgroundColor)
|
|
||||||
? isTransparent(bodyBackgroundColor)
|
|
||||||
? options.backgroundColor
|
|
||||||
? parseColor(options.backgroundColor)
|
|
||||||
: null
|
|
||||||
: bodyBackgroundColor
|
|
||||||
: documentBackgroundColor
|
|
||||||
: options.backgroundColor
|
|
||||||
? parseColor(options.backgroundColor)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const instanceName = (Math.round(Math.random() * 1000) + Date.now()).toString(16);
|
|
||||||
Logger.create(instanceName);
|
Logger.create(instanceName);
|
||||||
const cache = CacheStorage.create(instanceName, options);
|
|
||||||
Logger.getInstance(instanceName).debug(`Starting document clone`);
|
Logger.getInstance(instanceName).debug(`Starting document clone`);
|
||||||
const documentCloner = new DocumentCloner(element, {
|
const documentCloner = new DocumentCloner(element, {
|
||||||
id: instanceName,
|
id: instanceName,
|
||||||
@ -102,22 +92,37 @@ const renderElement = async (element: HTMLElement, opts: Options): Promise<HTMLC
|
|||||||
|
|
||||||
const container = await documentCloner.toIFrame(ownerDocument, windowBounds);
|
const container = await documentCloner.toIFrame(ownerDocument, windowBounds);
|
||||||
|
|
||||||
const {width, height, left, top} =
|
// http://www.w3.org/TR/css3-background/#special-backgrounds
|
||||||
isBodyElement(clonedElement) || isHTMLElement(clonedElement)
|
const documentBackgroundColor = ownerDocument.documentElement
|
||||||
? parseDocumentSize(ownerDocument)
|
? parseColor(getComputedStyle(ownerDocument.documentElement).backgroundColor as string)
|
||||||
: parseBounds(clonedElement);
|
: COLORS.TRANSPARENT;
|
||||||
|
const bodyBackgroundColor = ownerDocument.body
|
||||||
|
? parseColor(getComputedStyle(ownerDocument.body).backgroundColor as string)
|
||||||
|
: COLORS.TRANSPARENT;
|
||||||
|
|
||||||
|
const bgColor = opts.backgroundColor;
|
||||||
|
const defaultBackgroundColor = typeof bgColor === 'string' ? parseColor(bgColor) : bgColor === null ? COLORS.TRANSPARENT : 0xffffffff;
|
||||||
|
|
||||||
|
const backgroundColor =
|
||||||
|
element === ownerDocument.documentElement
|
||||||
|
? isTransparent(documentBackgroundColor)
|
||||||
|
? isTransparent(bodyBackgroundColor)
|
||||||
|
? defaultBackgroundColor
|
||||||
|
: bodyBackgroundColor
|
||||||
|
: documentBackgroundColor
|
||||||
|
: defaultBackgroundColor;
|
||||||
|
|
||||||
const renderOptions = {
|
const renderOptions = {
|
||||||
id: instanceName,
|
id: instanceName,
|
||||||
cache,
|
cache: options.cache,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
scale: options.scale,
|
scale: options.scale,
|
||||||
x: typeof options.x === 'number' ? options.x : left,
|
x: options.x,
|
||||||
y: typeof options.y === 'number' ? options.y : top,
|
y: options.y,
|
||||||
scrollX: options.scrollX,
|
scrollX: options.scrollX,
|
||||||
scrollY: options.scrollY,
|
scrollY: options.scrollY,
|
||||||
width: typeof options.width === 'number' ? options.width : Math.ceil(width),
|
width: options.width,
|
||||||
height: typeof options.height === 'number' ? options.height : Math.ceil(height),
|
height: options.height,
|
||||||
windowWidth: options.windowWidth,
|
windowWidth: options.windowWidth,
|
||||||
windowHeight: options.windowHeight
|
windowHeight: options.windowHeight
|
||||||
};
|
};
|
||||||
@ -131,7 +136,7 @@ const renderElement = async (element: HTMLElement, opts: Options): Promise<HTMLC
|
|||||||
} else {
|
} else {
|
||||||
Logger.getInstance(instanceName).debug(`Document cloned, using computed rendering`);
|
Logger.getInstance(instanceName).debug(`Document cloned, using computed rendering`);
|
||||||
|
|
||||||
CacheStorage.attachInstance(cache);
|
CacheStorage.attachInstance(options.cache);
|
||||||
Logger.getInstance(instanceName).debug(`Starting DOM parsing`);
|
Logger.getInstance(instanceName).debug(`Starting DOM parsing`);
|
||||||
const root = parseTree(clonedElement);
|
const root = parseTree(clonedElement);
|
||||||
CacheStorage.detachInstance();
|
CacheStorage.detachInstance();
|
||||||
|
@ -39,11 +39,14 @@ import {SelectElementContainer} from '../../dom/elements/select-element-containe
|
|||||||
import {IFrameElementContainer} from '../../dom/replaced-elements/iframe-element-container';
|
import {IFrameElementContainer} from '../../dom/replaced-elements/iframe-element-container';
|
||||||
import {TextShadow} from '../../css/property-descriptors/text-shadow';
|
import {TextShadow} from '../../css/property-descriptors/text-shadow';
|
||||||
|
|
||||||
|
export type RenderConfigurations = RenderOptions & {
|
||||||
|
backgroundColor: Color | null;
|
||||||
|
};
|
||||||
|
|
||||||
export interface RenderOptions {
|
export interface RenderOptions {
|
||||||
id: string;
|
id: string;
|
||||||
scale: number;
|
scale: number;
|
||||||
canvas?: HTMLCanvasElement;
|
canvas?: HTMLCanvasElement;
|
||||||
backgroundColor: Color | null;
|
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
scrollX: number;
|
scrollX: number;
|
||||||
@ -60,11 +63,11 @@ const MASK_OFFSET = 10000;
|
|||||||
export class CanvasRenderer {
|
export class CanvasRenderer {
|
||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
ctx: CanvasRenderingContext2D;
|
ctx: CanvasRenderingContext2D;
|
||||||
options: RenderOptions;
|
options: RenderConfigurations;
|
||||||
private readonly _activeEffects: IElementEffect[] = [];
|
private readonly _activeEffects: IElementEffect[] = [];
|
||||||
private readonly fontMetrics: FontMetrics;
|
private readonly fontMetrics: FontMetrics;
|
||||||
|
|
||||||
constructor(options: RenderOptions) {
|
constructor(options: RenderConfigurations) {
|
||||||
this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
|
this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
|
||||||
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
@ -303,18 +306,20 @@ export class CanvasRenderer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const canvas = await iframeRenderer.render(container.tree);
|
const canvas = await iframeRenderer.render(container.tree);
|
||||||
|
if (container.width && container.height) {
|
||||||
this.ctx.drawImage(
|
this.ctx.drawImage(
|
||||||
canvas,
|
canvas,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
container.width,
|
container.width,
|
||||||
container.width,
|
container.height,
|
||||||
container.bounds.left,
|
container.bounds.left,
|
||||||
container.bounds.top,
|
container.bounds.top,
|
||||||
container.bounds.width,
|
container.bounds.width,
|
||||||
container.bounds.height
|
container.bounds.height
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (container instanceof InputElementContainer) {
|
if (container instanceof InputElementContainer) {
|
||||||
const size = Math.min(container.bounds.width, container.bounds.height);
|
const size = Math.min(container.bounds.width, container.bounds.height);
|
||||||
@ -573,8 +578,10 @@ export class CanvasRenderer {
|
|||||||
|
|
||||||
ctx.fillStyle = gradient;
|
ctx.fillStyle = gradient;
|
||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
if ((width > 0) && (height > 0)) {
|
||||||
const pattern = this.ctx.createPattern(canvas, 'repeat') as CanvasPattern;
|
const pattern = this.ctx.createPattern(canvas, 'repeat') as CanvasPattern;
|
||||||
this.renderRepeat(path, pattern, x, y);
|
this.renderRepeat(path, pattern, x, y);
|
||||||
|
}
|
||||||
} else if (isRadialGradient(backgroundImage)) {
|
} else if (isRadialGradient(backgroundImage)) {
|
||||||
const [path, left, top, width, height] = calculateBackgroundRendering(container, index, [
|
const [path, left, top, width, height] = calculateBackgroundRendering(container, index, [
|
||||||
null,
|
null,
|
||||||
@ -694,8 +701,9 @@ export class CanvasRenderer {
|
|||||||
let side = 0;
|
let side = 0;
|
||||||
for (const border of borders) {
|
for (const border of borders) {
|
||||||
if (border.style !== BORDER_STYLE.NONE && !isTransparent(border.color)) {
|
if (border.style !== BORDER_STYLE.NONE && !isTransparent(border.color)) {
|
||||||
await this.renderBorder(border.color, side++, paint.curves);
|
await this.renderBorder(border.color, side, paint.curves);
|
||||||
}
|
}
|
||||||
|
side++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {RenderOptions} from './canvas-renderer';
|
import {RenderConfigurations} from './canvas-renderer';
|
||||||
import {Logger} from '../../core/logger';
|
import {Logger} from '../../core/logger';
|
||||||
import {createForeignObjectSVG} from '../../core/features';
|
import {createForeignObjectSVG} from '../../core/features';
|
||||||
import {asString} from '../../css/types/color';
|
import {asString} from '../../css/types/color';
|
||||||
@ -6,9 +6,9 @@ import {asString} from '../../css/types/color';
|
|||||||
export class ForeignObjectRenderer {
|
export class ForeignObjectRenderer {
|
||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
ctx: CanvasRenderingContext2D;
|
ctx: CanvasRenderingContext2D;
|
||||||
options: RenderOptions;
|
options: RenderConfigurations;
|
||||||
|
|
||||||
constructor(options: RenderOptions) {
|
constructor(options: RenderConfigurations) {
|
||||||
this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
|
this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
|
||||||
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
@ -104,8 +104,11 @@ const parseStackTree = (
|
|||||||
parentStack.negativeZIndex.some((current, i) => {
|
parentStack.negativeZIndex.some((current, i) => {
|
||||||
if (order > current.element.container.styles.zIndex.order) {
|
if (order > current.element.container.styles.zIndex.order) {
|
||||||
index = i;
|
index = i;
|
||||||
|
return false;
|
||||||
|
} else if (index > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
parentStack.negativeZIndex.splice(index, 0, stack);
|
parentStack.negativeZIndex.splice(index, 0, stack);
|
||||||
@ -114,6 +117,8 @@ const parseStackTree = (
|
|||||||
parentStack.positiveZIndex.some((current, i) => {
|
parentStack.positiveZIndex.some((current, i) => {
|
||||||
if (order > current.element.container.styles.zIndex.order) {
|
if (order > current.element.container.styles.zIndex.order) {
|
||||||
index = i + 1;
|
index = i + 1;
|
||||||
|
return false;
|
||||||
|
} else if (index > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,12 @@
|
|||||||
border-bottom-width: 50px;
|
border-bottom-width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.box6 {
|
||||||
|
border-style: none solid none solid;
|
||||||
|
border-color: #807d32;
|
||||||
|
border-width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
background: #3a84c3;
|
background: #3a84c3;
|
||||||
}
|
}
|
||||||
@ -51,5 +57,6 @@
|
|||||||
<div class="box3"> </div>
|
<div class="box3"> </div>
|
||||||
<div class="box4"> </div>
|
<div class="box4"> </div>
|
||||||
<div class="box5"> </div>
|
<div class="box5"> </div>
|
||||||
|
<div class="box6"> </div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
71
tests/reftests/zindex/z-index19.html
Normal file
71
tests/reftests/zindex/z-index19.html
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head lang="en">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>z-index19</title>
|
||||||
|
<style>
|
||||||
|
.canvas-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
position: relative;
|
||||||
|
width: 500px;
|
||||||
|
height: 500px;
|
||||||
|
margin-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div1 {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div2 {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
top: 100px;
|
||||||
|
left: 100px;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div3 {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3;
|
||||||
|
top: 200px;
|
||||||
|
left: 200px;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div4 {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 4;
|
||||||
|
top: 300px;
|
||||||
|
left: 300px;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript" src="../../test.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="canvas-view" id="my-home">
|
||||||
|
<div class="main" id="my-div">
|
||||||
|
<div class="div1"></div>
|
||||||
|
<div class="div2"></div>
|
||||||
|
<div class="div3"></div>
|
||||||
|
<div class="div4"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user