C# SDK: trying to write a program to insert an image into a cell

I got some help here last March with writing a program to read and modify cells in a Calc spreadsheet, and with the help I received was successful in writing several very useful programs. Now I’m trying to write a program to insert images into a spreadsheet. I’ve got a test program that runs but throws an exception when trying to Add the XShape to XShapes and does not insert a visible image.

Here is the program:

using  System;
using  System.Collections.Generic;
using  System.Drawing;
using  System.IO;
using  System.Threading;
using unoidl.com.sun.star.awt;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.uno;
using unoidl.com.sun.star.bridge;
using unoidl.com.sun.star.frame;
using unoidl.com.sun.star.sheet;
using unoidl.com.sun.star.beans;
using unoidl.com.sun.star.container;
using unoidl.com.sun.star.drawing;
using unoidl.com.sun.star.table;
using unoidl.com.sun.star.text;
using unoidl.com.sun.star.util;

// addpic
// add picture to spreadsheet - debug version

class OpenOfficeApp {

  [STAThread]
  static void Main(string[] args) {

    bool lreadonly;
    string pqfile;
    string pqpic;
    XNameContainer XNC;

    pqfile = "file:///D:/Documents/NSexeye/ODS%20File%20Access/"+
                                                     "addpix/addpic.ods";
    Console.WriteLine("Using: "+pqfile);
    lreadonly = false;

    XComponentContext XCC = uno.util.Bootstrap.bootstrap();
    XMultiComponentFactory XMCF =
                              (XMultiComponentFactory)XCC.getServiceManager();
    XMultiServiceFactory XMSF1 = (XMultiServiceFactory)XCC.getServiceManager();
    XComponentLoader XCL =
        (XComponentLoader)XMSF1.createInstance("com.sun.star.frame.Desktop");

    // open the spreadsheet
    PropertyValue[] pPV = new PropertyValue[2];
    pPV[0] = new PropertyValue();
    pPV[0].Name = "Hidden";
    pPV[0].Value = new uno.Any(true);
    pPV[1] = new PropertyValue();
    pPV[1].Name = "ReadOnly";
    if (lreadonly) pPV[1].Value = new uno.Any(true);
    else pPV[1].Value = new uno.Any(false);
    XComponent XCo = XCL.loadComponentFromURL(pqfile,"_blank",0,pPV);

    XSpreadsheets XSSs = ((XSpreadsheetDocument)XCo).getSheets();
    XNameAccess XNA = (XNameAccess)XSSs;
    XSpreadsheet XSS = (XSpreadsheet)XNA.getByName("Sheet1").Value;
    XCellRange XCR = (XCellRange)XSS;

    // get size and position of cell
    // this is an attempt to read the Size from the cell...
    XCell XC = (XCell)XCR.getCellRangeByName("A1");
    XPropertySet XPSc = (XPropertySet)XC;
    uno.Any uAsz = XPSc.getPropertyValue("Size");
    // ...but I can't figure out how to cast the uno.Any to an awt.Size...
/*     unoidl.com.sun.star.awt.Size Sc = (unoidl.com.sun.star.awt.Size)uAsz; */
    // what I want but don't know how to do:
/*     new unoidl.com.sun.star.awt.Size Sc = XC.getSize(); */
/*     new unoidl.com.sun.star.awt.Point Pc = XC.getPosition(); */
    // just use constants for now...
    unoidl.com.sun.star.awt.Size Sc =
                new unoidl.com.sun.star.awt.Size(2541,2541);  //1" x 1"
    unoidl.com.sun.star.awt.Point Pc =
                new unoidl.com.sun.star.awt.Point(100,100);  //guess

    // add a picture to a cell
    pqpic = "addpic";
    Console.WriteLine("Inserting picture: "+pqpic+".jpg");

    XMultiServiceFactory XMSF2 = (XMultiServiceFactory)XCo;

    // create bitmap container containing image
    XNC = (XNameContainer)
             XMSF2.createInstance("com.sun.star.drawing.BitmapTable");
    if (XNC == null) Console.WriteLine("XNC is null");
    XNC.insertByName(pqpic,new uno.Any(pqpic+".jpg"));

    // get internal URL of image
    // this gives a NoSuchElementException -- I don't know why:
/*     string pqintURL = XNC.getByName(pqpic).Value.ToString(); */
    string pqintURL = "???";

    // create graphic shape service
    Object GOS = null;
    GOS = XMSF2.createInstance("com.sun.star.drawing.GraphicObjectShape");
    XShape XS = (XShape)GOS;
    if (XS == null) {
      Console.WriteLine("XS is null");
      return;
    }

    // set image internal URL, size and position
    XPropertySet XPS = (XPropertySet)XS;
    XPS.setPropertyValue("GraphicURL",new uno.Any(pqintURL));
    XS.setSize(Sc);
    XS.setPosition(Pc);

    // insert the image
    XDrawPageSupplier XDPS = (XDrawPageSupplier)XSS;
    XDrawPage XDP = XDPS.getDrawPage();
    if (XDP == null) {
      Console.WriteLine("XDP is null");
      return;
    }
    XShapes XSs = (XShapes)XDP;
    try {
      XSs.add(XS);
    }
    catch (System.Exception E) {
      Console.WriteLine("Add: "+E);
    }
    Console.WriteLine("Image "+pqpic+".jpg inserted as "+pqintURL);

    XModifiable XM = (XModifiable)XCo;
    XM.setModified(true);
    XCloseable XCl = (XCloseable)XCo;
    XCl.close(true);

    XDesktop XD = (XDesktop)XCL;
    if (XD != null) XD.terminate();
  }
}

(Sorry it’s long; I’ve included the whole program because it annoys me when code snippets use things that aren’t defined or do not show the required includes.)

If I run this I get the following output (stdout and stderr combined:

Using: file:///D:/Documents/NSexeye/ODS%20File%20Access/addpix/addpic.ods
Inserting picture: addpic.jpg
Add: unoidl.com.sun.star.lang.IllegalArgumentException

Server stack trace: 


Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at unoidl.com.sun.star.drawing.XShapes.add(XShape xShape)
   at OpenOfficeApp.Main(String[] args) in D:\Documents\NSexeye\ODS file access\addpix\addpic.cs:line 117
Image addpic.jpg inserted as ???

The main problem I am trying to solve is the exception when trying to do the Add. I have several other problems, some of which are described in comments in the source. If you can see a solution to any of them I would appreciate it if you could tell me, but they are secondary and I’ll get to them separately if needed.

I’ve attached the simple spreadsheet that I’m trying to run this program on:

addpic.ods (6.9 KB)

You should be able to compile and run this program. I work with the command-line compiler instead of an IDE because it suits my style, but I think you could just paste the code into Visual Studio.

My sample program consists of bits of code I’ve cobbled together from many samples and gotten to work, plus new code I’ve added for this add-image task. I don’t know if any of it is the correct way to do things, and errors may be hidden in any of it, any one of which could be contributing to it not working.

A little relevant information about me: I have a little bit of autism and think a little differently that most people. I have trouble grasping abstract complex things, which the LO SDK is chock full off. Recommendations like “learn the object model” won’t help me. I am, however, very good at coding and understanding what code does, which for complex stuff like this means lots of looking things up.

I’m also having difficulty learning/understanding MRI, because doing so seems to require knowing and understanding a lot of hidden stuff. I’ve spent many hours playing with it and reading the documentation for it, but I still feel that it could help me if I could get my head around it.

What I hope to achieve with this and related posts is, of course, getting my program (which will be much more complex than my sample) working, but also leaving behind instructions and sample code for doing what I am trying to do, which it seems that many other people want to do as well. I’ve found several examples of others trying to do this, but nothing complete and nothing that I can get to work.

I do understand what’s happening with inserting an image: the image is a Shape containing a Bitmap that is placed on an “overlay” of the spreadsheet called a DrawPage, and that there is little linkage between the overlay containing the image and the spreadsheet. I do have many questions about this, which I’ll get to in due time…

If you’ve had the patience to read through to the end of this very long initial post, I’m very grateful. And I’ll be especially grateful if you can help!

Thank you!

It is impossible to insert an image into the cell of an arithmetic calculator…Images on sheets are either decoration or illustration but not cell content. They are attached to the sheet’s DrawPage layer which has nothing to do with the cell’s content. You can anchor an image’s position to a cell but this will still not be related to the cell’s content.
In order to relate a picture with table information, you may use hyperlinks pointing to image files, so you get the image on request. The image is opened with the system’s image viewer.
In order to relate a picture with table information, you may use a DATABASE which can either store binary data as “cell content” or store the path of an image to be shown in a report or input form.

PicsDB is a sample databse with file names stored in a column and an input form showing the picture that is related to the current record. The context menu of a picture allows to remove a picture or choose another one by file picker dialog.

Thanks, but I pretty much knew that. I clarified it at the end of my long post, which I don’t blame you if you didn’t get to. The idea of using a database is good, but I already have a spreadsheet that works and I don’t know anything about LO databases.

Using LO’s UI I have created a spreadsheet and can “Insert” an “Image” into a cell (LO terminology not mine). I’ve inserted a number of images and they stay “in” the cell even if I do things like change column width (though they don’t scale, as I expect given that they are only loosely related to the cell).

So I can do what I want manually, which is fine, but I want to add hundreds or maybe thousands of images, using a naming convention so I can programmatically associate specific images with the cells they are supposed to be “in”. I’m laboring under what is perhaps a delusion, that if I can do something manually with LO’s UI I should also be able to do the same thing with a program.

Anyhow, thanks for your input, but I’m still hoping that someone can tell me where I’ve gone wrong with my program.

You can use any database you are familiar with, store the pcture names in a field and connect your database via ODBC or JDBC to a Base document. This is by far easier than writing Basic macros.

Did you search for macro code? Did you find Inserting an image by use of a formula ?

PicsDB contains a bunch of arbitrary pictures and 3 database documents. BasePics_Calc.odb is a connection to the Calc document Sheet_Pics.ods. The report embedded in BasePics_Calc.odb shows the records of the Calc document together with the associated pictures. Sorry for the sloppy layout. This is a 2 minutes report design.

I tried to figure out your PicsDB thing but could not.

Let me restate what I am looking for: I want to write a program that will work on a spreadsheet document and let me do things with images which are stored on the associated DrawPage. I don’t want a “workaround” using databases or macros or anything else. What I want to do can be done, the Calc program does it. I think my code is close but I need someone who is experienced in LO SDK programming to take a look at my code and tell me what’s wrong with it.

Villeroy, I appreciate the time you’ve taken to try to help me, but it’s clear that you’re experienced in finding ways to do things without using the SDK, and that’s just not what I need. My problem is simply that the SDK is quite complex, is poorly documented, and there’s no sample code that shows how to use the parts of it that I need.

Is there someone out there who can help with SDK programming?

Then go ahead and write your program. I know this beast of an API since 20 years, but there are certain things I would not try to do with with the wrong component. Thousands of lines of code can not turn Calc into a database application.

You have the MRI extension installed, don’t you? You should.
https://forum.openoffice.org/en/forum/viewtopic.php?t=49294

Code generated by MRI:

using System;
using unoidl.com.sun.star.container;
using unoidl.com.sun.star.drawing;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.sheet;
using unoidl.com.sun.star.text;
using unoidl.com.sun.star.uno;

public class Snippet {
public void snippet(XComponentContext xContext, object oInitialTarget)
{
	try
	{
		XIndexAccess xIndexAccess = (XIndexAccess) oInitialTarget;
		int nCount = xIndexAccess.getCount();
		
		XShape xShape = (XShape) xIndexAccess.getByIndex(0).Value;
		XTextContent xTextContent = (XTextContent) xShape;
		XTextRange xTextRange = xTextContent.getAnchor();
		
		XCellAddressable xCellAddressable = (XCellAddressable) xTextRange;
		CellAddress aCellAddress = xCellAddressable.getCellAddress();
		
	}
	catch (IndexOutOfBoundsException e)
	{
		// getByIndex
		Console.WriteLine(e.Message);
	}
	catch (WrappedTargetException e)
	{
		// getByIndex
		Console.WriteLine(e.Message);
	}
}
}

oInitialTarget was the currently selected picture pinned to some sheet cell.

1 Like

I don’t know many details about myself exactly, but I know my age, and that I won’t start to study “C sharp” and its bridge to the LibO API therefore (among other reasons related to my tendency to think differently), This may also have prevented me from reading the many program lines including the comments carefully enough. In any case, my desire to clearly understand what you eventually want to achieve with your code is unfulfilled.
In addition I would want to know what happens if you FIRST insert (add) the “empty shell” of a newly created graphic shape into(to) the DrawPage, then set the Anchor, and finally insert the image and set the Size.
See demo in Basic:
insertImage.ods (12.6 KB)
Sorry. Only 24 lines.

One of theese questions, where the answer is yes, but not useful at all.
.
This site does not see many people actually using the SDK. Developers use other sites, this is mostly users helping users.
.
As long as you need the API search for solutions in Basic or Python and translate from there.

Lupp: Thanks for your reply. C# for our purposes here is quite close to C++ and Java. If you can read either of those languages you can read and understand code in C#. My code doesn’t use any of the fancy stuff in C# that would make it hard to understand. It’s simple, linear, procedural code.

I do appreciate your response, especially for the link to the Basic program.

Wanderer: Thanks for your reply, and for the suggestion that I post my request on one of the developers’ sites. I will do so.

CLI Code generated by MRI based on Lupp’s macro.
The Size setting is missing.
And the GraphicURL assignment is missing.
I can’t test this code since I’m running Linux.

using System;
using unoidl.com.sun.star.beans;
using unoidl.com.sun.star.container;
using unoidl.com.sun.star.drawing;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.sheet;
using unoidl.com.sun.star.table;
using unoidl.com.sun.star.uno;

public class Snippet {
public void snippet(XComponentContext xContext, object oInitialTarget)
{
	try
	{
		XSpreadsheetDocument xSpreadsheetDocument = (XSpreadsheetDocument) oInitialTarget;
		XSpreadsheets xSpreadsheets = xSpreadsheetDocument.getSheets();
		
		XIndexAccess xIndexAccess = (XIndexAccess) xSpreadsheets;
		XSpreadsheet xSpreadsheet = (XSpreadsheet) xIndexAccess.getByIndex(0).Value;
		XCellRange xCellRange = (XCellRange) xSpreadsheet;
		XCell xCell = xCellRange.getCellByPosition(3, 5);
		
		XDrawPageSupplier xDrawPageSupplier = (XDrawPageSupplier) xSpreadsheet;
		XDrawPage xDrawPage = xDrawPageSupplier.getDrawPage();
		
		XMultiServiceFactory xMultiServiceFactory = (XMultiServiceFactory) oInitialTarget;
		object oObj1 = xMultiServiceFactory.createInstance("com.sun.star.drawing.GraphicObjectShape");
		
		XPropertySet xPropSet = (XPropertySet)oObj1;
		
		object oObj2 = (XInterface) xPropSet.getPropertyValue("Anchor").Value;
		
		XShapes xShapes = (XShapes) xDrawPage;
		
		/ xShapes.add(null);
		xShapes.add(oObj1);
	}
	catch (IndexOutOfBoundsException e)
	{
		// getByIndex, getCellByPosition
		Console.WriteLine(e.Message);
	}
	catch (WrappedTargetException e)
	{
		// getByIndex, getPropertyValue
		Console.WriteLine(e.Message);
	}
	catch (Exception e)
	{
		// createInstance
		Console.WriteLine(e.Message);
	}
	catch (RuntimeException e)
	{
		// createInstance
		Console.WriteLine(e.Message);
	}
	catch (UnknownPropertyException e)
	{
		// getPropertyValue
		Console.WriteLine(e.Message);
	}
}
}

Thanks for the code, Villeroy,

Just a note to all: my mother passed away this past Tuesday and I am understandably quite busy with all the resulting tasks. I would be examining all your suggestions in detail if I had the time. But for now it will have to wait until late this week. Thanks for your understanding.

Yes. I saw. And on a certain level I could read and understand the code. Therefore I also could tell you (just did not explicitly enough, obviously) that I think you chose the wrong order of the statements adding the new graphic shape to the DrawPage and setting its properties including the image. “Add the empty shell first. Then assign everything.”
I even had been able to rectify the C# code regarding my suggestion, but not to test it. Therefore I preferred to give a working example in the correct order in Basic.

(In fact I simply dislike C, C++, C#, Java, and to some degree Python. All these languages mimic an “efficient style” while encouraging/requiring long programs. The need to get access to the elements of LibO API emphasizes that effect, imo. I always preferred languages nearer to teaching - like what Wirth and others developed for the purpose. I started with “Algol 60” in 1964. My first “lecture” I gave in 1968. And I also disliked FORTRAN at that time though I had to study and understand programs written in that slang. This, of course, doesn’t mean that I regard LibO Basic a seriouis programming language. I just use it because of the short and wide bridge to the API it offers.)

Hi, I strongly recommend first to test your code in Basic, and then translate it to CSharp. If you want to insert linked pics the approach from Lupp is fine. If you want to embed the pics into your document you have to use the BitmapTable as in your c# code. Things are quite tricky and not well documented, though.

Some comments on your code:
You need to store the document
You should remove the BitmapTableEntry after using it, otherwise there remain orphan data in the document (see the doc size)
The sequence is important: I just succeeded when setting the GraphicURL after the shape was inserted into the drawpage. I also only succeeded when giving the shape a name
The below code was tested (and worked) on AO 4.1.6.

Good luck,
ms777

Basic

Sub Main
urlPicFile = "file:///C:/Users/Martin/Downloads/pic.jpg"
urlCalcFile = "file:///C:/Users/Martin/Desktop/InsertPicture2.ods"
oDoc = StarDesktop.loadComponentFromURL(urlCalcFile, "_blank", 0, Array())

sName = "jpg2" 'the name in the BitmapTable. Choose any.

bmt = oDoc.createInstance("com.sun.star.drawing.BitmapTable") 

if bmt.hasByName(sName) then
	bmt.replaceByName(sName, urlPicFile)
else
	bmt.insertByName(sName, urlPicFile)
endif

oG = bmt.getByName(sName)

oShape = oDoc.createInstance("com.sun.star.drawing.GraphicObjectShape")

oDrawPage = oDoc.getDrawPages().getByIndex(0)


oSize = oShape.Size
oSize.Width = 10000
oSize.Height = 10000
oShape.Size = oSize
oShape.Name = "My Shape" 'seems like you have to give it a name to be stored
oDrawPage.add(oShape)

oShape.GraphicUrl = oG ' must be after the shape has been added to the drawpage

bmt.removeByName(sName) 'this is required. Otherwise, some orphan data remain in the document. Test by checking the size of the document after manually removing the pic. 

oDoc.setModified(true) 'not sure if this is required
oDoc.store() 'this is required
oDoc.close(true)
End Sub

Here in C#:

        public static void Main() {

	        string urlPicFile  = @"file:///C:/Users/Martin/Downloads/pic.jpg";
	        string urlCalcFile = @"file:///C:/Users/Martin/Desktop/InsertPicture2.ods";

            Console.WriteLine("urlPicFile: "+urlPicFile);
            Console.WriteLine("urlCalcFile: "+urlCalcFile);

    // create the desktop
            XComponentContext XCC = uno.util.Bootstrap.bootstrap();
            XMultiComponentFactory XMCF = (XMultiComponentFactory)XCC.getServiceManager();
            XMultiServiceFactory XMSF1 = (XMultiServiceFactory)XCC.getServiceManager();
            XComponentLoader XCL = (XComponentLoader)XMSF1.createInstance("com.sun.star.frame.Desktop");

    // open the spreadsheet document
            PropertyValue[] pPV = new PropertyValue[2];
            pPV[0] = new PropertyValue();
            pPV[0].Name = "Hidden";
            pPV[0].Value = new uno.Any(false);
            pPV[1] = new PropertyValue();
            pPV[1].Name = "ReadOnly";
            pPV[1].Value = new uno.Any(false);
            XComponent XCo = XCL.loadComponentFromURL(urlCalcFile,"_blank",0,pPV);

            XMultiServiceFactory XMSF2 = (XMultiServiceFactory)XCo;


    // create bitmap container containing image
	        String sName = "jpg2";
            XNameContainer XNC = (XNameContainer) XMSF2.createInstance("com.sun.star.drawing.BitmapTable");
            if (XNC == null) Console.WriteLine("XNC is null");

            if (XNC.hasByName(sName)) {
                Console.WriteLine("already existing");
                XNC.replaceByName(sName, new uno.Any(urlPicFile));
            } else {
                XNC.insertByName(sName, new uno.Any(urlPicFile));
            }
            String oG = XNC.getByName(sName).Value.ToString();
            Console.WriteLine("finished creating BitmapTable entry, oG: " + oG);


     // create graphic shape object
            XShape XS = (XShape) XMSF2.createInstance("com.sun.star.drawing.GraphicObjectShape");
            if (XS == null) {
              Console.WriteLine("XS is null");
              return;
            }

     // get the drawpage (slightly different from the Basic solution)

            XSpreadsheets XSSs = ((XSpreadsheetDocument)XCo).getSheets();
            XIndexAccess XNA = (XIndexAccess)XSSs;
            XSpreadsheet XSS = (XSpreadsheet)XNA.getByIndex(0).Value;
            XDrawPageSupplier XDPS = (XDrawPageSupplier)XSS;
            XDrawPage XDP = XDPS.getDrawPage();
            if (XDP == null) {
              Console.WriteLine("XDP is null");
              return;
            }

    // set the shape size, add it to the drawpage

            unoidl.com.sun.star.awt.Size Sc = XS.getSize();
            Sc.Width = 10000;
            Sc.Height = 10000;

            XS.setSize(Sc);

            XShapes XSs = (XShapes)XDP;
            try {
              XSs.add(XS);
            }
            catch (System.Exception E) {
              Console.WriteLine("Add: "+E);
            }

    // set the shape GraphicUrl and Name
            XPropertySet XPS = (XPropertySet)XS;
            XPS.setPropertyValue("GraphicURL",new uno.Any(oG));
            XPS.setPropertyValue("Name",new uno.Any("My Shape"));


            XNC.removeByName(sName);

    // finish up
            XModifiable XM = (XModifiable)XCo;
            XM.setModified(true); 


            XStorable XSto = (XStorable)XCo;
            XSto.store();

            XCloseable XCl = (XCloseable)XCo;
            XCl.close(true);

            XDesktop XD = (XDesktop)XCL;
            if (XD != null) XD.terminate();
        }

I’ve been plugging away at this and have made a lot of progress. I’m quite sure that I can do what I want. However I’ve been stymied by the fact that LibreOffice has changed the way images are specified, and all the examples I could find use the old way, which doesn’t work any more.

Specifically, what I understand is that now you need to specify your image as an XGraphic (instead of a GraphicURL) using the XGraphicProvider interface, which gets a MediaProperties service specifying the source image file. I’ve been studying all the documentation trying to grasp what all these things are, and I’ve made a lot of progress but it’s hard for me to hold it all in my head at once. I also have gotten fairly good at using MRI but don’t know how to get it to tell me about XGraphicProvider and related things.

Below is a very stripped down version of what I’ve done with respect to creating the XGraphic that needs to be specified to the GraphicObjectShape which will ultimately contain the image. It returns a null when I make the call that should create the XGraphic I need. I’ve spent a couple of days trying to get past this obstacle with no progress, and I’ve run out of things to try.

using  System;
using  System.Collections.Generic;
using  System.Drawing;
using  System.IO;
using  System.Threading;
using unoidl.com.sun.star.awt;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.uno;
using unoidl.com.sun.star.bridge;
using unoidl.com.sun.star.frame;
using unoidl.com.sun.star.sheet;
using unoidl.com.sun.star.beans;
using unoidl.com.sun.star.container;
using unoidl.com.sun.star.drawing;
using unoidl.com.sun.star.graphic;
using unoidl.com.sun.star.table;
using unoidl.com.sun.star.text;
using unoidl.com.sun.star.util;

// addpic
// add picture to spreadsheet - debug version

class OpenOfficeApp {

  [STAThread]
  static void Main(string[] args) {

    bool lreadonly;
    string pqfile;
    string pqURL;
    string pqpic;

    pqfile = "file:///D:/Documents/NSexeye/ODS%20File%20Access/"+
                                                     "addpix/addpic.ods";
    pqpic = "addpic2";
    pqURL = pqpic+".jpg";
    lreadonly = false;

    Console.WriteLine("Using: "+pqfile);

    // get the desktop
    XComponentContext XCC = uno.util.Bootstrap.bootstrap();
    XMultiComponentFactory XMCF =
                              (XMultiComponentFactory)XCC.getServiceManager();
    XMultiServiceFactory XMSF = (XMultiServiceFactory)XCC.getServiceManager();
    XComponentLoader XCL =
        (XComponentLoader)XMSF.createInstance("com.sun.star.frame.Desktop");

    // open the spreadsheet
    PropertyValue[] pPV = new PropertyValue[2];
    pPV[0] = new PropertyValue();
    pPV[0].Name = "Hidden";
    pPV[0].Value = new uno.Any(true);
    pPV[1] = new PropertyValue();
    pPV[1].Name = "ReadOnly";
    if (lreadonly) pPV[1].Value = new uno.Any(true);
    else pPV[1].Value = new uno.Any(false);
    XComponent XCo = XCL.loadComponentFromURL(pqfile,"_blank",0,pPV);

    // create graphic object containing image
    object oGP = XMCF.createInstanceWithContext(
                              "com.sun.star.graphic.GraphicProvider",XCC);
    if (oGP == null) {
      Console.WriteLine("oGP is null.  Aborting.");
      return;
    }
    XGraphicProvider XGP = (XGraphicProvider)oGP;
    if (XGP == null) {
      Console.WriteLine("XGP is null.  Aborting.");
      return;
    }
    pPV = new PropertyValue[1];
    pPV[0] = new PropertyValue();
    pPV[0].Name = "URL";
    pPV[0].Value = new uno.Any(pqURL);
    Console.WriteLine("Creating XGraphic containing "+pqURL);
    XGraphic XG = XGP.queryGraphic(pPV);

    // *** XG is null here
    if (XG == null) {
      Console.WriteLine("XG is null.  Aborting.");
      return;
    }

    // ... lots of stuff to be added here

    // save and close the spreadsheet
    XModifiable XM = (XModifiable)XCo;
    XM.setModified(true);
    XStorable XSt = (XStorable)XCo;
    XSt.store();
    XCloseable XCl = (XCloseable)XCo;
    XCl.close(true);

    // terminate LibreOffice
    // *** I want this to not terminate it if something else is open
    XDesktop XD = (XDesktop)XCL;
    if (XD != null) XD.terminate();
  }
}

This is a complete program and should compile and run. I’d very much appreciate it if someone could look at it and try to figure out where I’m going wrong. Everyone’s help has been invaluable in getting me this far, and it would be great if you could get me past this obstacle.

My goal here, besides getting my program to work, is to end up with a working sample program that shows how to add images to a spreadsheet and how to manipulate them. There seems to be quite a bit of interest in doing this, and such a sample would be invaluable to a small but significant number of users.

Thanks!

Hi, if you want to store the picture in the document you have to follow the way I outlined here in this thread C# SDK: trying to write a program to insert an image into a cell - #14 by ms777

Good luck,
ms777

Thank you ms777, but I spent days trying to get your code to work and it doesn’t. I’m also not going to learn Basic to do this.

Here is the SDK documentation for the GraphicURL property of the GraphicObjectShape service:

This is a url to the source bitmap for this graphic shape.

**[Deprecated:](https://api.libreoffice.org/docs/idl/ref/deprecated.html#_deprecated000105)**

as of LibreOffice 6.1 - use Graphic instead

I tried to do it the way you suggested and it doesn’t work as of the current version of LibreOffice. That’s why I’m trying to do it using XGraphic.

Thanks for taking the time to reply. If I’m doing something wrong or misunderstanding your reply, please enlighten me.

… works like a charm on LO 7.4.3.2
TestInsertPicture.ods (8.5 KB)
You have to modify the paths in the BASIC function.
Generally, I recommend to develop the API calls in LO Basic. Once it is working there, it is easy to translate to C# or python or whatever

Edit: works also on LO 7.6.0.0.alpha0

Thanks. I decided to just try to compile and run your (C#) code rather than trying to merge what it does into my program. It compiled (once I got the paths straightened out) but throws an exception, in the same place it did when I tried it in my code: where it’s trying to set the “GraphicURL” property.

It also gets partway through inserting the image in the spreadsheet, but doesn’t. It shows


(I resized the cells and manually added the same picture in C2 to show what it’s supposed to look like.) Interestingly it looks like it got the size right, because it sets that before loading the image (which fails).

Doing this has given me a couple of new things to try to get my program working however. If I get it working I’ll post it.

Thanks again!

I got the XGraphic created; the trouble was that I was using a plain file path as the URL property, instead of the Uno path style (“file:///…”). Dumb mistake. Now I get a non-null XGraphic value, but of course I don’t know if it has my image in it.

It is supremely annoying that the LibreOffice C# SDK gives few clues when something is wrong, it just doesn’t work. I know the tradeoff when writing production code between having many error messages to help diagnose problems, and the desire to have the program (appear to) keep running regardless of what is going on. I’m strongly in favor of extensive error checking and reporting, even in production programs.

I’m now stuck on a new problem. I have my XGraphic, but I have to supply it to my GraphicObjectShape as the “URL” property, which is supposed to be an XGraphic. However setPropertyValue requires that it be supplied as a uno.Any value, and I can’t figure out how to do that. How do I convert XGraphic to a uno.Any? I’ve tried everything I could think of, and read all the seemingly relevant parts of the SDK documentation, without success.

Thank you!