How to get PDF annotations when i touch on ipad screen

牧云@^-^@ 提交于 2019-12-03 08:48:23

问题


I have developed an application which displays PDF on ipad using CATiled Layer. So far, so good& But there is a problem which really makes me cut my last hairs. I have a PDF with embedded annotations. Each annotation has an URL. I can find the coordinates of touch area but the question is how I can find if there is an annotation under my finger and how to extract URL to open it in browser?

If anyone can share any thoughts about how this can be done, I will really appreciate your help!

Thanks in advance


回答1:


Getting the annotations isn't very difficult with CGPDF although it required a little fiddling around for me at first.

//Get the current page ref 
CGPDFPageRef currentPdfPage = CGPDFDocumentGetPage(pdfDocumentRef, page);

//Get the page dictionary
CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(currentPdfPage);
CGPDFArrayRef annotsArray;

//Get the Annots array
if(!CGPDFDictionaryGetArray(pageDictionary, "Annots", &annotsArray)) {
    //DLog(@"No Annots found for page %d", page);
    [self updateProgress];
    return;
}

int annotsArrayCount = CGPDFArrayGetCount(annotsArray);
//DLog(@"%d annots found for page %d in file %@", annotsArrayCount, page, _fileName);
NSMutableArray* touchRectsArray = [[NSMutableArray alloc] initWithCapacity:annotsArrayCount];
for (int j=annotsArrayCount; j >= 0; j--) {
    int destPageNumber = 0;
    NSString* uri = nil;

    //DLog(@"%d/%d", j+1, annotsArrayCount);

    CGPDFObjectRef aDictObj;
    if(!CGPDFArrayGetObject(annotsArray, j, &aDictObj)) {
        //DLog(@"%@", @"can't get dictionary object");
        continue;
    }

    CGPDFDictionaryRef annotDict;
    if(!CGPDFObjectGetValue(aDictObj, kCGPDFObjectTypeDictionary, &annotDict)) {
        //DLog(@"%@", @"can't get annotDict");
        continue;
    }

    //------------
    CGPDFDictionaryRef aDict;
    CGPDFArrayRef destArray;
    if(CGPDFDictionaryGetDictionary(annotDict, "A", &aDict)) {
        CGPDFStringRef uriStringRef;
        if(CGPDFDictionaryGetString(aDict, "URI", &uriStringRef)) {
            char* uriString = (char *)CGPDFStringGetBytePtr(uriStringRef);
            uri = [NSString stringWithCString:uriString encoding:NSUTF8StringEncoding];
        }
    } else {
        continue;
    }

This will get you the URLs. Getting the rects:

CGPDFArrayRef rectArray;
    if(!CGPDFDictionaryGetArray(annotDict, "Rect", &rectArray)) {
        DLog(@"%@", @"can't get Rect");
    }

    int arrayCount = CGPDFArrayGetCount(rectArray);
    CGPDFReal coords[4];
    for (int k = 0; k < arrayCount; k++) {
        CGPDFObjectRef rectObj;
        if(!CGPDFArrayGetObject(rectArray, k, &rectObj)) {
            DLog(@"%@", @"can't get rect data");
        }

        CGPDFReal coord;
        if(!CGPDFObjectGetValue(rectObj, kCGPDFObjectTypeReal, &coord)) {
            DLog(@"%@", @"can't get coords");
        }

        coords[k] = coord;
    }

    CGRect drawRect = [[SharedConfig valueForKey:@"screenSize"] CGRectValue];
    BOOL drawBoxIsLandscape = NO;
    if (1 < drawRect.size.width/drawRect.size.height) {
        drawBoxIsLandscape = YES;
    }

    CGRect pageRect = CGRectIntegral(CGPDFPageGetBoxRect(currentPdfPage, kCGPDFMediaBox));
    landscape = NO;
    if (1 < pageRect.size.width/pageRect.size.height) {
        landscape = YES;
    }

    float ratio = 0.0;

    //Get the rect of the clickable area
    //CGRect coordsRect = CGRectMake(coords[0], coords[1], coords[2], coords[3]);
    //Transform to new coordinate system
    CGRect originalRect = CGRectMake(coords[0], (pageRect.size.height-(coords[3]-coords[1]))-coords[1], coords[2]-coords[0], coords[3]-coords[1]);

    CGPDFInteger pageRotate = 0;
    CGPDFDictionaryGetInteger(pageDictionary, "Rotate", &pageRotate);

    if (pageRotate == 90 || pageRotate == 270) {
        CGFloat temp = pageRect.size.width;
        pageRect.size.width = pageRect.size.height;
        pageRect.size.height = temp;
        ratio = drawRect.size.height / pageRect.size.height;
    }       

    if (drawBoxIsLandscape) {
        ratio = landscape ? (drawRect.size.height/pageRect.size.height) : (drawRect.size.height/pageRect.size.width);

        if (landscape && drawRect.size.width < pageRect.size.width*ratio) {
            ratio = drawRect.size.width/pageRect.size.width;
        } else if (!landscape && drawRect.size.height < pageRect.size.width*ratio) {
            ratio = drawRect.size.height/pageRect.size.width;
        }
    } else {
        ratio = landscape ? (drawRect.size.height/pageRect.size.width) : (drawRect.size.height/pageRect.size.height);

        if (landscape && drawRect.size.width < pageRect.size.height*ratio) {
            ratio = drawRect.size.width/pageRect.size.height;
        } else if (!landscape && drawRect.size.height < pageRect.size.height*ratio) {
            ratio = drawRect.size.height/pageRect.size.height;
        }
    }

    CGRect calculatedRect = CGRectMake(originalRect.origin.x*ratio, originalRect.origin.y*ratio, originalRect.size.width*ratio, originalRect.size.height*ratio);

    if ((landscape && !drawBoxIsLandscape) || (!landscape && drawBoxIsLandscape)) {
        CGFloat width = calculatedRect.size.width;
        calculatedRect.size.width = calculatedRect.size.height;
        calculatedRect.size.height = width;

        CGFloat yModifier = drawRect.size.height-(pageRect.size.width*ratio);

        CGFloat x = calculatedRect.origin.x;
        calculatedRect.origin.x = calculatedRect.origin.y;
        calculatedRect.origin.y = drawRect.size.height-(x+calculatedRect.size.height)-yModifier;

    }

    if (nil != uri) {
        [touchRectsArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithCGRect:calculatedRect], @"rect", uri, @"targetUrl", nil]];
    }

As you can see this bit of code first gets the rectangle of the annotation, transforms it to the device coordinate system, then does some recalculation, sizing and repositioning, based on page to screen ratio, rotation factor, etc. At the end you will have an array of the touch-active areas for that page. For handling the touches the following simple solution can be used:

- (void) tapGesture:(UIGestureRecognizer*)sender
{
if (UIGestureRecognizerStateEnded == sender.state) {
    CGPoint touchPoint = [sender locationInView:self.view];

    if (nil != self.touchRects) for (int i=0; i<[self.touchRects count]; i++) {
        if (CGRectContainsPoint([[[self.touchRects objectAtIndex:i] objectForKey:@"rect"] CGRectValue], touchPoint)) {
            if ([[self.touchRects objectAtIndex:i] objectForKey:@"targetUrl"]) {
                NSString* targetUrl = [[self.touchRects objectAtIndex:i] objectForKey:@"targetUrl"];
                DLog(@"Hit found for target url: %@", targetUrl);
                NSURL* url = [NSURL URLWithString:targetUrl];
                [[UIApplication sharedApplication] openURL:url];
            }               return;
        }       
    }
    DLog(@"No hit found for touch at %@", NSStringFromCGPoint(touchPoint));
}
} 


来源:https://stackoverflow.com/questions/5352090/how-to-get-pdf-annotations-when-i-touch-on-ipad-screen

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!