Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 51 additions & 17 deletions src-tauri/src/system_fonts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,21 @@ fn name_string(name: &[u8], id: u16) -> Option<String> {
let mut mac = None;
for i in 0..count {
let rec = 6 + i * 12;
if u16_at(name, rec + 6)? != id {
// Records are fixed and sequential, so a short one ends the table.
let Some(record) = name.get(rec..rec + 12) else {
break;
};
if u16_at(record, 6)? != id {
continue;
}
let platform = u16_at(name, rec)?;
let encoding = u16_at(name, rec + 2)?;
let len = u16_at(name, rec + 8)? as usize;
let at = strings + u16_at(name, rec + 10)? as usize;
let data = name.get(at..at + len)?;
let platform = u16_at(record, 0)?;
let encoding = u16_at(record, 2)?;
let len = u16_at(record, 8)? as usize;
let at = strings + u16_at(record, 10)? as usize;
// One record pointing outside the table must not hide the others.
let Some(data) = name.get(at..at + len) else {
continue;
};
match (platform, encoding) {
(3, 1) | (3, 10) | (0, _) => {
let units: Vec<u16> = data
Expand Down Expand Up @@ -314,17 +321,13 @@ mod tests {
.collect::<Vec<u8>>()
};
let (fam, sty) = (utf16(family), utf16(style));
let mut name = Vec::new();
name.extend(0u16.to_be_bytes());
name.extend(2u16.to_be_bytes());
name.extend((6u16 + 2 * 12).to_be_bytes());
for (id, data, offset) in [(1u16, &fam, 0u16), (2, &sty, fam.len() as u16)] {
for v in [3u16, 1, 0x0409, id, data.len() as u16, offset] {
name.extend(v.to_be_bytes());
}
}
name.extend(&fam);
name.extend(&sty);
let name = name_table(
&[
[3, 1, 0x0409, 1, fam.len() as u16, 0],
[3, 1, 0x0409, 2, sty.len() as u16, fam.len() as u16],
],
&[fam, sty].concat(),
);
let mut os2 = vec![0u8; 78];
os2[8..10].copy_from_slice(&fs_type.to_be_bytes());
let mut tables: Vec<(&[u8; 4], Vec<u8>)> = vec![
Expand Down Expand Up @@ -411,6 +414,37 @@ mod tests {
assert!(!parse(&font("A", "B", 0x0008, false)).unwrap().restricted);
}

/// Records as platform, encoding, language, id, length and offset into `pool`.
fn name_table(records: &[[u16; 6]], pool: &[u8]) -> Vec<u8> {
let mut table = Vec::new();
table.extend(0u16.to_be_bytes());
table.extend((records.len() as u16).to_be_bytes());
table.extend((6 + 12 * records.len() as u16).to_be_bytes());
for record in records {
for v in record {
table.extend(v.to_be_bytes());
}
}
table.extend(pool);
table
}

#[test]
fn skips_a_name_record_that_points_outside_the_table() {
let table = name_table(
&[[3, 1, 0x0409, 1, 2, 0xFFF0], [3, 1, 0x0409, 1, 2, 0]],
&[0, 65],
);
assert_eq!(name_string(&table, 1).as_deref(), Some("A"));
}

#[test]
fn keeps_the_fallback_name_when_the_record_count_overstates_the_table() {
let mut table = name_table(&[[1, 0, 0, 1, 1, 0]], b"B");
table[2..4].copy_from_slice(&2u16.to_be_bytes());
assert_eq!(name_string(&table, 1).as_deref(), Some("B"));
}

#[test]
fn rejects_a_foreign_or_truncated_file() {
assert!(parse(b"not a font at all").is_none());
Expand Down
7 changes: 4 additions & 3 deletions src/components/Fonts/AddFontDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useT } from '../../hooks/useT';
import { useUpload } from '../../hooks/useUpload';
import { isDesktopShell } from '../../lib/platform';
import { systemFontFaceStyle } from '../../lib/fontFaceStyle';
import { fontNameIssueText } from '../../lib/fontNameIssueText';
import { listSystemFonts, readSystemFont, type SystemFont } from '../../lib/systemFonts';
import { ariaDisabledCls, inputCls, labelCls } from '../ui/formStyles';
import { DialogShell } from '../ui/DialogShell';
Expand Down Expand Up @@ -152,7 +153,7 @@ export function AddFontDialog({ onDone }: AddFontDialogProps) {
/>
{issue && (
<p id={issueId} role="alert" className="text-[10px] font-mono text-red-400 leading-snug">
{{ error: t.fonts.uploadError, nameTaken: t.fonts.nameTaken, nameUnusable: t.fonts.nameUnusable }[issue]}
{{ error: t.fonts.uploadError, ...fontNameIssueText(t) }[issue]}
</p>
)}
</div>
Expand All @@ -172,7 +173,7 @@ export function AddFontDialog({ onDone }: AddFontDialogProps) {
aria-disabled={!canAdd}
aria-busy={busy || undefined}
>
{t.fonts.addFont}
{t.fonts.add}
</button>
</div>
</form>
Expand Down Expand Up @@ -241,7 +242,7 @@ function SystemFontList({
autoFocus
/>
{fonts === null && !failed && <p className="text-[10px] text-muted">…</p>}
{fonts !== null && fonts.length === 0 && <p className="text-[10px] text-muted">{t.fonts.noSystemFonts}</p>}
{fonts !== null && fonts.length === 0 && <p className="text-[10px] text-muted">{t.fonts.noInstalledFonts}</p>}
{fonts !== null && fonts.length > 0 && shown.length === 0 && <p className="text-[10px] text-muted">{t.fonts.noFilterMatch}</p>}
{failed && <p className="text-[10px] font-mono text-red-400">{t.fonts.systemFontsFailed}</p>}
<div
Expand Down
2 changes: 1 addition & 1 deletion src/components/Fonts/FontManager.systemFonts.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ afterEach(() => {

const open = (r: ReturnType<typeof render>) => fireEvent.click(r.getByText(en.fonts.addFont));
const add = (r: ReturnType<typeof render>) =>
within(r.getByRole("dialog")).getByRole("button", { name: en.fonts.addFont }) as HTMLButtonElement;
within(r.getByRole("dialog")).getByRole("button", { name: en.fonts.add }) as HTMLButtonElement;
const nameField = (r: ReturnType<typeof render>) => r.getByPlaceholderText(en.fonts.printerFilenamePlaceholder) as HTMLInputElement;

describe("FontManager installed fonts", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Fonts/FontManager.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const addFile = async (r: ReturnType<typeof render>, file: File) => {
fireEvent.change(input, { target: { files: [file] } });
});
await act(async () => {
fireEvent.click(within(r.getByRole("dialog")).getByRole("button", { name: "Add font" }));
fireEvent.click(within(r.getByRole("dialog")).getByRole("button", { name: "Add" }));
});
};

Expand Down
3 changes: 2 additions & 1 deletion src/components/PrinterSettings/StoredFontsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { storageKey, storageRefMatchesPath } from "@zplab/core/lib/storagePath";
import { useCachedFonts } from "../../hooks/useCachedFonts";
import { useUpload } from "../../hooks/useUpload";
import { useLabelStore, selectEditorFrozen } from "../../store/labelStore";
import { fontNameIssueText } from "../../lib/fontNameIssueText";
import { buttonCls, disabledCls } from "../ui/formStyles";
import { ConfirmDialog } from "../ui/ConfirmDialog";

Expand Down Expand Up @@ -91,7 +92,7 @@ export function StoredFontsTab() {
{loc.uploadFont}
</button>
</Tooltip>
{uploadIssue && <span className="text-[10px] text-warning">{{ error: loc.uploadError, notAFont: loc.uploadError, nameTaken: t.fonts.nameTaken, nameUnusable: t.fonts.nameUnusable, refused: t.printerSettings.frozenHint }[uploadIssue]}</span>}
{uploadIssue && <span className="text-[10px] text-warning">{{ error: loc.uploadError, notAFont: loc.uploadError, ...fontNameIssueText(t), refused: t.printerSettings.frozenHint }[uploadIssue]}</span>}
</div>
{rows.length === 0 ? (
<p className="text-xs text-muted/70">{loc.noFonts}</p>
Expand Down
7 changes: 7 additions & 0 deletions src/lib/fontNameIssueText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { FontNameIssue } from '@zplab/core/lib/customFonts';
import type { Translations } from '../locales';

/** Both font pickers answer a name issue with the same wording. */
export function fontNameIssueText(t: Translations): Record<FontNameIssue, string> {
return { nameTaken: t.fonts.nameTaken, nameUnusable: t.fonts.nameUnusable };
}
3 changes: 2 additions & 1 deletion src/locales/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const ar = {
heading: 'الخطوط',
noFonts: 'لا توجد خطوط محملة',
addFont: 'إضافة خط',
add: 'إضافة',
printerFilename: 'اسم ملف الطابعة',
printerFilenamePlaceholder: 'مثال: ARIAL.TTF',
upload: 'رفع',
fromComputer: 'من هذا الكمبيوتر',
filterFonts: 'تصفية الخطوط',
noSystemFonts: 'لا يوجد خط مثبت مناسب. يتم استثناء الخطوط التي يزيد حجمها عن 4 MiB ومجموعات الخطوط.',
noInstalledFonts: 'لا يوجد خط مثبت مناسب. يتم استثناء الخطوط التي يزيد حجمها عن 4 MiB ومجموعات الخطوط.',
noFilterMatch: 'لا يوجد خط مثبت يطابق التصفية.',
systemFontsFailed: 'تعذّر قراءة الخطوط المثبتة.',
restrictedLicense: 'ترخيص الخط يمنع التضمين.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/bg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const bg = {
heading: 'Шрифтове',
noFonts: 'Няма заредени шрифтове',
addFont: 'Добави шрифт',
add: 'Добавяне',
printerFilename: 'Файлово наименование на принтера',
printerFilenamePlaceholder: 'напр. ARIAL.TTF',
upload: 'Качване',
fromComputer: 'От този компютър',
filterFonts: 'Филтриране на шрифтове',
noSystemFonts: 'Няма инсталиран шрифт, който да отговаря. Шрифтове над 4 MiB и шрифтови колекции се пропускат.',
noInstalledFonts: 'Няма инсталиран шрифт, който да отговаря. Шрифтове над 4 MiB и шрифтови колекции се пропускат.',
noFilterMatch: 'Няма инсталиран шрифт, който да отговаря на филтъра.',
systemFontsFailed: 'Неуспешно прочитане на инсталираните шрифтове.',
restrictedLicense: 'Лицензът на шрифта не позволява вграждане.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/cs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const cs = {
heading: 'Písma',
noFonts: 'Žádná písma nenačtena',
addFont: 'Přidat písmo',
add: 'Přidat',
printerFilename: 'Název souboru tiskárny',
printerFilenamePlaceholder: 'např. ARIAL.TTF',
upload: 'Nahrát',
fromComputer: 'Z tohoto počítače',
filterFonts: 'Filtrovat písma',
noSystemFonts: 'Žádné nainstalované písmo nevyhovuje. Písma nad 4 MiB a kolekce písem se vynechávají.',
noInstalledFonts: 'Žádné nainstalované písmo nevyhovuje. Písma nad 4 MiB a kolekce písem se vynechávají.',
noFilterMatch: 'Žádné nainstalované písmo neodpovídá filtru.',
systemFontsFailed: 'Nainstalované fonty se nepodařilo načíst.',
restrictedLicense: 'Licence písma zakazuje vložení.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/da.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const da = {
heading: 'Skrifttyper',
noFonts: 'Ingen skrifttyper indlæst',
addFont: 'Tilføj skrifttype',
add: 'Tilføj',
printerFilename: 'Printerfilnavn',
printerFilenamePlaceholder: 'f.eks. ARIAL.TTF',
upload: 'Upload',
fromComputer: 'Fra denne computer',
filterFonts: 'Filtrer skrifttyper',
noSystemFonts: 'Ingen installeret skrifttype passer. Skrifttyper over 4 MiB og skrifttypesamlinger udelades.',
noInstalledFonts: 'Ingen installeret skrifttype passer. Skrifttyper over 4 MiB og skrifttypesamlinger udelades.',
noFilterMatch: 'Ingen installeret skrifttype matcher filteret.',
systemFontsFailed: 'De installerede skrifttyper kunne ikke læses.',
restrictedLicense: 'Skrifttypens licens forbyder indlejring.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1744,12 +1744,13 @@ const de = {
heading: 'Schriften',
noFonts: 'Keine Schriften geladen',
addFont: 'Schrift hinzufügen',
add: 'Hinzufügen',
printerFilename: 'Druckerdateiname',
printerFilenamePlaceholder: 'z. B. ARIAL.TTF',
upload: 'Hochladen',
fromComputer: 'Von diesem Computer',
filterFonts: 'Schriften filtern',
noSystemFonts: 'Keine installierte Schrift passt. Schriften über 4 MiB und Schriftsammlungen werden ausgelassen.',
noInstalledFonts: 'Keine installierte Schrift passt. Schriften über 4 MiB und Schriftsammlungen werden ausgelassen.',
noFilterMatch: 'Keine installierte Schrift passt zum Filter.',
systemFontsFailed: 'Die installierten Schriftarten konnten nicht gelesen werden.',
restrictedLicense: 'Die Lizenz dieser Schrift verbietet das Einbetten.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/el.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const el = {
heading: 'Γραμματοσειρές',
noFonts: 'Δεν φορτώθηκαν γραμματοσειρές',
addFont: 'Προσθήκη γραμματοσειράς',
add: 'Προσθήκη',
printerFilename: 'Όνομα αρχείου εκτυπωτή',
printerFilenamePlaceholder: 'π.χ. ARIAL.TTF',
upload: 'Μεταφόρτωση',
fromComputer: 'Από αυτόν τον υπολογιστή',
filterFonts: 'Φιλτράρισμα γραμματοσειρών',
noSystemFonts: 'Καμία εγκατεστημένη γραμματοσειρά δεν ταιριάζει. Γραμματοσειρές πάνω από 4 MiB και συλλογές γραμματοσειρών παραλείπονται.',
noInstalledFonts: 'Καμία εγκατεστημένη γραμματοσειρά δεν ταιριάζει. Γραμματοσειρές πάνω από 4 MiB και συλλογές γραμματοσειρών παραλείπονται.',
noFilterMatch: 'Καμία εγκατεστημένη γραμματοσειρά δεν ταιριάζει με το φίλτρο.',
systemFontsFailed: 'Δεν ήταν δυνατή η ανάγνωση των εγκατεστημένων γραμματοσειρών.',
restrictedLicense: 'Η άδεια χρήσης της γραμματοσειράς δεν επιτρέπει την ενσωμάτωση.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1744,12 +1744,13 @@ const en = {
heading: 'Fonts',
noFonts: 'No fonts loaded',
addFont: 'Add font',
add: 'Add',
printerFilename: 'Printer filename',
printerFilenamePlaceholder: 'e.g. ARIAL.TTF',
upload: 'Upload',
fromComputer: 'From this computer',
filterFonts: 'Filter fonts',
noSystemFonts: 'No installed font fits. Fonts above 4 MiB and font collections are left out.',
noInstalledFonts: 'No installed font fits. Fonts above 4 MiB and font collections are left out.',
noFilterMatch: 'No installed font matches the filter.',
systemFontsFailed: 'Could not read the installed fonts.',
restrictedLicense: "The font's license forbids embedding.",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const es = {
heading: 'Fuentes',
noFonts: 'No hay fuentes cargadas',
addFont: 'Agregar fuente',
add: 'Agregar',
printerFilename: 'Nombre de archivo de impresora',
printerFilenamePlaceholder: 'p. ej. ARIAL.TTF',
upload: 'Subir',
fromComputer: 'Desde este equipo',
filterFonts: 'Filtrar fuentes',
noSystemFonts: 'Ninguna fuente instalada coincide. Las fuentes de más de 4 MiB y las colecciones de fuentes se excluyen.',
noInstalledFonts: 'Ninguna fuente instalada coincide. Las fuentes de más de 4 MiB y las colecciones de fuentes se excluyen.',
noFilterMatch: 'Ninguna fuente instalada coincide con el filtro.',
systemFontsFailed: 'No se pudieron leer las fuentes instaladas.',
restrictedLicense: 'La licencia de la fuente prohíbe incrustarla.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/et.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const et = {
heading: 'Fondid',
noFonts: 'Fonte pole laaditud',
addFont: 'Lisa font',
add: 'Lisa',
printerFilename: 'Printeri failinimi',
printerFilenamePlaceholder: 'nt. ARIAL.TTF',
upload: 'Laadi üles',
fromComputer: 'Sellest arvutist',
filterFonts: 'Filtreeri fonte',
noSystemFonts: 'Ükski installitud font ei sobi. Fondid üle 4 MiB ja fondikollektsioonid jäetakse välja.',
noInstalledFonts: 'Ükski installitud font ei sobi. Fondid üle 4 MiB ja fondikollektsioonid jäetakse välja.',
noFilterMatch: 'Ükski installitud font ei vasta filtrile.',
systemFontsFailed: 'Installitud fonte ei saanud lugeda.',
restrictedLicense: 'Fondi litsents keelab manustamise.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/fa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const fa = {
heading: 'فونت‌ها',
noFonts: 'هیچ فونتی بارگذاری نشده',
addFont: 'افزودن فونت',
add: 'افزودن',
printerFilename: 'نام فایل چاپگر',
printerFilenamePlaceholder: 'مثلاً ARIAL.TTF',
upload: 'بارگذاری',
fromComputer: 'از این کامپیوتر',
filterFonts: 'فیلتر فونت‌ها',
noSystemFonts: 'هیچ فونت نصب‌شده‌ای مطابقت ندارد. فونت‌های بالای 4 MiB و مجموعه‌فونت‌ها کنار گذاشته می‌شوند.',
noInstalledFonts: 'هیچ فونت نصب‌شده‌ای مطابقت ندارد. فونت‌های بالای 4 MiB و مجموعه‌فونت‌ها کنار گذاشته می‌شوند.',
noFilterMatch: 'هیچ فونت نصب‌شده‌ای با فیلتر مطابقت ندارد.',
systemFontsFailed: 'خواندن فونت‌های نصب‌شده ممکن نبود.',
restrictedLicense: 'مجوز فونت تعبیه را ممنوع می‌کند.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/fi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const fi = {
heading: 'Fontit',
noFonts: 'Ei ladattuja fontteja',
addFont: 'Lisää fontti',
add: 'Lisää',
printerFilename: 'Tulostimen tiedostonimi',
printerFilenamePlaceholder: 'esim. ARIAL.TTF',
upload: 'Lataa',
fromComputer: 'Tästä tietokoneesta',
filterFonts: 'Suodata fontteja',
noSystemFonts: 'Mikään asennettu fontti ei sovi. Fontit, joiden koko on yli 4 MiB, ja fonttikokoelmat jätetään pois.',
noInstalledFonts: 'Mikään asennettu fontti ei sovi. Fontit, joiden koko on yli 4 MiB, ja fonttikokoelmat jätetään pois.',
noFilterMatch: 'Mikään asennettu fontti ei vastaa suodatinta.',
systemFontsFailed: 'Asennettuja fontteja ei voitu lukea.',
restrictedLicense: 'Fontin lisenssi kieltää upottamisen.',
Expand Down
3 changes: 2 additions & 1 deletion src/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const fr = {
heading: 'Polices',
noFonts: 'Aucune police chargée',
addFont: 'Ajouter une police',
add: 'Ajouter',
printerFilename: 'Nom de fichier imprimante',
printerFilenamePlaceholder: 'ex. ARIAL.TTF',
upload: 'Téléverser',
fromComputer: 'Depuis cet ordinateur',
filterFonts: 'Filtrer les polices',
noSystemFonts: 'Aucune police installée ne correspond. Les polices de plus de 4 MiB et les collections de polices sont exclues.',
noInstalledFonts: 'Aucune police installée ne correspond. Les polices de plus de 4 MiB et les collections de polices sont exclues.',
noFilterMatch: 'Aucune police installée ne correspond au filtre.',
systemFontsFailed: 'Impossible de lire les polices installées.',
restrictedLicense: "La licence de la police interdit l'intégration.",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/he.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,12 +1706,13 @@ const he = {
heading: 'גופנים',
noFonts: 'לא נטענו גופנים',
addFont: 'הוסף גופן',
add: 'הוסף',
printerFilename: 'שם קובץ המדפסת',
printerFilenamePlaceholder: 'לדוגמה: ARIAL.TTF',
upload: 'העלה',
fromComputer: 'מהמחשב הזה',
filterFonts: 'סינון גופנים',
noSystemFonts: 'אין גופן מותקן שמתאים. גופנים מעל 4 MiB ואוספי גופנים אינם נכללים.',
noInstalledFonts: 'אין גופן מותקן שמתאים. גופנים מעל 4 MiB ואוספי גופנים אינם נכללים.',
noFilterMatch: 'אין גופן מותקן שמתאים למסנן.',
systemFontsFailed: 'לא ניתן לקרוא את הגופנים המותקנים.',
restrictedLicense: 'הרישיון של הגופן אוסר על הטבעה.',
Expand Down
Loading
Loading