Search code examples
iosswiftpdfcore-graphics

Force PDF page to fill context


In my project, I do need to generate an image of a PDF page, generated with CoreGraphics.

I managed to create a context, with the size of the image I want (destinationSize: CGSize) but when I use the CGPDFPageGetDrawingTransform function, it only resize the page down, but it won't scale the context to make the page fill the destination rect.

Here is the extract of code I have right now in my project :

UIGraphicsBeginImageContextWithOptions(destinationSize, true, 0)

defer {
    UIGraphicsEndImageContext()
}

// Invert y axis (CoreGraphics and UIKit axes are differents)
CGContextTranslateCTM( ctx, 0, destinationSize.height);
CGContextScaleCTM(ctx, 1, -1)

let transform = CGPDFPageGetDrawingTransform(pageRef, .CropBox, CGRect(origin: CGPointZero, size: destinationSize), 0, true)
CGContextConcatCTM(ctx, transform)

// TODO We need force the page to fill all the dest rect when it's bigger than the CropBox size

CGContextDrawPDFPage(ctx, pageRef)

I tried to scale my context by a scale factor with this, replacing the TODO :

let contextScale: CGFloat = (pageRect.width < expectedWidth) ? expectedWidth / pageRect.width : 1
CGContextScaleCTM(ctx, contextScale, contextScale)

but it created an incorrect offset of the drawing, and I'm kind of lost with CoreGraphics transformations.

What would be the correct way to rescale the context to make sure the pdf page draws to fill the context size ?


Solution

  • This is the solution I came up with.

    For what I know, this is working for any pdf document page. (tested with many rotations, cropbox sizes and origins.

    func transformationForPage(_ pageNumber: Int, targetSize: CGSize) -> CGAffineTransform {
        let pageRef = getPage(pageNumber)
        let rotation = getPageRotationInteger(pageNumber)
        let cropbox = cropboxForPage(pageNumber)
    
        var transform = pageRef!.getDrawingTransform(.cropBox, rect: CGRect(origin: CGPoint.zero, size: targetSize), rotate: 0, preserveAspectRatio: true)
    
        // We change the context scale to fill completely the destination size
        if cropbox.width < targetSize.width {
            let contextScale = targetSize.width / cropbox.width
            transform = transform.scaledBy(x: contextScale, y: contextScale)
    
            transform.tx = -(cropbox.origin.x * transform.a + cropbox.origin.y * transform.b)
            transform.ty = -(cropbox.origin.x * transform.c + cropbox.origin.y * transform.d)
    
            // Rotation handling
            if rotation == 180 || rotation == 270 {
                transform.tx += targetSize.width
            }
            if rotation == 90 || rotation == 180 {
                transform.ty += targetSize.height
            }
        }
    
        return transform
    }