diff --git a/xlsxwriter/test/comparison/test_quote_name05.py b/xlsxwriter/test/comparison/test_quote_name05.py index e0dca586b..6f96acfaf 100644 --- a/xlsxwriter/test/comparison/test_quote_name05.py +++ b/xlsxwriter/test/comparison/test_quote_name05.py @@ -25,7 +25,9 @@ def test_create_file(self): workbook = Workbook(self.got_filename) - worksheet = workbook.add_worksheet() + sheet_name = "Sheet1" + + worksheet = workbook.add_worksheet(sheet_name) chart = workbook.add_chart({"type": "column"}) chart.axis_ids = [54437760, 59195776] @@ -44,9 +46,9 @@ def test_create_file(self): worksheet.set_portrait() worksheet.vertical_dpi = 200 - chart.add_series({"values": ["Sheet1", 0, 0, 4, 0]}) - chart.add_series({"values": ["Sheet1", 0, 1, 4, 1]}) - chart.add_series({"values": ["Sheet1", 0, 2, 4, 2]}) + chart.add_series({"values": [sheet_name, 0, 0, 4, 0]}) + chart.add_series({"values": [sheet_name, 0, 1, 4, 1]}) + chart.add_series({"values": [sheet_name, 0, 2, 4, 2]}) worksheet.insert_chart("E9", chart) diff --git a/xlsxwriter/test/comparison/test_quote_name06.py b/xlsxwriter/test/comparison/test_quote_name06.py index ac18a8437..a00f3758e 100644 --- a/xlsxwriter/test/comparison/test_quote_name06.py +++ b/xlsxwriter/test/comparison/test_quote_name06.py @@ -25,7 +25,9 @@ def test_create_file(self): workbook = Workbook(self.got_filename) - worksheet = workbook.add_worksheet("Sheet-1") + sheet_name = "Sheet-1" + + worksheet = workbook.add_worksheet(sheet_name) chart = workbook.add_chart({"type": "column"}) chart.axis_ids = [62284544, 83429248] @@ -44,9 +46,9 @@ def test_create_file(self): worksheet.set_portrait() worksheet.vertical_dpi = 200 - chart.add_series({"values": ["Sheet-1", 0, 0, 4, 0]}) - chart.add_series({"values": ["Sheet-1", 0, 1, 4, 1]}) - chart.add_series({"values": ["Sheet-1", 0, 2, 4, 2]}) + chart.add_series({"values": [sheet_name, 0, 0, 4, 0]}) + chart.add_series({"values": [sheet_name, 0, 1, 4, 1]}) + chart.add_series({"values": [sheet_name, 0, 2, 4, 2]}) worksheet.insert_chart("E9", chart) diff --git a/xlsxwriter/test/comparison/test_quote_name07.py b/xlsxwriter/test/comparison/test_quote_name07.py index 725774847..c6735f6b0 100644 --- a/xlsxwriter/test/comparison/test_quote_name07.py +++ b/xlsxwriter/test/comparison/test_quote_name07.py @@ -25,7 +25,9 @@ def test_create_file(self): workbook = Workbook(self.got_filename) - worksheet = workbook.add_worksheet("Sheet'1") + sheet_name = "Sheet'1" + + worksheet = workbook.add_worksheet(sheet_name) chart = workbook.add_chart({"type": "column"}) chart.axis_ids = [48135552, 54701056] @@ -44,9 +46,10 @@ def test_create_file(self): worksheet.set_portrait() worksheet.vertical_dpi = 200 - chart.add_series({"values": ["Sheet'1", 0, 0, 4, 0]}) - chart.add_series({"values": ["Sheet'1", 0, 1, 4, 1]}) - chart.add_series({"values": ["Sheet'1", 0, 2, 4, 2]}) + chart.add_series({"values": [sheet_name, 0, 0, 4, 0]}) + chart.add_series({"values": [sheet_name, 0, 1, 4, 1]}) + chart.add_series({"values": [sheet_name, 0, 2, 4, 2]}) + worksheet.insert_chart("E9", chart) workbook.close() diff --git a/xlsxwriter/test/comparison/test_quote_name08.py b/xlsxwriter/test/comparison/test_quote_name08.py new file mode 100644 index 000000000..0cf30424d --- /dev/null +++ b/xlsxwriter/test/comparison/test_quote_name08.py @@ -0,0 +1,57 @@ +############################################################################### +# +# Tests for XlsxWriter. +# +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c), 2013-2023, John McNamara, jmcnamara@cpan.org +# + +from ..excel_comparison_test import ExcelComparisonTest +from ...workbook import Workbook + + +class TestCompareXLSXFiles(ExcelComparisonTest): + """ + Test file created by XlsxWriter against a file created by Excel. + + """ + + def setUp(self): + + self.set_filename("quote_name08.xlsx") + + def test_create_file(self): + """Test the creation of a simple XlsxWriter file.""" + + workbook = Workbook(self.got_filename) + + sheet_name = "1Sheet" + + worksheet = workbook.add_worksheet(sheet_name) + chart = workbook.add_chart({"type": "column"}) + + chart.axis_ids = [55487104, 84573184] + + data = [ + [1, 2, 3, 4, 5], + [2, 4, 6, 8, 10], + [3, 6, 9, 12, 15], + ] + + worksheet.write_column("A1", data[0]) + worksheet.write_column("B1", data[1]) + worksheet.write_column("C1", data[2]) + + worksheet.repeat_rows(0, 1) + worksheet.set_portrait() + worksheet.vertical_dpi = 200 + + chart.add_series({"values": [sheet_name, 0, 0, 4, 0]}) + chart.add_series({"values": [sheet_name, 0, 1, 4, 1]}) + chart.add_series({"values": [sheet_name, 0, 2, 4, 2]}) + + worksheet.insert_chart("E9", chart) + + workbook.close() + + self.assertExcelEqual() diff --git a/xlsxwriter/test/comparison/test_quote_name09.py b/xlsxwriter/test/comparison/test_quote_name09.py new file mode 100644 index 000000000..b88ce4708 --- /dev/null +++ b/xlsxwriter/test/comparison/test_quote_name09.py @@ -0,0 +1,57 @@ +############################################################################### +# +# Tests for XlsxWriter. +# +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c), 2013-2023, John McNamara, jmcnamara@cpan.org +# + +from ..excel_comparison_test import ExcelComparisonTest +from ...workbook import Workbook + + +class TestCompareXLSXFiles(ExcelComparisonTest): + """ + Test file created by XlsxWriter against a file created by Excel. + + """ + + def setUp(self): + + self.set_filename("quote_name09.xlsx") + + def test_create_file(self): + """Test the creation of a simple XlsxWriter file.""" + + workbook = Workbook(self.got_filename) + + sheet_name = "Sheet_1" + + worksheet = workbook.add_worksheet(sheet_name) + chart = workbook.add_chart({"type": "column"}) + + chart.axis_ids = [54437760, 59195776] + + data = [ + [1, 2, 3, 4, 5], + [2, 4, 6, 8, 10], + [3, 6, 9, 12, 15], + ] + + worksheet.write_column("A1", data[0]) + worksheet.write_column("B1", data[1]) + worksheet.write_column("C1", data[2]) + + worksheet.repeat_rows(0, 1) + worksheet.set_portrait() + worksheet.vertical_dpi = 200 + + chart.add_series({"values": [sheet_name, 0, 0, 4, 0]}) + chart.add_series({"values": [sheet_name, 0, 1, 4, 1]}) + chart.add_series({"values": [sheet_name, 0, 2, 4, 2]}) + + worksheet.insert_chart("E9", chart) + + workbook.close() + + self.assertExcelEqual() diff --git a/xlsxwriter/test/comparison/test_quote_name10.py b/xlsxwriter/test/comparison/test_quote_name10.py new file mode 100644 index 000000000..7b0033545 --- /dev/null +++ b/xlsxwriter/test/comparison/test_quote_name10.py @@ -0,0 +1,57 @@ +############################################################################### +# +# Tests for XlsxWriter. +# +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c), 2013-2023, John McNamara, jmcnamara@cpan.org +# + +from ..excel_comparison_test import ExcelComparisonTest +from ...workbook import Workbook + + +class TestCompareXLSXFiles(ExcelComparisonTest): + """ + Test file created by XlsxWriter against a file created by Excel. + + """ + + def setUp(self): + + self.set_filename("quote_name10.xlsx") + + def test_create_file(self): + """Test the creation of a simple XlsxWriter file.""" + + workbook = Workbook(self.got_filename) + + sheet_name = "Sh.eet.1" + + worksheet = workbook.add_worksheet(sheet_name) + chart = workbook.add_chart({"type": "column"}) + + chart.axis_ids = [46905600, 46796800] + + data = [ + [1, 2, 3, 4, 5], + [2, 4, 6, 8, 10], + [3, 6, 9, 12, 15], + ] + + worksheet.write_column("A1", data[0]) + worksheet.write_column("B1", data[1]) + worksheet.write_column("C1", data[2]) + + worksheet.repeat_rows(0, 1) + worksheet.set_portrait() + worksheet.vertical_dpi = 200 + + chart.add_series({"values": [sheet_name, 0, 0, 4, 0]}) + chart.add_series({"values": [sheet_name, 0, 1, 4, 1]}) + chart.add_series({"values": [sheet_name, 0, 2, 4, 2]}) + + worksheet.insert_chart("E9", chart) + + workbook.close() + + self.assertExcelEqual() diff --git a/xlsxwriter/test/comparison/test_quote_name11.py b/xlsxwriter/test/comparison/test_quote_name11.py new file mode 100644 index 000000000..d9486f762 --- /dev/null +++ b/xlsxwriter/test/comparison/test_quote_name11.py @@ -0,0 +1,57 @@ +############################################################################### +# +# Tests for XlsxWriter. +# +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c), 2013-2023, John McNamara, jmcnamara@cpan.org +# + +from ..excel_comparison_test import ExcelComparisonTest +from ...workbook import Workbook + + +class TestCompareXLSXFiles(ExcelComparisonTest): + """ + Test file created by XlsxWriter against a file created by Excel. + + """ + + def setUp(self): + + self.set_filename("quote_name11.xlsx") + + def test_create_file(self): + """Test the creation of a simple XlsxWriter file.""" + + workbook = Workbook(self.got_filename) + + sheet_name = "Sheeté" + + worksheet = workbook.add_worksheet(sheet_name) + chart = workbook.add_chart({"type": "column"}) + + chart.axis_ids = [46720128, 46721664] + + data = [ + [1, 2, 3, 4, 5], + [2, 4, 6, 8, 10], + [3, 6, 9, 12, 15], + ] + + worksheet.write_column("A1", data[0]) + worksheet.write_column("B1", data[1]) + worksheet.write_column("C1", data[2]) + + worksheet.repeat_rows(0, 1) + worksheet.set_portrait() + worksheet.vertical_dpi = 200 + + chart.add_series({"values": [sheet_name, 0, 0, 4, 0]}) + chart.add_series({"values": [sheet_name, 0, 1, 4, 1]}) + chart.add_series({"values": [sheet_name, 0, 2, 4, 2]}) + + worksheet.insert_chart("E9", chart) + + workbook.close() + + self.assertExcelEqual() diff --git a/xlsxwriter/test/comparison/xlsx_files/quote_name08.xlsx b/xlsxwriter/test/comparison/xlsx_files/quote_name08.xlsx new file mode 100644 index 000000000..da4328d10 Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/quote_name08.xlsx differ diff --git a/xlsxwriter/test/comparison/xlsx_files/quote_name09.xlsx b/xlsxwriter/test/comparison/xlsx_files/quote_name09.xlsx new file mode 100644 index 000000000..a0310b432 Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/quote_name09.xlsx differ diff --git a/xlsxwriter/test/comparison/xlsx_files/quote_name10.xlsx b/xlsxwriter/test/comparison/xlsx_files/quote_name10.xlsx new file mode 100644 index 000000000..7edfe26a6 Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/quote_name10.xlsx differ diff --git a/xlsxwriter/test/comparison/xlsx_files/quote_name11.xlsx b/xlsxwriter/test/comparison/xlsx_files/quote_name11.xlsx new file mode 100644 index 000000000..63dc71bf4 Binary files /dev/null and b/xlsxwriter/test/comparison/xlsx_files/quote_name11.xlsx differ diff --git a/xlsxwriter/test/utility/test_quote_sheetname.py b/xlsxwriter/test/utility/test_quote_sheetname.py new file mode 100644 index 000000000..2ef47fe69 --- /dev/null +++ b/xlsxwriter/test/utility/test_quote_sheetname.py @@ -0,0 +1,157 @@ +############################################################################### +# +# Tests for XlsxWriter. +# +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c), 2013-2024, John McNamara, jmcnamara@cpan.org +# + +import unittest +from ...utility import quote_sheetname + + +class TestUtility(unittest.TestCase): + """ + Test xl_cell_to_rowcol_abs() utility function. + + """ + + def test_quote_sheetname(self): + """Test xl_cell_to_rowcol_abs()""" + + # The following unquoted and quoted sheet names were extracted from + # Excel files. + tests = [ + # A sheetname that is already quoted. + ("'Sheet 1'", "'Sheet 1'"), + # ---------------------------------------------------------------- + # Rule 1. + # ---------------------------------------------------------------- + # Some simple variants on standard sheet names. + ("Sheet1", "Sheet1"), + ("Sheet.1", "Sheet.1"), + ("Sheet_1", "Sheet_1"), + ("Sheet-1", "'Sheet-1'"), + ("Sheet 1", "'Sheet 1'"), + ("Sheet#1", "'Sheet#1'"), + ("#Sheet1", "'#Sheet1'"), + # Sheetnames with single quotes. + ("Sheet'1", "'Sheet''1'"), + ("Sheet''1", "'Sheet''''1'"), + # Single special chars that are unquoted in sheetnames. These are + # variants of the first char rule. + ("_", "_"), + (".", "'.'"), + # White space only. + (" ", "' '"), + (" ", "' '"), + # Sheetnames with unicode or emojis. + ("été", "été"), + ("mangé", "mangé"), + ("Sheet😀", "Sheet😀"), + ("Sheet🤌1", "Sheet🤌1"), + ("Sheet⟦1", "'Sheet⟦1'"), # Unicode punctuation. + ("Sheet᠅1", "'Sheet᠅1'"), # Unicode punctuation. + # ---------------------------------------------------------------- + # Rule 2. + # ---------------------------------------------------------------- + # Sheetnames starting with non-word characters. + ("_Sheet1", "_Sheet1"), + (".Sheet1", "'.Sheet1'"), + ("1Sheet1", "'1Sheet1'"), + ("-Sheet1", "'-Sheet1'"), + ("😀Sheet", "'😀Sheet'"), + # Sheetnames that are digits only also start with a non word char. + ("1", "'1'"), + ("2", "'2'"), + ("1234", "'1234'"), + ("12345678", "'12345678'"), + # ---------------------------------------------------------------- + # Rule 3. + # ---------------------------------------------------------------- + # Worksheet names that look like A1 style references (with the + # row/column number in the Excel allowable range). These are case + # insensitive. + ("A0", "A0"), + ("A1", "'A1'"), + ("a1", "'a1'"), + ("XFD", "XFD"), + ("xfd", "xfd"), + ("XFE1", "XFE1"), + ("ZZZ1", "ZZZ1"), + ("XFD1", "'XFD1'"), + ("xfd1", "'xfd1'"), + ("B1048577", "B1048577"), + ("A1048577", "A1048577"), + ("A1048576", "'A1048576'"), + ("B1048576", "'B1048576'"), + ("B1048576a", "B1048576a"), + ("XFD048576", "'XFD048576'"), + ("XFD1048576", "'XFD1048576'"), + ("XFD01048577", "XFD01048577"), + ("XFD01048576", "'XFD01048576'"), + ("A123456789012345678901", "A123456789012345678901"), # Exceeds u64. + # ---------------------------------------------------------------- + # Rule 4. + # ---------------------------------------------------------------- + # Sheet names that *start* with RC style references (with the + # row/column number in the Excel allowable range). These are case + # insensitive. + ("A", "A"), + ("B", "B"), + ("D", "D"), + ("Q", "Q"), + ("S", "S"), + ("c", "'c'"), + ("C", "'C'"), + ("CR", "CR"), + ("CZ", "CZ"), + ("r", "'r'"), + ("R", "'R'"), + ("C8", "'C8'"), + ("rc", "'rc'"), + ("RC", "'RC'"), + ("RCZ", "RCZ"), + ("RRC", "RRC"), + ("R0C0", "R0C0"), + ("R4C", "'R4C'"), + ("R5C", "'R5C'"), + ("rc2", "'rc2'"), + ("RC2", "'RC2'"), + ("RC8", "'RC8'"), + ("bR1C1", "bR1C1"), + ("R1C1", "'R1C1'"), + ("r1c2", "'r1c2'"), + ("rc2z", "'rc2z'"), + ("bR1C1b", "bR1C1b"), + ("R1C1b", "'R1C1b'"), + ("R1C1R", "'R1C1R'"), + ("C16384", "'C16384'"), + ("C16385", "'C16385'"), + ("C16385Z", "C16385Z"), + ("C16386", "'C16386'"), + ("C16384Z", "'C16384Z'"), + ("PC16384Z", "PC16384Z"), + ("RC16383", "'RC16383'"), + ("RC16385Z", "RC16385Z"), + ("R1048576", "'R1048576'"), + ("R1048577C", "R1048577C"), + ("R1C16384", "'R1C16384'"), + ("R1C16385", "'R1C16385'"), + ("RC16384Z", "'RC16384Z'"), + ("R1048576C", "'R1048576C'"), + ("R1048577C1", "R1048577C1"), + ("R1C16384Z", "'R1C16384Z'"), + ("R1048575C1", "'R1048575C1'"), + ("R1048576C1", "'R1048576C1'"), + ("R1048577C16384", "R1048577C16384"), + ("R1048576C16384", "'R1048576C16384'"), + ("R1048576C16385", "'R1048576C16385'"), + ("ZR1048576C16384", "ZR1048576C16384"), + ("C123456789012345678901Z", "C123456789012345678901Z"), # Exceeds u64. + ("R123456789012345678901Z", "R123456789012345678901Z"), # Exceeds u64. + ] + + for sheetname, exp in tests: + got = quote_sheetname(sheetname) + self.assertEqual(got, exp) diff --git a/xlsxwriter/utility.py b/xlsxwriter/utility.py index ee693c828..23acbabb6 100644 --- a/xlsxwriter/utility.py +++ b/xlsxwriter/utility.py @@ -114,10 +114,25 @@ "~": 7, } +# The following is a list of Emojis used to decide if worksheet names require +# quoting since there is (currently) no native support for matching them in +# Python regular expressions. It is probably unnecessary to exclude them since +# the default quoting is safe in Excel even when unnecessary (the reverse isn't +# true). The Emoji list was generated from: +# +# https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AEmoji%3DYes%3A%5D&abb=on&esc=on&g=&i= +# +emojis = "\u00A9\u00AE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299\U0001F004\U0001F0CF\U0001F170\U0001F171\U0001F17E\U0001F17F\U0001F18E\U0001F191-\U0001F19A\U0001F1E6-\U0001F1FF\U0001F201\U0001F202\U0001F21A\U0001F22F\U0001F232-\U0001F23A\U0001F250\U0001F251\U0001F300-\U0001F321\U0001F324-\U0001F393\U0001F396\U0001F397\U0001F399-\U0001F39B\U0001F39E-\U0001F3F0\U0001F3F3-\U0001F3F5\U0001F3F7-\U0001F4FD\U0001F4FF-\U0001F53D\U0001F549-\U0001F54E\U0001F550-\U0001F567\U0001F56F\U0001F570\U0001F573-\U0001F57A\U0001F587\U0001F58A-\U0001F58D\U0001F590\U0001F595\U0001F596\U0001F5A4\U0001F5A5\U0001F5A8\U0001F5B1\U0001F5B2\U0001F5BC\U0001F5C2-\U0001F5C4\U0001F5D1-\U0001F5D3\U0001F5DC-\U0001F5DE\U0001F5E1\U0001F5E3\U0001F5E8\U0001F5EF\U0001F5F3\U0001F5FA-\U0001F64F\U0001F680-\U0001F6C5\U0001F6CB-\U0001F6D2\U0001F6D5-\U0001F6D7\U0001F6DC-\U0001F6E5\U0001F6E9\U0001F6EB\U0001F6EC\U0001F6F0\U0001F6F3-\U0001F6FC\U0001F7E0-\U0001F7EB\U0001F7F0\U0001F90C-\U0001F93A\U0001F93C-\U0001F945\U0001F947-\U0001F9FF\U0001FA70-\U0001FA7C\U0001FA80-\U0001FA88\U0001FA90-\U0001FABD\U0001FABF-\U0001FAC5\U0001FACE-\U0001FADB\U0001FAE0-\U0001FAE8\U0001FAF0-\U0001FAF8" # noqa + # Compile performance critical regular expressions. re_leading = re.compile(r"^\s") re_trailing = re.compile(r"\s$") re_range_parts = re.compile(r"(\$?)([A-Z]{1,3})(\$?)(\d+)") +re_quote_rule1 = re.compile(rf"[^\w\.{emojis}]") +re_quote_rule2 = re.compile(rf"^[\d\.{emojis}]") +re_quote_rule3 = re.compile(r"^([A-Z]{1,3}\d+)$") +re_quote_rule4_row = re.compile(r"^R(\d+)") +re_quote_rule4_column = re.compile(r"^R?C(\d+)") def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False): @@ -368,8 +383,9 @@ def xl_range_formula(sheetname, first_row, first_col, last_row, last_col): def quote_sheetname(sheetname): """ - Convert a worksheet name to a quoted name if it contains spaces or - special characters. + Sheetnames used in references should be quoted if they contain any spaces, + special characters or if they look like a A1 or RC cell reference. The rules + are shown inline below. Args: sheetname: The worksheet name. String. @@ -378,8 +394,72 @@ def quote_sheetname(sheetname): A quoted worksheet string. """ - - if not sheetname.isalnum() and not sheetname.startswith("'"): + uppercase_sheetname = sheetname.upper() + requires_quoting = False + col_max = 163_84 + row_max = 1048576 + + # Don't quote sheetname if it is already quoted by the user. + if not sheetname.startswith("'"): + + # -------------------------------------------------------------------- + # Rule 1. Sheet names that contain anything other than \w and "." + # characters must be quoted. + # -------------------------------------------------------------------- + if re_quote_rule1.search(sheetname): + requires_quoting = True + + # -------------------------------------------------------------------- + # Rule 2. Sheet names that start with a digit or "." must be quoted. + # -------------------------------------------------------------------- + elif re_quote_rule2.search(sheetname): + requires_quoting = True + + # -------------------------------------------------------------------- + # Rule 3. Sheet names must not be a valid A1 style cell reference. + # Valid means that the row and column range values must also be within + # Excel row and column limits. + # -------------------------------------------------------------------- + elif re_quote_rule3.match(uppercase_sheetname): + match = re_quote_rule3.match(uppercase_sheetname) + cell = match.group(1) + (row, col) = xl_cell_to_rowcol(cell) + + if row >= 0 and row < row_max and col >= 0 and col < col_max: + requires_quoting = True + + # -------------------------------------------------------------------- + # Rule 4. Sheet names must not *start* with a valid RC style cell + # reference. Other characters after the valid RC reference are ignored + # by Excel. Valid means that the row and column range values must also + # be within Excel row and column limits. + # + # Note: references without trailing characters like R12345 or C12345 + # are caught by Rule 3. Negative references like R-12345 are caught by + # Rule 1 due to dash. + # -------------------------------------------------------------------- + + # Rule 4a. Check for sheet names that start with R1 style references. + elif re_quote_rule4_row.match(uppercase_sheetname): + match = re_quote_rule4_row.match(uppercase_sheetname) + row = int(match.group(1)) + + if row > 0 and row <= row_max: + requires_quoting = True + + # Rule 4b. Check for sheet names that start with C1 or RC1 style + elif re_quote_rule4_column.match(uppercase_sheetname): + match = re_quote_rule4_column.match(uppercase_sheetname) + col = int(match.group(1)) + + if col > 0 and col <= col_max: + requires_quoting = True + + # Rule 4c. Check for some single R/C references. + elif uppercase_sheetname in ("R", "C", "RC"): + requires_quoting = True + + if requires_quoting: # Double quote any single quotes. sheetname = sheetname.replace("'", "''")