From 556caea308b2b2686b59e1e774b7bc185cdc67c7 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Tue, 11 Jun 2024 21:38:50 +0200 Subject: [PATCH] Make the file name safe function less restrictive --- novelwriter/common.py | 5 ++-- tests/test_base/test_base_common.py | 37 +++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/novelwriter/common.py b/novelwriter/common.py index 0ef075419..181a79a19 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -523,12 +523,11 @@ def readTextFile(path: str | Path) -> str: def makeFileNameSafe(text: str) -> str: - """Return a filename safe string. + """Return a filename-safe string. See: https://unicode.org/reports/tr15/#Norm_Forms """ text = unicodedata.normalize("NFKC", text).strip() - allowed = (" ", ".", "-", "_") - return "".join(c for c in text if c.isalnum() or c in allowed) + return "".join(c for c in text if c.isprintable() and c not in r'\/:*?"<>|') def getFileSize(path: Path) -> int: diff --git a/tests/test_base/test_base_common.py b/tests/test_base/test_base_common.py index 5bf2d1db2..b46007b5b 100644 --- a/tests/test_base/test_base_common.py +++ b/tests/test_base/test_base_common.py @@ -621,13 +621,40 @@ def testBaseCommon_readTextFile(monkeypatch, fncPath, ipsumText): @pytest.mark.base def testBaseCommon_makeFileNameSafe(): """Test the makeFileNameSafe function.""" - assert makeFileNameSafe(" aaaa ") == "aaaa" - assert makeFileNameSafe("aaaa,bbbb") == "aaaabbbb" - assert makeFileNameSafe("aaaa\tbbbb") == "aaaabbbb" - assert makeFileNameSafe("aaaa bbbb") == "aaaa bbbb" - assert makeFileNameSafe("æøå") == "æøå" + # Trim edges + assert makeFileNameSafe(" Name ") == "Name" + + # Normalise Unicode assert makeFileNameSafe("Stuff œfi2⁵") == "Stuff œfi25" + # No control characters + assert makeFileNameSafe("One\tTwo") == "OneTwo" + assert makeFileNameSafe("One\nTwo") == "OneTwo" + assert makeFileNameSafe("One\rTwo") == "OneTwo" + + # Invalid special characters + assert makeFileNameSafe("One\\Two") == "OneTwo" + assert makeFileNameSafe("One/Two") == "OneTwo" + assert makeFileNameSafe("One:Two") == "OneTwo" + assert makeFileNameSafe("One*Two") == "OneTwo" + assert makeFileNameSafe("One?Two") == "OneTwo" + assert makeFileNameSafe('One"Two') == "OneTwo" + assert makeFileNameSafe("OneTwo") == "OneTwo" + assert makeFileNameSafe("One|Two") == "OneTwo" + + # Names that are valid + assert makeFileNameSafe("One Two") == "One Two" + assert makeFileNameSafe("One,Two") == "One,Two" + assert makeFileNameSafe("One-Two") == "One-Two" + assert makeFileNameSafe("One–Two") == "One–Two" + assert makeFileNameSafe("One—Two") == "One—Two" + assert makeFileNameSafe("Bob's Story") == "Bob's Story" + + # Unicode + assert makeFileNameSafe("æøå") == "æøå" + assert makeFileNameSafe("ßÜ") == "ßÜ" + @pytest.mark.base def testBaseCommon_getFileSize(fncPath):