Skip to content

Bug: Double-byte non-ASCII font display #132

Description

@dengyt2018

Describe the bug
在显示双字节的文字后会多出一个空白点位。
定位到 Backend 的 draw() 函数。当处理完一个 non-ascii 的双字节字符时会当两个字符占位,x 坐标多增加了一次。ascii 字符是正常显示。自定义字体也有同样问题。
测试的字体使用的是 embedded_graphics_unicodefonts::MONO_18X18_JA

After displaying double-byte characters, an extra blank space appears.

I've located the issue to the Backend's draw() function. When processing a non-ASCII double-byte character, it's being treated as two characters for spacing purposes, causing the x-coordinate to be incremented twice. ASCII characters display correctly.

Custom fonts have the same issue.

Test font used: embedded_graphics_unicodefonts::MONO_18X18_JA

Expected display image

Image

Abnormal display image

Image

Image from ESP32 development board

Image

Cargo.toml

[dependencies]
embedded-graphics-unicodefonts = "0.2"
embedded-graphics-simulator = "0.7"
embedded-graphics = "0.8.1"
ratatui = { version = "0.30.0-beta.0", default-features = false }
mousefood = { git = "https://github.com/j-g00da/mousefood.git", rev = "c8d5e4a5", default-features = false, features = ["fonts"] }

Test code

#![allow(unused)]

use embedded_graphics::image::ImageRaw;
use embedded_graphics::mono_font::MonoFont;
use embedded_graphics::pixelcolor::Bgr565;
use embedded_graphics::{
    mono_font::MonoTextStyle, pixelcolor::BinaryColor, prelude::*, text::Text,
};
use embedded_graphics_simulator::{
    BinaryColorTheme, OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window,
};
use mousefood::error::Error;
use mousefood::{EmbeddedBackend, EmbeddedBackendConfig};
use ratatui::layout::{Constraint, Layout};
use ratatui::style::Stylize;
use ratatui::widgets::{Paragraph, Wrap};
use ratatui::{Frame, Terminal};

const TEXT: &str = "A点陣字ACああA";

fn main() -> Result<(), Error> {
    //let font = embedded_graphics_unicodefonts::MONO_6X12;
    let font = embedded_graphics_unicodefonts::MONO_18X18_JA;
    //let font = CUSTOM_FONT_16X16;

    let output_settings = OutputSettingsBuilder::new()
        .theme(BinaryColorTheme::OledBlue)
        .build();
    let mut display = SimulatorDisplay::<BinaryColor>::new(Size::new(320, 128));
    let text_style = MonoTextStyle::new(&font, BinaryColor::On);
    Text::new(TEXT, Point::new(0, 20), text_style)
        .draw(&mut display)
        .unwrap();
    Text::new(TEXT, Point::new(0, 80), text_style)
        .draw(&mut display)
        .unwrap();
    Window::new(
        "Display with normal spacing for double-byte characters",
        &output_settings,
    )
    .show_static(&display);

    let mut display = SimulatorDisplay::<Bgr565>::new(Size::new(320, 120));

    let mut simulator_window = Window::new(
        "Ratatui display with double-byte character spacing",
        &output_settings,
    );
    let mut backend_config = EmbeddedBackendConfig {
        flush_callback: Box::new(move |display| {
            simulator_window.update(display);
            if simulator_window.events().any(|e| e == SimulatorEvent::Quit) {
                panic!("simulator window closed");
            }
        }),
        ..Default::default()
    };
    backend_config.font_regular = font;

    let backend: EmbeddedBackend<SimulatorDisplay<_>, _> =
        EmbeddedBackend::new(&mut display, backend_config);

    let mut terminal = Terminal::new(backend)?;

    loop {
        terminal.draw(draw)?;
    }
}

fn draw(frame: &mut Frame) {
    let [a, b] = Layout::vertical([Constraint::Ratio(1, 2); 2])
        .spacing(0)
        .areas(frame.area());
    let paragraph = Paragraph::new(TEXT.dark_gray()).wrap(Wrap { trim: true });
    let paragraph2 = Paragraph::new(TEXT.dark_gray()).wrap(Wrap { trim: true });
    frame.render_widget(paragraph, a);
    frame.render_widget(paragraph2, b);
}

const CUSTOM_FONT_16X16: MonoFont = MonoFont {
    image: ImageRaw::<BinaryColor>::new(&FONT_DATA, FONT_DATA.len().div_ceil(2) as u32),
    character_size: Size::new(16, 16),
    character_spacing: 2,
    baseline: 13,
    strikethrough: embedded_graphics::mono_font::DecorationDimensions::default_strikethrough(16),
    underline: embedded_graphics::mono_font::DecorationDimensions::default_underline(16),
    glyph_mapping: &CustomGlyphMapping,
};

/// ImageRaw Font Data: 点陣字ABCあ
const FONT_DATA: [u8; 224] = [
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7C, 0x20, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x02, 0x00, 0x7F, 0xFE, 0x02, 0x00, 0x45, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x02, 0xC0, 0x40, 0x02, 0x03, 0xFC, 0x48, 0x20, 0x18, 0x00, 0x7C, 0x00, 0x3C, 0x00, 0x1F, 0x00,
    0x80, 0x04, 0x02, 0x00, 0x49, 0xFC, 0x24, 0x00, 0x42, 0x00, 0x42, 0x00, 0x04, 0x80, 0x1F, 0xE0,
    0x02, 0x00, 0x51, 0x24, 0x24, 0x00, 0x42, 0x00, 0x42, 0x00, 0x04, 0x80, 0x00, 0x40, 0x1F, 0xF0,
    0x49, 0x24, 0x42, 0x00, 0x42, 0x00, 0x40, 0x00, 0x07, 0xE0, 0x00, 0x80, 0x20, 0x10, 0x49, 0xFC,
    0x42, 0x00, 0x7C, 0x00, 0x40, 0x00, 0x0D, 0x10, 0x01, 0x00, 0x20, 0x10, 0x45, 0x24, 0x7E, 0x00,
    0x42, 0x00, 0x40, 0x00, 0x15, 0x08, 0xFF, 0xFE, 0x20, 0x10, 0x45, 0x24, 0x42, 0x00, 0x42, 0x00,
    0x40, 0x00, 0x22, 0x08, 0x01, 0x00, 0x3F, 0xF0, 0x45, 0xFC, 0x42, 0x00, 0x42, 0x00, 0x42, 0x00,
    0x26, 0x08, 0x01, 0x00, 0x00, 0x00, 0x68, 0x20, 0x42, 0x00, 0x42, 0x00, 0x42, 0x00, 0x2A, 0x10,
    0x01, 0x00, 0x24, 0x88, 0x53, 0xFE, 0x42, 0x00, 0x7C, 0x00, 0x3C, 0x00, 0x10, 0x60, 0x01, 0x00,
    0x22, 0x44, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x42, 0x44,
    0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x04, 0x40, 0x20,
];
struct CustomGlyphMapping;

impl embedded_graphics::mono_font::mapping::GlyphMapping for CustomGlyphMapping {
    fn index(&self, c: char) -> usize {
        match c {
            'A' => 0,
            'B' => 1,
            'C' => 2,
            'あ' => 3,
            '字' => 4,
            '点' => 5,
            '陣' => 6,
            _ => 7,
        }
    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions