Libre Office Macro
==================
The other day, I saw a post on medium with the title "Are You Smart Enough
To Find The Radius". Solving it is not very difficult if you have finished
high-school and have had no difficulties in geometry of the plan. This
time, I want to upload a PDF file because I need to draw the shape, and
the best way to draw it is to know the solution and to use a script.
The languse I've chosen is JavaScript.
To locate script written in JavaScript, edit them and find an example
script, chhose:
Tools->Macros->Organize Macros->JavaScript
Now, in the opened dialog, you can find an example file under 'Library1' its
name is 'Hello.js'.
In the native editor, the text looks to faded for me, so I will use another
editor, such as emacs.
The location of scripts in JavaScript is:
~/.config/libreoffice/4/user/Scripts/javascript
'Hello1.js' is under directory 'Library1', which also contains a file named
'parcel-descriptor.xml'. I prefer LibreOffice to edit that file for me.
So, I'm creating a new script from the dialog by standing on 'Library1',
and pressing 'Create'. The macro's name is 'GeometryPuzzle'.
The newly created script is a script adding the text "Hello World".
I find there a little explanation about XSCRIPTCONTEXT, from which the script
retrieves the document model, and a link to the developers guide. but is
returns a "Page Not Found" error, so I use "
https://api.libreoffice.org ".
A great browser for that page is GNU Emacs, in which I can see everything
clearly.
I choose the Developer Guide from the content, then "7. Text Document" and
from then scroll to "Drawing Shapes". A good example in Java for drawing
shapes is the method "protected void DrawPageExample".
Let the journey begin!
----------------------
The first thing I do is add some useful variables: the model, the text
document property of the model, the text in the document, a cursor
and the factory feature of the model to instantiate services.
The first executable commands that are not class imports are:
oDoc = UnoRuntime.queryInterface(XModel,XSCRIPTCONTEXT.getInvocationContext());
if ( !oDoc )
oDoc = XSCRIPTCONTEXT.getDocument();
xTextDoc = UnoRuntime.queryInterface(XTextDocument,oDoc);
xText = xTextDoc.getText();
oCursor = xText.createTextCursor();
xFactory = UnoRuntime.queryInterface(XMultiServiceFactory,oDoc);
The first argument of queryInterface is the interface name, you have to add
a call to importClass with the exact path name. You will find them among
the rest of "importClass" commands.
The next step is to find a place for the shapes. The following lines create
two more paragraph at the end of the document, and move the paragraph cursor
to the penultimate paragraph:
// Add 2 paragraph, and go to to one before the end
oCursor.gotoEnd(false);
xText.insertControlCharacter(
oCursor,
ControlCharacter.PARAGRAPH_BREAK,
false);
xText.insertControlCharacter(
oCursor,
ControlCharacter.PARAGRAPH_BREAK,
false);
xParagraphCursor = UnoRuntime.queryInterface(XParagraphCursor,oCursor);
xParagraphCursor.gotoPreviousParagraph(false);
The enumeration ControlCharacter has to be added to the imports. I find
its full path the IDL Reference whose link is found under Content. This time
I need a standard browser to search classes. Then, I add:
importClass(Packages.com.sun.star.text.ControlCharacter);
to the list of import commands.
Now, it's time to choose shapes. Shapes are found in path i
'com.sun.stars.drawing'` the class names end with 'Shape'.
The shapes to use are:
* A polyline shape, with which I will draw two squares: a big one
of size 2x2 and a small one of size 1x1 on the bottom and left sides
of the big squares with a vertex inside. The relevnt class is
* A circle, tangent to the upper and right sides of the big square and
touching the upper-right vertex of the small square. The relevant class is
EllipseShape.
Now, about the polylines:
The value of the "PolyPolygon" is "PointSequenceSequence", or a two-dimensional
array. A Javascript array won't be accepted, but you can instantiate the
array using "java.lang.reflect.Array.newInstance" method. That creates a
Java array! The rest of the code is:
// Create the shape interfacws
polyLine1 = xFactory.createInstance("com.sun.star.drawing.PolyLineShape");
xPolyLine1 = UnoRuntime.queryInterface(XShape, polyLine1);
circle = xFactory.createInstance("com.sun.star.drawing.EllipseShape");
xCircle = UnoRuntime.queryInterface(XShape, circle);
// Set sizes for shapes.
aSize = Size();
aSize.Height=aSize.Width=2000;
xPolyLine1.setSize(aSize);
diameter=Math.round(2000*(2-Math.sqrt(2)));
aSize.Height=aSize.Width=diameter;
xCircle.setSize(aSize);
// Set location of each shape
aPoint = Point();
aPoint.X = 100;
aPoint.Y = 100;
xPolyLine1.setPosition(aPoint);
aPoint.X = 2100-diameter;
aPoint.Y = 100;
xCircle.setPosition(aPoint);
//ArrayObj=xFactory.createInstance("java.lang.reflect.Array");//[[{"X":100,"Y":1500},{"X":100,"Y":500}]];
polyLineCoords = java.lang.reflect.Array.newInstance(Point,[1,8]);
polyLineCoords[0][0] = Point(0,1000);
polyLineCoords[0][1] = Point(0,0);
polyLineCoords[0][2] = Point(2000,0);
polyLineCoords[0][3] = Point(2000,2000);
polyLineCoords[0][4] = Point(0,2000);
polyLineCoords[0][5] = Point(0,1000);
polyLineCoords[0][6] = Point(1000,1000);
polyLineCoords[0][7] = Point(1000,2000);
xPolyLine1Props = UnoRuntime.queryInterface(XPropertySet,xPolyLine1);
xPolyLine1Props.setPropertyValue("AnchorType",TextContentAnchorType.AT_PARAGRAPH);
xPolyLine1Props.setPropertyValue("Geometry", polyLineCoords);
xCircleProps = UnoRuntime.queryInterface(XPropertySet,xCircle);
xCircleProps.setPropertyValue("AnchorType", TextContentAnchorType.AT_PARAGRAPH);
xCircleProps.setPropertyValue("FillStyle", FillStyle.NONE);
// Use the XDrawPageSupplier interface from the document to add the
// Draw Page
xDrawPageSupplier=UnoRuntime.queryInterface(XDrawPageSupplier, oDoc);
xShapes = UnoRuntime.queryInterface(XShapes,xDrawPageSupplier.getDrawPage());
xShapes.add(xPolyLine1);
xShapes.add(xCircle);
==========================================
The complete script can be found at
gopher://zaibatsu.circumlunar.space/1/~zaphodb/phlog/GeometryPuzzle.js
There are still some bugs in the code:
at least if you try to add the 2 shapes to an empty document the circle is
drawn out of the big square. But you can find a work around.
I guess using a GroupShape will fix it.