Rasterize before setting as wallpaper

This makes Set As Wallpaper work on Windows 10 and macOS 10.14
This commit is contained in:
Isaiah Odhner 2023-07-17 21:36:03 -04:00
parent ebbab737d4
commit fece1c48c6
4 changed files with 104 additions and 5 deletions

View File

@ -197,7 +197,7 @@ To preview ANSI art files in file managers like Nautilus, Thunar, Nemo, or Caja,
- Free-Form Select stamping/finalizing is incorrect when the selection is off-screen to the left or top.
- Status bar description can be left blank when selecting a menu item. (I think the `Leave` event can come after closing, once the mouse moves.)
- Menu items like Copy/Cut/Paste are not grayed out when inapplicable. Only unimplemented items are grayed out.
- Set As Wallpaper may not work on your system. For me, on Ubuntu, the wallpaper setting is updated but the picture is not, unless I manually pick it. There is however untested support for many platforms, and you may have better luck than me.
- Set As Wallpaper may not work on your system. It works for me on Windows 10, and macOS 10.14, but on Ubuntu 22, the wallpaper setting is updated but the picture is not, unless I manually pick it. There is also untested support for many other platforms.
- ANSI files (.ans) are treated as UTF-8 when saving and loading, rather than CP437 or Windows-1252 or any other encodings. Unicode is nice and modern terminals support it, but it's not the standard for ANSI files. There isn't really a standard for ANSI files.
- ANSI files are loaded with a white background. This may make sense as a default for text files, but ANSI files either draw a background or assume a black background, being designed for terminals.
- Hitting Enter in View Bitmap mode exits the mode but may also trigger a menu item. Menu items ought to be disabled when hidden, and View Bitmap should prevent the key event from taking other actions if possible.

View File

@ -33,6 +33,7 @@
"Caja",
"clion",
"cmdpxl",
"Consolas",
"dasharray",
"Deutsch",
"DIALOGEX",
@ -57,17 +58,20 @@
"hsrgb",
"humbnail",
"icns",
"Inconsolata",
"Intelli",
"linewrap",
"𝗟𝙇",
"llpaper",
"lrgb",
"Lucida",
"mirc",
"modd",
"mspaint",
"mvim",
"myusername",
"Nemo",
"Noto",
"Odhner",
"pagedown",
"pageup",

View File

@ -41,6 +41,7 @@ from .windows import Window, DialogWindow, CharacterSelectorDialogWindow, Messag
from .file_dialogs import SaveAsDialogWindow, OpenDialogWindow
from .edit_colors import EditColorsDialogWindow
from .localization.i18n import get as _, load_language, remove_hotkey
from .rasterize_ansi_art import rasterize
from .wallpaper import get_config_dir, set_wallpaper
from .auto_restart import restart_on_changes, restart_program
@ -3562,10 +3563,13 @@ Columns: {len(palette) // 2}
try:
dir = os.path.join(get_config_dir("textual-paint"), "wallpaper")
os.makedirs(dir, exist_ok=True)
svg = self.image.get_svg()
image_path = os.path.join(dir, "wallpaper.svg")
with open(image_path, "w", encoding="utf-8") as f:
f.write(svg)
# svg = self.image.get_svg()
# image_path = os.path.join(dir, "wallpaper.svg")
# with open(image_path, "w", encoding="utf-8") as f:
# f.write(svg)
image_path = os.path.join(dir, "wallpaper.png")
pil_image = rasterize(self.image)
pil_image.save(image_path)
set_wallpaper(image_path)
except Exception as e:
self.message_box(_("Paint"), _("Failed to set the wallpaper."), "ok", error=e)

View File

@ -0,0 +1,91 @@
import os
from pathlib import Path
from typing import TYPE_CHECKING
from PIL import Image, ImageDraw, ImageFont
if TYPE_CHECKING:
from textual_paint.paint import AnsiArtDocument
# load font
# font = ImageFont.truetype('fonts/ansi.ttf', size=16, layout_engine=ImageFont.Layout.BASIC)
# font = ImageFont.load_default()
# Pillow actually looks in most of these system font folders by default.
font_dirs = [
# Windows
R"C:\Windows\Fonts",
R"C:\WINNT\Fonts",
R"%LocalAppData%\Microsoft\Windows\Fonts", # (fonts installed only for the current user)
# macOS
"/Library/Fonts",
"/System/Library/Fonts",
"~/Library/Fonts", # (fonts installed only for the current user)
# Linux:
# "/usr/share/fonts", # handled more generally below:
*[data_dir + "/fonts" for data_dir in os.environ.get("XDG_DATA_DIRS", "/usr/share").split(":")],
"/usr/local/share/fonts",
"~/.fonts", # (fonts installed only for the current user)
# Android:
"/system/fonts",
"/data/fonts",
]
font_names = [
"NotoSansMono",
"DejaVuSansMono",
"LiberationMono",
"UbuntuMono",
"Hack",
"FiraMono",
"Inconsolata",
"SourceCodePro",
"DroidSansMono",
"Consolas",
"CourierNew",
"LucidaConsole",
"Monaco",
]
font = None
for font_dir in font_dirs:
path = Path(os.path.expandvars(os.path.expanduser(font_dir)))
files = path.glob("**/*.ttf")
for file in files:
# print(file.stem)
if file.stem in font_names:
font = ImageFont.truetype(str(file), size=16, layout_engine=ImageFont.LAYOUT_BASIC)
break
if font:
break
if not font:
print("Font not found, using default (built-in) font.")
font = ImageFont.load_default()
ch_width: int
ch_height: int
ch_width, ch_height = font.getsize('A') # type: ignore
assert isinstance(ch_width, int), "ch_width is not an int, but a " + str(type(ch_width)) # type: ignore
assert isinstance(ch_height, int), "ch_height is not an int, but a " + str(type(ch_height)) # type: ignore
def rasterize(doc: 'AnsiArtDocument') -> Image.Image:
# make PIL image
img = Image.new('RGB', (doc.width * ch_width, doc.height * ch_height), color='black')
draw = ImageDraw.Draw(img)
# draw cell backgrounds
for y in range(doc.height):
for x in range(doc.width):
bg_color = doc.bg[y][x]
draw.rectangle((x * ch_width, y * ch_height, (x + 1) * ch_width, (y + 1) * ch_height), fill=bg_color)
# draw text
for y in range(doc.height):
for x in range(doc.width):
char = doc.ch[y][x]
bg_color = doc.bg[y][x]
fg_color = doc.fg[y][x]
try:
draw.text((x * ch_width, y * ch_height), char, font=font, fill=fg_color)
except UnicodeEncodeError:
pass
return img