html2canvas
html2canvas copied to clipboard
line-height problem?
when I use line-height=height to center text vertically,I find it does not seem to be centered vertically in the picture generated by html2canvas.
Bug reports:
https://jsbin.com/cohugezaxo/1/edit?html,output
Specifications:
- html2canvas version tested with: 1.0.0-rc.5
- Browser & version: chrome 79.0.3945.88
- Operating system: macos sierra 10.12.4
Hi there, I've met your problem in my project these days, but in this case you've mentioned, I find it draws quite well...I'don't know how to repeat it again, looking forward for your help :-)
Specifications:
- Browser & version: chrome 79.0.3945.88
- Operating system: macos sierra 10.12.4
Try to set 'font-family:serif'...
Try to set 'font-family:serif'...
Thanks for your reply, I've solved my problem~Have a nice day:-)
how do you solved it
I met the same prolem, and i found that it's the problem of getRangeBounds
;
the rangeBounds's height of a TextNode may bigger than the container node's clientHeight;
and then when fillText with bounds.top + bounds.height
and textBaseline: 'bottom'
into canvas, the text will be lower than the original position
my solution is adjust all text nodes's top in clonedElement:
but i think this should be solved in the library itself.
Try to set 'font-family:serif'...
Thanks for your reply, I've solved my problem~Have a nice day:-)
How do you solved this problem? Need your help.
Try to set 'font-family:serif'...
Thanks for your reply, I've solved my problem~Have a nice day:-)
How do you solved this problem? Need your help.
It seems html2canvas doesn't support some fonts, just try to disable them one by one, then you will find the unsupported one. in my case, it's FangZheng xxx
.
Try to set 'font-family:serif'...
Thanks for your reply, I've solved my problem~Have a nice day:-)
How do you solved this problem? Need your help.
It seems html2canvas doesn't support some fonts, just try to disable them one by one, then you will find the unsupported one. in my case, it's
FangZheng xxx
.
Yes,you are right! html2canvas doesn't support most fonts.
in html2canvas
first: different font with get different textnode top and height
line-height: 70px;
font-size: 50px;
in MacOs
in Chromium (chrome 84.0.4147.89 and new edge)
Ping Fang, sans-serif, serif will get 70px height and STKaiti is 50px height inside html2canvas so
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
will fillText in different position but in Safari and Firefox, html2canvas result is almost same with dom then I find that
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline
html2canvas set context.textBaseline to middle or bottom but it's useless to let different font-family vertical-align: middle in Chromium
I try all context.textBaseline values and find ideographic useful in Chromium and Safari
but ideographic will result in first image in FireFox
in html2canvas
first: different font with get different textnode top and height
line-height: 70px; font-size: 50px;
in MacOs
in Chromium (chrome 84.0.4147.89 and new edge)
Ping Fang, sans-serif, serif will get 70px height and STKaiti is 50px height inside html2canvas so
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
will fillText in different position but in Safari and Firefox, html2canvas result is almost same with dom then I find that
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline
html2canvas set context.textBaseline to middle or bottom but it's useless to let different font-family vertical-align: middle in Chromium
I try all context.textBaseline values and find ideographic useful in Chromium and Safari
but ideographic will result in first image in FireFox
Please explain me how can i fix this. Where should I write which code? Should I edit html2canvas.js or just edit in html2canvas(elemet).then(canvas => {--------});?
in my scene, I only need to support Chrome, so I change in line 6200 @1.0.0-rc.5
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) {
var _this = this;
if (letterSpacing === 0) {
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
}
to
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) {
var _this = this;
if (letterSpacing === 0) {
this.ctx.textBaseline = 'ideographic'
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
}
I think you can try to design a new system to get same text.bounds.top and text.bounds.height for different font-family in all browsers such as Chrome, Edge, Safari, Firefox, and main mobile browsers
or
set different canvas context.textBaseline in different browsers before use canvas context.fillText
or
is it possibility to use a div to
<div style="font-size: textnode-font-size; line-height: textnode-line-height; white-space: nowrap;">textnode</div>
then use div.clientHeight and top = recursion computation div.parent.offsetTop and div.parent.parent.offsetTop to fillText(text, left, top+div.clientHeight)
in html2canvas
first: different font with get different textnode top and height
line-height: 70px; font-size: 50px;
in MacOs
in Chromium (chrome 84.0.4147.89 and new edge)
Ping Fang, sans-serif, serif will get 70px height and STKaiti is 50px height inside html2canvas so
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
will fillText in different position but in Safari and Firefox, html2canvas result is almost same with dom then I find that
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline
html2canvas set context.textBaseline to middle or bottom but it's useless to let different font-family vertical-align: middle in Chromium
I try all context.textBaseline values and find ideographic useful in Chromium and Safari
but ideographic will result in first image in FireFox
Please explain me how can i fix this. Where should I write which code? Should I edit html2canvas.js or just edit in html2canvas(elemet).then(canvas => {--------});?
in my scene, I only need to support Chrome, so I change in line 6200 @1.0.0-rc.5
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) { var _this = this; if (letterSpacing === 0) { this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height); }
to
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) { var _this = this; if (letterSpacing === 0) { this.ctx.textBaseline = 'ideographic' this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height); }
I think you can try to design a new system to get same text.bounds.top and text.bounds.height for different font-family in all browsers such as Chrome, Edge, Safari, Firefox, and main mobile browsers or set different canvas context.textBaseline in different browsers before use canvas context.fillText or is it possibility to use a div to
<div style="font-size: textnode-font-size; line-height: textnode-line-height; white-space: nowrap;">textnode</div>
then use div.clientHeight and top = recursion computation div.parent.offsetTop and div.parent.parent.offsetTop to fillText(text, left, top+div.clientHeight)
Thanks. It work for me!
in html2canvas
first: different font with get different textnode top and height
line-height: 70px; font-size: 50px;
in MacOs
in Chromium (chrome 84.0.4147.89 and new edge)
Ping Fang, sans-serif, serif will get 70px height and STKaiti is 50px height inside html2canvas so
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
will fillText in different position but in Safari and Firefox, html2canvas result is almost same with dom then I find that
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline
html2canvas set context.textBaseline to middle or bottom but it's useless to let different font-family vertical-align: middle in Chromium
I try all context.textBaseline values and find ideographic useful in Chromium and Safari
but ideographic will result in first image in FireFox
Please explain me how can i fix this. Where should I write which code? Should I edit html2canvas.js or just edit in html2canvas(elemet).then(canvas => {--------});?
in my scene, I only need to support Chrome, so I change in line 6200 @1.0.0-rc.5
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) { var _this = this; if (letterSpacing === 0) { this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height); }
to
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) { var _this = this; if (letterSpacing === 0) { this.ctx.textBaseline = 'ideographic' this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height); }
I think you can try to design a new system to get same text.bounds.top and text.bounds.height for different font-family in all browsers such as Chrome, Edge, Safari, Firefox, and main mobile browsers or set different canvas context.textBaseline in different browsers before use canvas context.fillText or is it possibility to use a div to
<div style="font-size: textnode-font-size; line-height: textnode-line-height; white-space: nowrap;">textnode</div>
then use div.clientHeight and top = recursion computation div.parent.offsetTop and div.parent.parent.offsetTop to fillText(text, left, top+div.clientHeight)
It works for me!
I improved it simply in line 6200
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) {
var _this = this;
if (letterSpacing === 0) {
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
}
// ...
};
to
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) {
var _this = this;
if (navigator.userAgent.indexOf('Firefox') === -1){
// non-Firefox browser add this
this.ctx.textBaseline = 'ideographic';
}
if (letterSpacing === 0) {
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
}
// ...
};
in html2canvas
first: different font with get different textnode top and height
line-height: 70px; font-size: 50px;
in MacOs
in Chromium (chrome 84.0.4147.89 and new edge)
Ping Fang, sans-serif, serif will get 70px height and STKaiti is 50px height inside html2canvas so
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
will fillText in different position but in Safari and Firefox, html2canvas result is almost same with dom then I find that
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline
html2canvas set context.textBaseline to middle or bottom but it's useless to let different font-family vertical-align: middle in Chromium
I try all context.textBaseline values and find ideographic useful in Chromium and Safari
but ideographic will result in first image in FireFox
Please explain me how can i fix this. Where should I write which code? Should I edit html2canvas.js or just edit in html2canvas(elemet).then(canvas => {--------});?
in my scene, I only need to support Chrome, so I change in line 6200 @1.0.0-rc.5
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) { var _this = this; if (letterSpacing === 0) { this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height); }
to
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) { var _this = this; if (letterSpacing === 0) { this.ctx.textBaseline = 'ideographic' this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height); }
I think you can try to design a new system to get same text.bounds.top and text.bounds.height for different font-family in all browsers such as Chrome, Edge, Safari, Firefox, and main mobile browsers or set different canvas context.textBaseline in different browsers before use canvas context.fillText or is it possibility to use a div to
<div style="font-size: textnode-font-size; line-height: textnode-line-height; white-space: nowrap;">textnode</div>
then use div.clientHeight and top = recursion computation div.parent.offsetTop and div.parent.parent.offsetTop to fillText(text, left, top+div.clientHeight)
it works ! thank you too much
Maybe it is different for other peoples code, but in my case I was able to correct this with one line of code in the html2canvas file. Right before the mentioned letter spacing conditional add the following:
text.bounds.top = text.bounds.top - text.bounds.height
Maybe it is different for other peoples code, but in my case I was able to correct this with one line of code in the html2canvas file. Right before the mentioned letter spacing conditional add the following:
text.bounds.top = text.bounds.top - text.bounds.height
for me it's half the text height (without modifying textBaseline):
text.bounds.top = text.bounds.top - text.bounds.height / 2
Maybe it is different for other peoples code, but in my case I was able to correct this with one line of code in the html2canvas file. Right before the mentioned letter spacing conditional add the following:
text.bounds.top = text.bounds.top - text.bounds.height
for me it's half the text height (without modifying textBaseline):
text.bounds.top = text.bounds.top - text.bounds.height / 2
@niaodan2b solution worked for me - not happy about having to monkey patch this though... any reason this can't be added to the source? @niklasvh - i'll work on a PR to include a passed option for text alignment
Another similar case which is fix my problem #2775
I met the same prolem, and i found that it's the problem of
getRangeBounds
; the rangeBounds's height of a TextNode may bigger than the container node's clientHeight;and then when fillText with
bounds.top + bounds.height
andtextBaseline: 'bottom'
into canvas, the text will be lower than the original positionmy solution is adjust all text nodes's top in clonedElement:
but i think this should be solved in the library itself.
Text dropping down is still an issue for me. And this solution worked. Below is how I used it.
I added a css class to the elements/text that would need adjusting during the clone and used that to update the style accordingly.
html2canvas(
document.querySelector('#container-element-target'), // html I am converting to canvas
{ // options
onclone: (el) => {
const elementsWithShiftedDownwardText = el.querySelectorAll('.shifted-text');
elementsWithShiftedDownwardText.forEach(element => {
// adjust styles or do whatever you want here
element.style.transform = 'translateY(-50%)';
});
}
}
).then(function(canvas) {
// whatever you are doing with the canvas after creation
document.body.appendChild(canvas);
});
I am using Tailwind and if you are as well then this solution may be helpful.
/* global.css */
@tailwind base;
@layer base {
img {
@apply inline-block;
}
}
@tailwind components;
@tailwind utilities;
https://github.com/niklasvh/html2canvas/issues/2775#issuecomment-1204988157
Since html2canvas append div tag to body to get some metrics, we can do the following:
const downloadPdf = () => {
const style = document.createElement('style');
document.head.appendChild(style);
style.sheet?.insertRule('body > div:last-child img { display: inline-block; }');
html2canvas(element).then(canvas => {
style.remove();
});
};
Maybe it is different for other peoples code, but in my case I was able to correct this with one line of code in the html2canvas file. Right before the mentioned letter spacing conditional add the following:
text.bounds.top = text.bounds.top - text.bounds.height
for me it's half the text height (without modifying textBaseline):
text.bounds.top = text.bounds.top - text.bounds.height / 2
This works for me! Thank you so much!
Importing import { CanvasRenderer } from "html2canvas/dist/types/render/canvas/canvas-renderer"
throws module not found error
good job,thx
None of the above really worked for me, i dont know if DOM manipulation has changed within recent years but here is my two cents on how I achieved something workable.
// Generate canvas element
const html2canvasElement = await html2canvas(props.node, {...props.options, onclone(document, element) {
// Select all text nodes
const textNodeList = document
.evaluate('descendant-or-self::text()', element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
// Update each text node
for(let i=0; i < textNodeList.snapshotLength; i++)
{
const textNode = textNodeList.snapshotItem(i);
if (!textNode) {
continue;
}
const parent = textNode.parentElement;
if (!parent){
continue;
}
// Get text node bounds
const range = document.createRange();
range.selectNode(textNode);
const bounds = range.getBoundingClientRect();
// Offset marginTop
if (parent.style.marginTop)
{
throw new Error('Text with marginTop not supported');
}
parent.style.marginTop = `-${Math.ceil(bounds.height)}px`;
}
}});
I am using Tailwind and if you are as well then this solution may be helpful.
/* global.css */ @tailwind base; @layer base { img { @apply inline-block; } } @tailwind components; @tailwind utilities;
OMG this save me a night