Shape Drawing Add-In Function
From Apache OpenOffice Wiki
< Python
Revision as of 09:04, 28 August 2013 by Marcoagpinto (talk | contribs) ([Python, Calc] Shape Drawing Add-In Function)
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",),)