My Android app is generating multi-page PDF files programmatically using PdfDocument:
public byte[] buildPDF() {
int pageWidth = Math.round(8.5f * 72f);
int pageHeight = Math.round(11.0f * 72f);
int margin = Math.round(.5f * 72f);
int fontSize = 12;
TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(fontSize);
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
PdfDocument document = new PdfDocument();
PdfDocument.Page page = null;
Canvas canvas = null;
for (int i=0; i<10; i++) {
PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(pageWidth, pageHeight, document.getPages().size()).create();
page = document.startPage(pageInfo);
canvas = page.getCanvas();
String content = "Content for page " + (i + 1);
canvas.drawText(content, margin, margin + fontSize, textPaint);
document.finishPage(page);
}
byte[] output = new byte[0];
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
document.writeTo(outputStream);
} catch (Exception e) {
Log.d("buildPDF", "error writing PDF output");
return output;
}
output = outputStream.toByteArray();
document.close();
return output;
}
In some cases, I want to place an existing PDF file from the local device into this series of pages. The easiest workflow would be to somehow open the PDF file as data, then write it to the canvas. Is there any way to do this with the Android SDK?
This is easy in iOS using CGPDFDocumentRef and CGContextDrawPDFPage, but I haven't found an equivalent for Android.
If there's no built-in way, then it seems like the multipdf package in Apache PDFBox might to the job. However, I need the data in memory as shown above, so I think that would require writing the generated data to a temp file, performing operations on the temp file and then reading the data back into memory.
My Android app is generating multi-page PDF files programmatically using PdfDocument:
Note that PdfDocument is designed for use with printing, not for creating arbitrary PDF files.
The easiest workflow would be to somehow open the PDF file as data, then write it to the canvas. Is there any way to do this with the Android SDK?
No, sorry.
CommonsWare answered my specific question (the SDK can't do this), but here's some more info about third-party tools that can:
I tried Apache PDFBox, then realized it's not compatible with Android. Then I found PdfBox-Android and tried installing that, but ran into a series of build problems starting with "The number of Dex files cannot exceed 64,000...." I spent a few hours trying to get that to build before it felt like I was on a wild goose chase and I moved on.
iTextG comes up a lot in situations like this, but I've always followed the rule that if a software company doesn't list its prices on its website, I can't afford it. I did go ahead and send a price request, but they forwarded my request to a reseller, and then the reseller's response went into my spam folder and I didn't see it until it was too late. Their cost was not as high as I feared, but still not very feasible given the relatively small role PDFs play in my app.
I'm already using Radaee PDF as a PDF viewer in my app, and I realized that it includes some document editing and merging functions. I couldn't get this to work with the older library version I was using, but with some help from their tech support I was able to update my project for the current version, and that worked. This requires a Premium license, but they offer a flat fee pricing structure that wasn't out of reach for my project.
So, I spent most of a week dealing with third-party libraries and working out compatibility issues, but once this was running it only took 14 lines of code to get the functionality I wanted. That's a great example of why software development times are so hard to estimate ... LOL.
Related
There are the Bitmap for Android and UIImage for iOS. Is there a way to display both somehow in the Xamarin Forms Image control?
Obviously I need the Dependency Service. I will have two implementations that create either a bitmap or an uiimage using some source, but how do I bring those two products together to a single forms control? Both Android and iOS methods have to return something, that the image control can understand and display. I don't know what that might be.
Edit: I look for a way where I don't use storage space, if possible.
Edit2:
I tried Jasons suggestion and it works fine.
I create a bitmap in the Android project and return a MemoryStream object:
MemoryStream stream = new MemoryStream();
newImage.Compress(Bitmap.CompressFormat.Png, 0, stream);
return stream;
Then I consume it in my Xamarin.Forms Image control:
var stream = DependencyService.Get<ICrossPlatformImageProcesor>().Combine_Images(imagePath);
stream.Position = 0;
img_ImageView.Source = Xamarin.Forms.ImageSource.FromStream(() => stream);
I will have two implementations that create either a bitmap or an uiimage using some source, but how do I bring those two products together to a single forms control?
You can simply use Image Control of xamarin forms, images can be loaded specifically for each platform, or they can be downloaded for display.
For more information, you can refer to Working with Images.
I look for a way where I don't use storage space, if possible.
I'm not quite understand this, if you mean don't use memory, then I think it is not possible. If you mean your images are not saved in storage, then possibly you have an URL address on internet of your images?
Anyway, Image control in Xamarin.Forms support image source form ImageSource instance, file, Uri, and resources, to load image from uri, you can simply code like this:
var webImage = new Image { Aspect = Aspect.AspectFit };
webImage.Source = ImageSource.FromUri(new Uri("https://xamarin.com/content/images/pages/forms/example-app.png"));
I want to displaythe thumb images of pdf in my app. I am using this answer. But I don't know how to use it in android as Rectangle2D can't be userd in java. I tried using RectFand modified the code as:
File file = new File(arrayOfResults.get(arg0).filePath);
RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel channel = raf.getChannel();
ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY,
0, channel.size());
PDFFile pdffile = new PDFFile(buf);
// draw the first page to an image
PDFPage page = pdffile.getPage(0);
// get the width and height for the doc at the default zoom
RectF rect = new RectF(0, 0, (int) page.getWidth(),
(int) page.getHeight());
But the app crashed with the following error:
java.lang.NoClassDefFoundError: java.awt.geom.Rectangle2D$Double
at com.sun.pdfview.PDFFile.parseNormalisedRectangle(PDFFile.java:1874)
Please help.
Android added a PdfRenderer class that can do that in API 21.
If you need to go below that, you need to ship with your own PDF render engine. We've looked at the marketplaces and found that the options are all lacking in one way or another, and created PSPDFKit for Android. It's a commercial PDF framework and (full disclaimer) I'm part of the team that builds it.
In PSPDFKit for Android, you can use renderPageToBitmap to render the PDF into a bitmap.
If your project is personal or you are an indie, try http://plugpdf.com/
They offer their Android PDF SDK free for you. You can also check out their blog article about rendering a PDF page to bitmap image on Android. http://plugpdf.com/how-to-render-a-pdf-document-to-bitmap-image-on-android/
Disclaimer: I am the Founder & CEO at ePapyrus Inc. which runs plugpdf.com.
I want to generate a PDF File from a View using the PdfDocument android class introduced in KitKat. I managed to do it, and the file is so far generated ok, ending up having a correct PDF. The only problem is the file is huge, 12Mb for just one page. Is there a way to reduce the File size?
The code I am using to generate the PDF is:
public static File generateDocument(Activity activity, String fileName, ViewGroup container) throws IOException{
File f = new File(activity.getExternalFilesDir(null), fileName);
PdfDocument document = new PdfDocument();
try{
for(int i=0;i<container.getChildCount();i++){
View v = container.getChildAt(i);
PdfDocument.PageInfo.Builder pageBuilder = new PdfDocument.PageInfo.Builder(v.getWidth(), v.getHeight(), i);
Page page = document.startPage(pageBuilder.create());
v.draw(page.getCanvas());
document.finishPage(page);
}
document.writeTo(new FileOutputStream(f));
} finally{
if(document!=null){
document.close();
}
}
return f;
}
In case anyone is still looking for a solution... I was working on a project to generate PDF from images and not satisfied with the file size generated by both Android's PdfDocument and 3rd party AndroidPdfWriter APW.
After some trials I ended up using Apache's PdfBox, which gave me a PDF file (A4 size with a single 1960x1080 image) for around 80K, while it's usually 2~3M with PdfDocument or AndroidPdfWriter.
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
// Define a content stream for adding to the PDF
contentStream = new PDPageContentStream(document, page);
Bitmap bimap = _get_your_bitmap_();
// Here you have great control of the compression rate and DPI on your image.
// Update 2017/11/22: The DPI param actually is useless as of current version v1.8.9.1 if you take a look into the source code. Compression rate is enough to achieve a much smaller file size.
PDImageXObject ximage = JPEGFactory.createFromImage(document, bitmap, 0.75, 72);
// You may want to call PDPage.getCropBox() in order to place your image
// somewhere inside this page rect with (x, y) and (width, height).
contentStream.drawImage(ximage, 0, 0);
// Make sure that the content stream is closed:
contentStream.close();
document.save(_your_file_path_);
document.close();
=====
btw. I guess the reason why they generate a huge file size is because they don't compress the image data while writing to PDF file. If you take a look into AndroidPdfWriter's XObjectImage.deflateImageData() method you will see it's using java.util.zip.Deflater.NO_COMPRESSION option to write the image data which is kind of horrible if you've got a picture with size 1960x1080. If you change the options to e.g. Deflater.BEST_COMPRESSION you get much smaller file size however it takes up to 3-4 seconds for me to handle one single page which is not acceptable.
There are a few main things that increases the size of a PDF file:
hi-resolution pictures (where lo-res would suffice)
embedded fonts (where content would still be readable "good enough" without them)
PDF content not required any more for the current version/view (older version of certain objects)
embedded ICC profiles
embedded third-party files (using the PDF as a container)
embedded job tickets (for printing)
embedded Javascript
and a few more
Try using iText. Following links give a basice idea for iText in android.
http://technotransit.wordpress.com/2011/06/17/using-itext-in-android/
http://www.mysamplecode.com/2013/05/android-itext-pdf-bluetooth-printer.html
https://stackoverflow.com/a/21025162/3110609
Using PDFDocument, be sure to downscale your images prior to drawing them in the canvas.
When drawing to the screen, this is enough to scale the bitmap :
canvas.drawBitmap(bmp, src, dst, paint);
However, when using the canvas from PdfDocument.Page.getCanvas, this canvas will not downscale the bitmap, it will just squeeze it into a smaller zone. Instead you should do something like this:
// Scale bitmap : filter = false since we are always downSampling
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, dstWidth, dstHeight,
false); // filter=false if downscaling, true if upscaling
canvas.drawBitmap(scaledBitmap, null, dst, paint);
scaledBitmap.recycle();
This is embedded in Android so it is much easier than using a third-party library. (The above was tested on a Marshmallow platform)
This seems to just be a bug in PdfDocument. The PDF file I created with PdfDocument was 5.6 megabytes. The same document generated through the iOS equivalent was 500K. If I take the Android PDF and run it through Adobe Acrobat's pdf optimization, without compressing any images, the 5.6MB file becomes 350K. They look identical, and I applied no compression in Adobe Acrobat.
In the actual PDF code, the Android image object dictionary is this
<</Type /XObject
/Subtype /Image
/Width 1224
/Height 1584
/ColorSpace /DeviceRGB
/BitsPerComponent 8
/Length 5816448
>>
The PDF from iOS has this dict
<< /Length 8 0 R
/Type /XObject
/Subtype /Image
/Width 1224
/Height 1584
/ColorSpace /DeviceRGB
/SMask 9 0 R
/BitsPerComponent 8
/Filter /FlateDecode >>
I think the problem is the lack of the FlateDecode filter in the Android version. When I run it through the Adobe Acrobat PDF optimizer, it gets the FlateDecode filter.
Is there any way to get a high resolution screen shot of a certain view in an activity.
I want to convert html content of my webview to PDF. For that I tried to take screen shot of the webview content and then converted it to PDF using itext. The resulted PDF is not in much more clarity.
My code:
protected void takeimg() {
Picture picture = mWebView.capturePicture();
Bitmap b = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
picture.draw(c);
// byte[] bt = b.getNinePatchChunk();
// Bitmap b;
// View v1 = mWebView.getRootView();
// v1.setDrawingCacheEnabled(true);
// b = Bitmap.createBitmap(v1.getDrawingCache());
// v1.setDrawingCacheEnabled(false);
FileOutputStream fos = null;
try {
File root = new File(Environment.getExternalStorageDirectory(),
"Sample");
if (!root.exists()) {
root.mkdir();
}
String sdcardhtmlpath = root.getPath().toString() + "/"
+ "temp_1.png";
fos = new FileOutputStream(sdcardhtmlpath);
// fos = openFileOutput("samsp_1.jpg", MODE_WORLD_WRITEABLE);
if (fos != null) {
b.compress(Bitmap.CompressFormat.PNG, 100, fos);
// fos.write(bt);
fos.close();
}
} catch (Exception e) {
Log.e("takeimg", e.toString());
e.printStackTrace();
}
}
protected void pdfimg() {
Document mydoc = new Document(PageSize.A3);
try {
File root = new File(Environment.getExternalStorageDirectory(),
"Sample");
if (!root.exists()) {
root.mkdir();
}
String sdcardhtmlpath = root.getPath().toString() + "/";
mydoc.setMargins(0, 0, 0, 0);
PdfWriter.getInstance(mydoc, new FileOutputStream(sdcardhtmlpath
+ PDFfilename));
mydoc.open();
Image image1 = Image.getInstance(sdcardhtmlpath + "temp_1.jpg");
image1.scalePercent(95f);
mydoc.add(image1);
// mydoc.newPage();
mydoc.close();
} catch (Exception e) {
Log.e("pdi name", e.toString());
}
}
Update: See Edit 3 for an answer to op's original question
There are two options:
Use a library to convert the HTML to PDF. This is by far the best option, since it will (probably) preserve text as vectors.
Get a high resolution render of the HTML and save it as a PNG (not PDF surely!).
For HTML to PDF, wkhtmltopdf looks like a good option, but it relies on Qt which you can't really use on Android. There are some other libraries but I doubt they do the PDF rendering very well.
For getting a high-res webview, you could try creating your own WebView and calling onMeasure(...) and onLayout(...) and pass appropriate parameters so the view is really big. Then call onDraw(myOwnCanvas) and the webview will draw itself to your canvas, which can be backed by a Bitmap using Canvas.setBitmap().
You can probably copy the state into the new WebView using something like
screenshotterWebview.onRestoreInstanceState(mWebView.onSaveInstanceState());
Orrr it may even be possible to use the same WebView, just temporarily resize it to be large, onDraw() it to your canvas, and resize it back again. That's getting very hacky though!
You might run into memory issues if you make it too big.
Edit 1
I thought of a third, exactly-what-you-want option, but it's kind of hardcore. You can create a custom Canvas, that writes to a PDF. In fact, it is almost easy, because underlying Canvas is Skia, which actually includes a PDF backend. Unfortunately you don't get access to it on Android, so you'll basically have to build your own copy of it on Android (there are instructions), and duplicate/override all the Canvas methods to point to your Skia instead of Androids. Note that there is a tempting Picture.writeToStream() method which serializes the Skia data, but unfortunately this format is not forwards or backwards compatible so if you use it your code will probably only work on a few versions of Android.
I'll update if/when I have fully working code.
Edit 2
Actually it is impossible to make your own "intercepting" Canvas. I started doing it and went through the tedious process of serializing all function calls. A few you can't do because they are hidden, but those didn't look important. But right at the end I came to serializing Path only to discover that it is write-only. That seems like a killer to me, so the only option is to interpret the result of Picture.writeToStream(). Fortunately there are only two versions of that format in use, and they are nearly identical.
Edit 3 - Really simple way to get a high resolution Bitmap of a view
Ok, it turns out just getting a high res bitmap of a view (which can be the entire app) is trivial. Here is how to get double resolution. Obviously all the bitmaps look a bit crap, but the text is rendered at full resolution:
View window = activity.getWindow().getDecorView()
Canvas bitmapCanvas = new Canvas();
Bitmap bitmap = Bitmap.createBitmap(window.getWidth()*2, window.getHeight()*2, Bitmap.Config.ARGB_8888);
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.scale(2.0f, 2.0f);
window.draw(bitmapCanvas);
bitmap.compress(Bitmap.CompressFormat.PNG, 0, myOutputStream);
Works like a charm. I've now given up on getting a PDF screenshot with vector text. It's certainly possible, but very difficult. Instead I am working on getting a high-res PSD where each draw operation is a separate layer, which should be much easier.
Edit 4
Woa this is getting a bit long, but success! I've generated an .xcf (GIMP) and PDF where each layer is a different canvas drawing operation. It's not quite as fine-grained as I was expecting, but still, pretty useful!
Actually my code just outputs full-size PNGs and I used "Open as layers..." and "Autocrop layer" in GIMP to make these files, but of course you can do that in code if you like. I think I will turn this into a blog post.
Download the GIMP or Photoshop demo file (rendered at 3x resolution).
When you capture the view, just screen bound will capture ( due to control weight and android render pipeline ).
Capturing screenshot for converting to PDF is tricky way. I think two way is more reasonable solutions.
Solution #1
Write a parser ( it's simple ) to convert webview content ( that is HTML ) to iText format.
You can refer to this article for more information.
http://www.vogella.com/articles/JavaPDF/article.html
Also to write a parser you can use REGEX and provide your own methods like parseTable, parseImage, ...
Solution #2 Internet Required
Provide a URL ( or webservice ) to convert HTML to PDF using PHP or C# that has a lot of nice libraries. Next you can send download link to the Client ( Android Device ).
So you can also dynamically add some Tags, Banners, ... to the PDF from server side.
Screen Shot is nothing but picture of your device display which usually depend upon your phone absolute pixels, if your phone is 480x800 screen shot will be same and generally applicable for all scenarios.
Sure, Use this:
Bitmap bitmap;
View v1 = MyView.getRootView();
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
Here MyView is the View you need a screenshot of.
I want to display pdf in android for that i have user some libraries like vudroid,pdfreader but all these libraries show pdf pages as image i want to implement such functionality that user can view pdf and select text from pdf change background color of text using color picker and save the existing pdf, i have referred some of libraries but they doesn't provide such functionality,please give some idea if such library is available
I have used following libraries
Android PDF Viewer
APDFViewer
droidreader
Thanks
Disclaimer: I'm a developer on the PSPDFKit team
PSPDFKit for Android was recently released as version 2.0 including annotation editing support. We've spent a lot of time in making annotation editing easy – both for developers integrating PDFs in their apps, as for end users. Here is an example of how to create a NoteAnnotation with PSPDFKit:
PSPDFDocument document = ...
// Create the annotation.
final int pageNum = 3;
final RectF pagePosition = new RectF(10, 10, 30, 30);
final NoteAnnotation annotation = new NoteAnnotation(pageNum, pagePosition, "This is a note with cross icon!", CROSS);
// Attach the annotation to the document.
document.getAnnotationProvider().addAnnotationToPage(annotation);
There's also a bunch of ready-to-use UI components for annotation creation and editing. PSPDFKit is a commercial product, but we offer demo versions which can be requested on our website.