Reputation: 11
I’m using pdfjs-dist
to render a PDF in a React component. The PDF can have varying sizes (e.g., 1800x1200, 580x250, etc.), and I want to dynamically scale the canvas and render the PDF to fit within the available space (a container). Additionally, I need to draw polygons (rectangles) on specific coordinates that are provided as relative values (e.g., X and Y ranging from 0 to 1).
Here’s my current approach:
I calculate the scale dynamically based on the PDF dimensions (viewport.width, viewport.height) and the container dimensions (Box width and height). I render the PDF using page.getViewport({ scale }) and draw the polygons by mapping relative coordinates to pixels:
const coords = polygon.map((point) => ({
x: point.X * canvasWidth,
y: point.Y * canvasHeight, // Invert Y-axis
}));
However, the PDF renders correctly, but the canvas size and polygons are misaligned. For example:
A PDF with original dimensions 1800x1200 displays as 580x250 when logged from the viewport. The polygons don’t align with the correct positions on the PDF. Here’s a snippet of my React code:
const [viewportCoords, setViewPortCoords] = useState({ width: 0, height: 0 });
const [scale, setScale] = useState(1);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const polygonDummy = [
{ X: 0.34002524614334106, Y: 0.27618247270584106 },
{ X: 0.8136482834815979, Y: 0.2764469087123871 },
{ X: 0.8136501908302307, Y: 0.2848358154296875 },
{ X: 0.34002646803855896, Y: 0.2845710515975952 },
];
useEffect(() => {
if (!pdfView) return;
const url = URL.createObjectURL(pdfView);
const canvas = canvasRef.current;
if (!canvas) return;
GlobalWorkerOptions.workerSrc = '../../node_modules/pdfjs-dist/build/pdf.worker.mjs';
const loadingTask = getDocument(url);
loadingTask.promise
.then(async (pdf) => {
const page = await pdf.getPage(1);
// Obtain pdf size
const viewport = page.getViewport({ scale: 1.5 });
// Scale based on container size
const containerWidth = viewportCoords.width || window.innerWidth;
const containerHeight = viewportCoords.height || window.innerHeight;
// Calculate scaling factor to fit PDF to container
const scaleX = containerWidth / viewport.width;
const scaleY = containerHeight / viewport.height;
const scale = Math.min(scaleX, scaleY); // Mantener la relación de aspecto
setScale(scale);
setViewPortCoords({ width: viewport.width * scale, height: viewport.height * scale });
const context = canvas.getContext('2d');
if (context) {
// Scale the canvas size
canvas.width = Math.floor(viewport.width * scale);
canvas.height = Math.floor(viewport.height * scale);
canvas.style.width = `${viewport.width * scale}px`;
canvas.style.height = `${viewport.height * scale}px`;
const renderContext = {
canvasContext: context,
transform: [scale, 0, 0, scale, 0, 0],
viewport,
};
await page.render(renderContext).promise;
drawPolygon(context, viewport, polygonDummy);
}
})
.catch((error) => console.error('Error al cargar el PDF:', error));
return () => URL.revokeObjectURL(url);
}, [pdfView]);
const drawPolygon = (
context: CanvasRenderingContext2D,
viewport: any,
polygon: { X: number; Y: number }[]
) => {
const canvasWidth = viewport.width * scale;
const canvasHeight = viewport.height * scale;
context.beginPath();
context.strokeStyle = 'red';
context.lineWidth = 2;
const coords = polygon.map((point) => ({
x: point.X * canvasWidth,
y: point.Y * canvasHeight, // Invertir Y
}));
if (coords.length > 0) {
context.moveTo(coords[0].x, coords[0].y);
for (let i = 1; i < coords.length; i++) {
context.lineTo(coords[i].x, coords[i].y);
}
context.closePath();
context.stroke();
}
};
return (
<Box
sx={{
width: viewportCoords.width,
height: viewportCoords.height,
overflow: 'auto',
border: '1px solid black',
}}
>
<canvas ref={canvasRef}></canvas>
</Box>
How can I ensure:
The canvas size matches the PDF dimensions and scales correctly to the container? The polygons align perfectly with the specified coordinates on the PDF?
Upvotes: 1
Views: 61