White border when rendering PDF when scale > 1 - iphone

I have a method that return an UIImage from a CGPDFPageRef. You can specify a width for the image.
The problem is that when pdfScale is > 1, a white border appears in the image. So the PDF is always drawn at scale 1 with a border instead of a bigger scale. Smaller scales are OK.
I've tried to change the type of PDFBox but that doesn't seems to change anything and the documentation is not really clear.
Does somebody sees the error?
- (UIImage*) PDFImageForWidth:(CGFloat) width {
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
CGFloat pdfScale = width/pageRect.size.width;
pageRect.size = CGSizeMake(pageRect.size.width*pdfScale, pageRect.size.height*pdfScale);
pageRect.origin = CGPointZero;
UIGraphicsBeginImageContext(pageRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context, pageRect);
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, pageRect, 0, true));
CGContextDrawPDFPage(context, page);
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

The problem was that CGPDFPageGetDrawingTransform doesn't scale the PDF when scale > 1. You have to do it yourself.
Also, the method is thread safe now.
- (UIImage*) PDFImageForWidth:(CGFloat) width {
CGRect smallPageRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
CGFloat pdfScale = width/smallPageRect.size.width;
CGRect pageRect;
pageRect.size = CGSizeMake(smallPageRect.size.width*pdfScale, smallPageRect.size.height*pdfScale);
pageRect.origin = CGPointZero;
__block CGContextRef context;
dispatch_sync(dispatch_get_main_queue(), ^{
UIGraphicsBeginImageContext(pageRect.size);
context = UIGraphicsGetCurrentContext();
});
if (context != nil) {
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context, pageRect);
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGAffineTransform transform = CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, pageRect, 0, true);
if (pdfScale > 1) {
transform = CGAffineTransformScale(transform, pdfScale, pdfScale);
transform.tx = 0;
transform.ty = 0;
}
CGContextConcatCTM(context, transform);
CGContextDrawPDFPage(context, page);
__block UIImage* image;
dispatch_sync(dispatch_get_main_queue(), ^{
image = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
});
return [image autorelease];
}
else return nil;
}

I was having the same problem and the answer before did help me in some parts but it didn't solve the whole problem.
This is the how I solved my problem.
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
NSLog(#"pdfQuartzViewViewController - drawLayer");
CGPDFDocumentRef myDocumentRef = CGPDFDocumentCreateWithURL((CFURLRef)[NSURL fileURLWithPath:self.strFilename]);
CGPDFPageRef pageReference = CGPDFDocumentGetPage(myDocumentRef, 1);
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // White
CGContextFillRect(context, CGContextGetClipBoundingBox(context)); // Fill
CGContextTranslateCTM(context, 0.0f, layer.bounds.size.height);
// Scaling the pdf page a little big bigger than the view so we can cover the white borders on the page
CGContextScaleCTM(context, 1.0015f, -1.0015f);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(pageReference, kCGPDFCropBox, layer.bounds, 0, true);
pdfTransform.tx = pdfTransform.tx - 0.25f;
pdfTransform.ty = pdfTransform.ty - 0.40f;
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, pageReference); // Render the PDF page into the context
CGPDFDocumentRelease(myDocumentRef);
myDocumentRef = NULL;
}
Here there is a good link where you can find more information about it. The post explains how to use all this methods.
http://iphonedevelopment.blogspot.ch/2008/10/demystifying-cgaffinetransform.html

Related

CGContextShowTextAtPoint producing non-Retina output on Retina device

I'm trying to add a text to an UIImage and I'm getting a pixelated drawing.
I have tried some other answers with no success:
Drawing on the retina display using CoreGraphics - Image pixelated
Retina display core graphics font quality
Drawing with Core Graphics looks chunky on Retina display
My code:
-(UIImage *)addText:(UIImage *)imgV text:(NSString *)text1
{
int w = self.frame.size.width * 2;
int h = self.frame.size.height * 2;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate
(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), imgV.CGImage);
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1);
char* text = (char *)[text1 cStringUsingEncoding:NSASCIIStringEncoding];
CGContextSelectFont(context, "Arial", 24, kCGEncodingMacRoman);
// Adjust text;
CGContextSetTextDrawingMode(context, kCGTextInvisible);
CGContextShowTextAtPoint(context, 0, 0, text, strlen(text));
CGPoint pt = CGContextGetTextPosition(context);
float posx = (w/2 - pt.x)/2.0;
float posy = 54.0;
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextSetRGBFillColor(context, 255, 255, 255, 1.0);
CGContextShowTextAtPoint(context, posx, posy, text, strlen(text));
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return [UIImage imageWithCGImage:imageMasked];
}
Like Peter said, use UIGraphicsBeginImageContextWithOptions. You might want to pass image.scale to the scale attribute (where image.scale is the scale of one of the images you're drawing), or simply use [UIScreen mainScreen].scale.
This code can be made simpler overall. Try something like:
// Create the image context
UIGraphicsBeginImageContextWithOptions(_baseImage.size, NO, _baseImage.scale);
// Draw the image
CGRect rect = CGRectMake(0, 0, _baseImage.size.width, _baseImage.size.height);
[_baseImage drawInRect:rect];
// Get a vertically centered rect for the text drawing
rect.origin.y = (rect.size.height - FONT_SIZE) / 2 - 2;
rect = CGRectIntegral(rect);
rect.size.height = FONT_SIZE;
// Draw the text
UIFont *font = [UIFont boldSystemFontOfSize:FONT_SIZE];
[[UIColor whiteColor] set];
[text drawInRect:rect withFont:font lineBreakMode:NSLineBreakByClipping alignment:NSTextAlignmentCenter];
// Get and return the new image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
Where text is an NSString object.
I was facing the exact same problem and posted a question last night to which no-one replied. I have combined fnf's suggested changes with Clif Viegas's answer in the following question:
CGContextDrawImage draws image upside down when passed UIImage.CGImage
to come up with the solution. My addText method is slightly different from yours:
+(UIImage *)addTextToImage:(UIImage *)img text:(NSString *)text1{
int w = img.size.width;
int h = img.size.height;
CGSize size = CGSizeMake(w, h);
if (UIGraphicsBeginImageContextWithOptions != NULL) {
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
} else {
UIGraphicsBeginImageContext(size);
}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0,h);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1);
char* text = (char *)[text1 cStringUsingEncoding:NSASCIIStringEncoding];// \"05/05/09\";
CGContextSelectFont(context, "Times New Roman", 14, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextSetRGBFillColor(context, 0, 0, 0, 1);
//rotate text
CGContextSetTextMatrix(context, CGAffineTransformMakeRotation( -M_PI/8 ));
CGContextShowTextAtPoint(context, 70, 88, text, strlen(text));
CGColorSpaceRelease(colorSpace);
UIGraphicsEndImageContext();
return newImg;
}
In summary, what I have done is add
if (UIGraphicsBeginImageContextWithOptions != NULL) {
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
} else {
UIGraphicsBeginImageContext(size);
}
Remove
// CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
and put
CGContextRef context = UIGraphicsGetCurrentContext();
instead. But this causes the image to be upside down. Hence I put
CGContextTranslateCTM(context, 0,h);
CGContextScaleCTM(context, 1.0, -1.0);
just before
CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
This way you can use CG functions instead of using drawInRect etc.
Don't forget
UIGraphicsEndImageContext
at the end. Hope this helps.

UIView: Draw at double the size

I have a UIView with a dynamically generated graphic context. Its normal size is 100x100, but it can be scaled up to 200x200, so I want its size to be actually 200x200 in order to appear nicely and not blurred however it is displayed.
How can always draw it 200x200 in drawRect, being it displayed at a size of 100 or at a size of 200? If I'm showing it at 100x100, the rect passed to drawRect is smaller than the area I need to draw into.
My code:
- (void) drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
// prevent the drawings to be flipped
CGContextTranslateCTM(ctx, 0, rect.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextDrawImage(ctx, CGRectMake(0, 0, rect.size.width, rect.size.height), [UIImage imageNamed:#"actionBg.png"].CGImage);
// generate the overlay
if ([self isActive] == NO && self.fullDelay != 0) { // TODO: remove fullDelay check!
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
CGContextRef overlayCtx = UIGraphicsGetCurrentContext();
int segmentSize = (rect.size.height / [self fullDelay]);
for (int i=0; i<[self fullDelay]; i++) {
float alpha = 0.9 - (([self fullDelay] * 0.1) - (i * 0.1));
[[UIColor colorWithRed:120.0/255.0 green:14.0/255.0 blue:14.0/255.0 alpha:alpha] setFill];
if (currentDelay > i) {
CGRect r = CGRectMake(0, i * segmentSize, rect.size.width, segmentSize);
CGContextFillRect(overlayCtx, r);
}
[[UIColor colorWithRed:1 green:1 blue:1 alpha:0.3] setFill];
CGRect line = CGRectMake(0, (i * segmentSize) + segmentSize - 1 , rect.size.width, 1);
CGContextFillRect(overlayCtx, line);
}
UIImage *overlay = UIGraphicsGetImageFromCurrentImageContext();
UIImage *overlayMasked = [TDUtilities maskImage:overlay withMask:[UIImage imageNamed:#"actionMask.png"]];
// prevent the drawings to be flipped
CGContextTranslateCTM(ctx, 0, rect.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
// put the overlay
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextDrawImage(ctx, rect, overlayMasked.CGImage);
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
UIGraphicsEndImageContext();
}
// prevent the drawings to be flipped
CGContextTranslateCTM(ctx, 0, rect.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
// draw the delay symbol
CGContextDrawImage(ctx, rect, [UIImage imageNamed:#"delaySymbol.png"].CGImage);
CGContextSetAlpha(ctx, 0.8);
// draw the symbol
NSString *imgName = [K_ACTION_IMAGES objectAtIndex:[self actionType]];
CGContextDrawImage(ctx, rect, [UIImage imageNamed:imgName].CGImage);
CGContextSetAlpha(ctx, 1);
// draw the delay number
imgName = [NSString stringWithFormat:#"%d.png", [self fullDelay]];
CGContextDrawImage(ctx, rect, [UIImage imageNamed:imgName].CGImage);
// draw the priority number
imgName = [NSString stringWithFormat:#"%d.png", [self actionType]];
CGContextDrawImage(ctx, CGRectMake(32, 0, rect.size.width, rect.size.height), [UIImage imageNamed:imgName].CGImage);
}
Have you called setNeedsDisplay method for you view after setting its frame

missing or invalid `MediaBox' when converting PDF to UIImage

I'm trying to convert a PDF to a UIImage, but I'm getting all sorts of weird errors. I'm not doing the conversion in a drawRect method because I'm not displaying the graphics to the screen. Any ideas how I can make this work?
The code I'm trying is this:
CGPDFPageRef page = [self getPage:pageIndex];
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
float pdfScale = 1024.0/pageRect.size.height;
pageRect.size = CGSizeMake(pageRect.size.width*pdfScale, pageRect.size.height*pdfScale);
UIGraphicsBeginImageContext(pageRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,pageRect);
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, pdfScale,-pdfScale);
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Blurry PDF Drawing compared to CATiledLayer

In my application I'm creating a PDF reader. To cater for performance, I draw a view at default scale. This view is not CATiledLayer based. When user zooms in a bit, I overlay this view with a CATiledLayer one which gives much crisper image and low memory usage in normal use cases.
Now the problem is that in default state (when CALayer view is being shown) the image is coming out a bit blurry. When I try to zoom in just a wee bit and CATiledLayer kicks in, the same text and everything becomes sharp.
Here's the code for the CATiledLayer view (which shows crisp image and text as it should be):
- (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)context{
CGPDFPageRef drawPDFPageRef = NULL;
CGPDFDocumentRef drawPDFDocRef = NULL;
#synchronized(self) {
if (_PDFDocRef==nil) {
NSLog(#"PDF NIL");
_PDFDocRef = [SharedPDFDocRef sharedInstanceForUrl:_fileURL andPhrase:_password];
_PDFPageRef = CGPDFDocumentGetPage(_PDFDocRef, _page);
}
drawPDFDocRef = _PDFDocRef;
drawPDFPageRef = _PDFPageRef;
}
if (drawPDFPageRef != NULL) {
CGContextTranslateCTM(context, 0.0f, self.bounds.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);
CGAffineTransform transform =
CGPDFPageGetDrawingTransform(drawPDFPageRef, kCGPDFCropBox, layer.bounds, 0, true);
CGContextConcatCTM(context, transform);
CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);
CGContextSetInterpolationQuality(context, kCGInterpolationDefault);
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f);
CGContextFillRect(context, CGPDFPageGetBoxRect(drawPDFPageRef, kCGPDFBleedBox));
CGContextDrawPDFPage(context, drawPDFPageRef);
}
//CGPDFPageRelease(drawPDFPageRef); CGPDFDocumentRelease(drawPDFDocRef);
}
Here's the code for the Normal view (shows okay but image is not in the same quality as CATiledLayer one):
- (void) drawRect:(CGRect)rect {
[super drawRect:rect];
CGPDFPageRef drawPDFPageRef = NULL;
CGPDFDocumentRef drawPDFDocRef = NULL;
#synchronized(self) {
drawPDFDocRef = CGPDFDocumentRetain(_PDFDocRef);
drawPDFPageRef = CGPDFPageRetain(_PDFPageRef);
}
// get the page reference
CGPDFPageRef page = drawPDFPageRef;
CGContextRef context = UIGraphicsGetCurrentContext();
// white background
// transformation/-lation
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.00f, -1.00f);
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page,
kCGPDFCropBox, self.bounds, 0, true));
CGContextSetInterpolationQuality(context, kCGInterpolationDefault);
CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f);
CGContextFillRect(context, CGPDFPageGetBoxRect(drawPDFPageRef, kCGPDFBleedBox));
CGContextDrawPDFPage(context, page);
CGPDFPageRelease(drawPDFPageRef);
CGPDFDocumentRelease(drawPDFDocRef);
}
Have you tried to set the content scale factor of the view after the zoom was performed?
In your UIScrollViewDelegate you would do:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
[view setContentScaleFactor:scale];
}

Rendering a CGPDFPage into a UIImage

I'm trying to render a CGPDFPage (selected from a CGPDFDocument) into a UIImage to display on a view.
I have the following code in MonoTouch which gets me part way there.
RectangleF PDFRectangle = new RectangleF(0, 0, UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height);
public override void ViewDidLoad ()
{
UIGraphics.BeginImageContext(new SizeF(PDFRectangle.Width, PDFRectangle.Height));
CGContext context = UIGraphics.GetCurrentContext();
context.SaveState();
CGPDFDocument pdfDoc = CGPDFDocument.FromFile("test.pdf");
CGPDFPage pdfPage = pdfDoc.GetPage(1);
context.DrawPDFPage(pdfPage);
UIImage testImage = UIGraphics.GetImageFromCurrentImageContext();
pdfDoc.Dispose();
context.RestoreState();
UIImageView imageView = new UIImageView(testImage);
UIGraphics.EndImageContext();
View.AddSubview(imageView);
}
A section of the CGPDFPage is displayed but rotated back-to-front and upside down. My question is, how do I select the full pdf page and flip it round to display correctly. I have seen a few examples using ScaleCTM and TranslateCTM but couldn't seem to get them working.
Any examples in ObjectiveC are fine, I'll take all the help I can get :)
Thanks
I haven't worked with MonoTouch. However, in objective-C you would get an image for a PDF page like this (notice the CTM transforms):
-(UIImage *)getThumbForPage:(int)page_number{
CGFloat width = 60.0;
// Get the page
CGPDFPageRef myPageRef = CGPDFDocumentGetPage(myDocumentRef, page);
// Changed this line for the line above which is a generic line
//CGPDFPageRef page = [self getPage:page_number];
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
CGFloat pdfScale = width/pageRect.size.width;
pageRect.size = CGSizeMake(pageRect.size.width*pdfScale, pageRect.size.height*pdfScale);
pageRect.origin = CGPointZero;
UIGraphicsBeginImageContext(pageRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// White BG
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,pageRect);
CGContextSaveGState(context);
// ***********
// Next 3 lines makes the rotations so that the page look in the right direction
// ***********
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page, kCGPDFMediaBox, pageRect, 0, true));
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
UIImage *thm = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return thm;
}