html2canvas/src/index.js

180 lines
6.4 KiB
JavaScript
Raw Normal View History

2017-07-29 05:07:42 +03:00
/* @flow */
'use strict';
2017-08-06 19:26:09 +03:00
import type {RenderTarget} from './Renderer';
2017-07-29 05:07:42 +03:00
import {NodeParser} from './NodeParser';
import Renderer from './Renderer';
2017-08-17 18:14:44 +03:00
import ForeignObjectRenderer from './renderer/ForeignObjectRenderer';
import CanvasRenderer from './renderer/CanvasRenderer';
2017-07-29 05:07:42 +03:00
import Logger from './Logger';
import ImageLoader from './ImageLoader';
2017-08-17 18:14:44 +03:00
import Feature from './Feature';
import {Bounds, parseDocumentSize} from './Bounds';
2017-08-17 18:14:44 +03:00
import {cloneWindow, DocumentCloner} from './Clone';
import {FontMetrics} from './Font';
2017-08-17 18:14:44 +03:00
import Color, {TRANSPARENT} from './Color';
2017-07-29 05:07:42 +03:00
export type Options = {
2017-08-01 17:36:51 +03:00
async: ?boolean,
allowTaint: ?boolean,
canvas: ?HTMLCanvasElement,
imageTimeout: number,
2017-08-01 17:36:51 +03:00
proxy: ?string,
removeContainer: ?boolean,
scale: number,
target: RenderTarget<*>,
type: ?string,
windowWidth: number,
2017-08-08 19:50:31 +03:00
windowHeight: number,
offsetX: number,
offsetY: number
2017-07-29 05:07:42 +03:00
};
2017-08-06 19:26:09 +03:00
const html2canvas = (element: HTMLElement, config: Options): Promise<*> => {
2017-08-06 13:13:40 +03:00
if (typeof console === 'object' && typeof console.log === 'function') {
console.log(`html2canvas ${__VERSION__}`);
}
2017-07-29 05:07:42 +03:00
const logger = new Logger();
2017-08-01 15:54:18 +03:00
const ownerDocument = element.ownerDocument;
const defaultView = ownerDocument.defaultView;
2017-08-01 17:36:51 +03:00
const defaultOptions = {
async: true,
allowTaint: false,
2017-08-17 18:14:44 +03:00
imageTimeout: 15000,
2017-08-01 17:36:51 +03:00
proxy: null,
removeContainer: true,
scale: defaultView.devicePixelRatio || 1,
2017-08-06 19:26:09 +03:00
target: new CanvasRenderer(config.canvas),
type: null,
windowWidth: defaultView.innerWidth,
2017-08-08 19:50:31 +03:00
windowHeight: defaultView.innerHeight,
offsetX: defaultView.pageXOffset,
offsetY: defaultView.pageYOffset
2017-08-01 17:36:51 +03:00
};
const options = {...defaultOptions, ...config};
const windowBounds = new Bounds(
2017-08-08 19:50:31 +03:00
options.offsetX,
options.offsetY,
options.windowWidth,
options.windowHeight
);
2017-08-17 18:14:44 +03:00
const bounds = options.type === 'view' ? windowBounds : parseDocumentSize(ownerDocument);
// http://www.w3.org/TR/css3-background/#special-backgrounds
const documentBackgroundColor = ownerDocument.documentElement
? new Color(getComputedStyle(ownerDocument.documentElement).backgroundColor)
: TRANSPARENT;
const backgroundColor =
element === ownerDocument.documentElement
? documentBackgroundColor.isTransparent()
? ownerDocument.body
? new Color(getComputedStyle(ownerDocument.body).backgroundColor)
: null
: documentBackgroundColor
: null;
// $FlowFixMe
const result = Feature.SUPPORT_FOREIGNOBJECT_DRAWING.then(
supportForeignObject =>
supportForeignObject
? (cloner => {
if (__DEV__) {
logger.log(`Document cloned, using foreignObject rendering`);
}
return cloner.imageLoader.ready().then(() => {
const renderer = new ForeignObjectRenderer(cloner.clonedReferenceElement);
return renderer.render({
bounds,
backgroundColor,
logger,
scale: options.scale
});
});
})(new DocumentCloner(element, options, logger, true))
2017-08-17 18:14:44 +03:00
: cloneWindow(
ownerDocument,
windowBounds,
element,
options,
logger
).then(([container, clonedElement]) => {
if (__DEV__) {
logger.log(`Document cloned, using computed rendering`);
}
const imageLoader = new ImageLoader(
options,
logger,
clonedElement.ownerDocument.defaultView
);
const stack = NodeParser(clonedElement, imageLoader, logger);
const clonedDocument = clonedElement.ownerDocument;
const width = bounds.width;
const height = bounds.height;
if (backgroundColor === stack.container.style.background.backgroundColor) {
stack.container.style.background.backgroundColor = TRANSPARENT;
}
return imageLoader.ready().then(imageStore => {
if (options.removeContainer === true) {
if (container.parentNode) {
container.parentNode.removeChild(container);
} else if (__DEV__) {
logger.log(
`Cannot detach cloned iframe as it is not in the DOM anymore`
);
}
}
const fontMetrics = new FontMetrics(clonedDocument);
if (__DEV__) {
logger.log(`Starting renderer`);
}
const renderOptions = {
backgroundColor,
fontMetrics,
imageStore,
logger,
scale: options.scale,
width,
height
};
if (Array.isArray(options.target)) {
return Promise.all(
options.target.map(target => {
const renderer = new Renderer(target, renderOptions);
return renderer.render(stack);
})
);
} else {
const renderer = new Renderer(options.target, renderOptions);
return renderer.render(stack);
}
});
})
);
2017-08-03 19:13:20 +03:00
if (__DEV__) {
return result.catch(e => {
logger.error(e);
throw e;
});
}
return result;
2017-07-29 05:07:42 +03:00
};
2017-08-06 19:26:09 +03:00
html2canvas.CanvasRenderer = CanvasRenderer;
2017-07-29 05:07:42 +03:00
module.exports = html2canvas;