You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS PDFKit添加旋转图片图章注释解决边角裁剪问题

PDFKit图片注释旋转后边角被裁剪的问题

我在给PDF文件添加图章注释时,未旋转图片功能正常,但旋转后注释边角会出现裁剪现象,如图所示:

旋转后裁剪的注释


问题相关代码

自定义ImageAnnotation类

import PDFKit
import UIKit

final class ImageAnnotation: PDFAnnotation {
    var image: UIImage
    var currentRotationAngle: CGFloat = 0.0
    
    init(bounds: CGRect, image: UIImage) {
        self.image = image
        super.init(bounds: bounds, forType: .stamp, withProperties: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(with box: PDFDisplayBox, in context: CGContext) {
        context.saveGState()
        let midX = bounds.midX
        let midY = bounds.midY
        context.translateBy(x: midX, y: midY)
        context.rotate(by: -currentRotationAngle)
        context.translateBy(x: -midX, y: -midY)
        
        guard let cgImage = image.cgImage else { return }
        context.draw(cgImage, in: bounds)
        context.restoreGState()
    }
}

添加图片贴纸到PDF overlay的代码

func addImage(_ image: UIImage) {
        var rect = frameForImage(image, within: overlayView)
        rect.origin.x = overlayView.center.x - rect.width / 2
        rect.origin.y = overlayView.center.y - rect.height / 2
        self.frame = rect
        
        let imageView = UIImageView(image: image)
        imageView.isUserInteractionEnabled = true
        addSubview(imageView)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            imageView.topAnchor.constraint(equalTo: imageView.superview!.topAnchor, constant: 24),
            imageView.leadingAnchor.constraint(equalTo: imageView.superview!.leadingAnchor, constant: 24),
            imageView.trailingAnchor.constraint(equalTo: imageView.superview!.trailingAnchor, constant: -24),
            imageView.bottomAnchor.constraint(equalTo: imageView.superview!.bottomAnchor, constant: -24)
        ])
    }

向PDFDocument添加注释的方法

func addImageAnnotation(image: UIImage) {
        guard let imageView = firstView(type: UIImageView.self) else {
            return
        }
        let frameAfterMargin = CGRect(x: center.x - imageView.bounds.width / 2,
                                      y: center.y - imageView.bounds.height / 2,
                                      width: imageView.bounds.width,
                                      height: imageView.bounds.height)
        let viewFrame = overlayView.convert(frameAfterMargin, to: pdfView)
        let annotationBounds = pdfView.convert(viewFrame, to: pdfPage)
        let annotation = ImageAnnotation(bounds: annotationBounds, image: image)
        annotation.currentRotationAngle = rotationAngle
        pdfPage.addAnnotation(annotation)
    }

解决方案

问题核心是注释的bounds未适配旋转后的图片外接尺寸,旋转后图片实际占位范围变大,但原bounds仍为初始矩形大小,导致超出部分被裁剪。需从两个关键环节调整:

1. 计算旋转后的正确注释bounds

添加工具方法计算矩形旋转后的外接矩形,确保注释能容纳完整的旋转后图片:

func rotatedRect(for rect: CGRect, angle: CGFloat) -> CGRect {
    let radians = angle * .pi / 180
    let rotationTransform = CGAffineTransform(rotationAngle: radians)
    let rotatedSize = rect.size.applying(rotationTransform)
    let x = rect.origin.x + (rect.width - rotatedSize.width) / 2
    let y = rect.origin.y + (rect.height - rotatedSize.height) / 2
    return CGRect(x: x, y: y, width: rotatedSize.width, height: rotatedSize.height)
}

2. 修改注释创建逻辑,使用旋转后的bounds

更新addImageAnnotation方法,替换原bounds为旋转后的外接矩形:

func addImageAnnotation(image: UIImage) {
        guard let imageView = firstView(type: UIImageView.self) else {
            return
        }
        let frameAfterMargin = CGRect(x: center.x - imageView.bounds.width / 2,
                                      y: center.y - imageView.bounds.height / 2,
                                      width: imageView.bounds.width,
                                      height: imageView.bounds.height)
        let viewFrame = overlayView.convert(frameAfterMargin, to: pdfView)
        let originalAnnotationBounds = pdfView.convert(viewFrame, to: pdfPage)
        // 替换为旋转后的外接矩形
        let rotatedBounds = rotatedRect(for: originalAnnotationBounds, angle: rotationAngle)
        let annotation = ImageAnnotation(bounds: rotatedBounds, image: image)
        annotation.currentRotationAngle = rotationAngle
        pdfPage.addAnnotation(annotation)
    }

3. 调整注释绘制逻辑,确保图片居中显示

修改ImageAnnotation的draw方法,让图片在旋转后的bounds中居中绘制:

override func draw(with box: PDFDisplayBox, in context: CGContext) {
        context.saveGState()
        
        // 计算适配旋转后bounds的绘制矩形,保证图片居中
        let originalSize = image.size
        let radians = currentRotationAngle * .pi / 180
        let rotatedWidth = originalSize.width * abs(cos(radians)) + originalSize.height * abs(sin(radians))
        let rotatedHeight = originalSize.height * abs(cos(radians)) + originalSize.width * abs(sin(radians))
        let scaleX = bounds.width / rotatedWidth
        let scaleY = bounds.height / rotatedHeight
        let scale = min(scaleX, scaleY)
        let scaledSize = CGSize(width: originalSize.width * scale, height: originalSize.height * scale)
        let drawRect = CGRect(
            x: bounds.midX - scaledSize.width / 2,
            y: bounds.midY - scaledSize.height / 2,
            width: scaledSize.width,
            height: scaledSize.height
        )
        
        // 以bounds中心为旋转点执行旋转
        let midX = bounds.midX
        let midY = bounds.midY
        context.translateBy(x: midX, y: midY)
        context.rotate(by: -currentRotationAngle)
        context.translateBy(x: -midX, y: -midY)
        
        guard let cgImage = image.cgImage else { return }
        context.draw(cgImage, in: drawRect)
        context.restoreGState()
    }

修改完成后,注释的bounds会自动适配旋转后的图片尺寸,边角裁剪问题即可解决。

内容的提问来源于stack exchange,提问作者kmithi1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 06:24:53