Mega update (updates will be more normal from here on)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# Tools
|
||||
## clangd.zsh
|
||||
This is a simple script that generates a `.clangd` file pointing clang to your local Pebble SDK header.
|
||||
## scale_bdf.py
|
||||
This script integer scales BDF fonts.
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env zsh
|
||||
echo "CompileFlags:
|
||||
Add:
|
||||
[
|
||||
-DPBL_DISPLAY_WIDTH=200,
|
||||
-DPBL_DISPLAY_HEIGHT=228,
|
||||
-xc,
|
||||
-nostdinc,
|
||||
-DPBL_COLOR,
|
||||
-DPBL_RECT,
|
||||
-I${HOME}/.pebble-sdk/SDKs/current/sdk-core/pebble/emery/include,
|
||||
-include$(pwd)/build/include/message_keys.auto.h,
|
||||
-I$(pwd)/build/emery,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/stdint.h,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/stdlib.h,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/time.h,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/string.h,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/include/stddef.h,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/include/stdbool.h,
|
||||
]" > ./.clangd
|
||||
Executable
+182
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
|
||||
def scale_bitmap_rows(rows, bbx_width, factor):
|
||||
new_width = bbx_width * factor
|
||||
new_bytes = (new_width + 7) // 8
|
||||
new_bits = new_bytes * 8
|
||||
|
||||
old_bytes = (bbx_width + 7) // 8
|
||||
old_bits = old_bytes * 8
|
||||
|
||||
result = []
|
||||
for row_hex in rows:
|
||||
hex_val = int(row_hex, 16)
|
||||
|
||||
# Extract bits MSB-first — BDF pixels are left-aligned within
|
||||
# ceil(width/8) bytes, MSB of first byte = leftmost pixel.
|
||||
bits = []
|
||||
for bit_idx in range(bbx_width):
|
||||
bit = (hex_val >> (old_bits - 1 - bit_idx)) & 1
|
||||
bits.append(bit)
|
||||
|
||||
# Expand horizontally: each bit repeated `factor` times
|
||||
expanded = []
|
||||
for b in bits:
|
||||
expanded.extend([b] * factor)
|
||||
|
||||
# Pack back into MSB-first, left-aligned hex
|
||||
packed = 0
|
||||
for idx, b in enumerate(expanded):
|
||||
if b:
|
||||
packed |= 1 << (new_bits - 1 - idx)
|
||||
|
||||
hex_str = format(packed, f'0{new_bytes * 2}X')
|
||||
|
||||
# Expand vertically: duplicate the row `factor` times
|
||||
for _ in range(factor):
|
||||
result.append(hex_str)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def scale_bdf(input_path, output_path, factor):
|
||||
if factor < 1 or not isinstance(factor, int):
|
||||
print(f"Error: scale factor must be a positive integer, got {factor}")
|
||||
sys.exit(1)
|
||||
|
||||
with open(input_path) as f:
|
||||
lines = f.readlines()
|
||||
|
||||
out = []
|
||||
in_bitmap = False
|
||||
bitmap_rows = []
|
||||
current_bbx_w = 0
|
||||
current_bbx_h = 0
|
||||
current_bbx_x = 0
|
||||
current_bbx_y = 0
|
||||
|
||||
for line in lines:
|
||||
line = line.rstrip('\n')
|
||||
|
||||
if in_bitmap:
|
||||
if line == 'ENDCHAR':
|
||||
scaled_rows = scale_bitmap_rows(bitmap_rows, current_bbx_w, factor)
|
||||
out.extend(scaled_rows)
|
||||
out.append('ENDCHAR')
|
||||
in_bitmap = False
|
||||
bitmap_rows = []
|
||||
else:
|
||||
bitmap_rows.append(line)
|
||||
continue
|
||||
|
||||
if line == 'STARTCHAR':
|
||||
in_bitmap = False
|
||||
out.append(line)
|
||||
elif line == 'BITMAP':
|
||||
in_bitmap = True
|
||||
out.append('BITMAP')
|
||||
elif line.startswith('FONT '):
|
||||
out.append(scale_font_line(line, factor))
|
||||
elif line.startswith('SIZE '):
|
||||
parts = line.split()
|
||||
out.append(
|
||||
f'SIZE {int(parts[1]) * factor} {parts[2]} {parts[3]}'
|
||||
)
|
||||
elif line.startswith('FONTBOUNDINGBOX '):
|
||||
parts = line.split()
|
||||
out.append(
|
||||
f'FONTBOUNDINGBOX {int(parts[1]) * factor} '
|
||||
f'{int(parts[2]) * factor} '
|
||||
f'{int(parts[3]) * factor} '
|
||||
f'{int(parts[4]) * factor}'
|
||||
)
|
||||
elif line.startswith('BBX '):
|
||||
parts = line.split()
|
||||
current_bbx_w = int(parts[1])
|
||||
current_bbx_h = int(parts[2])
|
||||
current_bbx_x = int(parts[3])
|
||||
current_bbx_y = int(parts[4])
|
||||
out.append(
|
||||
f'BBX {current_bbx_w * factor} '
|
||||
f'{current_bbx_h * factor} '
|
||||
f'{current_bbx_x * factor} '
|
||||
f'{current_bbx_y * factor}'
|
||||
)
|
||||
elif line.startswith('PIXEL_SIZE '):
|
||||
parts = line.split()
|
||||
out.append(f'PIXEL_SIZE {int(parts[1]) * factor}')
|
||||
elif line.startswith('POINT_SIZE '):
|
||||
parts = line.split()
|
||||
out.append(f'POINT_SIZE {int(parts[1]) * factor}')
|
||||
elif line.startswith('FONT_ASCENT '):
|
||||
parts = line.split()
|
||||
out.append(f'FONT_ASCENT {int(parts[1]) * factor}')
|
||||
elif line.startswith('FONT_DESCENT '):
|
||||
parts = line.split()
|
||||
out.append(f'FONT_DESCENT {int(parts[1]) * factor}')
|
||||
elif line.startswith('CAP_HEIGHT '):
|
||||
parts = line.split()
|
||||
val = int(parts[1])
|
||||
if val > 0:
|
||||
out.append(f'CAP_HEIGHT {val * factor}')
|
||||
else:
|
||||
out.append(line)
|
||||
elif line.startswith('X_HEIGHT '):
|
||||
parts = line.split()
|
||||
val = int(parts[1])
|
||||
if val > 0:
|
||||
out.append(f'X_HEIGHT {val * factor}')
|
||||
else:
|
||||
out.append(line)
|
||||
elif line.startswith('AVERAGE_WIDTH '):
|
||||
parts = line.split()
|
||||
out.append(f'AVERAGE_WIDTH {int(parts[1]) * factor}')
|
||||
elif line.startswith('DWIDTH '):
|
||||
parts = line.split()
|
||||
out.append(f'DWIDTH {int(parts[1]) * factor} {parts[2]}')
|
||||
elif line.startswith('SWIDTH '):
|
||||
parts = line.split()
|
||||
out.append(f'SWIDTH {int(parts[1]) * factor} {parts[2]}')
|
||||
elif line.startswith('BITED_DWIDTH '):
|
||||
parts = line.split()
|
||||
out.append(f'BITED_DWIDTH {int(parts[1]) * factor}')
|
||||
elif line.startswith('METRICSSET '):
|
||||
pass # skip Apple metrics, they'll be regenerated
|
||||
else:
|
||||
out.append(line)
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
f.write('\n'.join(out) + '\n')
|
||||
|
||||
print(f'Scaled {input_path} by {factor}x → {output_path}')
|
||||
|
||||
|
||||
def scale_font_line(line, factor):
|
||||
"""Scale the XLFD FONT line."""
|
||||
# FONT -foundry-family-weight-slant-setwidth-addstyle-pixel-point-resx-resy...
|
||||
# Fields are dash-separated
|
||||
parts = line.split('-')
|
||||
# parts[0] = 'FONT '
|
||||
# parts[7] = pixel size (should be int)
|
||||
# parts[8] = point size (decipoints)
|
||||
try:
|
||||
pixel_size = int(parts[7])
|
||||
parts[7] = str(pixel_size * factor)
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
try:
|
||||
point_size = int(parts[8])
|
||||
parts[8] = str(point_size * factor)
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
return '-'.join(parts)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 4:
|
||||
print(f'Usage: {sys.argv[0]} <input.bdf> <output.bdf> <scale_factor>')
|
||||
print(f'Example: {sys.argv[0]} lecoishmono.bdf lecoishmono_18.bdf 2')
|
||||
sys.exit(1)
|
||||
scale_bdf(sys.argv[1], sys.argv[2], int(sys.argv[3]))
|
||||
Reference in New Issue
Block a user