@@ -1578,6 +1578,149 @@ def _convert_to_style(cls, style_dict, num_format_str=None):
1578
1578
register_writer (_XlwtWriter )
1579
1579
1580
1580
1581
+ class _XlsxStyler (object ):
1582
+ # Map from openpyxl-oriented styles to flatter xlsxwriter representation
1583
+ # Ordering necessary for both determinism and because some are keyed by
1584
+ # prefixes of others.
1585
+ STYLE_MAPPING = {
1586
+ 'font' : [
1587
+ (('name' ,), 'font_name' ),
1588
+ (('sz' ,), 'font_size' ),
1589
+ (('size' ,), 'font_size' ),
1590
+ (('color' , 'rgb' ,), 'font_color' ),
1591
+ (('color' ,), 'font_color' ),
1592
+ (('b' ,), 'bold' ),
1593
+ (('bold' ,), 'bold' ),
1594
+ (('i' ,), 'italic' ),
1595
+ (('italic' ,), 'italic' ),
1596
+ (('u' ,), 'underline' ),
1597
+ (('underline' ,), 'underline' ),
1598
+ (('strike' ,), 'font_strikeout' ),
1599
+ (('vertAlign' ,), 'font_script' ),
1600
+ (('vertalign' ,), 'font_script' ),
1601
+ ],
1602
+ 'number_format' : [
1603
+ (('format_code' ,), 'num_format' ),
1604
+ ((), 'num_format' ,),
1605
+ ],
1606
+ 'protection' : [
1607
+ (('locked' ,), 'locked' ),
1608
+ (('hidden' ,), 'hidden' ),
1609
+ ],
1610
+ 'alignment' : [
1611
+ (('horizontal' ,), 'align' ),
1612
+ (('vertical' ,), 'valign' ),
1613
+ (('text_rotation' ,), 'rotation' ),
1614
+ (('wrap_text' ,), 'text_wrap' ),
1615
+ (('indent' ,), 'indent' ),
1616
+ (('shrink_to_fit' ,), 'shrink' ),
1617
+ ],
1618
+ 'fill' : [
1619
+ (('patternType' ,), 'pattern' ),
1620
+ (('patterntype' ,), 'pattern' ),
1621
+ (('fill_type' ,), 'pattern' ),
1622
+ (('start_color' , 'rgb' ,), 'fg_color' ),
1623
+ (('fgColor' , 'rgb' ,), 'fg_color' ),
1624
+ (('fgcolor' , 'rgb' ,), 'fg_color' ),
1625
+ (('start_color' ,), 'fg_color' ),
1626
+ (('fgColor' ,), 'fg_color' ),
1627
+ (('fgcolor' ,), 'fg_color' ),
1628
+ (('end_color' , 'rgb' ,), 'bg_color' ),
1629
+ (('bgColor' , 'rgb' ,), 'bg_color' ),
1630
+ (('bgcolor' , 'rgb' ,), 'bg_color' ),
1631
+ (('end_color' ,), 'bg_color' ),
1632
+ (('bgColor' ,), 'bg_color' ),
1633
+ (('bgcolor' ,), 'bg_color' ),
1634
+ ],
1635
+ 'border' : [
1636
+ (('color' , 'rgb' ,), 'border_color' ),
1637
+ (('color' ,), 'border_color' ),
1638
+ (('style' ,), 'border' ),
1639
+ (('top' , 'color' , 'rgb' ,), 'top_color' ),
1640
+ (('top' , 'color' ,), 'top_color' ),
1641
+ (('top' , 'style' ,), 'top' ),
1642
+ (('top' ,), 'top' ),
1643
+ (('right' , 'color' , 'rgb' ,), 'right_color' ),
1644
+ (('right' , 'color' ,), 'right_color' ),
1645
+ (('right' , 'style' ,), 'right' ),
1646
+ (('right' ,), 'right' ),
1647
+ (('bottom' , 'color' , 'rgb' ,), 'bottom_color' ),
1648
+ (('bottom' , 'color' ,), 'bottom_color' ),
1649
+ (('bottom' , 'style' ,), 'bottom' ),
1650
+ (('bottom' ,), 'bottom' ),
1651
+ (('left' , 'color' , 'rgb' ,), 'left_color' ),
1652
+ (('left' , 'color' ,), 'left_color' ),
1653
+ (('left' , 'style' ,), 'left' ),
1654
+ (('left' ,), 'left' ),
1655
+ ],
1656
+ }
1657
+
1658
+ @classmethod
1659
+ def convert (cls , style_dict , num_format_str = None ):
1660
+ """
1661
+ converts a style_dict to an xlsxwriter format dict
1662
+
1663
+ Parameters
1664
+ ----------
1665
+ style_dict: style dictionary to convert
1666
+ num_format_str: optional number format string
1667
+ """
1668
+
1669
+ # Create a XlsxWriter format object.
1670
+ props = {}
1671
+
1672
+ if num_format_str is not None :
1673
+ props ['num_format' ] = num_format_str
1674
+
1675
+ if style_dict is None :
1676
+ return props
1677
+
1678
+ if 'borders' in style_dict :
1679
+ style_dict = style_dict .copy ()
1680
+ style_dict ['border' ] = style_dict .pop ('borders' )
1681
+
1682
+ for style_group_key , style_group in style_dict .items ():
1683
+ for src , dst in cls .STYLE_MAPPING .get (style_group_key , []):
1684
+ # src is a sequence of keys into a nested dict
1685
+ # dst is a flat key
1686
+ if dst in props :
1687
+ continue
1688
+ v = style_group
1689
+ for k in src :
1690
+ try :
1691
+ v = v [k ]
1692
+ except (KeyError , TypeError ):
1693
+ break
1694
+ else :
1695
+ props [dst ] = v
1696
+
1697
+ if isinstance (props .get ('pattern' ), string_types ):
1698
+ # TODO: support other fill patterns
1699
+ props ['pattern' ] = 0 if props ['pattern' ] == 'none' else 1
1700
+
1701
+ for k in ['border' , 'top' , 'right' , 'bottom' , 'left' ]:
1702
+ if isinstance (props .get (k ), string_types ):
1703
+ try :
1704
+ props [k ] = ['none' , 'thin' , 'medium' , 'dashed' , 'dotted' ,
1705
+ 'thick' , 'double' , 'hair' , 'mediumDashed' ,
1706
+ 'dashDot' , 'mediumDashDot' , 'dashDotDot' ,
1707
+ 'mediumDashDotDot' , 'slantDashDot' ].\
1708
+ index (props [k ])
1709
+ except ValueError :
1710
+ props [k ] = 2
1711
+
1712
+ if isinstance (props .get ('font_script' ), string_types ):
1713
+ props ['font_script' ] = ['baseline' , 'superscript' , 'subscript' ].\
1714
+ index (props ['font_script' ])
1715
+
1716
+ if isinstance (props .get ('underline' ), string_types ):
1717
+ props ['underline' ] = {'none' : 0 , 'single' : 1 , 'double' : 2 ,
1718
+ 'singleAccounting' : 33 ,
1719
+ 'doubleAccounting' : 34 }[props ['underline' ]]
1720
+
1721
+ return props
1722
+
1723
+
1581
1724
class _XlsxWriter (ExcelWriter ):
1582
1725
engine = 'xlsxwriter'
1583
1726
supported_extensions = ('.xlsx' ,)
@@ -1612,7 +1755,7 @@ def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0,
1612
1755
wks = self .book .add_worksheet (sheet_name )
1613
1756
self .sheets [sheet_name ] = wks
1614
1757
1615
- style_dict = {}
1758
+ style_dict = {'null' : None }
1616
1759
1617
1760
if _validate_freeze_panes (freeze_panes ):
1618
1761
wks .freeze_panes (* (freeze_panes ))
@@ -1633,7 +1776,8 @@ def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0,
1633
1776
if stylekey in style_dict :
1634
1777
style = style_dict [stylekey ]
1635
1778
else :
1636
- style = self ._convert_to_style (cell .style , num_format_str )
1779
+ style = self .book .add_format (
1780
+ _XlsxStyler .convert (cell .style , num_format_str ))
1637
1781
style_dict [stylekey ] = style
1638
1782
1639
1783
if cell .mergestart is not None and cell .mergeend is not None :
@@ -1647,49 +1791,5 @@ def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0,
1647
1791
startcol + cell .col ,
1648
1792
val , style )
1649
1793
1650
- def _convert_to_style (self , style_dict , num_format_str = None ):
1651
- """
1652
- converts a style_dict to an xlsxwriter format object
1653
- Parameters
1654
- ----------
1655
- style_dict: style dictionary to convert
1656
- num_format_str: optional number format string
1657
- """
1658
-
1659
- # If there is no formatting we don't create a format object.
1660
- if num_format_str is None and style_dict is None :
1661
- return None
1662
-
1663
- # Create a XlsxWriter format object.
1664
- xl_format = self .book .add_format ()
1665
-
1666
- if num_format_str is not None :
1667
- xl_format .set_num_format (num_format_str )
1668
-
1669
- if style_dict is None :
1670
- return xl_format
1671
-
1672
- # Map the cell font to XlsxWriter font properties.
1673
- if style_dict .get ('font' ):
1674
- font = style_dict ['font' ]
1675
- if font .get ('bold' ):
1676
- xl_format .set_bold ()
1677
-
1678
- # Map the alignment to XlsxWriter alignment properties.
1679
- alignment = style_dict .get ('alignment' )
1680
- if alignment :
1681
- if (alignment .get ('horizontal' ) and
1682
- alignment ['horizontal' ] == 'center' ):
1683
- xl_format .set_align ('center' )
1684
- if (alignment .get ('vertical' ) and
1685
- alignment ['vertical' ] == 'top' ):
1686
- xl_format .set_align ('top' )
1687
-
1688
- # Map the cell borders to XlsxWriter border properties.
1689
- if style_dict .get ('borders' ):
1690
- xl_format .set_border ()
1691
-
1692
- return xl_format
1693
-
1694
1794
1695
1795
register_writer (_XlsxWriter )
0 commit comments