I'm attempting to replicate and automate a well used practice in work: I'm trying to place multiple images and text within a cell in openpyxl, however I can only anchor images to the top left of a cell, and have not found a way to position any more accurately than that. So they end up overlapping with eachother and the text.
from openpyxl import Workbook
from openpyxl.drawing.image import Image
wb = Workbook()
ws = wb.active
img = Image('image.png')
ws.add_image(img, 'B2') # img.anchor is now 'B2'
wb.save('test.xlsx')
In the above case 'B2'. I haven't found any way yet to have it take (or convert from) pixel coordinates. But if I could do that then I think I could then format images and text within cells to a legible/acceptable (if not pretty) level.
Neither openpyxl.drawing.image nor openpyxl.worksheet.worksheet have an absolute anchor method that I can see. openpyxl.drawing.spreadsheet_drawing does but I'm not sure if or how to use it.
Is there a way to do this? Or is there another way I'm overlooking to achieve this goal?
(Edit: This was with Python 3.7 and openpyxl 2.6.1)
So I opened renamed my output excel file to a zip file and found "drawing1.xml" there I could see it was using OneCellAnchor. So following Charlie's suggestion I dug deeper into the openpyxl code and found there was an AbsoluteAnchor class I could use. To set the positions I needed to import XDR coordinates, and then from utils.units some conversion functions from pixel/cm to EMU (excel measurement units?). Then I just set the image anchor to absolute and gave the position and "ext" (dimensions).
from openpyxl import Workbook
from openpyxl.drawing.image import Image
from openpyxl.drawing.spreadsheet_drawing import AbsoluteAnchor
from openpyxl.drawing.xdr import XDRPoint2D, XDRPositiveSize2D
from openpyxl.utils.units import pixels_to_EMU, cm_to_EMU
wb = Workbook()
ws = wb.active
img = Image('image.png')
p2e = pixels_to_EMU
h, w = img.height, img.width
positie = XDRPoint2D(p2e(500), p2e(500))
size = XDRPositiveSize2D(p2e(h), p2e(w))
img.anchor = AbsoluteAnchor(pos=position, ext=size)
ws.add_image(img)
wb.save('test.xlsx')
But this would still be troublesome, as I'd need to know the absolute coordinates for EVERY image I want to place... ideally I still want it anchored to a cell, but then be able to more finely move it within a cell. That's when I noticed that OneCellAnchor has an offset argument. This was perfect, then I made lambda functions with the measured centimetre height and width of default excel cells.
from openpyxl.drawing.spreadsheet_drawing import OneCellAnchor, AnchorMarker
c2e = cm_to_EMU
# Calculated number of cells width or height from cm into EMUs
cellh = lambda x: c2e((x * 49.77)/99)
cellw = lambda x: c2e((x * (18.65-1.71))/10)
# Want to place image in row 5 (6 in excel), column 2 (C in excel)
# Also offset by half a column.
column = 2
coloffset = cellw(0.5)
row = 5
rowoffset = cellh(0.5)
marker = AnchorMarker(col=column, colOff=coloffset, row=row, rowOff=rowoffset)
img.anchor = OneCellAnchor(_from=marker, ext=size)
ws.add_image(img)
wb.save('test.xlsx')
Which produces:
Perfect! Exactly the precision I was looking for :)