Difference between revisions of "Python/ShapeDrawingFunction"

From Apache OpenOffice Wiki
Jump to: navigation, search
([Python, Calc] Shape Drawing Add-In Function)
 
m
 
Line 1: Line 1:
 
{{DISPLAYTITLE:Shape Drawing Add-In Function}}
 
{{DISPLAYTITLE:Shape Drawing Add-In Function}}
  
This script was published by Charlie Young at the [http://forum.openoffice.org/en/forum/viewtopic.php?f=21&t=57754 Code Snippets Forum].
+
This script was published by Charlie Young at the [https://forum.openoffice.org/en/forum/viewtopic.php?f=21&t=57754 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.
 
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 ==
 
== Original code ==
<source lang="Python">
+
<syntaxhighlight lang="Python">
 
import uno
 
import uno
 
import unohelper
 
import unohelper
Line 331: Line 331:
  
  
</source>
+
</syntaxhighlight>
  
 
== Process of the script ==
 
== Process of the script ==
  
 
[[Category:Python]]
 
[[Category:Python]]

Latest revision as of 14:05, 15 May 2021


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