mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-09 03:49:34 -06:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11fec161cc |
@@ -148,19 +148,7 @@ pub fn rotate(dir: &Path) -> std::io::Result<(PathBuf, File)> {
|
||||
/// One formatted log line: `<timestamp> <LEVEL> [<thread>] <target>: <message>\n`.
|
||||
pub fn format_line(ts: &str, level: log::Level, thread: &str, target: &str, message: &str) -> String {
|
||||
let message = message.strip_suffix('\n').unwrap_or(message);
|
||||
// Messages can embed document-controlled text (a part name, a relationship id): control
|
||||
// characters other than tab are escaped and each continuation line is marked, so such text
|
||||
// cannot start a line that looks like a record of its own.
|
||||
let mut text = String::with_capacity(message.len());
|
||||
for c in message.chars() {
|
||||
match c {
|
||||
'\n' => text.push_str("\n | "),
|
||||
'\t' => text.push(c),
|
||||
c if c.is_control() => text.extend(c.escape_default()),
|
||||
c => text.push(c),
|
||||
}
|
||||
}
|
||||
format!("{ts} {level:<5} [{thread}] {target}: {text}\n")
|
||||
format!("{ts} {level:<5} [{thread}] {target}: {message}\n")
|
||||
}
|
||||
|
||||
/// Where formatted lines go besides standard error: kept in memory until a file is attached,
|
||||
@@ -458,13 +446,8 @@ mod tests {
|
||||
),
|
||||
"2026-10-08T07:59:17.728Z WARN [main] deckcraft_pptx::opc: pptx: skipping oversized part ppt/media/image1.png\n"
|
||||
);
|
||||
// A multi-line message keeps its lines, each continuation marked; the record still ends in
|
||||
// exactly one newline.
|
||||
assert!(format_line("t", log::Level::Error, "w", "x", "a\nb\n").ends_with("x: a\n | b\n"));
|
||||
// Injected line breaks and escape sequences cannot forge a record.
|
||||
let line = format_line("t", log::Level::Warn, "w", "x", "part a\r\n2026 ERROR [m] y: forged\x1b[2J");
|
||||
assert_eq!(line.matches('\n').count(), 2);
|
||||
assert!(!line.contains('\r') && !line.contains('\x1b'));
|
||||
// A multi-line message keeps its lines; the record still ends in exactly one newline.
|
||||
assert!(format_line("t", log::Level::Error, "w", "x", "a\nb\n").ends_with("x: a\nb\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1215,9 +1215,8 @@ impl FontDb {
|
||||
fn instance(&self, family: &str, style: &str) -> Option<Arc<FontFace>> {
|
||||
let open = style.find('{')?;
|
||||
let base = self.find(family, style[..open].trim())?;
|
||||
if base.skrifa().is_none_or(|f| f.axes().is_empty()) {
|
||||
return Some(base);
|
||||
}
|
||||
let Some(font) = base.skrifa().filter(|f| !f.axes().is_empty()) else { return Some(base) };
|
||||
let axis_tags: Vec<[u8; 4]> = font.axes().iter().map(|a| a.tag().to_be_bytes()).collect();
|
||||
let mut coords = base.coords.clone();
|
||||
for kv in style[open + 1..].trim_end_matches('}').split(',') {
|
||||
let (k, v) = kv.split_once(':')?;
|
||||
@@ -1227,6 +1226,11 @@ impl FontDb {
|
||||
return None;
|
||||
}
|
||||
let tag = [k[0], k[1], k[2], k[3]];
|
||||
// A tag the font has no axis for changes nothing; keeping it would grow the list with
|
||||
// every distinct tag in the style text.
|
||||
if !axis_tags.contains(&tag) {
|
||||
continue;
|
||||
}
|
||||
match coords.iter_mut().find(|(t, _)| *t == tag) {
|
||||
Some(c) => c.1 = v,
|
||||
None => coords.push((tag, v)),
|
||||
|
||||
Reference in New Issue
Block a user