Copy link to clipboard
Copied
Hi,
I was building a plugin for acrobat pro that adds a button to the edit toolbar and the button calls a function to add annotation to the selected text. I am using the Basic Plugin as my base and making changes to the BasicPluginInit.cpp file. I tried to replicate what I saw in the documentation here-
https://opensource.adobe.com/dc-acrobat-sdk-docs/library/plugin/Plugins_Toolbutton.html
Unfortunately, even though my plugin builds and I place the .api file at the correct address I am not able to see any additional items on the edit toolbar.
Here is the code for my init.cpp file-
/*********************************************************************
ADOBE SYSTEMS INCORPORATED
Copyright (C) 1998-2006 Adobe Systems Incorporated
All rights reserved.
NOTICE: Adobe permits you to use, modify, and distribute this file
in accordance with the terms of the Adobe license agreement
accompanying it. If you have received this file from a source other
than Adobe, then your use, modification, or distribution of it
requires the prior written permission of Adobe.
-------------------------------------------------------------------*/
/**
\file BasicPluginInit.cpp
- Skeleton .cpp file for a plug-in. It implements the basic
handshaking methods required for all plug-ins.
- A convenient function is provided to add a menu item easily.
*********************************************************************/
// Acrobat Headers.
#ifndef MAC_PLATFORM
#include "PIHeaders.h"
#endif
/*-------------------------------------------------------
Constants/Declarations
-------------------------------------------------------*/
// stuff for Menu set up
static AVMenuItem menuItem = NULL;
ACCB1 ASBool ACCB2 PluginMenuItem(char* MyMenuItemTitle, char* MyMenuItemName);
// callback functions implemented in file "BasicPlugin.cpp"
extern ACCB1 void ACCB2 MyPluginCommand(void *clientData);
extern ACCB1 ASBool ACCB2 MyPluginIsEnabled(void *clientData);
extern ACCB1 ASBool ACCB2 MyPluginSetmenu();
extern ACCB1 void ACCB2 AcroAppModeSwitchNotification(void* clientData);
extern const char* MyPluginExtensionName;
/*-------------------------------------------------------
Annotation helper / toolbar additions
-------------------------------------------------------*/
// Global references for toolbar button and its callback so we can release during unload
static AVToolButton CreateAnnotButton = NULL;
static ASCallback gCreateAnnotExecCB = NULL; // store as ASCallback
// Forward declaration
static ACCB1 void ACCB2 CreateSampleAnnotExecute(void* clientData);
static void AddCreateAnnotationToolButton()
{
const char* toolbarName = "UndoRedo";
AVToolBar toolBar = AVAppGetToolBarByName(toolbarName);
if (!toolBar)
return;
if (CreateAnnotButton)
return;
AVIcon icon = NULL;
CreateAnnotButton = AVToolButtonNew(ASAtomFromString("MyExtn:CreateAnnotButton"), icon, false, false);
if (!CreateAnnotButton)
return;
gCreateAnnotExecCB = ASCallbackCreateProto(AVExecuteProc, &CreateSampleAnnotExecute);
AVToolButtonSetExecuteProc(CreateAnnotButton, (AVExecuteProc)gCreateAnnotExecCB, NULL);
AVToolBarAddButton(toolBar, CreateAnnotButton, false, NULL);
}
static void CreateSimpleTextAnnotation()
{
AVDoc avDoc = AVAppGetActiveDoc();
if (!avDoc) { AVAlertNote("No active document."); return; }
PDDoc pdDoc = AVDocGetPDDoc(avDoc);
PDPage page = PDDocAcquirePage(pdDoc, 0);
DURING
{
ASFixedRect fr;
fr.left = ASInt32ToFixed(36);
fr.right = ASInt32ToFixed(136);
fr.top = ASInt32ToFixed(792 - 36);
fr.bottom = ASInt32ToFixed(792 - 136);
PDAnnot annot = PDPageCreateAnnot(page, ASAtomFromString("Text"), &fr);
#ifdef CastToPDTextAnnot
PDTextAnnot textAnnot = CastToPDTextAnnot(annot);
#else
PDTextAnnot textAnnot = (PDTextAnnot)annot;
#endif
const char* contents = "Created by toolbar button.";
PDTextAnnotSetOpen(textAnnot, true);
PDTextAnnotSetContents(textAnnot, contents, (ASInt32)strlen(contents));
PDPageAddAnnot(page, -2, textAnnot);
AVPageView pv = AVDocGetPageView(avDoc);
if (pv) {
AVRect devRect; ASFixedRect fixedRect = fr; AVPageViewRectToDevice(pv, &fixedRect, &devRect); AVPageViewInvalidateRect(pv, &devRect);
}
}
HANDLER { AVAlertNote("Failed to create annotation (exception)."); }
END_HANDLER
PDPageRelease(page);
}
static ACCB1 void ACCB2 CreateSampleAnnotExecute(void* clientData)
{
CreateSimpleTextAnnotation();
}
/*-------------------------------------------------------
Core Handshake Callbacks
-------------------------------------------------------*/
/**
Callback invoked by the application to give the plug-in an opportunity to register
an HFTServer with the application.
@return true to indicate the plug-in should continue loading.
*/
ACCB1 ASBool ACCB2 PluginExportHFTs(void)
{
return true;
}
/**
The application calls this function to allow it to
<ul>
<li> Import plug-in supplied HFTs.
<li> Replace functions in the HFTs you're using (where allowed).
<li> Register to receive notification events.
</ul>
*/
ACCB1 ASBool ACCB2 PluginImportReplaceAndRegister(void)
{
return true;
}
/**
The main initialization routine.
We register our action handler with the application.
@return true to continue loading the plug-in
@return false to cause plug-in loading to stop.
*/
/* PluginInit
** ------------------------------------------------------
**
** The main initialization routine.
**
** Return true to continue loading plug-in.
** Return false to cause plug-in loading to stop.
*/
ACCB1 ASBool ACCB2 PluginInit(void)
{
//Register for mode switch notification.
AVAppRegisterNotification(AcroAppModeSwitchNSEL, gExtensionID,
(void*)ASCallbackCreateNotification(AcroAppModeSwitch, AcroAppModeSwitchNotification), NULL);
ASBool ok = MyPluginSetmenu();
// Add our toolbar button after menus
AddCreateAnnotationToolButton();
return ok;
}
/**
The unload routine.
Called when your plug-in is being unloaded when the application quits.
Use this routine to release any system resources you may have
allocated.
Returning false will cause an alert to display that unloading failed.
@return true to indicate the plug-in unloaded.
*/
ACCB1 ASBool ACCB2 PluginUnload(void)
{
if (menuItem)
AVMenuItemRemove(menuItem);
if (gCreateAnnotExecCB)
ASCallbackDestroy(gCreateAnnotExecCB);
gCreateAnnotExecCB = NULL;
CreateAnnotButton = NULL;
return true;
}
/**
Return the unique ASAtom associated with your plug-in.
@return the plug-ins name as an ASAtom.
*/
ASAtom GetExtensionName()
{
return ASAtomFromString(MyPluginExtensionName);
}
/**
Function that provides the initial interface between your plug-in and the application.
This function provides the callback functions to the application that allow it to
register the plug-in with the application environment.
Required Plug-in handshaking routine: <b>Do not change it's name!</b>
@param handshakeVersion the version this plug-in works with. There are two versions possible, the plug-in version
and the application version. The application calls the main entry point for this plug-in with its version.
The main entry point will call this function with the version that is earliest.
@param handshakeData OUT the data structure used to provide the primary entry points for the plug-in. These
entry points are used in registering the plug-in with the application and allowing the plug-in to register for
other plug-in services and offer its own.
@return true to indicate success, false otherwise (the plug-in will not load).
*/
ACCB1 ASBool ACCB2 PIHandshake(Uns32 handshakeVersion, void *handshakeData)
{
//Enable the below code in #if 0 to make this plugin Acrobat only.
#if 0
char* product = (char*)ASGetConfiguration(ASAtomFromString("Product"));
if(strcmp(product,"Reader") == 0)
return false;
#endif
if (handshakeVersion == HANDSHAKE_V0200) {
/* Cast handshakeData to the appropriate type */
PIHandshakeData_V0200 *hsData = (PIHandshakeData_V0200 *)handshakeData;
/* Set the name we want to go by */
hsData->extensionName = GetExtensionName();
/* If you export your own HFT, do so in here */
hsData->exportHFTsCallback = (void*)ASCallbackCreateProto(PIExportHFTsProcType, &PluginExportHFTs);
/*
** If you import plug-in HFTs, replace functionality, and/or want to register for notifications before
** the user has a chance to do anything, do so in here.
*/
hsData->importReplaceAndRegisterCallback = (void*)ASCallbackCreateProto(PIImportReplaceAndRegisterProcType,
&PluginImportReplaceAndRegister);
/* Perform your plug-in's initialization in here */
hsData->initCallback = (void*)ASCallbackCreateProto(PIInitProcType, &PluginInit);
/* Perform any memory freeing or state saving on "quit" in here */
hsData->unloadCallback = (void*)ASCallbackCreateProto(PIUnloadProcType, &PluginUnload);
/* All done */
return true;
} /* Each time the handshake version changes, add a new "else if" branch */
/*
** If we reach here, then we were passed a handshake version number we don't know about.
** This shouldn't ever happen since our main() routine chose the version number.
*/
return false;
}
/*-------------------------------------------------------
Menu Utility
-------------------------------------------------------*/
/**
A convenient function to add a menu item under Acrobat SDK menu.
@param MyMenuItemTitle IN String for the menu item's title.
@param MyMenuItemName IN String for the menu item's internal name.
@return true if successful, false if failed.
@see AVAppGetMenubar
@see AVMenuItemNew
@see AVMenuItemSetExecuteProc
@see AVMenuItemSetComputeEnabledProc
@see AVMenubarAcquireMenuItemByName
@see AVMenubarAcquireMenuByName
*/
ACCB1 ASBool ACCB2 PluginMenuItem(char* MyMenuItemTitle, char* MyMenuItemName)
{
AVMenubar menubar = AVAppGetMenubar();
AVMenu volatile commonMenu = NULL;
if (!menubar)
return false;
DURING
// Create our menuitem
menuItem = AVMenuItemNew (MyMenuItemTitle, MyMenuItemName, NULL, true, NO_SHORTCUT, 0, NULL, gExtensionID);
AVMenuItemSetExecuteProc (menuItem, ASCallbackCreateProto(AVExecuteProc, MyPluginCommand), NULL);
AVMenuItemSetComputeEnabledProc (menuItem,
ASCallbackCreateProto(AVComputeEnabledProc, MyPluginIsEnabled), (void *)pdPermEdit);
commonMenu = AVMenubarAcquireMenuByName (menubar, "ADBE:Acrobat_SDK");
// if "Acrobat SDK" menu is not existing, create one.
if (!commonMenu) {
commonMenu = AVMenuNew ("Acrobat SDK", "ADBE:Acrobat_SDK", gExtensionID);
AVMenubarAddMenu(menubar, commonMenu, APPEND_MENU);
}
AVMenuAddMenuItem(commonMenu, menuItem, APPEND_MENUITEM);
AVMenuRelease(commonMenu);
HANDLER
if (commonMenu)
AVMenuRelease (commonMenu);
return false;
END_HANDLER
return true;
}
I am expecting an item to show up here-
And I am able to see the plugin avalialble in the context menu-
Please let me know what I'm missing, Thanks!
Copy link to clipboard
Copied
So first, where did you get the name of the toolbar? I do not believe that "UndoRedo" is the name of the "Edit PDF" toolbar. Did you step through the code to see if the call to "AVAppGetToolBarByName" returns null?
Next, the change to the new interface changed how toolbars are handled. I'm pretty sure that random plug-ins are now restricted from adding menus or toolbuttons to built-in Acrobat items.
After you've gotten the correct toolbar name, change Acrobat back to the old UI and see if the button gets added.
Use the "AVAppGetLegacyToolBar" function to get a list of all toolbars.
Here's the reference:
https://opensource.adobe.com/dc-acrobat-sdk-docs/acrobatsdk/apireference/index.html
Copy link to clipboard
Copied
Hi @Thom Parker ,
I got the toolbar name from here-
Also how do I do the following-
1) change acrobat to older version
2) I am unable to debug as well, the project builds fine but when I run with debugger I get an error.
So what all functionalities from plugins are not aplicable to the new acrobat?
Copy link to clipboard
Copied
Change to the old UI from the Hamburger menu. Here:
The names in that table may be more applicable to the old Acrobat, than the new. The equivalent names in the new UI are probably different. Also, the toolbar you are interested in is the "Advanced Editing" .
Debug is pretty important, so you need to get that figured out. You'll also need to verify the UI items before continuing with anything else. So, until you get debug figured out. Write a test plug-in to display the info you need to figure out. Use the AVAlertNote() function.
Copy link to clipboard
Copied
Thanks this helped a lot! I ended up creating a new toolbar and adding buttons to it. I did want to understand if I can open a panel when the button in my toolbar is clicked(something like the comments panel). I would later like to embed a web browser through CEF to show up in this panel. Would appreciate any guidance that can be provided. Thanks!
Copy link to clipboard
Copied
I've found that adding menu items and toolbar buttons to Acrobat's existing items can be problematic. Having your own toolbar is best.
The connection between a plug-in and the Acrobat UI panels is tenous. A couple of the things you can do is to execute menu items that open such items, and use JavaScript to set the viewState object.
Find more inspiration, events, and resources on the new Adobe Community
Explore Now