I have a leak that I can't track down. I am new to CoreText (and C in general) so please be gentle! The static analyser doesn't show any problems but Instruments does in this method:
- (void)drawAttributedStringInBubbleInContext:(CGContextRef)context {
static CGFloat const kTextInset = 10;
// Add the text to the bubble using an ellipse path inside the main speech bubble if the text property is set
if (text) {
// Create an attributed string from the text property
NSMutableAttributedString *bubbleText = [[NSMutableAttributedString alloc] initWithString:text];
// Justify the text by adding a paragraph style
CFIndex stringLength = CFAttributedStringGetLength((CFAttributedStringRef)bubbleText);
CTTextAlignment alignment = kCTJustifiedTextAlignment;
CTParagraphStyleSetting _settings[] = {
{kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment}
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(_settings, sizeof(_settings) / sizeof(_settings[0]));
CFRange stringRange = CFRangeMake(0, stringLength);
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)bubbleText, stringRange, kCTParagraphStyleAttributeName, paragraphStyle);
CFRelease(paragraphStyle);
// Layout the text within an elliptical frame
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)bubbleText);
// Create elliptical path that is inset from the frame of the view
CGMutablePathRef path = CGPathCreateMutable();
CGRect drawingRect = self.bounds;
drawingRect.origin.x = kTextInset;
drawingRect.origin.y = kTextInset;
drawingRect.size.width -= 2 * kTextInset;
drawingRect.size.height -= 2 * kTextInset;
CGPathAddEllipseInRect(path, NULL, drawingRect);
// Create a text frame from the framesetter and the path
CTFrameRef textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);
// Draw the text frame in the view's graphics context
CTFrameDraw(textFrame, context);
// Clean up
CGPathRelease(path);
CFRelease(framesetter);
[bubbleText release];
}
}
The main culprit according to Instruments is the CTFrameRef textFrame = line, although I thought I have released everything properly.
That is the culprit, the Core Foundation rule for Create methods are you have to release them. Apple releases it properly in an example in the Core Text Programming Guide.
// Clean up
CGPathRelease(path);
CFRelease(framesetter);
CFRelease(textFrame);
Related
I want to draw text in the NPAPI plugin CGContextRef, but i don’t know to make it work.
I get the CGContext Ref as follow:
int16_t NPP_HandleEvent(NPP instance, void* event)
{
int16_t iRet = 0;
PluginObject *obj = (PluginObject *)instance->pdata;
NPCocoaEvent *cocoaEvent = (NPCocoaEvent *)event;
switch(cocoaEvent->type)
{
case NPCocoaEventDrawRect:
obj->m_NPContext = CGContextRetain(cocoaEvent->data.draw.context);
DrawSealOnContext(obj->m_NPContext, obj->m_pstSealAPInfo);
iRet = 1;
break;
default:
iRet = 0;
break;
}
return iRet;
}
In function DrawSealOnContext, I want to draw an Ellipse and some text in the window. the function as follow:
int DrawSealOnContext(CGContextRef contextRef, PSEAL_APPEARANCE_INFO pstSealAPInfo)
{
// draw ellipses
CGRect rect = {2, 25, 146, 100};
CGContextSetLineWidth(contextRef, 4.0);
CGContextSetStrokeColorWithColor(contextRef, [NSColor blueColor].CGColor);
CGContextBeginPath(contextRef);
CGContextAddEllipseInRect(contextRef, rect);
CGContextDrawPath(contextRef, kCGPathStroke);
// draw text
CGContextSetFillColorWithColor(contextRef, [NSColor blueColor].CGColor);
NSMutableParagraphStyle * paragraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
[paragraphStyle setAlignment:NSCenterTextAlignment];
NSDictionary * attributes = [NSDictionary dictionaryWithObject:paragraphStyle
forKey:NSParagraphStyleAttributeName];
NSString * mystr = #“hello\n and";
NSRect strFrame = { { 0, 0 }, { 150, 150 } };
[mystr drawInRect:strFrame withAttributes:attributes];
}
I can get the ellipse on the screen, but the text doesn’t show up?
I also try this:
// draw text
CGContextShowText(contextRef, "hello", 5);
It doesn’t work either.
what’s wrong with my program, really appreciate your
answers.
I have solve this problem, with the help of this article:
http://www.cocoabuilder.com/archive/cocoa/240527-setting-cgcontextref-to-the-current-context.html
The problem is that, function
[mystr drawInRect:strFrame withAttributes:attributes];
draw test in current context, but my current is not the the context "CGContextRef contextRef" i decleared. So, before i used drawInRect method, i need to set contextRef my current context.
How to set CGContextRef to current context, please refer to the website i pasted.
Hope this answer can solve your problem!
I used CoreText to create the following text which fills a rectangle and surrounds an area in which I'll insert an image.
attrString = [[NSMutableAttributedString alloc] initWithString:string];
CFAttributedStringSetAttribute((__bridge CFMutableAttributedStringRef) attrString, CFRangeMake(0, attrString.length), kCTFontAttributeName, font);
CFAttributedStringSetAttribute((__bridge CFMutableAttributedStringRef) attrString, CFRangeMake(0, attrString.length), kCTForegroundColorAttributeName, ForegroundTextColor);
CFAttributedStringSetAttribute((__bridge CFMutableAttributedStringRef) attrString, CFRangeMake(0, attrString.length), kCTParagraphStyleAttributeName, paragraphStyle);
/* Get a framesetter to draw the actual text */
CTFramesetterRef fs = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) attrString);
CTFrameRef frame = CTFramesetterCreateFrame(fs, CFRangeMake(0, attrString.length), pathToRenderIn, NULL);
/* Draw the text */
CTFrameDraw(frame, ctx);
/* Draw the text */
CTFrameDraw(frame, ctx);
In a later method, I'm using the attached statement to change the color of a subset of the text:
[attrString addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(startRangeValue, endRangeValue)];
Unfortunately, the color doesn't change. Thanks in advance for your help.
I'm adding my comment as an answer since following it solved the problem:
After you add an attribute to a string you have to redraw. Basically what you see on the screen is not an NSString object that is changeable, it is a graphical drawing of an object that it read. If you change the object you have to tell it to draw the object again.
So the answer to the problem is that you need to call the drawing code every time you try to change anything about the string you are displaying.
So I am currently working on a subclass of UIView which uses TBXML to parse a SVG XML files and strip the 'g' path instructions out. Then it passes these sets of instructions to SvgToBezier - a simple parser that takes the path instructions as input and returns a UIBezierPath object. Then it draws them in the drawRect method.
The paths render fine, but the problem is they don't line up with each other the way they're supposed to (they way they show up in google chrome, adobe illustrator, etc)
I know the SVGKit library allows parsing of complex svg files, however I want to write this myself so I can expand my understanding of the concepts involved.
First I'll show you my drawRect method, followed by an example SVG file, it's expected output, and it's actual output drawn on the iPhone in a 300x300 rect.
- (void)drawRect:(CGRect)rect
{
//getting the XML from set property
TBXMLElement *root = self.svgRoot.rootXMLElement;
//getting the rect tag, which is the background
TBXMLElement *rectColor = [TBXML childElementNamed:#"rect" parentElement:root];
//getting the first g tag
TBXMLElement *g1 = [TBXML childElementNamed:#"g" parentElement:root];
//and it's child path tag
TBXMLElement *path1 = [TBXML childElementNamed:#"path" parentElement:g1];
//getting the sibbling g tag
TBXMLElement *g2 = g1->nextSibling;
//and it's child path tag
TBXMLElement *path2 = [TBXML childElementNamed:#"path" parentElement:g2];
//create a context reference
CGContextRef ctx = UIGraphicsGetCurrentContext();
//flip the context so the origin points jive
CGContextTranslateCTM(ctx, 0, rect.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
//get fill color for first path
const char *str1 = [[TBXML valueOfAttributeNamed:#"fill" forElement:g1] cStringUsingEncoding:NSASCIIStringEncoding];
long x1 = strtol(str1+1, NULL, 16);
UIColor *rgb1 = UIColorFromRGB(x1);
//get fill color for second path
const char *str2 = [[TBXML valueOfAttributeNamed:#"fill" forElement:g2] cStringUsingEncoding:NSASCIIStringEncoding];
long x2 = strtol(str2+1, NULL, 16);
UIColor *rgb2 = UIColorFromRGB(x2);
//get fill color for rect background
const char *str3 = [[TBXML valueOfAttributeNamed:#"fill" forElement:rectColor] cStringUsingEncoding:NSASCIIStringEncoding];
long x3 = strtol(str3+1, NULL, 16);
UIColor *rgb3 = UIColorFromRGB(x3);
//turn them into CGColors
CGColorRef color1 = rgb1.CGColor;
CGColorRef color2 = rgb2.CGColor;
CGColorRef color3 = rgb3.CGColor;
//draw the rect background and fill it
CGContextSetFillColorWithColor(ctx, color3);
CGContextFillRect(ctx, CGRectMake(0, 0, rect.size.width, rect.size.height));
//make a bezier path from the first path tag using SvgToBezier
SvgToBezier *bezier1 = [[SvgToBezier alloc] initFromSVGPathNodeDAttr:[TBXML valueOfAttributeNamed:#"d" forElement:path1] rect:rect];
//make second bezier path
SvgToBezier *bezier2 = [[SvgToBezier alloc] initFromSVGPathNodeDAttr:[TBXML valueOfAttributeNamed:#"d" forElement:path2] rect:rect];
//set fill color for first bezier
CGContextSetFillColorWithColor(ctx, color1);
//draw it
UIBezierPath *bezierPath1 = bezier1.bezier;
CGContextAddPath(ctx, bezierPath1.CGPath);
CGContextDrawPath(ctx, kCGPathEOFill);
//second bezier
CGContextSetFillColorWithColor(ctx, color2);
UIBezierPath *bezierPath2 = bezier2.bezier;
CGContextAddPath(ctx, bezierPath2.CGPath);
CGContextDrawPath(ctx, kCGPathEOFill);
}
Here's the Raw SVG
Here's the Expected Drawing Result
And here's the Actual iPhone Output (screenshot from iOS Simulator)
I've been experimenting with all kinds of calculations and CGAffineTransforms.. left all that out of the code because it will just confuse things imo.
Thanks for any and all help!
Cheers
EDIT:
Including another case, just to illustrate the inconsistency (consistency?) of the problem.
Raw
Expected
Actual
Hey just wanted to let anyone who read this know, I haven't solved the issue and I don't plan on it. I'm using UIWebView instead. Safari seems to render SVGs just fine. I used an HTML wrapper to make the SVG scale to fit its viewport.
I like the UIWebView better anyway because it deems to draw faster than my method.
Cheers
I have heard that I can display a NSAttributedString using CoreText, can anyone say me how (The simplest way)?
Please, don't answer with CATextLayer or OHAttributedLabel.
I know that there are a lot of questions about this in this forum, but I haven't find the answer
Thanks!!
Simplest way? Something like this:
CGContextRef context = UIGraphicsGetCurrentContext();
// Flip the coordinate system
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// Create a path to render text in
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, self.bounds );
// An attributed string containing the text to render
NSAttributedString* attString = [[NSAttributedString alloc]
initWithString:...];
// create the framesetter and render text
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
CFRangeMake(0, [attString length]), path, NULL);
CTFrameDraw(frame, context);
// Clean up
CFRelease(frame);
CFRelease(path);
CFRelease(framesetter);
I think that the simplest way (using Core Text) is:
// Create the CTLine with the attributed string
CTLineRef line = CTLineCreateWithAttributedString(attrString);
// Set text position and draw the line into the graphics context called context
CGContextSetTextPosition(context, x, y);
CTLineDraw(line, context);
// Clean up
CFRelease(line);
Using a Framesetter is more efficient IF you are drawing lots of text but this is the method recommended by Apple if you just need to display a small amount of text (like a label) and doesn't require you to create a path or frame (since it is done for you automatically by CTLineDraw).
Starting from ios 6 you can do the following :
NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle alloc] init];
[paragrahStyle setLineSpacing:40];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragrahStyle range:NSMakeRange(0, [labelText length])];
cell.label.attributedText = attributedString ;
Hi all I am working on Objective-C. my previous Question was How can I edit PDF files in an iOS application?
after a lot of googling I found out the following. display the pdf in UIWebView, extract the data using C/javascript and edit it. I am still not sure about this procedure. now what I have planned is
1) display the pdf
2) when user wants to edit the pdf I covert the pdf to text and allow him to edit it
3) tryin to save wil convert the content back to pdf.
is this a gud way to proceed?? im k with step 1. now how do i convert pdf--> text and text-->pdf.
thanks in advance
When you load a custom document type (doc, ppt, pdf, etc) into a UIWebView, the webview returns a nil HTML string, even via javascript. There's a few suggestions for extracting PDF text here.
But turning the string back into a PDF is different. If you want to retain the formatting of the original PDF, I'm rather sure that's impossible because NSAttributedString on iOS doesn't do much. But this will work for plain text or NSAttributedString, if its possible:
NSData *PDFDataFromString(NSString *str) {
NSMutableData *data = [NSMutableData data];
//Create an NSAttributedString for CoreText. If you find a way to translate
//PDF into an NSAttributedString, you can skip this step and simply use an
//NSAttributedString for this method's argument.
NSAttributedString* string = [[[NSAttributedString alloc] initWithString:str] autorelease];
//612 and 792 are the dimensions of the paper in pixels. (8.5" x 11")
CGRect paperRect = CGRectMake(0.0, 0.0, 612, 792);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef) string);
CGSize requiredSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, [string length]), NULL, CGSizeMake(paperRect.size.width - 144, 1e40), NULL);
//Subtract the top and bottom margins (72 and 72), so they aren't factored in page count calculations.
NSUInteger pageCount = ceill(requiredSize.height / (paperRect.size.height - 144));
CFIndex resumePageIndex = 0;
UIGraphicsBeginPDFContextToData(data, paperRect, nil);
for(NSUInteger i = 0; i < pageCount; i++)
{
//After calculating the required number of pages, break up the string and
//draw them into sequential pages.
UIGraphicsBeginPDFPage();
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState (currentContext);
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
CGMutablePathRef framePath = CGPathCreateMutable();
//72 and 72 are the X and Y margins of the page in pixels.
CGPathAddRect(framePath, NULL, CGRectInset(paperRect, 72.0, 72.0));
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(resumePageIndex, 0), framePath, NULL);
resumePageIndex += CTFrameGetVisibleStringRange(frameRef).length;
CGPathRelease(framePath);
CGContextTranslateCTM(currentContext, 0, paperRect.size.height);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CTFrameDraw(frameRef, currentContext);
CFRelease(frameRef);
CGContextRestoreGState (currentContext);
}
CFRelease(framesetter);
UIGraphicsEndPDFContext();
return data;
}