I am working on a web application whose HTML user interface involves SVG graphics at some point. A part of that SVG is subject to various transformations, such as rotation.
Within that transformed SVG graphic, I am now required to display (the first page of) a PDF file. For this purpose, I am using a <foreignObject>
element that contains an HTML body with a <canvas>
element that can serve as the target of the PDF.js library1.
Here is a minimal example that contains a <g>
element with a rotate transform on it. The <g>
element contains a regular SVG rectangle for comparison and the above construct with the PDF.js output:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.2/pdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.2/pdf.worker.min.js"></script>
</head>
<body>
<svg width="800" height="600">
<rect width="100%" height="100%" fill="rgb(186, 224, 255)"/>
<g transform="rotate(-30) translate(10, 20) scale(1.5)">
<rect x="100px" y="50px" width="100px" height="80px" stroke="red" fill="none"/>
<foreignObject x="150px" y="300px" width="200" height="150">
<xhtml:body margin="0px" padding="0px" width="200px" height="150px" border="1px solid black">
<canvas width="200" height="150" id="cnv"/>
</xhtml:body>
</foreignObject>
</g>
</svg>
<script type="text/javascript">
pdfjsLib.getDocument('https://en.wikipedia.org/api/rest_v1/page/pdf/Canvas_element').promise.then(function (pdf) {
return pdf.getPage(1);
}).then(function (page) {
var viewport = page.getViewport({
scale: 1
});
var pdfCanvas = document.getElementById('cnv');
pdfCanvas.width = viewport.width;
pdfCanvas.height = viewport.height;
var renderCtx = {
canvasContext: pdfCanvas.getContext('2d'),
viewport: viewport
};
page.render(renderCtx);
});
</script>
</body>
</html>
In Firefox 66, this appears as expected:
In Chrome 74, however, the canvas shows a seemingly arbitrary rectangle from the first page of the PDF (rather than its top left corner) and this output is not rotated, either:
Is there any way to work around this issue and rotated PDFs to display within my SVG in Chrome?
1: Somehow, whatever resource I looked at, the question of how to render PDF in JavaScript invariably seemed to lead to this library. Furthermore, from what I read on the library's website the SVG renderer is still very much work in progress and thus cannot be used at a place where customers may upload arbitrary PDF content.
I already met similar issue. Probably for performance, in chrome - canvas uses isolated context which is not derived from SVG.
You may use img
instead & render pdf to hidden canvas.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.2/pdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.2/pdf.worker.min.js"></script>
</head>
<body>
<canvas width="200" height="150" id="cnv" style="position:absolute;left:-10000px"></canvas>
<svg width="800" height="600">
<rect width="100%" height="100%" fill="rgb(186, 224, 255)"/>
<g transform="rotate(-30) translate(10, 20) scale(1.5)">
<rect x="100px" y="50px" width="100px" height="80px" stroke="red" fill="none"/>
<foreignObject x="150px" y="300px" width="200" height="150">
<xhtml:body margin="0px" padding="0px" width="200px" height="150px" border="1px solid black">
<img id="img" x="150px" y="300px" width="200" height="150"/>
</xhtml:body>
</foreignObject>
</g>
</svg>
<script type="text/javascript">
pdfjsLib.getDocument('https://en.wikipedia.org/api/rest_v1/page/pdf/Canvas_element').promise.then(function (pdf) {
return pdf.getPage(1);
}).then(function (page) {
var viewport = page.getViewport({
scale: 1
});
var pdfCanvas = document.getElementById('cnv');
pdfCanvas.width = viewport.width;
pdfCanvas.height = viewport.height;
var renderCtx = {
canvasContext: pdfCanvas.getContext('2d'),
viewport: viewport
};
page.render(renderCtx);
});
// wait for page rendered, just use some pdf.js callback
setTimeout(function(){
const cnv = document.getElementById('cnv');
document.getElementById('img').src = cnv.toDataURL("image/png");
}, 2000);
</script>
</body>
</html>