Wrap Text in CGContextShowTextAtPoint - objective-c

I'm trying to display texts in a CGContext and I want to know how to wrap the texts. How can I make this possible?
Here's my code:
NSString *text1 = #"These three first articles describe the God in whom we believe. The pictures of the triangle represents the Triune God), of Jesus Christ and of the dove representing the Holy Spirit are grouped together in order to manifest their intimate relationships and unity.";
- (void) renderPageAtIndex:(NSUInteger)index inContext:(CGContextRef)ctx {
if(index>[images count]-1)return;
UIImage *image = [images objectAtIndex:index];
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
CGAffineTransform transform = aspectFit(imageRect,
CGContextGetClipBoundingBox(ctx));
NSString *text1 = [script objectAtIndex:index];
char* text = (char *)[text1 cStringUsingEncoding:NSASCIIStringEncoding];
CGContextSelectFont(ctx, "Arial", 30, kCGEncodingMacRoman);
CGContextConcatCTM(ctx, transform);
CGContextDrawImage(ctx, imageRect, [image CGImage]);
CGContextSetTextPosition(ctx, 0.0f, round(30 / 4.0f));
CGContextSetTextDrawingMode(ctx, kCGTextFill);
CGContextSetRGBFillColor(ctx, 0, 0, 0, 1);
CGContextShowTextAtPoint(ctx,10,10,text, strlen(text));
}

If you need more advanced text formatting (like wrapping), use Core Text instead. To display a simple string in a rectangle, use
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
CGPath p = CGPathCreateMutable();
CGPathAddRect(p, rect);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0), p, NULL);
CTFrameDraw(frame, context);

Related

Objective-C cocoa create PDF from txt

I am porting my iOS App to MAC-OS and I am stopped because I cannot create a PDF file programmatically.
What I need to do is to create a PDF from a given .txt file
My code in ios version is this:
- (void) createPDF:(NSString *)fileName withContent:(NSString *)content forSize:(int)fontSize andFont:(NSString *)font andColor:(NSColor *)color{
CGRect a4Page = CGRectMake(0, 0, DOC_WIDTH, DOC_HEIGHT);
NSDictionary *fileMetaData = [[NSDictionary alloc] init];
if (!UIGraphicsBeginPDFContextToFile(fileName, a4Page, fileMetaData )) {
NSLog(#"error creating PDF context");
return;
}
BOOL done = NO;
CGContextRef context = UIGraphicsGetCurrentContext();
CFRange currentRange = CFRangeMake(0, 0);
CGContextSetTextDrawingMode (context, kCGTextFill);
CGContextSelectFont (context, [font cStringUsingEncoding:NSUTF8StringEncoding], fontSize, kCGEncodingMacRoman);
CGContextSetFillColorWithColor(context, [color CGColor]);
// Initialize an attributed string.
CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrString, currentRange, (CFStringRef)content);
// Create the framesetter with the attributed string.
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
do {
UIGraphicsBeginPDFPage();
CGMutablePathRef path = CGPathCreateMutable();
CGRect bounds = CGRectMake(LEFT_MARGIN,
TOP_MARGIN,
DOC_WIDTH - RIGHT_MARGIN - LEFT_MARGIN,
DOC_HEIGHT - TOP_MARGIN - BOTTOM_MARGIN);
CGPathAddRect(path, NULL, bounds);
// Create the frame and draw it into the graphics context
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, currentRange, path, NULL);
if(frame) {
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0, bounds.origin.y);
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -(bounds.origin.y + bounds.size.height));
CTFrameDraw(frame, context);
CGContextRestoreGState(context);
// Update the current range based on what was drawn.
currentRange = CTFrameGetVisibleStringRange(frame);
currentRange.location += currentRange.length;
currentRange.length = 0;
CFRelease(frame);
}
// If we're at the end of the text, exit the loop.
if (currentRange.location == CFAttributedStringGetLength((CFAttributedStringRef)attrString))
done = YES;
}
while(!done);
UIGraphicsEndPDFContext();}
XCode gives to me a lot of warnings and errors:
Errors: Undefined symbols for architecture x86_64
Warnings: Implicit declaration of function 'UIGraphicsBeginPDFContextToFile' is invalid in C99 [for example]
Could someone help me to find a solution to my problem?
I tried to use CGPDFContextCreateWithURL but I didn't get any result
Thank you all

Displaying a block of text with an oversized initial letter in a UITextView

Is there any way to provide this kind of effect in a UITextView? If not, then how can I achieve this effect?
Having the same requirement, I created a UIView subclass that draws text with a drop cap. The text is drawn using core text, and as #CodaFi suggested the drop cap is drawn in a separate core text frame.
The full implementation of the class: https://gist.github.com/4596476
The meat of it looks something like this:
- (void)drawRect:(CGRect)rect {
// Create attributed strings
NSAttributedString *bodyText = [[NSAttributedString alloc] initWithString:[self.text substringWithRange:NSMakeRange(1, self.text.length -1)] attributes:_bodyAttributes];
NSAttributedString *capText = [[NSAttributedString alloc] initWithString:[[self.text substringWithRange:NSMakeRange(0, 1)] uppercaseString] attributes:_capAttributes];
CGRect capFrame = CGRectMake(0, 0, [capText size].width, [capText size].height);
// Set up graphics context
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// Create type frames
CGMutablePathRef bodyPath = CGPathCreateMutable();
CGAffineTransform transform = CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1), 0, -self.bounds.size.height);
CGPathMoveToPoint(bodyPath, &transform, CGRectGetMaxX(capFrame), 0);
CGPathAddLineToPoint(bodyPath, &transform, self.bounds.size.width, 0);
CGPathAddLineToPoint(bodyPath, &transform, self.bounds.size.width, self.bounds.size.height);
CGPathAddLineToPoint(bodyPath, &transform, 0, self.bounds.size.height);
CGPathAddLineToPoint(bodyPath, &transform, 0, CGRectGetMaxY(capFrame));
CGPathAddLineToPoint(bodyPath, &transform, CGRectGetMaxX(capFrame), CGRectGetMaxY(capFrame));
CGPathCloseSubpath(bodyPath);
CGMutablePathRef capPath = CGPathCreateMutable();
CGPathAddRect(capPath, &transform, CGRectMake(0, 0, capFrame.size.width+10, capFrame.size.height+10));
// Draw text
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) bodyText);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), bodyPath, NULL);
CFRelease(framesetter);
CTFrameDraw(frame, context);
CFRelease(frame);
framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)capText);
frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), capPath, NULL);
CFRelease(framesetter);
CTFrameDraw(frame, context);
CFRelease(frame);
}
The gist of it is you create two paths, one rectangle to contain the drop cap, and a rectangle with a notch removed for the rest of the text. The full implementation on gist allows you to control the fonts, spacing around the drop cap, and content inset of the whole view using properties.
It does not implement most of the features of a UITextView (only the features I needed) so it may not be a full solution.
Hope that helps!

Core Text a bit smaller than NSString drawInRect:?

In theory, these should be the same size, but they're not:
The text in blue is from Core Text, in black is from -[NSString drawInRect:]. Here is the code:
//Some formatting to get the correct frame
int y = MESSAGE_FRAME.origin.y + 8;
if (month) y = y + 27;
int height = [JHomeViewCellContentView heightOfMessage:self.entry.message];
CGRect rect = CGRectMake(MESSAGE_FRAME.origin.x + 8, y, MESSAGE_FRAME.size.width - 16, height);
//Draw in rect method
UIFont *font = [UIFont fontWithName:#"Crimson" size:15.0f];
[[UIColor colorWithWhite:0.2 alpha:1.0] setFill];
[self.entry.message drawInRect:rect withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentLeft];
//Frame change to suit CoreText
CGRect rect2 = CGRectMake(MESSAGE_FRAME.origin.x + 8, self.bounds.size.height - y - height, MESSAGE_FRAME.size.width - 16, height);
//Core text method
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)#"Crimson", 15.0f, NULL);
NSDictionary *attDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge_transfer id)fontRef, (NSString *)kCTFontAttributeName,
(id)[[UIColor blueColor] CGColor], (NSString *)kCTForegroundColorAttributeName,
nil];
NSAttributedString *attString = [[NSAttributedString alloc] initWithString:self.entry.message attributes:attDictionary];
//Flip the coordinate system
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, rect2);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge_retained CFAttributedStringRef)attString);
CTFrameRef theFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [attString length]), path, NULL);
CFRelease(framesetter);
CFRelease(path);
CTFrameDraw(theFrame, context);
CFRelease(theFrame);
The font is the same. I don't understand why one is being rendered differently.
It’s because NSLayoutManager uses some unusual heuristics to cope with certain fonts.
See How does line spacing work in Core Text? (and why is it different from NSLayoutManager?) for more detail.

Draw text from uitextview to pdf as it is

I have 4 editable uitextviews, user can set its font font color etc. Now i want to draw pdf of these textviews, but using [self.view.layer renderInContext:UIGraphicsGetCurrentContext()] method causes to lose its quality, so i cannot use this method, instead i am iterating over subviews and drawing the text in pdf. Now my problem is that for some textviews text is printed correctly in pdf,but for other textviews text is not drawn in proper position.This is my code to draw pdf from textview.
NSArray *arrayOfsubviews=[self.view subviews];
for (int i=0; i<[arrayOfsubviews count]; i++) {
if ([[arrayOfsubviews objectAtIndex:i]isKindOfClass:[UITextView class]]) {
UITextView *texts=[arrayOfsubviews objectAtIndex:i];
CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)texts.font.fontName, texts.font.pointSize,NULL);
CGColorRef color = texts.textColor.CGColor;
NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)ctFont, (id)kCTFontAttributeName,
color, (id)kCTForegroundColorAttributeName,nil];
NSAttributedString *stringToDraw = [[NSAttributedString alloc] initWithString:texts.text
attributes:attributesDict];
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) stringToDraw);
CGRect rect=CGRectMake(texts.frame.origin.x,916-texts.frame.origin.y-texts.frame.size.height, texts.frame.size.width, texts.frame.size.height);
CGMutablePathRef pathRef = CGPathCreateMutable();
CGPathAddRect(pathRef, NULL,rect);
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0),pathRef, NULL);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0,916);
CGContextScaleCTM(context, 1.0, -1.0);
CTFrameDraw(frameRef, context);
CGContextRestoreGState(context);
CGPathRelease(pathRef);
}
}
UIGraphicsEndPDFContext();
I use this to draw my text, works fairly well.. maybe it can point you in the right direction.
#define kBorderInset 20.0
#define kMarginInset 10.0
- (void) drawText{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(currentContext, 0.0, 0.0, 0.0, 1.0);
NSString *textToDraw = #"Your Text Here";
UIFont *font = [UIFont systemFontOfSize:14.0];
CGSize stringSize = [textToDraw sizeWithFont:font
constrainedToSize:CGSizeMake(pageSize.width - 2*kBorderInset-2*kMarginInset, pageSize.height - 2*kBorderInset - 2*kMarginInset)
lineBreakMode:UILineBreakModeWordWrap];
CGRect renderingRect = CGRectMake(kBorderInset + kMarginInset, kBorderInset + kMarginInset + 350.0, pageSize.width - 2*kBorderInset - 2*kMarginInset, stringSize.height);
[textToDraw drawInRect:renderingRect
withFont:font
lineBreakMode:UILineBreakModeWordWrap
alignment:UITextAlignmentLeft];
}
You want to use drawInContext: instead of renderInContext:. renderInContext will rasterize the text, drawInContext write encode it as editable, resolution-independent text.
Some looses of quality or blurry views are because of the frame. The vales for locate a frame in the screen are CGFloat, so you could have a frame located in the point (0.5, 5.7), obviously this dowsn't have any sense and the point.x must be 0 or 1. Decimal values confuse the Operative System, so it's recommend to ensure that frames have always values with no decimal numbers.
You can ges some more information here: http://www.cobiinteractive.com/blog/2011/06/objective-c-blurry-uiview/ or here: http://www.markj.net/iphone-uiview-blurred-blurry-view/

Core Text View has black background color

I'm having a UIView that renders some text using CoreText,everything works fine,
except for the fact that the entire view has black background color.
I've tried the most basic solutions like
[self setBackgroundColor:[UIColor clearColor]];
or
CGFloat components[] = {0.f, 0.f, 0.f, 0.f };
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef transparent = CGColorCreate(rgbColorSpace, components);
CGContextSetFillColorWithColor(context, transparent);
CFRelease(transparent);
but this didn't help at all.
I'm just pounding my head on this issue, because I can't quite understand
how does really core text works and how is this pure-C layer related
with all the other objective-c stuff.
And this doesn't help very much in solving this problem,
so I'm asking you folks, if could you provide me a solution and (most important)
an explanation about it.
FIY I'm posting also the code of the CoreText view.
it's all based on 2 functions: parseText which builds up the CFAttributedString and the
drawRect that takes care of the drawing part.
-(void) parseText {
NSLog(#"rendering this text: %#", symbolHTML);
attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), (CFStringRef)symbolHTML);
CFStringRef fontName = (CFStringRef)#"MetaSerif-Book-Accent";
CTFontRef myFont = CTFontCreateWithName((CFStringRef)fontName, 18.f, NULL);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = { 1.0, 0.3f, 0.3f, 1.f };
CGColorRef redd = CGColorCreate(rgbColorSpace, components);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0,0),
kCTForegroundColorAttributeName, redd);
CFRelease(redd);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFStringGetLength((CFStringRef)symbolHTML)), kCTFontAttributeName, myFont);
CTTextAlignment alignment = kCTLeftTextAlignment;
CTParagraphStyleSetting settings[] = {
{kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment}
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFStringGetLength((CFStringRef)attrString)), kCTParagraphStyleAttributeName, paragraphStyle);
[self calculateHeight];
}
and this is the DrawRect function:
- (void)drawRect:(CGRect)rect {
/* get the context */
CGContextRef context = UIGraphicsGetCurrentContext();
/* flip the coordinate system */
float viewHeight = self.bounds.size.height;
CGContextTranslateCTM(context, 0, viewHeight);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0, 1.0));
/* generate the path for the text */
CGMutablePathRef path = CGPathCreateMutable();
CGRect bounds = CGRectMake(0, 0, self.bounds.size.width-20.0, self.bounds.size.height);
CGPathAddRect(path, NULL, bounds);
/* draw the text */
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
CFRangeMake(0, 0), path, NULL);
CFRelease(framesetter);
CFRelease(path);
CTFrameDraw(frame, context);
CGFloat components[] = {0.f, 0.f, 0.f, 0.f };
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef transparent = CGColorCreate(rgbColorSpace, components);
CGContextSetFillColorWithColor(context, transparent);
CFRelease(transparent);
}
I hope that everything is clear, but if something looks
confusing, just ask and I'll be glad to explain myself better.
thank you in advance for this hardcore issue :)
k
Here's a question. Did you remember to set view.opaque = NO? Because if not you'll get that black background regardless of what else you do.