pdfjs: How to create more than one canvas to show all the pages? - angularjs

I am following the approach of
here. The problem is that all the pages are in first canvas. This is because I have only one canvas and I am not sure how I can generate more canvas one after other?
function handlePages(page)
{
var viewport = page.getViewport(canvas.width / page.getViewport(1.0).width);
var ctx = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({ canvasContext: ctx, viewport: viewport });
//Add it to the web page
div.appendChild( canvas);
//Move to next page
currPage++;
if ( $scope.pdfDoc !== null && currPage <= numPages )
{
$scope.pdfDoc.getPage( currPage ).then( handlePages );
}
}

you cut out part of the code from the linked answer that you took this from. notice the document.createElement( "canvas" ). That creates a new canvas for every page.

Related

Not getting accurate mouse mouseup and mousedown coordinate

I am using react with typescript. I have a canvas and I am trying to draw a rectangle inside the canvas based on mouseup and mousedown event. I don't know what I am doing wrong here my rectangles are drawn but in different positions.
This is how my code looks like:
const Home = () => {
const divRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
let paint = false;
let StartClientXCord: any;
let StartClientYCord: any;
let FinishedClientXCord: any;
let FinishedClientYCord: any;
function startPosition(e:any){
paint = true;
StartClientXCord = e.clientX;
StartClientYCord = e.clientY;
}
function finishPosition(e:any){
FinishedClientXCord = e.clientX;
FinishedClientYCord = e.clientY;
paint = false;
}
function draw(e: any){
if(!paint){
return
}
if(divRef.current){
const canvas = divRef.current.getContext('2d');
if(canvas){
canvas?.rect(StartClientXCord, StartClientYCord, FinishedClientXCord - StartClientXCord, FinishedClientYCord - StartClientYCord);
canvas.fillStyle = 'rgba(100,100,100,0.5)';
canvas?.fill();
canvas.strokeStyle = "#ddeadd";
canvas.lineWidth = 1;
canvas?.stroke();
}
}
}
if(divRef.current){
divRef.current.addEventListener("mousedown", startPosition);
divRef.current.addEventListener("mouseup", finishPosition);
divRef.current.addEventListener("mousemove", draw);
}
})
return (
<canvas className='canvas' style={{height: height, width: width}} ref={divRef}>
</canvas>
)
}
I am drawing on a different position box is drawing on a different position. Here I attached the screenshot as well
I believe it's because you are using clientX and clientY, which MDN defines as:
The clientX read-only property of the MouseEvent interface provides the horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page).
This is the absolute position on the entire page. What you want is the is the position reative to the top left corner of the canvas. You want offsetX and offsetY which MDN defines as:
The offsetX read-only property of the MouseEvent interface provides the offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node.
So give this a try:
StartClientXCord = e.offsetX;
StartClientYCord = e.offsetY;

FabricJS object is not selectable/clickable/resizable in top left corner (React + FabricJs)

I have FabricJs canvas. And I have multiple objects outside canvas. When I click on object, it appears in Canvas. I am not doing anything else with this object programmatically.
Here is part of code that add image to Canvas (it is React code):
const { state, setState } = React.useContext(Context);
const { canvas } = state;
...
const handleElementAdd = (event, image) => {
const imageSize = event.target.childNodes[0].getBoundingClientRect();
const imageUrl = `/storage/${image.src}`;
new fabric.Image.fromURL(imageUrl, img => {
var scale = 1;
if (img.width > 100) {
scale = 100/img.width;
}
var oImg = img.set({
left: state.width / 2 - img.width * scale,
top: state.height / 2 - img.height * scale,
angle: 0})
.scale(scale);
canvas.add(oImg).renderAll.bind(canvas);
canvas.setActiveObject(oImg);
});
};
...
<div
key={img.name}
onClick={(e) => handleElementAdd(e, img)}
className={buttonClass}
>
It appears, I can drag and drop, resize, select it. However, when my object is placed in top left corner of canvas, it is not clickable, resizable etc. I can select larger area and then object is selected, but it doesn't respond to any events like resize, select etc.
Not sure where to look at.
I read that if coordinates are changed programmatically object.setCoord() to object can help, but in this case I do not understand where to put it.
You have to put (call) object.setCoords() each time you have modified the object, or moved the object. So, you can call it on mouseUp event of the canvas.
You can write a function naming "moveObject" in which you get the object you are trying to move/moved/updated. And then just selectedObject.setCoords().
Then call that function inside canvas.on('selection:updated', e => {----})

Upload Image Width and Height get in react

I am new to React and react hook I want to upload an image from anywhere but I want to know the width and height of a select image before storing it in the database. I want to store the original image metadata width and height. anyone can help me
One solution can be to use FileReader, something like this should work:
const image = new Image();
let fr = new FileReader();
fr.onload = function() {
if (fr !== null && typeof fr.result == "string") {
image.src = fr.result;
}
};
fr.readAsDataURL(inputFile);
image.onload = async function() {
const width = image.width;
};
You can take a look here: https://github.com/toncic/Image-Classification/blob/master/src/Components/ImageClassification.tsx#L45

how to fix this Error Cannot use the same canvas during multiple render() operations

I am using canvas in react and rendering pdf's as images using the canvas.
Now, when I get new data i.e another pdf get's added, then
again have to use the canvases for that.
I am not sure how to fix this error or how to remove the canvas or even clear the canvas before using it again.
Here's the relevant code:
pdfLoop = (item,index) => {
var that = this;
PDFJS.getDocument(item).then(function getPdfHelloWorld(pdf) {
//
// Fetch the first page
console.log('url is : ',item);
pdf.getPage(1).then(function getPageHelloWorld(page) {
var scale = 0.5;
var viewport = page.getViewport(scale);
let cref = 'canvas'+index;
let imgref ='img'+index;
console.log('cref no : ',cref);
console.log('img no : ',imgref);
// Prepare canvas using PDF page dimensions
//
var canvas = that.canvasRefs[cref];
//let imagez = that.imageRefs[imgref];
var context = canvas.getContext('2d');
context.globalcompositeoperation = 'source-over';
// context.fillStyle = "#fff";
//draw on entire canvas
//context.fillRect( 0, 0, canvas.width, canvas.height );
canvas.height = viewport.height;
canvas.width = viewport.width;
//imagez.src = canvas.toDataURL("image/png");
//
// Render PDF page into canvas context
//
//page.render({canvasContext: context, viewport: viewport});
var task = page.render({canvasContext: context, viewport: viewport})
task.promise.then(function(){
//console.log(canvas.toDataURL('image/png'));
let imgItem = {imgref:canvas.toDataURL('image/png'),page:index+1,rotate:0}
let newState = that.state.imgsrc;
newState[index] = imgItem;
//let newState = that.state.imgsrc.concat(imgItem);
that.setState({
imgsrc:newState
});
//imagez.src = canvas.toDataURL('image/png')
});
});
});
}
In case someone stumbles across this, the error message states the following Use different canvas or ensure previous operations were cancelled or completed.
When getting the document, if there already is a document, it has to be destroyed. I.e:
PDFJS.getDocument({ url: pdf_url }).promise
.then((pdf_doc) => {
if (this.pdf_doc) {
this.pdf_doc.destroy();
}
this.pdf_doc = pdf_doc;
this.total_pages = this.pdf_doc.numPages;
})
I have no idea if this is a good solution, but at least it worked for me.
I've had the same exact problem as you, but my solution was a bit different than the answers previously posted.
For me the problem was the fact that I was unnecessarily re-rendering with a state change. Check if you aren't re-rendering the component without properly clearing the canvas, or if you even need to re-render at all (in my case I didn't).
Hopefully this could help
In order to avoid this situation, put your canvas object in a div object as is shown below:
<div id="div_canvas">
<canvas id="cnv"></canvas>
</div>
Then, before to call pdf.js functions, remove the "div_canvas" content and recreate it:
$("#div_canvas").html("");
$("#div_canvas").html("<canvas id='cnv'></canvas>");
I had exactly the same issue when working with PDF.js, the solution to this is,
You will have to clear your context after the render completes.
if (context) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
}
This will make sure that the context is cleared and ready for the next render and the error disappears.
this worked for me.
In some cases, this error will be displayed when the pdf has action buttons such as next/previous or scaling.
In this cases, often you have a function for rendering pdf page such as:
renderPage(num) {
// Using promise to fetch the page
pdfDoc.getPage(num).then(function (page) {
const viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
const renderContext = {
canvasContext: ctx,
viewport: viewport
};
page.render(renderContext);
});
}
To fix the error just perform following changes:
Add two variable for controlling conflict and cache waiting page number:
pageRendering = false; // Check conflict
pageNumPending = null; // Cache waiting page number
Use these variables as follow:
renderPage(num) {
if (this.pageRendering) { // Check if other page is rendering
this.pageNumPending = num; // Cache waited page number until previous page rendering completed
} else {
this.pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function (page) {
const viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
const renderContext = {
canvasContext: ctx,
viewport: viewport
};
const renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function () {
this.pageRendering = false;
if (pageNumPending !== null) {
// Waited page must be rendered
this.renderPage(pageNumPending);
// Must be set to null to prevent infinite loop
this.pageNumPending = null;
}
});
});
One possible cause: React Strict mode renders twice

Appcelerator Titanium: Using eventListener & fireEvent on Database Array on View

I'm new to titanium. I've been having some problems on this for a while now. Help would be greatly appreciated.
I have a array loop that cycles through a database to retrieve images and labels. These are all stored in a view. I am adding an event listener to the the view and then adding fire event to the listener to open the selected image in a new window with the id of that video acting as a reference point to play the selected video in the new window. Hope my code makes more sense.
The problem is that the even listener will only return the value of the last row in the database, not the info of the video image that the user clicks on
function getChannelVideos(){
// create an empty data array
var video = [];
// create the httpRequest
var xhr = Titanium.Network.createHTTPClient();
xhr.open('GET','getVideoFeed.php?chid='+channelView.channelId);
// this method will be called when the request is complete
xhr.onload = function()
{
// parse json coming from the server
var json = JSON.parse(this.responseText);
// if Channel Videos are returned
if(json.channelVideos)
{
for (var i = 0; i < json.channelVideos.length; i++){
var video = Ti.UI.createView({ // creates video view
width:'60%', // sets height
backgroundColor: 'transparent',
});
var videoThumb = Ti.UI.createImageView({ // creates thumb
image:json.channelVideos[i].vThumb,
width:'100%', // sets height
top:150 + i*200, // positions from top
backgroundColor: '#000'
});
video.add(videoThumb); // adds thumb to video view
var videoTitle = Ti.UI.createLabel({ // creates label
text:json.channelVideos[i].vTitle,
font:{
fontSize:12
},
color:'#fff',
backgroundColor:'#000',
textAlign:'center',
width:'100%',
height:20,
top:140 + i*200, // positions from top
});
video.add(videoTitle); // adds video title to video view
var videoSpeaker = Ti.UI.createLabel({ // creates Label
text:json.channelVideos[i].vSpeaker,
font:{
fontSize:12
},
color:'#fff',
backgroundColor:'#000',
textAlign:'center',
width:'100%',
height:20,
top:295 + i*200, // positions from top
});
video.add(videoSpeaker); // adds speaker name to video view
var videoPlay = Ti.UI.createImageView({
image:'/images/light_play.png',
top:215 + i*200, // positions from top
});
video.add(videoPlay); // adds playbutton to videoview
chContentAreaView.add(video); // adds video view to scrollview
var vId=json.channelVideos[i].vId;
video.vId = vId; // Here vId is custom property of video
video.addEventListener('click', function(e){
alert(e.source.vId);
Ti.App.fireEvent('videoPlay',{
videoId:e.source.vId
});
});
}
}
};
// this method will be called if there is an error
xhr.onerror = function(){
alert(this.error + ': ' + this.statusText);
return false;
};
// open the httpRequest
xhr.setRequestHeader("contentType","application/json; charset=utf-8");
xhr.send();
}
try some things:
give video.vId = json.channelVideos[i].vId directly in the property of video like :
var video = Ti.UI.createView({ // creates video view
width:'60%', // sets height
backgroundColor: 'transparent',
vId : json.channelVideos[i].vId
});
use property touchEnabled : false in other views and labels for videoThumb, videoTitle, videoSpeaker, videoPlay etc.
I think by doing this all things will work fine.

Resources