Blog Post

How to Add Attribution to PDFs with PSPDFKit for iOS

Illustration: How to Add Attribution to PDFs with PSPDFKit for iOS

In some scenarios, it’s useful to provide users with an easy way to share the contents of a PDF document, such as text copied from the document or a subset of the document pages. There’s also a possibility that, depending on the specific application use case, we’d like the content to be marked with where it came from (e.g. copyright notices, attribution).

There are various ways in which PSPDFKit for iOS can help you add such information, and in this blog post, we’ll look at some of these available solutions.

Text Extraction

PSPDFKit for iOS can parse the text contents of a document and let the user highlight, redact, copy, or share it. The latter two of these actions are what we’ll use to add attribution to the contents of a PDF.

The selected text, available to us as a simple NSString instance, can be updated to add our copyright notice, and we can do so by implementing one particular PSPDFDocumentViewControllerDelegate method:

func pdfViewController(_ pdfController: PSPDFViewController, shouldShow menuItems: [PSPDFMenuItem], atSuggestedTargetRect rect: CGRect, forSelectedText selectedText: String, in textRect: CGRect, on pageView: PSPDFPageView) -> [PSPDFMenuItem] {
    // Modify the selected text to add our copyright information.
    let copyrightedText = selectedText + "\n\(copyrightNotice)"

    // Then check which actions we'd like to update this for.
    for menuItem in menuItems {
        switch menuItem.identifier! {
        // Update the Copy menu item.
        case PSPDFTextMenu.copy.rawValue:
            menuItem.actionBlock = {
                UIPasteboard.general.string = copyrightedText
            }

        // Update the Share menu item.
        case PSPDFTextMenu.share.rawValue:
            menuItem.actionBlock = {
                let activityViewController = UIActivityViewController(activityItems: [copyrightedText], applicationActivities: nil)
                pdfController.present(activityViewController, options: [PSPDFPresentationRectKey: rect], animated: true, sender: nil, completion: nil)
            }

        default: break
        }
    }

    return menuItems
}
- (NSArray<PSPDFMenuItem *> *)pdfViewController:(PSPDFViewController *)pdfController shouldShowMenuItems:(NSArray<PSPDFMenuItem *> *)menuItems atSuggestedTargetRect:(CGRect)rect forSelectedText:(NSString *)selectedText inRect:(CGRect)textRect onPageView:(PSPDFPageView *)pageView {

    NSString *copyrightedText = [NSString stringWithFormat:@"%@", @""];

    [menuItems enumerateObjectsUsingBlock:^(PSPDFMenuItem *menuItem, NSUInteger index, BOOL *stop) {
        if ([menuItem.identifier isEqualToString:PSPDFTextMenuCopy]) {
            menuItem.actionBlock = ^{
                UIPasteboard.generalPasteboard.string = copyrightedText;
            };

            return;
        }

        if ([menuItem.identifier isEqualToString:PSPDFTextMenuShare]) {
            menuItem.actionBlock = ^{
                UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[copyrightedText] applicationActivities:nil];
                [pdfController presentViewController:activityViewController options:nil animated:YES sender:nil completion:nil];
            };

            return;
        }
    }];

    return menuItems;
}

Document Sharing

We can also hook into the sharing flow to insert our attribution in various ways — such as watermarking or appending content — depending on what we want to achieve. We’ll outline this below.

Watermarking

A common practice is to add watermarks to the shared pages of a document whose content we want to protect. To achieve this, we can add custom drawing code to the document, and the code will only be applied when the document goes through the sharing flow:

let drawBlock: PSPDFRenderDrawBlock = { context, page, cropBox, options in
	let text: NSString = "ACME Corp. 2019"
	let drawingContext = NSStringDrawingContext()
	drawingContext.minimumScaleFactor = 0.1

	context.translateBy(x: 0, y: cropBox.size.height / 2)
	context.rotate(by: -CGFloat.pi / 4)

	let attributes = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 100), .foregroundColor: UIColor.red]

	text.draw(with: cropBox, options: .usesLineFragmentOrigin, attributes: attributes, context: drawingContext)
}

document?.updateRenderOptions(for: .processor) { options in
	options.drawBlock = drawBlock
}
const PSPDFRenderDrawBlock drawBlock = ^(CGContextRef context, NSUInteger page, CGRect cropBox, PSPDFRenderOptions *options) {
	// Careful, this code is executed on background threads. Only use thread-safe drawing methods.
	NSString *text = @"ACME Corp. 2019";
	NSStringDrawingContext *stringDrawingContext = [NSStringDrawingContext new];
	stringDrawingContext.minimumScaleFactor = 0.1;

	CGContextTranslateCTM(context, 0.0, cropBox.size.height / 2.);
	CGContextRotateCTM(context, -(CGFloat)M_PI / 4.);
	[text drawWithRect:cropBox options:NSStringDrawingUsesLineFragmentOrigin attributes:@{ NSFontAttributeName: [UIFont boldSystemFontOfSize:100], NSForegroundColorAttributeName: [UIColor.redColor colorWithAlphaComponent:0.5] } context:stringDrawingContext];
};

[document updateRenderOptionsForType:PSPDFRenderTypeProcessor withBlock:^(PSPDFRenderOptions * options) {
	options.drawBlock = drawBlock;
}];

By setting a custom drawBlock to the render options for the PSPDFRenderTypeProcessor render type, we tell PSPDFKit to only draw on the pages of the document when sharing the document, but not when rendering it on the default PSPDFViewController instance.

Appending Content

Another strategy we can adopt to add attribution when watermarking is not an option is to include additional information alongside the shared files themselves.

Let’s get to the code first:

class CopyrightedSharingViewController: PSPDFDocumentSharingViewController {
    override func activityViewController(forSharingItems activityItems: [Any], sender: Any) -> UIActivityViewController? {
        // Copy the items that were generated when processing the document for sharing.
        var items = activityItems

        // Create our formatted message to share with the documents.
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.alignment = .center

        let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13), .foregroundColor: UIColor.red, .paragraphStyle: paragraphStyle]

        let attributedCopyrightNotice = NSAttributedString(string: "Copyright ACME Corp. 2019", attributes: attributes)
        items.append(attributedCopyrightNotice)

        // Call the superclass with the updated list of items and proceed normally.
        return super.activityViewController(forSharingItems: items, sender: sender)
    }
}
@interface CopyrightedSharingViewController : PSPDFDocumentSharingViewController
@end

@implementation CopyrightedSharingViewController

- (UIActivityViewController *)activityViewControllerForSharingItems:(NSArray *)activityItems sender:(id)sender {
    NSMutableArray *items = [NSMutableArray arrayWithArray:activityItems];

    NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
    paragraphStyle.alignment = NSTextAlignmentCenter;

    NSDictionary<NSAttributedStringKey, id> *attributes = @{NSFontAttributeName: [UIFont systemFontOfSize:13], NSForegroundColorAttributeName: UIColor.redColor, NSParagraphStyleAttributeName: paragraphStyle};

    NSAttributedString *attributedCopyrightNotice = [[NSAttributedString alloc] initWithString:@"Copyright ACME Corp. 2019" attributes:attributes];
    [items addObject:attributedCopyrightNotice];

    return [super activityViewControllerForSharingItems:items sender:sender];
}

@end

In the example above, we override the -PSPDFDocumentSharingViewController activityViewControllerForSharingItems:sender: method on our subclass to insert our attribution as a UIActivityItem that’s going to be shared with the document itself.

What’s important to note here is that not every application that we share to will respond the same way. For instance, below you can find screenshots of how sharing the items from the above code will look in Mail.app and Messages.app.

Mail.app Messages.app
Screenshot of attachment in mail app Screenshot of attachment in messages app

As you can see, Mail.app happily accepts the formatted text and includes it in the email body, but Messages.app completely ignores it.

Conclusion

As we saw in this blog post, there are multiple ways in which you can add attribution to the documents your users share with PSPDFKit for iOS. Depending on your use case, some may seem more appropriate than others.

However, this just scratches the surface of the customizability options that PSPDFKit for iOS offers. You can take a deep dive into our documentation to learn more about customizing document rendering and our PSPDFRenderOptions class, which also offers many options for correctly marking documents shared with PSPDFKit.

Related Products
Share Post
Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
DEVELOPMENT  |  iOS • Swift • Tips

Privacy Manifests and Required Reason APIs on iOS

PRODUCTS  |  iOS • Releases

PSPDFKit 13.4 for iOS Introduces Revamped API Documentation and Improves Multiple Annotation Selection

DEVELOPMENT  |  visionOS • iOS

Apple’s Vision of Our Digital Future