How to Get the URL of an Embedded Image using a Macro

Here’s my problem. I insert an image into my Writer document so that it’s embedded, not linked. (If you rename the document with a .zip extension then open it, you’ll find the image in a folder named Pictures with a name like:

100000010000001D0000001D366B66751315A5F3

Now, what I do is hide this image in a hidden and password protected Section. The thing that I want to do now is get access to this image so that I can display it on a dialog. The thing is, I want to display this image only when you click on an empty image control which invokes an event and calls a Sub something like this:

Sub ImgPressed(oEvent as Object)
...
           oEvent.source.model.imageURL = "Pictures/100000010000001D0000001D6CBAE1108AA1FCB1.gif"
...
End Sub

This does not work. I did a bit of searching and found this:

In addition to the normal protocols like file:// or http:// you can use private URLs as follows to get access to graphics lying inside the graphicrepository system within an Office context:

private:graphicrepository/<path_in_repository>

Note: As of LibreOffice 6.1 GraphicObject scheme URLs are not supported anymore. For example:

vnd.sun.star.GraphicObject:10000000000001940000012FB99807BD

So it would seem that you used to be able to do this using “vnd.sun.star.GraphicObject: …”. In the quoted pie above, it says you can now use “private:graphicrepository/<path_in_repository>” to obtain a URL of an embedded image. (I’m assuming that’s what it means.)

I tried all kinds of variations like this:

Sub ImgPressed(oEvent as Object)
...
           oEvent.source.model.imageURL = "private:Pictures/10000000000001940000012FB99807BD.gif"
...
End Sub

But nothing I tried worked.

I can also get the image I want using this:

  oDP = ThisComponent.DrawPage
  oImg = oDP(0)
  If isNull(oImg) Then MsgBox("Got nothing!")
  MsgBox("The name of the image is " & oImg.Name)

Exit Sub

So the above code can get me the actual image as an object. But what I need is to get the location of the embedded image as a URL string of some sort so that I can then assign it to the image control using:

oEvent.source.model.imageURL =

I did a lot of searching for any example of this being done, but found nothing. Does anyone have any idea about this?

No, it is literally private:graphicrepository/..., so do not replace the graphicrepository with anything else. But this kind of URLs point to the images in the LibreOffice itself, not in the document.

Please provide a complete sample, like a document with an embedded image, and a macro that doesn’t work, but that demonstrate the idea. I have an impression, that it should be possible to simply use XGraphic instead of URLs, like explained here.

May it be that there once was something like an “internal file system” per document where embedded images were stored in a sense, and where related “internal URLs” pointed to, but no longer is such a thing?
If I create a text document not containing any ordinary content but an image (.jpg in my case) I can inspect the packed file, and find a wrapped-in folder Pictures containing a rather cryptic name, but this name is only referenced once in the context.xml as follows

				<draw:frame draw:style-name="fr1"
				            draw:name="Image1"
				            text:anchor-type="char"
				            svg:width="9.525cm"
				            svg:height="6.35cm"
				            draw:z-index="0">
					<draw:image xlink:href="Pictures/10000000000001C20000012C068766EAFF222785.jpg"
					            xlink:type="simple"
					            xlink:show="embed"
					            xlink:actuate="onLoad"
					            draw:mime-type="image/jpeg"/>

and the path element Pictures does nowhere occur in the .fodt file nor does any name related to the menmtioned one. The image is contained in the flat file as a long sequence of HEX encoded values enclosed by a tag pair for <office:binary-data>.
Editing:
I missed to mention that my old portable LibreOffice V 3.3 (“heritage”) can open the document from .odt, and shows

vnd.sun.star.GraphicObject:10000000000001C20000012C8A6EFF25
as the .GraphicURL of the frameshape.
I didn’try the .fodt because V3.3. asked for a Java I couldn’t supply.

1 Like

Sounds like your problem is linked to this:

https://lists.freedesktop.org/archives/libreoffice/2018-April/080091.html

Thanks for you clarification of how to use the private:graphicrepository/<path_in_repository>. I’ll have to look at this more closely at another time, but I think it won’t help with what I want to do.

Having said that, I did some more experimentation with the code I was playing around with and I discovered that, sadly, images inside a Section that has been set to hidden are no longer available to the Basic code – it can no longer see them. So my entire idea was shot down by this fact alone.

But, as I’ve started this, I might as well explain what I was trying to do, what I actually did, and why it failed. Maybe someone will come up with another idea.

So my original idea was to place vital images into a hidden and password-protected Section so that they would be safe from accidental erasure and – more importantly – the images would be inside the document and therefore would go with that document if it was moved and would always be available. (ie it wouldn’t be reliant on images outside the file.)

So I started with a new Writer doc. I created a Section and named it ChessImgs. I then inserted three small checkered GIF files into the section:

I also created this dialog for the experiment:

PickBoardDialog

The square image to the right of the Pick button is an Image control named “Img1”.

You begin the test by running the PickBoard() Sub:

Dim Dlg As Object
Dim oDP As Object
Dim DPCount As Integer
Dim ImgNum As Integer


Sub PickBoard()

oDP = ThisComponent.DrawPage()
DPCount = oDP.Count
ImgNum = MatchImageName("Image2")
If ImgNum < 0 Then MsgBox("Couldn't find the image!") : Exit Sub

DialogLibraries.LoadLibrary("Standard")
Dlg = CreateUnoDialog(DialogLibraries.Standard.PickBoardDialog)
Dlg.Execute()
 ' Now we can execute the Dialog and allow the user to change things
Dlg.dispose()

End Sub


Function MatchImageName(ImgName As String) As Integer

Dim oGraphic As Object

    For i = 0 To DPCount - 1
        oGraphic = oDP(i) ' Would the simple oDP.getByName(ImgName ) work?
        If oGraphic.Name = ImgName Then MatchImageName  = i : Exit Function
    Next
    
   MatchImageName = -1

End Function

This simply gets the DrawPage and the number of images in the Writer document. It then looks for the image named “Image2” as this is the one we’ll try to insert into the image control of the dialog.

The idea is that the user clicks on the Pick button and this invokes an event which calls the Sub below which then changes the current board image to a different one, namely Image2:

Sub ChangeBoard(oEvent as Object)

Dim oGraphic, Ctrl As Object

oGraphic = oDP(ImgNum)
 ' Now we want to change the the image on the dialog's image control, named Img1
 ' So we must use somthing like this to set the new image, but as that image is in the document, how do we do that? 
Ctrl = Dlg.getControl("Img1").Graphic = oGraphic.Graphic
Ctrl = Dlg.getControl("Img1").imageURL = oGraphic.Graphic.OriginURL
End Sub

Neither of the two options I’ve tried work at all. In the first line above, where I try to set the image control, I used .Graphic because imageURL is not a property of that control So I tried using .Graphic and setting it to the image I want using oGraphic.Graphic. That failed. In the second line I tried using .imageURL, but as I said, that is not a property of the image control (in this case.) And – oGraphic.Graphic does not appear to have a URL at all. OriginURL simply returned a Null string.

But as I said, it appears it was all for naught anyway as placing the images in a hidden section hides them from the Basic code as well, just as if they did not exist.

I’d really like to be able to load a few small images into a Writer document and have access to them via Basic, but have them otherwise protected from the user and not actually seen on the page of the document.

I seem to recall reading about begin able to create your own storage in a Writer document and being able to put images and such in it and then access them via Basic. (It was a very long time ago, so I may be completely wrong about this.)

Maybe I’ll look into this new idea. Anyone ever hear about this kind of thing?

Here’s the experimental Writer doc. (Updated)
! LO Test2.odt (49.8 KB)

Hi Lupp and iplaw67. Yes, it used to be possible to use:

vnd.sun.star.GraphicObject:

– but that was deprecated from LO 6.1 on. Now it seems impossible to get a URL string to an embedded image so that it can be used, for example, with a dialog image control. What to do?

Correct design:

Dlg.getControl("Img1").Model.Graphic=oGraphic.Graphic

By the way, you have a “broken” link to the event listener in your example (Standard.Chess.ChangeBoard).

Yes, that was the way to do it. Thanks so much for your answer. And you were right about the broken link to my event listener. A careless mistake on my part as I had copied the test document from another and then renamed the module but forgot to update the link. Sorry about that. (I’ve update the odt file with your solution and the fixed link.)

Unfortunately, my original intent was to place the images in a hidden Section that was also password protected. When I do that the macro code can no longer see those images and get access to them. So it turns out that I’m still in search of another way to do this.

We don’t see images from hidden sections through Drawpage, but they can be approached in a different way.

Let’s try this with a hidden section:

Option Explicit

Dim Dlg As Object

Sub PickBoard()
   DialogLibraries.LoadLibrary("Standard")
   
   Dlg = CreateUnoDialog(DialogLibraries.Standard.PickBoardDialog)
   Dlg.Execute() ' Now we can execute the Dialog and allow the user to change things
End Sub

Sub ChangeBoard(oEvent as Object)
  Dim oGraphicProvider, oGraphic, oPictures, oImage, i As Long
  Dim props(0) As New com.sun.star.beans.PropertyValue
  
  i=2   ' Index of image in document's folder /Pictures  
     
  oGraphicProvider=CreateUnoService("com.sun.star.graphic.GraphicProvider")
  oPictures=ThisComponent.DocumentStorage.getByName("Pictures")
  oImage=oPictures.getByName(oPictures.ElementNames(i))
  
  props(0).Name="InputStream"
  props(0).Value=oImage
  Dlg.getControl("Img1").Model.Graphic=oGraphicProvider.queryGraphic(props)
End Sub

LO Test3.odt (52.0 KB)

2 Likes

Thank you so much for taking the time to solve this problem for me, even providing the code for it. This is exactly the solution that I was hoping for. Amazing!