techprimate/TPPDF

Use Image to Generate PDF on iOS 17, there is crash

wangzhizhou opened this issue · 8 comments

What did you do?

I want use image to generate a pdf file, on iOS 17, there is crash occured!

What did you expect to happen?

generate images pdf file as expected, but crashed

What happened instead?

image

*** Assertion failure in void _UIGraphicsBeginImageContextWithOptions(CGSize, BOOL, CGFloat, BOOL)(), UIGraphics.m:410
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UIGraphicsBeginImageContext() failed to allocate CGBitampContext: size={0, 0}, scale=1.000000, bitmapInfo=0x2002. Use UIGraphicsImageRenderer to avoid this assert.'
*** First throw call stack:
(0x1882cab28 0x180142f78 0x18772fa5c 0x18a54e33c 0x102c4cdfc 0x102c4c53c 0x102c5ed74 0x102c0b41c 0x102c079a0 0x102c06848 0x102c06748 0x102c0f740 0x102c0fa00 0x102b15014 0x102b15b1c 0x104634b98 0x1046367bc 0x10463930c 0x10464aae4 0x10464b4d8 0x1e41c0ee4 0x1e41c0fc0)
libc++abi: terminating due to uncaught exception of type NSException

TPPDF Environment

TPPDF version: 2.4.1
Xcode version: 15.3
Swift version: 5.10

Demo Code / Project

@IBAction func pdfBtnAction(_ sender: UIBarButtonItem) {
        sender.isEnabled = false
        DispatchQueue.global().async {
            guard let localComicDir = self.localComicDir,
                  let comicNameSubstring = localComicDir.split(separator: "/").last
            else {
                return
            }
            let name = String(comicNameSubstring)
            let document = PDFDocument(format: .b5)
            document.add(.contentCenter, text: name)
            self.images?[0..<10].compactMap({ imageFileUrl -> PDFImage? in
                guard let imageURL = URL(string: imageFileUrl),
                      let image = UIImage(contentsOfFile: imageURL.path(percentEncoded: false)),
                      image.size != .zero
                else {
                    return nil
                }
                
                let pdfImage = PDFImage(image: image)
                return pdfImage
            }).forEach({ pdfImage in
                document.add(image: pdfImage)
            })
            let generator = PDFGenerator(document: document)
            let info = PDFInfo()
            info.title = name
            do {
                let pdfFileURL = try generator.generateURL(filename: "test.pdf", info: info)
                DispatchQueue.main.async {
                    self.airDropFile(at: pdfFileURL)
                    sender.isEnabled = true
                }
            } catch {
                DispatchQueue.main.async {
                    self.toast(error.localizedDescription)
                    sender.isEnabled = true
                }
            }
        }
    }

Hi, thanks for reporting this issue.
Looking at the crash indicates that the context bitmap has size {0,0}:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason:'
UIGraphicsBeginImageContext() failed to allocate CGBitampContext: 
  size={0, 0}, 
  scale=1.000000, 
  bitmapInfo=0x2002. 
Use UIGraphicsImageRenderer to avoid this assert.'

I believe that simply using the UIGraphicsImageRenderer is not a valid solution, because it basically just avoids the assert, instead of correctly handling the situation.

Looking at the relevant code, I believe that there is a calculation issue instead.
Due to floating-point errors, I had to add a floor(..), which could cause a non-zero frame to be scaled to zero size.

let factor: CGFloat = min(3 * quality, 1)
let resizeFactor = factor.isZero ? 0.2 : factor
// If there is a floating point error, e.g. 24.000000000000004, then UIKit will use the next higher integer value, but AppKit does not
let size = CGSize(width: floor(frame.width * resizeFactor),
height: floor(frame.height * resizeFactor))
#if os(iOS)
UIGraphicsBeginImageContext(size)
image.draw(in: CGRect(origin: .zero, size: size))
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

Can you please provide the image which causes the issue? It would make it easier to test.
If that is not posssible, can you please provide the resolution of the image.

Hi, thanks for reporting this issue. Looking at the crash indicates that the context bitmap has size {0,0}:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason:'
UIGraphicsBeginImageContext() failed to allocate CGBitampContext: 
  size={0, 0}, 
  scale=1.000000, 
  bitmapInfo=0x2002. 
Use UIGraphicsImageRenderer to avoid this assert.'

I believe that simply using the UIGraphicsImageRenderer is not a valid solution, because it basically just avoids the assert, instead of correctly handling the situation.

Looking at the relevant code, I believe that there is a calculation issue instead. Due to floating-point errors, I had to add a floor(..), which could cause a non-zero frame to be scaled to zero size.

let factor: CGFloat = min(3 * quality, 1)
let resizeFactor = factor.isZero ? 0.2 : factor
// If there is a floating point error, e.g. 24.000000000000004, then UIKit will use the next higher integer value, but AppKit does not
let size = CGSize(width: floor(frame.width * resizeFactor),
height: floor(frame.height * resizeFactor))
#if os(iOS)
UIGraphicsBeginImageContext(size)
image.draw(in: CGRect(origin: .zero, size: size))
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

Can you please provide the image which causes the issue? It would make it easier to test. If that is not posssible, can you please provide the resolution of the image.

The image is as follow image:
image

test_images.zip

I found the issue and it is unrelated to the resizing of images, but instead there is an edge case that wasn't handled with zero available space left on a page.

Can you please your application with the code on branch issue-366 and let me know if the issue is resolved?
If so, I'll merge the changes into main and release a new version

I found the issue and it is unrelated to the resizing of images, but instead there is an edge case that wasn't handled with zero available space left on a page.

Can you please your application with the code on branch issue-366 and let me know if the issue is resolved?

If so, I'll merge the changes into main and release a new version

ok I will test this fix later and notify you the result

I found the issue and it is unrelated to the resizing of images, but instead there is an edge case that wasn't handled with zero available space left on a page.

Can you please your application with the code on branch issue-366 and let me know if the issue is resolved? If so, I'll merge the changes into main and release a new version

today I test thist case with your branch issue-366,and problem is not occured again.

image

Perfect, then I am going to release this now

2.5.0 has fixed it

Awesome! Thanks for the update