Konva converting stage toDataUrl() do not work in chrome browser,but work well in safari browser - konvajs

I have Konva Stage with few layers, when I try convert to image all stage - result is OK in safari browser , when I try convert stage in chrome ,firefox etc. - result is failed. I think that toDataUrl() method does
not work well in chrome browser ,firefox browser etc except for safari browser
<button id="save" type="button">save</button>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
function downloadURI(uri, name) {
var link = document.createElement('a');
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
}
function drawImage(imageObj) {
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
// darth vader
var darthVaderImg = new Konva.Image({
image: imageObj,
x: stage.getWidth() / 2 - 200 / 2,
y: stage.getHeight() / 2 - 137 / 2,
width: 200,
height: 137,
name: 'myimg',
draggable: true
});
// add cursor styling
darthVaderImg.on('mouseover', function () {
document.body.style.cursor = 'pointer';
});
darthVaderImg.on('mouseout', function () {
document.body.style.cursor = 'default';
});
layer.add(darthVaderImg);
stage.add(layer);
stage.on('click tap', function (e) {
// if click on empty area - remove all transformers
if (e.target === stage) {
stage.find('Transformer').destroy();
layer.draw();
return;
}
// do nothing if clicked NOT on our rectangles
if (!e.target.hasName('myimg')) {
return;
}
// remove old transformers
// TODO: we can skip it if current rect is already selected
stage.find('Transformer').destroy();
// create new transformer
var tr = new Konva.Transformer();
layer.add(tr);
tr.attachTo(e.target);
layer.draw();
})
document.getElementById('save').addEventListener(
'click',
function () {
var dataURL = stage.toDataURL({ pixelRatio: 3 });
downloadURI(dataURL, 'stage.png');
},
false
);
}
var imageObj = new Image();
imageObj.onload = function () {
drawImage(this);
};
imageObj.src = 'https://www.decanterchina.com/assets/images/article/550/136031_decanter-cava-tasting-1.jpg';
</script>
Any possible reasons or solutions ? Thanks!!

If you look into the console you will see a message:
Konva warning: Unable to get data URL. Failed to execute 'toDataURL'
on 'HTMLCanvasElement': Tainted canvases may not be exported.
You have CORS issue. Take a look here for solutions:
Tainted canvases may not be exported
https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image

Related

How i can extract svg element with text pdf js

I need extarct svg element with text from pdf. But if i did like this:
PDF:
Pdf link https://dropfiles.org/2dTlpxTN
const PDF_PATH = "https://dropfiles.org/2dTlpxTN";
const PAGE_NUMBER = 1;
const PAGE_SCALE = 1.5;
const SVG_NS = "http://www.w3.org/2000/svg";
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.js";
function buildSVG(viewport, textContent) {
// Building SVG with size of the viewport (for simplicity)
const svg = document.createElementNS(SVG_NS, "svg:svg");
svg.setAttribute("width", viewport.width + "px");
svg.setAttribute("height", viewport.height + "px");
// items are transformed to have 1px font size
svg.setAttribute("font-size", 1);
// processing all items
textContent.items.forEach(function (textItem) {
// we have to take in account viewport transform, which includes scale,
// rotation and Y-axis flip, and not forgetting to flip text.
const tx = pdfjsLib.Util.transform(
pdfjsLib.Util.transform(viewport.transform, textItem.transform),
[1, 0, 0, -1, 0, 0]
);
const style = textContent.styles[textItem.fontName];
// adding text element
const text = document.createElementNS(SVG_NS, "svg:text");
text.setAttribute("transform", "matrix(" + tx.join(" ") + ")");
text.setAttribute("font-family", style.fontFamily);
text.textContent = textItem.str;
svg.append(text);
});
return svg;
}
async function pageLoaded() {
// Loading document and page text content
const loadingTask = pdfjsLib.getDocument({ url: PDF_PATH });
const pdfDocument = await loadingTask.promise;
const page = await pdfDocument.getPage(PAGE_NUMBER);
const viewport = page.getViewport({ scale: PAGE_SCALE });
const textContent = await page.getTextContent();
// building SVG and adding that to the DOM
const svg = buildSVG(viewport, textContent);
document.getElementById("pageContainer").append(svg);
// Release page resources.
page.cleanup();
}
document.addEventListener("DOMContentLoaded", function () {
if (typeof pdfjsLib === "undefined") {
// eslint-disable-next-line no-alert
alert("Please build the pdfjs-dist library using\n `gulp dist-install`");
return;
}
pageLoaded();
});
I extracted text with out Font and i extract all text. I need extract text as image.
If i do this:
pdfDoc_.getPage(pageNumber).then(function (page) {
var canvasEl = document.createElement('canvas');
var context = canvasEl.getContext('2d');
var viewport = page.getViewport({scale: imagesSetting_reader.scaleText});
canvasEl.height = viewport.height;
canvasEl.width = viewport.width;
var back;
var renderContext = {
canvasContext: context,
background:back,
viewport: viewport
};
page.render(renderContext).promise.then(function () {
resolve(canvasEl.toDataURL("image/png"));
})
});
It extracted text and background image.
if i do like this :
page.getOperatorList().then(opList => {
var svgGfx = new pdfjsLib.SVGGraphics(page.commonObjs, page.objs);
return svgGfx.getSVG(opList, viewport);
}).then(svg => {
console.log(svg)
return svg;
});
And extracted text from svg, i got error :
error on line 1 at column 101: Namespace prefix svg on svg is not defined
How i can extract only svg text with font and convert it to image ?
Like this:

How to make a GUI to visually add Mattertags into a Matterport scene?

There are 2 examples in the Matterport SDK for Embeds documentation to show how to place Mattertags in a scene:
The Intersection Inspector which only allows you to see coordinates for placing a Mattertag where the cursor is if you wait a little bit ... Not very user friendly, you need to copy the coordinates manually in your program.
The Transient Tags Editor which enable you to interactively place multiple Mattertags visually, edit them and then to extract them easily in a JSON file ...
I was wondering how to reproduce the Transient Tags Editor visual UX as I would like to use it in an application.
Insert Mattertags into the model visually
The source code of the app of the Transient Tags Editor is privately hosted on github (Maybe because it doesn't run perfectly on Firefox?), unlike the source code of the Intersection Inspector which is publicly hosted on JSFiddle.
But the user friendliness of the Transient Tags Editor intrigued me and I wanted to understand the difference between the two tools Matterport SDK provides to find out Mattertags coordinates.
How the Intersection Inspector works
The Intersection Inspector uses a timer to display a button at the position of the Pointer when the user does not move the pointer for more than one second. The user can then click the button to see the Mattertag coordinates and copy them manually ...
To achieve that, it needs the current Camera position, which it obtains by observing the camera's pose property:
var poseCache;
mpSdk.Camera.pose.subscribe(function(pose) {
poseCache = pose;
});
Also, it needs the current Pointer position, which it obtains by observing the pointer's intersection property:
var intersectionCache;
mpSdk.Pointer.intersection.subscribe(function(intersection) {
intersectionCache = intersection;
intersectionCache.time = new Date().getTime();
button.style.display = 'none';
buttonDisplayed = false;
});
※ An intersection event is triggered the user moves the pointer, so we hide the button to make sure it is not displayed before the one second delay is over.
Then, a timer is set up using setInterval() to display the button at the right time:
setInterval(() => {
// ...
}, 16);
In the timer callback, we check wether all the conditions to display the button are met ...
First, check we have the information we need:
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
// ...
}, 16);
Then, check one second has elapsed since the last intersection event was received, or we wait the next tick to check again:
var delayBeforeShow = 1000;
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
const nextShow = intersectionCache.time + delayBeforeShow;
if (new Date().getTime() > nextShow) {
// ...
}
}, 16);
Finally, we check the button is not already being displayed:
var delayBeforeShow = 1000;
var buttonDisplayed = false;
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
const nextShow = intersectionCache.time + delayBeforeShow;
if (new Date().getTime() > nextShow) {
if (buttonDisplayed) {
return;
}
// ...
}
}, 16);
Once the conditions are met, we can display the button using Conversion.worldToScreen() to get the screen coordinate of the pointer :
// ...
setInterval(() => {
// ...
if (new Date().getTime() > nextShow) {
// ...
var size = {
w: iframe.clientWidth,
h: iframe.clientHeight,
};
var coord = mpSdk.Conversion.worldToScreen(intersectionCache.position, poseCache, size);
button.style.left = `${coord.x - 25}px`;
button.style.top = `${coord.y - 22}px`;
button.style.display = 'block';
buttonDisplayed = true;
}
}, 16);
The button is a simple HTML button hidden by default using display: none; and positioned relative to the body with position: absolute;.
When the user clicks the button, the current coordinates of the pointer are displayed in a <div> tag above the <iframe> and the button is hidden:
button.addEventListener('click', function() {
text.innerHTML = `position: ${pointToString(intersectionCache.position)}\nnormal: ${pointToString(intersectionCache.normal)}\nfloorId: ${intersectionCache.floorId}`;
button.style.display = 'none';
iframe.focus();
});
The coordinates are formatted using the following function:
function pointToString(point) {
var x = point.x.toFixed(3);
var y = point.y.toFixed(3);
var z = point.z.toFixed(3);
return `{ x: ${x}, y: ${y}, z: ${z} }`;
}
Now, let's see how the easier-to-use and user-friendlier Transient Tags Editor interface works ...
How the Transient Tag Editor works
The Intersection Inspector is enough if you just have a few __Mattertag__s to set permanently in a few models in your application. But if you need your users to set tags interactively in models, something like the Transient Tags Editor's GUI is a better starting point.
The main advantage of using the Transient Tags Editor is that you can see how the Mattertag will be displayed before creating it and! That allows you to place the tag precisely without trial and error ...
To add a tag, you must click on the "Place New Tag" button to enter the "add tag" mode, then you can place one new tag anywhere you want.
We will only focus on that aspect of the editor and produce a simplified code sample that only add tags:
As the user move a tag along the pointer when in "add tag" mode, the first step is to create a new tag and place it. Let's create a function for that using Mattertag.add():
function addTag() {
if(!tag){
mpSdk.Mattertag.add([{
label: "Matterport Tag",
description: "",
anchorPosition: {x: 0, y: 0, z: 0},
stemVector: {x:0, y: 0, z: 0},
color: {r: 1, g: 0, b: 0},
}])
.then((sid) => {
tag = sid[0];
})
.catch( (e) => {
console.error(e);
})
}
}
Then we will have to place the tag at a position near the pointer, and update its position as the user moves the pointer, so let's create a function for that using Mattertag.editPosition():
function updateTagPos(newPos, newNorm=undefined, scale=undefined){
if(!newPos) return;
if(!scale) scale = .33;
if(!newNorm) newNorm = {x: 0, y: 1, z: 0};
mpSdk.Mattertag.editPosition(tag, {
anchorPosition: newPos,
stemVector: {
x: scale * newNorm.x,
y: scale * newNorm.y,
z: scale * newNorm.z,
}
})
.catch(e =>{
console.error(e);
tag = null;
});
}
As you can see the updateTagPos() function takes 3 parameters:
newPos: the new anchor position for the Mattertag.
newNorm: an optional new stem vector for the Mattertag.
scale: an optional new scale for the stem of the Mattertag.
To update the tag position as the user moves the pointer, let's observe the pointer's intersection property to call updateTagPos():
mpSdk.Pointer.intersection.subscribe(intersectionData => {
if(tag){
if(intersectionData.object === 'intersectedobject.model' || intersectionData.object === 'intersectedobject.sweep'){
updateTagPos(intersectionData.position, intersectionData.normal);
}
}
});
To place the new tag, the user simply clicks their mouse button, the Transient Tags Editor provides its own version of the document.activeElement method for intercepting clicks on the <iframe> (but does not work with Firefox so the editor use a quite complex workaround):
function focusDetect(){
const eventListener = window.addEventListener('blur', function() {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
setTimeout(function(){ window.focus(); }, 0);
}
window.removeEventListener('blur', eventListener );
});
}
But, we will use our version which works better with Firefox (But still stop working after the first click in Firefox for whatever reason):
window.addEventListener('blur',function(){
window.setTimeout(function () {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
window.focus()
addTag();
}
}, 0);
});
Finally, let's the function that navigates to the new tag and opens its billboard, usingMattertag.navigateToTag()
function placeTag(){
if(tag) mpSdk.Mattertag.navigateToTag(tag, mpSdk.Mattertag.Transition.INSTANT);
tag = null;
}
Simple Editor Code Sample
First, the complete JavaScript source code:
"use strict";
const sdkKey = "aaaaXaaaXaaaXaaaXaaaXaaa"
const modelSid = "iL4RdJqi2yK";
let iframe;
let tag;
document.addEventListener("DOMContentLoaded", () => {
iframe = document.querySelector('.showcase');
iframe.setAttribute('src', `https://my.matterport.com/show/?m=${modelSid}&help=0&play=1&qs=1&gt=0&hr=0`);
iframe.addEventListener('load', () => showcaseLoader(iframe));
});
function showcaseLoader(iframe){
try{
window.MP_SDK.connect(iframe, sdkKey, '3.10')
.then(loadedShowcaseHandler)
.catch(console.error);
} catch(e){
console.error(e);
}
}
function loadedShowcaseHandler(mpSdk){
addTag()
function placeTag(){
if(tag) mpSdk.Mattertag.navigateToTag(tag, mpSdk.Mattertag.Transition.INSTANT);
tag = null;
}
window.addEventListener('blur',function(){
window.setTimeout(function () {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
window.focus()
addTag();
}
}, 0);
});
function updateTagPos(newPos, newNorm=undefined, scale=undefined){
if(!newPos) return;
if(!scale) scale = .33;
if(!newNorm) newNorm = {x: 0, y: 1, z: 0};
mpSdk.Mattertag.editPosition(tag, {
anchorPosition: newPos,
stemVector: {
x: scale * newNorm.x,
y: scale * newNorm.y,
z: scale * newNorm.z,
}
})
.catch(e =>{
console.error(e);
tag = null;
});
}
mpSdk.Pointer.intersection.subscribe(intersectionData => {
if(tag){
if(intersectionData.object === 'intersectedobject.model' || intersectionData.object === 'intersectedobject.sweep'){
updateTagPos(intersectionData.position, intersectionData.normal);
}
}
});
function addTag() {
if(!tag){
mpSdk.Mattertag.add([{
label: "Matterport Tag",
description: "",
anchorPosition: {x: 0, y: 0, z: 0},
stemVector: {x:0, y: 0, z: 0},
color: {r: 1, g: 0, b: 0},
}])
.then((sid) => {
tag = sid[0];
})
.catch( (e) => {
console.error(e);
})
}
}
} // loadedShowcaseHandler
The HTML source code:
<!DOCTYPE html>
<html>
<head>
<title>Transient Tag Editor</title>
<style>
#showcase {
width: 100%;
height: 100vh;
}
</style>
<script src="https://static.matterport.com/showcase-sdk/2.0.1-0-g64e7e88/sdk.js"></script>
<script src="/js/app-editor.js" type="text/javascript" defer></script>
</head>
<body>
<iframe id="showcase" frameborder="0" allowFullScreen allow="xr-spatial-tracking"></iframe>
</body>
</html>
It works!
Complete Code
The complete code for this sample and others is available on github:
github.com/loic-meister-guild/pj-matterport-sdk-2021-tutorial
See Also
Matterport SDK 2021 Tutorial
Node.js + Express Tutorial for 2021
How to detect a click event on a cross domain iframe

What is the proper way how to fire event on start and finish of the complex drawing using KanvaJS?

I am drawing a complex graphic on canvas using KonvaJS library. Execution takes few seconds and I want to monitor progress on user screen, but I can't make work functions like:
layer.on('draw', () => {
console.log('Draw finished')
})
What is the proper way how to fire event on start and finish of the drawing using KanvaJS?
Here is a test code where console.log('Draw finished') is not executed
var stage = new Konva.Stage({
container: 'canvas-container', // id of HTML div container
width: 1000,
height: 500,
});
function drawGrid(){
let layerMPE = new Konva.Layer();
let pxMax = stage.width();
let pyMax = stage.height();
let res = 10;
for (let px=0; px < pxMax; px += res){
for (let py=0; py < pyMax; py += res){
let rect = new Konva.Rect({
x: px,
y: py,
width: res,
height: res,
fill: 'red'
});
layerMPE.add(rect);
}
}
stage.add(layerMPE);
layerMPE.draw();
layerMPE.on('draw', () => {
console.log('Draw finished')
});
}
drawGrid();

Drag and Drop limitation in Konva js

I recently began to learn Konva-JS... please help me :)
<script>
var width = window.innerWidth;
var height = window.innerHeight;
function loadImages(sources, callback) {
var assetDir = '/assets/';
var images = {};
var loadedImages = 0;
var numImages = 0;
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = assetDir + sources[src];
}
}
function isNearOutline(animal, outline) {
var a = animal;
var o = outline;
var ax = a.getX();
var ay = a.getY();
if(ax > o.x - 20 && ax < o.x + 20 && ay > o.y - 20 && ay < o.y + 20) {
return true;
}
else {
return false;
}
}
function drawBackground(background, beachImg, text) {
var context = background.getContext();
context.drawImage(beachImg, 0, 0);
context.setAttr('font', '20pt Calibri');
context.setAttr('textAlign', 'center');
context.setAttr('fillStyle', 'white');
context.fillText(text, background.getStage().getWidth() / 2, 40);
}
function initStage(images) {
var stage = new Konva.Stage({
container: 'container',
width: 578,
height: 530
});
var background = new Konva.Layer();
var animalLayer = new Konva.Layer();
var animalShapes = [];
var score = 0;
// image positions
var animals = {
snake: {
x: 10,
y: 70
},
giraffe: {
x: 90,
y: 70
},
monkey: {
x: 275,
y: 70
},
lion: {
x: 400,
y: 70
}
};
var outlines = {
snake_black: {
x: 275,
y: 350
},
giraffe_black: {
x: 390,
y: 250
},
monkey_black: {
x: 300,
y: 420
},
lion_black: {
x: 100,
y: 390
}
};
// create draggable animals
for(var key in animals) {
// anonymous function to induce scope
(function() {
var privKey = key;
var anim = animals[key];
var animal = new Konva.Image({
image: images[key],
x: anim.x,
y: anim.y,
draggable: true
});
animal.on('dragstart', function() {
this.moveToTop();
animalLayer.draw();
});
/*
* check if animal is in the right spot and
* snap into place if it is
*/
animal.on('dragend', function() {
var outline = outlines[privKey + '_black'];
if(!animal.inRightPlace && isNearOutline(animal, outline)) {
animal.position({
x : outline.x,
y : outline.y
});
animalLayer.draw();
animal.inRightPlace = true;
if(++score >= 4) {
var text = 'You win! Enjoy your booty!';
drawBackground(background, images.beach, text);
}
// disable drag and drop
setTimeout(function() {
animal.draggable(false);
}, 50);
}
});
// make animal glow on mouseover
animal.on('mouseover', function() {
animal.image(images[privKey + '_glow']);
animalLayer.draw();
document.body.style.cursor = 'pointer';
});
// return animal on mouseout
animal.on('mouseout', function() {
animal.image(images[privKey]);
animalLayer.draw();
document.body.style.cursor = 'default';
});
animal.on('dragmove', function() {
document.body.style.cursor = 'pointer';
});
animalLayer.add(animal);
animalShapes.push(animal);
})();
}
// create animal outlines
for(var key in outlines) {
// anonymous function to induce scope
(function() {
var imageObj = images[key];
var out = outlines[key];
var outline = new Konva.Image({
image: imageObj,
x: out.x,
y: out.y
});
animalLayer.add(outline);
})();
}
stage.add(background);
stage.add(animalLayer);
drawBackground(background, images.beach, 'Ahoy! Put the animals on the beach!');
}
var sources = {
beach: 'beach.png',
snake: 'snake.png',
snake_glow: 'snake-glow.png',
snake_black: 'snake-black.png',
lion: 'lion.png',
lion_glow: 'lion-glow.png',
lion_black: 'lion-black.png',
monkey: 'monkey.png',
monkey_glow: 'monkey-glow.png',
monkey_black: 'monkey-black.png',
giraffe: 'giraffe.png',
giraffe_glow: 'giraffe-glow.png',
giraffe_black: 'giraffe-black.png'
};
loadImages(sources, initStage);
</script>
as we can see in this example Animals_on_the_Beach_Game the animal's images are drag-able and can be drop ever where.... but I want to change it in the way that it just can drop on the specific place ... what can I do ?
thank you :)
This is more of a design question, as letting go of the mouse button isn't something you can prevent. It would also be non-intuitive to keep the image attached to the mouse position as you would then need a new mouse event to associate with dropping it. What I've done for a drag and drop UI was to either (1) destroy the dropped shape, or if that wasn't an option, (2) animate the shape back (i.e. snap back) to its original position. Alternatively, you might (3) find the closest likely valid drop target and snap to that location.
First you define lionOrigin, that maybe you already have.
You have to implement the call on the dragend event of the object dragged, so let's say the lion. You have to check position of the lion in relation to the end desired position, let's call it lionDestiny. That can be done with a simple grometry: calculate the distance between to point. We do that with distanceA2B() function.
Now you can establish an offset inside wich you can snap the object, as it is close enough. If the minimal offset is not achieved, then you place the lion back on lionOrigin.
Al last, in konvajs you can use .x() and .y() to easily get or set position to lion.
Something like this:
var lionOrigin = [50,50];
var lionDestiny = [200,200];
var offset = 20;
distanceA2B(a,b) {
return Math.sqrt( ((a[0]-b[0])*(a[0]-b[0])) + ((a[1]-b[1])*(a[1]-b[1])) );
}
lion.on('dragend', (e) => {
var d = distanceA2B([lion.x(),lion.y()],lionDestiny);
if(d<offset){
lion.x(lionDestiny[0]);
lion.y(lionDestiny[1]);
}else{
lion.x(lionOrigin[0]);
lion.y(lionOrigin[1]);
}
});
Hope this helps!
It would have been better if you could explain your question more when you say you want to move any shape to a specific position. Though konva.js provides you with various events through which you can do this. For example, suppose you want to interchange the location of two shapes when you drag and move the first shape to the second and drop it there. In this case, you can use dragend event of konva. So when you move the target element to another element and drop it there, check if they are intersecting each other or not and then interchange their coordinates with each other.
Here is the function to find the intersection between two elements:
haveIntersection(r1, r2) {
return !(
r2.x > r1.x + r1.width ||
r2.x + r2.width < r1.x ||
r2.y > r1.y + r1.height ||
r2.y + r2.height < r1.y
);
}
And from here, you can try to understand the functionality. Though it's in nuxt.js but the events and scripts would be almost same if you are using only javascript. You can find sample code with an explanation for replacing the location of two shapes with each other. So even if you don't want to replace the locations but you want to move your target element to any position this will make you understand how to do this.

Check if image is valid

I want to check, if a given image file is valid (means displayable in a browser window and not getting a indicating, that the image cannot be displayed. This is the code I tried:
var bWindow;
bWindow = new electron.BrowserWindow({"show": false});
bWindow.loadURL("file://defect.png");
bWindow.webContents.on("did-fail-load", function (event, errCode, errDescription) {
console.log("error", errDescription);
});
bWindow.webContents.on("did-finish-load", function () {
console.log("success");
});
expecting to get "error". But I always get "success". How can I achieve what I want?
Try-
function checkImage(imageSrc, good, bad) {
var img = new Image();
img.onload = good;
img.onerror = bad;
img.src = imageSrc;
}
checkImage("foo.gif", function(){ alert("good"); }, function(){ alert("bad"); } );

Resources