mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
fix: test for ios range line break error (#2635)
This commit is contained in:
parent
7a06d0c2c2
commit
f43f942fcd
@ -1,3 +1,5 @@
|
|||||||
|
import {fromCodePoint, toCodePoints} from 'css-line-break';
|
||||||
|
|
||||||
const testRangeBounds = (document: Document) => {
|
const testRangeBounds = (document: Document) => {
|
||||||
const TEST_HEIGHT = 123;
|
const TEST_HEIGHT = 123;
|
||||||
|
|
||||||
@ -22,6 +24,45 @@ const testRangeBounds = (document: Document) => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const testIOSLineBreak = (document: Document) => {
|
||||||
|
const testElement = document.createElement('boundtest');
|
||||||
|
testElement.style.width = '50px';
|
||||||
|
testElement.style.display = 'block';
|
||||||
|
testElement.style.fontSize = '12px';
|
||||||
|
testElement.style.letterSpacing = '0px';
|
||||||
|
testElement.style.wordSpacing = '0px';
|
||||||
|
document.body.appendChild(testElement);
|
||||||
|
const range = document.createRange();
|
||||||
|
|
||||||
|
testElement.innerHTML = typeof ''.repeat === 'function' ? '👨'.repeat(10) : '';
|
||||||
|
|
||||||
|
const node = testElement.firstChild as Text;
|
||||||
|
|
||||||
|
const textList = toCodePoints(node.data).map((i) => fromCodePoint(i));
|
||||||
|
let offset = 0;
|
||||||
|
let prev: DOMRect = {} as DOMRect;
|
||||||
|
|
||||||
|
// ios 13 does not handle range getBoundingClientRect line changes correctly #2177
|
||||||
|
const supports = textList.every((text, i) => {
|
||||||
|
range.setStart(node, offset);
|
||||||
|
range.setEnd(node, offset + text.length);
|
||||||
|
const rect = range.getBoundingClientRect();
|
||||||
|
|
||||||
|
offset += text.length;
|
||||||
|
const boundAhead = rect.x > prev.x || rect.y > prev.y;
|
||||||
|
|
||||||
|
prev = rect;
|
||||||
|
if (i === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return boundAhead;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.removeChild(testElement);
|
||||||
|
return supports;
|
||||||
|
};
|
||||||
|
|
||||||
const testCORS = (): boolean => typeof new Image().crossOrigin !== 'undefined';
|
const testCORS = (): boolean => typeof new Image().crossOrigin !== 'undefined';
|
||||||
|
|
||||||
const testResponseType = (): boolean => typeof new XMLHttpRequest().responseType === 'string';
|
const testResponseType = (): boolean => typeof new XMLHttpRequest().responseType === 'string';
|
||||||
@ -132,6 +173,12 @@ export const FEATURES = {
|
|||||||
Object.defineProperty(FEATURES, 'SUPPORT_RANGE_BOUNDS', {value});
|
Object.defineProperty(FEATURES, 'SUPPORT_RANGE_BOUNDS', {value});
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
|
get SUPPORT_WORD_BREAKING(): boolean {
|
||||||
|
'use strict';
|
||||||
|
const value = FEATURES.SUPPORT_RANGE_BOUNDS && testIOSLineBreak(document);
|
||||||
|
Object.defineProperty(FEATURES, 'SUPPORT_WORD_BREAKING', {value});
|
||||||
|
return value;
|
||||||
|
},
|
||||||
get SUPPORT_SVG_DRAWING(): boolean {
|
get SUPPORT_SVG_DRAWING(): boolean {
|
||||||
'use strict';
|
'use strict';
|
||||||
const value = testSVG(document);
|
const value = testSVG(document);
|
||||||
|
@ -15,6 +15,20 @@ export class Bounds {
|
|||||||
clientRect.height
|
clientRect.height
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fromDOMRectList(context: Context, domRectList: DOMRectList): Bounds {
|
||||||
|
const domRect = domRectList[0];
|
||||||
|
return domRect
|
||||||
|
? new Bounds(
|
||||||
|
domRect.x + context.windowBounds.left,
|
||||||
|
domRect.y + context.windowBounds.top,
|
||||||
|
domRect.width,
|
||||||
|
domRect.height
|
||||||
|
)
|
||||||
|
: Bounds.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EMPTY = new Bounds(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseBounds = (context: Context, node: Element): Bounds => {
|
export const parseBounds = (context: Context, node: Element): Bounds => {
|
||||||
|
@ -27,7 +27,16 @@ export const parseTextBounds = (
|
|||||||
textList.forEach((text) => {
|
textList.forEach((text) => {
|
||||||
if (styles.textDecorationLine.length || text.trim().length > 0) {
|
if (styles.textDecorationLine.length || text.trim().length > 0) {
|
||||||
if (FEATURES.SUPPORT_RANGE_BOUNDS) {
|
if (FEATURES.SUPPORT_RANGE_BOUNDS) {
|
||||||
textBounds.push(new TextBounds(text, getRangeBounds(context, node, offset, text.length)));
|
if (!FEATURES.SUPPORT_WORD_BREAKING) {
|
||||||
|
textBounds.push(
|
||||||
|
new TextBounds(
|
||||||
|
text,
|
||||||
|
Bounds.fromDOMRectList(context, createRange(node, offset, text.length).getClientRects())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
textBounds.push(new TextBounds(text, getRangeBounds(context, node, offset, text.length)));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const replacementNode = node.splitText(text.length);
|
const replacementNode = node.splitText(text.length);
|
||||||
textBounds.push(new TextBounds(text, getWrapperBounds(context, node)));
|
textBounds.push(new TextBounds(text, getWrapperBounds(context, node)));
|
||||||
@ -58,10 +67,10 @@ const getWrapperBounds = (context: Context, node: Text): Bounds => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Bounds(0, 0, 0, 0);
|
return Bounds.EMPTY;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRangeBounds = (context: Context, node: Text, offset: number, length: number): Bounds => {
|
const createRange = (node: Text, offset: number, length: number): Range => {
|
||||||
const ownerDocument = node.ownerDocument;
|
const ownerDocument = node.ownerDocument;
|
||||||
if (!ownerDocument) {
|
if (!ownerDocument) {
|
||||||
throw new Error('Node has no owner document');
|
throw new Error('Node has no owner document');
|
||||||
@ -69,7 +78,11 @@ const getRangeBounds = (context: Context, node: Text, offset: number, length: nu
|
|||||||
const range = ownerDocument.createRange();
|
const range = ownerDocument.createRange();
|
||||||
range.setStart(node, offset);
|
range.setStart(node, offset);
|
||||||
range.setEnd(node, offset + length);
|
range.setEnd(node, offset + length);
|
||||||
return Bounds.fromClientRect(context, range.getBoundingClientRect());
|
return range;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRangeBounds = (context: Context, node: Text, offset: number, length: number): Bounds => {
|
||||||
|
return Bounds.fromClientRect(context, createRange(node, offset, length).getBoundingClientRect());
|
||||||
};
|
};
|
||||||
|
|
||||||
const breakText = (value: string, styles: CSSParsedDeclaration): string[] => {
|
const breakText = (value: string, styles: CSSParsedDeclaration): string[] => {
|
||||||
|
Loading…
Reference in New Issue
Block a user