Please read my scenario carefully,
I have one UITextView and one UIImageView bottom of TextView.
Each time there will be dynamic content in TextView and accordingly that, I am asking User to make a signature and it will be displayed as an image in bottom ImageView.
Now the requirement is I have to pass these details on the server along with Signature in one PDF file, So I have to create PDF file which contains both TextView text and ImageView image.
Note: TextView is containing Html text also, so it should show in the same format in PDF also.
Check below Images as required and current pdfs.
This is required PDF
This is current PDF
Only Put the code which can be helpful for both HTML support and Image merge with text. Please don't show simple PDF creation as I have done it already.
you don't need a 3rd party library, Cocoa and Cocoa touch have rich PDF support. I've stubbed you out a little start, do this in your viewController. There may be a few small errors, Ive been using swift for a couple of years now but I used my very rusty objC here because you tagged the question that way. Let me know any problems, good luck
-(NSData *)drawPDFdata{
// default pdf..
// 8.5 X 11 inch #72dpi
// = 612 x 792
CGRect rct = {{0.0 , 0.0 } , {612.0 , 792.0}}
NSMutableData *pdfData = [[NSMutableData alloc]init];
UIGraphicsBeginPDFContextToData(pdfData, rct, nil);
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
UIGraphicsBeginPDFPage();
//textView drawing
CGContextSaveGState(pdfContext);
CGContextConcatCTM(pdfContext, CGAffineTransformMakeTranslation(50.0,50.0));//this is just an offset for the textView drawing. You will want to play with the values, espeecially if supporting multiple screen sizes you might tranform the scale as well..
[textView.layer renderInContext:pdfContext]
CGContextRestoreGState(pdfContext);
//imageView drawing
CGContextSaveGState(pdfContext);
CGContextConcatCTM(pdfContext, CGAffineTransformMakeTranslation(50.0,50.0)); //this is just an offset for the imageView drawing. Thee same stuff applies as above..
[imageView.layer renderInContext:pdfContext]
CGContextRestoreGState(pdfContext);
//cleanup
UIGraphicsEndPDFContext();
return pdfData;
}
here's a couple of client functions to use this NSData
//ways to use the pdf Data
-(Bool)savePDFtoPath: (NSString *)path {
return [ [self drawPDFdata] writeToFile:path atomically:YES] ;
}
//requires Quartz framework.. (can be drawn straight to a UIView)
// note you MAY owe a CGPDFDocumentRelease() on the result of this function (sorry i've not used objC in a couple of years...)
-(CGPDFDocument *)createPDFdocument {
NSData *data = [self drawPDFdata];
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL , data , sizeof(data) ,NULL);
CGPDFDocument result = CGPDFDocumentCreateWithProvider(provider);
CGDataProviderRelease(provider); //not sure if this is still required under ARC?? (def not in swift)
return result;
}
Try this useful third party library :
https://github.com/iclems/iOS-htmltopdf
Use this function for your problem :
+ (id)createPDFWithHTML:(NSString*)HTML pathForPDF:(NSString*)PDFpath pageSize:(CGSize)pageSize margins:(UIEdgeInsets)pageMargins successBlock:(NDHTMLtoPDFCompletionBlock)successBlock errorBlock:(NDHTMLtoPDFCompletionBlock)errorBlock;
Related
Is it possible to repeat an image in ios similar to CSS function
background-image:imageurl;
background-repeat :repeat-x;
so that an image is perfectly scaled for iphone and iPad screen sizes
You could try this:
- (UIImage *) imageFromAssetImageNamed: (NSString *) name {
NSString * fullKeyPath = [[NSBundle mainBundle] pathForResource:name
ofType:#"png"
inDirectory:#"assets"] ;
return [UIImage imageWithContentsOfFile:fullKeyPath] ;
}
- (UIColor *) colorPatternFromAssetImageNamed: (NSString *) name {
return [UIColor colorWithPatternImage:[self imageFromAssetImageNamed:name]] ;
}
You can then set the background color, for example, using:
self.window.backgroundColor = [self colorPatternFromAssetImageNamed:#"my-bg-color"] ;
You will still need to adjust the frame to control how much of the width/height is covered.
You have loads of options.
Core Graphics gives you
CGContextDrawTiledImage()
UIImage gives you
drawPatternInRect:
(Probably a wrapper of the above )
But the most useful thing is to look at transformations.
CGAffineTransform in the Quartz 2D Drawing guide is the thing you want to read about.
It's pretty cheap and easy in draw rect to just do some iteration that draws the same image at a bunch of locations that are in CG terms translations of the image, meaning it's drawn at another place.
You can even draw to an image context before drawing to a view and get a cached representation so you don't need to always redraw every thing.
Core Animation has transforms as well.
this question has been posted before (Creating PDF from UIScrollView in iphone app) and the code I am using is from here.
Here is the code
-(void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
// Creates a mutable data object for updating with binary data, like a byte array
NSMutableData *pdfData = [NSMutableData data];
// Points the pdf converter to the mutable data object and to the UIView to be converted
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView.layer renderInContext:pdfContext];
// remove PDF rendering context
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
}
Setup: I have a UIScrollView, inside is a UIView. I want to save the entire UIView (950,500px) and it's in a space (the UIScrollView size) of (520,500). The PDF being generated is only the size of UIScrollView (520,500)
I read the answer but he apparently changed the code but it doesn't work for me. I've been trying to fix this all day.
I'm a beginner so please indicate anything I should add to my question that I missed. Thank you.
PS - this is an iPad app.
The context should have the size of the scrollview's content size, not the bounds.
Then you need to temporarily resize the scrollview to its content size, render it in the PDF context, and restore the size of the scrollview to its original size.
UIGraphicsBeginPDFContextToData(pdfData, (CGRect){0,0, scrollView.contentSize}, nil);
CGRect origSize = scrollView.frame;
CGRect newSize = origSize;
newSize.size = scrollView.contentSize;
[scrollView setFrame:newSize];
[scrollView.layer renderInContext:pdfContext];
[scrollView setFrame:origSize];
Check ScrollViewToPDF example and you will understand what you need to do.
It uses same scrollview's layer renderInContext but here PDF is created according to your requirement such as one page PDF or multiple page PDF
Note : It captures all visible as well as invisible part of scrollView
If anyone was wondering, I found how to fix this.
Since my UIView is IN a UIScrollView, and I use another class for the UIView, when I call the method and chose the parameter aView, I just put in self.
So in my UIView class, when I want the PDF to be generated I type in
[self createPDFfromUIView:self saveToDocumentsWithFileName:UIViewPDF];
Is anyone using this delegate method ? I get callbacks on
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
But not on this one. The documentation seems a bit ambiguous about what this is intended for
- (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange
According to the documentation on the Web this is what its intended for:
Discussion
The text view calls this method if the user taps or long-presses the text attachment and its image property is not nil. Implementation of this method is optional. You can use this method to trigger an action in addition to displaying the text attachment inline with the text.
And here is Xcode 5 documentation:
Asks the delegate if the specified text view should display the provided text attachment in the given range of text.
The text view calls this method when a text attachment character is recognized in its text container by a data detector. Implementation of this method is optional. You can use this method to trigger an alternative action besides displaying the text attachment inline with the text in the given range.
EDIT:
Mmm OK I figured out the problem. If I paste an image in from iOS then it works, however if the image was pasted in from OS X it does not. It seems that the actual attachment formats used are not quite the same on both platforms despite the fact that the image appears to show up correctly in the text views. On closer inspection the NSTextAttachment classes don't appear to be the same on iOS as on OS X.
If anyone can shed any light on the cross platform compatibility here please do.
Also if I save the attributed string after pasting the image in on iOS and then retrieve it and display it in the UITextView interaction with the attachment is no longer possible. It would appear that when storing the image the image is actually placed in contents if contents is nil. So maybe I am going to have to iterate through all attachments to check what data is stored where particularly to figure out any differences in behaviour across the OS X and iOS platforms.
FURTHER EDIT:
The method only gets called if the attachment image is NOT nil and despite the fact that an image is displayed the actual image attribute can actually be nil, silly me! Anyway the fix seems to be to check all the attachments in the attributed string and to set their image attribute to something, usually the contents of the fileWrapper. The default NSTextAttachment behaviour seems to be to store the image in the fileWrapper when its archived but it does not do the reverse when its unarchived. Anyway I want to retain the original image in the attachment but depending on the device display a suitably scaled version of the original !
The chief thing is that the text view's editable property must be NO and it's selectable property must be YES. Those are the only circumstances under which this delegate method is called. If you are getting shouldBeginEditing then your text field is editable which is exactly what it must not be.
Here is what I do to ensure the NSTextAttachments image attribute gets set when restoring the UITextView's attributed string from archived data (in this case whenever the user selects a record from a Core Data store).
I set the UITextView up as a delegate for textStorage and in the didProcessEditing look for any attachments that may have been added and then check that their image attribute is set. I am also setting the scaling factor on the image to make sure the image scales appropriately for the device.
This way I don't loose the original resolution of the image and if the user wants to view it in more detail I provide the option to open it in an image browser window from a popup menu.
Hope this helps someone else.
EDIT:
Check here for more details on NSTextView and UITextView http://ossh.com.au/design-and-technology/software-development/
- (void)textStorage:(NSTextStorage *)textStorage didProcessEditing:(NSTextStorageEditActions)editedMask range:(NSRange)editedRange changeInLength:(NSInteger)delta {
//FLOG(#"textStorage:didProcessEditing:range:changeInLength: called");
[textStorage enumerateAttributesInRange:editedRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:
^(NSDictionary *attributes, NSRange range, BOOL *stop) {
// Iterate over each attribute and look for a Font Size
[attributes enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([[key description] isEqualToString:NSAttachmentAttributeName]) {
NSTextAttachment *attachment = obj;
//Reset the image attribute and scale for the device size if necessary
[self resetAttachmentImage:attachment];
}
}];
}];
}
- (void)resetAttachmentImage:(NSTextAttachment*)attachment {
UIImage *image = [attachment image];
float factor = 2;
if (image == nil) {
if (attachment.fileWrapper == nil || !attachment.fileWrapper.isRegularFile) {
attachment.image = [UIImage imageNamed:#"unknown_attachment.png"];
return;
}
//Usually retrieved from store
image = [UIImage imageWithData:attachment.fileWrapper.regularFileContents];
} else {
// Convert any pasted image
image = [UIImage imageWithData:UIImagePNGRepresentation(image)];
}
float imgWidth = image.size.width;
// If its wider than the view scale it to fit
if (imgWidth > _viewWidth) {
factor = imgWidth / _viewWidth + 0.5;
attachment.image = [UIImage imageWithData:UIImagePNGRepresentation(image) scale:factor];
} else {
attachment.image = [UIImage imageWithData:UIImagePNGRepresentation(image) scale:_scale];
}
}
I want to generate a good-looking PDF in my iOS 6 app.
I've tried:
UIView render in context
Using CoreText
Using NSString drawInRect
Using UILabel drawRect
Here is a code example:
-(CGContextRef) createPDFContext:(CGRect)inMediaBox path:(NSString *) path
{
CGContextRef myOutContext = NULL;
NSURL * url;
url = [NSURL fileURLWithPath:path];
if (url != NULL) {
myOutContext = CGPDFContextCreateWithURL ((__bridge CFURLRef) url,
&inMediaBox,
NULL);
}
return myOutContext;
}
-(void)savePdf:(NSString *)outputPath
{
if (!pageViews.count)
return;
UIView * first = [pageViews objectAtIndex:0];
CGContextRef pdfContext = [self createPDFContext:CGRectMake(0, 0, first.frame.size.width, first.frame.size.height) path:outputPath];
for(UIView * v in pageViews)
{
CGContextBeginPage (pdfContext,nil);
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformMakeTranslation(0, (int)(v.frame.size.height));
transform = CGAffineTransformScale(transform, 1, -1);
CGContextConcatCTM(pdfContext, transform);
CGContextSetFillColorWithColor(pdfContext, [UIColor whiteColor].CGColor);
CGContextFillRect(pdfContext, v.frame);
[v.layer renderInContext:pdfContext];
CGContextEndPage (pdfContext);
}
CGContextRelease (pdfContext);
}
The UIViews that are rendered only contain a UIImageView + a bunch of UILabels (some with and some without borders).
I also tried a suggestion found on stackoverflow: subclassing UILabel and doing this:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
BOOL isPDF = !CGRectIsEmpty(UIGraphicsGetPDFContextBounds());
if (!layer.shouldRasterize && isPDF)
[self drawRect:self.bounds]; // draw unrasterized
else
[super drawLayer:layer inContext:ctx];
}
But that didn't change anything either.
No matter what I do, when opening the PDF in Preview the text parts are selectable as a block, but not character per character, and zooming the pdf shows it is actually a bitmap image.
Any suggestions?
This Tutorial From Raywenderlich Saved my Day.Hope it will work for you too.
http://www.raywenderlich.com/6818/how-to-create-a-pdf-with-quartz-2d-in-ios-5-tutorial-part-2
My experience when I did this last year was that Apple didn't provide any library to do it. I ended up importing an open source C library (libHaru). Then I added a function for outputting to it in each class in my view hierarchy. Any view with subviews would call render on its subviews. My UILabels, UITextFields, UIImageViews, UISwitches etc would output their content either as text or graphics accordingly I also rendered background colors for some views.
It wasn't very daunting, but libHaru gave me some problems with fonts so iirc I ended up just using the default font and font size.
It works good with UILabels except that you have to work around a bug:
Rendering a UIView into a PDF as vectors on an iPad - Sometimes renders as bitmap, sometimes as vectors
I have a large drilldown menu in my iOS app.
The menu content originated from XML.
It's stored in an SQLite database and Webview is being to used.
Why is Webview being used? Because the text has styling (font size/ color).
Here's the problem: When you scroll down the menu, the text bars that were previously offscreen remain invisible for a 1/2 second. They're taking awhile to load. And you scroll up again, the top items that were moved off screen now take time to appear as well.
Is there a way to improve this performance when working with WebView? Is there anyway to eliminate this visual loading time? Are there approaches we should consider? Thanks for any help.
Are you putting web views inside of a UITableView? You really can't do that; they'll fight each other (and this is explicitly documented as not supported).
There are two main solutions: redesign this part of the program as a web app, or layout with Core Text. Doing all the work in a single UIWebView is much faster, but requires moving between JavaScript and Objective-C which is difficult to develop and debug. If you go this way, I generally recommend that you build the JavaScript part as a "mini-app" that you can develop and debug in Safari, and then drop-in.
For simple layout, Core Text isn't too hard. If you create a CFAttributedString, here's all you need to draw a very simple frame:
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
CGAffineTransform
transform = CGAffineTransformMakeScale(1, -1);
CGAffineTransformTranslate(transform,
0,
-self.bounds.size.height);
self.transform = transform;
self.backgroundColor = [UIColor whiteColor];
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGPathRef path = CGPathCreateWithRect(self.bounds, NULL);
CFAttributedStringRef
attrString = (__bridge CFTypeRef)self.attributedString;
// Create the framesetter using the attributed string
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
// Create a single frame using the entire string (CFRange(0,0))
// that fits inside of path.
CTFrameRef
frame = CTFramesetterCreateFrame(framesetter,
CFRangeMake(0, 0),
path,
NULL);
// Draw the frame into the current context
CTFrameDraw(frame, context);
CFRelease(frame);
CFRelease(framesetter);
CGPathRelease(path);
}
And the required plug: Yes, my book will include over 20 pages on all kinds of fancy text layout, mostly using Core Text (just finishing that chapter today). But for the simple stuff the above is enough, as long as you don't need cut and paste.