Pick Page Editor fonts and images from the platform's banks
The font and image rows are now dropdowns filled from <sedona home>/ontrol: fontBank/imageBank on Rion, fontBankXL/imageBankXL on Orion. Orion lists .oxfl fonts in landscape and .oxfp fonts in portrait, read from the platform service's screenOrientation (loaded from the device in live mode when the tree has not reached it). Images show their file extension while the slot keeps the bare name. The font dialog is gone. In portrait the Page Editor draws a 480x800 screen in frame_orion_portrait.png, and the canvas renders text with the .oxfp fonts, whose glyphs are stored a quarter turn anticlockwise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+18
-17
@@ -18,7 +18,6 @@ Everything is drawn in canvas units: device pixels times the zoom.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import glob
|
||||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import font as tkfont
|
||||
@@ -33,7 +32,8 @@ import app_state
|
||||
import properties
|
||||
from ox_widget_properties import (_Model, BACKGROUND_BIT, ALIGN_MASK, SUBSCRIPT_MASK,
|
||||
DATE_HIDE_YEAR, DATE_TWO_DIGIT_YEAR, DATE_LEADING_ZERO,
|
||||
DATE_MONTH_FIRST, IMAGE_EXTENSIONS, font_names,
|
||||
DATE_MONTH_FIRST, IMAGE_EXTENSIONS, PORTRAIT_FONT_EXTENSION,
|
||||
bank_folders, font_extensions, font_names,
|
||||
parse_lookup, parse_numeric_format, rgb565_to_hex)
|
||||
|
||||
PLACEHOLDER_FILL = "#dce6f2"
|
||||
@@ -71,6 +71,10 @@ def _font(family, pixels, style):
|
||||
class BitmapFont:
|
||||
"""A device font file: fontBank/'32pt Tahoma.bin', or fontBankXL/'.oxfl' (same layout).
|
||||
|
||||
Orion's portrait cut, fontBankXL/'.oxfp', has the same header and table but stores
|
||||
each glyph turned a quarter turn anticlockwise: `height` pixels to a row, `width`
|
||||
rows. glyph_image turns it back, so every caller sees the upright glyph.
|
||||
|
||||
0..63 header; u16 character count at 32, line height at 34
|
||||
64.. 10 bytes per character: code (u8, or u16 above 191), data offset (u24
|
||||
at 2), width, height, x offset (signed), y offset, x advance
|
||||
@@ -80,6 +84,7 @@ class BitmapFont:
|
||||
def __init__(self, path):
|
||||
data = open(path, "rb").read()
|
||||
self.data = data
|
||||
self.rotated = path.lower().endswith(PORTRAIT_FONT_EXTENSION)
|
||||
self.height = data[34]
|
||||
count = data[32] | data[33] << 8
|
||||
self.glyphs = {}
|
||||
@@ -100,7 +105,8 @@ class BitmapFont:
|
||||
|
||||
def glyph_image(self, code):
|
||||
offset, width, height, _, _, _ = self.glyphs[code]
|
||||
img = Image.new("L", (max(1, width), max(1, height)))
|
||||
stored = (height, width) if self.rotated else (width, height)
|
||||
img = Image.new("L", (max(1, stored[0]), max(1, stored[1])))
|
||||
if width and height:
|
||||
raw = self.data[offset:offset + (width * height + 1) // 2]
|
||||
pixels = bytearray(width * height)
|
||||
@@ -108,7 +114,7 @@ class BitmapFont:
|
||||
byte = raw[i >> 1] if (i >> 1) < len(raw) else 0
|
||||
pixels[i] = ((byte >> 4) if i % 2 == 0 else (byte & 0x0F)) * 17
|
||||
img.putdata(pixels)
|
||||
return img
|
||||
return img.transpose(Image.ROTATE_270) if self.rotated else img
|
||||
|
||||
|
||||
_bitmap_fonts = {}
|
||||
@@ -119,18 +125,16 @@ def find_bitmap_font(family, size, style):
|
||||
|
||||
get_Font_File_Name's naming: '<size>pt <Name>', prefixed 'bold ' for bold. There is
|
||||
no italic cut, so italic falls back to the regular one as the device's lookup would
|
||||
find nothing better.
|
||||
find nothing better. The file comes from the platform's own bank, and on Orion in
|
||||
the cut its screen orientation draws with.
|
||||
"""
|
||||
home = getattr(app_state, "SEDONA_HOME", None)
|
||||
if not home or not _HAS_PIL:
|
||||
if not _HAS_PIL:
|
||||
return None
|
||||
stem = f"{'bold ' if style == 1 else ''}{size}pt {family}"
|
||||
candidates = [os.path.join(home, "ontrol", "fontBank", stem + ".bin"),
|
||||
os.path.join(home, "ontrol", "fontBankXL", stem + ".oxfl")]
|
||||
stems = [f"{'bold ' if style == 1 else ''}{size}pt {family}"]
|
||||
if style == 1:
|
||||
plain = f"{size}pt {family}"
|
||||
candidates += [os.path.join(home, "ontrol", "fontBank", plain + ".bin"),
|
||||
os.path.join(home, "ontrol", "fontBankXL", plain + ".oxfl")]
|
||||
stems.append(f"{size}pt {family}")
|
||||
candidates = [os.path.join(folder, stem + ext) for stem in stems
|
||||
for folder in bank_folders("font") for ext in font_extensions()]
|
||||
for path in candidates:
|
||||
if path in _bitmap_fonts:
|
||||
return _bitmap_fonts[path]
|
||||
@@ -179,10 +183,7 @@ def find_image_file(name):
|
||||
"""The local file for a device image name, looked for in Sedona home's image banks."""
|
||||
if not name:
|
||||
return None
|
||||
home = getattr(app_state, "SEDONA_HOME", None)
|
||||
if not home:
|
||||
return None
|
||||
for folder in sorted(glob.glob(os.path.join(home, "ontrol", "imageBank*"))):
|
||||
for folder in bank_folders("image"):
|
||||
for ext in IMAGE_EXTENSIONS:
|
||||
path = os.path.join(folder, name + ext)
|
||||
if os.path.isfile(path):
|
||||
|
||||
Reference in New Issue
Block a user