Skip to content
Guides

Annotation Rotation

Annotation rotation provides the functionality to rotate standard stamp annotations, custom stamp annotations, image annotations, and electronic signature annotations, allowing users to rotate annotations through a full range (-180~180 degrees). ComPDFKit offers convenient APIs for rotating annotations, along with demo demonstrations.

Rotating Annotations Programmatically

To rotate annotations programmatically, we need to set three properties of the annotation when initializing it: annotationRotation, saveSourceRect, and saveRectRotationPoints.

  • annotationRotation

    Sets the rotation angle of the annotation in degrees, with a range from -180 to 180.

  • saveSourceRect

    The rectangular Rect before rotation. It's worth noting that this only needs to be refreshed when the annotation is scaled or moved, not when it is rotated.

  • saveRectRotationPoints

    All rectangular vertices after rotation. Below is an example of how to obtain the vertices after rotation.

Here is the example code for annotation rotation:

swift
// Create standard Stamp annotations
let stampAnnotation = CPDFStampAnnotation(document: document, type: selectedIndex)
 annotation?.bounds = CGRect(x: 100, y: 100, width: 100, height: 100)

// Sets the rotation property of the annotations
stampAnnotation.annotationRotation = 0
// rotatePoints initialize the four vertices of the bounds
stampAnnotation.setSaveRectRotationPoints(rotatePoints)
stampAnnotation.updateRotationAppearanceStream()
stampAnnotation.setSaveSourceRect(stampAnnotation.bounds)
stampAnnotation.setSaveRectRotationPoints(rotatePoints)

// Annotation rotation
@objc func rotateStampAnnotation(_ stampAnnotation: CPDFStampAnnotation, Roate angleDifference: CGFloat) {
    stampAnnotation.annotationRotation = Int(angleDifference * 180 / .pi)
  // Get stampAnnotation.saveSourceRect() four vertices
    var rotatedVertices = computeRotatedRectVerticesWithMidpoints(bounds: stampAnnotation.saveSourceRect(), annotationRotationDegrees: 0, includeMidpoints: true)
  // rotatedVertices are sorted, with the lower left foot of saveSourceRect as the first point, sorted counterclockwise
    rotatedVertices = sortPointsToFormRectangle(points: rotatedVertices)
    let center =  CGPoint(x: stampAnnotation.saveSourceRect().midX, y: stampAnnotation.saveSourceRect().midY)
    var rotatePoints: [NSValue] = []
    var newRotatedVertices: [CGPoint] = []
    for point in rotatedVertices {
      // Gets the rotated vertex
        let rotatePoint = rotatePointAroundCenter(point: point, center: center, angleRadians: angleDifference)
        let pointValue = NSValue(cgPoint: rotatePoint)
        rotatePoints.append(pointValue)
        newRotatedVertices.append(rotatePoint)
    }
    stampAnnotation.setSaveRectRotationPoints(rotatePoints)
    stampAnnotation.bounds = boundingRectForPoints(points: newRotatedVertices)
    stampAnnotation.updateRotationAppearanceStream()
    stampAnnotation.setSaveRectRotationPoints(rotatePoints)
}

// Gets the rotated vertex
func rotatePointAroundCenter(point: CGPoint, center: CGPoint, angleRadians: CGFloat) -> CGPoint {
    let s = sin(angleRadians)
    let c = cos(angleRadians)

    // Translate point back to origin
    var translatedPoint = CGPoint(x: point.x - center.x, y: point.y - center.y)

    // Rotate point
    let xnew = translatedPoint.x * c - translatedPoint.y * s
    let ynew = translatedPoint.x * s + translatedPoint.y * c

    // Translate point back
    translatedPoint.x = xnew + center.x
    translatedPoint.y = ynew + center.y

    return translatedPoint
}
objective-c
// Create standard Stamp annotations
CPDFStampAnnotation *stampAnnotation = [[CPDFStampAnnotation alloc] initWithDocument:document type:selectedIndex];
stampAnnotation.bounds = CGRectMake(100, 100, 100, 100);

// Sets the rotation property of the annotations
stampAnnotation.annotationRotation = 0;
// rotatePoints initialize the four vertices of the bounds
[stampAnnotation setSaveRectRotationPoints:rotatedVertices];
[stampAnnotation updateAnnotationRotationAppearanceStream];
[stampAnnotation setSaveRectRotationPoints:rotatedVertices];
[stampAnnotation setSaveSourceRect:stampAnnotation.bounds];

// Annotation rotation
- (void)rotateStampAnnotation:(CPDFStampAnnotation *)stampRotation rotateAngle:(CGFloat)currentAngle {
    CGPoint center = CGPointMake(CGRectGetMidX(stampRotation.saveSourceRect), CGRectGetMidY(stampRotation.saveSourceRect));
   // Get stampAnnotation.saveSourceRect() four vertices
    NSMutableArray<NSValue *> *saveSourceRectPoint = computeRotatedRectVerticesWithMidpoints(stampRotation.saveSourceRect, 0, YES);
  // rotatedVertices are sorted, with the lower left foot of saveSourceRect as the first point, sorted counterclockwise
    NSMutableArray<NSValue *> *saveRectPoints = sortPointsToFormRectangle(saveSourceRectPoint);
    
    [stampRotation setAnnotationRotation :currentAngle];
    NSMutableArray<NSValue *> *saveRectRotationPoints = [[NSMutableArray array] mutableCopy];
    for (NSUInteger i = 0; i < saveRectPoints.count ; i ++) {
        NSValue *savePointValue = [saveRectPoints objectAtIndex:i];
        CGPoint savePoint = [savePointValue pointValue];
        CGFloat angleRadians = currentAngle * M_PI / 180.0;
      // Gets the rotated vertex
        CGPoint saveRotationPoint = rotatePointAroundCenter(savePoint, center, angleRadians);
        [saveRectRotationPoints addObject:[NSValue valueWithPoint:saveRotationPoint]];
    }
    [stampRotation setSaveRectRotationPoints:saveRectRotationPoints];
    stampRotation.bounds = boundingRectForPoints(saveRectRotationPoints);
    [stampRotation updateAnnotationRotationAppearanceStream];
    [stampRotation setSaveRectRotationPoints:saveRectRotationPoints];
    
    [self setNeedsDisplayAnnotationViewForPage:stampRotation.page];
}

// Gets the rotated vertex
CGPoint rotatePointAroundCenter(CGPoint point, CGPoint center, CGFloat angleRadians) {
    CGFloat s = sin(angleRadians);
    CGFloat c = cos(angleRadians);
    
    // Translate point back to origin
    point.x -= center.x;
    point.y -= center.y;
    
    // Rotate point
    CGFloat xnew = point.x * c - point.y * s;
    CGFloat ynew = point.x * s + point.y * c;
    
    // Translate point back
    point.x = xnew + center.x;
    point.y = ynew + center.y;
    return point;
}