I am trying to find all hyperlinks on a page in a PDF document and place a UIButton over them.
I have found and played around with the script below which should be able to do it. It finds the hyperlinks and places UIButtons bt they are not on top of the links. Can anyone tell me why that may be?
I have attached an image to show how the buttons are off. The uppermost buttons should be on top of the blue "METROPOLITAN" text and the lowermost buttons should be on top of the text saying "SÆSONEN 2008-2009".
This is the code I am using to find the links and place the buttons.
NSMutableArray *rectArrays;
NSMutableArray *storeURLs;
CGPDFPageRef pdfPage = CGPDFDocumentGetPage(_pdf, pageNumber);
CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(pdfPage);
CGPDFArrayRef outputArray;
if(!CGPDFDictionaryGetArray(pageDictionary, "Annots", &outputArray)) {
//break;
}
int arrayCount = 0;
arrayCount = CGPDFArrayGetCount(outputArray);
if(arrayCount > 0) {
for(int j = 0; j < arrayCount; ++j) {
CGPDFObjectRef aDictObj;
if(!CGPDFArrayGetObject(outputArray, j, &aDictObj)) {
return;
}
CGPDFDictionaryRef annotDict;
if(!CGPDFObjectGetValue(aDictObj, kCGPDFObjectTypeDictionary, &annotDict)) {
return;
}
CGPDFDictionaryRef aDict;
if(!CGPDFDictionaryGetDictionary(annotDict, "A", &aDict)) {
return;
}
CGPDFStringRef uriStringRef;
if(!CGPDFDictionaryGetString(aDict, "URI", &uriStringRef)) {
return;
}
CGPDFArrayRef rectArray;
if(!CGPDFDictionaryGetArray(annotDict, "Rect", &rectArray)) {
return;
}
int arrayCount = CGPDFArrayGetCount( rectArray );
CGPDFReal coords[4];
for(int k = 0; k < arrayCount; ++k) {
CGPDFObjectRef rectObj;
if(!CGPDFArrayGetObject(rectArray, k, &rectObj)) {
return;
}
CGPDFReal coord;
if(!CGPDFObjectGetValue(rectObj, kCGPDFObjectTypeReal, &coord)) {
return;
}
coords[k] = coord;
}
char *uriString = (char *)CGPDFStringGetBytePtr(uriStringRef);
NSString *uri = [NSString stringWithCString:uriString encoding:NSUTF8StringEncoding];
CGRect rect = CGRectMake(coords[0], coords[1], coords[2], coords[3]);
NSLog(@"Found: %f ; %f => %f x %f", coords[0], coords[1], coords[2], coords[3]);
CGPDFInteger pageRotate = 0;
CGPDFDictionaryGetInteger(pageDictionary, "Rotate", &pageRotate);
CGRect pageRect = CGRectIntegral( CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox));
if(pageRotate == 90 || pageRotate == 270) {
CGFloat temp = pageRect.size.width;
pageRect.size.width = pageRect.size.height;
pageRect.size.height = temp;
}
rect.size.width = rect.size.width - rect.origin.x;
rect.size.height = rect.size.height - rect.origin.y;
CGAffineTransform trans = CGAffineTransformIdentity;
trans = CGAffineTransformTranslate(trans, 0, pageRect.size.height);
trans = CGAffineTransformScale(trans, 1.0, -1.0);
rect = CGRectApplyAffineTransform(rect, trans);
NSURL *url = [NSURL URLWithString:uri];
[rectArrays addObject:[NSValue valueWithCGRect:rect]];
[storeURLs addObject:url];
for(int i = 0; i <= [rectArrays count]; i++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setFrame:rect];
[button setTitle:@"Link" forState:UIControlStateNormal];
[[self view] addSubview:button];
NSLog(@"Added: %f ; %f => %f x %f", button.frame.origin.x, button.frame.origin.y, button.frame.size.width, button.frame.size.height);
}
}
}
EDIT: Trying to adjust for the scale factor but it is not working. The UIButtons are still placed wrong.
CGAffineTransform trans = CGAffineTransformIdentity;
trans = CGAffineTransformTranslate(trans, 0, pageRect.size.height * 1.5);
trans = CGAffineTransformScale(trans, 1.5, -1.5);
EDIT I took a look at the source code which iPDFDev is linking too and it seems that I have got the positioning working now. My buttons are now too long. Does anyone know why this may be? If I substract viewRext.origin.x
from viewRect.size.width
the buttons are too short.
This is my code now.
CGPDFPageRef pdfPage = CGPDFDocumentGetPage(_pdf, pageNumber);
CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(pdfPage);
CGPDFArrayRef outputArray;
if(!CGPDFDictionaryGetArray(pageDictionary, "Annots", &outputArray)) {
//break;
}
int arrayCount = 0;
arrayCount = CGPDFArrayGetCount(outputArray);
if(arrayCount > 0) {
for(int j = 0; j < arrayCount; ++j) {
CGPDFObjectRef aDictObj;
if(!CGPDFArrayGetObject(outputArray, j, &aDictObj)) {
return;
}
CGPDFDictionaryRef annotDict;
if(!CGPDFObjectGetValue(aDictObj, kCGPDFObjectTypeDictionary, &annotDict)) {
return;
}
CGPDFDictionaryRef aDict;
if(!CGPDFDictionaryGetDictionary(annotDict, "A", &aDict)) {
return;
}
CGPDFStringRef uriStringRef;
if(!CGPDFDictionaryGetString(aDict, "URI", &uriStringRef)) {
return;
}
CGPDFArrayRef rectArray;
if(!CGPDFDictionaryGetArray(annotDict, "Rect", &rectArray)) {
return;
}
int arrayCount = CGPDFArrayGetCount( rectArray );
CGPDFReal coords[4];
for(int k = 0; k < arrayCount; ++k) {
CGPDFObjectRef rectObj;
if(!CGPDFArrayGetObject(rectArray, k, &rectObj)) {
return;
}
CGPDFReal coord;
if(!CGPDFObjectGetValue(rectObj, kCGPDFObjectTypeReal, &coord)) {
return;
}
coords[k] = coord;
}
char *uriString = (char *)CGPDFStringGetBytePtr(uriStringRef);
NSString *uri = [NSString stringWithCString:uriString encoding:NSUTF8StringEncoding];
CGRect rect = CGRectMake(coords[0], coords[1], coords[2], coords[3]);
CGPoint pdfPoint = CGPointMake(rect.origin.x, rect.origin.y);
CGPoint viewPoint = CGPointMake(0, 0);
CGRect cropBox = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
int rotation = CGPDFPageGetRotationAngle(pdfPage);
CGRect pageRenderRect;
switch (rotation) {
case 90:
case -270:
pageRenderRect = CGRectMake(0, 0, 1024, 768);
viewPoint.x = pageRenderRect.size.width * (pdfPoint.y - cropBox.origin.y) / cropBox.size.height;
viewPoint.y = pageRenderRect.size.height * (pdfPoint.x - cropBox.origin.x) / cropBox.size.width;
break;
case 180:
case -180:
pageRenderRect = CGRectMake(0, 0, 768, 1024);
viewPoint.x = pageRenderRect.size.width * (cropBox.size.width - (pdfPoint.x - cropBox.origin.x)) / cropBox.size.width;
viewPoint.y = pageRenderRect.size.height * (pdfPoint.y - cropBox.origin.y) / cropBox.size.height;
break;
case -90:
case 270:
pageRenderRect = CGRectMake(0, 0, 1024, 768);
viewPoint.x = pageRenderRect.size.width * (cropBox.size.height - (pdfPoint.y - cropBox.origin.y)) / cropBox.size.height;
viewPoint.y = pageRenderRect.size.height * (cropBox.size.width - (pdfPoint.x - cropBox.origin.x)) / cropBox.size.width;
break;
case 0:
default:
pageRenderRect = CGRectMake(0, 0, 768, 1024);
viewPoint.x = pageRenderRect.size.width * (pdfPoint.x - cropBox.origin.x) / cropBox.size.width;
viewPoint.y = pageRenderRect.size.height * (cropBox.size.height - pdfPoint.y) / cropBox.size.height;
break;
}
viewPoint.x = viewPoint.x + pageRenderRect.origin.x;
viewPoint.y = viewPoint.y + pageRenderRect.origin.y;
CGRect viewRect = CGRectMake(viewPoint.x, viewPoint.y, rect.size.width, rect.size.height - rect.origin.y);
viewRect = CGRectMake(viewRect.origin.x, viewRect.origin.y - viewRect.size.height, viewRect.size.width, viewRect.size.height);
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:viewRect];
[button setBackgroundColor:[UIColor greenColor]];
[button setAlpha:0.65];
[button setTag:kPDFLinkButton];
[[self view] addSubview:button];
}
}
EDIT: This is my final code. See the answer from iPDFDev for more information.
- (void)getLinksFromPDF:(CGPDFDocumentRef)_pdf withPageNumber:(NSUInteger)pageNumber
{
CGPDFPageRef pdfPage = CGPDFDocumentGetPage(_pdf, pageNumber);
CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(pdfPage);
CGPDFArrayRef outputArray;
if(!CGPDFDictionaryGetArray(pageDictionary, "Annots", &outputArray)) {
//break;
}
int arrayCount = 0;
arrayCount = CGPDFArrayGetCount(outputArray);
if(arrayCount > 0) {
for(int j = 0; j < arrayCount; ++j) {
CGPDFObjectRef aDictObj;
if(!CGPDFArrayGetObject(outputArray, j, &aDictObj)) {
return;
}
CGPDFDictionaryRef annotDict;
if(!CGPDFObjectGetValue(aDictObj, kCGPDFObjectTypeDictionary, &annotDict)) {
return;
}
CGPDFDictionaryRef aDict;
if(!CGPDFDictionaryGetDictionary(annotDict, "A", &aDict)) {
return;
}
CGPDFStringRef uriStringRef;
if(!CGPDFDictionaryGetString(aDict, "URI", &uriStringRef)) {
return;
}
CGPDFArrayRef rectArray;
if(!CGPDFDictionaryGetArray(annotDict, "Rect", &rectArray)) {
return;
}
int arrayCount = CGPDFArrayGetCount( rectArray );
CGPDFReal coords[4];
for(int k = 0; k < arrayCount; ++k) {
CGPDFObjectRef rectObj;
if(!CGPDFArrayGetObject(rectArray, k, &rectObj)) {
return;
}
CGPDFReal coord;
if(!CGPDFObjectGetValue(rectObj, kCGPDFObjectTypeReal, &coord)) {
return;
}
coords[k] = coord;
}
char *uriString = (char *)CGPDFStringGetBytePtr(uriStringRef);
NSString *uri = [NSString stringWithCString:uriString encoding:NSUTF8StringEncoding];
CGPoint lowerLeft = [self convertPDFPointToViewPoint:CGPointMake(coords[0], coords[1])];
CGPoint upperRight = [self convertPDFPointToViewPoint:CGPointMake(coords[2], coords[3])];
// This is the rectangle positioned under the link
CGRect viewRect = CGRectMake(lowerLeft.x, lowerLeft.y, upperRight.x - lowerLeft.x, lowerLeft.y - upperRight.y);
// Now adjusting the rectangle to be on top of the link
viewRect = CGRectMake(viewRect.origin.x, viewRect.origin.y - viewRect.size.height, viewRect.size.width, viewRect.size.height);
NSLog(@"%@", uri);
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:viewRect];
[button setBackgroundColor:[UIColor greenColor]];
[button setAlpha:0.65];
[button setTag:kPDFLinkButton];
[[self view] addSubview:button];
}
}
}
- (CGPoint)convertPDFPointToViewPoint:(CGPoint)pdfPoint
{
CGPoint viewPoint = CGPointMake(0, 0);
CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdf, currentPage);
CGRect cropBox = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
int rotation = CGPDFPageGetRotationAngle(pdfPage);
CGRect pageRenderRect;
switch (rotation) {
case 90:
case -270:
pageRenderRect = CGRectMake(0, 0, 1024, 768);
viewPoint.x = pageRenderRect.size.width * (pdfPoint.y - cropBox.origin.y) / cropBox.size.height;
viewPoint.y = pageRenderRect.size.height * (pdfPoint.x - cropBox.origin.x) / cropBox.size.width;
break;
case 180:
case -180:
pageRenderRect = CGRectMake(0, 0, 768, 1024);
viewPoint.x = pageRenderRect.size.width * (cropBox.size.width - (pdfPoint.x - cropBox.origin.x)) / cropBox.size.width;
viewPoint.y = pageRenderRect.size.height * (pdfPoint.y - cropBox.origin.y) / cropBox.size.height;
break;
case -90:
case 270:
pageRenderRect = CGRectMake(0, 0, 1024, 768);
viewPoint.x = pageRenderRect.size.width * (cropBox.size.height - (pdfPoint.y - cropBox.origin.y)) / cropBox.size.height;
viewPoint.y = pageRenderRect.size.height * (cropBox.size.width - (pdfPoint.x - cropBox.origin.x)) / cropBox.size.width;
break;
case 0:
default:
pageRenderRect = CGRectMake(0, 0, 768, 1024);
viewPoint.x = pageRenderRect.size.width * (pdfPoint.x - cropBox.origin.x) / cropBox.size.width;
viewPoint.y = pageRenderRect.size.height * (cropBox.size.height - pdfPoint.y) / cropBox.size.height;
break;
}
viewPoint.x = viewPoint.x + pageRenderRect.origin.x;
viewPoint.y = viewPoint.y + pageRenderRect.origin.y;
return viewPoint;
}