2011-07-01 34 views
9

Có cách nào trong iOS để hợp nhất các tệp PDF, có nghĩa là, nối các trang của một tệp vào cuối tệp khác và lưu nó vào đĩa không?Hợp nhất các tệp PDF trên iOS

+0

Đây không phải là một câu trả lời cho điều này, nhưng nếu ai đó muốn [nối thêm một tập tin pdf hiện có] (http://stackoverflow.com/a/15355168/1603234) – Hemang

Trả lời

21

tôi bước ra với giải pháp này:

// Documents dir 
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
NSString *documentsDirectory = [paths objectAtIndex:0]; 

// File paths 
NSString *pdfPath1 = [documentsDirectory stringByAppendingPathComponent:@"1.pdf"]; 
NSString *pdfPath2 = [documentsDirectory stringByAppendingPathComponent:@"2.pdf"]; 
NSString *pdfPathOutput = [documentsDirectory stringByAppendingPathComponent:@"out.pdf"]; 

// File URLs 
CFURLRef pdfURL1 = (CFURLRef)[[NSURL alloc] initFileURLWithPath:pdfPath1]; 
CFURLRef pdfURL2 = (CFURLRef)[[NSURL alloc] initFileURLWithPath:pdfPath2]; 
CFURLRef pdfURLOutput = (CFURLRef)[[NSURL alloc] initFileURLWithPath:pdfPathOutput]; 

// File references 
CGPDFDocumentRef pdfRef1 = CGPDFDocumentCreateWithURL((CFURLRef) pdfURL1); 
CGPDFDocumentRef pdfRef2 = CGPDFDocumentCreateWithURL((CFURLRef) pdfURL2); 

// Number of pages 
NSInteger numberOfPages1 = CGPDFDocumentGetNumberOfPages(pdfRef1); 
NSInteger numberOfPages2 = CGPDFDocumentGetNumberOfPages(pdfRef2); 

// Create the output context 
CGContextRef writeContext = CGPDFContextCreateWithURL(pdfURLOutput, NULL, NULL); 

// Loop variables 
CGPDFPageRef page; 
CGRect mediaBox; 

// Read the first PDF and generate the output pages 
NSLog(@"GENERATING PAGES FROM PDF 1 (%i)...", numberOfPages1); 
for (int i=1; i<=numberOfPages1; i++) { 
    page = CGPDFDocumentGetPage(pdfRef1, i); 
    mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); 
    CGContextBeginPage(writeContext, &mediaBox); 
    CGContextDrawPDFPage(writeContext, page); 
    CGContextEndPage(writeContext); 
} 

// Read the second PDF and generate the output pages 
NSLog(@"GENERATING PAGES FROM PDF 2 (%i)...", numberOfPages2); 
for (int i=1; i<=numberOfPages2; i++) { 
    page = CGPDFDocumentGetPage(pdfRef2, i); 
    mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); 
    CGContextBeginPage(writeContext, &mediaBox); 
    CGContextDrawPDFPage(writeContext, page); 
    CGContextEndPage(writeContext);  
} 
NSLog(@"DONE!"); 

// Finalize the output file 
CGPDFContextClose(writeContext); 

// Release from memory 
CFRelease(pdfURL1); 
CFRelease(pdfURL2); 
CFRelease(pdfURLOutput); 
CGPDFDocumentRelease(pdfRef1); 
CGPDFDocumentRelease(pdfRef2); 
CGContextRelease(writeContext); 

Vấn đề lớn nhất ở đây là cấp phát bộ nhớ. Như bạn có thể thấy, trong phương pháp này, bạn phải đọc cả hai tệp PDF mà bạn muốn hợp nhất và, đồng thời, tạo đầu ra. Các bản phát hành chỉ xảy ra ở cuối. Tôi đã thử kết hợp một tập tin PDF với 500 trang (~ 15MB) với một trang khác có chứa 100 trang (~ 3MB) và nó tạo ra một trang mới với 600 trang (tất nhiên!) Chỉ có kích thước ~ 5MB (ma thuật?). Việc thực hiện mất khoảng 30 giây (không quá tệ, xem xét iPad 1) và phân bổ 17MB (ouch!). Các ứng dụng may mắn đã không sụp đổ, nhưng tôi nghĩ rằng iOS sẽ thích giết một ứng dụng tiêu thụ 17MB như thế này. ; P

+0

Không nên khó sửa đổi mã để nó phát hành từng tài liệu (hoặc thậm chí mỗi x trang), nếu bạn đang viết mỗi trang vào đĩa cùng một lúc ... heck, bạn có thể đọc và phát hành một trang tại một thời điểm! Đó là một sự cân bằng giữa tốc độ và trí nhớ… – FeifanZ

+0

Điều đó sẽ hiệu quả! Mặc dù tôi không thể tìm được cách để thực hiện điều đó. Sau khi bạn đóng một bối cảnh (lưu tệp vào đĩa) bằng cách sử dụng 'CGPDFContextClose', bạn không thể mở lại và tiếp tục chỉnh sửa từ nơi bạn đã dừng, như di chuyển con trỏ đến cuối tệp và thêm nội dung mới. – Jonatan

+0

Hmmm ... Tôi không quá quen thuộc với các hàm và phương thức ở dưới đó, nhưng có thể có một số loại tệp luồng I/O cho phép bạn nâng cao con trỏ. Hoặc chỉ cần rút ra một cái gì đó giống như một megabyte bit tại một thời điểm và ghi nó vào đĩa (không chắc chắn như thế nào mà sẽ làm việc mặc dù) – FeifanZ

0

Tôi tin rằng FastPdfKit chính xác là những gì bạn đang tìm kiếm và miễn phí!

+0

Đó là một thư viện tốt đẹp o hiển thị pdf, nhưng nó không hỗ trợ sáp nhập AFAICT. – lkraider

23

Tôi đã thực hiện một chút cấu trúc lại trên mã của Jonathan tham gia bất kỳ tập tin PDF của bất kỳ kích thước:

+ (NSString *)joinPDF:(NSArray *)listOfPaths { 
    // File paths 
    NSString *fileName = @"ALL.pdf"; 
    NSString *pdfPathOutput = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:fileName]; 

    CFURLRef pdfURLOutput = ( CFURLRef)CFBridgingRetain([NSURL fileURLWithPath:pdfPathOutput]); 

    NSInteger numberOfPages = 0; 
    // Create the output context 
    CGContextRef writeContext = CGPDFContextCreateWithURL(pdfURLOutput, NULL, NULL); 

    for (NSString *source in listOfPaths) { 
     CFURLRef pdfURL = ( CFURLRef)CFBridgingRetain([[NSURL alloc] initFileURLWithPath:source]); 

     //file ref 
     CGPDFDocumentRef pdfRef = CGPDFDocumentCreateWithURL((CFURLRef) pdfURL); 
     numberOfPages = CGPDFDocumentGetNumberOfPages(pdfRef); 

     // Loop variables 
     CGPDFPageRef page; 
     CGRect mediaBox; 

     // Read the first PDF and generate the output pages 
     DLog(@"GENERATING PAGES FROM PDF 1 (%@)...", source); 
     for (int i=1; i<=numberOfPages; i++) { 
      page = CGPDFDocumentGetPage(pdfRef, i); 
      mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); 
      CGContextBeginPage(writeContext, &mediaBox); 
      CGContextDrawPDFPage(writeContext, page); 
      CGContextEndPage(writeContext); 
     } 

     CGPDFDocumentRelease(pdfRef); 
     CFRelease(pdfURL); 
    } 
    CFRelease(pdfURLOutput); 

    // Finalize the output file 
    CGPDFContextClose(writeContext); 
    CGContextRelease(writeContext); 

    return pdfPathOutput; 
} 

Hy vọng rằng sẽ giúp

+2

chỉ là bản cập nhật cho ARC: Có vẻ như điều này sẽ ném ngoại lệ trừ khi bạn sử dụng phương thức tự động phát lại fileURLWithPath: -> (__bridge CFURLRef) [NSURL fileURLWithPath: pdfPathOutput] thay vì phương thức init. – Bek

+0

Bản gốc pdf khác biệt về kích thước và phiên bản tương thích, chỉ xem xét một tệp pdf để hợp nhất, bản gốc nằm trong phiên bản 1.4 nhưng bản gốc được tạo trong 1.3. Làm thế nào tôi có thể thiết lập phiên bản? – Cam

3

tôi đang đẩy mạnh thư viện riêng của tôi ở đây ... nhưng Tôi có một thư viện đọc/ghi PDF miễn phí, mà gần đây tôi đã giới thiệu cách sử dụng trong ngữ cảnh iOS. nó hoàn hảo cho việc sáp nhập các tệp PDF và thao tác chúng, và làm điều đó với một chữ ký bộ nhớ tương đối nhỏ. Hãy xem xét sử dụng nó, xem ở đây một ví dụ - ios with PDFHummus. Một lần nữa, đó là tôi thúc đẩy thư viện của riêng tôi, vì vậy hãy thực hiện lời khuyên này trong bối cảnh phù hợp.

1

Tôi dựa trên giải pháp của mình về giải pháp được tạo bởi @matsoftware.

Tôi tạo ra một đoạn mã cho giải pháp của tôi: https://gist.github.com/jefferythomas/7265536

+ (void)combinePDFURLs:(NSArray *)PDFURLs writeToURL:(NSURL *)URL 
{ 
    CGContextRef context = CGPDFContextCreateWithURL((__bridge CFURLRef)URL, NULL, NULL); 

    for (NSURL *PDFURL in PDFURLs) { 
     CGPDFDocumentRef document = CGPDFDocumentCreateWithURL((__bridge CFURLRef)PDFURL); 
     size_t numberOfPages = CGPDFDocumentGetNumberOfPages(document); 

     for (size_t pageNumber = 1; pageNumber <= numberOfPages; ++pageNumber) { 
      CGPDFPageRef page = CGPDFDocumentGetPage(document, pageNumber); 
      CGRect mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); 

      CGContextBeginPage(context, &mediaBox); 
      CGContextDrawPDFPage(context, page); 
      CGContextEndPage(context); 
     } 

     CGPDFDocumentRelease(document); 
    } 

    CGPDFContextClose(context); 
    CGContextRelease(context); 
} 
2

tôi nghĩ rằng tôi muốn chia sẻ câu trả lời bằng Swift kể từ khi tôi đang tìm kiếm nó trong Swift và không thể tìm thấy nó và phải dịch nó. Ngoài ra, câu trả lời của tôi sử dụng một mảng của từng tệp pdf riêng lẻ pdfPagesURLArray và lặp lại để tạo bản pdf hoàn chỉnh. Tôi khá mới ở đây vì vậy bất kỳ đề xuất được chào đón.

let file = "fileName.pdf" 
    guard var documentPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first else { 
     NSLog("Doh - can't find that path") 
     return 
    } 
    documentPaths = documentPaths.stringByAppendingString(file) 
    print(documentPaths) 

    let fullPDFOutput: CFURLRef = NSURL(fileURLWithPath: documentPaths) 

    let writeContext = CGPDFContextCreateWithURL(fullPDFOutput, nil, nil) 

    for pdfURL in pdfPagesURLArray { 
     let pdfPath: CFURLRef = NSURL(fileURLWithPath: pdfURL) 
     let pdfReference = CGPDFDocumentCreateWithURL(pdfPath) 
     let numberOfPages = CGPDFDocumentGetNumberOfPages(pdfReference) 
     var page: CGPDFPageRef 
     var mediaBox: CGRect 

     for index in 1...numberOfPages { 

có thể làm lực lượng unwrapping đây như thế này: page = CGPDFDocumentGetPage(pdfReference, index)! Nhưng để tiếp tục thực hành tốt nhất:

 guard let getCGPDFPage = CGPDFDocumentGetPage(pdfReference, index) else { 
       NSLog("Error occurred in creating page") 
       return 
      } 
      page = getCGPDFPage 
      mediaBox = CGPDFPageGetBoxRect(page, .MediaBox) 
      CGContextBeginPage(writeContext, &mediaBox) 
      CGContextDrawPDFPage(writeContext, page) 
      CGContextEndPage(writeContext) 
     } 
    } 
    NSLog("DONE!") 

    CGPDFContextClose(writeContext); 

    NSLog(documentPaths) 
+0

Cảm ơn một bó, tôi đang tạo pdf từ html và tôi cần ngắt trang, Vì tôi không thể tìm thấy bất kỳ, Vì vậy, tôi đã tạo nhiều pdf với nhiều html và sau đó sử dụng mã ur để kết hợp tất cả, cảm ơn rất nhiều :) – vinbhai4u

+1

@ vinbhai4u Tôi không thể tìm thấy bất kỳ giải pháp trong swift vì vậy tôi đã hy vọng những gì tôi đã đưa ra sẽ là hữu ích cho người khác. Vui mừng nó đã giúp bạn :) – FromTheStix

1

chức năng của tôi trong nhanh chóng 3:

// sourcePdfFiles is array of source file full paths, destPdfFile is dest file full path 
func mergePdfFiles(sourcePdfFiles:[String], destPdfFile:String) { 

    guard UIGraphicsBeginPDFContextToFile(destPdfFile, CGRect.zero, nil) else { 
     return 
    } 
    guard let destContext = UIGraphicsGetCurrentContext() else { 
     return 
    } 

    for index in 0 ..< sourcePdfFiles.count { 
     let pdfFile = sourcePdfFiles[index] 
     let pdfUrl = NSURL(fileURLWithPath: pdfFile) 
     guard let pdfRef = CGPDFDocument(pdfUrl) else { 
      continue 
     } 

     for i in 1 ... pdfRef.numberOfPages { 
      if let page = pdfRef.page(at: i) { 
       var mediaBox = page.getBoxRect(.mediaBox) 
       destContext.beginPage(mediaBox: &mediaBox) 
       destContext.drawPDFPage(page) 
       destContext.endPage() 
      } 
     } 
    } 

    destContext.closePDF() 
    UIGraphicsEndPDFContext() 
} 
+0

Điều này cực kỳ hữu ích. Cảm ơn bạn! :) –

Các vấn đề liên quan