123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- from __future__ import absolute_import
- # Copyright (c) 2010-2019 openpyxl
- from openpyxl.descriptors.serialisable import Serialisable
- from openpyxl.descriptors import (
- Typed,
- Bool,
- NoneSet,
- Integer,
- Sequence,
- Alias,
- )
- from openpyxl.descriptors.nested import (
- NestedText,
- NestedNoneSet,
- )
- from openpyxl.descriptors.excel import Relation
- from openpyxl.packaging.relationship import (
- Relationship,
- RelationshipList,
- )
- from openpyxl.utils import coordinate_to_tuple
- from openpyxl.utils.units import (
- cm_to_EMU,
- pixels_to_EMU,
- )
- from openpyxl.drawing.image import Image
- from openpyxl.xml.constants import SHEET_DRAWING_NS
- from openpyxl.chart._chart import ChartBase
- from .xdr import (
- XDRPoint2D,
- XDRPositiveSize2D,
- )
- from .fill import Blip
- from .connector import Shape
- from .graphic import (
- GroupShape,
- GraphicFrame,
- )
- from .geometry import PresetGeometry2D
- from .picture import PictureFrame
- from .relation import ChartRelation
- class AnchorClientData(Serialisable):
- fLocksWithSheet = Bool(allow_none=True)
- fPrintsWithSheet = Bool(allow_none=True)
- def __init__(self,
- fLocksWithSheet=None,
- fPrintsWithSheet=None,
- ):
- self.fLocksWithSheet = fLocksWithSheet
- self.fPrintsWithSheet = fPrintsWithSheet
- class AnchorMarker(Serialisable):
- tagname = "marker"
- col = NestedText(expected_type=int)
- colOff = NestedText(expected_type=int)
- row = NestedText(expected_type=int)
- rowOff = NestedText(expected_type=int)
- def __init__(self,
- col=0,
- colOff=0,
- row=0,
- rowOff=0,
- ):
- self.col = col
- self.colOff = colOff
- self.row = row
- self.rowOff = rowOff
- class _AnchorBase(Serialisable):
- #one of
- sp = Typed(expected_type=Shape, allow_none=True)
- shape = Alias("sp")
- grpSp = Typed(expected_type=GroupShape, allow_none=True)
- groupShape = Alias("grpSp")
- graphicFrame = Typed(expected_type=GraphicFrame, allow_none=True)
- cxnSp = Typed(expected_type=Shape, allow_none=True)
- connectionShape = Alias("cxnSp")
- pic = Typed(expected_type=PictureFrame, allow_none=True)
- contentPart = Relation()
- clientData = Typed(expected_type=AnchorClientData)
- __elements__ = ('sp', 'grpSp', 'graphicFrame',
- 'cxnSp', 'pic', 'contentPart', 'clientData')
- def __init__(self,
- clientData=None,
- sp=None,
- grpSp=None,
- graphicFrame=None,
- cxnSp=None,
- pic=None,
- contentPart=None
- ):
- if clientData is None:
- clientData = AnchorClientData()
- self.clientData = clientData
- self.sp = sp
- self.grpSp = grpSp
- self.graphicFrame = graphicFrame
- self.cxnSp = cxnSp
- self.pic = pic
- self.contentPart = contentPart
- class AbsoluteAnchor(_AnchorBase):
- tagname = "absoluteAnchor"
- pos = Typed(expected_type=XDRPoint2D)
- ext = Typed(expected_type=XDRPositiveSize2D)
- sp = _AnchorBase.sp
- grpSp = _AnchorBase.grpSp
- graphicFrame = _AnchorBase.graphicFrame
- cxnSp = _AnchorBase.cxnSp
- pic = _AnchorBase.pic
- contentPart = _AnchorBase.contentPart
- clientData = _AnchorBase.clientData
- __elements__ = ('pos', 'ext') + _AnchorBase.__elements__
- def __init__(self,
- pos=None,
- ext=None,
- **kw
- ):
- if pos is None:
- pos = XDRPoint2D(0, 0)
- self.pos = pos
- if ext is None:
- ext = XDRPositiveSize2D(0, 0)
- self.ext = ext
- super(AbsoluteAnchor, self).__init__(**kw)
- class OneCellAnchor(_AnchorBase):
- tagname = "oneCellAnchor"
- _from = Typed(expected_type=AnchorMarker)
- ext = Typed(expected_type=XDRPositiveSize2D)
- sp = _AnchorBase.sp
- grpSp = _AnchorBase.grpSp
- graphicFrame = _AnchorBase.graphicFrame
- cxnSp = _AnchorBase.cxnSp
- pic = _AnchorBase.pic
- contentPart = _AnchorBase.contentPart
- clientData = _AnchorBase.clientData
- __elements__ = ('_from', 'ext') + _AnchorBase.__elements__
- def __init__(self,
- _from=None,
- ext=None,
- **kw
- ):
- if _from is None:
- _from = AnchorMarker()
- self._from = _from
- if ext is None:
- ext = XDRPositiveSize2D(0, 0)
- self.ext = ext
- super(OneCellAnchor, self).__init__(**kw)
- class TwoCellAnchor(_AnchorBase):
- tagname = "twoCellAnchor"
- editAs = NoneSet(values=(['twoCell', 'oneCell', 'absolute']))
- _from = Typed(expected_type=AnchorMarker)
- to = Typed(expected_type=AnchorMarker)
- sp = _AnchorBase.sp
- grpSp = _AnchorBase.grpSp
- graphicFrame = _AnchorBase.graphicFrame
- cxnSp = _AnchorBase.cxnSp
- pic = _AnchorBase.pic
- contentPart = _AnchorBase.contentPart
- clientData = _AnchorBase.clientData
- __elements__ = ('_from', 'to') + _AnchorBase.__elements__
- def __init__(self,
- editAs=None,
- _from=None,
- to=None,
- **kw
- ):
- self.editAs = editAs
- if _from is None:
- _from = AnchorMarker()
- self._from = _from
- if to is None:
- to = AnchorMarker()
- self.to = to
- super(TwoCellAnchor, self).__init__(**kw)
- def _check_anchor(obj):
- """
- Check whether an object has an existing Anchor object
- If not create a OneCellAnchor using the provided coordinate
- """
- anchor = obj.anchor
- if not isinstance(anchor, _AnchorBase):
- row, col = coordinate_to_tuple(anchor.upper())
- anchor = OneCellAnchor()
- anchor._from.row = row -1
- anchor._from.col = col -1
- if isinstance(obj, ChartBase):
- anchor.ext.width = cm_to_EMU(obj.width)
- anchor.ext.height = cm_to_EMU(obj.height)
- elif isinstance(obj, Image):
- anchor.ext.width = pixels_to_EMU(obj.width)
- anchor.ext.height = pixels_to_EMU(obj.height)
- return anchor
- class SpreadsheetDrawing(Serialisable):
- tagname = "wsDr"
- mime_type = "application/vnd.openxmlformats-officedocument.drawing+xml"
- _rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
- _path = PartName="/xl/drawings/drawing{0}.xml"
- _id = None
- twoCellAnchor = Sequence(expected_type=TwoCellAnchor, allow_none=True)
- oneCellAnchor = Sequence(expected_type=OneCellAnchor, allow_none=True)
- absoluteAnchor = Sequence(expected_type=AbsoluteAnchor, allow_none=True)
- __elements__ = ("twoCellAnchor", "oneCellAnchor", "absoluteAnchor")
- def __init__(self,
- twoCellAnchor=(),
- oneCellAnchor=(),
- absoluteAnchor=(),
- ):
- self.twoCellAnchor = twoCellAnchor
- self.oneCellAnchor = oneCellAnchor
- self.absoluteAnchor = absoluteAnchor
- self.charts = []
- self.images = []
- self._rels = []
- def __hash__(self):
- """
- Just need to check for identity
- """
- return id(self)
- def __bool__(self):
- return bool(self.charts) or bool(self.images)
- __nonzero__ = __bool__
- def _write(self):
- """
- create required structure and the serialise
- """
- anchors = []
- for idx, obj in enumerate(self.charts + self.images, 1):
- anchor = _check_anchor(obj)
- if isinstance(obj, ChartBase):
- rel = Relationship(type="chart", Target=obj.path)
- anchor.graphicFrame = self._chart_frame(idx)
- elif isinstance(obj, Image):
- rel = Relationship(type="image", Target=obj.path)
- child = anchor.pic or anchor.groupShape and anchor.groupShape.pic
- if not child:
- anchor.pic = self._picture_frame(idx)
- else:
- child.blipFill.blip.embed = "rId{0}".format(idx)
- anchors.append(anchor)
- self._rels.append(rel)
- for a in anchors:
- if isinstance(a, OneCellAnchor):
- self.oneCellAnchor.append(a)
- elif isinstance(a, TwoCellAnchor):
- self.twoCellAnchor.append(a)
- else:
- self.absoluteAnchor.append(a)
- tree = self.to_tree()
- tree.set('xmlns', SHEET_DRAWING_NS)
- return tree
- def _chart_frame(self, idx):
- chart_rel = ChartRelation("rId%s" % idx)
- frame = GraphicFrame()
- nv = frame.nvGraphicFramePr.cNvPr
- nv.id = idx
- nv.name = "Chart {0}".format(idx)
- frame.graphic.graphicData.chart = chart_rel
- return frame
- def _picture_frame(self, idx):
- pic = PictureFrame()
- pic.nvPicPr.cNvPr.descr = "Picture"
- pic.nvPicPr.cNvPr.id = idx
- pic.nvPicPr.cNvPr.name = "Image {0}".format(idx)
- pic.blipFill.blip = Blip()
- pic.blipFill.blip.embed = "rId{0}".format(idx)
- pic.blipFill.blip.cstate = "print"
- pic.spPr.prstGeom = PresetGeometry2D(prst="rect")
- pic.spPr.ln = None
- return pic
- def _write_rels(self):
- rels = RelationshipList()
- rels.Relationship = self._rels
- return rels.to_tree()
- @property
- def path(self):
- return self._path.format(self._id)
- @property
- def _chart_rels(self):
- """
- Get relationship information for each chart and bind anchor to it
- """
- rels = []
- anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor
- for anchor in anchors:
- if anchor.graphicFrame is not None:
- graphic = anchor.graphicFrame.graphic
- rel = graphic.graphicData.chart
- if rel is not None:
- rel.anchor = anchor
- rel.anchor.graphicFrame = None
- rels.append(rel)
- return rels
- @property
- def _blip_rels(self):
- """
- Get relationship information for each blip and bind anchor to it
- """
- rels = []
- anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor
- for anchor in anchors:
- child = anchor.pic or anchor.groupShape and anchor.groupShape.pic
- if child and child.blipFill:
- rel = child.blipFill.blip
- if rel is not None:
- rel.anchor = anchor
- rels.append(rel)
- return rels
|