Javascript API - Add Images to Tool Palette via Selected Object Hyperlinks

Javascript API - Add Images to Tool Palette via Selected Object Hyperlinks

CodeDing
Advisor Advisor
2,195 Views
13 Replies
Message 1 of 14

Javascript API - Add Images to Tool Palette via Selected Object Hyperlinks

CodeDing
Advisor
Advisor

Hey all, I haven't been contributing on the forums much recently but I think that's just how life goes sometimes eh?

 

I need some help using the Javascript API because I just can't totally wrap my head around the Javascript language yet. I do not have much code AT ALL because I can't even run a "simple" operation via the api yet. Here's what my ultimate workflow should look like:

- User initiates the PhotoPreview (PP) command.

- Javascript API creates an empty Tool Palette.

- User is prompted with a selection set picker to select any objects they want, however the ONLY entities that are returned is my block called "HCL_Camera".

- Program loops through all of the HCL_Camera blocks checking for and retrieving the hyperlinks from each (hyperlinks are FULL paths, not Relative).

- For EACH hyperlink retrieved, if the hyperlink is an image format (.bmp/.jpg/.png/.tiff), then an "Img" element is created to display each photo and stacked vertically (scrolling necessary) in the tool palette.

 

Pseudocode would look something like:

 

PP command called

create empty tool palette

user prompted for selection set

if selection set exists w/ items then filter selection set for "HCL_Camera" blocks only

if selection set of blocks exists then

        foreach block in block selection set

                if hyperlink ends in (.bmp/.png/.jpg/.tiff) then

                        create Img element in tool palette displaying the image

 

I have 2 files currently.. The simple JS file which creates the palette and opens the HTML document:

// Create "PHOTOPREVIEW" palette and load html
Acad.Application.addPalette("Photo Preview", "c:/user/Desktop/PP.html");

And the HTML document which I used a default AutoCAD Example as the baseline (this file needs all the work I believe):

<!DOCTYPE html>
<html lang="en">
   <head>
       <meta charset="utf-8">
       <script type="text/javascript" src="https://df-prod.autocad360.com/jsapi/v4/Autodesk.AutoCAD.js"></script>
       <title>Photo Preview</title>
       <script>
           function photoPreview()
           {
               let pso = Acad.PromptSelectionOptions();
               pso.allowDuplicates(false);
               Acad.Editor.getSelection(pso).then(success,error);
           }

           function success(ss)
           {
               var container = document.getElementById('imageContainer');
               ss.forEach(addImg);
               //var img = document.createElement('img');
               //var src="data&colon;image/bmp;base64," + ss;
               //img.setAttribute('src', src);
               //img.setAttribute('id', 'previewImg');
               //container.appendChild(img);
           }

           function addImg(e)
           {
               //alert("Test");
           }

           function error()
           {
               alert("error");
           }

           // photoPreview();
       </script>
   </head>
   <body>
       <div id='imageContainer'></div>
       <p>Hello</p>
   </body>
   <script>
       photoPreview();
   </script>
</html>

 

Here's the Lisp command to call the JS file:

(defun c:PP nil (command "._webload" "_l" "c:\\user\\Desktop\\PP.js"))

 

Can anybody help please. I feel like if you're a Javascript pro, this would be crazy easy to accomplish.

 

Javascript API Documentation:

AutoCAD 2023 Developer and ObjectARX Help | About AutoCAD JavaScript APIs | Autodesk

 

Camera blocks to select:

image.png

 

Generic idea of what the final Tool Palette should look like:

image.png

 

Best,

~DD

0 Likes
Accepted solutions (3)
2,196 Views
13 Replies
Replies (13)
Message 2 of 14

Sea-Haven
Mentor
Mentor

Using a mnu POP menu you can do this very easy.

SeaHaven_0-1690068209089.png

 

You could do similar possibly by writing a tool palette file, not sure what they look like internally, CUI files are XML code as an example.

0 Likes
Message 3 of 14

CodeDing
Advisor
Advisor

@Sea-Haven ,

 

Sorry for late reply, but can you perhaps provide an example of that menu if you had to populate a local image?

For example, if you had this image on your computer:

https://via.placeholder.com/600/92c952 

..and it was saved local to your machine:

C:\\users\\me\\desktop\\600_img.png"

 

How would you get that to populate in your menu?

 

Also, is that popup menu modeless? or modal? I am looking for a modeless solution.

 

Best,

~DD

0 Likes
Message 4 of 14

CodeDing
Advisor
Advisor
Accepted solution

Well, after MUCH MUCH trial & error.. many tears.. and a few breakthroughs.. It seems apparent to me that my desired task can NOT be accomplished via the JS API.

 

I was able to eventually retrieve the list of properties for an entity. There does not appear to be any useful way to retrieve hyperlinks. Nor much other information for that matter. Even a task as simple as retrieving the block name is not so easy. (I say this because, although you may SEE the Object Ids listed in these properties, that does NOT mean that you can access them via JS API. So many items are still not exposed via the API. And even if they have tried to expose them, some retrievals just cause AutoCAD to crash.. EVERY time you retrieve them)

Here is what the array of 54 properties looks like when I select my block:

Array(54)
0: {key: "Annotative", value: "1"}
1: {key: "AnnotativeScale", value: "1" = 20'"}
2: {key: "AnonymousBlockTableRecord", value: "0"}
3: {key: "BlockId", value: "250a21771f0"}
4: {key: "BlockTableRecord", value: "250f9c6dd30"}
5: {key: "BlockTransform", value: "((0.400000 0.000000 0.000000 0.000000)(0.000000 0.… (595068.304606 362843.052789 0.000000 1.000000))"}
6: {key: "CastShadows", value: "1"}
7: {key: "CollisionType", value: "1"}
8: {key: "Color", value: "BYLAYER"}
9: {key: "DynamicBlockTableRecord", value: "250f9c6dd30"}
10: {key: "ExtensionDictionary", value: "250a2176470"}
11: {key: "Handle", value: "104669b"}
12: {key: "HasFields", value: "0"}
13: {key: "HasSaveVersionOverride", value: "0"}
14: {key: "IsA", value: "AcDbBlockReference"}
15: {key: "IsAProxy", value: "0"}
16: {key: "IsCancelling", value: "0"}
17: {key: "IsComparedReference", value: "0"}
18: {key: "IsDynamicBlock", value: "0"}
19: {key: "IsEraseStatusToggled", value: "0"}
20: {key: "IsErased", value: "0"}
21: {key: "IsModified", value: "0"}
22: {key: "IsModifiedGraphics", value: "0"}
23: {key: "IsModifiedXData", value: "0"}
24: {key: "IsNewObject", value: "0"}
25: {key: "IsNotifyEnabled", value: "0"}
26: {key: "IsNotifying", value: "0"}
27: {key: "IsObjectIdsInFlux", value: "0"}
28: {key: "IsPersistent", value: "1"}
29: {key: "IsPlanar", value: "1"}
30: {key: "IsReadEnabled", value: "1"}
31: {key: "IsReallyClosing", value: "1"}
32: {key: "IsTransactionResident", value: "0"}
33: {key: "IsUndoing", value: "0"}
34: {key: "IsWriteEnabled", value: "0"}
35: {key: "LayerId", value: "250f9c6de30"}
36: {key: "LineWeight", value: "-1"}
37: {key: "LinetypeId", value: "250a2177150"}
38: {key: "LinetypeScale", value: "1.000000"}
39: {key: "LocalizedName", value: "Block Reference"}
40: {key: "MatchOrientationToLayout", value: "0"}
41: {key: "MaterialId", value: "250a2177560"}
42: {key: "MergeStyle", value: "1"}
43: {key: "Normal", value: "0.000000 0.000000 1.000000"}
44: {key: "ObjectId", value: "250a2163230"}
45: {key: "OwnerId", value: "250a21771f0"}
46: {key: "PlotStyleName", value: "ByColor"}
47: {key: "Position", value: "595068.304606 362843.052789 0.000000"}
48: {key: "ReceiveShadows", value: "1"}
49: {key: "Rotation", value: "0.000000"}
50: {key: "ScaleFactors", value: "0.400000 0.400000 0.400000"}
51: {key: "Transparency", value: "0"}
52: {key: "TreatAsBlockRefForExplode", value: "0"}
53: {key: "Visible", value: "0"}

 

And here is my code I used to do so. It took a LONG time to learn how to implement these JS functions. The JS API documentation does a TERRIBLE job of providing examples or insight.

<!DOCTYPE html>
<html lang="en">
   <head>
       <meta charset="utf-8">
       <script type="text/javascript" src="https://df-prod.autocad360.com/jsapi/v4/Autodesk.AutoCAD.js"></script>
       <title>Photo Preview</title>
       <script>

         // Define a function that will launch the Photo Preview tool
         function photoPreview() {
           // Get the editor object
           var editor = Acad.Editor;

           // Create a PromptSelectionOptions object to customize the selection prompt
           var options = new Acad.PromptSelectionOptions();
           options.messageForAdding = "Select blocks with hyperlinks: ";

           // Prompt the user for a selection set using options
           var result = editor.getSelection(options); // var result = editor.getSelection(options);
           result.then(
             function(result) {

               // Get the selected entities as an array of DBEntity objects
               var entities = result.value.map(function(ent){
                 return ent.objectId;
               });

               entities.forEach(function(id){
                 var ent = new Acad.DBEntity(id);
                 console.log(ent);
                 var props = ent.getProperties();
                 props.then(
                   function(result) {
                     console.log(result); //  show all properties in console
                     //
                     // THIS IS AS FAR AS I GOT, NO APPARENTLY USEFUL WAY TO ACQUIRE HYPERLINKS VIA JAVASCRIPT API
                     //
                   },
                   function(error) {
                     console.log('Get Entity Properties Failed: ' + Acad.ErrorStatus[error]);
                   }
                 );
               });
             },
             function(error) {
               // Display an error message if the selection was not successful
               console.log('Selection Failed: ' + Acad.ErrorStatus[error]);
             }
           );
         }

         // Define a function that will add an image to the HTML file
         function addImageToHTML(url) {
           // Get the image container element by its id
           var container = document.getElementById('imageContainer');

           // Create a new image element
           var image = document.createElement('img');

           // Set the source attribute of the image element to the url
           image.setAttribute('src', url);

           // Append the image element to the container element
           container.appendChild(image);
         }

       </script>
   </head>
   <body>
      <div id='imageContainer'>
      </div>
   </body>
   <script>
       photoPreview();
   </script>
</html>

 

I will probably just have to accomplish my task in .NET. Which stinks, I was hoping the JS API could be somewhat useful, yet I have only ever found 1 single use case for it ever.. which was displaying a web page in a palette. Oh well.

 

Best of luck to those who tread on the JS API in the future.

JS API Developer Documentation here:

https://help.autodesk.com/view/OARX/2023/ENU/?guid=adsk_jsdev_autocad_javascript_api_about 

 

Best,

~DD

Message 5 of 14

CodeDing
Advisor
Advisor
Accepted solution

Had to get creative, so technically I still ended up using JS API... but again, only useful for creating a palette to show a custom web page..

 

If you're not familiar already, I created a PHOTOS command a while back. But the problem is that there's no easy way to preview a BUNCH of images from within AutoCAD, so my problem has always been what is the best way to show them to the user? Which is why I started this thread/project..

 

I utilized AutoLISP to retrieve all of my photo URLs (located on network drive), and just created a custom "webpage" for the user to view them. The webpage (HTML file) and JavaScript (JS) file (to create tool palette to host "webpage") are both created as Temp files on the user's local machine.

 

Here's the workflow / end product:

Initiate PHOTOPREVIEW (aka: PP) commandInitiate PHOTOPREVIEW (aka: PP) command

 

User selects my "HCL_Camera" blocks w/ image hyperlinkedUser selects my "HCL_Camera" blocks w/ image hyperlinked

 

A Tool Palette displaying those images (again, also hyperlinked) is created for the user.A Tool Palette displaying those images (again, also hyperlinked) is created for the user.

 

Here's the PHOTOPREVIEW Code:

;; ss - selection set, of HCL_Camera blocks (with image hyperlinks)
;; returns - list, of strings of image links from blocks, else nil if none found
(defun PP_GetPhotoLinks (ss / cnt eg apps url return)
  (repeat (setq cnt (sslength ss))
    (setq eg (entget (ssname ss (setq cnt (1- cnt))) '("PE_URL")))
    (if (and (setq apps (cdr (assoc -3 eg)))
             (setq url (cdr (assoc "PE_URL" apps))))
      (setq return (cons (cdr (assoc 1000 url)) return))
      return
    );if
  );repeat
);defun

;; photoLinks - list, of strings of image links, as returned by 'PP_GetPhotoLinks'
;; returns - string, of temporary HTML file created on local user machine.
;; ^^^^^^^ ..the HTML file is placed into Acad tool palette to neatly display photos to user
(defun PP_CreateHTMLFile (photoLinks / str htmlFile f)
  (setq str
    (strcat
      (strcat
        "<!DOCTYPE html>"
        "\n <html>"
        "\n <head>"
        "\n <title>HCL Photo Preview</title>"
        "\n <style>"
        "\n body{background-color:#33475b;}"
        "\n div{display:flex;flex-direction:column;align-items:center;}"
        "\n a{margin:15px 0px;}"
        "\n img{max-width:100%;max-height:100%;display:block;border-radius:20px;}"
        "\n </style>"
        "\n </head>"
        "\n <body>"
        "\n <div>"
      );strcat
      (apply 'strcat
        (mapcar
          '(lambda (pl)
            (setq pl (vl-string-translate "\\" "/" pl))
            (strcat "\n <a target=\"_blank\" href=\"" pl "\">"
                    "\n <img src=\"" pl "\" alt=\"" pl "\"></a>")
          );lambda
          photoLinks
        );mapcar
      );apply
      (strcat
        "\n </div>"
        "\n </body>"
        "\n </html>"
      );strcat
    );strcat
  );setq
  (setq htmlFile (strcat (vl-filename-mktemp "PP_" ".html") ".html"))
  (setq f (open htmlFile "w"))
  (write-line str f)
  (close f)
  htmlFile
);defun

;; htmlFile - string, of temporary HTML file created, as returned by 'PP_CreateHTMLFile'
;; returns - string, of temporary JavaScript file (.js) created on local user machine.
;; ^^^^^^^ ..the JS file is needed to create a palette, pointing to the HTML file
(defun PP_CreateJSFile (htmlFile / str jsFile f)
  (setq str
    (strcat "Acad.Application.addPalette(\"HCL Photo Preview\", \""
            (vl-string-translate "\\" "/" htmlFile)
            "\");"
    );strcat
  );setq
  (setq jsFile (strcat (vl-filename-mktemp "PP_" ".js") ".js"))
  (setq f (open jsFile "w"))
  (write-line str f)
  (close f)
  jsFile
);defun

;; This tool is meant to be used in conjunction with the PHOTOS custom command.
;; After the PHOTOS command creates 'HCL_Camera' blocks with hyperlinks,
;; it has been desired to have an easier way to look at the photos rather
;; than opening each one individually. This tool solves that issue by opening
;; many photos in a custom HTML webpage (displayed inside an AutoCAD palette)
;; which is MUCH easier on the end user.
(defun c:PHOTOPREVIEW ( / ss photoLinks htmlFile jsFile)
  (prompt "\nSelect HCL Camera blocks: ")
  (if (and (setq ss (ssget '((0 . "INSERT") (2 . "HCL_Camera"))))
           (setq photoLinks (PP_GetPhotoLinks ss)))
    (progn
      (setq htmlFile (PP_CreateHTMLFile photoLinks))
      (setq jsFile (PP_CreateJSFile htmlFile))
      (command "_.WEBLOAD" "_l" jsFile)
    );progn
  ;else
    (prompt "\nNo HCL Camera blocks selected.")
  );if
  (setq ss nil)
  (princ)
);defun

(defun c:PP nil (c:PHOTOPREVIEW))

 

Best,

~DD

Message 6 of 14

mohamed_srour23591
Community Visitor
Community Visitor

can you give me the final lisp as it gives me error 

unable to locate drawing

0 Likes
Message 7 of 14

CodeDing
Advisor
Advisor

@mohamed_srour23591 ,

 

The final lisp is one of the accepted solutions for this post. It was the last message right before your reply.

The error "unable to locate drawing" would not pertain to this post / lisp. You need to review your code for accuracy & implementation.

 

Best,

~DD

0 Likes
Message 8 of 14

neilyj666
Mentor
Mentor

I had to change the camera block name but works great

neilyj (No connection with Autodesk other than using the products in the real world)
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature


AEC Collection 2026 UKIE (mainly Civil 3D UKIE and IW)
Win 11 Pro x64, 1Tb Primary SSD, 1Tb Secondary SSD
64Gb RAM Intel(R) Xeon(R) W-11855M CPU @ 3.2GHz
NVIDIA RTX A5000 16Gb, Dual 27" Monitor, Dell Inspiron 7760
Message 9 of 14

CodeDing
Advisor
Advisor
Accepted solution

For future wanderers,

 

I expanded on this PHOTOPREVIEW project and turned it into PERFECTPREVIEW.

That way users can interact with and view MORE hyperlinked objects. Instead of just images, it can do PDFs, Text files, Videos, etc.

See marked PERFECTPREVIEW solutions here:

https://forums.autodesk.com/t5/visual-lisp-autolisp-and-general/perfect-preview-hyperlinks-are-under... 

 

Best,

~DD

0 Likes
Message 10 of 14

Sea-Haven
Mentor
Mentor

Just a comment for images on roads taken with say a mobile phone can insert the image directly into a dwg as each image can have the Lat & Long buried in image details, CIV3D supports import lat and long point files so description was image name, a second pass inserted the image based on the cogo point description.

0 Likes
Message 11 of 14

yuri_abuazar
Explorer
Explorer
 

It was very good, it helps a lot with visualization. Thank you.

0 Likes
Message 12 of 14

Sea-Haven
Mentor
Mentor

Pretty sure for "no easy way to preview a BUNCH of images from within AutoCAD" that just did a grid of images all resized to same size can then click on an image and say move and resize if required, when finished just erase all the other images. You can insert images pretty fast. Can look for post if of interest, all done in lisp.

 

Remember now was for pick pages in a multi page pdf. Should be simple for BMP PNG JPG etc.

0 Likes
Message 13 of 14

CodeDing
Advisor
Advisor

@Sea-Haven ,

 

The goal was to find a better solution that what AutoCAD can do with images, because OLEs would take up too much file space and bog down drawing, and XREFs would bog down drawing and images would clutter up model space. Hyperlinks were a perfect solution because they had just information to convey that an image existed (and where it existed) yet didn't rely on AutoCAD to do the heavy lifting to preview them. Instead a simple HTML page just fetches images (very quickly I might add) and it works perfectly and they're neatly organized and everything.

 

Feel free to provide other solutions for different results, but I am very proud of the solution for this problem and I can not imagine how it could possibly be done in a more-effective manner.

 

Best,

~DD

0 Likes
Message 14 of 14

Sea-Haven
Mentor
Mentor

@CodeDing yes looking good, not sure how your working out location of photo in dwg, as I mentioned I used an external program to pull Lat and Long from image, in CIV3D you could import LAT & LONG points. So based on a point should be able to do the hyperlink at real world location. Maybe I am missing something in your code always learning.

0 Likes