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

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.

Related

Objective C - Create PDF (Mac OSX / Cocoa)

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.

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];
}

CGContextDrawPDFPage displays white or garbled text

In the process of updating my iPad app I've been attempting to draw a page from an existing PDF document into a Core Graphics context then save it as a new PDF, but am having difficulty getting the text to display properly. Images in the newly-created PDF look great, but text rarely appears correctly: more often that not it appears white/invisible or garbled. When the text is invisible, I am still able to to select where it -should- be and copy/paste correctly into a text editor. Is this an issue related to the limited number of fonts available on the iPad?
My code is as follows:
CGPDFDocumentRef document = CGPDFDocumentCreateWithProvider(dataProvider);
CGPDFPageRef page = CGPDFDocumentGetPage(document, pageNumberToRetrieve);
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
UIGraphicsBeginPDFContextToFile(pathToFile, pageRect, nil);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextBeginPage(context, NULL);
// I don't think this line is necessary, but I have tried both with and without it.
CGContextSetTextDrawingMode (context, kCGTextFill);
CGContextDrawPDFPage(context, page);
CGContextEndPage(context);
UIGraphicsEndPDFContext();
CGDataProviderRelease(dataProvider);
CGPDFDocumentRelease(document);
If anyone has any suggestions I would greatly appreciate hearing them.
Thanks for your time.
Rob
Drawing into an image context does not pose a problem (text displays correctly).
What I am trying to do is create a -new- PDF file containing just a few pages from the original PDF. It seems that text does not draw correctly into the new file for some reason.
The information is there (I can select text by 'guessing' where it should be in Preview) but it doesn't render. I assume CGContextDrawPDFPage writes the string to the PDF file, but doesn't draw it because it doesn't know what the characters of that font 'look like'?
I thought the point of embedded fonts in PDFs was that programs would be able to perform these sorts of manipulations even if that font wasn't installed on the system (in this case, the iPad). Is this a limitation of the format, or the Quartz framework?
Do you want to render on the screen? I don't see the need for UIGraphicsBeginPDFContextToFile?
However, to render on the screen, you can use something like this:
pageReference = CGPDFDocumentGetPage(pdfReference, page);
CGContextRef context = UIGraphicsGetCurrentContext();
#try {
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, scale, -scale);
CGContextSaveGState(context);
#try {
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(pageReference, kCGPDFCropBox, self.bounds, 0, true);
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, pageReference);
}
#finally {
CGContextRestoreGState(context);
}
}
#finally {
UIGraphicsEndImageContext();
}

Another IKImageView Question: copying a region

I'm trying to use the select and copy feature of the IKImageView. If all you want to do is have an app with an image, select a portion and copy it to the clipboard, it's easy. You set the copy menu pick to the first responder's copy:(id) method and magically everything works.
However, if you want something more complicated, like you want to copy as part of some other operation, I can't seem to find the method to do this.
IKImageView doesn't seem to have a copy method, it doesn't seem to have a method that will even tell you the selected rectangle!
I have gone through Hillegass' book, so I understand how the clipboard works, just not how to get the portion of the image out of the view...
Now, I'm starting to think that I made a mistake in basing my project on IKImageView, but it's what Preview is built on (or so I've read), so I figured it had to be stable... and anyway, now it's too late, I'm too deep in this to start over...
So, other than not using IKImageView, any suggestions on how to copy the select region to the clipboard manually?
EDIT actually, I have found the copy(id) method, but when I call it, I get
<Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 16 bits/pixel; 1-component color space; kCGImageAlphaPremultipliedLast; 2624 bytes/row.
Which obviously doesn't happen when I do a normal copy through the first-responder... I understand the error message, but I'm not sure where it's getting those parameters from...
Is there any way to trace through this and see how this is happening? A debugger won't help for obvious reasons, as well as the fact that I'm doing this in Mozilla, so a debugger isn't an option anyway...
EDIT 2 It occurs to me that the copy:(id) method I found may be copying the VIEW rather than copying a chunk of the image to the clipboard, which is what I need.
The reason I thought it was the clipboard copy is that in another project, where I'm copying from an IKImageView to the clipboard straight from the edit menu, it just sends a copy:(id) to the firstResponder, but I'm not actually sure what the firstresponder does with it...
EDIT 3 It appears that the CGBitmapContextCreate error is coming from [imageView image] which, oddly enough, IS a documented method.
It's possible that this is happening because I'm putting the image in there with a setImage:(id) method, passing it an NSImage*... Is there some other, more clever way of getting an NSImage into an IKImageView?
The -copy: method in IKImageView does what every other -copy: method does: it copies the current selection to the clipboard. It is, however, implemented as a private method in IKImageView for some reason.
You can just call it directly:
[imageView copy:nil];
This will copy whatever is currently selected to the clipboard.
I don't think there's a way to directly access the image content of the current selection in IKImageView using public methods, this is a good candidate for a bug report/feature request.
You can, however, use the private method -selectionRect to get a CGRect of the current selection and use that to extract the selected portion of the image:
//stop the compiler from complaining when we call a private method
#interface IKImageView (CompilerSTFU)
- (CGRect)selectionRect
#end
#implementation YourController
//imageView is an IBOutlet connected to your IKImageView
- (NSImage*)selectedImage
{
//get the current selection
CGRect selection = [imageView selectionRect];
//get the portion of the image that the selection defines
CGImageRef selectedImage = CGImageCreateWithImageInRect([imageView image],(CGRect)selection);
//convert it to an NSBitmapImageRep
NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:selectedImage] autorelease];
CGImageRelease(selectedImage);
//create an image from the bitmap data
NSImage* image = [[[NSImage alloc] initWithData:[bitmap TIFFRepresentation]] autorelease];
//in 10.6 you can skip converting to an NSBitmapImageRep by doing this:
//NSImage* image = [[NSImage alloc] initWithCGImage:selectedImage size:NSZeroSize];
return image;
}
#end
Ok, so the copy: nil fails, and the [imageView image] fails, but it turns out that I have another copy of the NSImage from when I added it into the view in the first place, so I could that. Also, CGImageCreateWithImageInRect expects a CGImageRef not an NSImage*, so I had to do some conversions.
In addition, for some reason the selection rectangle is flipped, either it's bottom origined, and the image is top, or the other way around, so I had to flip it.
And for some reason, the compiler suddenly started complaining that NSRect isn't the same type as CGRect (Which implies that it suddenly went from 32 to 64 bit or something... not sure why...)
Anyway, here is my copy of selectedImage:
- (NSImage*)selectedImage
{
//get the current selection
CGRect selection = flipCGRect(imageView, [imageView selectionRect]);
//get the portion of the image that the selection defines
struct CGImage * full = [[doc currentImage] CGImageForProposedRect: NULL context: NULL hints: NULL];
CGImageRef selectedImage = CGImageCreateWithImageInRect( full, selection);
//convert it to an NSBitmapImageRep
NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:selectedImage] autorelease];
CGImageRelease(selectedImage);
// //create an image from the bitmap data
NSImage* image = [[[NSImage alloc] initWithData:[bitmap TIFFRepresentation]] autorelease];
// //in 10.6 you can skip converting to an NSBitmapImageRep by doing this:
//NSImage* image = [[NSImage alloc] initWithCGImage:selectedImage size:NSZeroSize];
return image;
}
I wrote flipCGRect, and [doc currentImage] returns an NSImage*...

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);