Cocoa PDFKit add text to PDF - objective-c

I am trying to add text to t a PDFPage. Specifically, I want to add it to the header/footer of the pdf document. I have looked everywhere for an example of doing this but I can't find it. If someone can point to a tutorial or give a quick example of how I can add text to PDFPage it would be really helpful. Thanks.

You can do it using Quartz 2D - PDF Document Creation, Viewing, and Transforming. This code will help you to write into file:
[someString drawAtPoint:CGPointMake(100, 200) withFont:[NSFont systemFontOfSize:20.0f]];

The short answer is to subclass PDFPage and override the drawing methods. There's a description here.

The answers here are incomplete, after putting various pieces together, here is complete sample code (Objective-C):
// Need to create pdf Graphics context for Drawing text
CGContextRef pdfContextRef = NULL;
CFURLRef writeFileUrl = (CFURLRef)[NSURL fileURLWithPath:writeFilePath];
if(writeFileUrl != NULL){
pdfContextRef = CGPDFContextCreateWithURL(writeFileUrl, NULL, NULL);
}
// Start page in PDF context
CGPDFContextBeginPage(pdfContextRef, NULL);
NSGraphicsContext* pdfGraphicsContext = [NSGraphicsContext graphicsContextWithCGContext:pdfContextRef flipped:false];
[NSGraphicsContext saveGraphicsState];
// Need to set the current graphics context
[NSGraphicsContext setCurrentContext:pdfGraphicsContext];
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont fontWithName:#"Helvetica" size:26], NSFontAttributeName,[NSColor blackColor], NSForegroundColorAttributeName, nil];
NSAttributedString * currentText=[[NSAttributedString alloc] initWithString:#"Write Something" attributes: attributes];
// [currentText drawInRect: CGRectMake(0, 300, 500, 100 )];
[currentText drawAtPoint:NSMakePoint(100, 100)];
[NSGraphicsContext restoreGraphicsState];
// Close all the created pdf Contexts
CGPDFContextEndPage(pdfContextRef);
CGPDFContextClose(pdfContextRef);

Related

CTFrame dropping glyphs at the edge

I am trying to render Arabic text in my iOS app with custom TTF font (scheherazade) using core-text, which works for the most part - however certain glyphs at the edge of the CTFrame are dropped.
When I adjust the frame-size to make the dropped-glyphs appear in the interior of the frame, they display corretly, which leads me believe something is going wrong in inside CTFrameDraw. Below is the code I'm using to render the Arabic-text:
CGContextRef context = UIGraphicsGetCurrentContext();
// Flip the coordinate system
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, v.textFrame.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGMutablePathRef path = CGPathCreateMutable(); //1
CGPathAddRect(path, NULL, v.textFrame );
CGFloat minLineHeight = 60.0;
CGFloat maxLineHeight = 60.0;
CTTextAlignment paragraphAlignment = kCTRightTextAlignment;
CTLineBreakMode lineBrkMode = kCTLineBreakByWordWrapping;
CTParagraphStyleSetting setting[4] = {
{kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &paragraphAlignment},
{kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat), &minLineHeight},
{kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat), &maxLineHeight},
{kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &lineBrkMode}
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(setting, 4);
NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys:
(id)v.arabicFont, (id)kCTFontAttributeName,
paragraphStyle, (id)kCTParagraphStyleAttributeName,
nil];
CFRelease(paragraphStyle);
NSAttributedString* attString = [[[NSAttributedString alloc]
initWithString:v.verseText attributes:attr] autorelease]; //2
CTFramesetterRef framesetter =
CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString); //3
CTFrameRef frame =
CTFramesetterCreateFrame(framesetter,
CFRangeMake(0, [attString length]), path, NULL);
CTFrameDraw(frame, context); //4
CFRelease(frame); //5
CFRelease(path);
CFRelease(framesetter);
Also attached are the screenshots showing the problem I face. Any help would be much appreciated. Thanks.
invalid: http://stellarbeacon.com.au/invalid.png
valid : http://stellarbeacon.com.au/valid.png
There are some bugs in CoreText related to determining the correct frame size. Some of these where fixed in iOS 6, e.g. http://www.cocoanetics.com/2012/02/radar-coretext-line-spacing-bug/
If your problem still exists there then you should file a Radar with the specifics. Also you should open a call with Apple DTS which can probably provide you with a workaround. Often - if your problem is indeed a bug - then you get your DTS call credited back.
PS: try to display your text with my DTCoreText views which do manual layouting and display and see if the problem can be reproduced there. If not then you have your workaround.

CATextLayer + NSAttributtedString + CTParagraphStyleRef

I want to have some text with a custom line-spacing, so I wrote an attribute string with CTParagraphStyleAttributte and pass it to my CATextLayer:
UIFont *font = [UIFont systemFontOfSize:20];
CTFontRef ctFont = CTFontCreateWithName((CFStringRef)font.fontName,
font.pointSize, NULL);
CGColorRef cgColor = [UIColor whiteColor].CGColor;
CGFloat leading = 25.0;
CTTextAlignment alignment = kCTRightTextAlignment; // just for test purposes
const CTParagraphStyleSetting styleSettings[] = {
{kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof(CGFloat), &leading},
{kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment}
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(styleSettings, 2));
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(id)ctFont, (id)kCTFontAttributeName,
(id)cgColor, (id)kCTForegroundColorAttributeName,
(id)paragraphStyle, (id)kCTParagraphStyleAttributeName,
nil];
CFRelease(ctFont);
CFRelease(paragraphStyle);
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc]
initWithString:string
attributes:attributes];
_textLayer.string = attrStr;
[attrStr release];
But the line height is not changing. I think I am missing something here but I don't know what.
I've tried with kCTParagraphStyleSpecifierLineSpacingAdjustment and kCTParagraphStyleSpecifierLineSpacing but either of them don't seem to work (?). I tried also to set the alignment using kCTParagraphStyleSpecifierAlignment (I know CATextLayer has a property for that) just to test kCTParagraphStyleAttributeName is indeed working and it didn't.
I've noticed that even if I pass some crazy values (for example: CTParagraphStyleCreate(styleSettings, -555);) which leads me to ask myself: Does CATextLayer support paragraph attributes? If so, what am I missing here?
I tried your code, putting the NSAttributedString in a CATextLayer, and it ignored the formatting, as you said.
Then I tried drawing the exact same attributed string to a UIView drawRect method using CTFrameDraw, and it obeyed all your formatting. I can only assume that CATextLayer ignores the majority of its formatting. The CATextLayer Class Reference has a number of warnings about what it does in the interests of efficiency.
If you really need to draw to a CALayer, not a UIView, you may be able to create your own CALayer subclass or delegate and do the drawing there.
- (void)drawRect:(CGRect)rect
{
//
// Build attrStr as before.
//
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect bounds = [self bounds];
// Text ends up drawn inverted, so we have to reverse it.
CGContextSetTextMatrix(ctx, CGAffineTransformIdentity);
CGContextTranslateCTM( ctx, bounds.origin.x, bounds.origin.y+bounds.size.height );
CGContextScaleCTM( ctx, 1, -1 );
// Build a rectangle for drawing in.
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, bounds);
// Create the frame and draw it into the graphics context
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) attrStr);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
CFRelease(framesetter);
CFRelease(path);
// Finally do the drawing.
CTFrameDraw(frame, ctx);
CFRelease(frame);
}

programmatically create links in a PDF file

While I think it is a basic question, I didn't manage to find a response that works yet. I am creating a PDF file by stroking paths to a PDF context, and I want different areas on the drawing to be hyperlinks to outside contents (http://bla.bla). I'd be happy even with areas that are non-intersecting rectangles. Anyone knows how to do that?
check the answer to this question it works:Embed hyperlink in PDF using Core Graphics on iOS.
- (void) drawTextLink:(NSString *) text inFrame:(CGRect) frameRect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform ctm = CGContextGetCTM(context);
// Translate the origin to the bottom left.
// Notice that 842 is the size of the PDF page.
CGAffineTransformTranslate(ctm, 0.0, 842);
// Flip the handedness of the coordinate system back to right handed.
CGAffineTransformScale(ctm, 1.0, -1.0);
// Convert the update rectangle to the new coordiante system.
CGRect xformRect = CGRectApplyAffineTransform(frameRect, ctm);
NSURL *url = [NSURL URLWithString:text];
UIGraphicsSetPDFContextURLForRect( url, xformRect );
CGContextSaveGState(context);
NSDictionary *attributesDict;
NSMutableAttributedString *attString;
NSNumber *underline = [NSNumber numberWithInt:NSUnderlineStyleSingle];
attributesDict = #{NSUnderlineStyleAttributeName : underline, NSForegroundColorAttributeName : [UIColor blueColor]};
attString = [[NSMutableAttributedString alloc] initWithString:url.absoluteString attributes:attributesDict];
[attString drawInRect:frameRect];
CGContextRestoreGState(context);
}

Turning an NSImage* into a CGImageRef?

Is there an easy way to do this that works in 10.5?
In 10.6 I can use nsImage CGImageForProposedRect: NULL context: NULL hints: NULL
If I'm not using 1b black and white images (Like Group 4 TIFF), I can use bitmaps, but cgbitmaps seem to not like that setup... Is there a general way of doing this?
I need to do this because I have an IKImageView that seems to only want to add CGImages, but all I've got are NSImages. Currently, I'm using a private setImage:(NSImage*) method that I'd REALLY REALLY rather not be using...
Found the following solution on this page:
NSImage* someImage;
// create the image somehow, load from file, draw into it...
CGImageSourceRef source;
source = CGImageSourceCreateWithData((CFDataRef)[someImage TIFFRepresentation], NULL);
CGImageRef maskRef = CGImageSourceCreateImageAtIndex(source, 0, NULL);
All the methods seem to be 10.4+ so you should be fine.
[This is the long way around. You should only do this if you're still supporting an old version of OS X. If you can require 10.6 or later, just use the CGImage method instead.]
Create a CGBitmapContext.
Create an NSGraphicsContext for the bitmap context.
Set the graphics context as the current context.
Draw the image.
Create the CGImage from the contents of the bitmap context.
Release the bitmap context.
As of Snow Leopard, you can just ask the image to create a CGImage for you, by sending the NSImage a CGImageForProposedRect:context:hints: message.
Here's a more detailed answer for using - CGImageForProposedRect:context:hints::
NSImage *image = [NSImage imageNamed:#"image.jpg"];
NSRect imageRect = NSMakeRect(0, 0, image.size.width, image.size.height);
CGImageRef cgImage = [image CGImageForProposedRect:&imageRect context:NULL hints:nil];
Just did this using CGImageForProposedRect:context:hints:...
NSImage *image; // an image
NSGraphicsContext *context = [NSGraphicsContext currentContext];
CGRect imageCGRect = CGRectMake(0, 0, image.size.width, image.size.height);
NSRect imageRect = NSRectFromCGRect(imageCGRect);
CGImageRef imageRef = [image CGImageForProposedRect:&imageRect context:context hints:nil];
As of 2021, CALayers contents can directly be feed with NSImage but you still may get an [Utility] unsupported surface format: LA08 warning.
After a couple of days research and testing around i found out that this Warning is triggered if you created an NSView with backingLayer, aka CALayer and just used an NSImage to feed its contents. This alone is not much of a problem. But if you try to render it via [self.layer renderInContext:ctx], each rendering will trigger the warning again.
To make use simple, i created an NSImage extension to fix this..
#interface NSImage (LA08FIXExtension)
-(id)CGImageRefID;
#end
#implementation NSImage (LA08FIXExtension)
-(id)CGImageRefID {
NSSize size = [self size];
NSRect rect = NSMakeRect(0, 0, size.width, size.height);
CGImageRef ref = [self CGImageForProposedRect:&rect context:[NSGraphicsContext currentContext] hints:NULL];
return (__bridge id)ref;
}
#end
see how it does not return an CGImageRef directly but a bridged cast to id..! This does the trick.
Now you can use it like..
CALayer *somelayer = [CALayer layer];
somelayer.frame = ...
somelayer.contents = [NSImage imageWithName:#"SomeImage"].CGImageRefID;
PS: posted her because if you got that problem also, google will lead you to this Thread of answers anyway, and all above answers inspired me for this fix.

NSBitmapImageRep and multi-page TIFFs

I've got a program that can open TIFF documents and display them. I'm using setFlipped:YES.
If I'm just dealing with single page image files, I can do
[image setFlipped: YES];
and that, in addition to the view being flipped, seems to draw the image correctly.
However, for some reason, setting the flipped of the image doesn't seem to affect the flippedness of the individual representations.
This is relevant because the multiple images of a multi-page TIFF seem to appear as different "representations" of the same image. So, if I just draw the IMAGE, it's flipped, but if I draw a specific representation, it isn't flipped. I also can't seem to figure out how to chose which representation is the default one that gets drawn when you draw the NSImage.
thanks.
You shouldn't use the -setFlipped: method to control how the image is drawn. You should use a transform based on the flipped-ness of the context you are drawing into. Something like this (a category on NSImage):
#implementation NSImage (FlippedDrawing)
- (void)drawAdjustedInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta
{
NSGraphicsContext* context = [NSGraphicsContext currentContext];
BOOL contextIsFlipped = [context isFlipped];
if (contextIsFlipped)
{
NSAffineTransform* transform;
[context saveGraphicsState];
// Flip the coordinate system back.
transform = [NSAffineTransform transform];
[transform translateXBy:0 yBy:NSMaxY(dstRect)];
[transform scaleXBy:1 yBy:-1];
[transform concat];
// The transform above places the y-origin right where the image should be drawn.
dstRect.origin.y = 0.0;
}
[self drawInRect:dstRect fromRect:srcRect operation:op fraction:delta];
if (contextIsFlipped)
{
[context restoreGraphicsState];
}
}
- (void)drawAdjustedAtPoint:(NSPoint)point
{
[self drawAdjustedAtPoint:point fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
- (void)drawAdjustedInRect:(NSRect)rect
{
[self drawAdjustedInRect:rect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
- (void)drawAdjustedAtPoint:(NSPoint)aPoint fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta
{
NSSize size = [self size];
[self drawAdjustedInRect:NSMakeRect(aPoint.x, aPoint.y, size.width, size.height) fromRect:srcRect operation:op fraction:delta];
}
#end
I believe that the answer is that Yes, different pages are separate representations, and the correct way to deal with them is to turn them into images with:
NSImage *im = [[NSImage alloc] initWithData:[representation TIFFRepresentation]];
[im setFlipped:YES];