Print and PDF export use the Numeral setting (#3784) (#3824)

This commit is contained in:
MarekSpodBiedry
2026-10-23 12:17:45 -04:00
committed by GitHub
parent 6cab843f00
commit 4571cd58c1
7 changed files with 75 additions and 9 deletions
+8 -2
View File
@@ -364,10 +364,16 @@ fn save_as(s: &mut Session, v: &Value) -> CmdResult {
}
/// How a copy is written: Print Markup from the command's `markup`, else the File › Print
/// setting (`file.printMarkup`); comments as Track Changes Options show them.
/// setting (`file.printMarkup`); comments as Track Changes Options show them; digits as the
/// Numeral setting shows them on screen.
pub fn export_options(s: &Session, v: &Value) -> crate::io::ExportOptions {
let markup = p::bool(v, "markup").unwrap_or(s.prefs.print_markup);
crate::io::ExportOptions { markup, comments: markup && s.prefs.markup.comments, word2003: p::str(v, "format") == Some(FORMAT_WORD_2003) }
crate::io::ExportOptions {
markup,
comments: markup && s.prefs.markup.comments,
word2003: p::str(v, "format") == Some(FORMAT_WORD_2003),
numeral: s.view.numeral.resolve(crate::session::system_locale()),
}
}
fn export_png(s: &mut Session, v: &Value) -> CmdResult {
+2 -2
View File
@@ -141,8 +141,8 @@ pub fn specs() -> Vec<CommandSpec> {
.pure(),
CommandSpec::new("view.state", "View State", "View", |s, _| serde_json::to_value(&s.view).map_err(|e| CmdError::Failed(e.to_string())))
.pure(),
// Word › File › Options › Advanced › Numeral: how digits look on screen (display
// only; the document text keeps the typed codepoints).
// Word › File › Options › Advanced › Numeral: how digits look on screen, in print, PDFs
// and page images (display only; the document text keeps the typed codepoints).
CommandSpec::new("view.numeral", "Numerals", "File › Options › Advanced", |s, v| match v.get("value") {
None => Ok(json!({"value": s.view.numeral})),
Some(Value::String(name)) => {
+2
View File
@@ -59,6 +59,8 @@ pub struct ExportOptions {
/// An `.xml` copy is a Word 2003 XML Document (WordprocessingML 2003) instead of a Word XML
/// Document (Flat OPC): `format: "wordml2003"` on the save commands.
pub word2003: bool,
/// How digits look in PDFs and page images (the Numeral setting, `System` resolved).
pub numeral: wordcraft_layout::NumeralMode,
}
/// A document password: wiped from memory when dropped, never printed (`Debug` shows `***`).
+7 -2
View File
@@ -31,7 +31,12 @@ pub fn save(ext: &str, doc: &Document, opts: &crate::io::ExportOptions) -> Optio
"xml" => Some(wordcraft_docx::write_flat(doc).map_err(|e| e.to_string())),
"png" => Some(render_png(doc, 0, 2.0, opts)),
"pdf" => {
let pdf = wordcraft_pdf::PdfOptions { include_markup: opts.markup, comments: opts.markup && opts.comments, ..Default::default() };
let pdf = wordcraft_pdf::PdfOptions {
include_markup: opts.markup,
comments: opts.markup && opts.comments,
numeral: opts.numeral,
..Default::default()
};
Some(wordcraft_pdf::export(doc, &pdf).map_err(|e| e.to_string()))
}
other => wordcraft_formats::export_with(other, doc, &wordcraft_formats::ExportOptions { comments: opts.markup && opts.comments }),
@@ -41,7 +46,7 @@ pub fn save(ext: &str, doc: &Document, opts: &crate::io::ExportOptions) -> Optio
/// Render page `page` as PNG at `scale` px/pt. With Print Markup's comments the page has its
/// markup area with the comments in balloons (page images always show tracked changes).
pub fn render_png(doc: &Document, page: usize, scale: f32, opts: &crate::io::ExportOptions) -> Result<Vec<u8>, String> {
let lo = wordcraft_layout::LayoutOptions { comment_balloons: opts.markup && opts.comments, ..Default::default() };
let lo = wordcraft_layout::LayoutOptions { comment_balloons: opts.markup && opts.comments, numeral: opts.numeral, ..Default::default() };
let l = wordcraft_layout::layout(doc, &mut wordcraft_layout::LayoutCache::new(), &lo);
let p = l.pages.get(page).ok_or_else(|| format!("no page {}", page + 1))?;
let img = wordcraft_render::render_page(doc, p, scale, &Default::default());
+1
View File
@@ -973,6 +973,7 @@ impl Session {
let opts = LayoutOptions {
view: ViewMode::Print,
comment_balloons: markup && self.prefs.markup.comments,
numeral: self.view.numeral.resolve(SYSTEM_LOCALE.get().map(String::as_str)),
filename: Some(Arc::from(fn_name.as_str())),
filepath: Some(Arc::from(fn_path.as_str())),
..Default::default()
+39
View File
@@ -5036,6 +5036,45 @@ fn numeral_mode_sets_gets_and_rejects_junk() {
assert_eq!(text(&s), "Year 2024");
}
/// The Numeral setting also applies to print and PDF output, not just the screen layout.
#[test]
fn export_layout_follows_the_numeral_setting() {
use wordcraft_layout::Placed;
fn glyph_ids(s: &Session) -> Vec<u32> {
let mut out = Vec::new();
for page in &s.export_layout().pages {
for it in &page.items {
if let Placed::Lines { para, .. } = it {
out.extend(para.glyphs.iter().map(|g| g.gid));
}
}
}
out
}
let mut s = s();
s.doc = wordcraft_doc::Document::from_text("Year 2024");
let western = glyph_ids(&s);
assert!(!western.is_empty());
run(&mut s, "view.numeral", json!({"value": "hindi"}));
assert_ne!(glyph_ids(&s), western, "Hindi digits reach the printed layout");
}
/// File › Export PDF, Save As PDF and page images take the Numeral setting too.
#[test]
fn pdf_and_png_copies_follow_the_numeral_setting() {
let mut s = s();
s.doc = wordcraft_doc::Document::from_text("Year 2024");
// A fixed creation date, so the PDF bytes depend only on the layout.
s.doc.core.created = "2024-01-01T00:00:00Z".into();
let copy = |s: &Session, name: &str| crate::io::save_bytes_opts(name, &s.doc, &cmd::file::export_options(s, &json!({}))).unwrap();
let (pdf, png) = (copy(&s, "x.pdf"), copy(&s, "x.png"));
assert_eq!(copy(&s, "x.pdf"), pdf);
run(&mut s, "view.numeral", json!({"value": "hindi"}));
assert_eq!(cmd::file::export_options(&s, &json!({})).numeral, wordcraft_layout::NumeralMode::Hindi);
assert_ne!(copy(&s, "x.pdf"), pdf, "Hindi digits reach the PDF");
assert_ne!(copy(&s, "x.png"), png, "Hindi digits reach the page image");
}
/// Issue #67: View › Zoom steps. Zoom In/Out leave a fit mode and step 10% from the current zoom
/// (the UI keeps `view.zoom` equal to the shown zoom while a fit mode is on), and never get stuck
/// short of the 10%–500% limits.
+16 -3
View File
@@ -50,7 +50,7 @@ use wordcraft_geom::Rect;
use wordcraft_layout::display::Paint;
use wordcraft_layout::display::{DisplayOptions, Draw, Stroke as LineStyle, page_display};
use wordcraft_layout::para::{ClKind, LineEnd};
use wordcraft_layout::{DocLayout, LayoutCache, LayoutOptions, Page, Placed, layout};
use wordcraft_layout::{DocLayout, LayoutCache, LayoutOptions, NumeralMode, Page, Placed, layout};
use wordcraft_metafile::{Picture, PlacedItem};
/// Largest page side we write (200", the PDF limit).
@@ -95,11 +95,24 @@ pub struct PdfOptions {
/// is scaled to fit and centred, the paper turned to the page's orientation (Scale to paper
/// size, #2108). `None` keeps the document's page sizes.
pub paper: Option<(f32, f32)>,
/// How [`export`] shows digits (the Numeral setting; callers resolve `System`, see
/// [`NumeralMode::resolve`]). [`export_layout`] keeps the layout it is given.
pub numeral: NumeralMode,
}
impl Default for PdfOptions {
fn default() -> Self {
PdfOptions { pages: None, title: None, author: None, tagged: true, include_markup: false, comments: false, compress: true, paper: None }
PdfOptions {
pages: None,
title: None,
author: None,
tagged: true,
include_markup: false,
comments: false,
compress: true,
paper: None,
numeral: NumeralMode::Arabic,
}
}
}
@@ -108,7 +121,7 @@ impl Default for PdfOptions {
/// markup area with the comment balloons.
pub fn export(doc: &Document, opts: &PdfOptions) -> Result<Vec<u8>, PdfError> {
write::guard(|| {
let lo = LayoutOptions { hide_deleted: !opts.include_markup, comment_balloons: opts.comments, ..Default::default() };
let lo = LayoutOptions { hide_deleted: !opts.include_markup, comment_balloons: opts.comments, numeral: opts.numeral, ..Default::default() };
let lay = layout(doc, &mut LayoutCache::new(), &lo);
export_layout_inner(doc, &lay, opts)
})