feat: render .pxd thumbnails (#1430)

* feat: render .pxd thumbnails.

* move .pxd to _DOCUMENT_SET, rename method, fix doc and typos.

* add missing break.
This commit is contained in:
Sola-ris
2026-07-09 08:05:48 +02:00
committed by GitHub
parent 377593b190
commit c755894c84
3 changed files with 33 additions and 24 deletions
+2 -1
View File
@@ -83,7 +83,7 @@ Audio thumbnails will default to embedded cover art (if any) and fallback to gen
Preview support for office documents or well-known project file formats varies by the format and whether or not embedded thumbnails are available to be read from. OpenDocument-based files are typically supported.
| Filetype | Extensions | Preview Type |
| ------------------------------------ | --------------------- | -------------------------------------------------------------------------- |
|------------------------------------- |-----------------------|----------------------------------------------------------------------------|
| Blender | `.blend`, `.blend<#>` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
| Clip Studio Paint | `.clip` | Embedded thumbnail |
| Keynote (Apple iWork) | `.key` | Embedded thumbnail |
@@ -98,6 +98,7 @@ Preview support for office documents or well-known project file formats varies b
| Paint.NET | `.pdn` | Embedded thumbnail |
| PDF | `.pdf` | First page render |
| Photoshop | `.psd` | Flattened image render |
| Pixelmator Pro (Apple iWork) | `.pxd` | Embedded thumbnail |
| PowerPoint (Microsoft Office) | `.pptx`, `.ppt` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
### :material-archive: Archives
+11 -10
View File
@@ -12,14 +12,14 @@ import structlog
logger = structlog.get_logger(__name__)
FILETYPE_EQUIVALENTS = [
set(["aif", "aiff", "aifc"]),
set(["html", "htm", "xhtml", "shtml", "dhtml"]),
set(["jfif", "jpeg_large", "jpeg", "jpg_large", "jpg"]),
set(["json", "jsonc", "json5"]),
set(["md", "markdown", "mkd", "rmd"]),
set(["tar.gz", "tgz"]),
set(["xml", "xul"]),
set(["yaml", "yml"]),
{"aif", "aiff", "aifc"},
{"html", "htm", "xhtml", "shtml", "dhtml"},
{"jfif", "jpeg_large", "jpeg", "jpg_large", "jpg"},
{"json", "jsonc", "json5"},
{"md", "markdown", "mkd", "rmd"},
{"tar.gz", "tgz"},
{"xml", "xul"},
{"yaml", "yml"},
]
@@ -75,7 +75,7 @@ class MediaCategory:
extensions (set[str]): The set of file extensions associated with this category.
Includes leading ".", all lowercase, and does not need to be unique to this category.
is_iana (bool): Represents whether or not this is an IANA registered category.
is_iana (bool): Represents whether this is an IANA registered category.
"""
media_type: MediaType
@@ -257,6 +257,7 @@ class MediaCategories:
".odt",
".pages",
".pdf",
".pxd",
".rtf",
".tex",
".wpd",
@@ -336,7 +337,7 @@ class MediaCategories:
".webp",
}
_INSTALLER_SET: set[str] = {".appx", ".msi", ".msix"}
_IWORK_SET: set[str] = {".key", ".pages", ".numbers"}
_IWORK_SET: set[str] = {".key", ".numbers", ".pages"}
_MATERIAL_SET: set[str] = {".mtl"}
_MDIPACK_SET: set[str] = {".mdp"}
_MODEL_SET: set[str] = {".3ds", ".fbx", ".obj", ".stl"}
+20 -13
View File
@@ -623,7 +623,7 @@ class ThumbRenderer(QObject):
image (Image.Image): The image to apply the edge to.
edge (tuple[Image.Image, Image.Image]): The edge images to apply.
Item 0 is the inner highlight, and item 1 is the outer shadow.
faded (bool): Whether or not to apply a faded version of the edge.
faded (bool): Whether to apply a faded version of the edge.
Used for light themes.
"""
opacity: float = 1.0 if not faded else 0.8
@@ -847,7 +847,7 @@ class ThumbRenderer(QObject):
@staticmethod
def _krita_thumb(filepath: Path) -> Image.Image | None:
"""Extract and render a thumbnail for an Krita file.
"""Extract and render a thumbnail for a Krita file.
Args:
filepath (Path): The path of the file.
@@ -1218,14 +1218,18 @@ class ThumbRenderer(QObject):
return im
@staticmethod
def _iwork_thumb(filepath: Path) -> Image.Image | None:
"""Extract and render a thumbnail for an Apple iWork (Pages, Numbers, Keynote) file.
def _apple_embedded_thumb(filepath: Path) -> Image.Image | None:
"""Extract and render an apple embedded thumbnail (iWork, Apple Creative Studio).
Args:
filepath (Path): The path of the file.
"""
preview_thumb_dir = "preview.jpg"
quicklook_thumb_dir = "QuickLook/Thumbnail.jpg"
thumb_files: list[str] = [
"preview.jpg",
"QuickLook/Preview.heic",
"QuickLook/Thumbnail.jpg",
"QuickLook/Thumbnail.heic",
]
im: Image.Image | None = None
def get_image(path: str) -> Image.Image | None:
@@ -1240,10 +1244,10 @@ class ThumbRenderer(QObject):
thumb: Image.Image | None = None
# Check if the file exists in the zip
if preview_thumb_dir in zip_file.namelist():
thumb = get_image(preview_thumb_dir)
elif quicklook_thumb_dir in zip_file.namelist():
thumb = get_image(quicklook_thumb_dir)
for thumb_file in thumb_files:
if thumb_file in zip_file.namelist():
thumb = get_image(thumb_file)
break
else:
logger.error("Couldn't render thumbnail", filepath=filepath)
@@ -1314,7 +1318,7 @@ class ThumbRenderer(QObject):
page_size *= size / page_size.height()
else:
page_size *= size / page_size.width()
# Enlarge image for antialiasing
# Enlarge image for anti-aliasing
scale_factor = 2.5
page_size *= scale_factor
# Render image with no anti-aliasing for speed
@@ -1865,8 +1869,11 @@ class ThumbRenderer(QObject):
):
image = self._open_doc_thumb(_filepath)
# Apple iWork Suite ============================================
elif MediaCategories.is_ext_in_category(ext, MediaCategories.IWORK_TYPES):
image = self._iwork_thumb(_filepath)
elif (
MediaCategories.is_ext_in_category(ext, MediaCategories.IWORK_TYPES)
or ext == ".pxd"
):
image = self._apple_embedded_thumb(_filepath)
# Plain Text ===================================================
elif MediaCategories.is_ext_in_category(
ext, MediaCategories.PLAINTEXT_TYPES, mime_fallback=True