Asynchronous Operations In Node.js

Node.js is a JavaScript server-side framework. One of the things you can do easily with it is write a HTTP server.

a simple “Hello, world” server looks like:

http = require ('http');
http.createServer(function(request, response){
  response.write("Hello, world");
  response.end();
}).listen(8888);

Now, if you run the simple server and type ‘http://127.0.0.1:8888’ in the URL bar of your browser you’ll see the text “Hello, world” in the browser’s window.

I’m sure you’re going to use Node.js for things more complicated than “Hello, world” programs. This server is one process that receive a request and sends a response. Other servers may perform operations that take longer, and we don’t want an operation to block the server from performing other tasks, such as handling other requests.
Node.js allows you to performs those operation in a non-blocking manner by providing the developer with function that accept a callback function as their last parameter. Those function are performed in another thread, and call the callback function upon completion.

The example I want to show you is a code fragment for disk I/O opeerations:

fs=require('fs');

fs.open('a.txt','w', function(err, fd){
  if (err)
    throw err;
  buffer=new Buffer("some long text");
  bufferLen=buffer.length;
  bytesWrittenSoFar = 0;
  pos=0;
  (function writeIt(){
    console.log('Buffer Len ' + bufferLen + ', Bytes Written ' + bytesWrittenSoFar);
    fs.write(fd,buffer,bytesWrittenSoFar, bufferLen - bytesWrittenSoFar, null, function(err,writtenBytes){
      if (err)
        throw err;
      if (bytesWrittenSoFar == bufferLen){
        console.log('Done!');
      } else {
        bytesWrittenSoFar += writtenBytes;
        writeIt();
      }
    });
  })();
});

How Does It Work?

In this example you can see two asynchronous function:

  • ‘fs.open’, that opens the file, and as the file is open calls the callback function, its 3rd argument.
  • fs.write – that attempts to write the output, and then calls the function ‘writeIt’.

This callback functions are called upon completion of opening a file, or writing it. The completion may be successful or unsuccessful. If unsuccessful, error handling should take place.

The Function WriteIt

The function ‘writeIt’ is a function which is defined and immediately performed. This function is called asynchronously, and until all the data is written, it keeps calling itself. We cannot perform this task using a loop, because a loop will create threads running in parallel. Therefore we use “asynchronous recursion”.

File System function with a simpler syntax have the suffix ‘Sync’ at the end of their names. Those functions you would usually want to avoid, so instead of having the default names, they have a name with a suffix.

Learn more about how to program in Node.js here, and maybe purchase a tutorial with exercises.

Find documentation about Node.js modules and functions here.

Animated GIF Manipulating With GD

I had a task: to write a program that makes text from animated ‘gif’ images of letters. For example, the following image:

hw

Usually, the PHP extension ‘GD’ is good enough to perform the job. GD is an image processing extension, that allows a developer to create canvasses, place images of common formats in there, save your picture to an image file of your favorite format or to a string, and other operations. This time, GD generated the big image, but without the animation. But GD is still useful if you learn about the structure of a GIF  file, and you can get all the relevant information in this text file.

The Structure of a GIF File Made Simpler

A GIF File consists of the following:

  • File Header, begins with ‘GIF’ followed by ’87a’ or ’89a’ and 7 more bytes.
  • There may be a global color map, depending on byte 10. It’s size is calculated according to the 3 rightmost bytes of byte 10.
  • Extensions that begin with a byte of value 21h (“\x21”), including graphic control (“\x21\xf9”), text (“\x21\x01”), app data(“\x21\xff”), and comments (“\x21\xfe”).
  • Image Separator (“\x2c”).
  • A file Terminator (‘|x3b”).

The file can be processed by reading the header and using a state machine as follows:

  // Read the header
  $gif_string = my_fread($fd, 13);
  // Get the packed field;
  $value = ord(substr($gif_string, 10, 1));

  $globalMapExists = $value & 0x80;
  if ($globalMapExists){
    $tmp=($value & 0x07) + 1;
    $globalMapSize = 3 * (1 << $tmp);
  }  else
    $globalMapSize = 0;

  // Read Global Color Map
  if ($globalMapExists){
    $globalColorMap = my_fread($fd, $globalMapSize );
    $gif_string .= $globalColorMap;
  }

  // Data begins here.
  // Data blocks begin with:
  //   0x2C - Image Descriptor
  //   0x21 - Extensions: Graphic Control, Comment, Text, Application.
  $curr_frame = -1;
  $gc_encountered = FALSE;
  while (($chr = my_fread($fd, 1)) != "\x3B"){
    switch (ord($chr)){
      case 0x21:
        $next_chr = my_fread($fd, 1);
        switch (ord($next_chr)){
          case 0xf9:
            $curr_frame++;
            $frames[] = $gif_string . chr(0x21) . chr(0xf9) .
               read_frame($fd, $curr_frame);
            $gc_encountered = TRUE;
            break;
          case 0x01:
            if (!$gc_encountered){
               $frames[0] = $gif_string;
               $curr_frame = 0;
            }
            $frames[$curr_frame] .= chr(0x21) . chr(0x01) . read_text($fd);
            break;
          case 0xff:
            $gif_string .= chr(0x21) . chr(0xff) . read_app_data($fd);
            break;
          case 0xfe:
            $gif_string .= chr(0x21) . chr(0xfe) . read_comment($fd);
            break;

        }
        break;
      case 0x2c:
            if (!$gc_encountered){
               $frames[0] = $gif_string;
               $curr_frame = 0;
               $gc_encountered=TRUE;
            }
            $frames[$curr_frame] .= chr(0x2c) . read_image($fd, $curr_frame);
            break;    }
  }

Doing it With GD

The steps of the program are as follows:

  1. Create an image resource for each letter using the function $res=imagecreatefromgif($url).
  2. Get the width and height of each letter using imagesx($res) and imagesy($res). Use these values to find the size of the output image.
  3. Split each image into frames using the state machine.
  4. Create the output frames using ‘imagecreate($total_width, $total_height);’Note: the image created by GD is not stored in GIF format.
  5. If the frame is new set its transparent background using the following commands:
    1. $tci = imagecolortransparent($frame_res);  // Get the transparent color index from the original frams.
    2. imagepalettecopy($out_frames[$curr_frame_no],$frame_res); // Copy the palette to the output frame.
    3. imagefill($out_frames[$curr_frame_no], 0, 0, $tci);  // Fill the output frame with the transparent color
    4. imagecolortransparent($out_frames[$curr_frame_no], $tci);  // Set the output frame’s transparent color.
  6. Place each letter’s frame in the output frame using the command:
     imagecopymerge($out_frames[$curr_frame_no], // Destination image      $frame_res, // Source image      $pos_in_pic + $left_positions[$curr_frame_no], // Destination x,      $top_positions[$curr_frame_no], // Destination y,       0, //$left_positions[$curr_frame_no], // Source x,      0, //$top_positions[$curr_frame_no], // source y,     $width,     $height,100);
  7. Now, use a state machine similar to the one described above to create the output GIF file.

Communication Between Backbone.js And PHP

Backbone.js is a Javascript framework known as MV* (Model, View and stuff). This framework has the Backbone.sync function to perform CRUD (Create, Read, Update, Delete) operations. In order for a model object to perform these operations, it should have a property named ‘url’. The ‘sync’ function receives 3 arguments: method, model and options. Model objects also have functions that call the ‘sync’ functions, these functions are: ‘fetch’, ‘save’ and ‘destroy’. The function Save does both Create and Update operation: If the property idAttribute is defined, and the idAttribute is set, ‘save’ sends an update request, otherwise it sends a ‘create’ request. The content type of the request is ”application/json”.

How Does The Server Know What Request Type It Receives?

The client-side developer specifies one ‘url’ only per model. The server-side program can determine the type by checking the value of $_SERVER[‘REQUEST_MOTHED’]:

  • ‘GET’ – a read (or fetch) request.
  • ‘POST’ – a create(save a new model) request.
  • ‘PUT’ – an update(save existing model’s detail) request.
  • ‘DELETE’ – a delete(destroy) request.

What To Send and How To Read?

Backbone.sync uses AJAX to send requests to the server. What you have to pass is the method and model. You better not skip the 3rd arguments, options, which contains at least two member:

  • ‘success’ – a function called when the server processes the request successfully.
  • ‘error’ – a function called when the server fails to process the request.

For example:

Backbone.sync(‘GET’, myModel, {

success: function(resp){

// process the response

},

error: function(resp){

// Recover or print an error message.

}

});

If you call Backbone.sync from an object don’t use ‘this” inside the success and error function as a reference to your object.

For example, if you call ‘sync’ from a view, write ‘myView=this;’ before your call to the ‘sync’ function.

Read Requests

If you send a ‘fetch’ request, specify ‘data’ in the ‘options’ argument, for example:

myModel.fetch({

data: {‘attr1′:’value1’, ‘attr2′:’value2’, … , ‘attrN’:”valueN’},

success:

….

error:

});

In the Server Side

The request attributes are taken from $_GET or $_REQUEST, as usual.

Save Requests

Send the attributes and the options.

For example:

myModel.save(myModel.attributes, {

success:

error:

….

});

In The Server Side

The data should be read from the file “php://input”, a filename used for reading data from the request body. The content is a string, and should be parsed, but since the incoming request data is JSON encoded, you should use the PHP method “json_decode”.

For example:

$post_vars = json_decode(file_get_contents(‘php://input’), true);

Destroy Requests

This time, you should set the ‘data’ member of the options argument to a JSON string. For example:

Backbone.sync(‘delete’, myModel,{
data: JSON.stringify(myModel),  // This time the developer encodes the data.

success: function(model, resp) {

…..

},

error: function(){

….

} );

In The Server Side

Read the data from ‘php://input’ and decode it using json_decode. For example:

$post_vars = json_decode(file_get_contents(‘php://input’), true);

Anyways …

You can make sure you read the data correctly by sending the contents of ‘$_SERVER’, $post_vars and ‘$_REQUEST’ to the error_log. To print an array use json_encode.

Sending a Response To the Client

The response is everything the server side application prints to the standard output. You should print your response data encoded into JSON, as follows:

echo json_encode(responsedata);

Reading a Response

The response returned from the server is the first argument passed to the ‘succss’ callback function. It is not a model object, and you should get values from there using the method ‘get(attr-name’ or the member ‘attributes’.

For example:

{success: function(msg){
switch (msg.get(‘status’)){  // Get the value of the attribute ‘status’ in the server’s response.

To debug your code, you can use ‘alert(JSON.stringify(msg));’.

LibreOffice Javascript

There are other languages in which you can write LibreOffice macros. One of them is Javascript. If you’ve installed the package “libreoffice-script-provider-js”, you can write Javascript macros. In Linux’ you can use apt-get or rpm to install it.

Javascript macros can be edited from LibreOffice, and is ready to run without compilation.

.In the Developer Guide, you will find how to write macros in Java. Those example can be easily translated to Javascript. You can even implement interfaces and extend classes. For example, you can override an “actionPerformed” method of a button, as shown in the previous postLibreOffice – The Kakuro Cell Macro.

LibreOffice provides you with a friendlier way to create dialogs:

1. Tools=>Macros=>Organize Dialogs.

2. Edit an existing dialog or a new one.

3. If you added a control, such as a button, double-click it. a window is opened. In the General Tab, you can get or change information such as the name (important if you want to use it in the macro), color, type (“Ok” button, for example), etc. In the Event tab, you can assign macros to events.

The dialog can be created and executed by a macro. Following is an example in LibreOffice Basic:

Sub kakuro_cell
DialogLibraries.LoadLibrary( “Standard” )

oDialog1 = CreateUnoDialog( DialogLibraries.Standard.kakuro )

oDialog1.Execute()
End Sub

This will show the following dialog:

Clicking “Submit” will run another macro with the event details, and the “Button type” selected in the general tab was “OK”. We can also start our macro by pressing the “tab” key until our button gains focus and then pressing the Enter key, thus the event starting the macro is “Item status change”.

Now, what does event handling look like in a Javascript macro:

 event=ARGUMENTS[0];      // Because this macro is a callback, it has the event for first argument.
 evtSource=event.Source;       // The control that fired the event.
 xControl=UnoRuntime.queryInterface(XControl, evtSource);
 xControlModel = xControl.getModel();
 xPropertySet = UnoRuntime.queryInterface(XPropertySet, xControlModel);
 peer=xControl.getPeer();       // The dialog window.
 ctx=xControl.getContext();
 ctxControlContainer=UnoRuntime.queryInterface(XControlContainer, ctx);  // Yes, the window is a control container that contains the button and input fields.
 lowerPartControl = ctxControlContainer.getControl("lowerPart");
 lowerPartModel=lowerPartControl.getModel();
 lowerPartPropertySet=UnoRuntime.queryInterface(XPropertySet,
                                                lowerPartModel);
 upperPartControl = ctxControlContainer.getControl("upperPart");
 upperPartModel=upperPartControl.getModel();
 upperPartPropertySet=UnoRuntime.queryInterface(XPropertySet,
                                                upperPartModel);

How to Translate from Java to Javascript?

The interpreter used by the script provider is named Rhino, an interpreter developed by Mozilla.  Rhino is written entirely in Java, and allows developers to embed Java objects within their code.

Importing

To embed a Java object, you should first import it using the command “importClass(Packages.<class-name>);”.

For example:

importClass(Packages.com.sun.star.sheet.XSpreadsheetDocument);

XSCRIPTCONTEXT

In Javascript, the first command executed is not found inside a function, so instead of an argument of class XScriptContext, a variable named XSCRIPT CONTEXT is supplied upon invocation of a Javascript macro.

Java Objects

Java objects are accessed the same way in both Java and Javascript, but without the casting in the latter because Javascript is loosely type. The “new” command in Javascript has the same syntax as in Java, except for instantiating a Java array.

Instantiating a Java Array

A Javascript array containing Java objects is not a Java array. To instantiate a Java array, you should use class “java.lang.reflect.Array”. This class and other classes from the “reflect” package should not be imported.

To instantiate an array use the function “newInstance(<class>, <integer array>);”. For example:
points = java.lang.reflect.Array.newInstance(Point, [1, 3]);
Will create a bi-dimensional array with one row and 3 columns of Point objects.

Implementing an Interface or Extending a Class

Use the syntax:

<var> = new <class or interface>(<args>){
      <func-name>: function(<args>){
     }
}

For example:

xButton.addActionListener(new XActionListener() {
                    
     disposing: function(evtObj) {
        // TODO Auto-generated method stub
                        
     }
      
     actionPerformed: function(evt){
        // Javascript code
        .
        .
        .
     }
}


LibreOffice – The Kakuro Cell Macro

This macro is to be run from the Calc program.

How does it work?

When the user starts the macro, a dialog is opened:

The user insets values for the upper and lower triangles, and clicks the submit button.

Then the macro draws two triangles in the cell selected. Each triangle is empty or contains a numeric value.

The program uses 3 main components:

  • The spreadsheet document.
  • dialog.
  • The draw-page part of the document, since graphics are not part of the cell contents. So, you would use a draw page to draw shapes.

The Code

This macro will create the dialog, add controls, and execute the dialog. A callback function will be attached to the submit button, and will draw the triangles.

Imports are not included; A link to the relevant data type will replace the import.


 // Define the triangle type in the cell
 enum TriangleType {
   UPPER, LOWER
 }

 public class KakuroCell {
   public XNameContainer m_xDlgModelNameContainer = null; // Allows to access controls by name.
   public XControlContainer m_xDlgContainer = null;       // The dialog contains controls, such as buttons, input fields, etc.
   public XMultiServiceFactory m_xMSFDialogModel = null;
   public XControl m_xDialogControl = null;
   public XTopWindow m_xTopWindow;
   public XComponentContext m_xContext;
   public XModel m_doc;
   public XSpreadsheetDocument m_spreadSheetDoc;
   public XMultiServiceFactory doc_multiServiceFactory;

      private void createDialog(XMultiComponentFactory _xMCF) {
      try {
          Object oDialogModel = _xMCF.createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", m_xContext);

          // The XMultiServiceFactory of the dialog model is needed to instantiate the controls...
          m_xMSFDialogModel = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, oDialogModel);

          // The named container is used to insert the created controls into...
          m_xDlgModelNameContainer = (XNameContainer) UnoRuntime.queryInterface(XNameContainer.class, oDialogModel);
          String[] sPropertyNames = new String[] {"Height", "Moveable", "Name","PositionX","PositionY", "Step", "TabIndex","Title","Width"};

          Object[] oObjectValues = new Object[] { new Integer(129), Boolean.TRUE, "KakuroDialog", new Integer(95),new Integer(100), new Integer(1), new Short((short) 0), "Kakuro Cell", new Integer(149)};

          setPropertyValues(sPropertyNames, oObjectValues);

          // create the dialog...
          Object oUnoDialog = _xMCF.createInstanceWithContext("com.sun.star.awt.UnoControlDialog", m_xContext);
          m_xDialogControl = (XControl) UnoRuntime.queryInterface(XControl.class, oUnoDialog);

          // The scope of the control container is public...
          m_xDlgContainer = (XControlContainer) UnoRuntime.queryInterface(XControlContainer.class, oUnoDialog);

          m_xTopWindow = (XTopWindow) UnoRuntime.queryInterface(XTopWindow.class, m_xDlgContainer); 

          // link the dialog and its model...
          XControlModel xControlModel = (XControlModel) UnoRuntime.queryInterface(XControlModel.class, oDialogModel);
          m_xDialogControl.setModel(xControlModel);

      } catch (com.sun.star.uno.Exception exception) {
          exception.printStackTrace(System.out);
      }
     } 

      // Define the dialog at the model - keep in mind to pass the property names in alphabetical order!
      public XMultiComponentFactory m_xMCF;
      public XWindowPeer m_xWindowPeer;     // This will ensure a dialog is opened.

      public void setPropertyValues(String[] PropertyNames, Object[] PropertyValues){
      try{
          XMultiPropertySet xMultiPropertySet = (XMultiPropertySet) UnoRuntime.queryInterface(XMultiPropertySet.class, m_xDlgModelNameContainer);
          xMultiPropertySet.setPropertyValues(PropertyNames, PropertyValues);
      } catch (com.sun.star.uno.Exception ex) {
          ex.printStackTrace(System.out);
      }}

      public void insertSubmitButton(String name, String label, short tabIndex, int posX, int posY, int width, int height){
          try {
              Object oSubmitButton = m_xMSFDialogModel.createInstance("com.sun.star.awt.UnoControlButtonModel");
                String[] sPropertyNames= new String[]{ "BackgroundColor", "DefaultButton", "Height", "Label", "Name",   "PositionX", "PositionY", "PushButtonType", "TabIndex", "Toggle",  "Width"};
                Object [] oObjectValues=new Object[]{ new Integer(0x006e7f50), Boolean.FALSE, new Integer(height), label,  name,   new Integer(posX), new Integer(posY), new Short((short)com.sun.star.awt.PushButtonType.STANDARD_value), new Short(tabIndex), Boolean.TRUE, new Integer(width)};
                XMultiPropertySet xSBModelMPSet = (XMultiPropertySet) UnoRuntime.queryInterface(XMultiPropertySet.class, oSubmitButton);
                xSBModelMPSet.setPropertyValues(sPropertyNames, oObjectValues);
                m_xDlgModelNameContainer.insertByName(name, xSBModelMPSet);
                XControl xButtonControl = m_xDlgContainer.getControl(name);
                XButton  xButton = (XButton) UnoRuntime.queryInterface(XButton.class, xButtonControl);
                xButton.addActionListener(new XActionListener() {

                    @Override
                    public void disposing(EventObject evtObj) {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void actionPerformed(com.sun.star.awt.ActionEvent evt) {
                        // TODO Auto-generated method stub
                        try {
                          Object src=evt.Source;
                          XControl xControl=UnoRuntime.queryInterface(XXDialogControl.class, src);
                          Object controlModel=xControl.getModel();
                          XPropertySet ctlPropertySet=UnoRuntime.queryInterface(XPropertySet.class, controlModel);
                          String name=(String)ctlPropertySet.getPropertyValue("Name");
                            if (name.equals("Submit")){
                                ctlPropertySet.setPropertyValue("Enabled", Boolean.FALSE);
                                XControl upperCtl=m_xDlgContainer.getControl("UpperValue");
                                XControlModel upperModel = upperCtl.getModel();
                                XPropertySet upperPropertySet = UnoRuntime.queryInterface(XPropertySet.class, upperModel);
                                XControl lowerCtl=m_xDlgContainer.getControl("LowerValue");
                                XControlModel lowerModel = lowerCtl.getModel();
                                XPropertySet lowerPropertySet = UnoRuntime.queryInterface(XPropertySet.class, lowerModel);
                                String upperText = (String)upperPropertySet.getPropertyValue("Text");
                                String lowerText = (String)lowerPropertySet.getPropertyValue("Text");

                                int upperNumericValue = 0;
                                int lowerNumericValue = 0;
                                try {
                                    upperNumericValue=Integer.parseInt(upperText);
                                } catch (NumberFormatException nfe) {
                                    // Leave the numeric value zero
                                }

                                try {
                                    lowerNumericValue=Integer.parseInt(lowerText);
                                } catch (NumberFormatException nfe) {
                                    // Leave the numeric value zero
                                }

                                draw_cell (upperNumericValue, lowerNumericValue);
                                XDialog xDialog = (XDialog) UnoRuntime.queryInterface(XDialog.class, m_xDialogControl);
                                xDialog.endExecute();
                            }
                        } catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }); 
          } catch (Exception e){
             e.printStackTrace();
          }
      }

       // draw_cell - Get the cell's place and size, and draw the 2 triangles over it.
       public void draw_cell (int upperNumericValue, int lowerNumericValue) throws Exception{
          Object xSelection = m_doc.getCurrentSelection();
          XCellRange xCellRange = UnoRuntime.queryInterface(XCellRange.class, xSelection);
          XCell cell=null;
          try {
            cell=xCellRange.getCellByPosition(0, 0);
          } catch (IndexOutOfBoundsException e) {
            // TODO Auto-generated catch block
            throw new Exception("No cells selected.");
          }
          XCellAddressable xCellAddressable=UnoRuntime.queryInterface(XCellAddressable.class, cell);
          CellAddress cellAddress = xCellAddressable.getCellAddress();
          short sheetIx = cellAddress.Sheet;
          XSpreadsheets sheets = m_spreadSheetDoc.getSheets();
          XIndexAccess xIndexAccess = UnoRuntime.queryInterface(XIndexAccess.class, sheets);
          Object currentSheet = xIndexAccess.getByIndex(sheetIx);

          // Get the draw page properties from the spreadsheet
          XDrawPageSupplier xDrawPageSupplier=UnoRuntime.queryInterface(XDrawPageSupplier.class, currentSheet);
          XDrawPage drawPage = xDrawPageSupplier.getDrawPage();
          doc_multiServiceFactory = UnoRuntime.queryInterface(XMultiServiceFactory.class, m_doc);

          XPropertySet cellPropertySet=UnoRuntime.queryInterface(XPropertySet.class, cell);
          // Get the start point and the size of the cell.
          Point pt=(Point)cellPropertySet.getPropertyValue("Position");
          int ptx = pt.X;
          int pty = pt.Y;
          Size sz=(Size)cellPropertySet.getPropertyValue("Size");
          int width = sz.Width;
          int height = sz.Height;
          drawTriangle (drawPage, cell, ptx, pty, width, height, upperNumericValue, TriangleType.UPPER);
          drawTriangle (drawPage, cell, ptx, pty, width, height, lowerNumericValue, TriangleType.LOWER);
      }

      // Draw the triangle according to its coordinates and type, add its numeric value in it.
      private void drawTriangle(XDrawPage drawPage, XCell cell, int ptx, int pty, int width,
            int height, int numericValue, TriangleType triangleType) {
        // TODO Auto-generated method stub
        try {
            Object shapeObj = doc_multiServiceFactory.createInstance("com.sun.star.drawing.ClosedBezierShape");  // This is the type supported for polygons.
            XShape shape=UnoRuntime.queryInterface(XShape.class, shapeObj);
            Point shapePos=new Point(ptx, pty);
            shape.setPosition(shapePos);
            drawPage.add(shape);
            XPropertySet descriptor=UnoRuntime.queryInterface(XPropertySet.class, shape);
            Point[][] points=new Point[1][3];
            points[0][0]=new Point(ptx, pty);
            points[0][1]=triangleType==TriangleType.LOWER ? 
                    /* Yes */ new Point(ptx, pty + height) :
                    /* No  */ new Point(ptx + width, pty);
            points[0][2]=new Point(ptx+width, pty+height);
            PolygonFlags flags[][] = new PolygonFlags[1][3];
            flags[0][0] = flags[0][1] = flags[0][2] = PolygonFlags.NORMAL;      // All points are normal because the shape contains no curves.
            XText shapetext=UnoRuntime.queryInterface(XText.class, shapeObj);
            PolyPolygonBezierCoords coords=new PolyPolygonBezierCoords();
            coords.Flags = flags;
            coords.Coordinates = points;
            descriptor.setPropertyValue("PolyPolygonBezier", coords);
            String shapeString = numericValue!=0 ? Integer.toString(numericValue) : "";
            shapetext.setString(shapeString);
            XPropertySet textPropertySet = UnoRuntime.queryInterface(XPropertySet.class, shapetext);
            textPropertySet.setPropertyValue("CharHeight", 12.0);
            textPropertySet.setPropertyValue("CharColor", new Integer(0xffffff));
            textPropertySet.setPropertyValue("TextHorizontalAdjust", 
                    triangleType == TriangleType.LOWER ? 
                            /* Yes */ TextHorizontalAdjust.LEFT :
                            /* No  */ TextHorizontalAdjust.RIGHT);
            textPropertySet.setPropertyValue("TextVerticalAdjust", 
                    triangleType == TriangleType.LOWER ?
                            /* Yes */ TextVerticalAdjust.BOTTOM :
                            /* No  */ TextVerticalAdjust.TOP);
            textPropertySet.setPropertyValue("CharHeightComplex", 12.0);
            descriptor.setPropertyValue("Visible", true);
            descriptor.setPropertyValue("FillColor", new Integer(0));
            descriptor.setPropertyValue("Anchor", cell);
        } catch (Exception e){
            e.printStackTrace();
        }

    }

    // More dialog controls:
    public void insertNumericField(String name, String label, short tabIndex, int posX, int posY, int width, int height){
          try {
              Object oNumericField = m_xMSFDialogModel.createInstance("com.sun.star.awt.UnoControlEditModel");
                String[] sPropertyNames= new String[]{ "Height", "Name",   "PositionX", "PositionY", "TabIndex", "Width"};
                Object [] oObjectValues=new Object[]{ new Integer(height), name,   new Integer(posX), new Integer(posY), new Short(tabIndex), new Integer(width)};
                XMultiPropertySet xNFModelMPSet = (XMultiPropertySet) UnoRuntime.queryInterface(XMultiPropertySet.class, oNumericField);
                xNFModelMPSet.setPropertyValues(sPropertyNames, oObjectValues);
                m_xDlgModelNameContainer.insertByName(name, xNFModelMPSet);
          } catch (Exception e){
             e.printStackTrace();
          }
      }

      public void insertTextlabel(String name, String label, short tabIndex, int posX, int posY, int width, int height){
          try {
              Object oTextLabel = m_xMSFDialogModel.createInstance("com.sun.star.awt.UnoControlFixedTextModel");
                String[] sPropertyNames= new String[]{ "Height", "Label", "Name",   "PositionX", "PositionY", "TabIndex", "Width", "WritingMode"};
                Object [] oObjectValues=new Object[]{ new Integer(height), label, name,   new Integer(posX), new Integer(posY), new Short(tabIndex), new Integer(width), new Short(com.sun.star.text.WritingMode2.LR_TB)};
                XMultiPropertySet xFTModelMPSet = (XMultiPropertySet) UnoRuntime.queryInterface(XMultiPropertySet.class, oTextLabel);
                xFTModelMPSet.setPropertyValues(sPropertyNames, oObjectValues);
                m_xDlgModelNameContainer.insertByName(name, xFTModelMPSet);
          } catch (Exception e){
              e.printStackTrace();
          }
      }

      public void insertGroupBox (String id, short tabIndex, int posX, int posY, int width, int height){
          try {
            Object oGroupBox=null;
                oGroupBox = m_xMSFDialogModel.createInstance("com.sun.star.awt.UnoControlGroupBoxModel");
            String sName="FrameControl1";
            String[] sPropertyNames= new String[]{ "Height", "Name",   "PositionX", "PositionY", "Width", "WritingMode"};
            Object [] oObjectValues=new Object[]{ new Integer(height), sName,   new Integer(posX), new Integer(posY), new Integer(width), new Short(com.sun.star.text.WritingMode2.LR_TB)};

            XMultiPropertySet xGBModelMPSet = (XMultiPropertySet) UnoRuntime.queryInterface(XMultiPropertySet.class, oGroupBox);
            xGBModelMPSet.setPropertyValues(sPropertyNames, oObjectValues);
            m_xDlgModelNameContainer.insertByName(sName, xGBModelMPSet);
            XPropertySet xGBPSet = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, oGroupBox);
            xGBPSet.setPropertyValue("Label", "Kakuro Values");
          } catch (Exception e){
              e.printStackTrace();               
        }
      }

      public short executeDialog() throws Exception{
          XWindow xWindow = (XWindow) UnoRuntime.queryInterface(XWindow.class, m_xDlgContainer);
          // set the dialog invisible until it is executed
          xWindow.setVisible(false);
          Object oToolkit = m_xMCF.createInstanceWithContext("com.sun.star.awt.Toolkit", m_xContext);
          XToolkit xToolkit = (XToolkit) UnoRuntime.queryInterface(XToolkit.class, oToolkit);
          XWindowPeer xWindowParentPeer = xToolkit.getDesktopWindow();
          m_xDialogControl.createPeer(xToolkit, xWindowParentPeer);
          m_xWindowPeer = m_xDialogControl.getPeer();
          XDialog xDialog = (XDialog) UnoRuntime.queryInterface(XDialog.class, m_xDialogControl);
          XComponent xDialogComponent = (XComponent) UnoRuntime.queryInterface(XComponent.class, m_xDialogControl);     
          // the return value contains information about how the dialog has been closed...
          short nReturnValue = xDialog.execute();
          // free the resources...
          xDialogComponent.dispose();
          //xDialogComponent.dispose();
          return nReturnValue;
      }

      // The entry point: this function will be called, when the user runs the macro.
      public  void start(XScriptContext xScriptContext) throws BootstrapException, Exception{
          m_doc=xScriptContext.getDocument();
          m_spreadSheetDoc = UnoRuntime.queryInterface(XSpreadsheetDocument.class, m_doc);
          if (m_spreadSheetDoc == null)
              throw new Exception("Invalid Document Type. Please call from a Calc document.");
          m_xMCF = xScriptContext.getComponentContext().getServiceManager();
          createDialog(m_xMCF);
          insertGroupBox("abc", (short)0, 16, 20, 112, 80);
          insertTextlabel("Label1", "Upper:", (short)-1, 22, 41, 22, 9);
          insertNumericField("UpperValue", "", (short)1, 55, 40, 56, 12);
          insertTextlabel("Label2", "Lower:", (short)-1, 22, 57, 26, 7);
          insertNumericField("LowerValue", "666", (short)2, 55, 56, 56, 12);
          insertSubmitButton("Submit", "Submit", (short)3, 48, 82, 46, 12);

          executeDialog();
      }

}

The Parcel Descriptor

 <?xml version="1.0" encoding="UTF-8"?>
<parcel language="Java" xmlns:parcel="scripting.dtd">
  <script language="Java">
    <locale lang="en">
      <displayname value="Kakuro Cell"/>
      <description>
        Dialog Excercise
      </description>
    </locale>
    <functionname value="KakuroCell.start"/>
    <logicalname value="KakuroCell.start"/>
    <languagedepprops>
        <prop name="classpath" value="kakuro.jar"/>
    </languagedepprops>
  </script>
</parcel>

LibreOffice – Scripting Your Editor

I think using a spreadsheet document for Sudoku and Kakuro puzzles is great because those documents contain cells where you can place your content: numeric value and text. As you probably know, Kakuro puzzles have squares split by diagonal lines into triangles.

A kakuro puzzle copied to a spreadsheet document

The cells split into triangles are not provided by the office suite and should be crated by the user. The best way to create them is by a macro. The macro can be written in any language that accesses UNO(Universal Network Objects) components. For example: Javascript, Java, BeanShell, Python and Basic.
To learn how to write a HelloWorld script in each language, click here.

Files And Directories

A macro needs an entry point. If your macro is written in Java, it mus have a function with a parameter of type ‘XScriptContext’. The path to function name should be found in a file named ‘parcel-descriptor.xml’. The path includes the package name, the class name, and the function name in the format ‘<pacckage-name>.<class-name>.<function-name>, for example:

“hello.HelloWorld.printHW”.

The parcel-descriptor should also contain the location of the jar file (a zipped directory containing Java classes).

The parcel descriptor is located in ‘<path>/Scripts/java/<Script Dir>/’.

Path may be one of

  • a user path, such as’ ${HOME}/.config/libreoffice/3/user/’ – for a specific user.
  • a LibreOffice path, such as ‘/usr/lib/libreoffice/share/’         – for all LibreOffice users

Example:

The function ‘printHW’ is in the class ‘HelloWorld’ in package hello. The package is stored in “${HOME}/.config/libreoffice/3/user/Scripts/java/HelloWorld1/HelloWorld1.jar”

The file “${HOME}/.config/libreoffice/3/user/Scripts/java/HelloWorld1/parcel-descriptor.xml” will look like:

<parcel language=”Java”>

   <script language=”Java”>

      <locale lang=”en”>

         <displayname value=”HelloWorld1″/>

         <description>Prints “Hello World”.</description>

      </locale>

     <functionname value=”hello.HelloWorld.printHW“/>

     <logicalname value=”HelloWorld.printHW“/>

     <languagedepprops>

        <prop name=”classpath” value=”HelloWorld1.jar“/>

     </languagedepprops>

   </script>

</parcel>

Library Files for the Class Path

Your macro will access classes found in JAR files. Some of the jar files can be found in the Java directory (In my Ubuntu 12.04, it is ‘/usr/share/java’) and some in the ‘libreoffice/program/classes’ (‘/usr/lib/libreoffice/program/classes’).

The files are:

  • ridl.jar – in ‘/usr/share/java’
  • unoil.jar – in (‘/usr/lib/libreoffice/program/classes’
  • unoloader.jar – in ‘/usr/share/java’
  • jurt.jar – in ‘/usr/share/java’
  • juh.jar – in ‘/usr/share/java’

Data Types

There are 4 kinds of data types in UNO:

  • Simple and primitive data types, with equivalents in Java, described here.
  • Structures – objects with public attributes, described here
  • Interfaces containing the functions to be used by the programmer, described here.
  • Services which are everything the module (Draw, Writer, etc) provides to the user, a Spreadsheet cell, for example.

The service implements interfaces and other services. To access the service function, get the relevant interface using  ‘UnoRuntime.queryInterface(InterfaceClass, object)’.

To instantiate (or create a Java object from) a service, use the function ‘createInstance’ of the MultiServiceFactory or MultiComponentFactory.

The next post will describe an example macro in java, The Kakuro Cell macro.

Drawing The Koch Snowflake Fractal With GIMP

I once found myself discussing in some forums about conversion of many pictures from one format to another. I knew that many image processors can work in batch mode. The first image processor I thought of was GIMP. As expected, you can get help on this tool when run from the command-line with the option ‘–help’. In linux you can use ‘man gimp” to get more details about the tool. I found that ‘-b’ is the option to use to run GIMP in batch mode, and that the default scripting language is Script-Fu. Now, what is Script-Fu?  From the menu bar: “Help -> GIMP-Online -> User Manual Web Site” leads to the following page:

http://docs.gimp.org/

Now, choose your language and version, and go to the chapter about scripting. Choose section ‘Using Script-Fu Scripts’. This language is based on Scheme, a language very similar to list. To work with Script Fu, choose ‘from the menu bar “Filters->Script-Fu”.

To load an image, use the function ‘gimp-file-load’.

Ti save a file, use the function ‘gimp-file-save’

.Well, this is not the tool I would use to convert image files.

I decided to challenge myself in drawing the Koch-Snowflake  in Script-Fu. The following picture is what the fractal looks like:

Koch

Now, how to draw this fractal? Simply draw an equilateral triangle, and follow the following steps until you no longer have a polygon:

  1.  Split each side of the polygon into three sections equal in length.
  2. Replace the middle section by two sides of an equilateral triangle, pointing outwards.

Now, how to do it with Script-Fu?

Copy the draw-line function from here.

define a function to draw the fractal, with an inner function to draw a side (origianlly, a side of a triangle).

This is the code:

(define (sqr x) (* x x))

(define (draw-line layer x-from y-from x-to y-to) 
  (let* (
          (points (cons-array 4 'double))
        )

        (aset points 0 x-from)
        (aset points 1 y-from)
        (aset points 2 x-to  )
        (aset points 3 y-to  )

        (gimp-pencil layer 4 points)
  )
)

(define (draw-koch-snowflake layer height width)
  (let* (
      ; sin60, cos60 will be used for rotating lines 60 degrees
      (sin60 (/ (sqrt 3) 2)) ; sqrt(3)/2 - No operator precedence in LISP/Scheme
      (cos60 0.5)

      (min-size (min height width))
      (side-length (* sin60 min-size))
    )

    (define (draw-side layer from-x from-y to-x to-y)
      (let* (
           (diff-x (- to-x from-x))
           (diff-y (- to-y from-y))
           (third-diff-x (/ diff-x 3))
           (third-diff-y (/ diff-y 3))
           (squared-length (+ (sqr diff-x) (sqr diff-y)))
         )
       (if (<= squared-length 1)                    ;; The (squared) distance is less than or equal to 1 pixel?
          (draw-line layer from-x from-y to-x to-y) ;; Yes, just draw the line
          (begin                                    ;; No, recursively split the side and draw it.
            ;; recursively draw the 1st section
             (define end-section-1-x (+ from-x third-diff-x))
             (define end-section-1-y (+ from-y third-diff-y))
             (draw-side layer from-x from-y end-section-1-x end-section-1-y)

             ;; create a new vertex, the 2nd section is replaced by 2 sides
             ;; of an equilateral triangle
             ;; This is how you rotate a vector 60 degrees counterclockwise. 
             (define new-vertex-x
               (+ end-section-1-x (* cos60 third-diff-x) (* sin60 third-diff-y))
             ); define new-vertex-y
             (define new-vertex-y
               (+ end-section-1-y (* (- sin60) third-diff-x) (* cos60 third-diff-y))
             ); define new-vertex-y
             (draw-side layer end-section-1-x end-section-1-y new-vertex-x new-vertex-y)

             ;; recursively draw the last section
             ;; and the section from the new vertex to the beginning of
             ;; the last section.
             (define start-section-3x (- to-x third-diff-x))
             (define start-section-3y (- to-y third-diff-y))
             (draw-side layer new-vertex-x new-vertex-y start-section-3x start-section-3y)
             (draw-side layer start-section-3x start-section-3y to-x to-y)
          ) ; End of command block
       );if
      );let* in draw-side
    ); define draw-side
    (define first-vertex-x (/ (- width side-length) 2))
    (define first-vertex-y (- (- height (* side-length sin60)) 
                              (/ (- height min-size) 2)))
    (define start-x first-vertex-x)
    (define start-y first-vertex-y)
    (define end-x (+ start-x side-length))
    (define end-y start-y)
    (draw-side layer start-x start-y end-x end-y)

    (set! start-x end-x)
    (set! start-y end-y)
    (set! end-x (- end-x (* cos60 side-length)))
    (set! end-y (+ end-y (* sin60 side-length)))
    (draw-side layer start-x start-y end-x end-y)

    (set! start-x end-x)
    (set! start-y end-y)
    (set! end-x first-vertex-x)
    (set! end-y first-vertex-y)
    (draw-side layer start-x start-y end-x end-y)

  );  let* in draw-koch-snowflake
)

; The caller function:

(define (koch-caller)
 (let* 
   (
     (my-image (car (gimp-image-new 600 600 RGB)))
     (my-layer (car (gimp-layer-new my-image 600 600 RGB-IMAGE "my layer" 100 NORMAL))) 
     ;; You add a layer because you cannot draw directly on the image object.
   )
   (gimp-image-add-layer my-image my-layer -1)    ; Add the layer to the image
   (gimp-context-set-background '(255 255 255))   ; White background
   (gimp-context-set-foreground '(000 000 000))   ; Black foreground
   (gimp-context-set-opacity 100)                 ; So you can see the picture
   (gimp-drawable-fill my-layer BACKGROUND-FILL)  ; First, fill the layer with background color.
   (gimp-brushes-set-brush "Circle (01)")         ; The thinnest line.
   (draw-koch-snowflake my-layer 600 600)         ; Draw a 600 X 600 snowflake
   (gimp-display-new my-image)                    ; Physically display the result
 )
)

Now, to better understand how to rotate a vector, refer to this Wikipedia entry.

UTF-8 And Bidirectional Text

In the beginning there was ASCII code, which supported the English alphabet. Standard ASCII characters consist of 7 bits, which are not enough to support other languages. Because a byte usually consists of 8 bits, ASCII characters can be extended to characters of the ISO-8859 family. This is not very convenient if you want to send it over the web, and the reader needs to know what type of encoding is used. In addition, an ISO-8859 standard limits the text to 256 chars, and there are standards that support more characters and more special symbols.

Unicode extends ASCII

Unicode supports multi-byte characters, but still is an extension of ASCII, which means that text originally in ASCII remain the same in Unicode. How? By using the fact that ASCII characters are 7-bit long. So, if the characters leftmost bit is 0, it is an ASCII character.

The meaning of the byte in a Unicode string is determined by the number of ‘1’ bits before the first ‘0’. The following table will show you what the bytes mean:

Byte Value Meaning
0xxxxxxx Regular ASCII – Range: 00h-7Fh
110xxxxx First of a 2-byte character. Range: 0080h – 07FFh
1110xxxx First of a 3-byte character. Range: 0800h – FFFFh
11110xxxx First of a 4-byte character. RangeL 10000h – 10FFFFh
10xxxxxx Trailing byte. The rest of the multi-byte character bytes are of this form.

The conversion of the Unicode value into byte is very simple:

  1. According to the value, find the range.
  2. Place the bits of the value in the available bits of each byte.

For example, the value of the musical symbol “♬” is U+266c. In binary ‘0010 0110 0110 1100’

In the table you can see that its in the range 0800h-FFFFh. Thus, it is a 3-byte characters.

The first byte will hold the 4 bits ‘1110’ and the characters first four bits, i.e, 11100010 in binary, or E2h. (0xE2)

The second byte will hold ’10’ and the next 6 bits, i.e. 10011001, or 99h. (0x99)

The third byte will hold ’10’ and the last 6 bits, i.e. 10101100, or ACh. (0xAC)

The Bidirectional Type (Right-to-left? Left-to-right?)

Information about each Unicode characters can be found in the file ‘UnicodeData.txt’. To download it browse the Unicode Consortium site. “Side menu -> The Unicode Standard -> Unicode Character Database” will get you in a page where you can find a link to the latest version of the unicode character data.

The file ‘UnicodeData.txt’ and others, will be found here.

This file is a reference database of character information. Each character has a line in the database, and the fields are separated by semicolons. The bidirectional file is the fifth field in the row. It can be the character direction (Left to right, Right to left), a white-space, a non-spacing mark (a mark that is displayed without changing the position, such as Hebrew points, Arabic vowel signs, accents above vowels, etc.).

More information about the fields can be found in the Unicode Character Database page, section 5.3, table 9. To get there from the home-page:

  1. From the ‘Quick Links’ frame, choose specifications.
  2. From the Specification page, choose Unicode Character Database, under ‘General’.

Table 9, contains the description of each fields. The field number (after which semicolon?) is in parentheses in column 4 of the table.

The Algorithm

In the Specifications page – mentioned in the previous section -, under “Rendering”, you can find a link to the algorithm converting input bidirectional text into visual, i.e. how the text should be displayed if we just typed it left-to-right.

FriBiDi is an implementation of the algorithm in PHP.

Ruby TK – Events, Timers

Widget Events

Existing widgets will change as a result of an event, such as mouse-clicks, cursor movements and user input. When an event is fired, a callback function is invoked.

To bind an event to a widget, you would use the bind function of the widget using the function:


bind <event>, proc {command}

For example:

bind "1", proc {puts "Hello, world"}

Time Events

There is another sort of event to be used when a duration of time passes. To bind such an event, you should use a Timer object defined in ‘tk/timer’.

The syntax for creation of a timer event is:

my_timer = Timer.new(duration, nTimes, proc {command})

To start the timer, use the method ‘start’ as follows:
my_timer.start
Timer object start as the method ‘start’ is invoked. Commands following the timer start are performed simultaneously with the command passed to the object. If you don’t want other actions taking place while the timer perform its task, you can use the method:

my_timer.wait

You can see an example of using two timers in the following code from the function ‘show_solution”  in my Klotski puzzle solver

  my_timer=TkTimer.new(1, 1500, proc {move_piece 'move'=>[3,1,1,0]})
  my_timer.start
  my_timer.wait
  my_timer=TkTimer.new(1, 1000, proc {move_piece 'move'=>[3,1,0,1]})
  my_timer.start
  my_timer.wait

The code above getw the big square out of the frame. The square moves down 3 postions, and the 2 positions to the right.

The full can be downloaded from here.

Ruby TK – The Canvas

If you want to use Ruby TK for painting – and not just displaying pictures from the disk – you can use the widget TkCanvas. To this widget you can add lines, polygons, elipses and circles etc.

Now, to add a shape to a canvas in Perl you would use the syntax:

$canvas->createLine(x1, y1..., xn, yn, ?option, value, option, value, ...?)

In Ruby:
line=TkcLine.new(canvas,x1, y1..., xn, yn, ?option=> value, option=>value, ...?)

x1,y1,…,xn,yn are the coordinates of the shape. They can be changed in response to events.
It also has options such as ‘fill’, the color with which to fill the shape.
The options can be changed in response to events.

What Are Coordinates

Every shape has its coordinates, points that define the shape. You can get or configure the shape’s coordinates using the function ‘coords’.

TkcLine – points along the shape consisting of one or more lines. Each point is where the previous line ends and the next starts.

TkcPolygom – Like TkcLine, but with a line connecting the last point to the first.

TkcRectangle – x1,y1,x2,y2 are the coordinates of two opposite vertexes of a horizontal or vertical rectangle.

TkcOval – x1,y1,x2,y2 of the rectangle enclosing the ellipse or circle.

TkcArc – like TkcOval, but in addition has the options ‘start’ and ‘extent’. ‘start’ is the number of degrees from the 3 o’clock position of the oval. ‘extent’ is the size of the angular range occupied by the arc counterclockwise from the arc’s beginning point.

Learn more at http://perlhelp.web.cern.ch/PerlHelp/ (search for tk/canvas on the right side of the window) or feel free to ask your questions in a comment.