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