Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
1

Link associated with an image

Explorer ,
Oct 11, 2023 Oct 11, 2023

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.
@Param 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.

 

TOPICS
SDK
1.7K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Contributor , Oct 12, 2023 Oct 12, 2023

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->QueryLink

...
Translate
Mentor ,
Oct 11, 2023 Oct 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?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Oct 12, 2023 Oct 12, 2023

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Oct 11, 2023 Oct 11, 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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Oct 12, 2023 Oct 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:

 

 

 

support_query_1.jpg

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)?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 12, 2023 Oct 12, 2023

But why are trying to do it as a plugin? What is your end goal? What is wrong with scripting? 

 

My personal opinion - plugins are for when you need to ADD NEW / EXTRA FUNCTIONALITY - otherwise do scripting? 

 

Or am I missing something? Because, looking at an extremely detailed reply from @Dirk Becker - it's not worth the hassle...

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Mentor ,
Oct 12, 2023 Oct 12, 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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 12, 2023 Oct 12, 2023

@Dirk Becker, you are right with some parts - but everything depends on the end goal ...

 

I'm not looking to "expand" InDesign's internal functionality - like addding better footnotes or ability to set width of the column in the CellStyle - I'm just trying to eliminate unnecessary mundane clicking - which I can achieve much easier than you could do in JavaScript or through plugin.

 

Of course you can achieve "base" functionality in JS / AS or through plugins - in the end my "rules" are just small scripts doing single/simple operations - but the UI I'm offering beats JS and plugins by a mile.

 

And the way I can achieve this level of UI finctionality ... let's just say, that I don't need to care about much - all the sorting, searching and filtering of the data extracted from InDesign is done automatically so I can focus on what "rules" do with this data - and as each "rule" works on a "current selection" - unless it's a custom "rule" - they are pretty much universal and unbreakable.

 

Yes, my tool is not available on Macs - and rather never will be for a few reasons - but it doesn't mean that it isn't "most powerful" - it just means that Mac users will have to keep clicking and clicking and clicking ... unless they can connect a PC with my tool to their network - WatchedFolders mode...

 

You mentioned keyboard shortcuts - I think I've solved this "problem" very well - as long as they are pressed in my tool directly - I don't even have to care about creating "shortcut editor" - user just needs to add to the end of the filename of the task, e.g. "[AC5]", and one simple procedure takes care of it - no matter how many tasks are loaded and what shortcuts have been assigned ... what's more, user can "stack" those shortcuts so more than one task can have the same shortcut assigned and more than one task can be executed on the current file / selection - conditions inside the task will decide what happens - in this case "[AC5]" translates to "execute this task when Alt+Ctrl+5 is pressed".

 

I can also handle shortcuts globally - but in limited capacity - so you can execute task directly from InDesign - my tool can be just sitting in the background - kind of assigning shortcut to a script directly in the InDesign, but better.

 

Posibilities are truly endless - and you are not limited to controlling / connecting to only InDesign ... every now and then I have a new idea that makes it even more advanced - so it's not that everything has been already decided about the functionality.

 

The basic principle of my tool is - as long as you can click it in the InDesign - or Photoshop, Illustrator, etc. - you can save those mundane clicks as a Task - kind of Macros in WORD/Excel or Actions in Photoshop - and run them any time you want.

And even if at some point you need to "pitch in" and make a decisions - you can just insert "Wait for user interaction" rule and it will stop and wait and then continue - so if you need to import some pictures, but you don't know which ones or select additional objects / text - you can still automate steps before and after.

 

There is already access to ~2000 Menu actions, ~780 object properties and ~440 text properties - and I still have to do all the Styles...

 

And this is just InteractiveMode ... then there is a BatchMode - so you can process 100s of tables in your current Document or Book or process 100s of files on your server ...

 

Update unspecified logo with more or less unspecified size, placed as a layered PDF, on a "EN" layer, on the 5th page from the end of the document, located in top right corner max 40mm from the top and outside margin - by switching to visible "promo 15%" layer and "hiding" "promo 20%" layer in this placed PDF - and then export whole INDD document as a PDF for print with specified profile and upload this PDF to printer's FTP and then export another interacive PDF for web - also with a spcified profile - and then export JPEG preview of this page for your website and then upload both interactive PDF and JPEG to your FTP server and then save this updated INDD file to a new folder ...

and you have 1000 documents to process ... in 25 folders ...

 

There are even more options - what I can and JS / plugin can't do in Windows  - but I think it's enough for today...

 

Overall - sky is the limit in what new "rules" I can add and how user will use them ...

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Oct 12, 2023 Oct 12, 2023

@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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 13, 2023 Oct 13, 2023

@Rahul_Rastogi

 

Yes, low level coding is the best, assembler would be even faster, but like I've said before - everything depends on the end goal - or rather client's budget... 

 

No offence, but 500 pages document isn't a sensible argument. Why anyone would have to work with such a big document - when there is a Book option? Or even without Book option - it wouldn't be a big deal to work with smaller documents. 

 

Complex "business" logic - not sure what do you mean exactly - but if you are referring to "a lot of stuff inside" - also isn't a problem at all - I've done few quite complicated solutions, with a smaller price tag than plugin would require - and done much faster. 

 

Yes, the only "limiting" factor is speed - but scripting is still few times faster than manual clicking so overall - is not so bad. 

 

So again - it all depends on the end goal and how much it will cost and how much time it will take to achieve this end goal...

 

With my new tool - I can create a new "rule" in just a few minutes and it can be used with other "rules", that's been made month ago or will be next year - without much testing. 

 

Yes, my "videos" and "descriptions" lack clarity - I'm working on this - but after you "get the idea how it works" - it's like playing with Lego. 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 13, 2023 Oct 13, 2023

One more thing, I think you are thinking about scripting limitations from the CEP / UXP perspective.

 

I'm not working "from the inside" like you would normally do with JS. I have access to all the "goodies" without much of the "drawbacks".

Yes, there is a much bigger lag when sending a "command" from my tool to InDesign - but there is a workaround to make it work much faster. 

 

I think the biggest "selling point" of my tool is in the way I can show structure / contents of the InDesign file - or more than one file at a time - and then let user navigate through it and modify it.

Completely not possible from pure scripting and way to much work if done through plugin? 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Mentor ,
Oct 12, 2023 Oct 12, 2023

Again simplifying matters, the image attributes are fragments from the image binary stream that the image parser stored there for further reference. Even if your backend maintains e.g. an OPI path, you better know what that means or just ignore it

 

There are plenty more objects involved with your image. For the most typical scenario, the image file is represented by a link object, then a link resource (double indirection). You'd get there via IID_ILINKOBJECT of your kImageItem which leads you to an kImportLinkBoss describing how its link is to get used – there are multiple other uses for links so you better check you have the correct one. With another hop from kImportLinkBoss's IID_ILINK's resource reference you'd end up at kLinkResourceBoss / IID_ILINKRESOURCE which is closer to the actual location - e.g. a file. Plenty different forms of the same concept - e.g. file URL, or actual IDFile, plus file metadata remembered such as time stamps, sizes, file type. This information would be showing in the links panel.

 

Of course you should prepare yourself for other cases, e.g. that the link uses another scheme (http was mentioned recently) or that there are other links, or that it is embedded.

 

Instead of interface hopping, there might also be related Utils or Facade objects - especially if you intend to perform changes. The concepts above should give you a starting point for further searches in the SDK.

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Oct 12, 2023 Oct 12, 2023

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Oct 13, 2023 Oct 13, 2023
LATEST

Thanx A Lot Rahul,

 

It worked!!!!

🫡🙏🏻

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines