React Native HTML to PDF not displaying local images - ios

In case you guys don't know, there was a problem previously with this library not rendering local images on Android as well, but apparently it was solved. Now, I'm facing the exact same issue on iOS, with a difference that I can use static images like assets/src/assets/images/logo.png. But when the images start with something like file:///, storage://, ph:// it simply does not get rendered.
What I'm doing is trying to generate a PDF report file, which must be generated independently the user has an internet connection or not. That is the reason why I have to use local images.
The static image is the logo of the company, and the local image which is not getting rendered is an image saved to the phone's storage through Image Picker or Camera Roll. The React Native Image component displays the image perfectly, so I don't think I'm using a wrong path.
What I have tried so far:
Removing the file:/// or storage:// or ph:// from the beginning of the path string;
In some cases, when I save an image to the phone's library with Camera Roll, it will return a path that starts with ph:// but without an extension, such as .jpg or .png. I tried to put the extension manually, and still does not make any difference;
I tried to convert the image to base64 using rn-fetch-blob (with RNFetchBlob.fs.base64.encode(path)), but still got no success.
Devices:
iPhone SE with iOS 14 (also simulator iPhone 11 with iOS 15)
MacBook Air 2017 Core i5 1.8GHz and 8gb RAM (macOS Big Sur 11.5.2)
Environment
node: 12.22.7
npm: 6.14.15
react: 16.9.0
react-native: 0.61.5
react-native-html-to-pdf: ^0.11.0 (updating it to 0.12.0 also got me the same result)
Code:
sharePDF = async () => {
try {
this.changeVisibilityOptions(false);
this.changeVisibilityLoading('Gerando PDF...');
let htmlTemplate = '';
htmlTemplate = await getPDFDespesa(this.state);
const pdfOptions = {
html: htmlTemplate,
fileName: 'RelatorioDespesas',
directory: 'Relatorios'
};
let pdfFile = await RNHTMLtoPDF.convert(pdfOptions);
this.changeVisibilityLoading(false);
const shareOptions = {
title: 'Compartilhar com:',
url: `file://${pdfFile.filePath}`,
type: 'application/pdf',
failOnCancel: false
};
const ShareResponse = await Share.open(shareOptions);
} catch (error) {
this.setState({ visibilityLoadingScreen: false });
console.log('Error =>', error);
}
}
Final thoughts:
Well, since the code is stored at a private repository, I can't show the whole thing here for ethical reasons. But I'm doing my best to give you as much details as possible.
The output the code produces an almost complete PDF, with the only point that I see broken image icons where the images were supposed to be. For Android it works perfectly now.
I think this might be an issue related to WebView, since react-native-html-to-pdf uses WebView to generate the PDF from HTML code. I reached this conclusion after another developer at my job was trying to create a screen with a preview of the PDF before it could be shared got the very same problem for both Android and iOS. The library he used was react-native-webview.

Update with solution
Alright guys, after a long time of research, me and a colleague got to a solution which may not be the best but does what we expected.
First of all, one thing that was discovered is that we have to divide the problem in two, because we actually had two problems.
Images from react-native-image-picker: After a long time trying to find the problem which was preventing the local images from getting rendered, I tried updating the library to version 4.7.3 (latest version at that day) and did a number of required changes to the code, as the version we were using was considerably aged. Well, it happened to work out for my surprise, even with the response uri's format not being changed;
Images from #react-native-community/cameraroll: This one was a bit more complicated. It took me some time to realize that the iOS' PHAsset was not supported in the WebView or react-native-html-to-pdf (which uses WebView in background). So, after some research, me and my colleague found a workaround that lead us to a relatively easy solution. Basically we used react-native-fs to copy the PHAsset media file to a temporary directory, which would return a uri that started with file:// and could be rendered by WebView. That's the code we used to do this:
export default function getImageNameFromUrl(imageUrl = "") {
if (imageUrl) {
const splittedImageUrl = imageUrl.split('/');
return splittedImageUrl.pop();
}
return null;
};
export default async function copyAssetsFileIOSAndReturnURI(remoteURL = '', localURI = '') {
try {
if (remoteURL && localURI) {
const imageName = getImageNameFromUrl(remoteURL);
const imgPath = await RNFS.copyAssetsFileIOS(localURI, RNFS.TemporaryDirectoryPath+imageName, 0, 0);
return imgPath;
}
return null;
} catch (err) {
console.log(err);
return null;
}
}

Related

Why Flutter (dart) is so slow to process images?

I have this method, that loads a photo from the user's phone to memory, resizes it and encodes it as JPEG:
List<int> processPersonProfilePhoto(File file) {
var rawPhoto = file.readAsBytesSync();
var jpg = Image.decodeJpg(rawPhoto);
jpg = Image.copyResize(jpg, 512);
return Image.encodeJpg(jpg, quality: 70);
}
I am running the method above on a separated isolate, via:
var jpgByteArray = await compute(processPersonProfilePhoto, file);
This whole process sometimes takes 20 - 30 seconds (the release mode is even worse than debug) and I am running on middle and high end devices.
This is the package I am using to process:
image: ^2.0.7
What am I doing wrong? How to improve that?
PS.: I done some more debugging and realized that the line that takes much longer to process is the deconding one:
var jpg = Image.decodeJpg(rawPhoto);
I found out that this is a library issue. You can track the problem on the lib's github page, there are a lot of issues complaining about the slowness. (https://github.com/brendan-duncan/image/issues/104)
Until it gets fixed, use that library instead:
https://pub.dartlang.org/packages/flutter_image_compress
Edit: Seems now that the library has fixed the slowness problem on RELEASE builds: https://github.com/brendan-duncan/image/issues/104#issuecomment-490794535

How to copy from Storage to FileSystemStorage in Codenameone and display in BrowserComponent

I've been reading a lot of StackOverflow posts that discuss copying data from FileSystemStorage to Storage in CodenameOne, such as described in this answer from Shai, as seen below:
InputStream stream =
FileSystemStorage.getInstance().openInputStream(i);
OutputStream out =
Storage.getInstance().createOutputStream("MyImage");
Util.copy(stream, out);
Util.cleanup(stream);
Util.cleanup(out);`
I've been trying to do the reverse: save from Storage to FileSystemStorage in order to show a PDF in the BrowserComponent (while using iOS), but have not been able to do so. I need to show the PDF within the app (so I don't want to use Display.getInstance().execute()).
Basically, I'm trying to dynamically populate a Container with whatever files the user selects-- I am using the FileChooser library for CN1 from Steve Hannah. (Disclaimer: I have made slight modifications to this library as it used in the app I'm working on-- HOWEVER, when I choose images with this library and pull them from Storage to an Image via InputStream, they display perfectly in an ImageViewer so I know that all files are being saved correctly in Storage.)
Here is my code (with help from Steve Hannah's comment on GitHub):
//fileLocation and fileName are slightly different but both end with file extension
File file = new File(fileToUpload.getFileName());
FileSystemStorage fss = FileSystemStorage.getInstance();
InputStream is = Storage.getInstance().createInputStream(fileToUpload.getLocation());
OutputStream os = fss.openOutputStream(file.getAbsolutePath());
Util.copy(is, os);
ToastBar.Status status = ToastBar.getInstance().createStatus();
String message = file.exists() + " " + file.isFile() + file.getAbsolutePath();
status.setMessage(message);
status.setExpires(3000);
status.show();
NativeLogs.getNativeLogs();
if (Display.getInstance().getPlatformName().equals("ios")) {
//Log.p("in ios !!!!");
BrowserComponent browserComponent = new BrowserComponent();
browserComponent.setURL(file.getPath());
horizontalContainer.add(browserComponent);
}
The ToastBar displays true and true for file.exists() and file.isFile().
I stipulate iOS because as far as I've seen while researching previewing PDFs within an app, I've seen that Android needs to have a different implementation, like adding a NativeInterface with an Android library. I also saw in different answers on the Google Group that this functionality (using browserComponent to view PDFs) is only available for iOS and not on the simulator. In the simulator, I see a blank space. My iPhone just freezes and/or crashes after displaying the ToastBar (and I work on a Windows machine, so not much ability to see native logs....)
What can I do to access the file and show it in the BrowserComponent?
Thank you!
Simple solution -- the file had a space in it (eg. "Test page.pdf") and didn't show! When I used files that didn't have spaces this worked and after removing spaces in the file names, thankfully everything worked. I'll have to add code to handle this scenario.
Thanks for your help!

How to render the autodesk model in offline mode in iOS?

I'm making an ios app and have used the forge api to render the model in the webview. I'm using React Native but i'm open to suggestions for objective-c too.
The problem i'm having is to render the model in offline mode. The app could download svf file and save on the phone's disk, but how can i load the file and render that in the webview? I find the A360 app can render the model in offline mode but not sure if it's rendered in webview. I also find the Autodesk.Viewing.FileLoader api, but couldn't figure out with it and i don't find an example. This is what I have tried:
var viewer = new Autodesk.Viewing.Private.GuiViewer3D(document.getElementById('MyViewerDiv'));
var filepath = '/Users/zheminzhang/Library/Developer/CoreSimulator/Devices/901B40DC-1DB6-4596-A24A-6D3FAA1EA5C5/data/Containers/Data/Application/925466F3-A623-416B-81A4-1EB62123AA66/Documents/RNFetchBlob_tmp/RNFetchBlobTmp_d0411119-5006-4529-8734-7e70764a309b.svf';
var options = {
doc: filepath,
env: 'Local'
};
Autodesk.Viewing.Initializer (options, function () {
viewer.start(options.doc, options);
// viewer.initialize();
// var fileloader = new Autodesk.Viewing.FileLoader(viewer);
// fileloader.loadFile(filepath);
});
Can anyone help please?
I happened to notice this post. I am not sure how useful it would be, just sharing what I have practiced with iOS + Forge Viewer.
https://forge.autodesk.com/blog/standalone-app-of-forge-viewer-on-ios-by-cordova

How to move/copy a file from one one location to the other using phonegap

I am using the Phonegap File API but am having trouble copying a file from one location to the other.
my targeted device is iOS(ipad)
my iOS version is 9.2.1
I am working on windows platform
I am using phonegap build
my requirement :
I am capturing a video and its getting saved in some temp folder by default here is the location where its getting saved (/private/var/mobile/Containers/Data/Application/2183897E-3660-4145-A822-76F5B763E48D/tmp/capture-T0x17e7cae0.tmp.avqBi2/capturedvideo.MOV)
So I just want to move this (capturedvideo.MOV) video to photo album location in my ipad
This is my code which i am trying to make it work.
function success(entry) {
console.log("New Path: " + entry.fullPath);
}
function fail(error) {
alert(error.code);
}
function moveDir(entry) {
var parent = document.getElementById('parent').value,
parentName = parent.substring(parent.lastIndexOf('/')+1),
newName = document.getElementById('newName').value,
parentEntry = new DirectoryEntry(parentName, parent);
// move the directory to a new directory and rename it
entry.moveTo(saveToPhotoAlbum, newName, success, fail);
}
Any help or working examples would be great.
Thanks
Nik`
This is simply not possible on iOS, because Apple does not allow to move videos, images or any other files out of the app sandbox to other folders. The reason that the standard-camera on the iPad/iPhone is able to place pictures there is simply because this is a built-in feature that has special permissions which your third-party app will not be able to get.
This is of course only true as long as the device is not jailbroken.
For more infos, especially concerning the file-system layout and what you are allowed to store, you should take a look at the cordova-file-plugin Readme on Github:https://github.com/apache/cordova-plugin-file

Read EXIF orientation tag from a CameraRoll loaded image on iOS8

I'm working on an iOS8 iPhone app with Adobe Air and I would like to read the EXIF data for the image orientation from a CameraRoll loaded image.
I'm using this AS3 EXIF library: https://github.com/bashi/exif-as3
The provided example works for images that got loaded through an URLRequest but not for CameraRoll loaded images. (trace for EXIF = null)
This is my code:
import jp.shichiseki.exif.*;
var loaderCameraRoll:ExifLoader
var deviceCameraRoll:CameraRoll
function loadImageFromCameraRoll(e:Event=null):void {
deviceCameraRoll = new CameraRoll();
deviceCameraRoll.addEventListener(MediaEvent.SELECT, onSelectCameraRoll);
deviceCameraRoll.browseForImage();
}
function removeBrowseListeners():void {
deviceCameraRoll.removeEventListener(MediaEvent.SELECT, onSelectCameraRoll);
}
function onSelectCameraRoll(event:MediaEvent):void {
var promise:MediaPromise = event.data as MediaPromise;
loaderCameraRoll = new ExifLoader()
loaderCameraRoll.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadedCameraRoll);
loaderCameraRoll.loadFilePromise(promise);
}
function onLoadedCameraRoll(event:Event):void {
trace(loaderCameraRoll.exif.ifds.primary)
}
How to get the EXIF orientation value from a CameraRoll loaded image?
try using a Native Extenson this is a great option:
https://github.com/digicrafts/ANE-ImagePicker
if (AirImagePicker.getInstance().isCameraAvailable())
{
AirImagePicker.getInstance().displayCamera(function(status:String, ...mediaArgs):void {
// Do something with the Media information returned
});
}
// Pick an image from the gallery
if (AirImagePicker.getInstance().isImagePickerAvailable())
{
AirImagePicker.getInstance().displayImagePicker(function(status:String, ...mediaArgs):void {
// Do something with the Media information returned
});
}
Jay, I found a article that solved your problem perfectly, which works on both iOS and Android.
http://blogs.adobe.com/cantrell/archives/2011/10/parsing-exif-data-from-images-on-mobile-devices.html
And in the same time, I would like to ask you a question about uploading files from Air on iOS device. Did you try to upload the image file to somewhere? If you did, Which version of iOS and which version of Air SDK are you using? Did you do some configuration in the xxx-app.xml file?
I tried the solution in www.adobe.com/devnet/air/articles/uploading-images-media-promise.html , but the File.upload(URLRequest) did not work on iOS8. File.upload did not even send any request to the server side. It seems that the Air SDK for iOS just failed to send the http request for some reason.
You can go to the following link to see my problem about this.
ActionScript's File.upload does not work on Air SDK for iOS devices
My sample code is uploaded to github.
github.com/bluewindjava8/actionscript_file_upload_for_ios
Will you please check it for me if you have time. And will you please send me your code if it is ok? Thank you very much.

Resources