Nativescript get base64 data from native image - javascript

I try to use https://github.com/bradmartin/nativescript-drawingpad for saving a signature to my backend. But I am simply not capable to find a solution to get some "useful" data from getDrawing(), which returns a native image Object, for example UIImage on iOS.
I would love to "convert" the image data to some base64 (png, or whatever) string and send it to my server.
I tried someting like:
var ImageModule = require("ui/image");
var ImageSourceModule = require("image-source");
elements.drawingpad.getDrawing().then(function(a){
var image = ImageSourceModule.fromNativeSource( a );
api.post("sign", image.toBase64String());
});
I also tried to post simply a like seen in the demo stuff.
I would really love to see a demo of how to get my hands on the "image data" itself.
thanks!

Thanks to #bradmartin I found the solution:
var image = ImageSourceModule.fromNativeSource(a);
var base64 = image.toBase64String('png');

Actually after lots of digging with tons of error, I finally figured it out.
First you have to require the nativescript-imagepicker source module and also the nativescript image source.
var imagepicker = require("nativescript-imagepicker");
var ImageSourceModule = require("tns-core-modules/image-source");
a case where you want to update a user profile and also send a base64 string to your backend for processing
function changeProfileImage(args) {
var page = args.object;
var profile = page.getViewById("profile-avatar");
var context = imagepicker.create({ mode: "single" });
context.authorize().then(function() {
return context.present();
}).then(function(selection) {
profile.background = `url(${selection[0]._android})`;
profile.backgroundRepeat = `no-repeat`;
profile.backgroundSize = `cover`;
ImageSourceModule.fromAsset(selection[0]).then(image => {
var base64 = image.toBase64String('png');
// console.log(base64);
uploadMediaFile(base64);
});
}).catch(function (e) {
// process error
console.log(e);
});
}

Related

Render an image byte stream on angular client side app

I have a NodeJS / Express RESTful API that proxies requests from an Active Directory LDAP Server. I do this because LDAP queries tend to be slow. I use the RESTful API to cache and refresh data intermittently. I recently attempted to add the thumbnail photo. In research it appears the library that I am using ldapjs is converting the native ldap byte array to a string.
Example of what this looks like:
\ufffd\ufffd\ufffd\ufffd\u0000\u0010JFIF\u0000\u0001\u0000\u0001\u0000x\u0000x\u0000\u0000\ufffd\ufffd\u0000\u001fLEAD
Technologies Inc.
V1.01\u0000\ufffd\ufffd\u0000\ufffd\u0000\u0005\u0005\u0005\b\
Due to this fact the image does not render correctly on the angular client app. So based on my research, here are some attempts in correcting this problem:
Convert the string to a byte array using different methods (See code examples)
Modify the ldapjs library to render the data as a byte array in the RESTFUL as in the following, then bind the byte stream to the angular page:
https://github.com/joyent/node-ldapjs/issues/137
https://csjdpw.atlassian.net/wiki/spaces/~Genhan.Chen/pages/235044890/Display+LDAP+thumbnail+photos
html binding:
<div>
<img *ngIf="userImage" [src]="userImage" alt="{{dataSource.sAMAccountName}}">
</div>
controller:
public get userImage() {
let value = null;
if(this.dataSource.thumbnailPhoto) {
const byteArray = this.string2Bin(this.dataSource.thumbnailPhoto);
const image = `data:image/jpeg;base64,${Buffer.from(byteArray).toString('base64')}`;
value = this.domSanitizer.bypassSecurityTrustUrl(image);
}
return value;
}
private string2Bin(str) {
var result = [];
for (var i = 0; i < str.length; i++) {
result.push(str.charCodeAt(i));
}
return result;
}
and
alternate version of controller:
public get userImage() {
let value = null;
if(this.dataSource.thumbnailPhoto) {
const byteArray = new TextEncoder().encode(this.dataSource.thumbnailPhoto);
const image = `data:image/jpeg;base64,${Buffer.from(byteArray).toString('base64')}`;
value = this.domSanitizer.bypassSecurityTrustUrl(image);
}
return value;
}
another alternate version of controller:
public get userImage() {
let value = null;
if(this.dataSource.thumbnailPhoto) {
const blob = new Blob( [Buffer.from(this.dataSource.thumbnailPhoto).toString('base64')], { type: 'image/jpeg' } );
const value = window.URL.createObjectURL(blob);
return value;
}
I expected a rendered image on the angular page but all I get is the non-rendered placeholder.
Here are the versions of the libraries I am using
Angular - 8.0.3
NodeJS - 10.15.0
ldapjs - 1.0.2
I am sure I am missing something, I am just not sure what it is. Any assistance would be appreciated.
So after some guidance provided by #Aritra Chakraborty, I checked the RESTful api source code. It appears to be a problem with a ldapjs library. When using the entry object conversion, it is doing something strange with the data to which it is not usable. I then realized, I had access to the entry raw format which is the byte array . Instead of trying to convert to base64 on the client, I moved this to the API. Then just mapped it back on the client binding and bang it worked.
Here is some example code:
RESTFul api
_client.search(this._search_dn, opts, (error, res) => {
res.on("searchEntry", (entry) => {
let result = {};
result.id = string_service.formatGUID(JSON.parse(JSON.stringify(entry.raw)).objectGUID);
result = Object.assign({}, result, entry.object);
if(entry.raw.thumbnailPhoto) {
result.thumbnailPhoto = entry.raw.thumbnailPhoto.toString('base64');
}
// The previous 3 lines did not exist previously
On the Angular 8 client I simplified the binding:
public get userImage() {
let value = null;
if(this.dataSource.thumbnailPhoto) {
const image = `data:image/jpeg;base64,${this.dataSource.thumbnailPhoto}`;
value = this.domSanitizer.bypassSecurityTrustUrl(image);
}
return value;
}
I hope someone finds some value out of this.

WebKitSubtleCrypto error when using dispatchMessage in Safari Extension

I am attempting to build a Safari extension to share screenshots of webpages but when I try to pass the image back to Swift I get an error that makes Safari unstable and kills my task mid-process.
The idea is that when a user taps a tool bar button any selected text and a screenshot of the webpage are saved. I am trying to pass both of those through the userInfo dictionary. If I run my code as is with the dispatchMessage call commented out I do not see any errors. If I uncomment the dispatch call I see the following error:
WebKitSubtleCrypto is deprecated. Please use SubtleCrypto instead.
Here is my js code:
document.addEventListener("DOMContentLoaded", function(event) {
safari.self.addEventListener("message", handleMessage);
});
function handleMessage(event) {
var selectedText = window.getSelection().toString();
var screenshot;
if (window.top === window) {
html2canvas(document.getElementsByTagName('html')).then(function(canvas) {
screenshot = convertCanvasToImage(canvas);
console.log("canvas image: " + screenshot)
safari.extension.dispatchMessage("test", {"selectedText": selectedText, "screenshot" : canvas});
});
}
}
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
The html2canvas (latest - 0.5.0-beta4) script is in another file packaged with the extension.
Edit 1
After some more testing it looks as though this error only has to do with passing the 'screenshot' object in the messageDipatch call. If I take out screenshot and only pass the selectedText data it works as expected. I have also tried to pass the screenshot through as a canvas instead of running it through the 'convertCanvasToImage()' call but I am getting the same error with that.
The issue ended up being related to how I was init'ing the image data before converting the canvas to a data url.
Going from:
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
to:
function convertCanvasToImage(canvas) {
var imageData = canvas.toDataURL("image/png")
return imageData;
}
resolved the issue.
On the Swift side this is how I am decoding that data (be cautious of the all the forcing in this):
let imageString = userInfo?["screenshot"] as! String
let imageData = NSData.init(contentsOf: NSURL(string: imageString) as! URL)
let image = NSImage(data: imageData as! Data)

How can I get a buffer for a file (image) from CollectionFS

I'm trying to insert an image into a pdf I'm creating server-side with PDFkit. I'm using cfs:dropbox to store my files. Before when I was using cvs:filesystem, it was easy to add the images to my pdf's cause they were right there. Now that they're stored remotely, I'm not sure how to add them, since PDFkit does not support adding images with just the url. It will, however, accept a buffer. How can I get a buffer from my CollectionFS files?
So far I have something like this:
var portrait = Portraits.findOne('vS2yFy4gxXdjTtz5d');
readStream = portrait.createReadStream('portraits');
I tried getting the buffer two ways so far:
First using dataMan, but the last command never comes back:
var dataMan = new DataMan.ReadStream(readStream, portrait.type());
var buffer = Meteor.wrapAsync(Function.prototype.bind(dataMan.getBuffer, dataMan))();
Second buffering the stream manually:
var buffer = new Buffer(0);
readStream.on('readable', function() {
buffer = Buffer.concat([buffer, readStream.read()]);
});
readStream.on('end', function() {
console.log(buffer.toString('base64'));
});
That never seems to come back either. I double-checked my doc to make sure it was there and it has a valid url and the image appears when I put the url in my browser. Am I missing something?
I had to do something similar and since there's no answer to this question, here is how I do it:
// take a cfs file and return a base64 string
var getBase64Data = function(file, callback) {
// callback has the form function (err, res) {}
var readStream = file.createReadStream();
var buffer = [];
readStream.on('data', function(chunk) {
buffer.push(chunk);
});
readStream.on('error', function(err) {
callback(err, null);
});
readStream.on('end', function() {
callback(null, buffer.concat()[0].toString('base64'));
});
};
// wrap it to make it sync
var getBase64DataSync = Meteor.wrapAsync(getBase64Data);
// get a cfs file
var file = Files.findOne();
// get the base64 string
var base64str = getBase64DataSync(file);
// get the buffer from the string
var buffer = new Buffer(base64str, 'base64')
Hope it'll help!

Stream canvas to imgITools to make ico

So on WinXP I have been having a hard time converting a PNG file to an ICO with canvas. I found this method encodeImage. I don't know if it works but it looks promising but I can't figure out how to feed the image I drew on a canvas into imgITools.decodeData.
What should I use for aImageStream and/or aMimeType?
imgTools.decodeImageData(aImageStream, aMimeType, imgContainer);
This is more of my code:
img['asdf'].file = new FileUtils.File(myPathHere);
let iconStream;
try {
let imgTools = Cc["#mozilla.org/image/tools;1"]
.createInstance(Ci.imgITools);
let imgContainer = { value: null };
imgTools.decodeImageData(aImageStream, aMimeType, imgContainer);
iconStream = imgTools.encodeImage(imgContainer.value,
"image/vnd.microsoft.icon",
"format=bmp;bpp=32");
} catch (e) {
alert('failure converting icon ' + e)
throw("processIcon - Failure converting icon (" + e + ")");
}
let outputStream = FileUtils.openSafeFileOutputStream(img['asdf'].file);
NetUtil.asyncCopy(iconStream, outStream, netutilCallback);
Since you're having a canvas already(?), it would be probably easier to use either on of the following canvas methods:
toDataURI/toDataURIHD
toBlob/toBlobHD
mozFetchAsStream
There is also the undocumented -moz-parse-options:, e.g -moz-parse-options:format=bmp;bpp=32. (ref-tests seem to depend on it, so it isn't going away anytime soon I'd think).
So, here is an example for loading stuff into an ArrayBuffer.
(canvas.toBlobHD || canvas.toBlob).call(canvas, function (b) {
var r = new FileReader();
r.onloadend = function () {
// r.result contains the ArrayBuffer.
};
r.readAsArrayBuffer(b);
}, "image/vnd.microsoft.icon", "-moz-parse-options:format=bmp;bpp=32");
Here is a more complete example fiddle creating a 256x256 BMP icon.
Since you likely want to feed that data into js-ctypes, having an ArrayBuffer is great because you can create pointers from it directly, or write it to a file using OS.File.

WinJS, display image from a byte array?

I'm looking for a way to display a JPEG image stored in a byte array. This is a Windows 8 Store App built in Javascript. The byte array is returned from a C# WinRT component. Its type in C# is byte[].
Basically, what I want is to get an object so that I can call:
URL.createObjectURL(bytearray, {oneTimeOnly: true});
upon. Currently this generates a runtime error because the array interface is not supported.
Thanks in advance.
I discovered a far easier method.
var blob = new Blob([bytes], {type: "image/jpg"});
var url = URL.createObjectURL(blob, { oneTimeOnly: true });
Blob is actually supported directly by URL.createObjectURL. The only catch is that you have to specify a mime format to identify the buffer format, which in my case is possible.
Current solution I found is quite round about. Hence, before giving the solution for bytes to URL, few comments:
If there is better way to get to DOM stream/blob object from bytes, try out.
If you control the winrt component - check if you can return StorageFile object. In that case - code will simplifyto
var file = MSApp.createFileFromStorageFile(storageFile);
var url = URL.createObjectURL(file, { oneTimeOnly: true });
now solution:
var bytes;
var memoryStream;
var streams = Windows.Storage.Streams;
{
// get IBuffer out of bytes
var dataWriter = new streams.DataWriter();
dataWriter.writeBytes(bytes);
var buffer = dataWriter.detachBuffer();
dataWriter.close();
// get IInputStream out of IBuffer
memoryStream = new streams.InMemoryRandomAccessStream();
return memoryStream.writeAsync(buffer);
}).then(function onmemorystreamwritecomplete()
{
var inputStream = memoryStream.getInputStreamAt(0);
// get DOM MSStream from IInputStream
var domMStream = MSApp.createStreamFromInputStream('image/jpg', inputStream);
var url = URL.createObjectURL(domMStream, { oneTimeOnly: true });
memoryStream.close();
test3Img.setAttribute('src', url);
})

Categories