Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the issue where TextNode does not render when it is an empty string and the line-height issue in non-Firefox browsers; also, fix the misalignment of textDecorationLine at high resolutions. #3182

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions src/dom/node-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const LIST_OWNERS = ['OL', 'UL', 'MENU'];
const parseNodeTree = (context: Context, node: Node, parent: ElementContainer, root: ElementContainer) => {
for (let childNode = node.firstChild, nextNode; childNode; childNode = nextNode) {
nextNode = childNode.nextSibling;

if (isTextNode(childNode) && childNode.data.trim().length > 0) {
// Fixes #2238 #1624 - Fix the issue of TextNode content being overlooked in rendering due to being perceived as blank by trim().
if (isTextNode(childNode) && childNode.data.length > 0) {
parent.textNodes.push(new TextContainer(context, childNode, parent.styles));
} else if (isElementNode(childNode)) {
if (isSlotElement(childNode) && childNode.assignedNodes) {
Expand Down
39 changes: 14 additions & 25 deletions src/render/canvas/canvas-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,14 @@ export class CanvasRenderer extends Renderer {

renderTextWithLetterSpacing(text: TextBounds, letterSpacing: number, baseline: number): void {
if (letterSpacing === 0) {
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + baseline);
// Fixed an issue with characters moving up in non-Firefox.
// https://github.com/niklasvh/html2canvas/issues/2107#issuecomment-692462900
if (navigator.userAgent.indexOf('Firefox') === -1){
this.ctx.textBaseline = 'ideographic';
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
} else {
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + baseline);
}
} else {
const letters = segmentGraphemes(text.text);
letters.reduce((left, letter) => {
Expand Down Expand Up @@ -181,7 +188,7 @@ export class CanvasRenderer extends Renderer {
this.ctx.direction = styles.direction === DIRECTION.RTL ? 'rtl' : 'ltr';
this.ctx.textAlign = 'left';
this.ctx.textBaseline = 'alphabetic';
const {baseline, middle} = this.fontMetrics.getMetrics(fontFamily, fontSize);
const {baseline} = this.fontMetrics.getMetrics(fontFamily, fontSize);
const paintOrder = styles.paintOrder;

text.textBounds.forEach((text) => {
Expand Down Expand Up @@ -214,35 +221,17 @@ export class CanvasRenderer extends Renderer {
if (styles.textDecorationLine.length) {
this.ctx.fillStyle = asString(styles.textDecorationColor || styles.color);
styles.textDecorationLine.forEach((textDecorationLine) => {
// Fix the issue where textDecorationLine exhibits x-axis positioning errors on high-resolution devices due to varying devicePixelRatio, corrected by using relative values of element heights.
var decorationLineHeight = 1;
switch (textDecorationLine) {
case TEXT_DECORATION_LINE.UNDERLINE:
// Draws a line at the baseline of the font
// TODO As some browsers display the line as more than 1px if the font-size is big,
// need to take that into account both in position and size
this.ctx.fillRect(
text.bounds.left,
Math.round(text.bounds.top + baseline),
text.bounds.width,
1
);

this.ctx.fillRect(text.bounds.left, text.bounds.top + text.bounds.height - decorationLineHeight, text.bounds.width, decorationLineHeight);
break;
case TEXT_DECORATION_LINE.OVERLINE:
this.ctx.fillRect(
text.bounds.left,
Math.round(text.bounds.top),
text.bounds.width,
1
);
this.ctx.fillRect(text.bounds.left, text.bounds.top , text.bounds.width, decorationLineHeight);
break;
case TEXT_DECORATION_LINE.LINE_THROUGH:
// TODO try and find exact position for line-through
this.ctx.fillRect(
text.bounds.left,
Math.ceil(text.bounds.top + middle),
text.bounds.width,
1
);
this.ctx.fillRect(text.bounds.left, text.bounds.top + (text.bounds.height / 2 - decorationLineHeight / 2), text.bounds.width, decorationLineHeight);
break;
}
});
Expand Down