PDFKit and PDFDocument Annotations not visible in Adobe Acrobat - ios

After I remove/add the annotations I save the file to disk (is this even necessary to properly save the annotations?). I then have the document in a UIActivityViewController inside a UIActivityItemProvider.
The annotations are viewable in the print preview, PDF Expert, Firefox, Gmail browser, Preview, etc. - just not Acrobat (and exporting to .doc)
Using Acrobat Reader Build 19.21.20061.361316; using Xcode 11.3
The PDF I am editing or here
Example annotation:
// Open PDF etc
if annotation.fieldName == "form1[0].Page1[0].WE_FACTR[0]" {
annotation.setValue("30.0", forAnnotationKey: .widgetValue)
page.removeAnnotation(annotation)
page.addAnnotation(annotation)
}
// save PDF to file
// PDF File is in the UIACtivityViewController
I was wondering if anyone had a workaround for missing annotations in PDFKit for Adobe Reader.
When I asked #steipete from PSPDFKit he told me that this is why people use his framework. There is a drop-in replacement call PDFXKit that he suggested.
It didn't work with PSPDFKit either! XFA forms don't seem to be supported.

I had the same issue, I am opening a PDF with a form in the app and fill it up with data from different textField. Unfortunately as it was very well explained by mkl, all the annotations are visible on all Pdf reader expect the one that most people use...Acrobat...
I have been looking for days for a solution and since that unfortunately PDFKit does not have at this time an option to flatten a PDF, I have to come up with an "ugly" solution. But at least for my needs it works.
Once I have updated the PDF form with all the annotation, I convert it to an image and then convert it back to a PDF. I have now a "flatten" pdf...
It is not great and I hope that Apple will add a function to flatten a pdf in their PDFkit in the future.
if there is a better solution, Let me know!
convert to image
func pdfToImage(url: URL) -> [UIImage]? {
let pdfDocument = PDFDocument(url: url)!
let numbPages = pdfDocument.pageCount
var imagePdf = [UIImage]()
for i in 0...numbPages-1 {
let page = pdfDocument.page(at: i)!
let bounds = page.bounds(for: .mediaBox)
let renderer = UIGraphicsImageRenderer(bounds: bounds, format: UIGraphicsImageRendererFormat.default())
let img = renderer.image { (context) in
context.cgContext.saveGState()
context.cgContext.translateBy(x: 0, y: bounds.height)
context.cgContext.concatenate(CGAffineTransform.init(scaleX: 1, y: -1))
page.draw(with: .mediaBox, to: context.cgContext)
context.cgContext.restoreGState()
}
imagePdf.append(img)
}
return imagePdf
}
convert back to PDF
func imageToPDF(image: [UIImage])-> PDFDocument? {
let pdfDocument = PDFDocument()
for (index,image) in image.enumerated() {
let pdfPage = PDFPage(image: image)
pdfDocument.insert(pdfPage!, at: index)
}
return pdfDocument
}
and then I just call those functionss
let imageFromPdf = pdfToImage(url: pdfURL)!
let flattenPDF = imageToPDF(image: imageFromPdf)
flattenPDF?.write(to: pdfURL2)

Your PDF has a XFA/AcroForm hybrid form definition. That means that the form is defined twice in the PDF, once using AcroForm objects, the "native" PDF form format, and once using a XFA XML stream, a meanwhile deprecated alternative form technique allowing more dynamic forms.
If your PDF is opened in a PDF processor supporting XFA (which foremost means Adobe viewers plus very few other processors), the XFA form definition is processed (e.g. displayed) and all the normal PDF content is ignored, merely values in the AcroForm form definition are updated to match the XFA form values.
If your PDF is opened in a PDF processor not supporting XFA (i.e. essentially all except Adobe products), the normal PDF content including the Acroform form definition is processed.
This explains your observation
The annotations are viewable in the print preview, PDF Expert, Firefox, Gmail browser, Preview, etc. - just not Acrobat (and exporting to .doc)
(As I only see now you found out about XFA yourself and edited a remark pointing there into your question.)
The usual solution in this situation is to remove the XFA form definition. Once it is removed, all processors (the Adobe viewers, too) will only process the normal PDF content including the AcroForm form definition.
To do so you merely have to remove XFA entry in the AcroForm dictionary of the PDF.
If a usage rights signature is present in the document (as is in yours), that removal will invalidate it. In such a case you should also remove the Perms entry in the Catalog.
Unfortunately I do not know your libraries, so I cannot show the code to implement this with them.

Related

Appium Android: How to test pdf displayed inside WebView

In Android, I want to test PDF which contains terms and conditions, but this displayed inside WebView. I am able to switch to WebView, I am using below code.
String strWebContextName = getContexts().stream().filter(ctx -> ctx.contains(“WEBVIEW_”)).findAny().orElse(null);
if (Objects.nonNull(strWebContextName)) {
((AndroidDriver) getBaseMobileDriver()).context(strWebContextName);
}
Then locate the script tag and get the content
#FindBy(xpath = “//script[#type=“text/javascript” and contains(text(),”_init")]")
private WebElement webElementPdfPath;
String htmlCode = (String) ((JavascriptExecutor) getBaseMobileDriver()).executeScript(“return arguments[0].innerHTML;”, webElementPdfPath);
After this I don’t know how to proceed? Please help
In my experience with verifying PDF's in a WebView is that there is only limited you can search for with selectors. I'm used to only class or type attributes of the PDF container. I have never been able to search for specific text in an PDF with XPath (PDF's are also not part of the HTML but more an extension which opens the document).
Try a simpler XPath: //script[#type='text/javascript']. This way you know the PDF is opened, but that's all.
I've done this with desktop browsers as well. For browsers, there is no way to identify inner PDF elements, but only limited to: //embed[#type='application/x-google-chrome-pdf']. If I needed to verify the PDF with conditions I've used SikuliX image recognition for instance.

TCPDF Embedded Base64 Encoded Images in HTML String

I have read several posts on this subject but didn't want to piggy-back on any of them with additional questions.
Specifically this post: TCPDF and insert an image base64 encoded
I am generating a PDF from within a custom theme in Wordpress. I'm using TCPDF 6.2.3 (latest stable release, I believe).
I am building this PDF from the same HTML I am using to display on the page. If I embed the full base64 encoded string, it works correctly in the browser, but the image is missing from the PDF.
If I use the "#" method described in the linked post, I get a broken image in the browser (expectedly) but still nothing in the PDF.
All the rest of my HTML markup is rendering in the PDF, images are just not showing.
Is there some other setting or option I need to set in order to get the images to appear in the PDF, and/or can you spot anything I'm doing wrong here? No errors, the images are just not visible in the PDF.
This is how I set the image up:
$imageLocation = $img_root.$imgsrc;
$ext = end(explode(".", $imageLocation));
$image = base64_encode(file_get_contents($imageLocation));
//$response .= "<img src='data:image/$ext;base64,$image'>"; //works in browser but not in PDF
$response .= "<img src='#$image' class='socf_image'>"; //does not work in browser or PDF
And here is the method to create the PDF:
function createPDF($response)
{
// Include the main TCPDF library (search for installation path).
require_once('tcpdf_6_3_2/tcpdf/tcpdf.php');
// create new PDF document
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
// set document information
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('test');
$pdf->SetTitle('test');
$pdf->SetSubject('test');
$pdf->SetKeywords('test');
// set default header data
$pdf->SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE.' 001', PDF_HEADER_STRING, array(0,64,255), array(0,64,128));
$pdf->setFooterData(array(0,64,0), array(0,64,128));
// set header and footer fonts
$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
// set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
// set auto page breaks
$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
// set image scale factor
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
// set default font subsetting mode
$pdf->setFontSubsetting(true);
// Set font
$pdf->SetFont('helvetica', '', 14, '', true);
// Add a page
$pdf->AddPage();
$html = $response;
$pdf->writeHTML($response, true, false, true, false, '');
return $pdf;
}
Well, fortunately, I was able to figure it out on my own. Perhaps this isn't the best forum for seeking help with this library? If anyone can suggest a better place to get help, I'd appreciate the direction.
Ultimately, the issue was two-fold:
The "#" notation is required for the PDf while the approach is what works for displaying the HTML in browser. So a string replace before creating the PDF solves that.
This is the tricky part. The HTML needs to use double-quotes around the properties, not single quotes. My code was using double quotes for the PHP strings, so the HTML properties were surrounded with single quotes and that was the issue. Swapping the two quote types was the last piece of the puzzle to get the images to appear in the PDF.
Hopefully this will help someone else who is pulling their hair out trying to blindly find their way through this library like me.

Asset Catalog images inside a UIWebView

Xcode 9 has taken another step forward with vectors inside of iOS projects. It will apparently retain the source PDF so that images may be generated as needed with full quality.
I'm wondering if it's yet possible to access these images from the HTML inside of a UIWebView?
I can imagine three possible ways of doing this:
Some magic that you place within your HTML or CSS that iOS will recognize and replace. For example:
<img src={{Asset Name}}>
Knowing the naming scheme to get at the asset. For example:
<img src="some/path/asset~ipad#3x">
"Render" the image outside the UIWebView and pass it in.
Are any of these possible? Is there another way?
Given how far we've come with vectors, I'd hate to have to pre-render every image variation and package them outside the asset catalog if I don't have to.
Today i needed to render the UIImage from XCAsset's into an UIWebView.
My requirement was to prepare the appropriate HTML and load it into the UIWebView as follows
self.myWebView.loadHTMLString(html, baseURL: nil)
In order to load images from XCAsset's, first loaded the image into an UIImage, then convert it back to Base64 Encoding, then put the image into the <img> tag.
The code looks like following
let redClockImage = UIImage.init(named: "clock-red")!
let imageData:Data = UIImagePNGRepresentation(self)!
let base64String = imageData.base64EncodedString()
let html = """
//my html
<img src="data:image/png;base64, \(base64String)" />
//rest of my html
"""
//then i load this html into the webview
self.myWebView.loadHTMLString(html, baseURL: nil)
For Convenience i moved the UIImage -> Base64 String conversion into an extension.

Printing from a Xamarin.Forms app

I'm all new to Xamarin and I'm currently working on a sample or a "prove of concept" app using Xamarin.Forms.
I'm supposed to perform a print task from this app though I'm not at this point sure what to print yet (the screen, content of a label, a file etc.).
Either way, what is the easiest way to print from a Xamarin.Forms app?
(current target is primarily Android 4.4+).
I hope this isn't too complicated :)
EDIT:
Ok let me just update this post as the original text might be a bit ambitious/vague.
I have a Xamarin.Forms project (+ an Android part) and I have some HTML available in the XF part of the project that I need to get into a WebView and print it.
From what I understand, the thing with the WebView has to be done on the Android part of the project due to the fact that this is where the printing will be handled.
I was hoping this could be done from code since I don't really need to display the WebView, just print it's content.
The Android part of the project has only the MainActivity and no layouts or XAML files.
I don't know where to add the WebView or how to access it (other than DependecyService seems to be a buzz word here) so I'm kinda stuck here.
I'm thinking that this task should be rather trivial to someone with a little more Xamarin experience than me.
Every platform XF supports has it's own mechanism for printing. XF does not provide any abstractions for printing in a cross-platform manner. You will need to write printing logic for each layer and expose it to XF using DependencyService (or some other DI engine).
Here is a good example, of course, using dependency service:
https://codemilltech.com/xamarin-forms-e-z-print/
I so wanted to do this but it was too hard. Finally built it into Forms9Patch - a MIT licensed open source project.
Verifying that Printing is available
Before printing, you should verify that printing is available on your device. To do so, call:
if (Forms9Patch.PrintService.CanPrint)
{
// do the printing here
}
Print the contents of a Xamarin.Forms.WebView
using Forms9Patch;
...
var myWebView = new Xamarin.Forms.WebView
myWebView.Source = new HtmlWebViewSource
{
Html = "some HTML text here"
};
...
myWebView.Print("my_print_job_name");
Note that your WebView does not have to be attached to a Layout. This allows you to Print without having to display the WebView in your app’s UI.
Printing an HTML string
using Forms9Patch;
...
var myHtmlString = #"
<!DOCTYPE html>
<html>
<body>
<h1>Convert to PNG</h1>
<p>This html will be converted to a PNG, PDF, or print.</p>
</body>
</html>
";
...
myHtmlString.Print("my_print_job_name");
PLEASE NOTE: iOS sometimes places the page breaks in weird places. I have a StackOverflow Bounty on why this happens and how to fix it.
Using EmbeddedResource as a source for a Xamarin.Forms.WebView
This is sort of an experimental feature I’ve built that I’ve found it useful. As such the documentation is sparse. It allow you to put HTML content in a folder in your app’s EmbeddedResources folder and then use it as a source for a WebView. A much nicer solution than using platform specific approach provided by Xamarin. It also supports putting all of the HTML content into a zip file. Please take a look at the source code to see how it works.
You can handle the printing of lists/ invoices .. with the xfinium pdf component from xamarin componentstore. With that you create your _pdffile and then call the following method which starts the adobereader from where you can select a printer (in my case google cloudprint)
public void printPdfToCloud(string _pdffile)
{
try
{
var saveto = System.IO.Path.Combine(Android.OS.Environment.ExternalStorageDirectory.ToString(), "YourApp/"+_pdffile);
string file_path = saveto;
if (System.IO.File.Exists(file_path))
{
Android.Net.Uri pdfFile = Android.Net.Uri.FromFile(new Java.IO.File(file_path));
Intent pdfIntent = new Intent(Intent.ActionView);
pdfIntent.SetPackage("com.adobe.reader");
pdfIntent.SetDataAndType(pdfFile, "application/pdf");
pdfIntent.SetFlags(ActivityFlags.NoHistory);
StartActivity(pdfIntent);
}else
{
// give a note that the file does not exist
}
}
catch (Exception E)
{
// Do some Error dialog
}
}

Open XML SDK: opening a Word template and saving to a different file-name

This one very simple thing I can't find the right technique. What I want is to open a .dotx template, make some changes and save as the same name but .docx extension. I can save a WordprocessingDocument but only to the place it's loaded from. I've tried manually constructing a new document using the WordprocessingDocument with changes made but nothing's worked so far, I tried MainDocumentPart.Document.WriteTo(XmlWriter.Create(targetPath)); and just got an empty file.
What's the right way here? Is a .dotx file special at all or just another document as far as the SDK is concerned - should i simply copy the template to the destination and then open that and make changes, and save? I did have some concerns if my app is called from two clients at once, if it can open the same .dotx file twice... in this case creating a copy would be sensible anyway... but for my own curiosity I still want to know how to do "Save As".
I would suggest just using File.IO to copy the dotx file to a docx file and make your changes there, if that works for your situation. There's also a ChangeDocumentType function you'll have to call to prevent an error in the new docx file.
File.Copy(#"\path\to\template.dotx", #"\path\to\template.docx");
using(WordprocessingDocument newdoc = WordprocessingDocument.Open(#"\path\to\template.docx", true))
{
newdoc.ChangeDocumentType(WordprocessingDocumentType.Document);
//manipulate document....
}
While M_R_H's answer is correct, there is a faster, less IO-intensive method:
Read the template or document into a MemoryStream.
Within a using statement:
open the template or document on the MemoryStream.
If you opened a template (.dotx) and you want to store it as a document (.docx), you must change the document type to WordprocessingDocumentType.Document. Otherwise, Word will complain when you try to open the document.
Manipulate your document.
Write the contents of the MemoryStream to a file.
For the first step, we can use the following method, which reads a file into a MemoryStream:
public static MemoryStream ReadAllBytesToMemoryStream(string path)
{
byte[] buffer = File.ReadAllBytes(path);
var destStream = new MemoryStream(buffer.Length);
destStream.Write(buffer, 0, buffer.Length);
destStream.Seek(0, SeekOrigin.Begin);
return destStream;
}
Then, we can use that in the following way (replicating as much of M_R_H's code as possible):
// Step #1 (note the using declaration)
using MemoryStream stream = ReadAllBytesToMemoryStream(#"\path\to\template.dotx");
// Step #2
using (WordprocessingDocument newdoc = WordprocessingDocument.Open(stream, true)
{
// You must do the following to turn a template into a document.
newdoc.ChangeDocumentType(WordprocessingDocumentType.Document);
// Manipulate document (completely in memory now) ...
}
// Step #3
File.WriteAllBytes(#"\path\to\template.docx", stream.GetBuffer());
See this post for a comparison of methods for cloning (or duplicating) Word documents or templates.

Resources