Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/xlsxwriter/styles.py: 56%

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

372 statements  

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

2# 

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

4# 

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

6# 

7# Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org 

8# 

9 

10# Package imports. 

11from . import xmlwriter 

12 

13 

14class Styles(xmlwriter.XMLwriter): 

15 """ 

16 A class for writing the Excel XLSX Styles 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().__init__() 

34 

35 self.xf_formats = [] 

36 self.palette = [] 

37 self.font_count = 0 

38 self.num_formats = [] 

39 self.border_count = 0 

40 self.fill_count = 0 

41 self.custom_colors = [] 

42 self.dxf_formats = [] 

43 self.has_hyperlink = False 

44 self.hyperlink_font_id = 0 

45 self.has_comments = False 

46 

47 ########################################################################### 

48 # 

49 # Private API. 

50 # 

51 ########################################################################### 

52 

53 def _assemble_xml_file(self): 

54 # Assemble and write the XML file. 

55 

56 # Write the XML declaration. 

57 self._xml_declaration() 

58 

59 # Add the style sheet. 

60 self._write_style_sheet() 

61 

62 # Write the number formats. 

63 self._write_num_fmts() 

64 

65 # Write the fonts. 

66 self._write_fonts() 

67 

68 # Write the fills. 

69 self._write_fills() 

70 

71 # Write the borders element. 

72 self._write_borders() 

73 

74 # Write the cellStyleXfs element. 

75 self._write_cell_style_xfs() 

76 

77 # Write the cellXfs element. 

78 self._write_cell_xfs() 

79 

80 # Write the cellStyles element. 

81 self._write_cell_styles() 

82 

83 # Write the dxfs element. 

84 self._write_dxfs() 

85 

86 # Write the tableStyles element. 

87 self._write_table_styles() 

88 

89 # Write the colors element. 

90 self._write_colors() 

91 

92 # Close the style sheet tag. 

93 self._xml_end_tag("styleSheet") 

94 

95 # Close the file. 

96 self._xml_close() 

97 

98 def _set_style_properties(self, properties): 

99 # Pass in the Format objects and other properties used in the styles. 

100 

101 self.xf_formats = properties[0] 

102 self.palette = properties[1] 

103 self.font_count = properties[2] 

104 self.num_formats = properties[3] 

105 self.border_count = properties[4] 

106 self.fill_count = properties[5] 

107 self.custom_colors = properties[6] 

108 self.dxf_formats = properties[7] 

109 self.has_comments = properties[8] 

110 

111 ########################################################################### 

112 # 

113 # XML methods. 

114 # 

115 ########################################################################### 

116 

117 def _write_style_sheet(self): 

118 # Write the <styleSheet> element. 

119 xmlns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main" 

120 

121 attributes = [("xmlns", xmlns)] 

122 self._xml_start_tag("styleSheet", attributes) 

123 

124 def _write_num_fmts(self): 

125 # Write the <numFmts> element. 

126 if not self.num_formats: 

127 return 

128 

129 attributes = [("count", len(self.num_formats))] 

130 self._xml_start_tag("numFmts", attributes) 

131 

132 # Write the numFmts elements. 

133 for index, num_format in enumerate(self.num_formats, 164): 

134 self._write_num_fmt(index, num_format) 

135 

136 self._xml_end_tag("numFmts") 

137 

138 def _write_num_fmt(self, num_fmt_id, format_code): 

139 # Write the <numFmt> element. 

140 format_codes = { 

141 0: "General", 

142 1: "0", 

143 2: "0.00", 

144 3: "#,##0", 

145 4: "#,##0.00", 

146 5: "($#,##0_);($#,##0)", 

147 6: "($#,##0_);[Red]($#,##0)", 

148 7: "($#,##0.00_);($#,##0.00)", 

149 8: "($#,##0.00_);[Red]($#,##0.00)", 

150 9: "0%", 

151 10: "0.00%", 

152 11: "0.00E+00", 

153 12: "# ?/?", 

154 13: "# ??/??", 

155 14: "m/d/yy", 

156 15: "d-mmm-yy", 

157 16: "d-mmm", 

158 17: "mmm-yy", 

159 18: "h:mm AM/PM", 

160 19: "h:mm:ss AM/PM", 

161 20: "h:mm", 

162 21: "h:mm:ss", 

163 22: "m/d/yy h:mm", 

164 37: "(#,##0_);(#,##0)", 

165 38: "(#,##0_);[Red](#,##0)", 

166 39: "(#,##0.00_);(#,##0.00)", 

167 40: "(#,##0.00_);[Red](#,##0.00)", 

168 41: '_(* #,##0_);_(* (#,##0);_(* "-"_);_(_)', 

169 42: '_($* #,##0_);_($* (#,##0);_($* "-"_);_(_)', 

170 43: '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(_)', 

171 44: '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(_)', 

172 45: "mm:ss", 

173 46: "[h]:mm:ss", 

174 47: "mm:ss.0", 

175 48: "##0.0E+0", 

176 49: "@", 

177 } 

178 

179 # Set the format code for built-in number formats. 

180 if num_fmt_id < 164: 

181 format_code = format_codes.get(num_fmt_id, "General") 

182 

183 attributes = [ 

184 ("numFmtId", num_fmt_id), 

185 ("formatCode", format_code), 

186 ] 

187 

188 self._xml_empty_tag("numFmt", attributes) 

189 

190 def _write_fonts(self): 

191 # Write the <fonts> element. 

192 if self.has_comments: 

193 # Add extra font for comments. 

194 attributes = [("count", self.font_count + 1)] 

195 else: 

196 attributes = [("count", self.font_count)] 

197 

198 self._xml_start_tag("fonts", attributes) 

199 

200 # Write the font elements for xf_format objects that have them. 

201 for xf_format in self.xf_formats: 

202 if xf_format.has_font: 

203 self._write_font(xf_format) 

204 

205 if self.has_comments: 

206 self._write_comment_font() 

207 

208 self._xml_end_tag("fonts") 

209 

210 def _write_font(self, xf_format, is_dxf_format=False): 

211 # Write the <font> element. 

212 self._xml_start_tag("font") 

213 

214 # The condense and extend elements are mainly used in dxf formats. 

215 if xf_format.font_condense: 

216 self._write_condense() 

217 

218 if xf_format.font_extend: 

219 self._write_extend() 

220 

221 if xf_format.bold: 

222 self._xml_empty_tag("b") 

223 

224 if xf_format.italic: 

225 self._xml_empty_tag("i") 

226 

227 if xf_format.font_strikeout: 

228 self._xml_empty_tag("strike") 

229 

230 if xf_format.font_outline: 

231 self._xml_empty_tag("outline") 

232 

233 if xf_format.font_shadow: 

234 self._xml_empty_tag("shadow") 

235 

236 # Handle the underline variants. 

237 if xf_format.underline: 

238 self._write_underline(xf_format.underline) 

239 

240 if xf_format.font_script == 1: 

241 self._write_vert_align("superscript") 

242 

243 if xf_format.font_script == 2: 

244 self._write_vert_align("subscript") 

245 

246 if not is_dxf_format: 

247 self._xml_empty_tag("sz", [("val", xf_format.font_size)]) 

248 

249 if xf_format.theme == -1: 

250 # Ignore for excel2003_style. 

251 pass 

252 elif xf_format.theme: 

253 self._write_color([("theme", xf_format.theme)]) 

254 elif xf_format.color_indexed: 

255 self._write_color([("indexed", xf_format.color_indexed)]) 

256 elif xf_format.font_color: 

257 color = xf_format.font_color 

258 if not color._is_automatic: 

259 self._write_color(color._attributes()) 

260 elif not is_dxf_format: 

261 self._write_color([("theme", 1)]) 

262 

263 if not is_dxf_format: 

264 self._xml_empty_tag("name", [("val", xf_format.font_name)]) 

265 

266 if xf_format.font_family: 

267 self._xml_empty_tag("family", [("val", xf_format.font_family)]) 

268 

269 if xf_format.font_charset: 

270 self._xml_empty_tag("charset", [("val", xf_format.font_charset)]) 

271 

272 if xf_format.font_name == "Calibri" and not xf_format.hyperlink: 

273 self._xml_empty_tag("scheme", [("val", xf_format.font_scheme)]) 

274 

275 if xf_format.hyperlink: 

276 self.has_hyperlink = True 

277 if self.hyperlink_font_id == 0: 

278 self.hyperlink_font_id = xf_format.font_index 

279 

280 self._xml_end_tag("font") 

281 

282 def _write_comment_font(self): 

283 # Write the <font> element for comments. 

284 self._xml_start_tag("font") 

285 

286 self._xml_empty_tag("sz", [("val", 8)]) 

287 self._write_color([("indexed", 81)]) 

288 self._xml_empty_tag("name", [("val", "Tahoma")]) 

289 self._xml_empty_tag("family", [("val", 2)]) 

290 

291 self._xml_end_tag("font") 

292 

293 def _write_underline(self, underline): 

294 # Write the underline font element. 

295 

296 if underline == 2: 

297 attributes = [("val", "double")] 

298 elif underline == 33: 

299 attributes = [("val", "singleAccounting")] 

300 elif underline == 34: 

301 attributes = [("val", "doubleAccounting")] 

302 else: 

303 # Default to single underline. 

304 attributes = [] 

305 

306 self._xml_empty_tag("u", attributes) 

307 

308 def _write_vert_align(self, val): 

309 # Write the <vertAlign> font sub-element. 

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

311 

312 self._xml_empty_tag("vertAlign", attributes) 

313 

314 def _write_color(self, attributes): 

315 # Write the <color> element. 

316 self._xml_empty_tag("color", attributes) 

317 

318 def _write_fills(self): 

319 # Write the <fills> element. 

320 attributes = [("count", self.fill_count)] 

321 

322 self._xml_start_tag("fills", attributes) 

323 

324 # Write the default fill element. 

325 self._write_default_fill("none") 

326 self._write_default_fill("gray125") 

327 

328 # Write the fill elements for xf_format objects that have them. 

329 for xf_format in self.xf_formats: 

330 if xf_format.has_fill: 

331 self._write_fill(xf_format) 

332 

333 self._xml_end_tag("fills") 

334 

335 def _write_default_fill(self, pattern_type): 

336 # Write the <fill> element for the default fills. 

337 self._xml_start_tag("fill") 

338 self._xml_empty_tag("patternFill", [("patternType", pattern_type)]) 

339 self._xml_end_tag("fill") 

340 

341 def _write_fill(self, xf_format, is_dxf_format=False): 

342 # Write the <fill> element. 

343 pattern = xf_format.pattern 

344 bg_color = xf_format.bg_color 

345 fg_color = xf_format.fg_color 

346 

347 # Colors for dxf formats are handled differently from normal formats 

348 # since the normal xf_format reverses the meaning of BG and FG for 

349 # solid fills. 

350 if is_dxf_format: 

351 bg_color = xf_format.dxf_bg_color 

352 fg_color = xf_format.dxf_fg_color 

353 

354 patterns = ( 

355 "none", 

356 "solid", 

357 "mediumGray", 

358 "darkGray", 

359 "lightGray", 

360 "darkHorizontal", 

361 "darkVertical", 

362 "darkDown", 

363 "darkUp", 

364 "darkGrid", 

365 "darkTrellis", 

366 "lightHorizontal", 

367 "lightVertical", 

368 "lightDown", 

369 "lightUp", 

370 "lightGrid", 

371 "lightTrellis", 

372 "gray125", 

373 "gray0625", 

374 ) 

375 

376 # Special handling for pattern only case. 

377 if not fg_color and not bg_color and patterns[pattern]: 

378 self._write_default_fill(patterns[pattern]) 

379 return 

380 

381 self._xml_start_tag("fill") 

382 

383 # The "none" pattern is handled differently for dxf formats. 

384 if is_dxf_format and pattern <= 1: 

385 self._xml_start_tag("patternFill") 

386 else: 

387 self._xml_start_tag("patternFill", [("patternType", patterns[pattern])]) 

388 

389 if fg_color: 

390 if not fg_color._is_automatic: 

391 self._xml_empty_tag("fgColor", fg_color._attributes()) 

392 

393 if bg_color: 

394 if not bg_color._is_automatic: 

395 self._xml_empty_tag("bgColor", bg_color._attributes()) 

396 else: 

397 if not is_dxf_format and pattern <= 1: 

398 self._xml_empty_tag("bgColor", [("indexed", 64)]) 

399 

400 self._xml_end_tag("patternFill") 

401 self._xml_end_tag("fill") 

402 

403 def _write_borders(self): 

404 # Write the <borders> element. 

405 attributes = [("count", self.border_count)] 

406 

407 self._xml_start_tag("borders", attributes) 

408 

409 # Write the border elements for xf_format objects that have them. 

410 for xf_format in self.xf_formats: 

411 if xf_format.has_border: 

412 self._write_border(xf_format) 

413 

414 self._xml_end_tag("borders") 

415 

416 def _write_border(self, xf_format, is_dxf_format=False): 

417 # Write the <border> element. 

418 attributes = [] 

419 

420 # Diagonal borders add attributes to the <border> element. 

421 if xf_format.diag_type == 1: 

422 attributes.append(("diagonalUp", 1)) 

423 elif xf_format.diag_type == 2: 

424 attributes.append(("diagonalDown", 1)) 

425 elif xf_format.diag_type == 3: 

426 attributes.append(("diagonalUp", 1)) 

427 attributes.append(("diagonalDown", 1)) 

428 

429 # Ensure that a default diag border is set if the diag type is set. 

430 if xf_format.diag_type and not xf_format.diag_border: 

431 xf_format.diag_border = 1 

432 

433 # Write the start border tag. 

434 self._xml_start_tag("border", attributes) 

435 

436 # Write the <border> sub elements. 

437 self._write_sub_border("left", xf_format.left, xf_format.left_color) 

438 

439 self._write_sub_border("right", xf_format.right, xf_format.right_color) 

440 

441 self._write_sub_border("top", xf_format.top, xf_format.top_color) 

442 

443 self._write_sub_border("bottom", xf_format.bottom, xf_format.bottom_color) 

444 

445 # Condition DXF formats don't allow diagonal borders. 

446 if not is_dxf_format: 

447 self._write_sub_border( 

448 "diagonal", xf_format.diag_border, xf_format.diag_color 

449 ) 

450 

451 if is_dxf_format: 

452 self._write_sub_border("vertical", None, None) 

453 self._write_sub_border("horizontal", None, None) 

454 

455 self._xml_end_tag("border") 

456 

457 def _write_sub_border(self, border_type, style, color): 

458 # Write the <border> sub elements such as <right>, <top>, etc. 

459 attributes = [] 

460 

461 if not style: 

462 self._xml_empty_tag(border_type) 

463 return 

464 

465 border_styles = ( 

466 "none", 

467 "thin", 

468 "medium", 

469 "dashed", 

470 "dotted", 

471 "thick", 

472 "double", 

473 "hair", 

474 "mediumDashed", 

475 "dashDot", 

476 "mediumDashDot", 

477 "dashDotDot", 

478 "mediumDashDotDot", 

479 "slantDashDot", 

480 ) 

481 

482 attributes.append(("style", border_styles[style])) 

483 

484 self._xml_start_tag(border_type, attributes) 

485 

486 if color and not color._is_automatic: 

487 self._xml_empty_tag("color", color._attributes()) 

488 else: 

489 self._xml_empty_tag("color", [("auto", 1)]) 

490 

491 self._xml_end_tag(border_type) 

492 

493 def _write_cell_style_xfs(self): 

494 # Write the <cellStyleXfs> element. 

495 count = 1 

496 

497 if self.has_hyperlink: 

498 count = 2 

499 

500 attributes = [("count", count)] 

501 

502 self._xml_start_tag("cellStyleXfs", attributes) 

503 self._write_style_xf() 

504 

505 if self.has_hyperlink: 

506 self._write_style_xf(True, self.hyperlink_font_id) 

507 

508 self._xml_end_tag("cellStyleXfs") 

509 

510 def _write_cell_xfs(self): 

511 # Write the <cellXfs> element. 

512 formats = self.xf_formats 

513 

514 # Workaround for when the last xf_format is used for the comment font 

515 # and shouldn't be used for cellXfs. 

516 last_format = formats[-1] 

517 if last_format.font_only: 

518 formats.pop() 

519 

520 attributes = [("count", len(formats))] 

521 self._xml_start_tag("cellXfs", attributes) 

522 

523 # Write the xf elements. 

524 for xf_format in formats: 

525 self._write_xf(xf_format) 

526 

527 self._xml_end_tag("cellXfs") 

528 

529 def _write_style_xf(self, has_hyperlink=False, font_id=0): 

530 # Write the style <xf> element. 

531 num_fmt_id = 0 

532 fill_id = 0 

533 border_id = 0 

534 

535 attributes = [ 

536 ("numFmtId", num_fmt_id), 

537 ("fontId", font_id), 

538 ("fillId", fill_id), 

539 ("borderId", border_id), 

540 ] 

541 

542 if has_hyperlink: 

543 attributes.append(("applyNumberFormat", 0)) 

544 attributes.append(("applyFill", 0)) 

545 attributes.append(("applyBorder", 0)) 

546 attributes.append(("applyAlignment", 0)) 

547 attributes.append(("applyProtection", 0)) 

548 

549 self._xml_start_tag("xf", attributes) 

550 self._xml_empty_tag("alignment", [("vertical", "top")]) 

551 self._xml_empty_tag("protection", [("locked", 0)]) 

552 self._xml_end_tag("xf") 

553 

554 else: 

555 self._xml_empty_tag("xf", attributes) 

556 

557 def _write_xf(self, xf_format): 

558 # Write the <xf> element. 

559 xf_id = xf_format.xf_id 

560 font_id = xf_format.font_index 

561 fill_id = xf_format.fill_index 

562 border_id = xf_format.border_index 

563 num_fmt_id = xf_format.num_format_index 

564 

565 has_checkbox = xf_format.checkbox 

566 has_alignment = False 

567 has_protection = False 

568 

569 attributes = [ 

570 ("numFmtId", num_fmt_id), 

571 ("fontId", font_id), 

572 ("fillId", fill_id), 

573 ("borderId", border_id), 

574 ("xfId", xf_id), 

575 ] 

576 

577 if xf_format.quote_prefix: 

578 attributes.append(("quotePrefix", 1)) 

579 

580 if xf_format.num_format_index > 0: 

581 attributes.append(("applyNumberFormat", 1)) 

582 

583 # Add applyFont attribute if XF format uses a font element. 

584 if xf_format.font_index > 0 and not xf_format.hyperlink: 

585 attributes.append(("applyFont", 1)) 

586 

587 # Add applyFill attribute if XF format uses a fill element. 

588 if xf_format.fill_index > 0: 

589 attributes.append(("applyFill", 1)) 

590 

591 # Add applyBorder attribute if XF format uses a border element. 

592 if xf_format.border_index > 0: 

593 attributes.append(("applyBorder", 1)) 

594 

595 # Check if XF format has alignment properties set. 

596 (apply_align, align) = xf_format._get_align_properties() 

597 

598 # Check if an alignment sub-element should be written. 

599 if apply_align and align: 

600 has_alignment = True 

601 

602 # We can also have applyAlignment without a sub-element. 

603 if apply_align or xf_format.hyperlink: 

604 attributes.append(("applyAlignment", 1)) 

605 

606 # Check for cell protection properties. 

607 protection = xf_format._get_protection_properties() 

608 

609 if protection or xf_format.hyperlink: 

610 attributes.append(("applyProtection", 1)) 

611 

612 if not xf_format.hyperlink: 

613 has_protection = True 

614 

615 # Write XF with sub-elements if required. 

616 if has_alignment or has_protection or has_checkbox: 

617 self._xml_start_tag("xf", attributes) 

618 

619 if has_alignment: 

620 self._xml_empty_tag("alignment", align) 

621 

622 if has_protection: 

623 self._xml_empty_tag("protection", protection) 

624 

625 if has_checkbox: 

626 self._write_xf_format_extensions() 

627 

628 self._xml_end_tag("xf") 

629 else: 

630 self._xml_empty_tag("xf", attributes) 

631 

632 def _write_cell_styles(self): 

633 # Write the <cellStyles> element. 

634 count = 1 

635 

636 if self.has_hyperlink: 

637 count = 2 

638 

639 attributes = [("count", count)] 

640 

641 self._xml_start_tag("cellStyles", attributes) 

642 

643 if self.has_hyperlink: 

644 self._write_cell_style("Hyperlink", 1, 8) 

645 

646 self._write_cell_style() 

647 

648 self._xml_end_tag("cellStyles") 

649 

650 def _write_cell_style(self, name="Normal", xf_id=0, builtin_id=0): 

651 # Write the <cellStyle> element. 

652 attributes = [ 

653 ("name", name), 

654 ("xfId", xf_id), 

655 ("builtinId", builtin_id), 

656 ] 

657 

658 self._xml_empty_tag("cellStyle", attributes) 

659 

660 def _write_dxfs(self): 

661 # Write the <dxfs> element. 

662 formats = self.dxf_formats 

663 count = len(formats) 

664 

665 attributes = [("count", len(formats))] 

666 

667 if count: 

668 self._xml_start_tag("dxfs", attributes) 

669 

670 # Write the font elements for xf_format objects that have them. 

671 for dxf_format in self.dxf_formats: 

672 self._xml_start_tag("dxf") 

673 if dxf_format.has_dxf_font: 

674 self._write_font(dxf_format, True) 

675 

676 if dxf_format.num_format_index: 

677 self._write_num_fmt( 

678 dxf_format.num_format_index, dxf_format.num_format 

679 ) 

680 

681 if dxf_format.has_dxf_fill: 

682 self._write_fill(dxf_format, True) 

683 

684 if dxf_format.has_dxf_border: 

685 self._write_border(dxf_format, True) 

686 

687 if dxf_format.checkbox: 

688 self._write_dxf_format_extensions() 

689 

690 self._xml_end_tag("dxf") 

691 

692 self._xml_end_tag("dxfs") 

693 else: 

694 self._xml_empty_tag("dxfs", attributes) 

695 

696 def _write_table_styles(self): 

697 # Write the <tableStyles> element. 

698 count = 0 

699 default_table_style = "TableStyleMedium9" 

700 default_pivot_style = "PivotStyleLight16" 

701 

702 attributes = [ 

703 ("count", count), 

704 ("defaultTableStyle", default_table_style), 

705 ("defaultPivotStyle", default_pivot_style), 

706 ] 

707 

708 self._xml_empty_tag("tableStyles", attributes) 

709 

710 def _write_colors(self): 

711 # Write the <colors> element. 

712 custom_colors = self.custom_colors 

713 

714 if not custom_colors: 

715 return 

716 

717 self._xml_start_tag("colors") 

718 self._write_mru_colors(custom_colors) 

719 self._xml_end_tag("colors") 

720 

721 def _write_mru_colors(self, custom_colors): 

722 # Write the <mruColors> element for the most recently used colors. 

723 

724 # Write the custom custom_colors in reverse order. 

725 custom_colors.reverse() 

726 

727 # Limit the mruColors to the last 10. 

728 if len(custom_colors) > 10: 

729 custom_colors = custom_colors[0:10] 

730 

731 self._xml_start_tag("mruColors") 

732 

733 # Write the custom custom_colors in reverse order. 

734 for color in custom_colors: 

735 # For backwards compatibility convert possible 

736 self._write_color(color._attributes()) 

737 

738 self._xml_end_tag("mruColors") 

739 

740 def _write_condense(self): 

741 # Write the <condense> element. 

742 attributes = [("val", 0)] 

743 

744 self._xml_empty_tag("condense", attributes) 

745 

746 def _write_extend(self): 

747 # Write the <extend> element. 

748 attributes = [("val", 0)] 

749 

750 self._xml_empty_tag("extend", attributes) 

751 

752 def _write_xf_format_extensions(self): 

753 # Write the xfComplement <extLst> elements. 

754 schema = "http://schemas.microsoft.com/office/spreadsheetml" 

755 attributes = [ 

756 ("uri", "{C7286773-470A-42A8-94C5-96B5CB345126}"), 

757 ( 

758 "xmlns:xfpb", 

759 schema + "/2022/featurepropertybag", 

760 ), 

761 ] 

762 

763 self._xml_start_tag("extLst") 

764 self._xml_start_tag("ext", attributes) 

765 

766 self._xml_empty_tag("xfpb:xfComplement", [("i", "0")]) 

767 

768 self._xml_end_tag("ext") 

769 self._xml_end_tag("extLst") 

770 

771 def _write_dxf_format_extensions(self): 

772 # Write the DXFComplement <extLst> elements. 

773 schema = "http://schemas.microsoft.com/office/spreadsheetml" 

774 attributes = [ 

775 ("uri", "{0417FA29-78FA-4A13-93AC-8FF0FAFDF519}"), 

776 ( 

777 "xmlns:xfpb", 

778 schema + "/2022/featurepropertybag", 

779 ), 

780 ] 

781 

782 self._xml_start_tag("extLst") 

783 self._xml_start_tag("ext", attributes) 

784 

785 self._xml_empty_tag("xfpb:DXFComplement", [("i", "0")]) 

786 

787 self._xml_end_tag("ext") 

788 self._xml_end_tag("extLst")