From 91cf3adeb5e2d87f461396d6c371d6a3c6c8e952 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Sun, 13 Oct 2024 15:16:38 -0700 Subject: [PATCH 1/7] feat: add OpenDocument thumbnail support Co-Authored-By: Josh Beatty --- tagstudio/src/core/media_types.py | 19 +++++++++++++++ tagstudio/src/qt/widgets/thumb_renderer.py | 27 ++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/tagstudio/src/core/media_types.py b/tagstudio/src/core/media_types.py index bb1907009..beb7f17b8 100644 --- a/tagstudio/src/core/media_types.py +++ b/tagstudio/src/core/media_types.py @@ -31,6 +31,7 @@ class MediaType(str, Enum): INSTALLER: str = "installer" MATERIAL: str = "material" MODEL: str = "model" + OPEN_DOCUMENT: str = "open_document" PACKAGE: str = "package" PDF: str = "pdf" PLAINTEXT: str = "plaintext" @@ -214,6 +215,18 @@ class MediaCategories: _INSTALLER_SET: set[str] = {".appx", ".msi", ".msix"} _MATERIAL_SET: set[str] = {".mtl"} _MODEL_SET: set[str] = {".3ds", ".fbx", ".obj", ".stl"} + _OPEN_DOCUMENT_SET: set[str] = { + ".fodg", + ".fodp", + ".fods", + ".fodt", + ".mscz", + ".odf", + ".odg", + ".odp", + ".ods", + ".odt", + } _PACKAGE_SET: set[str] = { ".aab", ".akp", @@ -387,6 +400,11 @@ class MediaCategories: extensions=_MODEL_SET, is_iana=True, ) + OPEN_DOCUMENT_TYPES: MediaCategory = MediaCategory( + media_type=MediaType.OPEN_DOCUMENT, + extensions=_OPEN_DOCUMENT_SET, + is_iana=False, + ) PACKAGE_TYPES: MediaCategory = MediaCategory( media_type=MediaType.PACKAGE, extensions=_PACKAGE_SET, @@ -456,6 +474,7 @@ class MediaCategories: INSTALLER_TYPES, MATERIAL_TYPES, MODEL_TYPES, + OPEN_DOCUMENT_TYPES, PACKAGE_TYPES, PDF_TYPES, PLAINTEXT_TYPES, diff --git a/tagstudio/src/qt/widgets/thumb_renderer.py b/tagstudio/src/qt/widgets/thumb_renderer.py index 83eb1c579..ef86a8cfc 100644 --- a/tagstudio/src/qt/widgets/thumb_renderer.py +++ b/tagstudio/src/qt/widgets/thumb_renderer.py @@ -5,6 +5,7 @@ import math import struct +import zipfile from copy import deepcopy from io import BytesIO from pathlib import Path @@ -603,6 +604,27 @@ def _source_engine(self, filepath: Path) -> Image.Image: logger.error("Couldn't render thumbnail", filepath=filepath, error=e) return im + def _open_doc_thumb(self, filepath: Path) -> Image.Image: + """Extract and render a thumbnail for an OpenDocument file. + + Args: + filepath (Path): The path of the file. + """ + file_path_within_zip = "Thumbnails/thumbnail.png" + im: Image.Image = None + with zipfile.ZipFile(filepath, "r") as zip_file: + # Check if the file exists in the zip + if file_path_within_zip in zip_file.namelist(): + # Read the specific file into memory + file_data = zip_file.read(file_path_within_zip) + thumb_im = Image.open(BytesIO(file_data)) + if thumb_im: + im = Image.new("RGB", thumb_im.size, color="#1e1e1e") + im.paste(thumb_im) + else: + logger.error("Couldn't render thumbnail", filepath=filepath) + return im + def _font_short_thumb(self, filepath: Path, size: int) -> Image.Image: """Render a small font preview ("Aa") thumbnail from a font file. @@ -936,6 +958,11 @@ def render( ext, MediaCategories.VIDEO_TYPES, mime_fallback=True ): image = self._video_thumb(_filepath) + # OpenDocument/OpenOffice ====================================== + elif MediaCategories.is_ext_in_category( + ext, MediaCategories.OPEN_DOCUMENT_TYPES, mime_fallback=True + ): + image = self._open_doc_thumb(_filepath) # Plain Text =================================================== if MediaCategories.is_ext_in_category( ext, MediaCategories.PLAINTEXT_TYPES, mime_fallback=True From 2edb11f51ec569cafa4bf3e73f26a795a24b04ee Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Sun, 13 Oct 2024 15:45:58 -0700 Subject: [PATCH 2/7] tests: add test comparing odt to png snapshot --- tagstudio/tests/fixtures/sample.odt | Bin 0 -> 14468 bytes .../test_thumb_renderer/test_odt_preview.png | Bin 0 -> 13127 bytes tagstudio/tests/qt/test_thumb_renderer.py | 21 ++++++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 tagstudio/tests/fixtures/sample.odt create mode 100644 tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_odt_preview.png create mode 100644 tagstudio/tests/qt/test_thumb_renderer.py diff --git a/tagstudio/tests/fixtures/sample.odt b/tagstudio/tests/fixtures/sample.odt new file mode 100644 index 0000000000000000000000000000000000000000..4cb6f2f1cc987ab6025a1870454965355fb4605b GIT binary patch literal 14468 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 literal 0 HcmV?d00001 diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_odt_preview.png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_odt_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..ac2158e7d03c8019a3199e4e9cb7d7fe08549fd5 GIT binary patch literal 13127 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 kjPPI0{NK^cIXt0kps*4~Fsk+5i9m literal 0 HcmV?d00001 diff --git a/tagstudio/tests/qt/test_thumb_renderer.py b/tagstudio/tests/qt/test_thumb_renderer.py new file mode 100644 index 000000000..5a4bca605 --- /dev/null +++ b/tagstudio/tests/qt/test_thumb_renderer.py @@ -0,0 +1,21 @@ +# Copyright (C) 2024 Travis Abendshien (CyanVoxel). +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + +import io +from pathlib import Path + +from PIL import Image +from src.qt.widgets.thumb_renderer import ThumbRenderer +from syrupy.extensions.image import PNGImageSnapshotExtension + + +def test_odt_preview(cwd, snapshot): + file_path: Path = cwd / "fixtures" / "sample.odt" + renderer = ThumbRenderer() + img: Image.Image = renderer._open_doc_thumb(file_path) + + img_bytes = io.BytesIO() + img.save(img_bytes, format="PNG") + img_bytes.seek(0) + assert img_bytes.read() == snapshot(extension_class=PNGImageSnapshotExtension) From 57d0a5a7aee259794f4097dd98cb66231f9b6213 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Sun, 13 Oct 2024 15:57:13 -0700 Subject: [PATCH 3/7] tests: add test comparing ods to png snapshot --- tagstudio/tests/fixtures/sample.ods | Bin 0 -> 11271 bytes .../test_thumb_renderer/test_ods_preview.png | Bin 0 -> 5956 bytes tagstudio/tests/qt/test_thumb_renderer.py | 11 +++++++++++ 3 files changed, 11 insertions(+) create mode 100644 tagstudio/tests/fixtures/sample.ods create mode 100644 tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_ods_preview.png diff --git a/tagstudio/tests/fixtures/sample.ods b/tagstudio/tests/fixtures/sample.ods new file mode 100644 index 0000000000000000000000000000000000000000..ecc97b12f1e657c49f75b3b384e901f9dd83e7bc GIT binary patch literal 11271 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 literal 0 HcmV?d00001 diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_ods_preview.png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_ods_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..5e749f2d3f9bd8b7a65b281e765d54725c9775e1 GIT binary patch literal 5956 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 literal 0 HcmV?d00001 diff --git a/tagstudio/tests/qt/test_thumb_renderer.py b/tagstudio/tests/qt/test_thumb_renderer.py index 5a4bca605..6ea2c9ca5 100644 --- a/tagstudio/tests/qt/test_thumb_renderer.py +++ b/tagstudio/tests/qt/test_thumb_renderer.py @@ -19,3 +19,14 @@ def test_odt_preview(cwd, snapshot): img.save(img_bytes, format="PNG") img_bytes.seek(0) assert img_bytes.read() == snapshot(extension_class=PNGImageSnapshotExtension) + + +def test_ods_preview(cwd, snapshot): + file_path: Path = cwd / "fixtures" / "sample.ods" + renderer = ThumbRenderer() + img: Image.Image = renderer._open_doc_thumb(file_path) + + img_bytes = io.BytesIO() + img.save(img_bytes, format="PNG") + img_bytes.seek(0) + assert img_bytes.read() == snapshot(extension_class=PNGImageSnapshotExtension) From 880617ebb028b8da3e899e88bcc7a56b4fa5a67f Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Thu, 17 Oct 2024 21:15:40 -0700 Subject: [PATCH 4/7] test: combine OpenDocument tests --- tagstudio/src/qt/widgets/thumb_renderer.py | 3 +- ...t_preview[sample.ods-_open_doc_thumb].png} | Bin ...t_preview[sample.odt-_open_doc_thumb].png} | Bin tagstudio/tests/qt/test_thumb_renderer.py | 32 ++++++++++-------- 4 files changed, 19 insertions(+), 16 deletions(-) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_ods_preview.png => test_document_preview[sample.ods-_open_doc_thumb].png} (100%) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_odt_preview.png => test_document_preview[sample.odt-_open_doc_thumb].png} (100%) diff --git a/tagstudio/src/qt/widgets/thumb_renderer.py b/tagstudio/src/qt/widgets/thumb_renderer.py index ef86a8cfc..472516a27 100644 --- a/tagstudio/src/qt/widgets/thumb_renderer.py +++ b/tagstudio/src/qt/widgets/thumb_renderer.py @@ -604,7 +604,8 @@ def _source_engine(self, filepath: Path) -> Image.Image: logger.error("Couldn't render thumbnail", filepath=filepath, error=e) return im - def _open_doc_thumb(self, filepath: Path) -> Image.Image: + @classmethod + def _open_doc_thumb(cls, filepath: Path) -> Image.Image: """Extract and render a thumbnail for an OpenDocument file. Args: diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_ods_preview.png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.ods-_open_doc_thumb].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_ods_preview.png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.ods-_open_doc_thumb].png diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_odt_preview.png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.odt-_open_doc_thumb].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_odt_preview.png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.odt-_open_doc_thumb].png diff --git a/tagstudio/tests/qt/test_thumb_renderer.py b/tagstudio/tests/qt/test_thumb_renderer.py index 6ea2c9ca5..ce184f676 100644 --- a/tagstudio/tests/qt/test_thumb_renderer.py +++ b/tagstudio/tests/qt/test_thumb_renderer.py @@ -5,26 +5,28 @@ import io from pathlib import Path +import pytest from PIL import Image from src.qt.widgets.thumb_renderer import ThumbRenderer from syrupy.extensions.image import PNGImageSnapshotExtension -def test_odt_preview(cwd, snapshot): - file_path: Path = cwd / "fixtures" / "sample.odt" - renderer = ThumbRenderer() - img: Image.Image = renderer._open_doc_thumb(file_path) - - img_bytes = io.BytesIO() - img.save(img_bytes, format="PNG") - img_bytes.seek(0) - assert img_bytes.read() == snapshot(extension_class=PNGImageSnapshotExtension) - - -def test_ods_preview(cwd, snapshot): - file_path: Path = cwd / "fixtures" / "sample.ods" - renderer = ThumbRenderer() - img: Image.Image = renderer._open_doc_thumb(file_path) +@pytest.mark.parametrize( + ["fixture_file", "thumbnailer"], + [ + ( + "sample.odt", + ThumbRenderer._open_doc_thumb, + ), + ( + "sample.ods", + ThumbRenderer._open_doc_thumb, + ), + ], +) +def test_document_preview(cwd, fixture_file, thumbnailer, snapshot): + file_path: Path = cwd / "fixtures" / fixture_file + img: Image.Image = thumbnailer(file_path) img_bytes = io.BytesIO() img.save(img_bytes, format="PNG") From 5bda8cd3e47c23574774af5d38a34f975e45c61e Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Fri, 18 Oct 2024 21:05:17 -0700 Subject: [PATCH 5/7] test: combine compatible preview tests --- ...t_document_preview[sample.epub-_epub_cover].png} | Bin ...esizable_preview[sample.pdf-200-_pdf_thumb].png} | Bin ...preview[sample.svg-200-_image_vector_thumb].png} | Bin 3 files changed, 0 insertions(+), 0 deletions(-) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_epub_preview.png => test_document_preview[sample.epub-_epub_cover].png} (100%) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_pdf_preview.png => test_resizable_preview[sample.pdf-200-_pdf_thumb].png} (100%) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_svg_preview.png => test_resizable_preview[sample.svg-200-_image_vector_thumb].png} (100%) diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_epub_preview.png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.epub-_epub_cover].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_epub_preview.png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.epub-_epub_cover].png diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_pdf_preview.png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_resizable_preview[sample.pdf-200-_pdf_thumb].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_pdf_preview.png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_resizable_preview[sample.pdf-200-_pdf_thumb].png diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_svg_preview.png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_resizable_preview[sample.svg-200-_image_vector_thumb].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_svg_preview.png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_resizable_preview[sample.svg-200-_image_vector_thumb].png From bfbb44f494e9c14c774b4bdf4970b99e7bbc72fd Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Sat, 19 Oct 2024 13:26:52 -0700 Subject: [PATCH 6/7] test: combine preview render tests --- ...ment_preview[sample.pdf-thumbnailer3].png} | Bin ...ment_preview[sample.svg-thumbnailer4].png} | Bin tagstudio/tests/qt/test_thumb_renderer.py | 27 ++++-------------- 3 files changed, 5 insertions(+), 22 deletions(-) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_resizable_preview[sample.pdf-200-_pdf_thumb].png => test_document_preview[sample.pdf-thumbnailer3].png} (100%) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_resizable_preview[sample.svg-200-_image_vector_thumb].png => test_document_preview[sample.svg-thumbnailer4].png} (100%) diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_resizable_preview[sample.pdf-200-_pdf_thumb].png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.pdf-thumbnailer3].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_resizable_preview[sample.pdf-200-_pdf_thumb].png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.pdf-thumbnailer3].png diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_resizable_preview[sample.svg-200-_image_vector_thumb].png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.svg-thumbnailer4].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_resizable_preview[sample.svg-200-_image_vector_thumb].png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.svg-thumbnailer4].png diff --git a/tagstudio/tests/qt/test_thumb_renderer.py b/tagstudio/tests/qt/test_thumb_renderer.py index 4801dbeda..8290ff054 100644 --- a/tagstudio/tests/qt/test_thumb_renderer.py +++ b/tagstudio/tests/qt/test_thumb_renderer.py @@ -3,6 +3,7 @@ # Created for TagStudio: https://github.com/CyanVoxel/TagStudio import io +from functools import partial from pathlib import Path import pytest @@ -26,37 +27,19 @@ "sample.epub", ThumbRenderer._epub_cover, ), - ], -) -def test_document_preview(cwd, fixture_file, thumbnailer, snapshot): - file_path: Path = cwd / "fixtures" / fixture_file - img: Image.Image = thumbnailer(file_path) - - img_bytes = io.BytesIO() - img.save(img_bytes, format="PNG") - img_bytes.seek(0) - - assert img_bytes.read() == snapshot(extension_class=PNGImageSnapshotExtension) - - -@pytest.mark.parametrize( - ["fixture_file", "size", "thumbnailer"], - [ ( "sample.pdf", - 200, - ThumbRenderer._pdf_thumb, + partial(ThumbRenderer._pdf_thumb, size=200), ), ( "sample.svg", - 200, - ThumbRenderer._image_vector_thumb, + partial(ThumbRenderer._image_vector_thumb, size=200), ), ], ) -def test_resizable_preview(cwd, fixture_file, size, thumbnailer, snapshot): +def test_preview_render(cwd, fixture_file, thumbnailer, snapshot): file_path: Path = cwd / "fixtures" / fixture_file - img: Image.Image = thumbnailer(file_path, size) + img: Image.Image = thumbnailer(file_path) img_bytes = io.BytesIO() img.save(img_bytes, format="PNG") From 52ebd4f045d0f3e08641c7dbcab2c7c59b2a1e14 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Sat, 19 Oct 2024 13:29:49 -0700 Subject: [PATCH 7/7] fix: update test snapshots --- ...est_preview_render[sample.epub-_epub_cover].png} | Bin ..._preview_render[sample.ods-_open_doc_thumb].png} | Bin ..._preview_render[sample.odt-_open_doc_thumb].png} | Bin ...est_preview_render[sample.pdf-thumbnailer3].png} | Bin ...est_preview_render[sample.svg-thumbnailer4].png} | Bin 5 files changed, 0 insertions(+), 0 deletions(-) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_document_preview[sample.epub-_epub_cover].png => test_preview_render[sample.epub-_epub_cover].png} (100%) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_document_preview[sample.ods-_open_doc_thumb].png => test_preview_render[sample.ods-_open_doc_thumb].png} (100%) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_document_preview[sample.odt-_open_doc_thumb].png => test_preview_render[sample.odt-_open_doc_thumb].png} (100%) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_document_preview[sample.pdf-thumbnailer3].png => test_preview_render[sample.pdf-thumbnailer3].png} (100%) rename tagstudio/tests/qt/__snapshots__/test_thumb_renderer/{test_document_preview[sample.svg-thumbnailer4].png => test_preview_render[sample.svg-thumbnailer4].png} (100%) diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.epub-_epub_cover].png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.epub-_epub_cover].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.epub-_epub_cover].png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.epub-_epub_cover].png diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.ods-_open_doc_thumb].png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.ods-_open_doc_thumb].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.ods-_open_doc_thumb].png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.ods-_open_doc_thumb].png diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.odt-_open_doc_thumb].png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.odt-_open_doc_thumb].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.odt-_open_doc_thumb].png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.odt-_open_doc_thumb].png diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.pdf-thumbnailer3].png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.pdf-thumbnailer3].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.pdf-thumbnailer3].png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.pdf-thumbnailer3].png diff --git a/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.svg-thumbnailer4].png b/tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.svg-thumbnailer4].png similarity index 100% rename from tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_document_preview[sample.svg-thumbnailer4].png rename to tagstudio/tests/qt/__snapshots__/test_thumb_renderer/test_preview_render[sample.svg-thumbnailer4].png