Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wangzhizhou opened this issue Mar 30, 2024 · 4 comments · May be fixed by #377
Open

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

wangzhizhou opened this issue Mar 30, 2024 · 4 comments · May be fixed by #377
Labels

Comments

@wangzhizhou
Copy link

wangzhizhou commented Mar 30, 2024

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
                }
            }
        }
    }
@philprime
Copy link
Member

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.

@wangzhizhou
Copy link
Author

wangzhizhou commented May 6, 2024

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

@philprime
Copy link
Member

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

@wangzhizhou
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants