Creating a new tool

In this document

Because the Tools framework is open source, it is easy to modify and extend it to meet your app's unique needs. This article will show how to add an entirely new tool.

Creating a redaction tool

PDFTron includes the ability to redact information from a PDF using the PTRedactor class. It has a single method, which takes a document, a page number, and series of rectangles to redact.

The common flow in redacting a document is that the user creates one or more redaction region annotations (PTRedactionAnnot, rectangles indicating the region(s) to be redacted), and then instructs the system to redact them.

In the name of simplicity, this tool will allow the user to create a single rectangle, which is then immediately redacted.

Defining the interface

The class Tool is an abstract base class from which all tools derive. It implements the required functionality to fit into the tool system (as defined by the PTToolSwitching protocol), as well as some common helper methods, but does do anything useful for the user on its own.

For the redact tool, we could choose to extend from Tool, but a better choice is a more closely related tool, the abstract CreateToolBase tool. It already implements some useful behaviour, and is the base class for a number of similar concret tools such as RectangleCreate, LineCreate.

Like the RectangleCreate tool, our simple redact tool will not need to expose any public API, so we can define its interface simply as follows:

@interface RedactTool : CreateToolBase {
    
}

Implementing tool specific methods

Tools contain a number of identifying methods, which we can copy from the RectangleCreate tool, replacing what the methods return to make it specific to our redact tool.

- (instancetype)initWithPDFViewCtrl:(PTPDFViewCtrl*)in_pdfViewCtrl
{
    self = [super initWithPDFViewCtrl:in_pdfViewCtrl];
    if (self) {
        
    }
    return self;
}

+(BOOL)createsAnnotation
{
    return YES;
}

-(Class)annotClass
{
    return [PTRedactionAnnot class];
}

-(PTAnnotType)annotType
{
    return e_ptRedact;
}

You will notice that it returns YES in response to the method createsAnnotation. This is because it is, as you will see, creating an annotation (that is immediately deleted), and because this information is used further up the class chain to determine if scrolling should take place when two fingers are used rather than zooming (which is true for tools which create annotations and false for tools that do not).

Behaviour while touches are moving

The create tool base class keeps track of where the user touched down, where they are dragging their finger, and when they release. The only thing we need to do while the user drags their finger is define what the rectangle should look like, which is done it the tool's drawRect: method. What follows is a modified version of RectangleCreate's drawRect: method.

- (void)drawRect:(CGRect)rect
{
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    if( self.pageNumber >= 1 && !self.allowScrolling)
    {
        
        [super setupContext:currentContext];
        
        CGContextSetLineWidth(currentContext, 0);
        CGContextSetStrokeColorWithColor(currentContext, [UIColor clearColor].CGColor);
        CGContextSetFillColorWithColor(currentContext, [UIColor blackColor].CGColor);
        CGContextSetAlpha(currentContext, 0.5);
        
        CGContextFillRect(currentContext, self.drawArea);
        CGContextStrokeRect(currentContext, self.drawArea);
        
        CGContextStrokePath(currentContext);
    }
}

Redacting when touches end

Lastly, we need to implement what happens when the user stops dragging. Here, immediately redact the area, and delete the annotation.

- (BOOL)pdfViewCtrl:(PTPDFViewCtrl*)pdfViewCtrl onTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    BOOL ret = [super onTouchesEnded:touches withEvent:event];
    
    @try
    {
        // we will be modifying the document, so obtain a write lock.
        // yes cancels any rendering that may be occuring
        [self.pdfViewCtrl DocLock:YES];
        
        // get the rectangle of the PTRedactAnnot we just made
        PTPDFRect* annotRect = [self.currentAnnotation GetRect];
        
        // set this rectangle as the area to be deleted
        PTVectorRedaction *vec = [[PTVectorRedaction alloc] init];
        [vec add: [[PTRedaction alloc] initWithPage_num: self.annotationPageNumber bbox: annotRect negative: NO text:@""]];
        
        // define the appearance of the redacted area
        PTAppearance *app = [[PTAppearance alloc] init];
        [app setRedactionOverlay: YES];
        [app setBorder: NO];
        
        // redact the area
        [PTRedactor Redact: [self.pdfViewCtrl GetDoc] red_arr: vec app: app ext_neg_mode: NO page_coord_sys: YES];

        // delete the PTRedactAnnot we just made
        [self deleteSelectedAnnotation];
    
    }
    @catch (NSException *exception) {
        NSLog(@"Exception: %@: %@",exception.name, exception.reason);
    }
    @finally {
        [self.pdfViewCtrl DocUnlock];
    }
    
    return ret;
}

How to use the tool

To activate the RedactTool, you can set it as the ToolManager's active tool. This could, for example, be executed when the user presses the "Redact" button in a toolbar.



To add a "Redact" option to the long press menu that occurs when the user long-presses on the document, open the PanTool source file, find the method attachInitialMenuItems:, and add a new option for the Redact tool.

Adding functionality to AnnotEditTool

Instead of redacting immediately, a "Redact" option could be added to the redaction annotation when selected. To do this, remove the entire onTouchesEnded:withEvent: method from the RedactTool, and add everything that was in the try/catch/finally portion to a new method, redact, in AnnotEditTool. Then in the AnnotEditTool's method attachInitialMenuItemsForAnnotType:, add the following to the e_ptRedact case in the switch statement:

case e_ptRedact:
    menuItem = [[UIMenuItem alloc] initWithTitle:@"Redact" action:@selector(redact)];
    [menuItems addObject:menuItem];
    break;

Redacting a portion of a page with the annot edit tool