1###############################################################################
2#
3# Format - 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 warnings import warn
12
13from xlsxwriter.color import Color
14
15from . import xmlwriter
16
17
18class Format(xmlwriter.XMLwriter):
19 """
20 A class for writing the Excel XLSX Format file.
21
22
23 """
24
25 ###########################################################################
26 #
27 # Public API.
28 #
29 ###########################################################################
30
31 def __init__(self, properties=None, xf_indices=None, dxf_indices=None):
32 """
33 Constructor.
34
35 """
36 if properties is None:
37 properties = {}
38
39 super().__init__()
40
41 self.xf_format_indices = xf_indices
42 self.dxf_format_indices = dxf_indices
43 self.xf_index = None
44 self.dxf_index = None
45
46 self.num_format = "General"
47 self.num_format_index = 0
48 self.font_index = 0
49 self.has_font = False
50 self.has_dxf_font = False
51
52 self.bold = 0
53 self.underline = 0
54 self.italic = 0
55 self.font_name = "Calibri"
56 self.font_size = 11
57 self.font_color = None
58 self.font_strikeout = 0
59 self.font_outline = 0
60 self.font_shadow = 0
61 self.font_script = 0
62 self.font_family = 2
63 self.font_charset = 0
64 self.font_scheme = "minor"
65 self.font_condense = 0
66 self.font_extend = 0
67 self.theme = 0
68 self.hyperlink = False
69 self.xf_id = 0
70
71 self.hidden = 0
72 self.locked = 1
73
74 self.text_h_align = 0
75 self.text_wrap = 0
76 self.text_v_align = 0
77 self.text_justlast = 0
78 self.rotation = 0
79
80 self.fg_color = None
81 self.bg_color = None
82 self.pattern = 0
83 self.has_fill = False
84 self.has_dxf_fill = False
85 self.fill_index = 0
86 self.fill_count = 0
87
88 self.border_index = 0
89 self.has_border = False
90 self.has_dxf_border = False
91 self.border_count = 0
92
93 self.bottom = 0
94 self.bottom_color = None
95 self.diag_border = 0
96 self.diag_color = None
97 self.diag_type = 0
98 self.left = 0
99 self.left_color = None
100 self.right = 0
101 self.right_color = None
102 self.top = 0
103 self.top_color = None
104
105 self.indent = 0
106 self.shrink = 0
107 self.merge_range = 0
108 self.reading_order = 0
109 self.just_distrib = 0
110 self.color_indexed = 0
111 self.font_only = 0
112
113 self.quote_prefix = False
114 self.checkbox = False
115
116 # Convert properties in the constructor to method calls.
117 for key, value in properties.items():
118 getattr(self, "set_" + key)(value)
119
120 self._format_key = None
121
122 def __repr__(self):
123 """
124 Return a string representation of the Format instance.
125 """
126 return (
127 f"Format("
128 f"font_name={self.font_name!r}, "
129 f"font_size={self.font_size}, "
130 f"bold={self.bold}, "
131 f"italic={self.italic}, "
132 f"underline={self.underline}, "
133 f"font_color={self.font_color}, "
134 f"num_format={self.num_format!r}, "
135 f"text_h_align={self.text_h_align}, "
136 f"text_v_align={self.text_v_align}, "
137 f"fg_color={self.fg_color}, "
138 f"bg_color={self.bg_color}, "
139 f"pattern={self.pattern}, "
140 f"locked={self.locked}, "
141 f"hidden={self.hidden})"
142 )
143
144 ###########################################################################
145 #
146 # Format properties.
147 #
148 ###########################################################################
149
150 def set_font_name(self, font_name):
151 """
152 Set the Format font_name property such as 'Time New Roman'. The
153 default Excel font is 'Calibri'.
154
155 Args:
156 font_name: String with the font name. No default.
157
158 Returns:
159 Nothing.
160
161 """
162 self.font_name = font_name
163
164 def set_font_size(self, font_size=11):
165 """
166 Set the Format font_size property. The default Excel font size is 11.
167
168 Args:
169 font_size: Int with font size. No default.
170
171 Returns:
172 Nothing.
173
174 """
175 self.font_size = font_size
176
177 def set_font_color(self, font_color):
178 """
179 Set the Format font_color property. The Excel default is black.
180
181 Args:
182 font_color: String with the font color. No default.
183
184 Returns:
185 Nothing.
186
187 """
188 self.font_color = Color._from_value(font_color)
189
190 def set_bold(self, bold=True):
191 """
192 Set the Format bold property.
193
194 Args:
195 bold: Default is True, turns property on.
196
197 Returns:
198 Nothing.
199
200 """
201 self.bold = bold
202
203 def set_italic(self, italic=True):
204 """
205 Set the Format italic property.
206
207 Args:
208 italic: Default is True, turns property on.
209
210 Returns:
211 Nothing.
212
213 """
214 self.italic = italic
215
216 def set_underline(self, underline=1):
217 """
218 Set the Format underline property.
219
220 Args:
221 underline: Default is 1, single underline.
222
223 Returns:
224 Nothing.
225
226 """
227 self.underline = underline
228
229 def set_font_strikeout(self, font_strikeout=True):
230 """
231 Set the Format font_strikeout property.
232
233 Args:
234 font_strikeout: Default is True, turns property on.
235
236 Returns:
237 Nothing.
238
239 """
240 self.font_strikeout = font_strikeout
241
242 def set_font_script(self, font_script=1):
243 """
244 Set the Format font_script property.
245
246 Args:
247 font_script: Default is 1, superscript.
248
249 Returns:
250 Nothing.
251
252 """
253 self.font_script = font_script
254
255 def set_font_outline(self, font_outline=True):
256 """
257 Set the Format font_outline property.
258
259 Args:
260 font_outline: Default is True, turns property on.
261
262 Returns:
263 Nothing.
264
265 """
266 self.font_outline = font_outline
267
268 def set_font_shadow(self, font_shadow=True):
269 """
270 Set the Format font_shadow property.
271
272 Args:
273 font_shadow: Default is True, turns property on.
274
275 Returns:
276 Nothing.
277
278 """
279 self.font_shadow = font_shadow
280
281 def set_num_format(self, num_format):
282 """
283 Set the Format num_format property such as '#,##0'.
284
285 Args:
286 num_format: String representing the number format. No default.
287
288 Returns:
289 Nothing.
290
291 """
292 self.num_format = num_format
293
294 def set_locked(self, locked=True):
295 """
296 Set the Format locked property.
297
298 Args:
299 locked: Default is True, turns property on.
300
301 Returns:
302 Nothing.
303
304 """
305 self.locked = locked
306
307 def set_hidden(self, hidden=True):
308 """
309 Set the Format hidden property.
310
311 Args:
312 hidden: Default is True, turns property on.
313
314 Returns:
315 Nothing.
316
317 """
318 self.hidden = hidden
319
320 def set_align(self, alignment):
321 """
322 Set the Format cell alignment.
323
324 Args:
325 alignment: String representing alignment. No default.
326
327 Returns:
328 Nothing.
329 """
330 alignment = alignment.lower()
331
332 # Set horizontal alignment properties.
333 if alignment == "left":
334 self.set_text_h_align(1)
335 if alignment == "centre":
336 self.set_text_h_align(2)
337 if alignment == "center":
338 self.set_text_h_align(2)
339 if alignment == "right":
340 self.set_text_h_align(3)
341 if alignment == "fill":
342 self.set_text_h_align(4)
343 if alignment == "justify":
344 self.set_text_h_align(5)
345 if alignment == "center_across":
346 self.set_text_h_align(6)
347 if alignment == "centre_across":
348 self.set_text_h_align(6)
349 if alignment == "distributed":
350 self.set_text_h_align(7)
351 if alignment == "justify_distributed":
352 self.set_text_h_align(7)
353
354 if alignment == "justify_distributed":
355 self.just_distrib = 1
356
357 # Set vertical alignment properties.
358 if alignment == "top":
359 self.set_text_v_align(1)
360 if alignment == "vcentre":
361 self.set_text_v_align(2)
362 if alignment == "vcenter":
363 self.set_text_v_align(2)
364 if alignment == "bottom":
365 self.set_text_v_align(3)
366 if alignment == "vjustify":
367 self.set_text_v_align(4)
368 if alignment == "vdistributed":
369 self.set_text_v_align(5)
370
371 def set_center_across(self, align_type=None):
372 # pylint: disable=unused-argument
373 """
374 Set the Format center_across property.
375
376 Returns:
377 Nothing.
378
379 """
380 self.set_text_h_align(6)
381
382 def set_text_wrap(self, text_wrap=True):
383 """
384 Set the Format text_wrap property.
385
386 Args:
387 text_wrap: Default is True, turns property on.
388
389 Returns:
390 Nothing.
391
392 """
393 self.text_wrap = text_wrap
394
395 def set_rotation(self, rotation):
396 """
397 Set the Format rotation property.
398
399 Args:
400 rotation: Rotation angle. No default.
401
402 Returns:
403 Nothing.
404
405 """
406 rotation = int(rotation)
407
408 # Map user angle to Excel angle.
409 if rotation == 270:
410 rotation = 255
411 elif -90 <= rotation <= 90:
412 if rotation < 0:
413 rotation = -rotation + 90
414 else:
415 warn("Rotation rotation outside range: -90 <= angle <= 90")
416 return
417
418 self.rotation = rotation
419
420 def set_indent(self, indent=1):
421 """
422 Set the Format indent property.
423
424 Args:
425 indent: Default is 1, first indentation level.
426
427 Returns:
428 Nothing.
429
430 """
431 self.indent = indent
432
433 def set_shrink(self, shrink=True):
434 """
435 Set the Format shrink property.
436
437 Args:
438 shrink: Default is True, turns property on.
439
440 Returns:
441 Nothing.
442
443 """
444 self.shrink = shrink
445
446 def set_text_justlast(self, text_justlast=True):
447 """
448 Set the Format text_justlast property.
449
450 Args:
451 text_justlast: Default is True, turns property on.
452
453 Returns:
454 Nothing.
455
456 """
457 self.text_justlast = text_justlast
458
459 def set_pattern(self, pattern=1):
460 """
461 Set the Format pattern property.
462
463 Args:
464 pattern: Default is 1, solid fill.
465
466 Returns:
467 Nothing.
468
469 """
470 self.pattern = pattern
471
472 def set_bg_color(self, bg_color):
473 """
474 Set the Format bg_color property.
475
476 Args:
477 bg_color: Background color. No default.
478
479 Returns:
480 Nothing.
481
482 """
483 self.bg_color = Color._from_value(bg_color)
484
485 def set_fg_color(self, fg_color):
486 """
487 Set the Format fg_color property.
488
489 Args:
490 fg_color: Foreground color. No default.
491
492 Returns:
493 Nothing.
494
495 """
496 self.fg_color = Color._from_value(fg_color)
497
498 # set_border(style) Set cells borders to the same style
499 def set_border(self, style=1):
500 """
501 Set the Format bottom property.
502
503 Args:
504 bottom: Default is 1, border type 1.
505
506 Returns:
507 Nothing.
508
509 """
510 self.set_bottom(style)
511 self.set_top(style)
512 self.set_left(style)
513 self.set_right(style)
514
515 # set_border_color(color) Set cells border to the same color
516 def set_border_color(self, color):
517 """
518 Set the Format bottom property.
519
520 Args:
521 color: Color string. No default.
522
523 Returns:
524 Nothing.
525
526 """
527 self.set_bottom_color(color)
528 self.set_top_color(color)
529 self.set_left_color(color)
530 self.set_right_color(color)
531
532 def set_bottom(self, bottom=1):
533 """
534 Set the Format bottom property.
535
536 Args:
537 bottom: Default is 1, border type 1.
538
539 Returns:
540 Nothing.
541
542 """
543 self.bottom = bottom
544
545 def set_bottom_color(self, bottom_color):
546 """
547 Set the Format bottom_color property.
548
549 Args:
550 bottom_color: Color string. No default.
551
552 Returns:
553 Nothing.
554
555 """
556 self.bottom_color = Color._from_value(bottom_color)
557
558 def set_diag_type(self, diag_type=1):
559 """
560 Set the Format diag_type property.
561
562 Args:
563 diag_type: Default is 1, border type 1.
564
565 Returns:
566 Nothing.
567
568 """
569 self.diag_type = diag_type
570
571 def set_left(self, left=1):
572 """
573 Set the Format left property.
574
575 Args:
576 left: Default is 1, border type 1.
577
578 Returns:
579 Nothing.
580
581 """
582 self.left = left
583
584 def set_left_color(self, left_color):
585 """
586 Set the Format left_color property.
587
588 Args:
589 left_color: Color string. No default.
590
591 Returns:
592 Nothing.
593
594 """
595 self.left_color = Color._from_value(left_color)
596
597 def set_right(self, right=1):
598 """
599 Set the Format right property.
600
601 Args:
602 right: Default is 1, border type 1.
603
604 Returns:
605 Nothing.
606
607 """
608 self.right = right
609
610 def set_right_color(self, right_color):
611 """
612 Set the Format right_color property.
613
614 Args:
615 right_color: Color string. No default.
616
617 Returns:
618 Nothing.
619
620 """
621 self.right_color = Color._from_value(right_color)
622
623 def set_top(self, top=1):
624 """
625 Set the Format top property.
626
627 Args:
628 top: Default is 1, border type 1.
629
630 Returns:
631 Nothing.
632
633 """
634 self.top = top
635
636 def set_top_color(self, top_color):
637 """
638 Set the Format top_color property.
639
640 Args:
641 top_color: Color string. No default.
642
643 Returns:
644 Nothing.
645
646 """
647 self.top_color = Color._from_value(top_color)
648
649 def set_diag_color(self, diag_color):
650 """
651 Set the Format diag_color property.
652
653 Args:
654 diag_color: Color string. No default.
655
656 Returns:
657 Nothing.
658
659 """
660 self.diag_color = Color._from_value(diag_color)
661
662 def set_diag_border(self, diag_border=1):
663 """
664 Set the Format diag_border property.
665
666 Args:
667 diag_border: Default is 1, border type 1.
668
669 Returns:
670 Nothing.
671
672 """
673 self.diag_border = diag_border
674
675 def set_quote_prefix(self, quote_prefix=True):
676 """
677 Set the Format quote prefix property.
678
679 Args:
680 quote_prefix: Default is True, turns property on.
681
682 Returns:
683 Nothing.
684
685 """
686 self.quote_prefix = quote_prefix
687
688 def set_checkbox(self, checkbox=True):
689 """
690 Set the Format property to show a checkbox in a cell.
691
692 This format property can be used with a cell that contains a boolean
693 value to display it as a checkbox. This property isn't required very
694 often and it is generally easier to create a checkbox using the
695 ``worksheet.insert_checkbox()`` method.
696
697 Args:
698 checkbox: Default is True, turns property on.
699
700 Returns:
701 Nothing.
702
703 """
704 self.checkbox = checkbox
705
706 ###########################################################################
707 #
708 # Internal Format properties. These aren't documented since they are
709 # either only used internally or else are unlikely to be set by the user.
710 #
711 ###########################################################################
712
713 def set_has_font(self, has_font=True):
714 """
715 Set the property to indicate the format has a font.
716
717 Args:
718 has_font: Default is True, turns property on.
719
720 Returns:
721 Nothing.
722
723 """
724 self.has_font = has_font
725
726 def set_has_fill(self, has_fill=True):
727 """
728 Set the property to indicate the format has a fill.
729
730 Args:
731 has_fill: Default is True, turns property on.
732
733 Returns:
734 Nothing.
735
736 """
737 self.has_fill = has_fill
738
739 def set_font_index(self, font_index):
740 """
741 Set the unique font index property.
742
743 Args:
744 font_index: The unique font index.
745
746 Returns:
747 Nothing.
748
749 """
750 self.font_index = font_index
751
752 def set_xf_index(self, xf_index):
753 """
754 Set the unique format index property.
755
756 Args:
757 xf_index: The unique Excel format index.
758
759 Returns:
760 Nothing.
761
762 """
763 self.xf_index = xf_index
764
765 def set_dxf_index(self, dxf_index):
766 """
767 Set the unique conditional format index property.
768
769 Args:
770 dxf_index: The unique Excel conditional format index.
771
772 Returns:
773 Nothing.
774
775 """
776 self.dxf_index = dxf_index
777
778 def set_num_format_index(self, num_format_index):
779 """
780 Set the number format_index property.
781
782 Args:
783 num_format_index: The unique number format index.
784
785 Returns:
786 Nothing.
787
788 """
789 self.num_format_index = num_format_index
790
791 def set_text_h_align(self, text_h_align):
792 """
793 Set the horizontal text alignment property.
794
795 Args:
796 text_h_align: Horizontal text alignment.
797
798 Returns:
799 Nothing.
800
801 """
802 self.text_h_align = text_h_align
803
804 def set_text_v_align(self, text_v_align):
805 """
806 Set the vertical text alignment property.
807
808 Args:
809 text_h_align: Vertical text alignment.
810
811 Returns:
812 Nothing.
813
814 """
815 self.text_v_align = text_v_align
816
817 def set_reading_order(self, direction=0):
818 # Set the reading_order property.
819 """
820 Set the reading order property.
821
822 Args:
823 direction: Default is 0, left to right.
824
825 Returns:
826 Nothing.
827
828 """
829 self.reading_order = direction
830
831 def set_valign(self, align):
832 # Set vertical cell alignment. This is required by the constructor
833 # properties dict to differentiate between the vertical and horizontal
834 # properties.
835 """
836 Set vertical cell alignment property.
837
838 This is required by the constructor properties dict to differentiate
839 between the vertical and horizontal properties.
840
841 Args:
842 align: Alignment property.
843
844 Returns:
845 Nothing.
846
847 """
848 self.set_align(align)
849
850 def set_font_family(self, font_family):
851 """
852 Set the font family property.
853
854 Args:
855 font_family: Font family number.
856
857 Returns:
858 Nothing.
859
860 """
861 self.font_family = font_family
862
863 def set_font_charset(self, font_charset):
864 """
865 Set the font character set property.
866
867 Args:
868 font_charset: The font character set number.
869
870 Returns:
871 Nothing.
872
873 """
874 self.font_charset = font_charset
875
876 def set_font_scheme(self, font_scheme):
877 """
878 Set the font scheme property.
879
880 Args:
881 font_scheme: The font scheme.
882
883 Returns:
884 Nothing.
885
886 """
887 self.font_scheme = font_scheme
888
889 def set_font_condense(self, font_condense):
890 """
891 Set the font condense property.
892
893 Args:
894 font_condense: The font condense property.
895
896 Returns:
897 Nothing.
898
899 """
900 self.font_condense = font_condense
901
902 def set_font_extend(self, font_extend):
903 """
904 Set the font extend property.
905
906 Args:
907 font_extend: The font extend property.
908
909 Returns:
910 Nothing.
911
912 """
913 self.font_extend = font_extend
914
915 def set_theme(self, theme):
916 """
917 Set the theme property.
918
919 Args:
920 theme: Format theme.
921
922 Returns:
923 Nothing.
924
925 """
926 self.theme = theme
927
928 def set_hyperlink(self, hyperlink=True):
929 """
930 Set the properties for the hyperlink style.
931
932 Args:
933 hyperlink: Default is True, turns property on.
934
935 Returns:
936 Nothing.
937
938 """
939 self.xf_id = 1
940 self.set_underline(1)
941 self.set_theme(10)
942 self.hyperlink = hyperlink
943
944 def set_color_indexed(self, color_index):
945 """
946 Set the color index property. Some fundamental format properties use an
947 indexed color instead of a rbg or theme color.
948
949 Args:
950 color_index: Generally 0 or 1.
951
952 Returns:
953 Nothing.
954
955 """
956 self.color_indexed = color_index
957
958 def set_font_only(self, font_only=True):
959 """
960 Set property to indicate that the format is used for fonts only.
961
962 Args:
963 font_only: Default is True, turns property on.
964
965 Returns:
966 Nothing.
967
968 """
969 self.font_only = font_only
970
971 # Compatibility methods. These versions of the method names were added in an
972 # initial version for compatibility testing with Excel::Writer::XLSX and
973 # leaked out into production code. They are deprecated and will be removed
974 # in a future after a suitable deprecation period.
975 def set_font(self, font_name):
976 """Deprecated: Use set_font_name() instead."""
977 self.font_name = font_name
978
979 def set_size(self, font_size):
980 """Deprecated: Use set_font_size() instead."""
981 self.font_size = font_size
982
983 def set_color(self, font_color):
984 """Deprecated: Use set_font_color() instead."""
985 self.font_color = Color._from_value(font_color)
986
987 ###########################################################################
988 #
989 # Private API.
990 #
991 ###########################################################################
992
993 def _get_align_properties(self):
994 # pylint: disable=too-many-boolean-expressions
995 # Return properties for an Style xf <alignment> sub-element.
996 changed = 0
997 align = []
998
999 # Check if any alignment options in the format have been changed.
1000 if (
1001 self.text_h_align
1002 or self.text_v_align
1003 or self.indent
1004 or self.rotation
1005 or self.text_wrap
1006 or self.shrink
1007 or self.reading_order
1008 ):
1009 changed = 1
1010 else:
1011 return changed, align
1012
1013 # Indent is only allowed for some alignment properties. If it is
1014 # defined for any other alignment or no alignment has been set then
1015 # default to left alignment.
1016 if (
1017 self.indent
1018 and self.text_h_align != 1
1019 and self.text_h_align != 3
1020 and self.text_h_align != 7
1021 and self.text_v_align != 1
1022 and self.text_v_align != 3
1023 and self.text_v_align != 5
1024 ):
1025 self.text_h_align = 1
1026
1027 # Check for properties that are mutually exclusive.
1028 if self.text_wrap:
1029 self.shrink = 0
1030 if self.text_h_align == 4:
1031 self.shrink = 0
1032 if self.text_h_align == 5:
1033 self.shrink = 0
1034 if self.text_h_align == 7:
1035 self.shrink = 0
1036 if self.text_h_align != 7:
1037 self.just_distrib = 0
1038 if self.indent:
1039 self.just_distrib = 0
1040
1041 continuous = "centerContinuous"
1042
1043 if self.text_h_align == 1:
1044 align.append(("horizontal", "left"))
1045 if self.text_h_align == 2:
1046 align.append(("horizontal", "center"))
1047 if self.text_h_align == 3:
1048 align.append(("horizontal", "right"))
1049 if self.text_h_align == 4:
1050 align.append(("horizontal", "fill"))
1051 if self.text_h_align == 5:
1052 align.append(("horizontal", "justify"))
1053 if self.text_h_align == 6:
1054 align.append(("horizontal", continuous))
1055 if self.text_h_align == 7:
1056 align.append(("horizontal", "distributed"))
1057
1058 if self.just_distrib:
1059 align.append(("justifyLastLine", 1))
1060
1061 # Property 'vertical' => 'bottom' is a default. It sets applyAlignment
1062 # without an alignment sub-element.
1063 if self.text_v_align == 1:
1064 align.append(("vertical", "top"))
1065 if self.text_v_align == 2:
1066 align.append(("vertical", "center"))
1067 if self.text_v_align == 4:
1068 align.append(("vertical", "justify"))
1069 if self.text_v_align == 5:
1070 align.append(("vertical", "distributed"))
1071
1072 if self.rotation:
1073 align.append(("textRotation", self.rotation))
1074 if self.indent:
1075 align.append(("indent", self.indent))
1076
1077 if self.text_wrap:
1078 align.append(("wrapText", 1))
1079 if self.shrink:
1080 align.append(("shrinkToFit", 1))
1081
1082 if self.reading_order == 1:
1083 align.append(("readingOrder", 1))
1084 if self.reading_order == 2:
1085 align.append(("readingOrder", 2))
1086
1087 return changed, align
1088
1089 def _get_protection_properties(self):
1090 # Return properties for an Excel XML <Protection> element.
1091 attributes = []
1092
1093 if not self.locked:
1094 attributes.append(("locked", 0))
1095 if self.hidden:
1096 attributes.append(("hidden", 1))
1097
1098 return attributes
1099
1100 def _get_format_key(self):
1101 # Returns a unique hash key for a format. Used by Workbook.
1102 if self._format_key is None:
1103 self._format_key = ":".join(
1104 str(x)
1105 for x in (
1106 self._get_font_key(),
1107 self._get_border_key(),
1108 self._get_fill_key(),
1109 self._get_alignment_key(),
1110 self.num_format,
1111 self.locked,
1112 self.checkbox,
1113 self.quote_prefix,
1114 self.hidden,
1115 )
1116 )
1117
1118 return self._format_key
1119
1120 def _get_font_key(self):
1121 # Returns a unique hash key for a font. Used by Workbook.
1122 key = ":".join(
1123 str(x)
1124 for x in (
1125 self.bold,
1126 self.font_color,
1127 self.font_charset,
1128 self.font_family,
1129 self.font_outline,
1130 self.font_script,
1131 self.font_shadow,
1132 self.font_strikeout,
1133 self.font_name,
1134 self.italic,
1135 self.font_size,
1136 self.underline,
1137 self.theme,
1138 )
1139 )
1140
1141 return key
1142
1143 def _get_border_key(self):
1144 # Returns a unique hash key for a border style. Used by Workbook.
1145 key = ":".join(
1146 str(x)
1147 for x in (
1148 self.bottom,
1149 self.bottom_color,
1150 self.diag_border,
1151 self.diag_color,
1152 self.diag_type,
1153 self.left,
1154 self.left_color,
1155 self.right,
1156 self.right_color,
1157 self.top,
1158 self.top_color,
1159 )
1160 )
1161
1162 return key
1163
1164 def _get_fill_key(self):
1165 # Returns a unique hash key for a fill style. Used by Workbook.
1166 key = ":".join(str(x) for x in (self.pattern, self.bg_color, self.fg_color))
1167
1168 return key
1169
1170 def _get_alignment_key(self):
1171 # Returns a unique hash key for alignment formats.
1172
1173 key = ":".join(
1174 str(x)
1175 for x in (
1176 self.text_h_align,
1177 self.text_v_align,
1178 self.indent,
1179 self.rotation,
1180 self.text_wrap,
1181 self.shrink,
1182 self.reading_order,
1183 )
1184 )
1185
1186 return key
1187
1188 def _get_xf_index(self):
1189 # Returns the XF index number used by Excel to identify a format.
1190 if self.xf_index is not None:
1191 # Format already has an index number so return it.
1192 return self.xf_index
1193
1194 # Format doesn't have an index number so assign one.
1195 key = self._get_format_key()
1196
1197 if key in self.xf_format_indices:
1198 # Format matches existing format with an index.
1199 return self.xf_format_indices[key]
1200
1201 # New format requiring an index. Note. +1 since Excel
1202 # has an implicit "General" format at index 0.
1203 index = 1 + len(self.xf_format_indices)
1204 self.xf_format_indices[key] = index
1205 self.xf_index = index
1206 return index
1207
1208 def _get_dxf_index(self):
1209 # Returns the DXF index number used by Excel to identify a format.
1210 if self.dxf_index is not None:
1211 # Format already has an index number so return it.
1212 return self.dxf_index
1213
1214 # Format doesn't have an index number so assign one.
1215 key = self._get_format_key()
1216
1217 if key in self.dxf_format_indices:
1218 # Format matches existing format with an index.
1219 return self.dxf_format_indices[key]
1220
1221 # New format requiring an index.
1222 index = len(self.dxf_format_indices)
1223 self.dxf_format_indices[key] = index
1224 self.dxf_index = index
1225 return index
1226
1227 ###########################################################################
1228 #
1229 # XML methods.
1230 #
1231 ###########################################################################