Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/openpyxl/drawing/spreadsheet_drawing.py: 41%

228 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1# Copyright (c) 2010-2022 openpyxl 

2 

3from openpyxl.descriptors.serialisable import Serialisable 

4from openpyxl.descriptors import ( 

5 Typed, 

6 Bool, 

7 NoneSet, 

8 Integer, 

9 Sequence, 

10 Alias, 

11) 

12from openpyxl.descriptors.nested import ( 

13 NestedText, 

14 NestedNoneSet, 

15) 

16from openpyxl.descriptors.excel import Relation 

17 

18from openpyxl.packaging.relationship import ( 

19 Relationship, 

20 RelationshipList, 

21) 

22from openpyxl.utils import coordinate_to_tuple 

23from openpyxl.utils.units import ( 

24 cm_to_EMU, 

25 pixels_to_EMU, 

26) 

27from openpyxl.drawing.image import Image 

28 

29from openpyxl.xml.constants import SHEET_DRAWING_NS 

30 

31from openpyxl.chart._chart import ChartBase 

32from .xdr import ( 

33 XDRPoint2D, 

34 XDRPositiveSize2D, 

35) 

36from .fill import Blip 

37from .connector import Shape 

38from .graphic import ( 

39 GroupShape, 

40 GraphicFrame, 

41 ) 

42from .geometry import PresetGeometry2D 

43from .picture import PictureFrame 

44from .relation import ChartRelation 

45 

46 

47class AnchorClientData(Serialisable): 

48 

49 fLocksWithSheet = Bool(allow_none=True) 

50 fPrintsWithSheet = Bool(allow_none=True) 

51 

52 def __init__(self, 

53 fLocksWithSheet=None, 

54 fPrintsWithSheet=None, 

55 ): 

56 self.fLocksWithSheet = fLocksWithSheet 

57 self.fPrintsWithSheet = fPrintsWithSheet 

58 

59 

60class AnchorMarker(Serialisable): 

61 

62 tagname = "marker" 

63 

64 col = NestedText(expected_type=int) 

65 colOff = NestedText(expected_type=int) 

66 row = NestedText(expected_type=int) 

67 rowOff = NestedText(expected_type=int) 

68 

69 def __init__(self, 

70 col=0, 

71 colOff=0, 

72 row=0, 

73 rowOff=0, 

74 ): 

75 self.col = col 

76 self.colOff = colOff 

77 self.row = row 

78 self.rowOff = rowOff 

79 

80 

81class _AnchorBase(Serialisable): 

82 

83 #one of 

84 sp = Typed(expected_type=Shape, allow_none=True) 

85 shape = Alias("sp") 

86 grpSp = Typed(expected_type=GroupShape, allow_none=True) 

87 groupShape = Alias("grpSp") 

88 graphicFrame = Typed(expected_type=GraphicFrame, allow_none=True) 

89 cxnSp = Typed(expected_type=Shape, allow_none=True) 

90 connectionShape = Alias("cxnSp") 

91 pic = Typed(expected_type=PictureFrame, allow_none=True) 

92 contentPart = Relation() 

93 

94 clientData = Typed(expected_type=AnchorClientData) 

95 

96 __elements__ = ('sp', 'grpSp', 'graphicFrame', 

97 'cxnSp', 'pic', 'contentPart', 'clientData') 

98 

99 def __init__(self, 

100 clientData=None, 

101 sp=None, 

102 grpSp=None, 

103 graphicFrame=None, 

104 cxnSp=None, 

105 pic=None, 

106 contentPart=None 

107 ): 

108 if clientData is None: 

109 clientData = AnchorClientData() 

110 self.clientData = clientData 

111 self.sp = sp 

112 self.grpSp = grpSp 

113 self.graphicFrame = graphicFrame 

114 self.cxnSp = cxnSp 

115 self.pic = pic 

116 self.contentPart = contentPart 

117 

118 

119class AbsoluteAnchor(_AnchorBase): 

120 

121 tagname = "absoluteAnchor" 

122 

123 pos = Typed(expected_type=XDRPoint2D) 

124 ext = Typed(expected_type=XDRPositiveSize2D) 

125 

126 sp = _AnchorBase.sp 

127 grpSp = _AnchorBase.grpSp 

128 graphicFrame = _AnchorBase.graphicFrame 

129 cxnSp = _AnchorBase.cxnSp 

130 pic = _AnchorBase.pic 

131 contentPart = _AnchorBase.contentPart 

132 clientData = _AnchorBase.clientData 

133 

134 __elements__ = ('pos', 'ext') + _AnchorBase.__elements__ 

135 

136 def __init__(self, 

137 pos=None, 

138 ext=None, 

139 **kw 

140 ): 

141 if pos is None: 

142 pos = XDRPoint2D(0, 0) 

143 self.pos = pos 

144 if ext is None: 

145 ext = XDRPositiveSize2D(0, 0) 

146 self.ext = ext 

147 super(AbsoluteAnchor, self).__init__(**kw) 

148 

149 

150class OneCellAnchor(_AnchorBase): 

151 

152 tagname = "oneCellAnchor" 

153 

154 _from = Typed(expected_type=AnchorMarker) 

155 ext = Typed(expected_type=XDRPositiveSize2D) 

156 

157 sp = _AnchorBase.sp 

158 grpSp = _AnchorBase.grpSp 

159 graphicFrame = _AnchorBase.graphicFrame 

160 cxnSp = _AnchorBase.cxnSp 

161 pic = _AnchorBase.pic 

162 contentPart = _AnchorBase.contentPart 

163 clientData = _AnchorBase.clientData 

164 

165 __elements__ = ('_from', 'ext') + _AnchorBase.__elements__ 

166 

167 

168 def __init__(self, 

169 _from=None, 

170 ext=None, 

171 **kw 

172 ): 

173 if _from is None: 

174 _from = AnchorMarker() 

175 self._from = _from 

176 if ext is None: 

177 ext = XDRPositiveSize2D(0, 0) 

178 self.ext = ext 

179 super(OneCellAnchor, self).__init__(**kw) 

180 

181 

182class TwoCellAnchor(_AnchorBase): 

183 

184 tagname = "twoCellAnchor" 

185 

186 editAs = NoneSet(values=(['twoCell', 'oneCell', 'absolute'])) 

187 _from = Typed(expected_type=AnchorMarker) 

188 to = Typed(expected_type=AnchorMarker) 

189 

190 sp = _AnchorBase.sp 

191 grpSp = _AnchorBase.grpSp 

192 graphicFrame = _AnchorBase.graphicFrame 

193 cxnSp = _AnchorBase.cxnSp 

194 pic = _AnchorBase.pic 

195 contentPart = _AnchorBase.contentPart 

196 clientData = _AnchorBase.clientData 

197 

198 __elements__ = ('_from', 'to') + _AnchorBase.__elements__ 

199 

200 def __init__(self, 

201 editAs=None, 

202 _from=None, 

203 to=None, 

204 **kw 

205 ): 

206 self.editAs = editAs 

207 if _from is None: 

208 _from = AnchorMarker() 

209 self._from = _from 

210 if to is None: 

211 to = AnchorMarker() 

212 self.to = to 

213 super(TwoCellAnchor, self).__init__(**kw) 

214 

215 

216def _check_anchor(obj): 

217 """ 

218 Check whether an object has an existing Anchor object 

219 If not create a OneCellAnchor using the provided coordinate 

220 """ 

221 anchor = obj.anchor 

222 if not isinstance(anchor, _AnchorBase): 

223 row, col = coordinate_to_tuple(anchor.upper()) 

224 anchor = OneCellAnchor() 

225 anchor._from.row = row -1 

226 anchor._from.col = col -1 

227 if isinstance(obj, ChartBase): 

228 anchor.ext.width = cm_to_EMU(obj.width) 

229 anchor.ext.height = cm_to_EMU(obj.height) 

230 elif isinstance(obj, Image): 

231 anchor.ext.width = pixels_to_EMU(obj.width) 

232 anchor.ext.height = pixels_to_EMU(obj.height) 

233 return anchor 

234 

235 

236class SpreadsheetDrawing(Serialisable): 

237 

238 tagname = "wsDr" 

239 mime_type = "application/vnd.openxmlformats-officedocument.drawing+xml" 

240 _rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" 

241 _path = PartName="/xl/drawings/drawing{0}.xml" 

242 _id = None 

243 

244 twoCellAnchor = Sequence(expected_type=TwoCellAnchor, allow_none=True) 

245 oneCellAnchor = Sequence(expected_type=OneCellAnchor, allow_none=True) 

246 absoluteAnchor = Sequence(expected_type=AbsoluteAnchor, allow_none=True) 

247 

248 __elements__ = ("twoCellAnchor", "oneCellAnchor", "absoluteAnchor") 

249 

250 def __init__(self, 

251 twoCellAnchor=(), 

252 oneCellAnchor=(), 

253 absoluteAnchor=(), 

254 ): 

255 self.twoCellAnchor = twoCellAnchor 

256 self.oneCellAnchor = oneCellAnchor 

257 self.absoluteAnchor = absoluteAnchor 

258 self.charts = [] 

259 self.images = [] 

260 self._rels = [] 

261 

262 

263 def __hash__(self): 

264 """ 

265 Just need to check for identity 

266 """ 

267 return id(self) 

268 

269 

270 def __bool__(self): 

271 return bool(self.charts) or bool(self.images) 

272 

273 

274 

275 def _write(self): 

276 """ 

277 create required structure and the serialise 

278 """ 

279 anchors = [] 

280 for idx, obj in enumerate(self.charts + self.images, 1): 

281 anchor = _check_anchor(obj) 

282 if isinstance(obj, ChartBase): 

283 rel = Relationship(type="chart", Target=obj.path) 

284 anchor.graphicFrame = self._chart_frame(idx) 

285 elif isinstance(obj, Image): 

286 rel = Relationship(type="image", Target=obj.path) 

287 child = anchor.pic or anchor.groupShape and anchor.groupShape.pic 

288 if not child: 

289 anchor.pic = self._picture_frame(idx) 

290 else: 

291 child.blipFill.blip.embed = "rId{0}".format(idx) 

292 

293 anchors.append(anchor) 

294 self._rels.append(rel) 

295 

296 for a in anchors: 

297 if isinstance(a, OneCellAnchor): 

298 self.oneCellAnchor.append(a) 

299 elif isinstance(a, TwoCellAnchor): 

300 self.twoCellAnchor.append(a) 

301 else: 

302 self.absoluteAnchor.append(a) 

303 

304 tree = self.to_tree() 

305 tree.set('xmlns', SHEET_DRAWING_NS) 

306 return tree 

307 

308 

309 def _chart_frame(self, idx): 

310 chart_rel = ChartRelation(f"rId{idx}") 

311 frame = GraphicFrame() 

312 nv = frame.nvGraphicFramePr.cNvPr 

313 nv.id = idx 

314 nv.name = "Chart {0}".format(idx) 

315 frame.graphic.graphicData.chart = chart_rel 

316 return frame 

317 

318 

319 def _picture_frame(self, idx): 

320 pic = PictureFrame() 

321 pic.nvPicPr.cNvPr.descr = "Picture" 

322 pic.nvPicPr.cNvPr.id = idx 

323 pic.nvPicPr.cNvPr.name = "Image {0}".format(idx) 

324 

325 pic.blipFill.blip = Blip() 

326 pic.blipFill.blip.embed = "rId{0}".format(idx) 

327 pic.blipFill.blip.cstate = "print" 

328 

329 pic.spPr.prstGeom = PresetGeometry2D(prst="rect") 

330 pic.spPr.ln = None 

331 return pic 

332 

333 

334 def _write_rels(self): 

335 rels = RelationshipList() 

336 rels.Relationship = self._rels 

337 return rels.to_tree() 

338 

339 

340 @property 

341 def path(self): 

342 return self._path.format(self._id) 

343 

344 

345 @property 

346 def _chart_rels(self): 

347 """ 

348 Get relationship information for each chart and bind anchor to it 

349 """ 

350 rels = [] 

351 anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor 

352 for anchor in anchors: 

353 if anchor.graphicFrame is not None: 

354 graphic = anchor.graphicFrame.graphic 

355 rel = graphic.graphicData.chart 

356 if rel is not None: 

357 rel.anchor = anchor 

358 rel.anchor.graphicFrame = None 

359 rels.append(rel) 

360 return rels 

361 

362 

363 @property 

364 def _blip_rels(self): 

365 """ 

366 Get relationship information for each blip and bind anchor to it 

367 

368 Images that are not part of the XLSX package will be ignored. 

369 """ 

370 rels = [] 

371 anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor 

372 

373 for anchor in anchors: 

374 child = anchor.pic or anchor.groupShape and anchor.groupShape.pic 

375 if child and child.blipFill: 

376 rel = child.blipFill.blip 

377 if rel is not None and rel.embed: 

378 rel.anchor = anchor 

379 rels.append(rel) 

380 

381 return rels