mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Compare commits
13 Commits
v1.0.0-alp
...
v1.0.0-alp
Author | SHA1 | Date | |
---|---|---|---|
0aa973ab0d | |||
baaf9b0701 | |||
02de2ee829 | |||
a570f5df74 | |||
38749bc4b6 | |||
e1d6b4c76f | |||
31f2c22477 | |||
7335984ab7 | |||
4551976246 | |||
9e04772b42 | |||
54c4002df7 | |||
91641a3746 | |||
0c8d38d9c0 |
@ -48,7 +48,7 @@ deploy:
|
||||
- provider: pages
|
||||
skip_cleanup: true
|
||||
local_dir: www/public
|
||||
target_branch: gh-pages-test
|
||||
target_branch: gh-pages
|
||||
fqdn: html2canvas.hertzen.com
|
||||
github_token:
|
||||
secure: "PowO/Jat660k3gHcjgI6DlJz15RM7pLUu11UPsLCtYJ8ZwodppE6Keg0DfVkSFSIZttZor+UssDwP/WOEqfZNLqmXbcj3Gec4xolohet/GOe0KJKKuF/HgggbcxumopxMX6sMVePlMBpkLpHh7tgEAEHBWTlzC1c1a7Xa48fZ7k="
|
||||
|
@ -1,5 +1,11 @@
|
||||
### Changelog ###
|
||||
|
||||
#### v1.0.0-alpha5 - 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 ####
|
||||
* Fix rendering with multiple fonts defined (Fix #796)
|
||||
* Add support for radial-gradients
|
||||
|
@ -3,7 +3,7 @@ html2canvas
|
||||
|
||||
[Homepage](https://html2canvas.hertzen.com) | [Downloads](https://github.com/niklasvh/html2canvas/releases) | [Questions](http://stackoverflow.com/questions/tagged/html2canvas?sort=newest) | [Donate](https://www.gittip.com/niklasvh/)
|
||||
|
||||
[](https://gitter.im/niklasvh/html2canvas?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://travis-ci.org/niklasvh/html2canvas)
|
||||
[](https://gitter.im/niklasvh/html2canvas?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://travis-ci.org/niklasvh/html2canvas)
|
||||
|
||||
#### JavaScript HTML renderer ####
|
||||
|
||||
|
@ -21,6 +21,7 @@ These are all of the available configuration options.
|
||||
| proxy | `null` | Url to the [proxy](/proxy/) which is to be used for loading cross-origin images. If left empty, cross-origin images won't be loaded.
|
||||
| removeContainer | `true` | Whether to cleanup the cloned DOM elements html2canvas creates temporarily
|
||||
| scale | `window.devicePixelRatio` | The scale to use for rendering. Defaults to the browsers device pixel ratio.
|
||||
| useCORS | `false` | Whether to attempt to load images from a server using CORS
|
||||
| width | `Element` width | The width of the `canvas`
|
||||
| height | `Element` height | The height of the `canvas`
|
||||
| x | `Element` x-offset | Crop canvas x-coordinate
|
||||
@ -29,3 +30,5 @@ These are all of the available configuration options.
|
||||
| scrollY | `Element` scrollY | The y-scroll position to used when rendering element, (for example if the Element uses `position: fixed`)
|
||||
| windowWidth | `Window.innerWidth` | Window width to use when rendering `Element`, which may affect things like Media queries
|
||||
| windowHeight | `Window.innerHeight` | Window height to use when rendering `Element`, which may affect things like Media queries
|
||||
|
||||
If you wish to exclude certain `Element`s from getting rendered, you can add a `data-html2canvas-ignore` attribute to those elements and html2canvas will exclude them from the rendering.
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "html2canvas",
|
||||
"description": "Screenshots with JavaScript",
|
||||
"main": "dist/npm/index.js",
|
||||
"version": "1.0.0-alpha.4",
|
||||
"version": "1.0.0-alpha.5",
|
||||
"author": {
|
||||
"name": "Niklas von Hertzen",
|
||||
"email": "niklasvh@gmail.com",
|
||||
|
@ -182,6 +182,7 @@ export class DocumentCloner {
|
||||
removeContainer: this.options.removeContainer,
|
||||
scale: this.options.scale,
|
||||
foreignObjectRendering: this.options.foreignObjectRendering,
|
||||
useCORS: this.options.useCORS,
|
||||
target: new CanvasRenderer(),
|
||||
width,
|
||||
height,
|
||||
|
674
src/ListItem.js
Normal file
674
src/ListItem.js
Normal file
@ -0,0 +1,674 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
import type ResourceLoader from './ResourceLoader';
|
||||
import type {ListStyleType} from './parsing/listStyle';
|
||||
|
||||
import {copyCSSStyles, contains} from './Util';
|
||||
import NodeContainer from './NodeContainer';
|
||||
import TextContainer from './TextContainer';
|
||||
import {LIST_STYLE_POSITION, LIST_STYLE_TYPE} from './parsing/listStyle';
|
||||
import {fromCodePoint} from './Unicode';
|
||||
|
||||
// Margin between the enumeration and the list item content
|
||||
const MARGIN_RIGHT = 7;
|
||||
|
||||
const ancestorTypes = ['OL', 'UL', 'MENU'];
|
||||
|
||||
export const getListOwner = (container: NodeContainer): ?NodeContainer => {
|
||||
let parent = container.parent;
|
||||
if (!parent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
do {
|
||||
let isAncestor = ancestorTypes.indexOf(parent.tagName) !== -1;
|
||||
if (isAncestor) {
|
||||
return parent;
|
||||
}
|
||||
parent = parent.parent;
|
||||
} while (parent);
|
||||
|
||||
return container.parent;
|
||||
};
|
||||
|
||||
export const inlineListItemElement = (
|
||||
node: HTMLElement,
|
||||
container: NodeContainer,
|
||||
resourceLoader: ResourceLoader
|
||||
): void => {
|
||||
const listStyle = container.style.listStyle;
|
||||
|
||||
if (!listStyle) {
|
||||
return;
|
||||
}
|
||||
|
||||
const style = node.ownerDocument.defaultView.getComputedStyle(node, null);
|
||||
const wrapper = node.ownerDocument.createElement('html2canvaswrapper');
|
||||
copyCSSStyles(style, wrapper);
|
||||
|
||||
wrapper.style.position = 'absolute';
|
||||
wrapper.style.bottom = 'auto';
|
||||
wrapper.style.display = 'block';
|
||||
wrapper.style.letterSpacing = 'normal';
|
||||
|
||||
switch (listStyle.listStylePosition) {
|
||||
case LIST_STYLE_POSITION.OUTSIDE:
|
||||
wrapper.style.left = 'auto';
|
||||
wrapper.style.right = `${node.ownerDocument.defaultView.innerWidth -
|
||||
container.bounds.left -
|
||||
container.style.margin[1].getAbsoluteValue(container.bounds.width) +
|
||||
MARGIN_RIGHT}px`;
|
||||
wrapper.style.textAlign = 'right';
|
||||
break;
|
||||
case LIST_STYLE_POSITION.INSIDE:
|
||||
wrapper.style.left = `${container.bounds.left -
|
||||
container.style.margin[3].getAbsoluteValue(container.bounds.width)}px`;
|
||||
wrapper.style.right = 'auto';
|
||||
wrapper.style.textAlign = 'left';
|
||||
break;
|
||||
}
|
||||
|
||||
let text;
|
||||
const MARGIN_TOP = container.style.margin[0].getAbsoluteValue(container.bounds.width);
|
||||
const styleImage = listStyle.listStyleImage;
|
||||
if (styleImage) {
|
||||
if (styleImage.method === 'url') {
|
||||
const image = node.ownerDocument.createElement('img');
|
||||
image.src = styleImage.args[0];
|
||||
wrapper.style.top = `${container.bounds.top - MARGIN_TOP}px`;
|
||||
wrapper.style.width = 'auto';
|
||||
wrapper.style.height = 'auto';
|
||||
wrapper.appendChild(image);
|
||||
} else {
|
||||
const size = parseFloat(container.style.font.fontSize) * 0.5;
|
||||
wrapper.style.top = `${container.bounds.top -
|
||||
MARGIN_TOP +
|
||||
container.bounds.height -
|
||||
1.5 * size}px`;
|
||||
wrapper.style.width = `${size}px`;
|
||||
wrapper.style.height = `${size}px`;
|
||||
wrapper.style.backgroundImage = style.listStyleImage;
|
||||
}
|
||||
} else if (typeof container.listIndex === 'number') {
|
||||
text = node.ownerDocument.createTextNode(
|
||||
createCounterText(container.listIndex, listStyle.listStyleType)
|
||||
);
|
||||
wrapper.appendChild(text);
|
||||
wrapper.style.top = `${container.bounds.top - MARGIN_TOP}px`;
|
||||
}
|
||||
|
||||
// $FlowFixMe
|
||||
const body: HTMLBodyElement = node.ownerDocument.body;
|
||||
body.appendChild(wrapper);
|
||||
|
||||
if (text) {
|
||||
container.childNodes.push(TextContainer.fromTextNode(text, container));
|
||||
body.removeChild(wrapper);
|
||||
} else {
|
||||
// $FlowFixMe
|
||||
container.childNodes.push(new NodeContainer(wrapper, container, resourceLoader, 0));
|
||||
}
|
||||
};
|
||||
|
||||
const ROMAN_UPPER = {
|
||||
integers: [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
|
||||
values: ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']
|
||||
};
|
||||
|
||||
const ARMENIAN = {
|
||||
integers: [
|
||||
9000,
|
||||
8000,
|
||||
7000,
|
||||
6000,
|
||||
5000,
|
||||
4000,
|
||||
3000,
|
||||
2000,
|
||||
1000,
|
||||
900,
|
||||
800,
|
||||
700,
|
||||
600,
|
||||
500,
|
||||
400,
|
||||
300,
|
||||
200,
|
||||
100,
|
||||
90,
|
||||
80,
|
||||
70,
|
||||
60,
|
||||
50,
|
||||
40,
|
||||
30,
|
||||
20,
|
||||
10,
|
||||
9,
|
||||
8,
|
||||
7,
|
||||
6,
|
||||
5,
|
||||
4,
|
||||
3,
|
||||
2,
|
||||
1
|
||||
],
|
||||
values: [
|
||||
'Ք',
|
||||
'Փ',
|
||||
'Ւ',
|
||||
'Ց',
|
||||
'Ր',
|
||||
'Տ',
|
||||
'Վ',
|
||||
'Ս',
|
||||
'Ռ',
|
||||
'Ջ',
|
||||
'Պ',
|
||||
'Չ',
|
||||
'Ո',
|
||||
'Շ',
|
||||
'Ն',
|
||||
'Յ',
|
||||
'Մ',
|
||||
'Ճ',
|
||||
'Ղ',
|
||||
'Ձ',
|
||||
'Հ',
|
||||
'Կ',
|
||||
'Ծ',
|
||||
'Խ',
|
||||
'Լ',
|
||||
'Ի',
|
||||
'Ժ',
|
||||
'Թ',
|
||||
'Ը',
|
||||
'Է',
|
||||
'Զ',
|
||||
'Ե',
|
||||
'Դ',
|
||||
'Գ',
|
||||
'Բ',
|
||||
'Ա'
|
||||
]
|
||||
};
|
||||
|
||||
const HEBREW = {
|
||||
integers: [
|
||||
10000,
|
||||
9000,
|
||||
8000,
|
||||
7000,
|
||||
6000,
|
||||
5000,
|
||||
4000,
|
||||
3000,
|
||||
2000,
|
||||
1000,
|
||||
400,
|
||||
300,
|
||||
200,
|
||||
100,
|
||||
90,
|
||||
80,
|
||||
70,
|
||||
60,
|
||||
50,
|
||||
40,
|
||||
30,
|
||||
20,
|
||||
19,
|
||||
18,
|
||||
17,
|
||||
16,
|
||||
15,
|
||||
10,
|
||||
9,
|
||||
8,
|
||||
7,
|
||||
6,
|
||||
5,
|
||||
4,
|
||||
3,
|
||||
2,
|
||||
1
|
||||
],
|
||||
values: [
|
||||
'י׳',
|
||||
'ט׳',
|
||||
'ח׳',
|
||||
'ז׳',
|
||||
'ו׳',
|
||||
'ה׳',
|
||||
'ד׳',
|
||||
'ג׳',
|
||||
'ב׳',
|
||||
'א׳',
|
||||
'ת',
|
||||
'ש',
|
||||
'ר',
|
||||
'ק',
|
||||
'צ',
|
||||
'פ',
|
||||
'ע',
|
||||
'ס',
|
||||
'נ',
|
||||
'מ',
|
||||
'ל',
|
||||
'כ',
|
||||
'יט',
|
||||
'יח',
|
||||
'יז',
|
||||
'טז',
|
||||
'טו',
|
||||
'י',
|
||||
'ט',
|
||||
'ח',
|
||||
'ז',
|
||||
'ו',
|
||||
'ה',
|
||||
'ד',
|
||||
'ג',
|
||||
'ב',
|
||||
'א'
|
||||
]
|
||||
};
|
||||
|
||||
const GEORGIAN = {
|
||||
integers: [
|
||||
10000,
|
||||
9000,
|
||||
8000,
|
||||
7000,
|
||||
6000,
|
||||
5000,
|
||||
4000,
|
||||
3000,
|
||||
2000,
|
||||
1000,
|
||||
900,
|
||||
800,
|
||||
700,
|
||||
600,
|
||||
500,
|
||||
400,
|
||||
300,
|
||||
200,
|
||||
100,
|
||||
90,
|
||||
80,
|
||||
70,
|
||||
60,
|
||||
50,
|
||||
40,
|
||||
30,
|
||||
20,
|
||||
10,
|
||||
9,
|
||||
8,
|
||||
7,
|
||||
6,
|
||||
5,
|
||||
4,
|
||||
3,
|
||||
2,
|
||||
1
|
||||
],
|
||||
values: [
|
||||
'ჵ',
|
||||
'ჰ',
|
||||
'ჯ',
|
||||
'ჴ',
|
||||
'ხ',
|
||||
'ჭ',
|
||||
'წ',
|
||||
'ძ',
|
||||
'ც',
|
||||
'ჩ',
|
||||
'შ',
|
||||
'ყ',
|
||||
'ღ',
|
||||
'ქ',
|
||||
'ფ',
|
||||
'ჳ',
|
||||
'ტ',
|
||||
'ს',
|
||||
'რ',
|
||||
'ჟ',
|
||||
'პ',
|
||||
'ო',
|
||||
'ჲ',
|
||||
'ნ',
|
||||
'მ',
|
||||
'ლ',
|
||||
'კ',
|
||||
'ი',
|
||||
'თ',
|
||||
'ჱ',
|
||||
'ზ',
|
||||
'ვ',
|
||||
'ე',
|
||||
'დ',
|
||||
'გ',
|
||||
'ბ',
|
||||
'ა'
|
||||
]
|
||||
};
|
||||
|
||||
const createAdditiveCounter = (
|
||||
value: number,
|
||||
min: number,
|
||||
max: number,
|
||||
symbols,
|
||||
fallback: ListStyleType,
|
||||
suffix: string = '. '
|
||||
) => {
|
||||
if (value < min || value > max) {
|
||||
return createCounterText(value, fallback);
|
||||
}
|
||||
|
||||
return (
|
||||
symbols.integers.reduce((string, integer, index) => {
|
||||
while (value >= integer) {
|
||||
value -= integer;
|
||||
string += symbols.values[index];
|
||||
}
|
||||
return string;
|
||||
}, '') + suffix
|
||||
);
|
||||
};
|
||||
|
||||
const createCounterStyleWithSymbolResolver = (
|
||||
value: number,
|
||||
codePointRangeLength: number,
|
||||
isNumeric: boolean,
|
||||
resolver
|
||||
): string => {
|
||||
let string = '';
|
||||
|
||||
do {
|
||||
if (!isNumeric) {
|
||||
value--;
|
||||
}
|
||||
string = resolver(value) + string;
|
||||
value /= codePointRangeLength;
|
||||
} while (value * codePointRangeLength >= codePointRangeLength);
|
||||
|
||||
return string;
|
||||
};
|
||||
|
||||
const createCounterStyleFromRange = (
|
||||
value: number,
|
||||
codePointRangeStart: number,
|
||||
codePointRangeEnd: number,
|
||||
isNumeric: boolean,
|
||||
suffix: string = '. '
|
||||
): string => {
|
||||
const codePointRangeLength = codePointRangeEnd - codePointRangeStart + 1;
|
||||
|
||||
return (
|
||||
(value < 0 ? '-' : '') +
|
||||
(createCounterStyleWithSymbolResolver(
|
||||
Math.abs(value),
|
||||
codePointRangeLength,
|
||||
isNumeric,
|
||||
codePoint =>
|
||||
fromCodePoint(Math.floor(codePoint % codePointRangeLength) + codePointRangeStart)
|
||||
) +
|
||||
suffix)
|
||||
);
|
||||
};
|
||||
|
||||
const createCounterStyleFromSymbols = (
|
||||
value: number,
|
||||
symbols: string,
|
||||
suffix: string = '. '
|
||||
): string => {
|
||||
const codePointRangeLength = symbols.length;
|
||||
return (
|
||||
createCounterStyleWithSymbolResolver(
|
||||
Math.abs(value),
|
||||
codePointRangeLength,
|
||||
false,
|
||||
codePoint => symbols[Math.floor(codePoint % codePointRangeLength)]
|
||||
) + suffix
|
||||
);
|
||||
};
|
||||
|
||||
const CJK_ZEROS = 1 << 0;
|
||||
const CJK_TEN_COEFFICIENTS = 1 << 1;
|
||||
const CJK_TEN_HIGH_COEFFICIENTS = 1 << 2;
|
||||
const CJK_HUNDRED_COEFFICIENTS = 1 << 3;
|
||||
|
||||
const createCJKCounter = (
|
||||
value: number,
|
||||
numbers: string,
|
||||
multipliers: string,
|
||||
negativeSign: string,
|
||||
suffix: string,
|
||||
flags: number
|
||||
): string => {
|
||||
if (value < -9999 || value > 9999) {
|
||||
return createCounterText(value, LIST_STYLE_TYPE.CJK_DECIMAL);
|
||||
}
|
||||
let tmp = Math.abs(value);
|
||||
let string = suffix;
|
||||
|
||||
if (tmp === 0) {
|
||||
return numbers[0] + string;
|
||||
}
|
||||
|
||||
for (let digit = 0; tmp > 0 && digit <= 4; digit++) {
|
||||
let coefficient = tmp % 10;
|
||||
|
||||
if (coefficient === 0 && contains(flags, CJK_ZEROS) && string !== '') {
|
||||
string = numbers[coefficient] + string;
|
||||
} else if (
|
||||
coefficient > 1 ||
|
||||
(coefficient === 1 && digit === 0) ||
|
||||
(coefficient === 1 && digit === 1 && contains(flags, CJK_TEN_COEFFICIENTS)) ||
|
||||
(coefficient === 1 &&
|
||||
digit === 1 &&
|
||||
contains(flags, CJK_TEN_HIGH_COEFFICIENTS) &&
|
||||
value > 100) ||
|
||||
(coefficient === 1 && digit > 1 && contains(flags, CJK_HUNDRED_COEFFICIENTS))
|
||||
) {
|
||||
string = numbers[coefficient] + (digit > 0 ? multipliers[digit - 1] : '') + string;
|
||||
} else if (coefficient === 1 && digit > 0) {
|
||||
string = multipliers[digit - 1] + string;
|
||||
}
|
||||
tmp = Math.floor(tmp / 10);
|
||||
}
|
||||
|
||||
return (value < 0 ? negativeSign : '') + string;
|
||||
};
|
||||
|
||||
const CHINESE_INFORMAL_MULTIPLIERS = '十百千萬';
|
||||
const CHINESE_FORMAL_MULTIPLIERS = '拾佰仟萬';
|
||||
const JAPANESE_NEGATIVE = 'マイナス';
|
||||
const KOREAN_NEGATIVE = '마이너스 ';
|
||||
|
||||
const createCounterText = (value: number, type: ListStyleType): string => {
|
||||
switch (type) {
|
||||
case LIST_STYLE_TYPE.DISC:
|
||||
return '•';
|
||||
case LIST_STYLE_TYPE.CIRCLE:
|
||||
return '◦';
|
||||
case LIST_STYLE_TYPE.SQUARE:
|
||||
return '◾';
|
||||
case LIST_STYLE_TYPE.DECIMAL_LEADING_ZERO:
|
||||
const string = createCounterStyleFromRange(value, 48, 57, true);
|
||||
return string.length < 4 ? `0${string}` : string;
|
||||
case LIST_STYLE_TYPE.CJK_DECIMAL:
|
||||
return createCounterStyleFromSymbols(value, '〇一二三四五六七八九', '、');
|
||||
case LIST_STYLE_TYPE.LOWER_ROMAN:
|
||||
return createAdditiveCounter(
|
||||
value,
|
||||
1,
|
||||
3999,
|
||||
ROMAN_UPPER,
|
||||
LIST_STYLE_TYPE.DECIMAL
|
||||
).toLowerCase();
|
||||
case LIST_STYLE_TYPE.UPPER_ROMAN:
|
||||
return createAdditiveCounter(value, 1, 3999, ROMAN_UPPER, LIST_STYLE_TYPE.DECIMAL);
|
||||
case LIST_STYLE_TYPE.LOWER_GREEK:
|
||||
return createCounterStyleFromRange(value, 945, 969, false);
|
||||
case LIST_STYLE_TYPE.LOWER_ALPHA:
|
||||
return createCounterStyleFromRange(value, 97, 122, false);
|
||||
case LIST_STYLE_TYPE.UPPER_ALPHA:
|
||||
return createCounterStyleFromRange(value, 65, 90, false);
|
||||
case LIST_STYLE_TYPE.ARABIC_INDIC:
|
||||
return createCounterStyleFromRange(value, 1632, 1641, true);
|
||||
case LIST_STYLE_TYPE.ARMENIAN:
|
||||
case LIST_STYLE_TYPE.UPPER_ARMENIAN:
|
||||
return createAdditiveCounter(value, 1, 9999, ARMENIAN, LIST_STYLE_TYPE.DECIMAL);
|
||||
case LIST_STYLE_TYPE.LOWER_ARMENIAN:
|
||||
return createAdditiveCounter(
|
||||
value,
|
||||
1,
|
||||
9999,
|
||||
ARMENIAN,
|
||||
LIST_STYLE_TYPE.DECIMAL
|
||||
).toLowerCase();
|
||||
case LIST_STYLE_TYPE.BENGALI:
|
||||
return createCounterStyleFromRange(value, 2534, 2543, true);
|
||||
case LIST_STYLE_TYPE.CAMBODIAN:
|
||||
case LIST_STYLE_TYPE.KHMER:
|
||||
return createCounterStyleFromRange(value, 6112, 6121, true);
|
||||
case LIST_STYLE_TYPE.CJK_EARTHLY_BRANCH:
|
||||
return createCounterStyleFromSymbols(value, '子丑寅卯辰巳午未申酉戌亥', '、');
|
||||
case LIST_STYLE_TYPE.CJK_HEAVENLY_STEM:
|
||||
return createCounterStyleFromSymbols(value, '甲乙丙丁戊己庚辛壬癸', '、');
|
||||
case LIST_STYLE_TYPE.CJK_IDEOGRAPHIC:
|
||||
case LIST_STYLE_TYPE.TRAD_CHINESE_INFORMAL:
|
||||
return createCJKCounter(
|
||||
value,
|
||||
'零一二三四五六七八九',
|
||||
CHINESE_INFORMAL_MULTIPLIERS,
|
||||
'負',
|
||||
'、',
|
||||
CJK_TEN_COEFFICIENTS | CJK_TEN_HIGH_COEFFICIENTS | CJK_HUNDRED_COEFFICIENTS
|
||||
);
|
||||
case LIST_STYLE_TYPE.TRAD_CHINESE_FORMAL:
|
||||
return createCJKCounter(
|
||||
value,
|
||||
'零壹貳參肆伍陸柒捌玖',
|
||||
CHINESE_FORMAL_MULTIPLIERS,
|
||||
'負',
|
||||
'、',
|
||||
CJK_ZEROS |
|
||||
CJK_TEN_COEFFICIENTS |
|
||||
CJK_TEN_HIGH_COEFFICIENTS |
|
||||
CJK_HUNDRED_COEFFICIENTS
|
||||
);
|
||||
case LIST_STYLE_TYPE.SIMP_CHINESE_INFORMAL:
|
||||
return createCJKCounter(
|
||||
value,
|
||||
'零一二三四五六七八九',
|
||||
CHINESE_INFORMAL_MULTIPLIERS,
|
||||
'负',
|
||||
'、',
|
||||
CJK_TEN_COEFFICIENTS | CJK_TEN_HIGH_COEFFICIENTS | CJK_HUNDRED_COEFFICIENTS
|
||||
);
|
||||
case LIST_STYLE_TYPE.SIMP_CHINESE_FORMAL:
|
||||
return createCJKCounter(
|
||||
value,
|
||||
'零壹贰叁肆伍陆柒捌玖',
|
||||
CHINESE_FORMAL_MULTIPLIERS,
|
||||
'负',
|
||||
'、',
|
||||
CJK_ZEROS |
|
||||
CJK_TEN_COEFFICIENTS |
|
||||
CJK_TEN_HIGH_COEFFICIENTS |
|
||||
CJK_HUNDRED_COEFFICIENTS
|
||||
);
|
||||
case LIST_STYLE_TYPE.JAPANESE_INFORMAL:
|
||||
return createCJKCounter(value, '〇一二三四五六七八九', '十百千万', JAPANESE_NEGATIVE, '、', 0);
|
||||
case LIST_STYLE_TYPE.JAPANESE_FORMAL:
|
||||
return createCJKCounter(
|
||||
value,
|
||||
'零壱弐参四伍六七八九',
|
||||
'拾百千万',
|
||||
JAPANESE_NEGATIVE,
|
||||
'、',
|
||||
CJK_ZEROS | CJK_TEN_COEFFICIENTS | CJK_TEN_HIGH_COEFFICIENTS
|
||||
);
|
||||
case LIST_STYLE_TYPE.KOREAN_HANGUL_FORMAL:
|
||||
return createCJKCounter(
|
||||
value,
|
||||
'영일이삼사오육칠팔구',
|
||||
'십백천만',
|
||||
KOREAN_NEGATIVE,
|
||||
', ',
|
||||
CJK_ZEROS | CJK_TEN_COEFFICIENTS | CJK_TEN_HIGH_COEFFICIENTS
|
||||
);
|
||||
case LIST_STYLE_TYPE.KOREAN_HANJA_INFORMAL:
|
||||
return createCJKCounter(value, '零一二三四五六七八九', '十百千萬', KOREAN_NEGATIVE, ', ', 0);
|
||||
case LIST_STYLE_TYPE.KOREAN_HANJA_FORMAL:
|
||||
return createCJKCounter(
|
||||
value,
|
||||
'零壹貳參四五六七八九',
|
||||
'拾百千',
|
||||
KOREAN_NEGATIVE,
|
||||
', ',
|
||||
CJK_ZEROS | CJK_TEN_COEFFICIENTS | CJK_TEN_HIGH_COEFFICIENTS
|
||||
);
|
||||
case LIST_STYLE_TYPE.DEVANAGARI:
|
||||
return createCounterStyleFromRange(value, 0x966, 0x96f, true);
|
||||
case LIST_STYLE_TYPE.GEORGIAN:
|
||||
return createAdditiveCounter(value, 1, 19999, GEORGIAN, LIST_STYLE_TYPE.DECIMAL);
|
||||
case LIST_STYLE_TYPE.GUJARATI:
|
||||
return createCounterStyleFromRange(value, 0xae6, 0xaef, true);
|
||||
case LIST_STYLE_TYPE.GURMUKHI:
|
||||
return createCounterStyleFromRange(value, 0xa66, 0xa6f, true);
|
||||
case LIST_STYLE_TYPE.HEBREW:
|
||||
return createAdditiveCounter(value, 1, 10999, HEBREW, LIST_STYLE_TYPE.DECIMAL);
|
||||
case LIST_STYLE_TYPE.HIRAGANA:
|
||||
return createCounterStyleFromSymbols(
|
||||
value,
|
||||
'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわゐゑをん'
|
||||
);
|
||||
case LIST_STYLE_TYPE.HIRAGANA_IROHA:
|
||||
return createCounterStyleFromSymbols(
|
||||
value,
|
||||
'いろはにほへとちりぬるをわかよたれそつねならむうゐのおくやまけふこえてあさきゆめみしゑひもせす'
|
||||
);
|
||||
case LIST_STYLE_TYPE.KANNADA:
|
||||
return createCounterStyleFromRange(value, 0xce6, 0xcef, true);
|
||||
case LIST_STYLE_TYPE.KATAKANA:
|
||||
return createCounterStyleFromSymbols(
|
||||
value,
|
||||
'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヰヱヲン',
|
||||
'、'
|
||||
);
|
||||
case LIST_STYLE_TYPE.KATAKANA_IROHA:
|
||||
return createCounterStyleFromSymbols(
|
||||
value,
|
||||
'イロハニホヘトチリヌルヲワカヨタレソツネナラムウヰノオクヤマケフコエテアサキユメミシヱヒモセス',
|
||||
'、'
|
||||
);
|
||||
case LIST_STYLE_TYPE.LAO:
|
||||
return createCounterStyleFromRange(value, 0xed0, 0xed9, true);
|
||||
case LIST_STYLE_TYPE.MONGOLIAN:
|
||||
return createCounterStyleFromRange(value, 0x1810, 0x1819, true);
|
||||
case LIST_STYLE_TYPE.MYANMAR:
|
||||
return createCounterStyleFromRange(value, 0x1040, 0x1049, true);
|
||||
case LIST_STYLE_TYPE.ORIYA:
|
||||
return createCounterStyleFromRange(value, 0xb66, 0xb6f, true);
|
||||
case LIST_STYLE_TYPE.PERSIAN:
|
||||
return createCounterStyleFromRange(value, 0x6f0, 0x6f9, true);
|
||||
case LIST_STYLE_TYPE.TAMIL:
|
||||
return createCounterStyleFromRange(value, 0xbe6, 0xbef, true);
|
||||
case LIST_STYLE_TYPE.TELUGU:
|
||||
return createCounterStyleFromRange(value, 0xc66, 0xc6f, true);
|
||||
case LIST_STYLE_TYPE.THAI:
|
||||
return createCounterStyleFromRange(value, 0xe50, 0xe59, true);
|
||||
case LIST_STYLE_TYPE.TIBETAN:
|
||||
return createCounterStyleFromRange(value, 0xf20, 0xf29, true);
|
||||
case LIST_STYLE_TYPE.DECIMAL:
|
||||
default:
|
||||
return createCounterStyleFromRange(value, 48, 57, true);
|
||||
}
|
||||
};
|
@ -7,6 +7,8 @@ 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 {ListStyle} from './parsing/listStyle';
|
||||
import type {Margin} from './parsing/margin';
|
||||
import type {Overflow} from './parsing/overflow';
|
||||
import type {Padding} from './parsing/padding';
|
||||
import type {Position} from './parsing/position';
|
||||
@ -32,6 +34,8 @@ import {parseDisplay, DISPLAY} from './parsing/display';
|
||||
import {parseCSSFloat, FLOAT} from './parsing/float';
|
||||
import {parseFont} from './parsing/font';
|
||||
import {parseLetterSpacing} from './parsing/letterSpacing';
|
||||
import {parseListStyle} from './parsing/listStyle';
|
||||
import {parseMargin} from './parsing/margin';
|
||||
import {parseOverflow, OVERFLOW} from './parsing/overflow';
|
||||
import {parsePadding} from './parsing/padding';
|
||||
import {parsePosition, POSITION} from './parsing/position';
|
||||
@ -50,6 +54,7 @@ import {
|
||||
getInputBorderRadius,
|
||||
reformatInputBounds
|
||||
} from './Input';
|
||||
import {getListOwner} from './ListItem';
|
||||
|
||||
type StyleDeclaration = {
|
||||
background: Background,
|
||||
@ -60,6 +65,8 @@ type StyleDeclaration = {
|
||||
float: Float,
|
||||
font: Font,
|
||||
letterSpacing: number,
|
||||
listStyle: ListStyle | null,
|
||||
margin: Margin,
|
||||
opacity: number,
|
||||
overflow: Overflow,
|
||||
padding: Padding,
|
||||
@ -79,10 +86,14 @@ export default class NodeContainer {
|
||||
parent: ?NodeContainer;
|
||||
style: StyleDeclaration;
|
||||
childNodes: Array<TextContainer | Path>;
|
||||
listItems: Array<NodeContainer>;
|
||||
listIndex: ?number;
|
||||
listStart: ?number;
|
||||
bounds: Bounds;
|
||||
curvedBounds: BoundCurves;
|
||||
image: ?string;
|
||||
index: number;
|
||||
tagName: string;
|
||||
|
||||
constructor(
|
||||
node: HTMLElement | SVGSVGElement,
|
||||
@ -91,8 +102,13 @@ export default class NodeContainer {
|
||||
index: number
|
||||
) {
|
||||
this.parent = parent;
|
||||
this.tagName = node.tagName;
|
||||
this.index = index;
|
||||
this.childNodes = [];
|
||||
this.listItems = [];
|
||||
if (typeof node.start === 'number') {
|
||||
this.listStart = node.start;
|
||||
}
|
||||
const defaultView = node.ownerDocument.defaultView;
|
||||
const scrollX = defaultView.pageXOffset;
|
||||
const scrollY = defaultView.pageYOffset;
|
||||
@ -117,6 +133,8 @@ export default class NodeContainer {
|
||||
float: parseCSSFloat(style.float),
|
||||
font: parseFont(style),
|
||||
letterSpacing: parseLetterSpacing(style.letterSpacing),
|
||||
listStyle: display === DISPLAY.LIST_ITEM ? parseListStyle(style) : null,
|
||||
margin: parseMargin(style),
|
||||
opacity: parseFloat(style.opacity),
|
||||
overflow:
|
||||
INPUT_TAGS.indexOf(node.tagName) === -1
|
||||
@ -137,6 +155,20 @@ export default class NodeContainer {
|
||||
node.style.transform = 'matrix(1,0,0,1,0,0)';
|
||||
}
|
||||
|
||||
if (display === DISPLAY.LIST_ITEM) {
|
||||
const listOwner = getListOwner(this);
|
||||
if (listOwner) {
|
||||
const listIndex = listOwner.listItems.length;
|
||||
listOwner.listItems.push(this);
|
||||
this.listIndex =
|
||||
node.hasAttribute('value') && typeof node.value === 'number'
|
||||
? node.value
|
||||
: listIndex === 0
|
||||
? typeof listOwner.listStart === 'number' ? listOwner.listStart : 1
|
||||
: listOwner.listItems[listIndex - 1].listIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO move bound retrieval for all nodes to a later stage?
|
||||
if (node.tagName === 'IMG') {
|
||||
node.addEventListener('load', () => {
|
||||
@ -170,8 +202,7 @@ export default class NodeContainer {
|
||||
}
|
||||
getClipPaths(): Array<Path> {
|
||||
const parentClips = this.parent ? this.parent.getClipPaths() : [];
|
||||
const isClipped =
|
||||
this.style.overflow === OVERFLOW.HIDDEN || this.style.overflow === OVERFLOW.SCROLL;
|
||||
const isClipped = this.style.overflow !== OVERFLOW.VISIBLE;
|
||||
|
||||
return isClipped
|
||||
? parentClips.concat([calculatePaddingBoxPath(this.curvedBounds)])
|
||||
|
@ -6,6 +6,7 @@ import StackingContext from './StackingContext';
|
||||
import NodeContainer from './NodeContainer';
|
||||
import TextContainer from './TextContainer';
|
||||
import {inlineInputElement, inlineTextAreaElement, inlineSelectElement} from './Input';
|
||||
import {inlineListItemElement} from './ListItem';
|
||||
|
||||
export const NodeParser = (
|
||||
node: HTMLElement,
|
||||
@ -71,6 +72,11 @@ const parseNodeTree = (
|
||||
} else if (childNode.tagName === 'SELECT') {
|
||||
// $FlowFixMe
|
||||
inlineSelectElement(childNode, container);
|
||||
} else if (
|
||||
container.style.listStyle &&
|
||||
container.style.listStyle.listStyleType !== 'none'
|
||||
) {
|
||||
inlineListItemElement(childNode, container, resourceLoader);
|
||||
}
|
||||
|
||||
const SHOULD_TRAVERSE_CHILDREN = childNode.tagName !== 'TEXTAREA';
|
||||
|
32
src/Unicode.js
Normal file
32
src/Unicode.js
Normal file
@ -0,0 +1,32 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
export const fromCodePoint = (...codePoints: Array<number>): string => {
|
||||
if (String.fromCodePoint) {
|
||||
return String.fromCodePoint(...codePoints);
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
@ -97,16 +97,6 @@ export const renderElement = (
|
||||
}
|
||||
|
||||
return resourceLoader.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`);
|
||||
@ -133,7 +123,18 @@ export const renderElement = (
|
||||
);
|
||||
} else {
|
||||
const renderer = new Renderer(options.target, renderOptions);
|
||||
return renderer.render(stack);
|
||||
const canvas = renderer.render(stack);
|
||||
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`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
});
|
||||
})
|
||||
|
@ -20,6 +20,7 @@ export type Options = {
|
||||
removeContainer: ?boolean,
|
||||
scale: number,
|
||||
target: RenderTarget<*>,
|
||||
useCORS: boolean,
|
||||
width: number,
|
||||
height: number,
|
||||
x: number,
|
||||
@ -72,6 +73,7 @@ const html2canvas = (element: HTMLElement, conf: ?Options): Promise<*> => {
|
||||
foreignObjectRendering: false,
|
||||
scale: defaultView.devicePixelRatio || 1,
|
||||
target: new CanvasRenderer(config.canvas),
|
||||
useCORS: false,
|
||||
x: left,
|
||||
y: top,
|
||||
width: Math.ceil(width),
|
||||
|
209
src/parsing/listStyle.js
Normal file
209
src/parsing/listStyle.js
Normal file
@ -0,0 +1,209 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
import type {BackgroundSource} from './background';
|
||||
import {parseBackgroundImage} from './background';
|
||||
|
||||
export type ListStyle = {
|
||||
listStyleType: ListStyleType,
|
||||
listStyleImage: ?BackgroundSource,
|
||||
listStylePosition: ListStylePosition
|
||||
};
|
||||
|
||||
export const LIST_STYLE_POSITION = {
|
||||
INSIDE: 0,
|
||||
OUTSIDE: 1
|
||||
};
|
||||
|
||||
export const LIST_STYLE_TYPE = {
|
||||
NONE: -1,
|
||||
DISC: 0,
|
||||
CIRCLE: 1,
|
||||
SQUARE: 2,
|
||||
DECIMAL: 3,
|
||||
CJK_DECIMAL: 4,
|
||||
DECIMAL_LEADING_ZERO: 5,
|
||||
LOWER_ROMAN: 6,
|
||||
UPPER_ROMAN: 7,
|
||||
LOWER_GREEK: 8,
|
||||
LOWER_ALPHA: 9,
|
||||
UPPER_ALPHA: 10,
|
||||
ARABIC_INDIC: 11,
|
||||
ARMENIAN: 12,
|
||||
BENGALI: 13,
|
||||
CAMBODIAN: 14,
|
||||
CJK_EARTHLY_BRANCH: 15,
|
||||
CJK_HEAVENLY_STEM: 16,
|
||||
CJK_IDEOGRAPHIC: 17,
|
||||
DEVANAGARI: 18,
|
||||
ETHIOPIC_NUMERIC: 19,
|
||||
GEORGIAN: 20,
|
||||
GUJARATI: 21,
|
||||
GURMUKHI: 22,
|
||||
HEBREW: 22,
|
||||
HIRAGANA: 23,
|
||||
HIRAGANA_IROHA: 24,
|
||||
JAPANESE_FORMAL: 25,
|
||||
JAPANESE_INFORMAL: 26,
|
||||
KANNADA: 27,
|
||||
KATAKANA: 28,
|
||||
KATAKANA_IROHA: 29,
|
||||
KHMER: 30,
|
||||
KOREAN_HANGUL_FORMAL: 31,
|
||||
KOREAN_HANJA_FORMAL: 32,
|
||||
KOREAN_HANJA_INFORMAL: 33,
|
||||
LAO: 34,
|
||||
LOWER_ARMENIAN: 35,
|
||||
MALAYALAM: 36,
|
||||
MONGOLIAN: 37,
|
||||
MYANMAR: 38,
|
||||
ORIYA: 39,
|
||||
PERSIAN: 40,
|
||||
SIMP_CHINESE_FORMAL: 41,
|
||||
SIMP_CHINESE_INFORMAL: 42,
|
||||
TAMIL: 43,
|
||||
TELUGU: 44,
|
||||
THAI: 45,
|
||||
TIBETAN: 46,
|
||||
TRAD_CHINESE_FORMAL: 47,
|
||||
TRAD_CHINESE_INFORMAL: 48,
|
||||
UPPER_ARMENIAN: 49,
|
||||
DISCLOSURE_OPEN: 50,
|
||||
DISCLOSURE_CLOSED: 51
|
||||
};
|
||||
|
||||
export type ListStylePosition = $Values<typeof LIST_STYLE_POSITION>;
|
||||
export type ListStyleType = $Values<typeof LIST_STYLE_TYPE>;
|
||||
|
||||
const parseListStyleType = (type: string): ListStyleType => {
|
||||
switch (type) {
|
||||
case 'disc':
|
||||
return LIST_STYLE_TYPE.DISC;
|
||||
case 'circle':
|
||||
return LIST_STYLE_TYPE.CIRCLE;
|
||||
case 'square':
|
||||
return LIST_STYLE_TYPE.SQUARE;
|
||||
case 'decimal':
|
||||
return LIST_STYLE_TYPE.DECIMAL;
|
||||
case 'cjk-decimal':
|
||||
return LIST_STYLE_TYPE.CJK_DECIMAL;
|
||||
case 'decimal-leading-zero':
|
||||
return LIST_STYLE_TYPE.DECIMAL_LEADING_ZERO;
|
||||
case 'lower-roman':
|
||||
return LIST_STYLE_TYPE.LOWER_ROMAN;
|
||||
case 'upper-roman':
|
||||
return LIST_STYLE_TYPE.UPPER_ROMAN;
|
||||
case 'lower-greek':
|
||||
return LIST_STYLE_TYPE.LOWER_GREEK;
|
||||
case 'lower-alpha':
|
||||
return LIST_STYLE_TYPE.LOWER_ALPHA;
|
||||
case 'upper-alpha':
|
||||
return LIST_STYLE_TYPE.UPPER_ALPHA;
|
||||
case 'arabic-indic':
|
||||
return LIST_STYLE_TYPE.ARABIC_INDIC;
|
||||
case 'armenian':
|
||||
return LIST_STYLE_TYPE.ARMENIAN;
|
||||
case 'bengali':
|
||||
return LIST_STYLE_TYPE.BENGALI;
|
||||
case 'cambodian':
|
||||
return LIST_STYLE_TYPE.CAMBODIAN;
|
||||
case 'cjk-earthly-branch':
|
||||
return LIST_STYLE_TYPE.CJK_EARTHLY_BRANCH;
|
||||
case 'cjk-heavenly-stem':
|
||||
return LIST_STYLE_TYPE.CJK_HEAVENLY_STEM;
|
||||
case 'cjk-ideographic':
|
||||
return LIST_STYLE_TYPE.CJK_IDEOGRAPHIC;
|
||||
case 'devanagari':
|
||||
return LIST_STYLE_TYPE.DEVANAGARI;
|
||||
case 'ethiopic-numeric':
|
||||
return LIST_STYLE_TYPE.ETHIOPIC_NUMERIC;
|
||||
case 'georgian':
|
||||
return LIST_STYLE_TYPE.GEORGIAN;
|
||||
case 'gujarati':
|
||||
return LIST_STYLE_TYPE.GUJARATI;
|
||||
case 'gurmukhi':
|
||||
return LIST_STYLE_TYPE.GURMUKHI;
|
||||
case 'hebrew':
|
||||
return LIST_STYLE_TYPE.HEBREW;
|
||||
case 'hiragana':
|
||||
return LIST_STYLE_TYPE.HIRAGANA;
|
||||
case 'hiragana-iroha':
|
||||
return LIST_STYLE_TYPE.HIRAGANA_IROHA;
|
||||
case 'japanese-formal':
|
||||
return LIST_STYLE_TYPE.JAPANESE_FORMAL;
|
||||
case 'japanese-informal':
|
||||
return LIST_STYLE_TYPE.JAPANESE_INFORMAL;
|
||||
case 'kannada':
|
||||
return LIST_STYLE_TYPE.KANNADA;
|
||||
case 'katakana':
|
||||
return LIST_STYLE_TYPE.KATAKANA;
|
||||
case 'katakana-iroha':
|
||||
return LIST_STYLE_TYPE.KATAKANA_IROHA;
|
||||
case 'khmer':
|
||||
return LIST_STYLE_TYPE.KHMER;
|
||||
case 'korean-hangul-formal':
|
||||
return LIST_STYLE_TYPE.KOREAN_HANGUL_FORMAL;
|
||||
case 'korean-hanja-formal':
|
||||
return LIST_STYLE_TYPE.KOREAN_HANJA_FORMAL;
|
||||
case 'korean-hanja-informal':
|
||||
return LIST_STYLE_TYPE.KOREAN_HANJA_INFORMAL;
|
||||
case 'lao':
|
||||
return LIST_STYLE_TYPE.LAO;
|
||||
case 'lower-armenian':
|
||||
return LIST_STYLE_TYPE.LOWER_ARMENIAN;
|
||||
case 'malayalam':
|
||||
return LIST_STYLE_TYPE.MALAYALAM;
|
||||
case 'mongolian':
|
||||
return LIST_STYLE_TYPE.MONGOLIAN;
|
||||
case 'myanmar':
|
||||
return LIST_STYLE_TYPE.MYANMAR;
|
||||
case 'oriya':
|
||||
return LIST_STYLE_TYPE.ORIYA;
|
||||
case 'persian':
|
||||
return LIST_STYLE_TYPE.PERSIAN;
|
||||
case 'simp-chinese-formal':
|
||||
return LIST_STYLE_TYPE.SIMP_CHINESE_FORMAL;
|
||||
case 'simp-chinese-informal':
|
||||
return LIST_STYLE_TYPE.SIMP_CHINESE_INFORMAL;
|
||||
case 'tamil':
|
||||
return LIST_STYLE_TYPE.TAMIL;
|
||||
case 'telugu':
|
||||
return LIST_STYLE_TYPE.TELUGU;
|
||||
case 'thai':
|
||||
return LIST_STYLE_TYPE.THAI;
|
||||
case 'tibetan':
|
||||
return LIST_STYLE_TYPE.TIBETAN;
|
||||
case 'trad-chinese-formal':
|
||||
return LIST_STYLE_TYPE.TRAD_CHINESE_FORMAL;
|
||||
case 'trad-chinese-informal':
|
||||
return LIST_STYLE_TYPE.TRAD_CHINESE_INFORMAL;
|
||||
case 'upper-armenian':
|
||||
return LIST_STYLE_TYPE.UPPER_ARMENIAN;
|
||||
case 'disclosure-open':
|
||||
return LIST_STYLE_TYPE.DISCLOSURE_OPEN;
|
||||
case 'disclosure-closed':
|
||||
return LIST_STYLE_TYPE.DISCLOSURE_CLOSED;
|
||||
case 'none':
|
||||
default:
|
||||
return LIST_STYLE_TYPE.NONE;
|
||||
}
|
||||
};
|
||||
|
||||
export const parseListStyle = (style: CSSStyleDeclaration): ListStyle => {
|
||||
const listStyleImage = parseBackgroundImage(style.getPropertyValue('list-style-image'));
|
||||
return {
|
||||
listStyleType: parseListStyleType(style.getPropertyValue('list-style-type')),
|
||||
listStyleImage: listStyleImage.length ? listStyleImage[0] : null,
|
||||
listStylePosition: parseListStylePosition(style.getPropertyValue('list-style-position'))
|
||||
};
|
||||
};
|
||||
|
||||
const parseListStylePosition = (position: string): ListStylePosition => {
|
||||
switch (position) {
|
||||
case 'inside':
|
||||
return LIST_STYLE_POSITION.INSIDE;
|
||||
case 'outside':
|
||||
default:
|
||||
return LIST_STYLE_POSITION.OUTSIDE;
|
||||
}
|
||||
};
|
11
src/parsing/margin.js
Normal file
11
src/parsing/margin.js
Normal file
@ -0,0 +1,11 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
import Length from '../Length';
|
||||
|
||||
const SIDES = ['top', 'right', 'bottom', 'left'];
|
||||
|
||||
export type Margin = Array<Length>;
|
||||
|
||||
export const parseMargin = (style: CSSStyleDeclaration): Margin => {
|
||||
return SIDES.map(side => new Length(style.getPropertyValue(`margin-${side}`)));
|
||||
};
|
@ -250,7 +250,7 @@ export default class CanvasRenderer implements RenderTarget<HTMLCanvasElement> {
|
||||
const {baseline} = this.options.fontMetrics.getMetrics(font);
|
||||
this.rectangle(
|
||||
text.bounds.left,
|
||||
Math.round(text.bounds.top + baseline),
|
||||
Math.round(text.bounds.top + text.bounds.height - baseline),
|
||||
text.bounds.width,
|
||||
1,
|
||||
textDecorationColor
|
||||
|
102
tests/reftests/list/liststyle.html
Normal file
102
tests/reftests/list/liststyle.html
Normal file
@ -0,0 +1,102 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>List tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
|
||||
<style>
|
||||
li {
|
||||
margin: 10px 5%;
|
||||
}
|
||||
.list1 {
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
.list2 {
|
||||
list-style-image: url(../../assets/image.jpg);
|
||||
}
|
||||
|
||||
.list3 {
|
||||
list-style-image: linear-gradient(60deg, deeppink, aquamarine);
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
.list4 {
|
||||
}
|
||||
|
||||
.list5 {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
|
||||
.list6 {
|
||||
list-style-type: hiragana;
|
||||
}
|
||||
|
||||
.list7 {
|
||||
list-style-type: simp-chinese-informal;
|
||||
}
|
||||
|
||||
.list8 {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
|
||||
.list8 li {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.list9 {
|
||||
display: list-item;
|
||||
list-style-type: lower-alpha;
|
||||
margin: 10px;
|
||||
position: relative;
|
||||
left: 200px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<ul class="list1">
|
||||
<li>Alpha</li>
|
||||
<li>Beta</li>
|
||||
<li>Gamma</li>
|
||||
</ul>
|
||||
<ul class="list2">
|
||||
<li>Alpha</li>
|
||||
<li>Beta</li>
|
||||
<li>Gamma</li>
|
||||
</ul>
|
||||
<ul class="list3">
|
||||
<li>Alpha</li>
|
||||
<li>Beta</li>
|
||||
<li>Gamma</li>
|
||||
</ul>
|
||||
<ol class="list4" start="-1">
|
||||
<li>Alpha</li>
|
||||
<li>Beta</li>
|
||||
<li>Gamma</li>
|
||||
</ol>
|
||||
<ol class="list5">
|
||||
<li>Alpha</li>
|
||||
<li value="5">Beta</li>
|
||||
<li>Gamma</li>
|
||||
</ol>
|
||||
<ol class="list6">
|
||||
<li>Alpha</li>
|
||||
<li>Beta</li>
|
||||
<li>Gamma</li>
|
||||
</ol>
|
||||
<ol class="list7">
|
||||
<li>Alpha</li>
|
||||
<li>Beta</li>
|
||||
<li>Gamma</li>
|
||||
</ol>
|
||||
<ol class="list8">
|
||||
<li>Alpha</li>
|
||||
<li>Beta</li>
|
||||
<li>Gamma</li>
|
||||
</ol>
|
||||
<div class="list9">Alpha</div>
|
||||
<div class="list9">Beta</div>
|
||||
<div class="list9">Gamma</div>
|
||||
</body>
|
||||
</html>
|
@ -1,84 +1,122 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Overflow tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
<style>
|
||||
.small{
|
||||
font-size:14px;
|
||||
}
|
||||
<head>
|
||||
<title>Overflow tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
<style>
|
||||
.small{
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
.medium{
|
||||
font-size:18px;
|
||||
}
|
||||
.large{
|
||||
font-size:24px;
|
||||
}
|
||||
div{
|
||||
background: #ccc;
|
||||
border:6px solid black;
|
||||
width:300px;
|
||||
height:200px;
|
||||
margin: 0 0 60px 0;
|
||||
}
|
||||
h1 {
|
||||
margin:0;
|
||||
line-height: 40px;
|
||||
}
|
||||
body {
|
||||
font-family: Arial;
|
||||
line-height: 17px;
|
||||
font-size: 1em;
|
||||
}
|
||||
</style>
|
||||
.medium{
|
||||
font-size:18px;
|
||||
}
|
||||
.large{
|
||||
font-size:24px;
|
||||
}
|
||||
div{
|
||||
background: #ccc;
|
||||
border:6px solid black;
|
||||
width:300px;
|
||||
height:200px;
|
||||
margin: 0 0 60px 0;
|
||||
}
|
||||
h1 {
|
||||
margin:0;
|
||||
line-height: 40px;
|
||||
}
|
||||
body {
|
||||
font-family: Arial;
|
||||
line-height: 17px;
|
||||
font-size: 1em;
|
||||
}
|
||||
.cell {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
float: right;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
.cell p {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
<h1>Overflow: visible</h1>
|
||||
<div> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
</div>
|
||||
.visible {
|
||||
overflow: visible;
|
||||
}
|
||||
.hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
.scroll {
|
||||
overflow: scroll;
|
||||
}
|
||||
.auto {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>Overflow: hidden</h1>
|
||||
<div style="overflow:hidden;float: right; height: 100px; border-radius: 100%; border-color: transparent;">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
|
||||
</head>
|
||||
<body>
|
||||
<div class="cell">
|
||||
visible
|
||||
<p class="visible">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec luctus pretium facilisis. Praesent rutrum eget nisl in tristique. Sed tincidunt nisl et tellus vulputate, nec rhoncus orci pretium.</p>
|
||||
</div>
|
||||
<div class="cell">
|
||||
hidden
|
||||
<p class="hidden">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec luctus pretium facilisis. Praesent rutrum eget nisl in tristique. Sed tincidunt nisl et tellus vulputate, nec rhoncus orci pretium.</p>
|
||||
</div>
|
||||
<div class="cell">
|
||||
scroll
|
||||
<p class="scroll">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec luctus pretium facilisis. Praesent rutrum eget nisl in tristique. Sed tincidunt nisl et tellus vulputate, nec rhoncus orci pretium.</p>
|
||||
</div>
|
||||
<div class="cell">
|
||||
auto
|
||||
<p class="auto">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec luctus pretium facilisis. Praesent rutrum eget nisl in tristique. Sed tincidunt nisl et tellus vulputate, nec rhoncus orci pretium.</p>
|
||||
</div>
|
||||
<h1>Overflow: visible</h1>
|
||||
<div> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
</div>
|
||||
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
<h1>Overflow: hidden</h1>
|
||||
<div style="overflow:hidden;float: right; height: 100px; border-radius: 100%; border-color: transparent;">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
|
||||
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
|
||||
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
|
||||
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
|
||||
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow:hidden;">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow:hidden;">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
|
||||
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
|
||||
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
|
||||
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
|
||||
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow:scroll; height: 100px;" id="scroll">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow:scroll; height: 100px;" id="scroll">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
|
||||
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
|
||||
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
|
||||
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
|
||||
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
</div>
|
||||
<script>
|
||||
document.querySelector('#scroll').scrollTo(0, 200);
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</div>
|
||||
<script>
|
||||
document.querySelector('#scroll').scrollTo(0, 200);
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
6
www/package-lock.json
generated
6
www/package-lock.json
generated
@ -4895,9 +4895,9 @@
|
||||
"integrity": "sha1-nSLgyjKsyVs/RbjVtPb73AWv/VU="
|
||||
},
|
||||
"html2canvas": {
|
||||
"version": "1.0.0-alpha.3",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.0.0-alpha.3.tgz",
|
||||
"integrity": "sha1-0QeAa+W1kuRr0iI7B3KTgaBIGzo=",
|
||||
"version": "1.0.0-alpha.4",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.0.0-alpha.4.tgz",
|
||||
"integrity": "sha1-t1Q+6kUkAHitGEesk5CBpXUYCho=",
|
||||
"requires": {
|
||||
"punycode": "2.1.0"
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
"gatsby-source-filesystem": "^1.5.9",
|
||||
"gatsby-transformer-remark": "^1.7.23",
|
||||
"gzip-size": "^4.1.0",
|
||||
"html2canvas": "1.0.0-alpha.3",
|
||||
"html2canvas": "1.0.0-alpha.4",
|
||||
"mkdirp": "^0.5.1",
|
||||
"typography": "^0.16.6",
|
||||
"typography-theme-github": "^0.15.10"
|
||||
|
@ -66,7 +66,9 @@ export default class Example extends Component {
|
||||
position: 'fixed',
|
||||
zIndex: '1000',
|
||||
right: '-348.4px',
|
||||
bottom: '-327.2px'
|
||||
bottom: '-327.2px',
|
||||
visibility: this.state.open ? 'visible' : 'hidden',
|
||||
transition: 'visibility 0.3s cubic-bezier(0.42, 0, 0.58, 1)'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@ -107,6 +109,7 @@ export default class Example extends Component {
|
||||
cursor: 'pointer',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
borderRadius: '50%',
|
||||
visibility: 'visible',
|
||||
boxShadow:
|
||||
'0 2px 2px 0 rgba(0,0,0,0.14), 0 1px 5px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.2)'
|
||||
}}
|
||||
|
Reference in New Issue
Block a user