简体   繁体   中英

Reduce file size of rendered PDF in iOS CoreGraphics

In my app I create a PDF out of multiple images (10 to 15 images) and send it via email. My problem is that the created PDF is way too big, even if I use JPG images . The images are photos of receipts or invoices.

My debugger shows me the following information about the images I want to put in the PDF (they are taken with the built-in camera on an iPhone 6 )

print("UIScreen.main.scale = \(UIScreen.main.scale)") //prints 2
print("image.size = \(image.size)") //prints (2448.0, 3264.0)
print("image.scale = \(image.scale)") //prints image.scale = 1

let imgDataJpg: NSData = NSData(data: UIImageJPEGRepresentation(image, 0.5)!)
let imageSizeJpg = Double(imgDataJpg.length)

print("image size on disk in KB: %f ", imageSizeJpg / 1024.0) //prints 899.5888671875

I've written a protocol extension for creating the PDF file. Here's my code:

protocol RendersPdf {
    var realm: Realm { get }
    func renderPdf() -> URL?
}
extension RendersPdf {

//this is the default size of a PDF page
fileprivate var pageFrame: CGRect {
    get {
        return CGRect(x: 0, y: 0, width: 612, height: 792)
    }
}

func renderPdf(images: [UIImage]) -> URL? {
    let pdfUrl = startPdf()
    let context = UIGraphicsGetCurrentContext()
    defer {
        UIGraphicsEndPDFContext()
    }
    for image in images {
        draw(image: image)
    }   
}
func startPdf() -> URL {
    let pdfUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("somePdfName").appendingPathExtension("pdf")

    let success = UIGraphicsBeginPDFContextToFile(pdfUrl.path, CGRect.zero, nil)
    assert(success, "could not create pdf graphics context")
    return pdfUrl
}
fileprivate func draw(image: UIImage) {
    UIGraphicsBeginPDFPageWithInfo(pageFrame, nil)
    let imageFrame = pageFrame
    image.draw(in: imageFrame)   
}
fileprivate func resize(image: UIImage) -> UIImage {

    UIGraphicsBeginImageContextWithOptions(pageFrame.size, false, 0.0);
    image.draw(in: CGRect(x: 0, y: 0, width: pageFrame.size.width, height: pageFrame.size.height))
    let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() ?? image
    UIGraphicsEndImageContext()
    return newImage   
}
}

If I have 11 images, my PDF gets 11MB big so I tried to make it smaller by resizing the images manually using the resize function above. However, the PDF gets even bigger, in my case up to 32MB (how in the world is this possible, I've made the images smaller?)

What can I do to reduce the file size of my generated PDF? How can I make sure that the solution works on different screen resolutions (iPhone 6: 1 point is 2px on iPhone 6+ 1 point is 3px).

You need to the set the max file size of your pdf and reduce the image quality depending on this size. Check this example and feel free to use and modify it if you want:

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    //choose the size you want or use your own size
    let MB_10 = 10485760
    let MB_5 = 5242880
    let MB_3 = 3145728

    let images : [UIImage] = [] //<---- insert your images here

    if self.createPDF(images: images, maxSize: MB_5, quality: 100) != nil {
        print("worked check pdfFilePath")
    }
}

func createPDF(images:[UIImage], maxSize:Int, quality:Int) -> NSData? {
    if quality > 0 {
        guard let pdfFilePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("someName.pdf") else {
            print("Error creating pdf path")
            return nil
        }

        var largestImageSize = CGSize.zero

        for image in images {
            if image.size.width > largestImageSize.width {
                largestImageSize.width = image.size.width
            }

            if image.size.height > largestImageSize.height {
                largestImageSize.height = image.size.height
            }
        }

        let pdfData = NSMutableData()
        UIGraphicsBeginPDFContextToData(pdfData, CGRect(x: 0, y: 0, width: largestImageSize.width, height: largestImageSize.height), nil)

        let context = UIGraphicsGetCurrentContext()

        for image in images {
            UIGraphicsBeginPDFPage()
            UIGraphicsPushContext(context!)

            if quality != 100 {
                guard let imageData = UIImageJPEGRepresentation(image, CGFloat(quality) / 100.0) else {
                    print("Error reducing image size")
                    return nil
                }
                guard let newImage = UIImage(data: imageData) else {
                    print("Error creating image from data")
                    return nil
                }
                newImage.draw(at: CGPoint.zero)
            } else {
                image.draw(at: CGPoint.zero)
            }
            UIGraphicsPopContext()
        }

        UIGraphicsEndPDFContext();

        if (pdfData.length > maxSize) {
            print("reduces quality to \(quality - 10)")
            return self.createPDF(images: images, maxSize: maxSize, quality: quality - 10)
        }

        if !pdfData.write(toFile: pdfFilePath, atomically: true) {
            print("write to pdfFilePath failed")
        }

        return NSData(contentsOfFile: pdfFilePath)
    }

    return nil
}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM