Shape Drawing Add-In Function
From Apache OpenOffice Wiki
< Python
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",),)