Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/xlsxwriter/drawing.py: 14%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

535 statements  

1############################################################################### 

2# 

3# Drawing - A class for writing the Excel XLSX Drawing file. 

4# 

5# SPDX-License-Identifier: BSD-2-Clause 

6# Copyright 2013-2024, John McNamara, jmcnamara@cpan.org 

7# 

8 

9from . import xmlwriter 

10from .shape import Shape 

11from .utility import get_rgb_color 

12 

13 

14class Drawing(xmlwriter.XMLwriter): 

15 """ 

16 A class for writing the Excel XLSX Drawing file. 

17 

18 

19 """ 

20 

21 ########################################################################### 

22 # 

23 # Public API. 

24 # 

25 ########################################################################### 

26 

27 def __init__(self): 

28 """ 

29 Constructor. 

30 

31 """ 

32 

33 super(Drawing, self).__init__() 

34 

35 self.drawings = [] 

36 self.embedded = 0 

37 self.orientation = 0 

38 

39 ########################################################################### 

40 # 

41 # Private API. 

42 # 

43 ########################################################################### 

44 

45 def _assemble_xml_file(self): 

46 # Assemble and write the XML file. 

47 

48 # Write the XML declaration. 

49 self._xml_declaration() 

50 

51 # Write the xdr:wsDr element. 

52 self._write_drawing_workspace() 

53 

54 if self.embedded: 

55 index = 0 

56 for drawing_properties in self.drawings: 

57 # Write the xdr:twoCellAnchor element. 

58 index += 1 

59 self._write_two_cell_anchor(index, drawing_properties) 

60 

61 else: 

62 # Write the xdr:absoluteAnchor element. 

63 self._write_absolute_anchor(1) 

64 

65 self._xml_end_tag("xdr:wsDr") 

66 

67 # Close the file. 

68 self._xml_close() 

69 

70 def _add_drawing_object(self): 

71 # Add a chart, image or shape sub object to the drawing. 

72 

73 drawing_object = { 

74 "anchor_type": None, 

75 "dimensions": [], 

76 "width": 0, 

77 "height": 0, 

78 "shape": None, 

79 "anchor": None, 

80 "rel_index": 0, 

81 "url_rel_index": 0, 

82 "tip": None, 

83 "name": None, 

84 "description": None, 

85 "decorative": False, 

86 } 

87 

88 self.drawings.append(drawing_object) 

89 

90 return drawing_object 

91 

92 ########################################################################### 

93 # 

94 # XML methods. 

95 # 

96 ########################################################################### 

97 

98 def _write_drawing_workspace(self): 

99 # Write the <xdr:wsDr> element. 

100 schema = "http://schemas.openxmlformats.org/drawingml/" 

101 xmlns_xdr = schema + "2006/spreadsheetDrawing" 

102 xmlns_a = schema + "2006/main" 

103 

104 attributes = [ 

105 ("xmlns:xdr", xmlns_xdr), 

106 ("xmlns:a", xmlns_a), 

107 ] 

108 

109 self._xml_start_tag("xdr:wsDr", attributes) 

110 

111 def _write_two_cell_anchor(self, index, drawing_properties): 

112 # Write the <xdr:twoCellAnchor> element. 

113 anchor_type = drawing_properties["type"] 

114 dimensions = drawing_properties["dimensions"] 

115 col_from = dimensions[0] 

116 row_from = dimensions[1] 

117 col_from_offset = dimensions[2] 

118 row_from_offset = dimensions[3] 

119 col_to = dimensions[4] 

120 row_to = dimensions[5] 

121 col_to_offset = dimensions[6] 

122 row_to_offset = dimensions[7] 

123 col_absolute = dimensions[8] 

124 row_absolute = dimensions[9] 

125 width = drawing_properties["width"] 

126 height = drawing_properties["height"] 

127 shape = drawing_properties["shape"] 

128 anchor = drawing_properties["anchor"] 

129 rel_index = drawing_properties["rel_index"] 

130 url_rel_index = drawing_properties["url_rel_index"] 

131 tip = drawing_properties["tip"] 

132 name = drawing_properties["name"] 

133 description = drawing_properties["description"] 

134 decorative = drawing_properties["decorative"] 

135 

136 attributes = [] 

137 

138 # Add attribute for positioning. 

139 if anchor == 2: 

140 attributes.append(("editAs", "oneCell")) 

141 elif anchor == 3: 

142 attributes.append(("editAs", "absolute")) 

143 

144 # Add editAs attribute for shapes. 

145 if shape and shape.edit_as: 

146 attributes.append(("editAs", shape.edit_as)) 

147 

148 self._xml_start_tag("xdr:twoCellAnchor", attributes) 

149 

150 # Write the xdr:from element. 

151 self._write_from(col_from, row_from, col_from_offset, row_from_offset) 

152 

153 # Write the xdr:from element. 

154 self._write_to(col_to, row_to, col_to_offset, row_to_offset) 

155 

156 if anchor_type == 1: 

157 # Graphic frame. 

158 # Write the xdr:graphicFrame element for charts. 

159 self._write_graphic_frame(index, rel_index, name, description, decorative) 

160 elif anchor_type == 2: 

161 # Write the xdr:pic element. 

162 self._write_pic( 

163 index, 

164 rel_index, 

165 col_absolute, 

166 row_absolute, 

167 width, 

168 height, 

169 shape, 

170 description, 

171 url_rel_index, 

172 tip, 

173 decorative, 

174 ) 

175 else: 

176 # Write the xdr:sp element for shapes. 

177 self._write_sp( 

178 index, 

179 col_absolute, 

180 row_absolute, 

181 width, 

182 height, 

183 shape, 

184 description, 

185 url_rel_index, 

186 tip, 

187 decorative, 

188 ) 

189 

190 # Write the xdr:clientData element. 

191 self._write_client_data() 

192 

193 self._xml_end_tag("xdr:twoCellAnchor") 

194 

195 def _write_absolute_anchor(self, frame_index): 

196 self._xml_start_tag("xdr:absoluteAnchor") 

197 # Write the <xdr:absoluteAnchor> element. 

198 

199 # Different coordinates for horizontal (= 0) and vertical (= 1). 

200 if self.orientation == 0: 

201 # Write the xdr:pos element. 

202 self._write_pos(0, 0) 

203 

204 # Write the xdr:ext element. 

205 self._write_xdr_ext(9308969, 6078325) 

206 

207 else: 

208 # Write the xdr:pos element. 

209 self._write_pos(0, -47625) 

210 

211 # Write the xdr:ext element. 

212 self._write_xdr_ext(6162675, 6124575) 

213 

214 # Write the xdr:graphicFrame element. 

215 self._write_graphic_frame(frame_index, frame_index) 

216 

217 # Write the xdr:clientData element. 

218 self._write_client_data() 

219 

220 self._xml_end_tag("xdr:absoluteAnchor") 

221 

222 def _write_from(self, col, row, col_offset, row_offset): 

223 # Write the <xdr:from> element. 

224 self._xml_start_tag("xdr:from") 

225 

226 # Write the xdr:col element. 

227 self._write_col(col) 

228 

229 # Write the xdr:colOff element. 

230 self._write_col_off(col_offset) 

231 

232 # Write the xdr:row element. 

233 self._write_row(row) 

234 

235 # Write the xdr:rowOff element. 

236 self._write_row_off(row_offset) 

237 

238 self._xml_end_tag("xdr:from") 

239 

240 def _write_to(self, col, row, col_offset, row_offset): 

241 # Write the <xdr:to> element. 

242 self._xml_start_tag("xdr:to") 

243 

244 # Write the xdr:col element. 

245 self._write_col(col) 

246 

247 # Write the xdr:colOff element. 

248 self._write_col_off(col_offset) 

249 

250 # Write the xdr:row element. 

251 self._write_row(row) 

252 

253 # Write the xdr:rowOff element. 

254 self._write_row_off(row_offset) 

255 

256 self._xml_end_tag("xdr:to") 

257 

258 def _write_col(self, data): 

259 # Write the <xdr:col> element. 

260 self._xml_data_element("xdr:col", data) 

261 

262 def _write_col_off(self, data): 

263 # Write the <xdr:colOff> element. 

264 self._xml_data_element("xdr:colOff", data) 

265 

266 def _write_row(self, data): 

267 # Write the <xdr:row> element. 

268 self._xml_data_element("xdr:row", data) 

269 

270 def _write_row_off(self, data): 

271 # Write the <xdr:rowOff> element. 

272 self._xml_data_element("xdr:rowOff", data) 

273 

274 def _write_pos(self, x, y): 

275 # Write the <xdr:pos> element. 

276 

277 attributes = [("x", x), ("y", y)] 

278 

279 self._xml_empty_tag("xdr:pos", attributes) 

280 

281 def _write_xdr_ext(self, cx, cy): 

282 # Write the <xdr:ext> element. 

283 

284 attributes = [("cx", cx), ("cy", cy)] 

285 

286 self._xml_empty_tag("xdr:ext", attributes) 

287 

288 def _write_graphic_frame( 

289 self, index, rel_index, name=None, description=None, decorative=None 

290 ): 

291 # Write the <xdr:graphicFrame> element. 

292 attributes = [("macro", "")] 

293 

294 self._xml_start_tag("xdr:graphicFrame", attributes) 

295 

296 # Write the xdr:nvGraphicFramePr element. 

297 self._write_nv_graphic_frame_pr(index, name, description, decorative) 

298 

299 # Write the xdr:xfrm element. 

300 self._write_xfrm() 

301 

302 # Write the a:graphic element. 

303 self._write_atag_graphic(rel_index) 

304 

305 self._xml_end_tag("xdr:graphicFrame") 

306 

307 def _write_nv_graphic_frame_pr(self, index, name, description, decorative): 

308 # Write the <xdr:nvGraphicFramePr> element. 

309 

310 if not name: 

311 name = "Chart " + str(index) 

312 

313 self._xml_start_tag("xdr:nvGraphicFramePr") 

314 

315 # Write the xdr:cNvPr element. 

316 self._write_c_nv_pr(index + 1, name, description, None, None, decorative) 

317 

318 # Write the xdr:cNvGraphicFramePr element. 

319 self._write_c_nv_graphic_frame_pr() 

320 

321 self._xml_end_tag("xdr:nvGraphicFramePr") 

322 

323 def _write_c_nv_pr(self, index, name, description, url_rel_index, tip, decorative): 

324 # Write the <xdr:cNvPr> element. 

325 attributes = [("id", index), ("name", name)] 

326 

327 # Add description attribute for images. 

328 if description and not decorative: 

329 attributes.append(("descr", description)) 

330 

331 if url_rel_index or decorative: 

332 self._xml_start_tag("xdr:cNvPr", attributes) 

333 

334 if url_rel_index: 

335 self._write_a_hlink_click(url_rel_index, tip) 

336 

337 if decorative: 

338 self._write_decorative() 

339 

340 self._xml_end_tag("xdr:cNvPr") 

341 else: 

342 self._xml_empty_tag("xdr:cNvPr", attributes) 

343 

344 def _write_decorative(self): 

345 self._xml_start_tag("a:extLst") 

346 

347 self._write_uri_ext("{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}") 

348 self._write_a16_creation_id() 

349 self._xml_end_tag("a:ext") 

350 

351 self._write_uri_ext("{C183D7F6-B498-43B3-948B-1728B52AA6E4}") 

352 self._write_adec_decorative() 

353 self._xml_end_tag("a:ext") 

354 

355 self._xml_end_tag("a:extLst") 

356 

357 def _write_uri_ext(self, uri): 

358 # Write the <a:ext> element. 

359 attributes = [("uri", uri)] 

360 

361 self._xml_start_tag("a:ext", attributes) 

362 

363 def _write_adec_decorative(self): 

364 # Write the <adec:decorative> element. 

365 xmlns = "http://schemas.microsoft.com/office/drawing/2017/decorative" 

366 val = "1" 

367 

368 attributes = [ 

369 ("xmlns:adec", xmlns), 

370 ("val", val), 

371 ] 

372 

373 self._xml_empty_tag("adec:decorative", attributes) 

374 

375 def _write_a16_creation_id(self): 

376 # Write the <a16:creationId> element. 

377 

378 xmlns_a_16 = "http://schemas.microsoft.com/office/drawing/2014/main" 

379 creation_id = "{00000000-0008-0000-0000-000002000000}" 

380 

381 attributes = [ 

382 ("xmlns:a16", xmlns_a_16), 

383 ("id", creation_id), 

384 ] 

385 

386 self._xml_empty_tag("a16:creationId", attributes) 

387 

388 def _write_a_hlink_click(self, rel_index, tip): 

389 # Write the <a:hlinkClick> element. 

390 schema = "http://schemas.openxmlformats.org/officeDocument/" 

391 xmlns_r = schema + "2006/relationships" 

392 

393 attributes = [ 

394 ("xmlns:r", xmlns_r), 

395 ("r:id", "rId" + str(rel_index)), 

396 ] 

397 

398 if tip: 

399 attributes.append(("tooltip", tip)) 

400 

401 self._xml_empty_tag("a:hlinkClick", attributes) 

402 

403 def _write_c_nv_graphic_frame_pr(self): 

404 # Write the <xdr:cNvGraphicFramePr> element. 

405 if self.embedded: 

406 self._xml_empty_tag("xdr:cNvGraphicFramePr") 

407 else: 

408 self._xml_start_tag("xdr:cNvGraphicFramePr") 

409 

410 # Write the a:graphicFrameLocks element. 

411 self._write_a_graphic_frame_locks() 

412 

413 self._xml_end_tag("xdr:cNvGraphicFramePr") 

414 

415 def _write_a_graphic_frame_locks(self): 

416 # Write the <a:graphicFrameLocks> element. 

417 attributes = [("noGrp", 1)] 

418 

419 self._xml_empty_tag("a:graphicFrameLocks", attributes) 

420 

421 def _write_xfrm(self): 

422 # Write the <xdr:xfrm> element. 

423 self._xml_start_tag("xdr:xfrm") 

424 

425 # Write the xfrmOffset element. 

426 self._write_xfrm_offset() 

427 

428 # Write the xfrmOffset element. 

429 self._write_xfrm_extension() 

430 

431 self._xml_end_tag("xdr:xfrm") 

432 

433 def _write_xfrm_offset(self): 

434 # Write the <a:off> xfrm sub-element. 

435 

436 attributes = [ 

437 ("x", 0), 

438 ("y", 0), 

439 ] 

440 

441 self._xml_empty_tag("a:off", attributes) 

442 

443 def _write_xfrm_extension(self): 

444 # Write the <a:ext> xfrm sub-element. 

445 

446 attributes = [ 

447 ("cx", 0), 

448 ("cy", 0), 

449 ] 

450 

451 self._xml_empty_tag("a:ext", attributes) 

452 

453 def _write_atag_graphic(self, index): 

454 # Write the <a:graphic> element. 

455 self._xml_start_tag("a:graphic") 

456 

457 # Write the a:graphicData element. 

458 self._write_atag_graphic_data(index) 

459 

460 self._xml_end_tag("a:graphic") 

461 

462 def _write_atag_graphic_data(self, index): 

463 # Write the <a:graphicData> element. 

464 uri = "http://schemas.openxmlformats.org/drawingml/2006/chart" 

465 

466 attributes = [ 

467 ( 

468 "uri", 

469 uri, 

470 ) 

471 ] 

472 

473 self._xml_start_tag("a:graphicData", attributes) 

474 

475 # Write the c:chart element. 

476 self._write_c_chart("rId" + str(index)) 

477 

478 self._xml_end_tag("a:graphicData") 

479 

480 def _write_c_chart(self, r_id): 

481 # Write the <c:chart> element. 

482 

483 schema = "http://schemas.openxmlformats.org/" 

484 xmlns_c = schema + "drawingml/2006/chart" 

485 xmlns_r = schema + "officeDocument/2006/relationships" 

486 

487 attributes = [ 

488 ("xmlns:c", xmlns_c), 

489 ("xmlns:r", xmlns_r), 

490 ("r:id", r_id), 

491 ] 

492 

493 self._xml_empty_tag("c:chart", attributes) 

494 

495 def _write_client_data(self): 

496 # Write the <xdr:clientData> element. 

497 self._xml_empty_tag("xdr:clientData") 

498 

499 def _write_sp( 

500 self, 

501 index, 

502 col_absolute, 

503 row_absolute, 

504 width, 

505 height, 

506 shape, 

507 description, 

508 url_rel_index, 

509 tip, 

510 decorative, 

511 ): 

512 # Write the <xdr:sp> element. 

513 

514 if shape and shape.connect: 

515 attributes = [("macro", "")] 

516 self._xml_start_tag("xdr:cxnSp", attributes) 

517 

518 # Write the xdr:nvCxnSpPr element. 

519 self._write_nv_cxn_sp_pr(index, shape) 

520 

521 # Write the xdr:spPr element. 

522 self._write_xdr_sp_pr( 

523 index, col_absolute, row_absolute, width, height, shape 

524 ) 

525 

526 self._xml_end_tag("xdr:cxnSp") 

527 else: 

528 # Add attribute for shapes. 

529 attributes = [("macro", ""), ("textlink", shape.textlink)] 

530 

531 self._xml_start_tag("xdr:sp", attributes) 

532 

533 # Write the xdr:nvSpPr element. 

534 self._write_nv_sp_pr( 

535 index, shape, url_rel_index, tip, description, decorative 

536 ) 

537 

538 # Write the xdr:spPr element. 

539 self._write_xdr_sp_pr( 

540 index, col_absolute, row_absolute, width, height, shape 

541 ) 

542 

543 # Write the xdr:style element. 

544 self._write_style() 

545 

546 # Write the xdr:txBody element. 

547 if shape.text is not None: 

548 self._write_tx_body(col_absolute, row_absolute, width, height, shape) 

549 

550 self._xml_end_tag("xdr:sp") 

551 

552 def _write_nv_cxn_sp_pr(self, index, shape): 

553 # Write the <xdr:nvCxnSpPr> element. 

554 self._xml_start_tag("xdr:nvCxnSpPr") 

555 

556 name = shape.name + " " + str(index) 

557 if name is not None: 

558 self._write_c_nv_pr(index, name, None, None, None, None) 

559 

560 self._xml_start_tag("xdr:cNvCxnSpPr") 

561 

562 attributes = [("noChangeShapeType", "1")] 

563 self._xml_empty_tag("a:cxnSpLocks", attributes) 

564 

565 if shape.start: 

566 attributes = [("id", shape.start), ("idx", shape.start_index)] 

567 self._xml_empty_tag("a:stCxn", attributes) 

568 

569 if shape.end: 

570 attributes = [("id", shape.end), ("idx", shape.end_index)] 

571 self._xml_empty_tag("a:endCxn", attributes) 

572 

573 self._xml_end_tag("xdr:cNvCxnSpPr") 

574 self._xml_end_tag("xdr:nvCxnSpPr") 

575 

576 def _write_nv_sp_pr( 

577 self, index, shape, url_rel_index, tip, description, decorative 

578 ): 

579 # Write the <xdr:NvSpPr> element. 

580 attributes = [] 

581 

582 self._xml_start_tag("xdr:nvSpPr") 

583 

584 name = shape.name + " " + str(index) 

585 

586 self._write_c_nv_pr( 

587 index + 1, name, description, url_rel_index, tip, decorative 

588 ) 

589 

590 if shape.name == "TextBox": 

591 attributes = [("txBox", 1)] 

592 

593 self._xml_empty_tag("xdr:cNvSpPr", attributes) 

594 

595 self._xml_end_tag("xdr:nvSpPr") 

596 

597 def _write_pic( 

598 self, 

599 index, 

600 rel_index, 

601 col_absolute, 

602 row_absolute, 

603 width, 

604 height, 

605 shape, 

606 description, 

607 url_rel_index, 

608 tip, 

609 decorative, 

610 ): 

611 # Write the <xdr:pic> element. 

612 self._xml_start_tag("xdr:pic") 

613 

614 # Write the xdr:nvPicPr element. 

615 self._write_nv_pic_pr( 

616 index, rel_index, description, url_rel_index, tip, decorative 

617 ) 

618 # Write the xdr:blipFill element. 

619 self._write_blip_fill(rel_index) 

620 

621 # Write the xdr:spPr element. 

622 self._write_sp_pr(col_absolute, row_absolute, width, height, shape) 

623 

624 self._xml_end_tag("xdr:pic") 

625 

626 def _write_nv_pic_pr( 

627 self, index, rel_index, description, url_rel_index, tip, decorative 

628 ): 

629 # Write the <xdr:nvPicPr> element. 

630 self._xml_start_tag("xdr:nvPicPr") 

631 

632 # Write the xdr:cNvPr element. 

633 self._write_c_nv_pr( 

634 index + 1, 

635 "Picture " + str(index), 

636 description, 

637 url_rel_index, 

638 tip, 

639 decorative, 

640 ) 

641 

642 # Write the xdr:cNvPicPr element. 

643 self._write_c_nv_pic_pr() 

644 

645 self._xml_end_tag("xdr:nvPicPr") 

646 

647 def _write_c_nv_pic_pr(self): 

648 # Write the <xdr:cNvPicPr> element. 

649 self._xml_start_tag("xdr:cNvPicPr") 

650 

651 # Write the a:picLocks element. 

652 self._write_a_pic_locks() 

653 

654 self._xml_end_tag("xdr:cNvPicPr") 

655 

656 def _write_a_pic_locks(self): 

657 # Write the <a:picLocks> element. 

658 attributes = [("noChangeAspect", 1)] 

659 

660 self._xml_empty_tag("a:picLocks", attributes) 

661 

662 def _write_blip_fill(self, index): 

663 # Write the <xdr:blipFill> element. 

664 self._xml_start_tag("xdr:blipFill") 

665 

666 # Write the a:blip element. 

667 self._write_a_blip(index) 

668 

669 # Write the a:stretch element. 

670 self._write_a_stretch() 

671 

672 self._xml_end_tag("xdr:blipFill") 

673 

674 def _write_a_blip(self, index): 

675 # Write the <a:blip> element. 

676 schema = "http://schemas.openxmlformats.org/officeDocument/" 

677 xmlns_r = schema + "2006/relationships" 

678 r_embed = "rId" + str(index) 

679 

680 attributes = [("xmlns:r", xmlns_r), ("r:embed", r_embed)] 

681 

682 self._xml_empty_tag("a:blip", attributes) 

683 

684 def _write_a_stretch(self): 

685 # Write the <a:stretch> element. 

686 self._xml_start_tag("a:stretch") 

687 

688 # Write the a:fillRect element. 

689 self._write_a_fill_rect() 

690 

691 self._xml_end_tag("a:stretch") 

692 

693 def _write_a_fill_rect(self): 

694 # Write the <a:fillRect> element. 

695 self._xml_empty_tag("a:fillRect") 

696 

697 def _write_sp_pr(self, col_absolute, row_absolute, width, height, shape=None): 

698 # Write the <xdr:spPr> element, for charts. 

699 

700 self._xml_start_tag("xdr:spPr") 

701 

702 # Write the a:xfrm element. 

703 self._write_a_xfrm(col_absolute, row_absolute, width, height) 

704 

705 # Write the a:prstGeom element. 

706 self._write_a_prst_geom(shape) 

707 

708 self._xml_end_tag("xdr:spPr") 

709 

710 def _write_xdr_sp_pr(self, index, col_absolute, row_absolute, width, height, shape): 

711 # Write the <xdr:spPr> element for shapes. 

712 self._xml_start_tag("xdr:spPr") 

713 

714 # Write the a:xfrm element. 

715 self._write_a_xfrm(col_absolute, row_absolute, width, height, shape) 

716 

717 # Write the a:prstGeom element. 

718 self._write_a_prst_geom(shape) 

719 

720 if shape.fill: 

721 if not shape.fill["defined"]: 

722 # Write the a:solidFill element. 

723 self._write_a_solid_fill_scheme("lt1") 

724 elif "none" in shape.fill: 

725 # Write the a:noFill element. 

726 self._xml_empty_tag("a:noFill") 

727 elif "color" in shape.fill: 

728 # Write the a:solidFill element. 

729 self._write_a_solid_fill(get_rgb_color(shape.fill["color"])) 

730 

731 if shape.gradient: 

732 # Write the a:gradFill element. 

733 self._write_a_grad_fill(shape.gradient) 

734 

735 # Write the a:ln element. 

736 self._write_a_ln(shape.line) 

737 

738 self._xml_end_tag("xdr:spPr") 

739 

740 def _write_a_xfrm(self, col_absolute, row_absolute, width, height, shape=None): 

741 # Write the <a:xfrm> element. 

742 attributes = [] 

743 

744 if shape: 

745 if shape.rotation: 

746 rotation = shape.rotation 

747 rotation *= 60000 

748 attributes.append(("rot", rotation)) 

749 

750 if shape.flip_h: 

751 attributes.append(("flipH", 1)) 

752 if shape.flip_v: 

753 attributes.append(("flipV", 1)) 

754 

755 self._xml_start_tag("a:xfrm", attributes) 

756 

757 # Write the a:off element. 

758 self._write_a_off(col_absolute, row_absolute) 

759 

760 # Write the a:ext element. 

761 self._write_a_ext(width, height) 

762 

763 self._xml_end_tag("a:xfrm") 

764 

765 def _write_a_off(self, x, y): 

766 # Write the <a:off> element. 

767 attributes = [ 

768 ("x", x), 

769 ("y", y), 

770 ] 

771 

772 self._xml_empty_tag("a:off", attributes) 

773 

774 def _write_a_ext(self, cx, cy): 

775 # Write the <a:ext> element. 

776 attributes = [ 

777 ("cx", cx), 

778 ("cy", cy), 

779 ] 

780 

781 self._xml_empty_tag("a:ext", attributes) 

782 

783 def _write_a_prst_geom(self, shape=None): 

784 # Write the <a:prstGeom> element. 

785 attributes = [("prst", "rect")] 

786 

787 self._xml_start_tag("a:prstGeom", attributes) 

788 

789 # Write the a:avLst element. 

790 self._write_a_av_lst(shape) 

791 

792 self._xml_end_tag("a:prstGeom") 

793 

794 def _write_a_av_lst(self, shape=None): 

795 # Write the <a:avLst> element. 

796 adjustments = [] 

797 

798 if shape and shape.adjustments: 

799 adjustments = shape.adjustments 

800 

801 if adjustments: 

802 self._xml_start_tag("a:avLst") 

803 

804 i = 0 

805 for adj in adjustments: 

806 i += 1 

807 # Only connectors have multiple adjustments. 

808 if shape.connect: 

809 suffix = i 

810 else: 

811 suffix = "" 

812 

813 # Scale Adjustments: 100,000 = 100%. 

814 adj_int = str(int(adj * 1000)) 

815 

816 attributes = [("name", "adj" + suffix), ("fmla", "val" + adj_int)] 

817 

818 self._xml_empty_tag("a:gd", attributes) 

819 

820 self._xml_end_tag("a:avLst") 

821 else: 

822 self._xml_empty_tag("a:avLst") 

823 

824 def _write_a_solid_fill(self, rgb): 

825 # Write the <a:solidFill> element. 

826 if rgb is None: 

827 rgb = "FFFFFF" 

828 

829 self._xml_start_tag("a:solidFill") 

830 

831 # Write the a:srgbClr element. 

832 self._write_a_srgb_clr(rgb) 

833 

834 self._xml_end_tag("a:solidFill") 

835 

836 def _write_a_solid_fill_scheme(self, color, shade=None): 

837 attributes = [("val", color)] 

838 

839 self._xml_start_tag("a:solidFill") 

840 

841 if shade: 

842 self._xml_start_tag("a:schemeClr", attributes) 

843 self._write_a_shade(shade) 

844 self._xml_end_tag("a:schemeClr") 

845 else: 

846 self._xml_empty_tag("a:schemeClr", attributes) 

847 

848 self._xml_end_tag("a:solidFill") 

849 

850 def _write_a_ln(self, line): 

851 # Write the <a:ln> element. 

852 width = line.get("width", 0.75) 

853 

854 # Round width to nearest 0.25, like Excel. 

855 width = int((width + 0.125) * 4) / 4.0 

856 

857 # Convert to internal units. 

858 width = int(0.5 + (12700 * width)) 

859 

860 attributes = [("w", width), ("cmpd", "sng")] 

861 

862 self._xml_start_tag("a:ln", attributes) 

863 

864 if "none" in line: 

865 # Write the a:noFill element. 

866 self._xml_empty_tag("a:noFill") 

867 

868 elif "color" in line: 

869 # Write the a:solidFill element. 

870 self._write_a_solid_fill(get_rgb_color(line["color"])) 

871 

872 else: 

873 # Write the a:solidFill element. 

874 self._write_a_solid_fill_scheme("lt1", "50000") 

875 

876 # Write the line/dash type. 

877 line_type = line.get("dash_type") 

878 if line_type: 

879 # Write the a:prstDash element. 

880 self._write_a_prst_dash(line_type) 

881 

882 self._xml_end_tag("a:ln") 

883 

884 def _write_tx_body(self, col_absolute, row_absolute, width, height, shape): 

885 # Write the <xdr:txBody> element. 

886 attributes = [] 

887 

888 if shape.text_rotation != 0: 

889 if shape.text_rotation == 90: 

890 attributes.append(("vert", "vert270")) 

891 if shape.text_rotation == -90: 

892 attributes.append(("vert", "vert")) 

893 if shape.text_rotation == 270: 

894 attributes.append(("vert", "wordArtVert")) 

895 if shape.text_rotation == 271: 

896 attributes.append(("vert", "eaVert")) 

897 

898 attributes.append(("wrap", "square")) 

899 attributes.append(("rtlCol", "0")) 

900 

901 if not shape.align["defined"]: 

902 attributes.append(("anchor", "t")) 

903 else: 

904 if "vertical" in shape.align: 

905 align = shape.align["vertical"] 

906 if align == "top": 

907 attributes.append(("anchor", "t")) 

908 elif align == "middle": 

909 attributes.append(("anchor", "ctr")) 

910 elif align == "bottom": 

911 attributes.append(("anchor", "b")) 

912 else: 

913 attributes.append(("anchor", "t")) 

914 

915 if "horizontal" in shape.align: 

916 align = shape.align["horizontal"] 

917 if align == "center": 

918 attributes.append(("anchorCtr", "1")) 

919 else: 

920 attributes.append(("anchorCtr", "0")) 

921 

922 self._xml_start_tag("xdr:txBody") 

923 self._xml_empty_tag("a:bodyPr", attributes) 

924 self._xml_empty_tag("a:lstStyle") 

925 

926 lines = shape.text.split("\n") 

927 

928 # Set the font attributes. 

929 font = shape.font 

930 style_attrs = Shape._get_font_style_attributes(font) 

931 latin_attrs = Shape._get_font_latin_attributes(font) 

932 style_attrs.insert(0, ("lang", font["lang"])) 

933 

934 if shape.textlink != "": 

935 attributes = [ 

936 ("id", "{B8ADDEFE-BF52-4FD4-8C5D-6B85EF6FF707}"), 

937 ("type", "TxLink"), 

938 ] 

939 

940 self._xml_start_tag("a:p") 

941 self._xml_start_tag("a:fld", attributes) 

942 

943 self._write_font_run(font, style_attrs, latin_attrs, "a:rPr") 

944 

945 self._xml_data_element("a:t", shape.text) 

946 self._xml_end_tag("a:fld") 

947 

948 self._write_font_run(font, style_attrs, latin_attrs, "a:endParaRPr") 

949 

950 self._xml_end_tag("a:p") 

951 else: 

952 for line in lines: 

953 self._xml_start_tag("a:p") 

954 

955 if line == "": 

956 self._write_font_run(font, style_attrs, latin_attrs, "a:endParaRPr") 

957 self._xml_end_tag("a:p") 

958 continue 

959 elif "text" in shape.align: 

960 if shape.align["text"] == "left": 

961 self._xml_empty_tag("a:pPr", [("algn", "l")]) 

962 if shape.align["text"] == "center": 

963 self._xml_empty_tag("a:pPr", [("algn", "ctr")]) 

964 if shape.align["text"] == "right": 

965 self._xml_empty_tag("a:pPr", [("algn", "r")]) 

966 

967 self._xml_start_tag("a:r") 

968 

969 self._write_font_run(font, style_attrs, latin_attrs, "a:rPr") 

970 

971 self._xml_data_element("a:t", line) 

972 

973 self._xml_end_tag("a:r") 

974 self._xml_end_tag("a:p") 

975 

976 self._xml_end_tag("xdr:txBody") 

977 

978 def _write_font_run(self, font, style_attrs, latin_attrs, run_type): 

979 # Write a:rPr or a:endParaRPr. 

980 if font.get("color") is not None: 

981 has_color = True 

982 else: 

983 has_color = False 

984 

985 if latin_attrs or has_color: 

986 self._xml_start_tag(run_type, style_attrs) 

987 

988 if has_color: 

989 self._write_a_solid_fill(get_rgb_color(font["color"])) 

990 

991 if latin_attrs: 

992 self._write_a_latin(latin_attrs) 

993 self._write_a_cs(latin_attrs) 

994 

995 self._xml_end_tag(run_type) 

996 else: 

997 self._xml_empty_tag(run_type, style_attrs) 

998 

999 def _write_style(self): 

1000 # Write the <xdr:style> element. 

1001 self._xml_start_tag("xdr:style") 

1002 

1003 # Write the a:lnRef element. 

1004 self._write_a_ln_ref() 

1005 

1006 # Write the a:fillRef element. 

1007 self._write_a_fill_ref() 

1008 

1009 # Write the a:effectRef element. 

1010 self._write_a_effect_ref() 

1011 

1012 # Write the a:fontRef element. 

1013 self._write_a_font_ref() 

1014 

1015 self._xml_end_tag("xdr:style") 

1016 

1017 def _write_a_ln_ref(self): 

1018 # Write the <a:lnRef> element. 

1019 attributes = [("idx", "0")] 

1020 

1021 self._xml_start_tag("a:lnRef", attributes) 

1022 

1023 # Write the a:scrgbClr element. 

1024 self._write_a_scrgb_clr() 

1025 

1026 self._xml_end_tag("a:lnRef") 

1027 

1028 def _write_a_fill_ref(self): 

1029 # Write the <a:fillRef> element. 

1030 attributes = [("idx", "0")] 

1031 

1032 self._xml_start_tag("a:fillRef", attributes) 

1033 

1034 # Write the a:scrgbClr element. 

1035 self._write_a_scrgb_clr() 

1036 

1037 self._xml_end_tag("a:fillRef") 

1038 

1039 def _write_a_effect_ref(self): 

1040 # Write the <a:effectRef> element. 

1041 attributes = [("idx", "0")] 

1042 

1043 self._xml_start_tag("a:effectRef", attributes) 

1044 

1045 # Write the a:scrgbClr element. 

1046 self._write_a_scrgb_clr() 

1047 

1048 self._xml_end_tag("a:effectRef") 

1049 

1050 def _write_a_scrgb_clr(self): 

1051 # Write the <a:scrgbClr> element. 

1052 

1053 attributes = [ 

1054 ("r", "0"), 

1055 ("g", "0"), 

1056 ("b", "0"), 

1057 ] 

1058 

1059 self._xml_empty_tag("a:scrgbClr", attributes) 

1060 

1061 def _write_a_font_ref(self): 

1062 # Write the <a:fontRef> element. 

1063 attributes = [("idx", "minor")] 

1064 

1065 self._xml_start_tag("a:fontRef", attributes) 

1066 

1067 # Write the a:schemeClr element. 

1068 self._write_a_scheme_clr("dk1") 

1069 

1070 self._xml_end_tag("a:fontRef") 

1071 

1072 def _write_a_scheme_clr(self, val): 

1073 # Write the <a:schemeClr> element. 

1074 attributes = [("val", val)] 

1075 

1076 self._xml_empty_tag("a:schemeClr", attributes) 

1077 

1078 def _write_a_shade(self, shade): 

1079 # Write the <a:shade> element. 

1080 attributes = [("val", shade)] 

1081 

1082 self._xml_empty_tag("a:shade", attributes) 

1083 

1084 def _write_a_prst_dash(self, val): 

1085 # Write the <a:prstDash> element. 

1086 

1087 attributes = [("val", val)] 

1088 

1089 self._xml_empty_tag("a:prstDash", attributes) 

1090 

1091 def _write_a_grad_fill(self, gradient): 

1092 # Write the <a:gradFill> element. 

1093 

1094 attributes = [("flip", "none"), ("rotWithShape", "1")] 

1095 

1096 if gradient["type"] == "linear": 

1097 attributes = [] 

1098 

1099 self._xml_start_tag("a:gradFill", attributes) 

1100 

1101 # Write the a:gsLst element. 

1102 self._write_a_gs_lst(gradient) 

1103 

1104 if gradient["type"] == "linear": 

1105 # Write the a:lin element. 

1106 self._write_a_lin(gradient["angle"]) 

1107 else: 

1108 # Write the a:path element. 

1109 self._write_a_path(gradient["type"]) 

1110 

1111 # Write the a:tileRect element. 

1112 self._write_a_tile_rect(gradient["type"]) 

1113 

1114 self._xml_end_tag("a:gradFill") 

1115 

1116 def _write_a_gs_lst(self, gradient): 

1117 # Write the <a:gsLst> element. 

1118 positions = gradient["positions"] 

1119 colors = gradient["colors"] 

1120 

1121 self._xml_start_tag("a:gsLst") 

1122 

1123 for i in range(len(colors)): 

1124 pos = int(positions[i] * 1000) 

1125 attributes = [("pos", pos)] 

1126 self._xml_start_tag("a:gs", attributes) 

1127 

1128 # Write the a:srgbClr element. 

1129 color = get_rgb_color(colors[i]) 

1130 self._write_a_srgb_clr(color) 

1131 

1132 self._xml_end_tag("a:gs") 

1133 

1134 self._xml_end_tag("a:gsLst") 

1135 

1136 def _write_a_lin(self, angle): 

1137 # Write the <a:lin> element. 

1138 

1139 angle = int(60000 * angle) 

1140 

1141 attributes = [ 

1142 ("ang", angle), 

1143 ("scaled", "0"), 

1144 ] 

1145 

1146 self._xml_empty_tag("a:lin", attributes) 

1147 

1148 def _write_a_path(self, gradient_type): 

1149 # Write the <a:path> element. 

1150 

1151 attributes = [("path", gradient_type)] 

1152 

1153 self._xml_start_tag("a:path", attributes) 

1154 

1155 # Write the a:fillToRect element. 

1156 self._write_a_fill_to_rect(gradient_type) 

1157 

1158 self._xml_end_tag("a:path") 

1159 

1160 def _write_a_fill_to_rect(self, gradient_type): 

1161 # Write the <a:fillToRect> element. 

1162 

1163 if gradient_type == "shape": 

1164 attributes = [ 

1165 ("l", "50000"), 

1166 ("t", "50000"), 

1167 ("r", "50000"), 

1168 ("b", "50000"), 

1169 ] 

1170 else: 

1171 attributes = [ 

1172 ("l", "100000"), 

1173 ("t", "100000"), 

1174 ] 

1175 

1176 self._xml_empty_tag("a:fillToRect", attributes) 

1177 

1178 def _write_a_tile_rect(self, gradient_type): 

1179 # Write the <a:tileRect> element. 

1180 

1181 if gradient_type == "shape": 

1182 attributes = [] 

1183 else: 

1184 attributes = [ 

1185 ("r", "-100000"), 

1186 ("b", "-100000"), 

1187 ] 

1188 

1189 self._xml_empty_tag("a:tileRect", attributes) 

1190 

1191 def _write_a_srgb_clr(self, val): 

1192 # Write the <a:srgbClr> element. 

1193 

1194 attributes = [("val", val)] 

1195 

1196 self._xml_empty_tag("a:srgbClr", attributes) 

1197 

1198 def _write_a_latin(self, attributes): 

1199 # Write the <a:latin> element. 

1200 self._xml_empty_tag("a:latin", attributes) 

1201 

1202 def _write_a_cs(self, attributes): 

1203 # Write the <a:latin> element. 

1204 self._xml_empty_tag("a:cs", attributes)