CGContextDrawPDFPage Leak 100% - pdf

I try to Draw a pdf with CoreGraphics, everything work fine but in instrument there is a 100% leak on : CGContextDrawPDFPage(ctx, page2);
I release with CGPDFDocumentRelease(); everytime i use CGPDFDocumentCreateWithURL();
There is any solution to release : CGContextDrawPDFPage ?
- (void)drawRect:(CGRect)rect
{
if (state == 0) {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
[[UIColor whiteColor] set];
CGContextFillRect(ctx, CGRectMake(0, 0, rect.size.width, rect.size.height));
CGContextGetCTM(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 45, -rect.size.height);
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL(((CFURLRef)url));
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, currentPage);
CGRect mediaRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
CGContextScaleCTM(ctx, (678) / mediaRect.size.width,
(rect.size.height ) / (mediaRect.size.height));
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
CGContextDrawPDFPage(ctx, page);
CGPDFDocumentRelease(pdf);
CGContextRestoreGState(ctx);
}
else
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
[[UIColor whiteColor] set];
CGContextFillRect(ctx, CGRectMake(0, 0, rect.size.width, rect.size.height));
CGContextGetCTM(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 6, -rect.size.height);
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL(((CFURLRef)url));
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, currentPage);
CGRect mediaRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
CGContextScaleCTM(ctx, (497) / mediaRect.size.width,
(rect.size.height ) / (mediaRect.size.height));
// draw it
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
CGContextDrawPDFPage(ctx, page);
CGPDFDocumentRelease(pdf);
CGContextRestoreGState(ctx);
//
CGContextSaveGState(ctx);
CGContextGetCTM(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 506, -rect.size.height);
CGPDFDocumentRef pdf2 = CGPDFDocumentCreateWithURL(((CFURLRef)url));
CGPDFPageRef page2 = CGPDFDocumentGetPage(pdf2, (currentPage + 1));
CGContextScaleCTM(ctx, (497) / mediaRect.size.width,
(rect.size.height ) / (mediaRect.size.height));
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
//Leak 100%
CGContextDrawPDFPage(ctx, page2);
CGPDFDocumentRelease(pdf2);
CGContextRestoreGState(ctx);
}
}
And I don't know why. Any idea?
This is the only leak of my app :(
I don't see where the leak come from :s
PS : state = 0 = portrait orientation
state = 1 = landscape so I draw 2 pages in landscape orientation.

Looks like you're not releasing the CGPDFPageRef instances you're getting from CGPDFDocumentGetPage.
CGPDFPageRelease(page);
Add that for page and page2 at the appropriate locations.

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

Objective C: Draw(Write) on Touch in a UIScrollView

I have this code on my touchesMoved but like the others its not working on UIScrollView
Here it is, on my touchesMoved:
touchSwiped = YES;
currentTouch = [touch locationInView:self.view];
currentTouch.y -= 5;
UIGraphicsBeginImageContext(self.view.frame.size);
[writeView.image drawInRect:CGRectMake(0, 0, 768, 1024)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 15);
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextMoveToPoint(context, endPoint.x, endPoint.y);
CGContextAddLineToPoint(context, currentTouch.x, currentTouch.y);
CGContextStrokePath(context);
writeView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
endPoint = currentTouch;
touchMoved++;
if (touchMoved == 10) {
touchMoved = 0;
}
so, i transferred it using Gesture recognizer but still it is not working.
i used the PanGestureRecognizer
here:
- (void) writePan:(UIPanGestureRecognizer *)writingRecognizerP {
switch (writingRecognizerP.state)
{
case UIGestureRecognizerStateChanged:
[scrollView setScrollEnabled:NO];
[scrollView setUserInteractionEnabled:NO];
touchSwiped = YES;
currentTouch = [writingRecognizerP locationInView:scrollView];
currentTouch.y -= 5;
UIGraphicsBeginImageContext(self.view.frame.size);
[writeView.image drawInRect:CGRectMake(0, 0, 768, 1024)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 15);
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextMoveToPoint(context, endPoint.x, endPoint.y);
CGContextAddLineToPoint(context, currentTouch.x, currentTouch.y);
CGContextStrokePath(context);
writeView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
endPoint = currentTouch;
touchMoved++;
if (touchMoved == 10) {
touchMoved = 0;
}
break;
case UIGestureRecognizerStateEnded:
[scrollView setScrollEnabled:YES];
[scrollView setUserInteractionEnabled:YES];
break;
}
}
anyone who has an idea how will i able to write on touch??
it'll be much appreciated! :)
You should do all your drawing in -drawRect:, and when something changes send your view -setNeedsDisplay or setNeedsDisplayInRect: message.

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.

How do I add a UIImage to grouped UITableViewCell so it rounds the corners?

I'm trying to add images to table cells in a grouped UITableView but the corners of the images are not clipped. What's the best way to go about clipping these (besides clipping them in Photoshop? The table contents are dynamic.)
For example, the first image in a table would need the top left corner rounded only.
This was my solution, which could use a little refactoring:
void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth, float ovalHeight, BOOL top, BOOL bottom)
{
float fw, fh;
if (ovalWidth == 0 || ovalHeight == 0) {
CGContextAddRect(context, rect);
return;
}
CGContextSaveGState(context);
CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextScaleCTM (context, ovalWidth, ovalHeight);
fw = CGRectGetWidth (rect) / ovalWidth;
fh = CGRectGetHeight (rect) / ovalHeight;
CGContextMoveToPoint(context, fw, fh/2);
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 0);
NSLog(#"bottom? %d", bottom);
if (top) {
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 3);
} else {
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 0);
}
if (bottom) {
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 3);
} else {
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 0);
}
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 0);
CGContextClosePath(context);
CGContextRestoreGState(context);
}
- (UIImage *)roundCornersOfImage:(UIImage *)source roundTop:(BOOL)top roundBottom:(BOOL)bottom {
int w = source.size.width;
int h = source.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextBeginPath(context);
CGRect rect = CGRectMake(0, 0, w, h);
addRoundedRectToPath(context, rect, 4, 4, top, bottom);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), source.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return [UIImage imageWithCGImage:imageMasked];
}
Implement those functions, then check the indexPath in the cellForRowAtIndexPath delegate method to determine which corner to round.
if (indexPath.row == 0) {
cell.imageView.image = [self roundCornersOfImage:coverImage roundTop:YES roundBottom:NO];
} else if (indexPath.row == [indexPath length]) {
cell.imageView.image = [self roundCornersOfImage:coverImage roundTop:NO roundBottom:YES];
} else {
cell.imageView.image = coverImage;
}
If you're happy to have all four image corners rounded, then you can just do the following when creating the cell:
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.cornerRadius = 10.0;
If you also want to inset the image from the boundary, I described a simple category on UIImage to do it here.
There isn't a built-in standard way to do this, but it's not terribly hard to do in your own code. There are examples on how to round corners on an UIImage on the web, see for example http://blog.sallarp.com/iphone-uiimage-round-corners/.
A couple additions/changes, hope it helps someone:
1) roundTop and roundBottom impl changed slightly.
2) made a class method in separate class so reuse is easier, rather than copy/paste everywhere.
First, the new class details:
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#interface RoundedImages : NSObject {
}
+(UIImage *)roundCornersOfImage:(UIImage *)source roundTop:(BOOL)top roundBottom:(BOOL)bottom;
#end
And its implementation:
#import "RoundedImages.h"
#implementation RoundedImages
void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth, float ovalHeight, BOOL top, BOOL bottom)
{
float fw, fh;
if (ovalWidth == 0 || ovalHeight == 0) {
CGContextAddRect(context, rect);
return;
}
CGContextSaveGState(context);
CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextScaleCTM (context, ovalWidth, ovalHeight);
fw = CGRectGetWidth (rect) / ovalWidth;
fh = CGRectGetHeight (rect) / ovalHeight;
CGContextMoveToPoint(context, fw, fh/2);
if (top) {
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 3);
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 3);
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 0);
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 0);
} else {
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 0);
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 0);
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 3);
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 3);
}
CGContextClosePath(context);
CGContextRestoreGState(context);
}
+(UIImage *)roundCornersOfImage:(UIImage *)source roundTop:(BOOL)top roundBottom:(BOOL)bottom {
int w = source.size.width;
int h = source.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextBeginPath(context);
CGRect rect = CGRectMake(0, 0, w, h);
//addRoundedRectToPath(context, rect, 4, 4, top, bottom);
addRoundedRectToPath(context, rect, 5, 5, top, bottom);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), source.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
//return [UIImage imageWithCGImage:imageMasked];
UIImage *image = [UIImage imageWithCGImage:imageMasked];
CGImageRelease(imageMasked);
return image;
}
#end
To use in another class (eg, view controller):
#import "RoundedImages.h"
...and later we use it like this...
UIImageView *imageView = nil;
UIImage *img = [UIImage imageNamed:#"panel.png"];
if (indexPath.row == 0) {
imageView = [[UIImageView alloc]initWithImage:[RoundedImages roundCornersOfImage:img roundTop:YES roundBottom:NO]];
}
else if (indexPath.row == ([choices count]-1))
{
imageView = [[UIImageView alloc]initWithImage:[RoundedImages roundCornersOfImage:img roundTop:NO roundBottom:YES]];
}
else {
imageView = [[UIImageView alloc]initWithImage:img];
}
cell.backgroundColor = [UIColor clearColor];
imageView.clipsToBounds = NO;
cell.backgroundView = imageView;
cell.backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
[cell.backgroundView addSubview:imageView];
[imageView release];
Note that the "choices" above was just a mutable array I was using on this page that contained the data for the tableview.
I should add that the usage snippet above is used inside your cellForRowAtIndexPath method, and "cell" is a uitableviewcell.
Anyway, works like a champ for me.
There is built-i way if you want to just display the rounded corners. Put the image in a UIImageView and then set the cornerRadius of the layer of the UIImageView. You will also need to tell the UIImageView to clip to bounds but that will give you rounded corners.
UIImageView *myImageView = [[UIImageView alloc] initWithImage:...];
[myImageView setClipsToBounds:YES];
[[myImageView layer] setCornerRadius:5.0f];

Existing implementation of cropImage:to:andScaleTo: and straightenAndScaleImage()?

I'm writing code to use UIImagePickerController. Corey previously posted some nice sample code on SO related to cropping and scaling. However, it doesn't have implementations of cropImage:to:andScaleTo: nor straightenAndScaleImage().
Here's how they're used:
newImage = [self cropImage:originalImage to:croppingRect andScaleTo:scaledImageSize];
...
UIImage *rotatedImage = straightenAndScaleImage([editInfo objectForKey:UIImagePickerControllerOriginalImage], scaleSize);
Since I'm sure someone must be using something very similar to Corey's sample code, there's probably an existing implementation of these two functions. Would someone like to share?
If you check the post you linked to, you'll see a link to the apple dev forums where I got some of this code, here are the methods you are asking about. Note: I may have made some changes relating to data types, but I can't quite remember. It should be trivial for you to adjust if needed.
- (UIImage *)cropImage:(UIImage *)image to:(CGRect)cropRect andScaleTo:(CGSize)size {
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGImageRef subImage = CGImageCreateWithImageInRect([image CGImage], cropRect);
CGRect myRect = CGRectMake(0.0f, 0.0f, size.width, size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);
CGContextTranslateCTM(context, 0.0f, -size.height);
CGContextDrawImage(context, myRect, subImage);
UIImage* croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRelease(subImage);
return croppedImage;
}
UIImage *straightenAndScaleImage(UIImage *image, int maxDimension) {
CGImageRef img = [image CGImage];
CGFloat width = CGImageGetWidth(img);
CGFloat height = CGImageGetHeight(img);
CGRect bounds = CGRectMake(0, 0, width, height);
CGSize size = bounds.size;
if (width > maxDimension || height > maxDimension) {
CGFloat ratio = width/height;
if (ratio > 1.0f) {
size.width = maxDimension;
size.height = size.width / ratio;
}
else {
size.height = maxDimension;
size.width = size.height * ratio;
}
}
CGFloat scale = size.width/width;
CGAffineTransform transform = orientationTransformForImage(image, &size);
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
// Flip
UIImageOrientation orientation = [image imageOrientation];
if (orientation == UIImageOrientationRight || orientation == UIImageOrientationLeft) {
CGContextScaleCTM(context, -scale, scale);
CGContextTranslateCTM(context, -height, 0);
}else {
CGContextScaleCTM(context, scale, -scale);
CGContextTranslateCTM(context, 0, -height);
}
CGContextConcatCTM(context, transform);
CGContextDrawImage(context, bounds, img);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}