html2pdf creates a blank PDF and somethings shows a security error 'The operation is insecure' on iOS 16.x.x - jspdf

The html2pdf.js creates a Blank PDF or throws an error 'The operation is insecure' on new iOS 16.0.2.
Below is the html2pdf config:
function generatePDF(true) {
return new Promise(function(resolve, reject) {
var element = document.getElementById("main");
var options = {
filename: 'document.pdf',
image: {
type: 'jpeg',
quality: 0.5
},
html2canvas: {
scrollX: 0,
scrollY: 0,
scale: window.devicePixelRatio && window.devicePixelRatio > 1 ? 0.8 : 1
},
jsPDF: {
unit: 'pt',
format: 'a4',
orientation: 'portrait'
}
};
var pdf = html2pdf().set(options).from(element);
pdf.outputPdf('datauristring').then(function(data) {
// if (true)
// pdf.save();
resolve({
data: data.replace("data:application/pdf;filename=generated.pdf;base64,", ""),
type: "data:application/pdf"
});
}).catch(function(e) {
alert('catch: ' + e);
reject();
});
});
}
html2pdf.js version: 0.10.1
https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js
I have noticed, 'scale' property of 'html2canvas' creates the security error.
However, the above code works in earlier version of iOS.
I wonder what am I missing out from the config?
Thanks

Related

How to turn off Blackscreen in IOS Web View

Using Quagga.js, we are working on barcode recognition in web view.
It works fine on the mobile web, but it happens on the APP.
The problem is two things below.
The target div provided by Quagga does not contain the camera, but switches to the full screen.
Problem 1 Image
After barcode recognition, the camera remains in the Blackscreen state, not off.
Problem 1 Image
Already unpacked permissions from info.plist.
Privacy - Camera Usage Description
Privacy - Photo Library Additions Usage Description
Below is my code.
openItem.addEventListener("click", () => {
document.querySelector(".barcode_Camera").style.bottom = "0px";
/* Barcode Scan Script */
Quagga.init({
inputStream: {
type : "LiveStream",
target: document.querySelector('#camera_Area'), // Or '#yourElement' (optional)
constraints: {
width: {min: 1280},
height: {min: 480},
facingMode: "environment",
aspectRatio: {min: 1, max: 2}
}
},
locator: {
patchSize: "small",
halfSample: true
},
numOfWorkers: 4,
frequency: 10,
decoder: {
readers : [{
format: "code_93_reader",
config: {}
}],
debug: {
drawBoundingBox: false,
showFrequency: false,
drawScanline: false,
showPattern: false
},
multiple: false
},
}, function (err) {
if (err) {
console.log(err);
return
}
console.log("Initialization finished. Ready to start");
Quagga.start();
});
}); /*Close*/
Quagga.onProcessed(function (result) {
let drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;
if (result) {
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")),
parseInt(drawingCanvas.getAttribute("height")));
result.boxes.filter(function (box) {
return box !== result.box;
}).forEach(function (box) {
Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "#88d147", lineWidth: 2});
});
}
}
});
Quagga.onDetected(function (data) {
document.querySelector('#barcode').value = data.codeResult.code;
let closeTarget = closeItem.closest(".barcode_Camera");
document.querySelector(".barcode_Camera").style.bottom = "-100%";
Quagga.stop();
});
I would like to ask for your help if there is anything else I need to do in the info.plist or how I can solve the problem.

How to perform server side filtering on Kendo UI Grid

I am trying to implement server side filtering for a Kendo UI grid (client only). I am not sure how to pass the filter operator and value entered on the filter box. I was able to implement server paging and want the filtering to work alongside the server paging, i.e. show page 2 of 5 items of filtered rows. I saw some example of binding the request to "DataSourceRequest" object but we do not have licence for the server side Kendo UI and have to achieve it using the client side changes only.
Here is my jQuery code:
var page = 1;
var pageSize = 5;
var title = "test";
var selectWork = function (e) {
alert("selected");
};
$("#selectWorkGrid").empty();
$("#selectWorkGrid").kendoGrid({
dataSource:
{
transport: {
read: {
url: "http://example.com/" + "work/SearchWorkJ?worktitle=" + title,
dataType: "json",
contentType: "application/json",
data: {
page: page,
pageSize: pageSize
}
},
serverFiltering: true,
parameterMap: function (data, type) {
if (type == "read") {
return {
page: data.page,
pageSize: data.pageSize
}
}
}
},
schema: {
model: {
id: "workId",
fields: {
workId: { type: "number" },
workTitle: { type: "string" },
writers: { type: "string" },
durationInMmSs: { type: "string" }
}
},
data: "data",
total: "total"
},
pageSize: pageSize,
serverPaging: true,
serverFiltering: true
},
sortable: true,
resizable: true,
columnMenu: false,
filterable: {
mode: "row",
extra: false,
operators: {
string: {
startswith: "Starts with",
eq: "Is equal to",
neq: "Is not equal to"
}
}
},
noRecords: {
template: "No results available."
},
pageable: {
numeric: false,
refresh: true,
buttonCount: 15
},
scrollable: false,
columns: [
{
field: "workTitle",
title: "Title",
template: "#=workTitle#"
},
{
field: "writers",
title: "Writers",
filterable: false,
template: "${writers == null ? '':writers}",
width: 300
},
{
field: "durationInMmSs",
title: "Duration",
filterable: false,
headerAttributes: { style: "text-align:right;" },
attributes: { style: "text-align:right;" },
width: 80
},
{ command: { text: "Select", click: selectWork }, title: "", width: 60 }
]
});
Controller action returning json:
public ContentResult SearchWorkJ(string workTitle, int page = 0, int pageSize = 0)
{
var worksJson = "";
var works = WorkService.SearchWork(workTitle, page, pageSize);
if (works != null)
{
// Set total to upto current record + 1 so that next button works in kendo
int totalCount = page * pageSize + 1;
var sortedWorks = new List<WorkViewModel>();
sortedWorks.AddRange(works.Select(w => new WorkViewModel
{
WorkId = w.WorkId,
WorkTitle = w.WorkTitle,
Writers = w.Writers,
DurationInMmSs = w.Duration
}).OrderBy(w => w.WorkTitle));
worksJson = JsonConvert.SerializeObject(new { total = totalCount, data = sortedWorks });
}
return new ContentResult { Content = worksJson, ContentType = "application/json" };
}
If you look at this
https://dojo.telerik.com/EhUNUwOr
<div id="my-grid"></div>
<script>
$('#my-grid').kendoGrid({
dataSource: {
serverFiltering: true,
serverSorting: true,
serverPaging: true,
pageSize: 5,
transport: {
read: function(options) {
$.ajax({
url: '/yourapi',
contentType: 'application/json',
dataType: 'json',
type: 'POST',
data: JSON.stringify(options.data),
success: function(result) {
options.success(result);
}
})
}
},
schema: {
id: 'Id',
data: 'Data',
total: 'Total',
errors: 'Errors',
fields: [
{ field: 'Id', type: 'number' },
{ field: 'FirstName', type: 'string' },
{ field: 'LastName', type: 'string' }
]
},
filter: {
filters: [{ field: 'FirstName', operator: 'eq', value: 'David' }]
}
},
});
</script>
This will send
{"take":5,"skip":0,"page":1,"pageSize":5,"filter":{"filters":[{"field":"FirstName","operator":"eq","value":"David"}]}}
to your server / api
now if you have a model that shares this structure you can respond in the following format
{
"Data" : <your array of models>,
"Total" : the number of models that fits your filter regardless of the filter, this helps kendo grid knowing how many pages there is for the pager.,
"Errors" : is mostely used for create and update so just return null
}
From here its a bonus to the answer above.
I noticed you are using CSharp so you have two options to apply create dynamic queries from Queryable.
use a library I open sourced
https://github.com/PoweredSoft/DynamicLinq
which is available on Nuget https://www.nuget.org/packages/PoweredSoft.DynamicLinq/
There is an example you can look at on git hub.
You'll have to adapt the code around but it should get you started.
https://github.com/PoweredSoft/DynamicLinq#how-it-can-be-used-in-a-web-api
[HttpGet][Route("FindClients")]
public IHttpActionResult FindClients(string filterField = null, string filterValue = null,
string sortProperty = "Id", int? page = null, int pageSize = 50)
{
var ctx = new MyDbContext();
var query = ctx.Clients.AsQueryable();
if (!string.IsNullOrEmpty(filterField) && !string.IsNullOrEmpty(filterValue))
query = query.Query(t => t.Contains(filterField, filterValue)).OrderBy(sortProperty);
// count.
var clientCount = query.Count();
int? pages = null;
if (page.HasValue && pageSize > 0)
{
if (clientCount == 0)
pages = 0;
else
pages = clientCount / pageSize + (clientCount % pageSize != 0 ? 1 : 0);
}
if (page.HasValue)
query = query.Skip((page.Value-1) * pageSize).Take(pageSize);
var clients = query.ToList();
return Ok(new
{
total = clientCount,
pages = pages,
data = clients
});
}
An alternative is using
https://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library

ionic 2 how to HTTP.post image send the server side in ionic 2 / 3

I'm building an app with Ionic 2. I need to take a photo from gallery or camera and upload this picture to my server. I have this code that opens the Gallery and takes a picture. without base64Image, how can upload image.
private accessGallery(): void {
let options = {
quality: 75,
// sourceType: this.camera.PictureSourceType.SAVEDPHOTOALBUM,
// destinationType: this.camera.DestinationType.DATA_URL,
destinationType: this.camera.DestinationType.FILE_URI,
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
// encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE
}
this.imagePicker.getPictures(options).then((results) => {
this.imageURI = new Array();
for (var i = 0; i < results.length; i++) {
console.log('Image URI: ' + results[i]);
this.imageURI.push(normalizeURL(results[i]));
}
console.log("Body images Name*******:-=" + this.imageURI);
this.uploadFile();
}, (err) => { });
}
uploadFile() {
let body:any = new FormData();
body = {
images: this.imageURI
}
let headers = new Headers({
'token': this.token,
'sid': this.sid,
'user': this.user,
'to': this.to,
'node': this.node,
'type': 'image'
});
let options = new RequestOptions({ headers: headers });
console.log("header ----" + JSON.stringify(headers));
console.log("images data body----" + JSON.stringify(body));
this.http.post(this.apiURL, body, options)
.map(res => res.json())
.subscribe(
data => {
console.log(data);
},
err => {
console.log("ERROR!: ", err);
}
);
}
Error :- ERROR!: Response with status: 0 for URL: null

Ionic app image upload from camera / photo library

I'm working on a ionic chat app where the user can upload a photo as part of their message. I'm looking for a way to upload the image to my webhost server so I can retrieve it later via a URL.
The problem is that I'm not able to get it to upload to my web server.
I'm using these two plugins:
org.apache.cordova.file-transfer
cordova-plugin-camera
When I run the app in xcode simulator and select a picture from the device photolibrary, the console gives me the following messages:
File Transfer Finished with response code 200
void SendDelegateMessage(NSInvocation *): delegate (webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:) failed to return after waiting 10 seconds. main run loop mode: kCFRunLoopDefaultMode>
SUCCESS: ""
This is the code I currently use:
app.controller('HomeController', function($rootScope, $scope, $cordovaCamera, $ionicActionSheet, $cordovaFileTransfer){ ...
// open PhotoLibrary
$scope.openPhotoLibrary = function() {
var options = {
quality: 100,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
$cordovaCamera.getPicture(options).then(function(imageData) {
//console.log(imageData);
//console.log(options);
var url = "http://mydomein.com/upload.php";
//target path may be local or url
var targetPath = imageData;
var filename = targetPath.split("/").pop();
var options = {
fileKey: "file",
fileName: filename,
chunkedMode: false,
mimeType: "image/jpg"
};
$cordovaFileTransfer.upload(url, targetPath, options).then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
alert("success");
alert(JSON.stringify(result.response));
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
alert(JSON.stringify(err));
}, function (progress) {
// constant progress updates
$timeout(function () {
$scope.downloadProgress = (progress.loaded / progress.total) * 100;
})
});
}, function(err) {
// error
console.log(err);
});
}
This is my upload.php file:
<?php
// move_uploaded_file($_FILES["file"]["tmp_name"], $cwd . '/files/images/');
move_uploaded_file($_FILES["file"]["tmp_name"], "/files/images");
?>
After some digging around and lot's of trying I finally got it working.
This is the code I came up with:
// open PhotoLibrary
$scope.openPhotoLibrary = function() {
var options = {
quality: 50,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
$cordovaCamera.getPicture(options).then(function(imageData) {
//console.log(imageData);
//console.log(options);
var image = document.getElementById('tempImage');
image.src = imageData;
var server = "http://yourdomain.com/upload.php",
filePath = imageData;
var date = new Date();
var options = {
fileKey: "file",
fileName: imageData.substr(imageData.lastIndexOf('/') + 1),
chunkedMode: false,
mimeType: "image/jpg"
};
$cordovaFileTransfer.upload(server, filePath, options).then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
console.log('Result_' + result.response[0] + '_ending');
alert("success");
alert(JSON.stringify(result.response));
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
//alert(JSON.stringify(err));
}, function (progress) {
// constant progress updates
});
}, function(err) {
// error
console.log(err);
});
}
And the code in upload.php on the domain server:
<?php
// if you want to find the root path of a folder use the line of code below:
//echo $_SERVER['DOCUMENT_ROOT']
if ($_FILES["file"]["error"] > 0){
echo "Error Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{
echo "Uploaded file: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " kilobytes<br />";
if (file_exists("/files/".$_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. No joke-- this error is almost <i><b>impossible</b></i> to get. Try again, I bet 1 million dollars it won't ever happen again.";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],"/var/www/vhosts/yourdomain.com/subdomains/domainname/httpdocs/foldername/images/".$_FILES["file"]["name"]);
echo "Done";
}
}
?>
the app I am building for a company had the same issue, what we did is we just posted the image to our server as a base64 string. Then you can simple pull the string from the database and display it in a div. We used the NgCordova camera and then just pass in the data from the takePhoto function.
$scope.takePhoto = function () {
$ionicScrollDelegate.scrollTop();
console.log('fired camera');
$scope.uploadList = false;
$ionicPlatform.ready(function() {
var options = {
quality: 100,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
allowEdit: false,
encodingType: Camera.EncodingType.PNG,
targetWidth: 800,
targetHeight: 1100,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
$cordovaCamera.getPicture(options).then(function (imageData) {
$ionicLoading.show({
template: 'Processing Image',
duration: 2000
});
$scope.image = "data:image/png;base64," + imageData;
if (ionic.Platform.isAndroid() === true) {
$scope.Data.Image = LZString.compressToUTF16($scope.image);
$scope.Data.isCompressed = 1;
} else {
$scope.Data.Image = $scope.image;
$scope.Data.isCompressed = 0;
}
if ($scope.tutorial) {
$scope.showAlert("Instructions: Step 3", '<div class="center">Now that you have taken a photo of the POD form, you must upload it to the server. Press the upload doc button in the bottom right of the screen.</div>');
}
$scope.on('')
}, function (err) {
console.log(err);
});
}, false);
};
$scope.UploadDoc = function () {
var req = {
method: 'POST',
url: ffService.baseUrlAuth + 'cc/upload',
headers: {
'x-access-token': ffService.token
},
data: $scope.Data
};
if ($scope.Data.Image === null || $scope.Data.Value === '') {
$scope.showAlert("Uh Oh!", '<div class="center">Please take a photo of your document before attempting an upload.</div>');
} else {
$http(req).success(function (data, status, headers, config) {
localStorage.setItem('tutorial', false);
$scope.tutorial = false;
$scope.getUploads($scope.PODOrder.OrderNo);
$scope.showAlert("Success!", '<div class="center">Your Document has been successfully uploaded!</div>');
$scope.uploadList = true;
}).error(function (data, status, headers, config) {
$rootScope.$broadcast('loading:hide');
$scope.showAlert("Something went wrong!", '<div class="center">Please make sure you have an internet connection and try again.</div>');
}).then(function(data, status, headers, config){
$scope.Data.Image = null;
});
}
};

Highcharts columns height in phantomjs generated pdf

I am trying to generate a pdf with phantomjs from a page that's using highcharts. This is the script I am using
var port, server, service
system = require('system');
var page = require('webpage').create();
page.onError = function (msg, trace) {
console.log(msg);
trace.forEach(function(item) {
console.log(' ', item.file, ':', item.line);
})
}
var fs = require('fs');
function loadFile(name){
if(fs.exists(name)){
console.log(name+ " File exist");
return fs.open(name,"r");
}else {
console.log("File do not exist");
}
}
if (system.args.length !== 2) {
console.log('Usage: serverkeepalive.js <portnumber>');
phantom.exit(1);
} else {
port = system.args[1];
console.log('port: ' + port);
server = require('webserver').create();
service = server.listen(port, { keepAlive: true }, function (request, response) {
console.log('Request at ' + new Date());
console.log(JSON.stringify(request, null, 4));
console.log('ProjectId:' + request.headers.projectId)
var projectReportPage = 'http://localhost:55073/' + request.headers.projectId;
console.log(projectReportPage);
console.log(JSON.stringify(request.cookies, null, 4));
phantom.cookiesEnabled = true;
phantom.addCookie({
'name': 'hello', /* required property */
'value': 'helloFromPhantomJS', /* required property */
'domain': 'localhost', /* required property */
'expires': (new Date()).getTime() + 3600 /* <- expires in 1 hour */
});
console.log(JSON.stringify(phantom.cookies, null, 4));
page.paperSize = {
format: 'A4',
orientation: 'portrait',
margin:'1cm' };
page.open(projectReportPage, function (status) {
if (status !== 'success') {
console.log('FAIL to load the address');
} else {
console.log('Page obtained');
var reportName = 'report_' + request.headers.projectId + '.pdf';
page.evaluate( function(){$('h1').css('color', 'red');});
page.render(reportName);
var body = fs.absolute(reportName);
//var body = page.renderBase64('pdf');
// var fi = loadFile(reportName);
// var body = fi.read();
// var rawBody = fs.read(reportName);
// console.log(rawBody);
// var body = base64Encode(rawBody);
console.log(body);
response.statusCode = 200;
response.headers = {
'Cache': 'no-cache',
'Content-Type': 'application/pdf',
'Connection': 'Keep-Alive',
'Content-Length': body.length
};
response.write(body);
response.close();
}
});
console.log('After page open handler');
});
if (service) {
console.log('Web server running on port ' + port);
} else {
console.log('Error: Could not create web server listening on port ' + port);
phantom.exit();
}
}
When viewing the page, the chart looks like this:
http://i.imgur.com/kVImodv.png
This is what it looks like on the pdf:
http://i.imgur.com/vGon6vb.png
Any insights on why this happens would be appreciated!
The problem is that PhantomJS immediately takes a snapshot when the page is loaded. However, the Highcharts graph has an animation which builds up the graph.
This means the graph is not completely built up when PhantomJS is taking the snapshot. There are two possible solutions.
1. Skip the Highcharts animations
Add this to your graph configuration object.
plotOptions: {
series: {
animation: false
}
}
Source
2. Add a delay when taking a snapshot
page.open(address, function (status) {
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 200);
}
Source

Resources