Skip to main content
Participating Frequently
October 11, 2023
Answered

Link associated with an image

  • October 11, 2023
  • 2 replies
  • 1445 views

I want to get, into my (C++)plugin the link associated with a selected image in an indd document

 

I tried following to achieve:

 

  • I tried to attach an observer with the associated kImageItem so that when the image is clicked the observer is called and I can get the image link from the subject associated. However the observer::update is never called when I select or click on the image. The code for attaching the observer:
 
InterfacePtr<ISubject> isb(pr, UseDefaultIID());
if (isb == nil) {
ASSERT("Null ISubject from kImageItem");
break;
}
isb->AttachObserver(this, IBooleanControlData::kDefaultIID);

 

  • I tried to extend (AddIn) by adding  IID_IEVENTHANDLER interface and wrote its implementer, however when I click on the image, the event handler is never called. The event handler code:

 

extension of kImageItem

------------------------------

AddIn
{
kImageItem,
kInvalidClass,
{

IID_IEVENTHANDLER, kImageItemEventHandlerImpl,
}
},

 

Event Handletr Implementation

-----------------------------------------

class WFPImageItemEV : public CEventHandler
{
public:
 
/** Constructor.
@9397041 boss interface ptr on the boss object to which the interface implemented here belongs.
*/
WFPImageItemEV(IPMUnknown* boss);
 
/** Destructor
*/
virtual ~WFPImageItemEV(){}
 
virtual bool16 LButtonDn(IEvent* e); // { bool16 retval; InterfacePtr<IEventHandler> delegate(this, IID_IPNLTRVSHADOWEVENTHANDLER);  retval = delegate->LButtonDn(e);  return retval; }
 
};
 
/* CREATE_PMINTERFACE
 Binds the C++ implementation class onto its ImplementationID 
 making the C++ code callable by the application.
*/
 
CREATE_PMINTERFACE(WFPImageItemEV, kImageItemEventHandlerImpl)
 
 
WFPImageItemEV::WFPImageItemEV(IPMUnknown* boss) :
CEventHandler(boss)
{
 
}
 
bool16 WFPImageItemEV::LButtonDn(IEvent* e) {
bool16 retval = kTrue;
PMString s1 = "LButtonDn";
do {
InterfacePtr<IPanelControlData> panelData(this, IID_IPANELCONTROLDATA);
if (panelData == nil)
{
ASSERT_FAIL("IPanelControlData is invalid");
retval = kFalse;
break;
}
uint32 ln = panelData->Length();
IControlView* panelControlView = panelData->GetWidget(0);
 
} while (kFalse);
return retval;
}

 

  • I tried using IImageObjectSuite selection suite to get the graphics item associated with it but it doesn't give me the function GetGraphicItem (UIDRef &itemUIDRef) returns Failure (ErrorCode = 1), The code is as below:

ISelectionManager* sm = Utils<ISelectionUtils>()->GetActiveSelection();
InterfacePtr<IImageObjectSuite> ios(sm, UseDefaultIID());
UIDRef gi;
ErrorCode ec = ios->GetGraphicItem(gi);

 

My goal is to get the link associated with the image when I click on it or select it.

 

Kindly help.

 

This topic has been closed for replies.
Correct answer Rahul_Rastogi

Assuming you have got the UIDRef of kSplineItemBoss -

 

InterfacePtr<ILinkManager> iLinkManager(docRef, UseDefaultIID());

 

InterfacePtr<IHierarchy> iFrameHierarchy(frameRef, UseDefaultIID());

 

// below line get the iHierarchy of kImageItem or kEPSItem depending upon content is image or eps or pdf.

InterfacePtr<IHierarchy> iChildHierarchy(frameHier->QueryChild (0) );

 

InterfacePtr<ILinklObject> iLinkObject(iChildHierarchy, UseDefaultIID());

 

UIDList linkList(docRef.GetDataBase());
iLinkManager->QueryLinksByObjectUID(::GetUID(iChildHierarchy), linkList);
InterfacePtr<ILink> iLink (linkList.GetRef(0),UseDefaultIID());

 

InterfacePtr<ILinkResource> iLinkResource(iLinkManager->QueryResourceByUID(iLink->GetResource()));

 

PMString strFilePath  = iLinkResource->GetLongName(kFalse);

 

Now strFilePath contains the full path of the linked image. You can retrieve the image name from it.

 

- Rahul Rastogi

Adobe InDesign C++ Plugin Architect

2 replies

Inspiring
October 12, 2023

As Dirk Becker mentioned - your approach is wrong.

 

1. Implement ActiveSelectionObserver.

Search the Frame Label Sample code and you would see that if frame is selected then panel is updated showing the frame label. 

 

This will solve the problem of selection.

 

2. You might need to implement SelectionSuite, to get the UIDRef of the underlying selection.

Once you have the UIDRef of the frame, then detect whether it is text frame or graphic frame. If graphic frame then proceed to find the link.

 

- Rahul Rastogi

 Adobe InDesign SDK C++ Plugin Development Architect

Participating Frequently
October 12, 2023

Thanx Rahul for your answer.

 

It seems I have not made question clear. By "link associated with the image" I meant the name of  the image file from which the image was inserted. see the image below for reference:

 

 

 

I am able to get the UIDRef of the selected image which is UIDRef of the undelying kSplineItemBoss. From the kSplineItemBoss I get the kImageItem. From kItemBoss I get IImageAttributes assuming atleast one of the tags : kPMTagDocumentName = 104, // TYPE: array null terminated string

 OR

kPMTagOPIImageID = 121, // TYPE: array Full path name of original high-res image

 

will have the name of the file. But none of this tags are present when I get IImageAttributes.

 

How do I get the file name of the image, given that I have the UIDref of the selected image(underlying kSplineItemBoss)?

Inspiring
October 13, 2023

Scripting can become a problem with growing code size. When the debugger just gives up. The profiler stops working. Or, simpler friction. In a team effort of quite seasoned scripters (not neccessarily InDesign) across multiple years, you reach points where someone adds another parameter to that function and forgets to modify the other reference in the module that they never touched. After the first few 100k lines adding more is far slower than with your initial prototype. You run into naming collisions. Not dreamed up a sufficiently strong architecture for that large subsystem. Or the next developer to join the team realizes that Extendscript is very far from his understanding of Javascript. Uses paradigms that cause severe memory loss. For comparison, if you follow the patterns of InDesign plug-ins scale far better.

 

Or, you start off from an inherited code base that you have to add to InDesign, and that happens to not be implemented in a scripting language. The same way that Adobe added a reduced copy of Photoshop, plus a Chromium based browser, plus a ton of other functionality. Your company might have a background in other aspects of image processing. Or a backing DAM system. Accounting. Database connectivity. Code proven throughout a few decades, plus the programmers that can handle it in their language of choice.

 

You start to fight the problems with tools on top. Typescript, even though the underlying binding is not optimized for it, and d.ts files are volunteer effort or at best forthcoming, with lots of uncertainties. Or even worse you use external languages with strong typing, while InDesign Scripting never was made for that. VBScript wasn't, AppleScript was odd anyway. So you are at languages that come with other troubles including limited platform support (hmm - what is the most powerful tool worth that you ever created when the prospective customer is on the other platform?) Plug-In code is mostly cross-platform.

 

When you live in both worlds, it is not just added functionality. It is about things that you don't see from the scripting side. You start off with a pure script. Find edge cases that are not correctly handled and only work with a bit of plugin support. That character style assignment under certain conditions. Fonts that behave erratic because a variant is missing, and another one uses the wrong font technology. Where the scripting glue has not kept up with the true thing. That just work wrong, but you find ways to work around. Or something is so incomplete, that the scripting abstraction omits entire subsystems. You start with a little C++ support. In this case, and oh here is the next one. After a while, more and more functionality ends up at the plugin side, your entire control flow. Taking advantage of threads, background processing. Get the SDK programming guide and read about Wax. Notifications all over the place.

 

Or maybe things are slow, and you can stand a 10x speedup.

 

Then we have not even talked about UI integration. ScriptUI falling apart, UXP Spectrum filling a 40" screen with a dialog of very designy spaced UI elements that would otherwise fit on 16". Dropdowns that pop up 5000 screen pixels off. Mouse tracking that only works when your widget is placed at the origin and when UI scaling uses factor 1. With scripting you can only despair, bounce your ScriptUI dialog back to the center of the screen when it wanders off in record speed while the user resizes it.

 

That's about generic UI. Then there are those cases never available to scripting. You want a new tool, cursor, preview mode? Not run your own preferences dialog when you can add to the real thing? A new mouse gesture? A new shortcut context enabling another level of keyboard shortcuts? Add an indicator to the control strip on top of the application frame? Implement a preflight rule that checks your placed images against accounting criteria?  Add a column to the links panel?

 

Of course you can work around. Let a Windows window float on top of the application, have an own concept of preflight feed your duct tape status UI, or whatever. But that will never reach the degree of integration offered by InDesign.


@Robert at ID-Tasker  - 

 

@Dirk Becker  is correct.

 

There is no match for C++ Plugin with CEP or UXP when your priority is Speed, Scalability and maintainability of code base.

 

Till now, I have not seen CEP/UXP plugins doing complex task and with large code base. They are only meant for smaller functionalities. 

 

I have worked with complex projects where I automated generation of INDD document with 500 or more pages,  placing the content and images with some complex business logic. Going with UXP, this would be much slower generation. 

 

So if we only need to automate smaller functionalities then UXP is the way to go otherwise for enterprise applications it is C++ Plugin only.

Legend
October 11, 2023

There are plenty things wrong here. Welcome to a different world.

 

As a rough concept, an observer is notified for changes within or close to a particular object. So you'd watch for some very special aspect of a single page item, e.g. its attribute or its position (geometric bounds), or when it gets associated with a different link.

 

With your approach you'd have to attach such an observer to every single of the 10.000 images within the publication / document. Won't work. Instead, typical page item notifications are also notified at the document object itself. But only, when the item itself changes.

 

If you select an image, most of the time you actually select the enclosing page item which is a spline item. You'd use the direct selection tool (the other arrow) to select the image, likely you do not want that limitation.

 

Then, this is about the selection. A very different thing than the actual image. Your observer should observe the selection rather than the page item. Search the SDK for SelectionObserver, it is a bit complicated to even follow the selection across multiple windows of the same document, or other documents, or no document at all.

 

Your other approach to add an event handler is a no-go, as neither the image item boss nor the interface are yours. There can be only one implementation for an interface, that becomes part of the overall object. This is unlike scripting where any object can accept a ton of separate event listeners. Anyway, event handlers are far too lo level, forget about them. You would not want to decode the mouse click on an image to back-guess that it might have something to do with selection. That mouse click would get decoded in a far different object.

 

Let's return to the selection though. InDesign's selection is an abstraction. You might have selected no item, one, or a multitude of them. Different ones - text frames, form fields at the same time. Within the structure selection (aka XML) you might have a selection across many pages. Text frames, via their story, can also have a link.

To correctly participate in the selection, you have to think about those cases. You ask the selection to get its (in your case: image link), in an abstract way, and it deals about it in all the different concrete cases.

E.g. you have a text selection with three anchored image anchored somewhere within character 200 and 399, would you consider their links?

 

Also there are more kinds of images - as you mention kImageItem but omit PDF, EPS, WMF and so forth. What do you want to see, and later on what do you want to modify, when there are multiple images selected. Or if only one page item is selected that happens to be a group of 10 spline items which all enclose a different kind of image.

 

This concept of an abstract selection with different concrete implementations is expressed by a suite, as you already have found one. It is not meant though to just pass you the selection and let you work on that from outside. If you want to work on details, you have to implement your own suite. Define how they are aggregated across the multi-selection (even if that to you just means to give up and show blank) of each concrete selection's target. There you'd also handle the cases of these nested images (group, anchored etc.).

 

Finally, as you say "my goal … when I click or select it" - the click is a different beast. E.g. you can have an image locked (see layer panel) so not selectable, but use the eyedropper tool to pick up some attributes from it. That would be a click. A link-peeker could even display the link below it on mouse-over. Completely different subsystem, and somewhere closer to an event handler. Do you really want that?

Participating Frequently
October 12, 2023

Thanx Dirk for a detailed answer. Will get back to you if I have any questions.