Shape Drawing Add-In Function

From Apache OpenOffice Wiki
Jump to: navigation, search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.


This script was published by Charlie Young at the Code Snippets Forum. A recent thread in the Calc forum got me started on this. As discussed there, it is possible for a cell function to insert shapes into a sheet's DrawPage. In that thread I posted a Basic function, which I have now considerably extended using Python.


Original code

import uno
import unohelper
import math
from com.pydraw import XPyDraw
from com.sun.star.awt import Size, Point
from com.sun.star.drawing import HomogenMatrix3, HomogenMatrixLine3

#Set up enum classes.
class FillStyle():
    from com.sun.star.drawing.FillStyle import (NONE, SOLID, GRADIENT, HATCH, BITMAP)

class BitmapMode():
    from com.sun.star.drawing.BitmapMode import (REPEAT, STRETCH, NO_REPEAT)
   
class FontWeight():
    from com.sun.star.awt.FontWeight import (NORMAL, BOLD)

class FontUnderline():
    from com.sun.star.awt.FontUnderline import (SINGLE, NONE)

class FontSlant():
    from com.sun.star.awt.FontSlant import (NONE, ITALIC)

class TextHorizontalAdjust():
    from com.sun.star.drawing.TextHorizontalAdjust import (LEFT, CENTER, RIGHT, BLOCK)
   
class TextVerticalAdjust():
    from com.sun.star.drawing.TextVerticalAdjust import (TOP, CENTER, BOTTOM, BLOCK)

# Draw Shapes add-in function.

class PyDrawImpl( unohelper.Base, XPyDraw ):
    def __init__( self, ctx ):
        self.ctx = ctx

    def PyDraw(self, dims, ShapeName, FillSpecs, ShapeText):
        desktop = self.ctx.getServiceManager().createInstanceWithContext("com.sun.star.frame.Desktop", self.ctx)
        oDoc = desktop.getCurrentComponent()
        oSheets = oDoc.getSheets()
        
        #Gets style for default text properties.
        defaultStyle = oDoc.getStyleFamilies().getByName("CellStyles").getByName("Default")
        
        ShapeServices = ["com.sun.star.drawing.RectangleShape","com.sun.star.drawing.EllipseShape","com.sun.star.drawing.TextShape"]
        
        ShapesIn = getShapes(dims)

        #Erase any existing shapes named ShapeName_number
        self.delPyDraw(ShapeName)
        ReturnStr = ShapeName
        hasError = False
        pyShapes= []

        #Check shapes for valid coordinates and type.
        for i in range(len(ShapesIn)):
            ShapeError = False
            pyShape = ShapesIn[i]
            
            sheet, x, y, w, h, Rotate, ShapeType = map(float,pyShape)
            sheet = int(sheet)
            if sheet < 0 or sheet >= oSheets.getCount():
                if not ShapeError:
                    ShapeError = True
                    ReturnStr = ReturnStr + ": " if not hasError else ReturnStr + ", "
                else:
                    ReturnStr = ReturnStr + ", "
                ReturnStr = ReturnStr + "Shape " + str(i) + " Sheet out of range"
            else:
                oSheet = oSheets.getByIndex(sheet)
                x, y, w, h, ShapeType = long(1000*x), long(1000*y), long(1000*w), long(1000*h), int(ShapeType)
                SheetPos = oSheet.Position
                SheetSize = oSheet.Size
                if (x < SheetPos.X or x + w > SheetPos.X + SheetSize.Width) or (y < SheetPos.Y or y + h > SheetPos.Y + SheetSize.Height) or (w <= 0 or h <= 0):
                    if not ShapeError:
                        ShapeError = True
                        ReturnStr = ReturnStr + ": " if not hasError else ReturnStr + ", "
                    else:
                        ReturnStr = ReturnStr + ", "
                    ReturnStr = ReturnStr + "Shape " + str(i) + " dimensions out of range"
                elif ShapeType < 0 or ShapeType > 2:
                    if not ShapeError:
                        ShapeError = True
                        ReturnStr = ReturnStr + ": " if not hasError else ReturnStr + ", "
                    else:
                        ReturnStr = ReturnStr + ", "
                    ReturnStr = ReturnStr + "Shape " + str(i) + " Invalid shape type"
            #add shape if it's OK.    
            if not ShapeError:
                 pyShapes.append(pyShape)
            else:
                 hasError = True
           
        #pyShape = [sheet,x,y,width,height,theta,type]
        fillList = [FillStyle.NONE,FillStyle.SOLID,FillStyle.GRADIENT,FillStyle.HATCH,FillStyle.BITMAP,FillStyle.BITMAP]
        
        #Set defaults for Gradient, Hatch, and Bitmap fill.
        fillDefaults = ["Gradient 1","Black 0 degrees","Blank"]
        #Insert the shapes
        for i in range(len(pyShapes)):
            pyShape = pyShapes[i]
            sheet, x, y, w, h, Rotate, ShapeType = map(float,pyShape)
            x, y, w, h, ShapeType = long(1000*x), long(1000*y), long(1000*w), long(1000*h), int(ShapeType)
            filltrans = 0
            fillurl = False
            if type(FillSpecs) == tuple:
                FillSpec = FillSpecs[i] if len(FillSpecs) > i else [float(-1)]
                if type(FillSpec[0]) == float:
                    fillstyle = FillStyle.SOLID
                    FillColor = int(FillSpec[0])
                    # Read transparency value from second column.
                    filltrans = 0 if len(FillSpec) < 2 or type(FillSpec[1]) != float else int(FillSpec[1]) if 0 < FillSpec[1] <= 100 else 0 
                elif type(FillSpec[0]) == unicode:
                    if len(FillSpec[0]) > 0:
                        #Get fill type from letter code.
                        fillIndex = "nsghbu".find(FillSpec[0][0].lower())
                        if fillIndex != -1:
                            fillstyle = fillList[fillIndex]
                            fillurl = False if fillIndex < 5 else True
                        else:
                            ReturnStr = ReturnStr + ": " if not hasError else ReturnStr + ", "
                            ReturnStr = ReturnStr + "Shape " + str(i) + " Invalid fill, set to SOLID"
                            hasError = True
                    else:
                        fillstyle = FillStyle.SOLID #default to solid color.
                    if fillstyle in [FillStyle.GRADIENT,FillStyle.HATCH,FillStyle.BITMAP]:
                        #Get the fill name
                        if len(FillSpec) > 1 and type(FillSpec[1]) == unicode:
                            fillname = FillSpec[1] if len(FillSpec[1]) > 0 else fillDefaults[fillIndex - 2]
                        else:
                            fillname = fillDefaults[fillIndex - 2]
                        #Read transparency value from third column
                        filltrans = 0 if len(FillSpec) < 3 or type(FillSpec[2]) != float else int(FillSpec[2]) if 0 < FillSpec[2] <= 100 else 0
            elif type(FillSpecs) == float:
                #Default to solid color fill for all shapes with color FillSpecs
                fillstyle = FillStyle.SOLID
                FillColor = int(FillSpecs)
            else:
                fillstyle = FillStyle.SOLID
                FillColor = -1
   
            hasText = False   
            if type(ShapeText) == tuple:
                TextSpec = ShapeText[i] if len(ShapeText) > i else [""]
                ShapeString = ""
                if type(TextSpec[0]) == unicode and len(TextSpec[0]) > 0:
                    hasText = True
                    ShapeString = TextSpec[0]
                
                if hasText:
                    if len(TextSpec) > 1 and type(TextSpec[1]) == unicode and len(TextSpec[1]) > 0:
                        TextBold = FontWeight.BOLD if "b" in TextSpec[1].lower() else FontWeight.NORMAL
                        TextUnder = FontUnderline.SINGLE if "u" in TextSpec[1].lower() else FontUnderline.NONE
                        TextItalic = FontSlant.ITALIC if "i" in TextSpec[1].lower() else FontSlant.NONE
                    else:
                        TextBold = FontWeight.NORMAL
                        TextUnder = FontUnderline.NONE
                        TextItalic = FontSlant.NONE
                    
                    if len(TextSpec) > 2 and type(TextSpec[2]) == unicode:
                        if TextSpec[2].upper() == "TOP":
                     TextVertical = TextVerticalAdjust.TOP
                        elif TextSpec[2].upper() == "BOTTOM":
                           TextVertical = TextVerticalAdjust.BOTTOM
                        elif TextSpec[2].upper() == "BLOCK":
                           TextVertical = TextVerticalAdjust.BLOCK
                        else:
                           TextVertical = TextVerticalAdjust.CENTER
                    else:
                        TextVertical = TextVerticalAdjust.CENTER
                                            
                    if len(TextSpec) > 3 and type(TextSpec[3]) == unicode:
                        if TextSpec[3].upper() == "LEFT":
                     TextHorizontal = TextHorizontalAdjust.LEFT
                        elif TextSpec[3].upper() == "RIGHT":
                           TextHorizontal = TextHorizontalAdjust.RIGHT
                        elif TextSpec[3].upper() == "BLOCK":
                           TextHorizontal = TextHorizontalAdjust.BLOCK
                        else:
                           TextHorizontal = TextHorizontalAdjust.CENTER
                    else:
                        TextHorizontal = TextHorizontalAdjust.CENTER
                    if len(TextSpec) > 4 and type(TextSpec[4]) == float:
                        TextHeight = TextSpec[4] if TextSpec[4] > 0 else defaultStyle.CharHeight
                    else:
                        TextHeight = defaultStyle.CharHeight
                    if len(TextSpec) > 5 and type(TextSpec[5]) == float:
                        TextColor = long(TextSpec[5]) if TextSpec[5] > 0 else defaultStyle.CharColor
                    else:
                        TextColor = defaultStyle.CharColor
                    if len(TextSpec) > 6 and type(TextSpec[6]) == unicode:
                        TextFontName = TextSpec[6] if len(TextSpec[6]) > 0 else defaultStyle.CharFontName
                    else:
                        TextFontName = defaultStyle.CharFontName  
            
            oShape = oDoc.createInstance(ShapeServices[ShapeType])
            oShape.Name = ShapeName + "_" + str(i)
            sheet = pyShape[0]
            oShapes = oSheets.getByIndex(sheet).getDrawPage()
            oShapes.add(oShape)
            oShape.FillStyle = fillstyle
            try:
                if fillstyle == FillStyle.GRADIENT:
                    oShape.FillGradientName = fillname
                elif fillstyle == FillStyle.HATCH:
                    oShape.FillHatchName = fillname
                elif fillstyle == FillStyle.BITMAP:
                    if not fillurl:
                        oShape.FillBitmapName = fillname
                    else:
                        oShape.FillBitmapURL = fillname
                        oShape.FillBitmapMode = BitmapMode.STRETCH
                else:
                    oShape.FillColor = FillColor
            except:
                ReturnStr = ReturnStr + ": " if not hasError else ReturnStr + ", "
                ReturnStr = ReturnStr + "Shape " + str(i) + " Invalid fill name, set to default."
                hasError = True
                if fillstyle == FillStyle.GRADIENT:
                    oShape.FillGradientName = "Gradient 1"
                elif fillstyle == FillStyle.HATCH:
                    oShape.FillHatchName = "Black 0 degrees"
                elif fillstyle == FillStyle.BITMAP:
                    oShape.FillBitmapName = "Blank"
                else:
                    oShape.FillColor = FillColor
            
            if hasText:
                try:
                    oShape.String = ShapeString
                    oShape.CharWeight = TextBold
                    oShape.CharUnderline = TextUnder 
                    oShape.CharPosture = TextItalic
                    oShape.CharHeight = TextHeight
                    oShape.CharColor = TextColor
                    oShape.CharFontName = TextFontName
                    oShape.TextHorizontalAdjust = TextHorizontal
                    oShape.TextVerticalAdjust = TextVertical
                except:
                    ReturnStr = ReturnStr + ": " if not hasError else ReturnStr + ", "
                    ReturnStr = ReturnStr + "Shape " + str(i) + " Invalid text format, set to default"
                    hasError = True
                    oShape.String = ShapeString
                    oShape.CharWeight = defaultStyle.CharWeight
                    oShape.CharUnderline = defaultStyle.CharUnderline
                    oShape.CharPosture = defaultStyle.CharPosture
                    oShape.CharHeight = defaultStyle.CharHeight
                    oShape.CharColor = defaultStyle.CharColor
                    oShape.CharFontName = defaultStyle.CharFontName
                    oShape.TextHorizontalAdjust = TextHorizontalAdjust.CENTER
                    oShape.TextVerticalAdjust = TextVerticalAdjust.CENTER
                          
            oShape.FillTransparence = filltrans
            s = Size(w, h)
            p = Point(x, y)
        
            theta = math.radians(Rotate)
            oShape.setSize(s)
            oShape.setPosition(p)
            #Apply rotation
            if theta != 0:
                tMatrix = HomogenMatrix3()
                l1 = HomogenMatrixLine3()
                l2 = HomogenMatrixLine3()
                X = oShape.getPropertyValue("Transformation")
                xMatrix = [[0,0],[0,0]]
                xMatrix[0][0] = X.Line1.Column1
                xMatrix[0][1] = X.Line1.Column2
                xMatrix[1][0] = X.Line2.Column1
                xMatrix[1][1] = X.Line2.Column2
                
                #Rotate the transformation matrix.
                stheta = math.sin(theta)
                ctheta = math.cos(theta)
                l1.Column1 = xMatrix[0][0] * ctheta + xMatrix[1][0] * stheta
                l2.Column1 = -xMatrix[0][0] * stheta + xMatrix[1][0] * ctheta
                l1.Column2 = xMatrix[0][1] * ctheta + xMatrix[1][1] * stheta
                l2.Column2 = -xMatrix[0][1] * stheta + xMatrix[1][1] * ctheta
                
                #Adjust position to rotate about center.
                l1.Column3 = X.Line1.Column3 + long((w - w * ctheta - h * stheta)/2) 
                l2.Column3 = X.Line2.Column3 + long((h - h * ctheta + w * stheta)/2)
                tMatrix.Line1 = l1
                tMatrix.Line2 = l2
                tMatrix.Line3 = X.Line3
                oShape.setPropertyValue("Transformation",tMatrix)
        
        return ReturnStr
      
    #Deletes all shapes named ShapeName_number
    def delPyDraw(self, ShapeName):   
        desktop = self.ctx.getServiceManager().createInstanceWithContext("com.sun.star.frame.Desktop", self.ctx)
        oDoc = desktop.getCurrentComponent()
        ShapeServices = ["com.sun.star.drawing.RectangleShape","com.sun.star.drawing.EllipseShape","com.sun.star.drawing.TextShape"]
        oSheets =  oDoc.getSheets()
        delCount = 0
        for i in range(oSheets.getCount()):
            oSheet = oSheets.getByIndex(i)
            oShapes = oSheet.getDrawPage()
            c = oShapes.getCount()
            while c > 0:
                oShape = oShapes.getByIndex(c - 1) 
                if oShape.getShapeType() in ShapeServices and oShape.getName().startswith(ShapeName + "_"):
                    oShapes.remove(oShape)
                    delCount += 1
                c -= 1

        return delCount


def getShapes(dims):
    dimlen = len(dims)
    dimlen0 = len(dims[0])
    pyShapes = [tuple([int(dims[i][0]) if type(dims[i][0]) == float else 0] + [dims[i][j] if j < dimlen0 else 0 for j in range(1,7)]) for i in range(dimlen)]
    return pyShapes
   
def createInstance( ctx ):
    return PyDrawImpl( ctx )

g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(
   createInstance,"com.pydraw.python.PyDrawImpl",
      ("com.sun.star.sheet.AddIn",),)

Process of the script

Personal tools