Am working on a winjs based barcode reader application. Initially I will capture the image using camera capture API and will pass that file object to a canvas element and read its barcode using ZXing library. But the image passed to the canvas is not getting rendered completely as follows.
Following is my html code
<body>
<p>Decoding test for static images</p>
<canvas id="canvasDecode" height="200" width="200"></canvas>
<h3 id="result"></h3>
<p>Put some content here and leave the text box</p>
<input id="input" class="win-textarea" onchange="generate_barcode()">
<h3 id="content"></h3>
<canvas id="canvasEncode" height="200" width="200"></canvas>
<img class="imageHolder" id="capturedPhoto" alt="image holder" />
</body>
following is my javascript code
(function () {
"use strict";
WinJS.Binding.optimizeBindingReferences = true;
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
// TODO: This application has been newly launched. Initialize
// your application here.
var dialog = new Windows.Media.Capture.CameraCaptureUI();
var aspectRatio = { width: 1, height: 1 };
dialog.photoSettings.croppedAspectRatio = aspectRatio;
dialog.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo).then(function (file) {
if (file) {
// draw the image
var canvas = document.getElementById('canvasDecode')
var ctx = canvas.getContext('2d');
var img = new Image;
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
}
img.src = URL.createObjectURL(file);
// open a stream from the image
return file.openAsync(Windows.Storage.FileAccessMode.readWrite);
}
})
.then(function (stream) {
if (stream) {
// create a decoder from the image stream
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(stream);
}
})
.done(function (decoder) {
if (decoder) {
// get the raw pixel data from the decoder
decoder.getPixelDataAsync().then(function (pixelDataProvider) {
var rawPixels = pixelDataProvider.detachPixelData();
var pixels, format; // Assign these in the below switch block.
switch (decoder.bitmapPixelFormat) {
case Windows.Graphics.Imaging.BitmapPixelFormat.rgba16:
// Allocate a typed array with the raw pixel data
var pixelBufferView_U8 = new Uint8Array(rawPixels);
// Uint16Array provides a typed view into the raw 8 bit pixel data.
pixels = new Uint16Array(pixelBufferView_U8.buffer);
if (decoder.bitmapAlphaMode == Windows.Graphics.Imaging.BitmapAlphaMode.straight)
format = ZXing.BitmapFormat.rgba32;
else
format = ZXing.BitmapFormat.rgb32;
break;
case Windows.Graphics.Imaging.BitmapPixelFormat.rgba8:
// For 8 bit pixel formats, just use the returned pixel array.
pixels = rawPixels;
if (decoder.bitmapAlphaMode == Windows.Graphics.Imaging.BitmapAlphaMode.straight)
format = ZXing.BitmapFormat.rgba32;
else
format = ZXing.BitmapFormat.rgb32;
break;
case Windows.Graphics.Imaging.BitmapPixelFormat.bgra8:
// For 8 bit pixel formats, just use the returned pixel array.
pixels = rawPixels;
if (decoder.bitmapAlphaMode == Windows.Graphics.Imaging.BitmapAlphaMode.straight)
format = ZXing.BitmapFormat.bgra32;
else
format = ZXing.BitmapFormat.bgr32;
break;
}
// create a barcode reader
var reader = new ZXing.BarcodeReader();
reader.onresultpointfound = function (resultPoint) {
// do something with the resultpoint location
}
// try to decode the raw pixel data
var result = reader.decode(pixels, decoder.pixelWidth, decoder.pixelHeight, format);
// show the result
if (result) {
document.getElementById("result").innerText = result.text;
}
else {
document.getElementById("result").innerText = "no barcode found";
}
});
}
});
} else {
// TODO: This application has been reactivated from suspension.
// Restore application state here.
}
args.setPromise(WinJS.UI.processAll());
}
};
app.oncheckpoint = function (args) {
// TODO: This application is about to be suspended. Save any state
// that needs to persist across suspensions here. You might use the
// WinJS.Application.sessionState object, which is automatically
// saved and restored across suspension. If you need to complete an
// asynchronous operation before your application is suspended, call
// args.setPromise().
};
app.start();
})();
function generate_barcode() {
// get the content which the user puts into the textbox
var content = document.getElementById("input").value;
// create the barcode writer and set some options
var writer = new ZXing.BarcodeWriter();
writer.options = new ZXing.Common.EncodingOptions();
writer.options.height = 200;
writer.options.width = 200;
writer.format = ZXing.BarcodeFormat.qr_CODE;
// encode the content to a byte array with 4 byte per pixel as BGRA
var imagePixelData = writer.write(content);
// draw the pixel data to the canvas
var ctx = document.getElementById('canvasEncode').getContext('2d');
var imageData = ctx.createImageData(imagePixelData.width, imagePixelData.heigth);
var pixel = imagePixelData.pixel
for (var index = 0; index < pixel.length; index++) {
imageData.data[index] = pixel[index];
}
ctx.putImageData(imageData, 0, 0);
}
The same code worked well when I was using the file picker API. Let me knew where I went wrong.
I think that you're running into some problems with asynchronicity here. I applaud your use of chained calls to then(), but there's a hidden problem - assignment to img.src begins an asynchronous operation while the image is loaded. Your code continues on BEFORE the img.onload event has been raised, and so the closure which img.onload reaches into for the img variable (the pointer to the file URL) changes before the image has fully loaded.
Here's some code that worked for me.
// Inside handler for app.activated ...
var dialog = new Windows.Media.Capture.CameraCaptureUI();
var aspectRatio = { width: 1, height: 1 };
dialog.photoSettings.croppedAspectRatio = aspectRatio;
dialog.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo)
.then(function (file) {
// draw the image
var canvas = document.getElementById('canvasDecode')
var ctx = canvas.getContext('2d');
var img = new Image;
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
// open a stream from the image
decodePic(file);
}
img.onerror = function (err) {
WinJS.log && WinJS.log("Error loading image");
}
img.src = URL.createObjectURL(file);
});
And then I moved the file decoding / barcode reading stuff to a new function.
function decodePic(file) {
file.openAsync(Windows.Storage.FileAccessMode.readWrite)
.then(function (stream) {
if (stream) {
// create a decoder from the image stream
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(stream);
}
})
.done(function (decoder) {
if (decoder) {
// get the raw pixel data from the decoder
decoder.getPixelDataAsync().then(function (pixelDataProvider) {
// YOUR BARCODE READING CODE HERE.
});
}
});
}
I hope this helps!
Related
I am trying to generate a pdf report including current map screenshot of ESRI map using JavaScript. Here is my code.
var getCurrentMapScreenShot = function (success, error) {
esriLoader.Config.defaults.io.proxyUrl = myAppSettingsModel.SettingsModel.MapSettings.AGSProxyURL;
esriLoader.Config.defaults.io.alwaysUseProxy = true;
var printTask = new esriLoader.PrintTask("myexportUrl");
var template = new esriLoader.PrintTemplate();
template.exportOptions = {
width: 600,
height: 600,
dpi: 96
};
template.format = "image/png";
template.layout = "MAP_ONLY",
template.preserveScale = true;
template.layoutOptions = {
legendLayers: [], // empty array means no legend
scalebarUnit: "Miles"
};
var params = new esriLoader.PrintParameters();
params.map = map;
params.template = template;
printTask.execute(params, success, error);
}
This function will give you an event that contains a url , Then I am passing this url to get map base64 data.
Here is the function calling.
map.GetCurrentMapScreenShot(function (event) {
var mapScreenShotURL = event.url;
Factory.GetBase64ForImgUrl(mapScreenShotURL,
function (mapImageBase64Encoded) {});
and here is the function that converts url to base64image
function getBase64ForImgUrl(url, callback, outputFormat) {
console.log("#################### Summary Report Image " + url);
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
var img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL(outputFormat || 'image/png');
callback.call(this, dataURL);
// Clean up
canvas = null;
};
img.src = url;
}
I am getting base64 image data of the map , But the problem is that, I am getting blurred image of the map with no feature layers and no icons..And also map contains some junk texts.
Thanks
I resolved the issue almost, Now I am getting the map image that contains map Icons and layer graphics, But unfortunately still getting some junk texts.
I have changed the code as follows.
template.format = "JPG";
template.layout = "A4 Landscape",
template.preserveScale = false;
template.layoutOptions = {
"legendLayers": [], // empty array means no legend
"scalebarUnit": "Miles"
}
I have built an angularJS application, in this application SVG files represent garments that a user chooses. I have a download button which (currently) saves the first SVG as a PNG into a database and I use a view to display this "preview".
The directive I created looks like this:
.directive('kdExport', function () {
return {
restrict: 'A',
scope: {
target: '#kdExport',
team: '='
},
controller: 'ExportImageController',
link: function (scope, element, attrs, controller) {
console.log(scope.team);
// Bind to the onclick event of our button
element.bind('click', function (e) {
// Prevent the default action
e.preventDefault();
// Generate the image
controller.generateImage(scope.target, scope.team, function (preview) {
// Create our url
var url = '/kits/preview/' + preview.id;
// Open a new window
window.open(url, '_blank');
});
});
}
};
})
and the controller looks like this:
.controller('ExportImageController', ['PreviewService', function (service) {
var self = this;
// Function to remove the hidden layers of an SVG document
var removeHidden = function (element) {
// Get the element children
var children = element.children(),
i = children.length;
// If we have any children
if (children.length) {
// For each child
for (i; i >= 0; i--) {
// Get our child
var child = angular.element(children[i - 1]);
// Remove hidden from the child's children
removeHidden(child);
// Finally, if this child has the class "hidden"
if (child.hasClass("hidden")) {
// Remove the child
child.remove();
}
}
}
};
// Public function to generate the image
self.generateImage = function (element, team, onSuccess) {
// Get our SVG
var target = document.getElementById(element),
container = target.getElementsByClassName('svg-document')[0],
clone = container.cloneNode(true);
// Remove hidden layers
removeHidden(angular.element(clone));
// Create our data
var data = clone.innerHTML,
svg = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
// Get our context
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
// Create our image
var DOMURL = window.URL || window.webkitURL || window,
url = DOMURL.createObjectURL(svg),
img = new Image();
// When the image has loaded
img.onload = function () {
canvas.width = 1000;
canvas.height = 500;
// Draw our image using the context
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 1000, 500);
DOMURL.revokeObjectURL(url);
// Get our URL as a base64 string
var dataURL = canvas.toDataURL("image/png");
// Create our model
var model = {
teamName: team.name,
sport: team.sport,
data: dataURL
};
// Create our preview
service.create(model).then(function (response) {
// Invoke our success callback
onSuccess(response);
});
}
// Set the URL of the image
img.src = url;
};
}])
This works fine for a single SVG document, but now the client has asked me to do this for multiple SVGs with a title under each one and they want it all in one PNG.
I have not done a lot of work with canvasing, so I am not sure if this can be done.
Does anyone know how I might achieve this?
Ok, so I figured this out myself using promises.
Basically I created a method called drawImage that allowed me to draw an image for each SVG.
To make sure that all images were drawn before I invoke toDataURL I made the function return a promise and once the image loaded I resolved that promise.
Then I just used a $q.all to get the dataURL and save the data to my database.
The methods looked like this:
// Private function for drawing our images
var drawImage = function (canvas, ctx, clone) {
// Defer our promise
var deferred = $q.defer();
// Create our data
var data = clone.innerHTML,
svg = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
// Create our image
var DOMURL = window.URL || window.webkitURL || window,
url = DOMURL.createObjectURL(svg),
img = new Image();
// When the image has loaded
img.onload = function () {
// Get our location
getNextLocation(canvas.width, canvas.height, img);
// Draw our image using the context (Only draws half the image because I don't want to show the back)
ctx.drawImage(img, 0, 0, img.width / 2, img.height, location.x, location.y, location.width, location.height);
DOMURL.revokeObjectURL(url);
// Resolve our promise
deferred.resolve();
}
// Set the URL of the image
img.src = url;
// Return our promise
return deferred.promise;
};
// Public function to generate the image
self.generateImage = function (element, team, onSuccess) {
// Get our SVG
var target = document.getElementById('totals'),
containers = angular.element(target.getElementsByClassName('svg-document'));
// Get our context
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
// Set our canvas height and width
canvas.width = 2000;
canvas.height = calculateCanvasHeight(containers.length);
// Create our array of promises
var promises = [];
// For each container
for (var i = 0; i < containers.length; i++) {
// Get our container
var container = containers[i],
clone = container.cloneNode(true);
// Remove hidden layers
removeHidden(angular.element(clone));
// Add our promise to the array
promises.push(drawImage(canvas, ctx, clone));
}
// When all promises have resolve
$q.all(promises).then(function () {
// Get our URL as a base64 string
var dataURL = canvas.toDataURL("image/png");
// Create our model
var model = {
teamName: team.name,
sport: team.sport,
data: dataURL
};
// Create our preview
self.create(model).then(function (response) {
// Invoke our success callback
onSuccess(response);
});
})
};
Obviously there is missing code here, but this code answers my issue, the rest just makes my service work :)
I am using the code in http://jsfiddle.net/epistemex/LUNaJ/
PDFJS.disableWorker = true; // due to CORS
var canvas = document.createElement('canvas'), // single off-screen canvas
ctx = canvas.getContext('2d'), // to render to
pages = [],
currentPage = 1,
url = 'http://www.corsproxy.com/www.ohio.edu/technology/training/upload/Java-Script-Reference-Guide.pdf';
PDFJS.getDocument(url).then(function (pdf) {
PROGRESS.max = pdf.numPages; // just for demo
PROGRESS.value = 1; // just for demo
// init parsing of first page
if (currentPage <= pdf.numPages) getPage();
// main entry point/function for loop
function getPage() {
// when promise is returned do as usual
pdf.getPage(currentPage).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
// now, tap into the returned promise from render:
page.render(renderContext).then(function() {
// store compressed image data in array
pages.push(canvas.toDataURL());
if (currentPage < pdf.numPages) {
currentPage++;
PROGRESS.value = currentPage; // just for demo
getPage(); // get next page
}
else {
done(); // call done() when all pages are parsed
}
});
});
}
});
function done() {
// NOTE: Just for demo - correct order is not guaranteed here
// as the drawPage is async. use same method as above to make
// sure the order is correct (not for-loop, but use the callback
// to get next page). To present a single page it won't be
// a problem though... (just use drawPage() directly)
for(var i = 0; i < pages.length; i++) {
drawPage(i, addPage);
}
}
function addPage(img) {
img.style.width = '100px';
img.style.height = '120px';
document.body.appendChild(img);
}
function drawPage(index, callback) {
var img = new Image;
img.onload = function() {
ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
callback(this); // invoke callback when we're done
}
img.src = pages[index]; // start loading the data-uri as source
}
to render pdf pages to canvas. The problem with this is that it takes along time if the pdf file has large number of files. Is it possible to cache/save these generated files in the users computer/bowser so that if he runs the code a secondary time, he doesn't have to generate them again and instead they can be displayed immediately.
No, dataURI are not "cached" by browser since there is no download involved.
What you can do however, is to store all your pages into a globally accessible array, and check if you already have it before you call PDFJS.getDocument(url) :
PDFJS.disableWorker = true; // due to CORS
var canvas = document.createElement('canvas'), // single off-screen canvas
ctx = canvas.getContext('2d'), // to render to
docs = {}, // an object that will store our pdf documents
urls = ["https://www.ohio.edu/technology/training/upload/html-tag-reference-guide.pdf", "https://www.ohio.edu/technology/training/upload/Java-Script-Reference-Guide.pdf"];
btn0.onclick = getDoc;
btn1.onclick = getDoc;
function getDoc() {
// get the doc's url
var url = urls[+this.id.split('btn')[1]];
// clear the result div
result.innerHTML = '';
// we already have it
if (docs[url]) {
// simply call the callback
done(docs[url]);
}
else {
// create our array for this document
docs[url] = [];
// download and parse the doc
PDFJS.getDocument(url).then(function(pdf) {
PROGRESS.max = pdf.numPages; // just for demo
PROGRESS.value = 1; // just for demo
var currentPage = 1;
// init parsing of first page
if (currentPage <= pdf.numPages) getPage();
// main entry point/function for loop
function getPage() {
// when promise is returned do as usual
pdf.getPage(currentPage).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
// now, tap into the returned promise from render:
page.render(renderContext).then(function() {
// store compressed image data in array
docs[url].push(canvas.toDataURL());
if (currentPage < pdf.numPages) {
currentPage++;
PROGRESS.value = currentPage; // just for demo
getPage(); // get next page
} else {
done(docs[url]); // call done() when all pages are parsed
}
});
});
}
});
}
}
function done(pages) {
for (var i = 0; i < pages.length; i++) {
drawPage(pages[i], addPage);
}
}
function addPage(img) {
img.style.width = '100px';
img.style.height = '120px';
result.appendChild(img);
}
function drawPage(dataURI, callback) {
var img = new Image;
img.onload = function() {
ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
callback(this); // invoke callback when we're done
}
img.src = dataURI; // start loading the data-uri as source
}
#PROGRESS {
width: 100%
}
<script src="https://rawgit.com/mozilla/pdf.js/gh-pages/build/pdf.js"></script>
<button id="btn0">1st Doc</button>
<button id="btn1">2nd Doc</button>
<progress id="PROGRESS" value=0></progress>
<div id="result"></div>
i've image uploader which using canvas and trying to get orientation using load-image.all.min.js is fine. but when i choose multiple image orientation parsing function saving data not one by one.
which means if i choose 1 image. it transferring data to 'upload_canvas.php?ori='+ori with correct ori variable.
but when i choose multiple image to upload example 3 images (with orientation 1, 1, 8)
it passing data to server upload_canvas.php?ori=8, upload_canvas.php?ori=8, upload_canvas.php?ori=8. only last ori variable.
maybe orientation parsing function already looped before uploading image data to server one by one.
how to transfer image with correct orientation to server?
below my using code.
document.querySelector('form input[type=file]').addEventListener('change', function(event){
// Read files
var files = event.target.files;
var ori = 1;
// Iterate through files
for (var i = 0; i < files.length; i++) {
// Ensure it's an image
if (files[i].type.match(/image.*/)) {
//Get image orienatation
loadImage.parseMetaData(files[i], function (data) {
if (data.exif) {
ori = data.exif.get('Orientation');
console.log("ori: "+ori);
} else {ori = 1;}
});
// Load image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
canvas.width = image.width;
canvas.height = image.height;
drawImageIOSFix(canvas.getContext('2d'),image, 0, 0, image.width, image.height, 0, 0, width, height);
// Upload image
var xhr = new XMLHttpRequest();
if (xhr.upload) {
// Update progress
xhr.upload.addEventListener('progress', function(event) {
var percent = parseInt(event.loaded / event.total * 100);
progressElement.style.width = percent+'%';
}, false);
// File uploaded / failed
xhr.onreadystatechange = function(event) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
//some code
} else {
imageElement.parentNode.removeChild(imageElement);
}
}
}
xhr.open('post', 'upload_canvas.php?t=' + Math.random()+'&ori='+ori, true);
xhr.send(canvas.toDataURL('image/jpeg'));
}
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(files[i]);
}
}
// Clear files
event.target.value = '';});
Your variable ori is a global variable, that is shared between all images. The code in the .onload functions aren't run immediately, but only after your for() loop has gone through all the images. At this point ori will contain the orientation of the last image.
To fix, move the variable and parseMetaData into the reader.onload function.
...
var reader = new FileReader();
reader.onload = function (readerEvent) {
var ori;
loadImage.parseMetaData(files[i], ...)
var image = new Image();
image.onload = function (imageEvent) {
...
Warning: Not tested!
I am creating a Barcode scanner module for Windows 8 Metro App.
I some how success with my logic but suddenly I saw my application crash due to low memory issue.
<script>
var canvas = null;
var ctx = null;
var livePreview = null;
var count = 0,rescount=0;
function takepicture() {
var Capture = Windows.Media.Capture;
livePreview = document.getElementById("live-preview");
var mediaCapture = new Capture.MediaCapture();
canvas = document.getElementById("Vcanvas");
ctx=canvas.getContext('2d');
livePreview.addEventListener('play', function () { var i = window.setInterval(function () { ctx.drawImage(livePreview, 0, 0, canvas.width, canvas.height); scanCanvasEasy(); }, 20); }, false);
livePreview.addEventListener('pause', function () { window.clearInterval(i); }, false);
livePreview.addEventListener('ended', function () { clearInterval(i); }, false);
/*
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
openPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.videosLibrary;
openPicker.fileTypeFilter.replaceAll([".mp4", ".avi", ".ogg"]);
openPicker.pickSingleFileAsync()
.then(function (file) {
if (file) {
// draw the image
var img = new Image;
//img.onload = function () {
// canvas.width = img.width;
// canvas.height = img.height;
// ctx.drawImage(img, 0, 0, img.width, img.height);
// scanCanvasEasy();
//}
//img.src = URL.createObjectURL(file);
// open a stream from the image
livePreview.src = URL.createObjectURL(file);
livePreview.play();
}
})*/
mediaCapture.initializeAsync().then(function () {
livePreview.src = URL.createObjectURL(mediaCapture);
livePreview.play();
});
}
function scanCanvasEasy() {
var imgd = ctx.getImageData(0, 0,canvas.width,canvas.height);
var pix = imgd.data;
var reader = new ZXing.BarcodeReader();
reader.onresultpointfound = function (resultPoint) {
// do something with the resultpoint location
console.log(resultPoint.toString());
}
// try to decode the raw pixel data
var result = reader.decode(pix, canvas.width, canvas.height, ZXing.BitmapFormat.rgba32);
/*
The above line cause that memory issue, without that line there is no change in memory level.
*/
// show the result
if (result) {
document.getElementById("result").innerText ="Result(+"+rescount++ +")==>"+ result.text;
}
else {
document.getElementById("error").innerText = "no barcode found" + count++;
}
}
</script>
I posted the whole code i used here I Just called the takepicture() method from button click event.
var result = reader.decode(pix, canvas.width, canvas.height, ZXing.BitmapFormat.rgba32);
This line cause memory issue.
Thanks in advance.
var reader = new ZXing.BarcodeReader();
Multiple instance of reader cause this issue. Just created one instance of reader and use it for all subsequent scan will fixed that issue.