@@ -1578,6 +1578,149 @@ def _convert_to_style(cls, style_dict, num_format_str=None):
15781578register_writer (_XlwtWriter )
15791579
15801580
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+
15811724class _XlsxWriter (ExcelWriter ):
15821725 engine = 'xlsxwriter'
15831726 supported_extensions = ('.xlsx' ,)
@@ -1612,7 +1755,7 @@ def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0,
16121755 wks = self .book .add_worksheet (sheet_name )
16131756 self .sheets [sheet_name ] = wks
16141757
1615- style_dict = {}
1758+ style_dict = {'null' : None }
16161759
16171760 if _validate_freeze_panes (freeze_panes ):
16181761 wks .freeze_panes (* (freeze_panes ))
@@ -1633,7 +1776,8 @@ def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0,
16331776 if stylekey in style_dict :
16341777 style = style_dict [stylekey ]
16351778 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 ))
16371781 style_dict [stylekey ] = style
16381782
16391783 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,
16471791 startcol + cell .col ,
16481792 val , style )
16491793
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-
16941794
16951795register_writer (_XlsxWriter )
0 commit comments