2010-11-02 9 views
54

Ho passato tutto il giorno a cercare di ottenere i metadati dei collegamenti ipertestuali dai PDF nella mia applicazione iPad. Le API di CGPDF * sono un vero incubo e l'unica informazione che ho trovato in rete è che devo cercare un dizionario "Annot", ma non riesco a trovarlo nei miei PDF .Ottieni collegamenti ipertestuali PDF su iOS con Quartz

ho anche usato il vecchio Voyeur Xcode sample di ispezionare il mio file di prova PDF, ma nessuna traccia di questo dizionario "Annots" ...

Sai, questa è una caratteristica che vedo su ogni lettore PDF - questa stessa domanda ha beenaskedmultipletimes qui senza reali risposte pratiche. Di solito non chiedo direttamente codice di esempio, ma a quanto pare questa volta ne ho davvero bisogno ... qualcuno ha funzionato, possibilmente con un codice di esempio?

Aggiornamento: Ho appena realizzato che il ragazzo che ha eseguito il mio test PDF ha appena inserito un URL come testo e non un'annotazione reale. Ha provato a mettere un'annotazione e il mio codice funziona ora ... Ma non è quello che mi serve, quindi sembra che dovrò analizzare il testo e cercare gli URL. Ma questa è un'altra storia ...

Aggiornamento 2: Così ho finalmente trovato un codice funzionante. Sto postando qui, quindi spero che possa aiutare qualcuno. Presume che il documento PDF contenga effettivamente annotazioni.

for(int i=0; i<pageCount; i++) { 
    CGPDFPageRef page = CGPDFDocumentGetPage(doc, i+1); 

    CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(page); 

    CGPDFArrayRef outputArray; 
    if(!CGPDFDictionaryGetArray(pageDictionary, "Annots", &outputArray)) { 
     return; 
    } 

    int arrayCount = CGPDFArrayGetCount(outputArray); 
    if(!arrayCount) { 
     continue; 
    } 

    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]); 

     CGPDFInteger pageRotate = 0; 
     CGPDFDictionaryGetInteger(pageDictionary, "Rotate", &pageRotate); 
     CGRect pageRect = CGRectIntegral(CGPDFPageGetBoxRect(page, 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.origin.x; 
     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); 

     // do whatever you need with the coordinates. 
     // e.g. you could create a button and put it on top of your page 
     // and use it to open the URL with UIApplication's openURL 
    } 
} 
+0

riga 6, dovrebbe non essere 'continua' invece di' ritorno'? - perché ritorni dopo aver controllato oggetto, valore, dict, stringa, array, ecc. –

+0

Questo è solo un esempio di codice senza alcun controllo degli errori. – pt2ph8

+0

PDF rects non tradurre in rects nativi consultare il thread per i dettagli: scorrere verso il basso fino a: 'Altre funzionalità PDF', 'Ottenere collegamenti all'interno di un PDF', 'Capire il PDF Rect per il posizionamento dei collegamenti' http://stackoverflow.com/questions/3889634/quick-and-lean-pdf-viewer-for-iphone-ipad-ios-tips-and-hints –

risposta

14

ecco l'idea di base per arrivare agli annoti CGPDFDictionary per ogni pagina almeno. dopo di ciò dovresti essere in grado di capirlo con l'aiuto delle specifiche PDF di Adobe.

1.) ottenere CGPDFDocumentRef.

2.) ottenere ogni pagina.

3.) su ogni pagina, utilizzare CGPDFDictionaryGetArray(pageDictionary, "Annots", &outputArray) dove pageDictionary è il CGPDFDictionary che rappresenta il CGPDFPage, e outputArray è la variabile (CGPDFArrayRef) per memorizzare la matrice Annots di quella pagina in.

+0

@Jesse Naugher: Grazie mille per la risposta, ma: "dopo di ciò dovresti essere in grado di capirlo con l'aiuto delle specifiche PDF di Adobe" Non sono riuscito a trovare alcuna informazione utile da quel pasticcio gonfio che è l'Adobe Specifiche PDF L'unica parte in cui appare la parola "annotation" è la sezione 8, ma ancora una volta, non riesco a vedere nessuna informazione che possa aiutarmi qui ... * frustrazione * – pt2ph8

+1

c'è un'intera sezione su ogni tipo di annotazione che può essere in un documento pdf, inclusa l'annotazione del collegamento. Fondamentalmente quando si ottiene l'array di annotazioni, lo si scorre e ogni voce è un dizionario che * è * un'annotazione. Questi dizionari hanno una chiave chiamata 'Sottotipo' che determina il tipo di annotazione che è, e "Link" è uno di questi, ed è definito nella specifica pdf. –

+0

@Jesse Naugher: Incredibile, ho appena realizzato che stavo fissando il documento sbagliato - ora ho il ** vero ** documento delle specifiche PDF. Vado a dare un'occhiata ora, grazie (sì, è quello che succede quando sei stanco/frustrato). – pt2ph8

9

Grande codice, ma io sto avendo un piccolo problema a lavorarci nel mio progetto. Ottiene correttamente tutti gli URL ma quando faccio clic su di esso non succede nulla. Ecco il mio codice che ho dovuto modificare leggermente il tuo per lavorare con il mio progetto). C'è qualcosa che manca:

- (void) renderPageAtIndex:(NSUInteger)index inContext:(CGContextRef)ctx { 
//CGPDFPageRef page = CGPDFDocumentGetPage(pdf, index+1); 

CGPDFPageRef page = CGPDFDocumentGetPage(pdf, index+1); 
CGAffineTransform transform1 = aspectFit(CGPDFPageGetBoxRect(page, kCGPDFMediaBox), 
             CGContextGetClipBoundingBox(ctx)); 
CGContextConcatCTM(ctx, transform1); 
CGContextDrawPDFPage(ctx, page); 

int pageCount = CGPDFDocumentGetNumberOfPages(pdf); 
int i = 0; 
while (i<pageCount) { 
    i++; 
    CGPDFPageRef page = CGPDFDocumentGetPage(pdf, i+1); 

    CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(page); 

    CGPDFArrayRef outputArray; 
    if(!CGPDFDictionaryGetArray(pageDictionary, "Annots", &outputArray)) { 
     return; 
    } 

    int arrayCount = CGPDFArrayGetCount(outputArray); 
    if(!arrayCount) { 
     continue; 
    } 

    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]); 

     CGPDFInteger pageRotate = 0; 
     CGPDFDictionaryGetInteger(pageDictionary, "Rotate", &pageRotate); 
     CGRect pageRect = CGRectIntegral(CGPDFPageGetBoxRect(page, 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.origin.x; 
     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); 

     // do whatever you need with the coordinates. 
     // e.g. you could create a button and put it on top of your page 
     // and use it to open the URL with UIApplication's openURL 
     NSURL *url = [NSURL URLWithString:uri]; 
     NSLog(@"URL: %@", url); 
     CGPDFContextSetURLForRect(ctx, (CFURLRef)url, rect); 
     // CFRelease(url); 
     } 
    } 


} 

Grazie & grande Brainfeeder lavoro!

UPDATE:

Per chiunque utilizzando il progetto foglie vostra applicazione questo è come ho ottenuto i link PDF per lavorare (non è perfetto come il rettangolo sembra riempire l'intero schermo, ma è un inizio):

- (void) renderPageAtIndex:(NSUInteger)index inContext:(CGContextRef)ctx { 

CGPDFPageRef page = CGPDFDocumentGetPage(pdf, index+1); 
CGAffineTransform transform1 = aspectFit(CGPDFPageGetBoxRect(page, kCGPDFMediaBox), 
             CGContextGetClipBoundingBox(ctx)); 
CGContextConcatCTM(ctx, transform1); 
CGContextDrawPDFPage(ctx, page); 


    CGPDFPageRef pageAd = CGPDFDocumentGetPage(pdf, index); 

    CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(pageAd); 

    CGPDFArrayRef outputArray; 
    if(!CGPDFDictionaryGetArray(pageDictionary, "Annots", &outputArray)) { 
     return; 
    } 

    int arrayCount = CGPDFArrayGetCount(outputArray); 
    if(!arrayCount) { 
     //continue; 
    } 

    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]); 

     CGPDFInteger pageRotate = 0; 
     CGPDFDictionaryGetInteger(pageDictionary, "Rotate", &pageRotate); 
     CGRect pageRect = CGRectIntegral(CGPDFPageGetBoxRect(page, 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.origin.x; 
     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); 

      // do whatever you need with the coordinates. 
      // e.g. you could create a button and put it on top of your page 
      // and use it to open the URL with UIApplication's openURL 
      NSURL *url = [NSURL URLWithString:uri]; 
      NSLog(@"URL: %@", url); 
//   CGPDFContextSetURLForRect(ctx, (CFURLRef)url, rect); 
      UIButton *button = [[UIButton alloc] initWithFrame:rect]; 
      [button setTitle:@"LINK" forState:UIControlStateNormal]; 
      [button addTarget:self action:@selector(openLink:) forControlEvents:UIControlEventTouchUpInside]; 
      [self.view addSubview:button]; 
      // CFRelease(url); 
     } 
    //} 

aggiornamento finale di seguito si riporta il codice finale che ho usato nelle mie applicazioni.

- (void) renderPageAtIndex:(NSUInteger)index inContext:(CGContextRef)ctx { 
//If the view already contains a button control remove it 
if ([[self.view subviews] containsObject:button]) { 
    [button removeFromSuperview]; 
} 

CGPDFPageRef page = CGPDFDocumentGetPage(pdf, index+1); 
CGAffineTransform transform1 = aspectFit(CGPDFPageGetBoxRect(page, kCGPDFMediaBox), 
             CGContextGetClipBoundingBox(ctx)); 
CGContextConcatCTM(ctx, transform1); 
CGContextDrawPDFPage(ctx, page); 


CGPDFPageRef pageAd = CGPDFDocumentGetPage(pdf, index); 

CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(pageAd); 

CGPDFArrayRef outputArray; 
if(!CGPDFDictionaryGetArray(pageDictionary, "Annots", &outputArray)) { 
    return; 
} 

int arrayCount = CGPDFArrayGetCount(outputArray); 
if(!arrayCount) { 
    //continue; 
} 

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]); 
    CGPDFInteger pageRotate = 0; 
    CGPDFDictionaryGetInteger(pageDictionary, "Rotate", &pageRotate); 
    CGRect pageRect = CGRectIntegral(CGPDFPageGetBoxRect(page, 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.origin.x; 
    rect.size.height -= rect.origin.y; 

    CGAffineTransform trans = CGAffineTransformIdentity; 
    trans = CGAffineTransformTranslate(trans, 35, pageRect.size.height+150); 
    trans = CGAffineTransformScale(trans, 1.15, -1.15); 

    rect = CGRectApplyAffineTransform(rect, trans); 

    urlLink = [NSURL URLWithString:uri]; 
    [urlLink retain]; 

    //Create a button to get link actions 
    button = [[UIButton alloc] initWithFrame:rect]; 
    [button setBackgroundImage:[UIImage imageNamed:@"link_bg.png"] forState:UIControlStateHighlighted]; 
    [button addTarget:self action:@selector(openLink:) forControlEvents:UIControlEventTouchUpInside]; 
    [self.view addSubview:button]; 
} 
[leavesView reloadData]; 
} 

} 
+0

@ user470763: Sì, l'aggiunta di un pulsante è la soluzione più ovvia :) – pt2ph8

+0

@Brainfeeder L'unico problema che sto avendo ora è che le dimensioni rect si adattano solo per iPhone e non per iPad. Inoltre, sui link a pagina intera non riesco a scorrere per cambiare pagina. –

+0

@kmcg: Grazie per il tuo codice, sono in grado di ridimensionare le dimensioni rettali anche in ipad, l'unica cosa di cui hai bisogno è modificare i valori di xey, potrebbe essere che possa aiutarti. Volevo anche chiederti se riesci a trovare una parola dal file pdf diverso da URL. Grazie. – Sarah

0

devo essere confuso, perché questo tutte le opere se uso:

CGRect rect = CGRectMake(coords[0],coords[1],coords[2]-coords[0]+1,coords[3]-coords[1]+1); 

Perchè sono abusando qualcosa più tardi, forse?PDF fornisce gli angoli e CGRect vuole un angolo e una dimensione.