Some test text!
iOS / Guides / Custom tool
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.
Apryse 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.
The class PTTool
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 PTTool
, but a better choice is a more closely related tool, the abstract PTCreateToolBase
tool. It already implements some useful behaviour, and is the base class for a number of similar concrete tools such as PTRectangleCreate
, PTLineCreate
.
Like the PTRectangleCreate
tool, our simple redact tool will not need to expose any public API, so we can define its interface simply as follows:
@interface PTRedactTool : PTCreateToolBase {
}
Tools contain a number of identifying methods, which we can copy from the PTRectangleCreate
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).
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 PTRectangleCreate
'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);
}
}
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;
}
To activate the PTRedactTool
, you can set it as the PTToolManager
's active tool. This could, for example, be executed when the user presses the "Redact" button in a toolbar.
self.toolManager.tool = [[PTRedactTool alloc] initWithPDFViewCtrl:self.pdfViewCtrl];
To add a "Redact" option to the long press menu that occurs when the user long-presses on the document, open the PTPanTool
source file, find the method attachInitialMenuItems:
, and add a new option for the Redact tool.
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 PTRedactTool
, and add everything that was in the try
/catch
/finally
portion to a new method, redact
, in PTAnnotEditTool
. Then in the PTAnnotEditTool
'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;
Trial setup questions? Ask experts on Discord
Need other help? Contact Support
Pricing or product questions? Contact Sales