Objective C - Create PDF (Mac OSX / Cocoa) - objective-c

I'm following this guide to create PDF but I don't understand what I have to code on myDrawnContent function : https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_pdf/dq_pdf.html#//apple_ref/doc/uid/TP30001066-CH214-CJBFFHHA
Using this guide I'm able to create a PDF with the function:
-(void) MyCreatePDFFile:(CGRect)pageRect filename:(const char *)filename;
I need to code the -(void)myDrawContent:(CGContextRef) pdfContext; I want to set a Tittle on the top, a NSImage and a NSString after the image, how i do that?
Adiccionaly: for that I found this code:
NSString * path = #"/Users/admin/Downloads/prueba.pdf";
PDFDocument * pdf = [[PDFDocument alloc]init];
NSImage *image = [self getRepImage];
PDFPage * page = [[PDFPage alloc ] initWithImage:image];
[pdf insertPage:page atIndex: [pdf pageCount]];
[pdf writeToFile:path];
It creates a pdf with a NSImage but I how do i add text ?
Thank you

The PDFKit classes like PDFDocument and PDFPage do not let you add text. If you need to create a PDF with text, use the data structure CGContext in the Quartz framework to create a PDF context to create a PDF file and use Core Text to draw text in the PDF context.
To create a PDF context create a CGContext object. You must supply a URL for the PDF file. The second argument is a rectangle that specifies the page size. If you want a standard 8.5 by 11 inch page, you can pass NULL. The third and final argument is an optional dictionary of auxiliary information. You can pass NULL for this option.
Call the CGContext function beginPDFPage to create a PDF page that you can draw in. Draw your image. For small amounts of text, call the Core Text function CTLineCreateWithAttributedString to create a line for the text. Call the Core Text function CTLineDraw to draw the text in the PDF context. If you have large amounts of text to draw, you will need to create a Core Text framesetter and use that to create and draw frames of text.
Call the CGContext function endPDFPage to finish drawing the page. Repeat the calls to beginPDFPage and endPDFPage to draw additional pages. When you are finished, call the CGContext function closePDF to close the PDF context and save the PDF file.

Related

Unable to save pdf form data using PDFKit iOS 11

I have loaded a pdf form using PDFKit in iOS 11. I am trying to save the editable fields ( Checkbox, TextBox) values.
How do I get back the editable PDFDocument object to save the PDF contents using
(BOOL)writeToFile:(NSString *)path;
Thanks. I did something like this. I added and Observer and on receiving notification when the pdf changes write the contents to a filepath
PDFView *view = notification.object;
PDFDocument *myDocument = view.document;
NSData* content = myDocument.dataRepresentation;
[content writeToFile:newPath atomically:YES];
That worked for me.

how to display data from plist in pdf format in a new view?

i've been hammering my brain trying to figure this one out and can't find anything in the doc's or on SO that is helpful so far. i have a project that allows the user the input data and save it to a plist. is there a way to display the data that has been stored in the plist in a new view in pdf format? what i am trying to do is to display the recorded data in a new view controller with pdf format so the user can print that list. i know there is a way but i just can't figure it out and i finally threw the towel in and here i am. i will be eternally grateful for any help guys. and girls too.
i can create a new pdf with the following code. i just can't seem to understand how to get the eta from the plist to display.
- (IBAction)didClickMakePDF {
[self setupPDFDocumentNamed:#"NewPDF" Width:850 Height:1100];
[self beginPDFPage];
CGRect textRect = [self addText:#"This is some nice text here, don't you agree?"
withFrame:CGRectMake(kPadding, kPadding, 400, 200) fontSize:48.0f];
CGRect blueLineRect = [self addLineWithFrame:CGRectMake(kPadding, textRect.origin.y + textRect.size.height + kPadding, _pageSize.width - kPadding*2, 4)
withColor:[UIColor blueColor]];
UIImage *anImage = [UIImage imageNamed:#"tree.jpg"];
CGRect imageRect = [self addImage:anImage
atPoint:CGPointMake((_pageSize.width/2)-(anImage.size.width/2), blueLineRect.origin.y + blueLineRect.size.height + kPadding)];
[self addLineWithFrame:CGRectMake(kPadding, imageRect.origin.y + imageRect.size.height + kPadding, _pageSize.width - kPadding*2, 4)
withColor:[UIColor redColor]];
[self finishPDF];
}
So, you've got your PDF context and some text loaded from your plist. You need to decide how it will be laid out to be rendered into the PDF. Core Text can make a really nice job of it. The quick and easy route to get you started is:
start by flipping the context
CGContextScaleCTM(pdfContext, 1.0, -1.0);
CGContextTranslateCTM(pdfContext, 0.0, -bounds.size.height);
draw your text
[text drawAtPoint:CGPointMake(x, y) withFont:[UIFont boldSystemFontOfSize:48.0f]];
where you will obviously want to change:
the text content in a loop
the y position so each line is drawn further down the page
the font
Images can be drawn into the context in the same way.
Then, move on to Core Text to do a better job with paragraphs of text.
"When you draw to the PDF context using CGContext functions the drawing operations are recorded in PDF format. The PDF commands that represent the drawing are written to the destination specified when you create the PDF graphics context."
This comes from the same page referenced above: https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGPDFContext/Reference/reference.html
A CGPDFContext is "just" a CGContext. You could set a color in it using "CGContextSetCMYKFillColor" for example or draw text in it using the NSString "drawInRect" method.

PDFView doesn't update when adding PDFPage to the PDFDocument

I'm making an application to scan multiple page pdf files. I have a PDFView and a PDFThumbnailView that are linked. The first time a scan is completed, I create a new PDFDocument and set it to PDFView. Then whenever another scan is completed I add a PDFPage to [pdfView document].
Now the problem is whenever a page is added, neither the PDFView or PDFThumbnailView update to show the new document with the extra page. That is until I zoom in or out, then they both update to show the document with the new page.
The temporary solution I have now (zoom in and then autoscale) is certainly not the best one. Take for example when you have already zoomed in on the document, and you scan a new page, the view will then autoscale. I tried [pdfView setNeedsDisplay:YES] before but that doesn't seem to work.
This is the method where the scan arrives as NSData:
- (void)scannerDevice:(ICScannerDevice *)scanner didScanToURL:(NSURL *)url data:(NSData *)data {
//Hide the progress bar
[progressIndicator stopAnimation:nil];
//Create a pdf page from the data
NSImage *image = [[NSImage alloc] initWithData:data];
PDFPage *page = [[PDFPage alloc] initWithImage:image];
//If the pdf view has a document
if ([pdfView document]) {
//Set the page number and add it to the document
[page setValue:[NSString stringWithFormat:#"%d", [[pdfView document] pageCount] + 1] forKey:#"label"];
[[pdfView document] insertPage:page atIndex:[[pdfView document] pageCount]];
} else {
//Create a new document and add the page
[page setValue:#"1" forKey:#"label"];
PDFDocument *document = [[PDFDocument alloc] init];
[document insertPage:page atIndex:0];
[pdfView setDocument:document];
}
//Force a redraw for the pdf view so the pages are shown properly
[pdfView zoomIn:self];
[pdfView setAutoScales:YES];
}
Does anyone know of a way where I can add a PDFPage and have the PDFView update without messing with the zoom state of the PDFView?
You need to manually call:
- (void)layoutDocumentView
I imagine the reason that this is not called manually is to allow coalescing of multiple changes into one update.
This is documented:
The PDFView actually contains several subviews, such as the document
view (where the PDF is actually drawn) and a “matte view” (which may
appear as a gray area around the PDF content, depending on the
scaling). Changes to the PDF content may require changes to these
inner views, so you must call this method explicitly if you use PDF
Kit utility classes to add or remove a page, rotate a page, or perform
other operations affecting visible layout.
This method is called automatically from PDFView methods that affect
the visible layout (such as setDocument:, setDisplayBox: or zoomIn:).
The best solution I got so far to refresh manually the PDFView with annotation was to move the PDFView to another page and come back to the page you need to refresh because:
- (void)layoutDocumentView didn't work for me in my case.
Then:
[self.pdfView goToLastPage:nil];
[self.pdfView goToPage:destinationPage];

How to generate grid in PDF using Cocoa?

I'm a beginner to Cocoa and Objective-C.
I want to make a Cocoa application that will generate a grid of boxes (used for practicing Chinese calligraphy) to export as a PDF, similar to this online generator: http://incompetech.com/graphpaper/chinesequarter/.
How should I generate the grid? I've tried to use Quartz with a CustomView, but didn't manage to get very far. Also, once the grid is drawn in the CustomView, what is the method for "printing" that to a PDF?
Thanks for the help.
How should I generate the grid?
Implement a custom view that draws it.
I've tried to use Quartz with a CustomView, …
That's one way; AppKit drawing is the other. Most parts of them are very similar, though; AppKit is directly based on PostScript, while Quartz is indirectly based on PostScript.
… but didn't manage to get very far.
You should ask a more specific question about your problem.
Also, once the grid is drawn in the CustomView, what is the method for "printing" that to a PDF?
Send it a dataWithPDFInsideRect: message, passing its bounds.
Note that there is no “once the grid is drawn in the CustomView”. Though there may be some internal caching, conceptually, a view does not draw once and hold onto it; it draws when needed, every time it's needed, into where it's needed. When the window needs to be redrawn, Cocoa will tell any views that are in the dirty area to (re)draw, and they will draw ultimately to the screen. When you ask for PDF data, that will also tell the view to draw, and it will draw into a context that records PDF data. This allows the view both to be lazy (draw only when needed) and to draw differently in different contexts (e.g., when printing).
Oops, you were asking about Cocoa and this is Cocoa Touch, but I'll leave it here as it may be some use (at least to others who find this later).
You can draw things in the view and then put what's there into a pdf.
This code will take what's drawn in a UIView (called sheetView here), put it into a pdf, then put that as an attachment in an email (so you can see it for now). You'll need to reference the protocol MFMailComposeViewControllerDelegate in your header.
if ([MFMailComposeViewController canSendMail]) {
//set up PDF rendering context
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, sheetView.bounds, nil);
UIGraphicsBeginPDFPage();
//tell our view to draw (would normally use setNeedsDisplay, but need drawn now).
[sheetView drawRect:sheetView.bounds];
//remove PDF rendering context
UIGraphicsEndPDFContext();
//send PDF data in mail message as an attachment
MFMailComposeViewController *mailComposer = [[[MFMailComposeViewController alloc] init] autorelease];
mailComposer.mailComposeDelegate = self;If
[mailComposer addAttachmentData:pdfData mimeType:#"application/pdf" fileName:#"SheetView.pdf"];
[self presentModalViewController:mailComposer animated:YES];
}
else {
if (WARNINGS) NSLog(#"Device is unable to send email in its current state.");
}
You'll also need this method...
#pragma mark -
#pragma mark MFMailComposeViewControllerDelegate protocol method
//also need to implement the following method, so that the email composer can let
//us know that the user has clicked either Send or Cancel in the window.
//It's our duty to end the modal session here.
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
[self dismissModalViewControllerAnimated:YES];
}

NSImageRep confusion

I have an NSImage that came from a PDF, so it has one representation, of type NSPDFImageRep. I do an image setDataRetained:YES; to make sure that it remains a NSPDFImageRep. Later, I want to change the page, so I get the rep, and set the current page. This is fine.
The problem is that when I draw the image, only the 1st page comes out.
My impression is that when I draw an NSImage, it picks a representation, and draws that representation. Now, the image only has one rep, so that's the one that is being drawn, and that's the PDFrep. So, why when I draw the image, is it not drawing the correct page?
HOWEVER, when I draw the representation itself, I get the correct page.
What am I missing?
NSImage does a caching of the NSImageRep, when first displayed. In the case of NSPDFImageRep, the "setCacheMode:" message has no effect. Thus, the page that will be displayed will always be the first page. See this guide for more information.
You have then two solutions:
Drawing the representation directly.
Call the "recache" message on the NSImage to force the rasterization of the selected page.
An alternative mechanism to draw a PDF is to use the CGPDF* functions. To do this, use CGPDFDocumentCreateWithURL to create a CGPDFDocumentRef object. Then, use CGPDFDocumentGetPage to get a CGPDFPageRef object. You can then use CGContextDrawPDFPage to draw the page into your graphics context.
You may have to apply a transform to ensure that the document ends up sized like you want. Use a CGAffineTransform and CGContextConcatCTM to do this.
Here is some sample code pulled out of one of my projects:
// use your own constants here
NSString *path = #"/path/to/my.pdf";
NSUInteger pageNumber = 14;
CGSize size = [self frame].size;
// if we're drawing into an NSView, then we need to get the current graphics context
CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)path, kCFURLPOSIXPathStyle, NO);
CGPDFDocumentRef document = CGPDFDocumentCreateWithURL(url);
CGPDFPageRef page = CGPDFDocumentGetPage(document, pageNumber);
// in my case, I wanted the PDF page to fill in the view
// so we apply a scaling transform to fir the page into the view
double ratio = size.width / CGPDFPageGetBoxRect(page, kCGPDFTrimBox).size.width;
CGAffineTransform transform = CGAffineTransformMakeScale(ratio, ratio);
CGContextConcatCTM(context, transform);
// now we draw the PDF into the context
CGContextDrawPDFPage(context, page);
// don't forget memory management!
CGPDFDocumentRelease(document);