mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Compare commits
4 Commits
v1.0.0-rc.
...
v1.0.0-rc.
Author | SHA1 | Date | |
---|---|---|---|
8c04f94bc9 | |||
3f599103fb | |||
076492042a | |||
34b06d6365 |
11
CHANGELOG.md
11
CHANGELOG.md
@ -2,6 +2,17 @@
|
||||
|
||||
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.5](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.4...v1.0.0-rc.5) (2019-09-27)
|
||||
|
||||
|
||||
### fix
|
||||
|
||||
* correctly respect logging option (#2013) ([34b06d6365603c3b16664ab7804efe94c7945946](https://github.com/niklasvh/html2canvas/commit/34b06d6365603c3b16664ab7804efe94c7945946)), closes [#2013](https://github.com/niklasvh/html2canvas/issues/2013)
|
||||
* safari pseudo element content parsing (#2018) ([3f599103fb139f218ffe917800e74af2c7cc7ad5](https://github.com/niklasvh/html2canvas/commit/3f599103fb139f218ffe917800e74af2c7cc7ad5)), closes [#2018](https://github.com/niklasvh/html2canvas/issues/2018)
|
||||
* using existing canvas option (#2017) ([076492042a73d67b30e4562f2964200e07d25f5e](https://github.com/niklasvh/html2canvas/commit/076492042a73d67b30e4562f2964200e07d25f5e)), closes [#2017](https://github.com/niklasvh/html2canvas/issues/2017)
|
||||
|
||||
|
||||
|
||||
# [1.0.0-rc.4](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.3...v1.0.0-rc.4) (2019-09-22)
|
||||
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
ctx.stroke();
|
||||
|
||||
document.querySelector("button").addEventListener("click", function() {
|
||||
html2canvas(document.querySelector("#content"), {canvas: canvas}).then(function(canvas) {
|
||||
html2canvas(document.querySelector("#content"), {canvas: canvas, scale: 1}).then(function(canvas) {
|
||||
console.log('Drew on the existing canvas');
|
||||
});
|
||||
}, false);
|
||||
|
5
jest.config.js
Normal file
5
jest.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'jsdom',
|
||||
roots: ['src']
|
||||
};
|
2478
package-lock.json
generated
2478
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
"module": "dist/html2canvas.esm.js",
|
||||
"typings": "dist/types/index.d.ts",
|
||||
"browser": "dist/html2canvas.js",
|
||||
"version": "1.0.0-rc.4",
|
||||
"version": "1.0.0-rc.5",
|
||||
"author": {
|
||||
"name": "Niklas von Hertzen",
|
||||
"email": "niklasvh@gmail.com",
|
||||
@ -29,6 +29,7 @@
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/mocha": "^5.2.6",
|
||||
"@types/node": "^11.13.2",
|
||||
"@types/platform": "^1.3.2",
|
||||
@ -53,6 +54,7 @@
|
||||
"filenamify-url": "1.0.0",
|
||||
"glob": "7.1.3",
|
||||
"html2canvas-proxy": "1.0.1",
|
||||
"jest": "^24.9.0",
|
||||
"jquery": "^3.4.0",
|
||||
"js-polyfills": "^0.1.42",
|
||||
"karma": "^4.0.1",
|
||||
@ -79,6 +81,7 @@
|
||||
"serve-index": "^1.9.1",
|
||||
"slash": "1.0.0",
|
||||
"standard-version": "^5.0.2",
|
||||
"ts-jest": "^24.1.0",
|
||||
"ts-loader": "^5.3.3",
|
||||
"ts-node": "^8.0.3",
|
||||
"typescript": "^3.4.3",
|
||||
@ -99,7 +102,7 @@
|
||||
"format": "prettier --write \"{src,www/src,tests,scripts}/**/*.ts\"",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"test": "npm run lint && npm run unittest && npm run karma",
|
||||
"unittest": "mocha --require ts-node/register src/**/__tests__/*.ts",
|
||||
"unittest": "jest",
|
||||
"karma": "node karma",
|
||||
"watch": "rollup -c rollup.config.ts -w",
|
||||
"watch:unittest": "mocha --require ts-node/register --watch-extensions ts -w src/**/__tests__/*.ts",
|
||||
|
91
src/__tests__/index.ts
Normal file
91
src/__tests__/index.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import html2canvas from '../index';
|
||||
|
||||
import {CanvasRenderer} from '../render/canvas/canvas-renderer';
|
||||
import {DocumentCloner} from '../dom/document-cloner';
|
||||
import {COLORS} from '../css/types/color';
|
||||
|
||||
jest.mock('../core/logger');
|
||||
jest.mock('../css/layout/bounds');
|
||||
jest.mock('../dom/document-cloner');
|
||||
jest.mock('../dom/node-parser', () => {
|
||||
return {
|
||||
isBodyElement: () => false,
|
||||
isHTMLElement: () => false,
|
||||
parseTree: jest.fn().mockImplementation(() => {
|
||||
return {styles: {}};
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../render/stacking-context');
|
||||
jest.mock('../render/canvas/canvas-renderer');
|
||||
|
||||
describe('html2canvas', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
|
||||
const element = {
|
||||
ownerDocument: {
|
||||
defaultView: {
|
||||
pageXOffset: 12,
|
||||
pageYOffset: 34
|
||||
}
|
||||
}
|
||||
} as HTMLElement;
|
||||
|
||||
it('should render with an element', async () => {
|
||||
DocumentCloner.destroy = jest.fn().mockReturnValue(true);
|
||||
await html2canvas(element);
|
||||
expect(CanvasRenderer).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
backgroundColor: 0xffffffff,
|
||||
scale: 1,
|
||||
height: 50,
|
||||
width: 200,
|
||||
x: 0,
|
||||
y: 0,
|
||||
scrollX: 12,
|
||||
scrollY: 34,
|
||||
canvas: undefined
|
||||
})
|
||||
);
|
||||
expect(DocumentCloner.destroy).toBeCalled();
|
||||
});
|
||||
|
||||
it('should have transparent background with backgroundColor: null', async () => {
|
||||
await html2canvas(element, {backgroundColor: null});
|
||||
expect(CanvasRenderer).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
backgroundColor: COLORS.TRANSPARENT
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should use existing canvas when given as option', async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
|
||||
const canvas = {} as HTMLCanvasElement;
|
||||
await html2canvas(element, {canvas});
|
||||
expect(CanvasRenderer).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
canvas
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should not remove cloned window when removeContainer: false', async () => {
|
||||
DocumentCloner.destroy = jest.fn();
|
||||
await html2canvas(element, {removeContainer: false});
|
||||
expect(CanvasRenderer).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
backgroundColor: 0xffffffff,
|
||||
scale: 1,
|
||||
height: 50,
|
||||
width: 200,
|
||||
x: 0,
|
||||
y: 0,
|
||||
scrollX: 12,
|
||||
scrollY: 34,
|
||||
canvas: undefined
|
||||
})
|
||||
);
|
||||
expect(DocumentCloner.destroy).not.toBeCalled();
|
||||
});
|
||||
});
|
22
src/core/__mocks__/cache-storage.ts
Normal file
22
src/core/__mocks__/cache-storage.ts
Normal file
@ -0,0 +1,22 @@
|
||||
class MockCache {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private readonly _cache: {[key: string]: Promise<any>};
|
||||
|
||||
constructor() {
|
||||
this._cache = {};
|
||||
}
|
||||
|
||||
addImage(src: string): Promise<void> {
|
||||
const result = Promise.resolve();
|
||||
this._cache[src] = result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const current = new MockCache();
|
||||
|
||||
export class CacheStorage {
|
||||
static getInstance(): MockCache {
|
||||
return current;
|
||||
}
|
||||
}
|
8
src/core/__mocks__/features.ts
Normal file
8
src/core/__mocks__/features.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export const FEATURES = {
|
||||
SUPPORT_RANGE_BOUNDS: true,
|
||||
SUPPORT_SVG_DRAWING: true,
|
||||
SUPPORT_FOREIGNOBJECT_DRAWING: true,
|
||||
SUPPORT_CORS_IMAGES: true,
|
||||
SUPPORT_RESPONSE_TYPE: true,
|
||||
SUPPORT_CORS_XHR: true
|
||||
};
|
17
src/core/__mocks__/logger.ts
Normal file
17
src/core/__mocks__/logger.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export class Logger {
|
||||
debug() {}
|
||||
|
||||
static create() {}
|
||||
|
||||
static destroy() {}
|
||||
|
||||
static getInstance(): Logger {
|
||||
return logger;
|
||||
}
|
||||
|
||||
info() {}
|
||||
|
||||
error() {}
|
||||
}
|
||||
|
||||
const logger = new Logger();
|
@ -1,6 +1,49 @@
|
||||
import {deepStrictEqual, fail} from 'assert';
|
||||
import {FEATURES} from '../features';
|
||||
import {createMockContext, proxy} from './mock-context';
|
||||
import {CacheStorage} from '../cache-storage';
|
||||
import {Logger} from '../logger';
|
||||
|
||||
const proxy = 'http://example.com/proxy';
|
||||
|
||||
const createMockContext = (origin: string, opts = {}) => {
|
||||
const context = {
|
||||
location: {
|
||||
href: origin
|
||||
},
|
||||
document: {
|
||||
createElement(_name: string) {
|
||||
let _href = '';
|
||||
return {
|
||||
set href(value: string) {
|
||||
_href = value;
|
||||
},
|
||||
get href() {
|
||||
return _href;
|
||||
},
|
||||
get protocol() {
|
||||
return new URL(_href).protocol;
|
||||
},
|
||||
get hostname() {
|
||||
return new URL(_href).hostname;
|
||||
},
|
||||
get port() {
|
||||
return new URL(_href).port;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CacheStorage.setContext(context as Window);
|
||||
Logger.create({id: 'test', enabled: false});
|
||||
return CacheStorage.create('test', {
|
||||
imageTimeout: 0,
|
||||
useCORS: false,
|
||||
allowTaint: false,
|
||||
proxy,
|
||||
...opts
|
||||
});
|
||||
};
|
||||
|
||||
const images: ImageMock[] = [];
|
||||
const xhr: XMLHttpRequestMock[] = [];
|
||||
|
27
src/core/__tests__/logger.ts
Normal file
27
src/core/__tests__/logger.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import {Logger} from '../logger';
|
||||
|
||||
describe('logger', () => {
|
||||
let infoSpy: any;
|
||||
|
||||
beforeEach(() => {
|
||||
infoSpy = jest.spyOn(console, 'info').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
infoSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should call console.info when logger enabled', () => {
|
||||
const id = Math.random().toString();
|
||||
const logger = new Logger({id, enabled: true});
|
||||
logger.info('testing');
|
||||
expect(infoSpy).toHaveBeenLastCalledWith(id, expect.stringMatching(/\d+ms/), 'testing');
|
||||
});
|
||||
|
||||
it("shouldn't call console.info when logger disabled", () => {
|
||||
const id = Math.random().toString();
|
||||
const logger = new Logger({id, enabled: false});
|
||||
logger.info('testing');
|
||||
expect(infoSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -1,45 +0,0 @@
|
||||
import {CacheStorage} from '../cache-storage';
|
||||
import {URL} from 'url';
|
||||
import {Logger} from '../logger';
|
||||
|
||||
export const proxy = 'http://example.com/proxy';
|
||||
|
||||
export const createMockContext = (origin: string, opts = {}) => {
|
||||
const context = {
|
||||
location: {
|
||||
href: origin
|
||||
},
|
||||
document: {
|
||||
createElement(_name: string) {
|
||||
let _href = '';
|
||||
return {
|
||||
set href(value: string) {
|
||||
_href = value;
|
||||
},
|
||||
get href() {
|
||||
return _href;
|
||||
},
|
||||
get protocol() {
|
||||
return new URL(_href).protocol;
|
||||
},
|
||||
get hostname() {
|
||||
return new URL(_href).hostname;
|
||||
},
|
||||
get port() {
|
||||
return new URL(_href).port;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CacheStorage.setContext(context as Window);
|
||||
Logger.create('test');
|
||||
return CacheStorage.create('test', {
|
||||
imageTimeout: 0,
|
||||
useCORS: false,
|
||||
allowTaint: false,
|
||||
proxy,
|
||||
...opts
|
||||
});
|
||||
};
|
@ -1,22 +1,31 @@
|
||||
export interface LoggerOptions {
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export class Logger {
|
||||
static instances: {[key: string]: Logger} = {};
|
||||
|
||||
private readonly id: string;
|
||||
private readonly enabled: boolean;
|
||||
private readonly start: number;
|
||||
|
||||
constructor(id: string) {
|
||||
constructor({id, enabled}: LoggerOptions) {
|
||||
this.id = id;
|
||||
this.enabled = enabled;
|
||||
this.start = Date.now();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
debug(...args: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
if (typeof window !== 'undefined' && window.console && typeof console.debug === 'function') {
|
||||
if (this.enabled) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(this.id, `${this.getTime()}ms`, ...args);
|
||||
} else {
|
||||
this.info(...args);
|
||||
if (typeof window !== 'undefined' && window.console && typeof console.debug === 'function') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(this.id, `${this.getTime()}ms`, ...args);
|
||||
} else {
|
||||
this.info(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,8 +33,8 @@ export class Logger {
|
||||
return Date.now() - this.start;
|
||||
}
|
||||
|
||||
static create(id: string) {
|
||||
Logger.instances[id] = new Logger(id);
|
||||
static create(options: LoggerOptions) {
|
||||
Logger.instances[options.id] = new Logger(options);
|
||||
}
|
||||
|
||||
static destroy(id: string) {
|
||||
@ -42,21 +51,25 @@ export class Logger {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
info(...args: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
if (typeof window !== 'undefined' && window.console && typeof console.info === 'function') {
|
||||
if (this.enabled) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(this.id, `${this.getTime()}ms`, ...args);
|
||||
if (typeof window !== 'undefined' && window.console && typeof console.info === 'function') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(this.id, `${this.getTime()}ms`, ...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
error(...args: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
if (typeof window !== 'undefined' && window.console && typeof console.error === 'function') {
|
||||
if (this.enabled) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(this.id, `${this.getTime()}ms`, ...args);
|
||||
} else {
|
||||
this.info(...args);
|
||||
if (typeof window !== 'undefined' && window.console && typeof console.error === 'function') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(this.id, `${this.getTime()}ms`, ...args);
|
||||
} else {
|
||||
this.info(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
4
src/css/layout/__mocks__/bounds.ts
Normal file
4
src/css/layout/__mocks__/bounds.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export const {Bounds} = jest.requireActual('../bounds');
|
||||
export const parseBounds = () => {
|
||||
return new Bounds(0, 0, 200, 50);
|
||||
};
|
@ -4,15 +4,13 @@ import {backgroundImage} from '../background-image';
|
||||
import {CSSImageType} from '../../types/image';
|
||||
import {pack} from '../../types/color';
|
||||
import {deg} from '../../types/angle';
|
||||
import {createMockContext} from '../../../core/__tests__/mock-context';
|
||||
import {CacheStorage} from '../../../core/cache-storage';
|
||||
|
||||
jest.mock('../../../core/cache-storage');
|
||||
jest.mock('../../../core/features');
|
||||
|
||||
const backgroundImageParse = (value: string) => backgroundImage.parse(Parser.parseValues(value));
|
||||
|
||||
describe('property-descriptors', () => {
|
||||
before(() => {
|
||||
CacheStorage.attachInstance(createMockContext('http://example.com'));
|
||||
});
|
||||
describe('background-image', () => {
|
||||
it('none', () => deepStrictEqual(backgroundImageParse('none'), []));
|
||||
|
||||
|
@ -8,6 +8,9 @@ import {deg} from '../angle';
|
||||
const parse = (value: string) => image.parse(Parser.parseValue(value));
|
||||
const colorParse = (value: string) => color.parse(Parser.parseValue(value));
|
||||
|
||||
jest.mock('../../../core/cache-storage');
|
||||
jest.mock('../../../core/features');
|
||||
|
||||
describe('types', () => {
|
||||
describe('<image>', () => {
|
||||
describe('parsing', () => {
|
||||
|
@ -30,25 +30,29 @@ export class CounterState {
|
||||
parse(style: CSSParsedCounterDeclaration): string[] {
|
||||
const counterIncrement = style.counterIncrement;
|
||||
const counterReset = style.counterReset;
|
||||
let canReset = true;
|
||||
|
||||
if (counterIncrement !== null) {
|
||||
counterIncrement.forEach(entry => {
|
||||
const counter = this.counters[entry.counter];
|
||||
if (counter) {
|
||||
if (counter && entry.increment !== 0) {
|
||||
canReset = false;
|
||||
counter[Math.max(0, counter.length - 1)] += entry.increment;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const counterNames: string[] = [];
|
||||
counterReset.forEach(entry => {
|
||||
let counter = this.counters[entry.counter];
|
||||
counterNames.push(entry.counter);
|
||||
if (!counter) {
|
||||
counter = this.counters[entry.counter] = [];
|
||||
}
|
||||
counter.push(entry.reset);
|
||||
});
|
||||
if (canReset) {
|
||||
counterReset.forEach(entry => {
|
||||
let counter = this.counters[entry.counter];
|
||||
counterNames.push(entry.counter);
|
||||
if (!counter) {
|
||||
counter = this.counters[entry.counter] = [];
|
||||
}
|
||||
counter.push(entry.reset);
|
||||
});
|
||||
}
|
||||
|
||||
return counterNames;
|
||||
}
|
||||
|
16
src/dom/__mocks__/document-cloner.ts
Normal file
16
src/dom/__mocks__/document-cloner.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export class DocumentCloner {
|
||||
clonedReferenceElement?: HTMLElement;
|
||||
|
||||
constructor() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
|
||||
this.clonedReferenceElement = {} as HTMLElement;
|
||||
}
|
||||
|
||||
toIFrame() {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
|
||||
static destroy() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -71,7 +71,7 @@ export class DocumentCloner {
|
||||
if window url is about:blank, we can assign the url to current by writing onto the document
|
||||
*/
|
||||
|
||||
const iframeLoad = iframeLoader(iframe).then(() => {
|
||||
const iframeLoad = iframeLoader(iframe).then(async () => {
|
||||
this.scrolledElements.forEach(restoreNodeScroll);
|
||||
if (cloneWindow) {
|
||||
cloneWindow.scrollTo(windowSize.left, windowSize.top);
|
||||
@ -91,6 +91,10 @@ export class DocumentCloner {
|
||||
return Promise.reject(`Error finding the ${this.referenceElement.nodeName} in the cloned document`);
|
||||
}
|
||||
|
||||
if (documentClone.fonts && documentClone.fonts.ready) {
|
||||
await documentClone.fonts.ready;
|
||||
}
|
||||
|
||||
if (typeof onclone === 'function') {
|
||||
return Promise.resolve()
|
||||
.then(() => onclone(documentClone))
|
||||
@ -398,7 +402,8 @@ export class DocumentCloner {
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// console.log('ident', token, declaration);
|
||||
// safari doesn't parse string tokens correctly because of lack of quotes
|
||||
anonymousReplacedElement.appendChild(document.createTextNode(token.value));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -410,6 +415,14 @@ export class DocumentCloner {
|
||||
: ` ${PSEUDO_HIDE_ELEMENT_CLASS_AFTER}`;
|
||||
return anonymousReplacedElement;
|
||||
}
|
||||
|
||||
static destroy(container: HTMLIFrameElement): boolean {
|
||||
if (container.parentNode) {
|
||||
container.parentNode.removeChild(container);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
enum PseudoElementType {
|
||||
|
4
src/global.d.ts
vendored
4
src/global.d.ts
vendored
@ -7,3 +7,7 @@ interface CSSStyleDeclaration {
|
||||
interface DocumentType extends Node, ChildNode {
|
||||
readonly internalSubset: string | null;
|
||||
}
|
||||
|
||||
interface Document {
|
||||
fonts: any;
|
||||
}
|
||||
|
18
src/index.ts
18
src/index.ts
@ -11,7 +11,7 @@ import {ForeignObjectRenderer} from './render/canvas/foreignobject-renderer';
|
||||
export type Options = CloneOptions &
|
||||
RenderOptions &
|
||||
ResourceOptions & {
|
||||
backgroundColor: string;
|
||||
backgroundColor: string | null;
|
||||
foreignObjectRendering: boolean;
|
||||
logging: boolean;
|
||||
removeContainer?: boolean;
|
||||
@ -76,7 +76,7 @@ const renderElement = async (element: HTMLElement, opts: Partial<Options>): Prom
|
||||
|
||||
const windowBounds = new Bounds(options.scrollX, options.scrollY, options.windowWidth, options.windowHeight);
|
||||
|
||||
Logger.create(instanceName);
|
||||
Logger.create({id: instanceName, enabled: options.logging});
|
||||
Logger.getInstance(instanceName).debug(`Starting document clone`);
|
||||
const documentCloner = new DocumentCloner(element, {
|
||||
id: instanceName,
|
||||
@ -101,7 +101,8 @@ const renderElement = async (element: HTMLElement, opts: Partial<Options>): Prom
|
||||
: COLORS.TRANSPARENT;
|
||||
|
||||
const bgColor = opts.backgroundColor;
|
||||
const defaultBackgroundColor = typeof bgColor === 'string' ? parseColor(bgColor) : bgColor === null ? COLORS.TRANSPARENT : 0xffffffff;
|
||||
const defaultBackgroundColor =
|
||||
typeof bgColor === 'string' ? parseColor(bgColor) : bgColor === null ? COLORS.TRANSPARENT : 0xffffffff;
|
||||
|
||||
const backgroundColor =
|
||||
element === ownerDocument.documentElement
|
||||
@ -115,6 +116,7 @@ const renderElement = async (element: HTMLElement, opts: Partial<Options>): Prom
|
||||
const renderOptions = {
|
||||
id: instanceName,
|
||||
cache: options.cache,
|
||||
canvas: options.canvas,
|
||||
backgroundColor,
|
||||
scale: options.scale,
|
||||
x: options.x,
|
||||
@ -152,7 +154,7 @@ const renderElement = async (element: HTMLElement, opts: Partial<Options>): Prom
|
||||
}
|
||||
|
||||
if (options.removeContainer === true) {
|
||||
if (!cleanContainer(container)) {
|
||||
if (!DocumentCloner.destroy(container)) {
|
||||
Logger.getInstance(instanceName).error(`Cannot detach cloned iframe as it is not in the DOM anymore`);
|
||||
}
|
||||
}
|
||||
@ -162,11 +164,3 @@ const renderElement = async (element: HTMLElement, opts: Partial<Options>): Prom
|
||||
CacheStorage.destroy(instanceName);
|
||||
return canvas;
|
||||
};
|
||||
|
||||
const cleanContainer = (container: HTMLIFrameElement): boolean => {
|
||||
if (container.parentNode) {
|
||||
container.parentNode.removeChild(container);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
@ -71,10 +71,12 @@ export class CanvasRenderer {
|
||||
this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
|
||||
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
this.options = options;
|
||||
this.canvas.width = Math.floor(options.width * options.scale);
|
||||
this.canvas.height = Math.floor(options.height * options.scale);
|
||||
this.canvas.style.width = `${options.width}px`;
|
||||
this.canvas.style.height = `${options.height}px`;
|
||||
if (!options.canvas) {
|
||||
this.canvas.width = Math.floor(options.width * options.scale);
|
||||
this.canvas.height = Math.floor(options.height * options.scale);
|
||||
this.canvas.style.width = `${options.width}px`;
|
||||
this.canvas.style.height = `${options.height}px`;
|
||||
}
|
||||
this.fontMetrics = new FontMetrics(document);
|
||||
this.ctx.scale(this.options.scale, this.options.scale);
|
||||
this.ctx.translate(-options.x + options.scrollX, -options.y + options.scrollY);
|
||||
@ -578,10 +580,10 @@ export class CanvasRenderer {
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
if ((width > 0) && (height > 0)) {
|
||||
const pattern = this.ctx.createPattern(canvas, 'repeat') as CanvasPattern;
|
||||
this.renderRepeat(path, pattern, x, y);
|
||||
}
|
||||
if (width > 0 && height > 0) {
|
||||
const pattern = this.ctx.createPattern(canvas, 'repeat') as CanvasPattern;
|
||||
this.renderRepeat(path, pattern, x, y);
|
||||
}
|
||||
} else if (isRadialGradient(backgroundImage)) {
|
||||
const [path, left, top, width, height] = calculateBackgroundRendering(container, index, [
|
||||
null,
|
||||
|
@ -36,7 +36,7 @@ export default {
|
||||
// Allow json resolution
|
||||
json(),
|
||||
// Compile TypeScript files
|
||||
typescript({useTsconfigDeclarationDir: true}),
|
||||
typescript({useTsconfigDeclarationDir: true, tsconfig: resolve(__dirname, 'tsconfig.json')}),
|
||||
// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
|
||||
commonjs({
|
||||
include: 'node_modules/**',
|
||||
|
6
tests/tsconfig.json
Normal file
6
tests/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node", "mocha"]
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"resolveJsonModule": true,
|
||||
"typeRoots": [ "./types", "./node_modules/@types"],
|
||||
"types": ["node", "jest"],
|
||||
"target": "es5",
|
||||
"lib": ["es2015", "dom"],
|
||||
"sourceMap": true,
|
||||
|
@ -42,6 +42,10 @@ function onBrowserChange(browserTest: Test) {
|
||||
previewImage.src = `/results/${browserTest.screenshot}.png`;
|
||||
if (browserTest.devicePixelRatio > 1) {
|
||||
previewImage.style.transform = `scale(${1 / browserTest.devicePixelRatio})`;
|
||||
previewImage.style.transformOrigin = 'top left';
|
||||
} else {
|
||||
previewImage.style.transform = '';
|
||||
previewImage.style.transformOrigin = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user