Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream'
Browse files Browse the repository at this point in the history
  • Loading branch information
ethan-beauc committed Dec 18, 2023
2 parents 26291c8 + d3e2086 commit a3f7e09
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 108 deletions.
58 changes: 48 additions & 10 deletions docs/fonts.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Fonts

A font is a set of bitmapped glyphs for displaying text on a [DMS]. Fonts can
contain only printable ASCII characters (U+0020 to U+007E).
A font is a set of bitmapped glyphs for displaying text on a [DMS].

<details>
<summary>API Resources</summary>
Expand All @@ -15,6 +14,49 @@ contain only printable ASCII characters (U+0020 to U+007E).

</details>

## Importing

Fonts must be imported into the IRIS database. First, they must be in `tfon`
format, which looks like this:

```text
font_name: tfon example
font_number: 2
char_spacing: 1
line_spacing: 3
ch: 52 4
...@@@.
..@.@@.
.@..@@.
@...@@.
@@@@@@@
....@@.
....@@.
ch: 65 A
.@@@@.
@@..@@
@@..@@
@@@@@@
@@..@@
@@..@@
@@..@@
```

Many fonts are [included](#predefined-fonts) with IRIS. Alternatively,
existing fonts in the popular [BDF] format can be converted to `tfon` using
the [fontu] utility.

To import a font, use tfon_import.py (in `bin` directory):

```
tfon_import.py [font file] | psql tms
```

Also, each font file must be copied to the `/var/www/html/iris/api/tfon/`
directory to make it available in the [REST API].

## Predefined Fonts

A number of fonts are included in the `/var/lib/iris/fonts` directory. These
Expand Down Expand Up @@ -43,18 +85,11 @@ Name | Number | Description
`F24` | 24 | 24 px height
`F26` | 26 | 26 px height

_† Normally font number is the same as pixel height, but variations use
_Normally font number is the same as pixel height, but variations use
1xx or 2xx._

Numbers 1-4 are reserved for **permanent** fonts used by some signs.

To import a font into the IRIS database, use tfon_import.py (in `bin`
directory):

```
tfon_import.py [font file] | psql tms
```

### Non-ASCII Characters

Since NTCIP 1203 does not support Unicode, ASCII characters must be used as
Expand Down Expand Up @@ -103,4 +138,7 @@ Pressing `Send Settings` will cause all necessary fonts to be sent to the sign.
the `/var/lib/iris/fonts/{sign_name}` directory.


[BDF]: https://en.wikipedia.org/wiki/Glyph_Bitmap_Distribution_Format
[DMS]: dms.html
[fontu]: https://github.com/DougLau/tfon/tree/main/fontu
[REST API]: rest_api.html
99 changes: 1 addition & 98 deletions honeybee/src/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::Result;
use fstr::FStr;
use gift::{Encoder, Step};
use ntcip::dms::multi::Color;
use ntcip::dms::{tfon, CharacterEntry, Font, Graphic};
use ntcip::dms::Graphic;
use pix::{rgb::SRgb8, Palette};
use postgres::Client;
use serde_derive::Deserialize;
Expand All @@ -29,28 +29,6 @@ use std::path::Path;
use std::sync::mpsc::Sender;
use std::time::Instant;

/// Glyph from font
#[derive(Deserialize)]
struct Glyph {
code_point: u16,
width: u8,
#[serde(with = "super::base64")]
bitmap: Vec<u8>,
}

/// Font resource
#[derive(Deserialize)]
struct FontRes {
f_number: u8,
name: String,
height: u8,
#[allow(dead_code)]
width: u8,
char_spacing: u8,
line_spacing: u8,
glyphs: Vec<Glyph>,
}

/// Graphic resource
#[derive(Deserialize)]
struct GraphicRes {
Expand All @@ -64,33 +42,6 @@ struct GraphicRes {
bitmap: Vec<u8>,
}

impl From<Glyph> for CharacterEntry {
fn from(gl: Glyph) -> Self {
CharacterEntry {
number: gl.code_point,
width: gl.width,
bitmap: gl.bitmap,
}
}
}

impl<const C: usize> From<FontRes> for Font<C> {
fn from(fr: FontRes) -> Self {
let mut glyphs = fr.glyphs.into_iter();
Font {
number: fr.f_number,
name: FStr::from_str_lossy(&fr.name, 0),
height: fr.height,
char_spacing: fr.char_spacing,
line_spacing: fr.line_spacing,
characters: std::array::from_fn(|_i| match glyphs.next() {
Some(glyph) => CharacterEntry::from(glyph),
None => CharacterEntry::default(),
}),
}
}
}

impl From<GraphicRes> for Graphic {
fn from(gr: GraphicRes) -> Self {
let transparent_color = match gr.transparent_color {
Expand All @@ -117,11 +68,6 @@ impl From<GraphicRes> for Graphic {
/// A resource which can be fetched from a database connection.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum Resource {
/// Font resource.
///
/// * SQL query.
Font(&'static str),

/// Graphic resource.
///
/// * SQL query.
Expand Down Expand Up @@ -359,22 +305,6 @@ const FONT_LIST_RES: Resource = Resource::Simple(
) r",
);

/// Font resource
const FONT_RES: Resource = Resource::Font(
"SELECT row_to_json(f)::text FROM (\
SELECT f_number, name, height, width, char_spacing, line_spacing, \
array(SELECT row_to_json(c) FROM (\
SELECT code_point, width, \
replace(pixels, E'\n', '') AS bitmap \
FROM iris.glyph \
WHERE font = ft.name \
ORDER BY code_point \
) AS c) \
AS glyphs \
FROM iris.font ft ORDER BY name\
) AS f",
);

/// Graphic list resource
const GRAPHIC_LIST_RES: Resource = Resource::Simple(
"api/graphic",
Expand Down Expand Up @@ -870,7 +800,6 @@ const ALL: &[Resource] = &[
DMS_PUB_RES,
DMS_STAT_RES,
FONT_LIST_RES,
FONT_RES,
GRAPHIC_LIST_RES,
GRAPHIC_RES,
MSG_LINE_RES,
Expand Down Expand Up @@ -987,7 +916,6 @@ impl Resource {
/// Get the listen value
fn listen(self) -> Option<&'static str> {
match self {
Resource::Font(_) => None,
Resource::Graphic(_) => None,
Resource::RNode() => Some("r_node$1"),
Resource::Road() => Some("road$1"),
Expand All @@ -999,7 +927,6 @@ impl Resource {
/// Get the SQL value
fn sql(self) -> &'static str {
match self {
Resource::Font(sql) => sql,
Resource::Graphic(sql) => sql,
Resource::RNode() => unreachable!(),
Resource::Road() => unreachable!(),
Expand All @@ -1020,7 +947,6 @@ impl Resource {
sender: &Sender<SegMsg>,
) -> Result<()> {
match self {
Resource::Font(_) => self.fetch_fonts(client),
Resource::Graphic(_) => self.fetch_graphics(client),
Resource::RNode() => self.fetch_nodes(client, payload, sender),
Resource::Road() => self.fetch_roads(client, payload, sender),
Expand Down Expand Up @@ -1084,29 +1010,6 @@ impl Resource {
Ok(())
}

/// Fetch font resources
fn fetch_fonts(self, client: &mut Client) -> Result<()> {
log::debug!("fetch_fonts");
let t = Instant::now();
let dir = Path::new("api/tfon");
let mut count = 0;
let sql = self.sql();
for row in &client.query(sql, &[])? {
let font: FontRes = serde_json::from_str(row.get(0))?;
let font: Font<256> = font.into();
let name = format!("{}.tfon", font.name.slice_to_terminator('\0'));
let file = AtomicFile::new(dir, &name)?;
let writer = file.writer()?;
if let Err(e) = tfon::write(writer, &font) {
log::error!("fetch_fonts {name}: {e:?}");
let _res = file.cancel();
}
count += 1;
}
log::info!("fetch_fonts: wrote {count} rows in {:?}", t.elapsed());
Ok(())
}

/// Fetch graphics resource.
fn fetch_graphics(self, client: &mut Client) -> Result<()> {
log::debug!("fetch_graphics");
Expand Down

0 comments on commit a3f7e09

Please sign in to comment.