I've asked this question on a couple other forums and have had zero response, so I'm hoping someone here can help point me in the right direction. I have a pretty simple one screen application for my work. It's basically just a recreation of a 1 page paper report that has a company logo, some labels, a few text boxes and a scroll text box for the report.
I need to be able to fill out the report then click a button to save it in a graphical form so I can fax, print or email it later. Currently, I'm just programmatically taking a screen capture and saving it to the photo's library (default for screen capture). Then I can just email it from photo's. This works ok, but is kind of hacky, at best.
I've read through the new iPad 3.2 guide for creating PDF's (apparently it's supposed to be much easier than before) but I can not get it to work and I've spent countless hours on it now. I'm hoping someone has the answer for me.
Alternatively, if anyone knows how I can redirect where the screen capture is stored (default is in the photo album) then maybe I can make that function work. If I could redirect the screen capture to store in my applications document folder, then I can use MFMailCompose to attach it to an email.
Lastly, on a side note, does anyone know of a good way to capture a digital signature via touch. For instance, I'd love to have my users be able to just sign their name via touch at the bottom of the document before I convert to PDF or take a screen capture.
Thanks in advance for your help.
-Ray
I am using the following snippet. it works fine with single page PDF generation.
It uses any View where you have content and capture it as a PDF file and stores into the document directory.
on some function
{
....
// here container view is my content to be converted to PDF file
// filepath is the path to where it should be write in our documents directory
CGContextRef pdfContext = [self createPDFContext:containerView.bounds path:(CFStringRef)filePath];
NSLog(#"PDF Context created");
CGContextBeginPage (pdfContext,nil); // 6
//turn PDF upsidedown
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformMakeTranslation(0, containerView.bounds.size.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(pdfContext, transform);
//Draw view into PDF
[containerView.layer renderInContext:pdfContext];
CGContextEndPage (pdfContext);// 8
CGContextRelease (pdfContext);
....
}
- (CGContextRef) createPDFContext:(CGRect)inMediaBox path:(CFStringRef) path
{
CGContextRef myOutContext = NULL;
CFURLRef url;
url = CFURLCreateWithFileSystemPath (NULL, // 1
path,
kCFURLPOSIXPathStyle,
false);
if (url != NULL) {
myOutContext = CGPDFContextCreateWithURL (url,// 2
&inMediaBox,
NULL);
CFRelease(url);// 3
}
return myOutContext;// 4
}
Related
I have several shinobicharts in my App that I want to print into a PDF file. Everything else, like normal views, labels and images work fine, even the grid, legend and gridlabels are displayed. The only thing missing are the series. So basically I get an empty chart printed into the PDF file.
I print the PDF as follows:
NSMutableData * pdfData=[NSMutableData data];
PDFPage1ViewController *pdf1 = [self.storyboard instantiateViewControllerWithIdentifier:#"PDF1"];
pdf1.array1 = array1;
pdf1.array2 = array2;
pdf1.array3 = array3;
pdf1.array4 = array4;
UIGraphicsBeginPDFContextToData(pdfData, CGRectZero,nil);
CGContextRef pdfContext=UIGraphicsGetCurrentContext();
UIGraphicsBeginPDFPage();
[pdf1.view.layer renderInContext:pdfContext];
UIGraphicsEndPDFContext();
The exact same code from PDF1PageViewController draws beautiful charts in a normal viewController, not missing the series.
The arrays contain the data which should be displayed.
[EDIT]
This code did it for me:
UIGraphicsBeginImageContextWithOptions(pdf1.view.bounds.size, NO, 0.0);
[pdf1.view drawViewHierarchyInRect:pdf1.view.bounds afterScreenUpdates:YES];
UIImage *pdf1Image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *pdf1ImageView = [[UIImageView alloc] initWithImage:pdf1Image];
[pdf1ImageView.layer renderInContext:pdfContext];
Although the activity wheel stops spinning after drawViewHierarchyInRect and the label displaying the current page being rendered also stops updating. Anyone knows how to fix this?
The reason that you're having this problem is that the series part of the charts are rendered in openGLES, and therefore don't get rendered as part of renderInContext:.
You have a couple of options which you can investigate using. the first is the addition of some snapshotting methods on UIView in iOS7. If you're app can be restricted to iOS7 only, then snapshotViewAfterScreenUpdates: will return you a UIView which is a snapshot of the content. I think that the following (untested) code will work:
UIGraphicsBeginPDFPage();
UIView *pdfPage = [pd1.view snapshotViewAfterScreenUpdates:YES];
[pdfPage.layer renderInContext:pdfContext];
UIGraphicsEndPDFContext();
There are more details on this approach on the ShinobiControls blog at http://www.shinobicontrols.com/blog/posts/2014/02/24/taking-a-chart-snapshot-in-ios7
If restricting your app to iOS7 isn't an option then you can still achieve the result you want, but it is a little more complicated. Luckily, again, there is a blog post on the ShinobiControls blog (http://www.shinobicontrols.com/blog/posts/2012/03/26/taking-a-shinobichart-screenshot-from-your-app) which describes how to create a UIImage from a chart. This could easily be adapted to render into your PDF context, rather than the image context created in the post. There is an additional code snippet to accompany the post, available on github: https://github.com/ShinobiControls/charts-snippets/tree/master/iOS/objective-c/screenshot.
Hope this helps
sam
I am trying to create a rectangular region on my PDF that will go to a URL. The PDF is created in an iOS app using Core Graphics routines.
In a search to find anyone with a similar problem, I found this Stack Overflow question:
Embed hyperlink in PDF using Core Graphics on iOS
If I use the code provided in the answer of the above question, it sort of works for me. The text shows up and is a clickable link, but if I specify a rectangle that is much larger than the text, only the text is clickable. In addition, modified the code a bit so that I could provide a label and a URL separately, and when I did this and provided an empty string, there was nothing anywhere in the rectangle that was clickable.
However, if I use the UIKit routines for creating a PDF (basically UIGraphicsBeginPDFContextToFile and descendants), then the call to UIGraphicsSetPDFContextURLForRect creates a region in the resulting PDF file that is clickable, even if I do not provide any text.
Here is the method that I am using to create the rectangle link:
+ (void)rectLink:(NSString *)urlString frame:(CGRect)frameRect cropBox:(CGRect)cropBoxRect sourceContext:(CGContextRef)context
{
UIGraphicsPushContext(context);
#ifdef OUTPUT_TEXT_LINK_OUTLINE
CGContextSetLineWidth(context, 5.0);
CGContextSetStrokeColorWithColor(context, [UIColor purpleColor].CGColor);
CGContextStrokeRect(context, frameRect);
#endif
NSURL *url = [NSURL URLWithString:urlString];
UIGraphicsSetPDFContextURLForRect(url, frameRect);
UIGraphicsPopContext();
}
(The OUTPUT_TEXT_LINK_OUTLINE define is something that I am using for testing to make sure that the rectangle I am passing in is in fact where it is supposed to be.)
And here is a call I am making to the above method on the first page of my PDF generation:
[self rectLink:#"http://stackoverflow.com" frame:CGRectMake(474,22,28,28) cropBox:cropBoxRect sourceContext:context];
The variable cropBoxRect refers to the crop box for the current page, and context is the value that I am saving from the CGPDFContextCreateWithURL call at the start of the document.
It is better to write your own writer for writing such annotations to pdf instead of using core graphics APIs, As Apple has not taken PDFKit to cocoa-touch,
- You can follow simplest "incremental update" approach for modifying the pdf which is mentioned in PDF reference 1.7.
- You need to search the page object number in pdf then get /Annots array in it add your annotation token, append its object number to /Annots array
- Make entries for newly added/modified tokens in xref table properly.
- Add trailer token..
This is how you can add any types of annotations into pdf.
My business app requires a feature to let the user draw a signature on a UIView with his finger and save it (via button click in the toolbar) so it can be attached to an unit. These units are going to be uploaded to a server once the work is finished and already support camera picture attachments that are uploaded via Base64, so I simply want to convert the signature taken to an UIImage.
First of all, I needed a solution to draw the signature, I quickly found some sample code from Apple that seemed to meet my requirements: GLPaint
I integrated this sample code into my project with slight modifications since I work with ARC and Storyboards and didn't want the sound effects and the color palette etc., but the drawing code is a straight copy.
The integration seemed to be successful since I was able to draw the signatures on the view. So, next step was to add a save/image conversion function for the drawn signatures.
I've done endless searches and rolled dozens of threads with similar problems asked and most of them pointed to the exact same solution:
(Assumptions)
drawingView: subclassed UIView where the drawing is done on.)
<QuartzCore/QuartzCore.h> and QuartzCore.framework are included
CoreGraphics.framework is included
OpenGLES.framework is included
- (void) saveAsImage:(UIView*) drawingView
{
UIGraphicsBeginImageContext(drawingView.bounds.size);
[drawingView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentContext();
UIGraphicsEndImageContext();
}
Finally my problem: This code doesn't work for me as it always returns a blank image. Since I've already integrated support for picture attachments taken with the iPhone camera, I initially assumpted that the image processing code should work on the signature images as well.
But.. after some resultless searching I dropped that assumption, took the original GLPaint project and just added the few lines above and some code that just shows the image and it was also completely blank. So it is either an issue with that code not working on self-drawn content on UIViews or anything I'm missing.
I am basically out of ideas on this issue and hope some people can help me with it.
Best regards
Felix
I believe your problem might be you are trying to get an image from GL context. You might search around web for that but generally all you need is to call "glReadPixels" after all "draw" calls have been made.. Something like this should work:
BOOL createSnapshot;
int viewWidth, viewHeigth;
if(createSnapshot) {
uint8_t *iData = new uint8_t[viewHeigth * viewWidth * 4];
glReadPixels(0, 0, viewWidth, viewHeigth, GL_RGBA, GL_UNSIGNED_BYTE, iData);
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, iData, (viewWidth * viewHeigth * 4), NULL);
CGColorSpaceRef cref = CGColorSpaceCreateDeviceRGB();
CGImageRef cgImage = CGImageCreate(viewWidth, viewHeigth, 8, 32, viewWidth*4, cref, kCGBitmapByteOrderDefault, provider, NULL, NO, kCGRenderingIntentDefault);
UIImage *ret = [UIImage imageWithCGImage:cgImage scale:1.0f]; //the image you need
CGImageRelease(cgImage);
CGDataProviderRelease(provider);
CGColorSpaceRelease(cref);
delete [] iData;
createSnapshot = NO;
}
If you use multisampling you will need to call this after the buffers have been resolved and presenting frame buffer has been binded.
I am a bit new to iOS development and have a fairly general question regarding iOS and PDFs. I know there is built-in PDF support in iOS4+, but is it possible to open a PDF for viewing then maybe make notes on it and save it again?
I'm sure there are options like opening the PDF as a background and having a "writable" overlay over it, saving it as an image then writing it out as a PDF, but I was wondering if there was a more "inherent" way to do so.
Thanks.
I am working on the same issue. I'm starting to look at FastPDFKit http://mobfarm.eu/fastpdfkit ... You are supposed to be able to add bookmarks, view outline, perform search and highlight. I'm trying to see if I can overlay a series of things like images, graphics and text elements. Then I need to merge the pdf with the additions.
I think we are trying to do the same thing.
For searching, see this answer: Text searching PDF
For the overlay, you can just draw the pdf youself, and add you own overlay:
UIGraphicsBeginImageContextWithOptions(pageRect.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
// Flip the context so that the PDF page is rendered
// right side up.
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// Scale the context so that the PDF page is rendered
// at the correct size for the zoom level.
CGContextScaleCTM(context, pdfScale, pdfScale);
CGContextTranslateCTM(context, -pageRect.origin.x, -pageRect.origin.y);
CGContextDrawPDFPage(context, pdfPage);
CGContextRestoreGState(context);
// callback for custom overlay drawing (example_
if ([document shouldDrawOverlayRectForSize:size]) {
[document drawOverlayRect:pageRect inContext:context forPage:page zoomScale:1.0 size:size];
}
Update: Just a note, pdf can contain rotation information, so the actual drawing should read that metadata and transform the context accordingly.
There has been many Questions recently about drawing PDF's.
Yes, you can render PDF's very easily with a UIWebView but this cant give the performance and functionality that you would expect from a good PDF viewer.
You can draw a PDF page to a CALayer or to a UIImage. Apple even have sample code to show how draw a large PDF in a Zoomable UIScrollview
But the same issues keep cropping up.
UIImage Method:
PDF's in a UIImage don't optically
scale as well as a Layer approach.
The CPU and memory hit on generating
the UIImages from a PDFcontext
limits/prevents using it to create a
real-time render of new zoom-levels.
CATiledLayer Method:
Theres a significant Overhead (time)
drawing a full PDF page to a CALayer: individual tiles can be seen rendering (even with a tileSize tweak)
CALayers cant be prepared ahead of
time (rendered off-screen).
Generally PDF viewers are pretty heavy on memory too. Even monitor the memory usage of apple's zoomable PDF example.
In my current project, I'm developing a PDF viewer and am rendering a UIImage of a page in a separate thread (issues here too!) and presenting it while the scale is x1. CATiledLayer rendering kicks in once the scale is >1. iBooks takes a similar double take approach as if you scroll the pages you can see a lower res version of the page for just less than a second before a crisp version appears.
Im rendering 2 pages each side of the page in focus so that the PDF image is ready to mask the layer before it starts drawing.Pages are destroyed again when they are +2 pages away from the focused page.
Does anyone have any insights, no matter how small or obvious to improve the performance/ memory handling of Drawing PDF's? or any other issues discussed here?
EDIT: Some Tips (Credit- Luke Mcneice,VdesmedT,Matt Gallagher,Johann):
Save any media to disk when you can.
Use larger tileSizes if rendering on TiledLayers
init frequently used arrays with placeholder objects, alternitively another design approach is this one
Note that images will render faster than a CGPDFPageRef
Use NSOperations or GCD & Blocks to prepare pages ahead
of time.
call CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault); before CGContextDrawPDFPage to reduce memory usage while drawing
init'ing your NSOperations with a docRef is a bad idea (memory), wrap the docRef into a singleton.
Cancel needless NSOperations When you can, especially if they will be using memory, beware of leaving contexts open though!
Recycle page objects and destroy unused views
Close any open Contexts as soon as you don't need them
on receiving memory warnings release and reload the DocRef and any page Caches
Other PDF Features:
Getting Links inside a PDF (and here and here)
Understanding the PDF Rect for link positioning
Converting PDF annot datestrings
Getting the target of the link (Getting the page number from the /Dest array)
Getting a table of contents
Document title and Keywords
Getting Raw Text (and here and Here and here (positioning focused))
Searching(and here) (doesn't work with all PDFs (some just show weird characters, I guess it's an encoding issue but I'm not sure) -Credit BrainFeeder)
CALayer and Off-Screen Rendering - render the next page for fast/smooth display
Documentation
Quartz PDFObjects (Used for meta info, annotations, thumbs)
Abobe PDF Spec
Example projects
Apple/ ZoomingPDF - zooming, UIScrollView, CATiledLayer
vfr/ reader - zooming, paging, UIScrollView, CATiledView
brow/ leaves - paging with nice transitions
/ skim - everything it seems (PDF reader/editor for OSX)
I have build such kind of application using approximatively the same approach except :
I cache the generated image on the disk and always generate two to three images in advance in a separate thread.
I don't overlay with a UIImage but instead draw the image in the layer when zooming is 1. Those tiles will be released automatically when memory warnings are issued.
Whenever the user start zooming, I acquire the CGPDFPage and render it using the appropriate CTM. The code in - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) context is like :
CGAffineTransform currentCTM = CGContextGetCTM(context);
if (currentCTM.a == 1.0 && baseImage) {
//Calculate ideal scale
CGFloat scaleForWidth = baseImage.size.width/self.bounds.size.width;
CGFloat scaleForHeight = baseImage.size.height/self.bounds.size.height;
CGFloat imageScaleFactor = MAX(scaleForWidth, scaleForHeight);
CGSize imageSize = CGSizeMake(baseImage.size.width/imageScaleFactor, baseImage.size.height/imageScaleFactor);
CGRect imageRect = CGRectMake((self.bounds.size.width-imageSize.width)/2, (self.bounds.size.height-imageSize.height)/2, imageSize.width, imageSize.height);
CGContextDrawImage(context, imageRect, [baseImage CGImage]);
} else {
#synchronized(issue) {
CGPDFPageRef pdfPage = CGPDFDocumentGetPage(issue.pdfDoc, pageIndex+1);
pdfToPageTransform = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFMediaBox, layer.bounds, 0, true);
CGContextConcatCTM(context, pdfToPageTransform);
CGContextDrawPDFPage(context, pdfPage);
}
}
issue is the object containg the CGPDFDocumentRef. I synchronize the part where I access the pdfDoc property because I release it and recreate it when receiving memoryWarnings. It seems that the CGPDFDocumentRef object do some internal caching that I did not find how to get rid of.
For a simple and effective PDF viewer, when you require only limited functionality, you can now (iOS 4.0+) use the QuickLook framework:
First, you need to link against QuickLook.framework and #import
<QuickLook/QuickLook.h>;
Afterwards, in either viewDidLoad or any of the lazy initialization methods:
QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
previewController.currentPreviewItemIndex = indexPath.row;
[self presentModalViewController:previewController animated:YES];
[previewController release];
Since iOS 11, you can use the native framework called PDFKit for displaying and manipulating PDFs.
After importing PDFKit, you should initialize a PDFView with a local or a remote URL and display it in your view.
if let url = Bundle.main.url(forResource: "example", withExtension: "pdf") {
let pdfView = PDFView(frame: view.frame)
pdfView.document = PDFDocument(url: url)
view.addSubview(pdfView)
}
Read more about PDFKit in the Apple Developer documentation.