mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 10:46:50 -06:00
Print: JIS B4/B5/B6 and Japanese paper sizes
Japanese Excel's B4/B5/B6 are the JIS sizes (257×364, 182×257 and 128×182 mm), but Page Setup's `b4`/`b5` were the ISO ones (250×353, 176×250): a Japanese B5 worksheet printed 8 mm narrow. `known_paper` now reads `B4`/`B5`/`B6` as JIS and keeps the ISO sizes reachable as `B4 (ISO)`/`B5 (ISO)`/`B6 (ISO)`. This also fixes the XLSX round-trip, whose `paperSize` ids 12/13 are the JIS sizes. Adds the Japanese papers Page Setup lacked: Japanese Postcard (はがき, 100×148 mm), Japanese Double Postcard (往復はがき, 148×200 mm), Japanese Envelope #3/#4 (長形3号/4号, 120×235 and 90×225 mm), Japanese Envelope Kaku #2/#3 (角形2号/3号, 240×332 and 216×277 mm), A6, and aliases such as `postcard`, `chou #3`, `kaku #2`; the xlsx paper table gains ids 42 (ISO B4) and 43/69–74/88 so these names survive save and reopen (the ISO B5/B6 names have no ECMA-376 paper id and stay engine-side). The pageSetup/pageLayout.size parameter docs list the names, the Page Layout Size menu lists them, and tests pin the point sizes, the PDF MediaBox for A4 (unchanged), B5, postcard, double postcard and kaku #2, landscape and fit-to-page, the unknown-name Letter fallback, and the XLSX round-trip. Docs: docs/target-app-parity.md print row + revision history, and ROADMAP.md progress log, dated 2026-10-21.
This commit is contained in:
+3
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
**Stage: alpha** · next: beta, ~25 points of "ready for real work" and ~160–240 h away
|
||||
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-12 · **Change:** minor (rows auto-fit oversized fonts on open and whole-row/column formatting) · **Target:** Microsoft Excel (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-21 · **Change:** minor (Page Setup paper sizes: JIS B4/B5/B6, the Japanese postcards and envelopes, and A6) · **Target:** Microsoft Excel (Microsoft 365)
|
||||
|
||||
GridCraft aims at full Microsoft Excel parity, and at being better in places: faster, open (XLSX
|
||||
is the native format), fully scriptable by agents (MCP, control channel, CLI), and on the web.
|
||||
@@ -132,6 +132,7 @@ Ranked; estimates in Opus 5.5 agent-hours. Detail and the full beta-gate list in
|
||||
|
||||
| Date | What landed |
|
||||
|---|---|
|
||||
| 2026-10-21 | Page Setup paper sizes match Japanese Excel: `B4`/`B5`/`B6` are the JIS sizes (ISO B4/B5/B6 stay reachable by name), so a Japanese B5 worksheet prints at 182×257 mm; Japanese Postcard (100×148 mm), Double Postcard (148×200 mm), Envelope #3/#4 (120×235/90×225 mm), Kaku #2/#3 (240×332/216×277 mm) and A6 are added, both sizes reach the PDF page (`file.exportPdf`/`file.print` MediaBox) and the size survives an XLSX round-trip; the Page Layout Size menu lists them. `B4`/`B5` used to be the ISO sizes, which is changed here |
|
||||
| 2026-10-12 | Rows auto-fit oversized fonts: opening a file and whole-row/column formatting grow non-custom rows to their tallest font (#833) |
|
||||
| 2026-10-12 | Number formats: parsing a format code is linear in its length (long runs of `[`, `,` or `/` no longer take quadratic time), and an unclosed `[` is a literal when splitting sections as well as when tokenizing (#810) |
|
||||
| 2026-10-12 | Number formats: a fraction denominator with 65,536 zeros no longer crashes; format codes are capped at 255 characters like Excel (longer codes display as General, `TEXT` gives `#VALUE!`) (#807) |
|
||||
@@ -193,6 +194,7 @@ Ranked; estimates in Opus 5.5 agent-hours. Detail and the full beta-gate list in
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-21 | minor | Page Setup paper sizes: JIS B4/B5/B6 (ISO by name), Japanese postcards and envelopes, A6; PDF page size follows, XLSX round-trips |
|
||||
| 2026-10-12 | minor | Rows auto-fit oversized fonts on open and whole-row/column formatting (#833); progress log row |
|
||||
| 2026-10-12 | minor | Progress log: multi-area row/column delete (#758) |
|
||||
| 2026-10-12 | trivial | Progress log: XLSX chart sheets kept on save (#727) |
|
||||
|
||||
@@ -50,7 +50,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Page Setup…",
|
||||
["Page Layout", "Page Setup"],
|
||||
None,
|
||||
"{sheet?, orientation?: portrait|landscape, paper?: Letter|Legal|A4|A3|A5|Tabloid|Executive|Ledger|B5, margins?: {left,right,top,bottom,header,footer} (inches; also accepted as top-level keys), scale?: 10..400, fitWidth?, fitHeight? (pages; 0 = automatic), fitToPage?: bool, centerH?, centerV?, gridlines?, headings?, header?: \"&L&A&RPage &P of &N\", footer?, printArea?: \"A1:F50\" (\"\" clears), titleRows?: \"1:2\", titleCols?: \"A:A\", rowBreaks?: [46, 91], colBreaks?: [\"K\"]} → the sheet's print settings",
|
||||
"{sheet?, orientation?: portrait|landscape, paper?: Letter|Legal|A4|A3|A5|A6|Tabloid|Executive|Ledger|B4|B5|B6|B4 (ISO)|B5 (ISO)|B6 (ISO)|Japanese Postcard|Japanese Double Postcard|Japanese Envelope #3|Japanese Envelope #4|Japanese Envelope Kaku #2|Japanese Envelope Kaku #3 (B4/B5/B6 are the JIS sizes), margins?: {left,right,top,bottom,header,footer} (inches; also accepted as top-level keys), scale?: 10..400, fitWidth?, fitHeight? (pages; 0 = automatic), fitToPage?: bool, centerH?, centerV?, gridlines?, headings?, header?: \"&L&A&RPage &P of &N\", footer?, printArea?: \"A1:F50\" (\"\" clears), titleRows?: \"1:2\", titleCols?: \"A:A\", rowBreaks?: [46, 91], colBreaks?: [\"K\"]} → the sheet's print settings",
|
||||
has_doc,
|
||||
page_setup
|
||||
),
|
||||
@@ -71,19 +71,35 @@ pub fn paper_size(name: &str) -> (f32, f32) {
|
||||
known_paper(name).unwrap_or((612.0, 792.0))
|
||||
}
|
||||
|
||||
/// Paper sizes in points, portrait. `B4`/`B5`/`B6` are the **JIS** sizes Japanese Excel and its
|
||||
/// e-mail attachments use (257×364, 182×257 and 128×182 mm); the ISO B series stays reachable as
|
||||
/// `B4 (ISO)`/`B5 (ISO)`/`B6 (ISO)`. The Japanese postcards and envelopes are the JIS ones too.
|
||||
fn known_paper(name: &str) -> Option<(f32, f32)> {
|
||||
let n = name.trim().to_ascii_lowercase().replace([' ', '-', '_'], "");
|
||||
// Spaces, hyphens, underscores and parentheses don't matter: `B5 (JIS)`, `b5-jis` and `b5`
|
||||
// are one name.
|
||||
let n = name.trim().to_ascii_lowercase().replace([' ', '-', '_', '(', ')'], "");
|
||||
Some(match n.as_str() {
|
||||
"letter" | "usletter" => (612.0, 792.0),
|
||||
"legal" | "uslegal" => (612.0, 1008.0),
|
||||
"a4" => (595.28, 841.89),
|
||||
"a3" => (841.89, 1190.55),
|
||||
"a5" => (419.53, 595.28),
|
||||
"a6" => (297.64, 419.53),
|
||||
"tabloid" | "11x17" => (792.0, 1224.0),
|
||||
"ledger" => (1224.0, 792.0),
|
||||
"executive" => (522.0, 756.0),
|
||||
"b5" => (498.9, 708.66),
|
||||
"b4" => (708.66, 1000.63),
|
||||
"b4" | "b4jis" => (728.50, 1031.81),
|
||||
"b5" | "b5jis" => (515.91, 728.50),
|
||||
"b6" | "b6jis" => (362.83, 515.91),
|
||||
"b4iso" => (708.66, 1000.63),
|
||||
"b5iso" => (498.90, 708.66),
|
||||
"b6iso" => (354.33, 498.90),
|
||||
"japanesepostcard" | "postcard" => (283.46, 419.53),
|
||||
"japanesedoublepostcard" | "doublepostcard" => (419.53, 566.93),
|
||||
"japaneseenvelope#3" | "chou#3" => (340.16, 666.14),
|
||||
"japaneseenvelope#4" | "chou#4" => (255.12, 637.80),
|
||||
"japaneseenvelopekaku#2" | "kaku#2" => (680.31, 941.10),
|
||||
"japaneseenvelopekaku#3" | "kaku#3" => (612.28, 785.20),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -2061,6 +2077,76 @@ mod tests {
|
||||
assert!(s.doc().unwrap().wb.active().unwrap().print.print_area.is_some());
|
||||
}
|
||||
|
||||
/// The Japanese paper sizes are the JIS ones: `B4`/`B5`/`B6` changed meaning from ISO, and the
|
||||
/// ISO B series stays reachable by name. The postcards and envelopes are the JIS sizes too.
|
||||
#[test]
|
||||
fn japanese_paper_sizes_are_jis() {
|
||||
for (name, w, h) in [
|
||||
("A4", 595.28, 841.89), // unchanged
|
||||
("B4", 728.50, 1031.81),
|
||||
("B5", 515.91, 728.50),
|
||||
("B6", 362.83, 515.91),
|
||||
("B4 (JIS)", 728.50, 1031.81),
|
||||
("b5-jis", 515.91, 728.50),
|
||||
("B4 (ISO)", 708.66, 1000.63),
|
||||
("B5 (ISO)", 498.90, 708.66),
|
||||
("B6 (ISO)", 354.33, 498.90),
|
||||
("A6", 297.64, 419.53),
|
||||
("Japanese Postcard", 283.46, 419.53),
|
||||
("japanese double postcard", 419.53, 566.93),
|
||||
("Japanese Envelope #3", 340.16, 666.14),
|
||||
("chou #4", 255.12, 637.80),
|
||||
("Japanese Envelope Kaku #2", 680.31, 941.10),
|
||||
("kaku #3", 612.28, 785.20),
|
||||
] {
|
||||
let got = paper_size(name);
|
||||
assert!((got.0 - w).abs() < 0.01 && (got.1 - h).abs() < 0.01, "{name}: {got:?}");
|
||||
}
|
||||
// Unknown names keep the old Letter fallback.
|
||||
assert_eq!(paper_size("Napkin"), (612.0, 792.0));
|
||||
}
|
||||
|
||||
/// Page Setup's paper reaches the PDF page: the MediaBox is the paper's, A4 is unchanged, B5 is
|
||||
/// JIS B5, and the Japanese postcard prints at 283.46×419.53 pt. Landscape swaps the sides and
|
||||
/// fit-to-page keeps the paper size.
|
||||
#[test]
|
||||
fn page_setup_paper_sizes_reach_the_pdf_page() {
|
||||
let mut s = s();
|
||||
s.execute("range.setValues", json!({"range": "A1", "values": [["x"]]})).unwrap();
|
||||
let close = |v: &Json, want: f64| (v.as_f64().unwrap() - want).abs() < 0.01;
|
||||
for (paper, w, h) in [
|
||||
("A4", 595.28f64, 841.89f64),
|
||||
("B5", 515.91, 728.50),
|
||||
("Japanese Postcard", 283.46, 419.53),
|
||||
("Japanese Double Postcard", 419.53, 566.93),
|
||||
("Japanese Envelope Kaku #2", 680.31, 941.10),
|
||||
] {
|
||||
s.execute("pageLayout.pageSetup", json!({"paper": paper, "orientation": "portrait"})).unwrap();
|
||||
let pv = s.execute("file.printPreview", json!({})).unwrap();
|
||||
let page = &pv["pageList"][0];
|
||||
assert_eq!(page["paper"], paper);
|
||||
assert!(close(&page["width"], w) && close(&page["height"], h), "{paper}: {page}");
|
||||
let (pdf, pages) = pdf_of(&mut s, json!({}));
|
||||
assert_eq!(pages, 1);
|
||||
let want = format!("/MediaBox [0 0 {w} {h}]");
|
||||
assert!(contains(&pdf, &want), "{paper}: wanted {want}");
|
||||
}
|
||||
// Landscape swaps the sides, and fit-to-page does not change the paper.
|
||||
s.execute("pageLayout.pageSetup", json!({"paper": "B5", "orientation": "landscape"})).unwrap();
|
||||
let (pdf, pages) = pdf_of(&mut s, json!({}));
|
||||
assert_eq!(pages, 1);
|
||||
assert!(contains(&pdf, "/MediaBox [0 0 728.5 515.91]"));
|
||||
s.execute("pageLayout.pageSetup", json!({"paper": "B5", "orientation": "portrait"})).unwrap();
|
||||
let (pdf, _) = pdf_of(&mut s, json!({"fitToPage": true}));
|
||||
assert!(contains(&pdf, "/MediaBox [0 0 515.91 728.5]"));
|
||||
// `pageLayout.size` still takes any name, and an unknown one prints on Letter as before.
|
||||
s.execute("pageLayout.size", json!({"paper": "Napkin"})).unwrap();
|
||||
let pv = s.execute("file.printPreview", json!({})).unwrap();
|
||||
assert!(close(&pv["pageList"][0]["width"], 612.0) && close(&pv["pageList"][0]["height"], 792.0));
|
||||
// Page Setup refuses an unknown name.
|
||||
assert!(s.execute("pageLayout.pageSetup", json!({"paper": "Napkin"})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostile_settings_never_panic_and_cap_pages() {
|
||||
let mut s = s();
|
||||
|
||||
@@ -35,7 +35,15 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
}),
|
||||
cmd!("view.zoomToSelection", "Zoom to Selection", ["View", "Zoom"], None, "{viewWidth?, viewHeight?}", has_doc, zoom_selection),
|
||||
cmd!("pageLayout.orientation", "Orientation", ["Page Layout", "Page Setup"], None, "{orientation: portrait|landscape}", has_doc, orientation),
|
||||
cmd!("pageLayout.size", "Size", ["Page Layout", "Page Setup"], None, "{paper: Letter|Legal|A4|A3|A5|Tabloid|Executive}", has_doc, paper),
|
||||
cmd!(
|
||||
"pageLayout.size",
|
||||
"Size",
|
||||
["Page Layout", "Page Setup"],
|
||||
None,
|
||||
"{paper: Letter|Legal|A4|A3|A5|A6|Tabloid|Executive|Ledger|B4|B5|B6|B4 (ISO)|B5 (ISO)|B6 (ISO)|Japanese Postcard|Japanese Double Postcard|Japanese Envelope #3|Japanese Envelope #4|Japanese Envelope Kaku #2|Japanese Envelope Kaku #3 (B4/B5/B6 are the JIS sizes)}",
|
||||
has_doc,
|
||||
paper
|
||||
),
|
||||
cmd!(
|
||||
"pageLayout.margins",
|
||||
"Margins",
|
||||
|
||||
@@ -1663,7 +1663,28 @@ fn page_layout(app: &mut SheetApp, ui: &mut Ui) {
|
||||
let s = big_button(ui, Icon::Paper, "Size", "Paper Size", true);
|
||||
control_keytip(app, ui, &s, "pageLayout.size");
|
||||
egui::Popup::menu(&s).show(|ui| {
|
||||
for p in ["Letter", "Legal", "Tabloid", "Executive", "A3", "A4", "A5"] {
|
||||
// B4/B5/B6 are the JIS sizes Japanese Excel uses; the ISO ones stay selectable by name.
|
||||
for p in [
|
||||
"Letter",
|
||||
"Legal",
|
||||
"Tabloid",
|
||||
"Executive",
|
||||
"A3",
|
||||
"A4",
|
||||
"A5",
|
||||
"A6",
|
||||
"B4",
|
||||
"B5",
|
||||
"B6",
|
||||
"Japanese Postcard",
|
||||
"Japanese Double Postcard",
|
||||
"Japanese Envelope #3",
|
||||
"Japanese Envelope #4",
|
||||
"Japanese Envelope Kaku #2",
|
||||
"Japanese Envelope Kaku #3",
|
||||
"B4 (ISO)",
|
||||
"B5 (ISO)",
|
||||
] {
|
||||
if ui.button(p).clicked() {
|
||||
act(app, "pageLayout.size", json!({"paper": p}));
|
||||
}
|
||||
|
||||
@@ -64,8 +64,10 @@ pub const INDEXED: [u32; 64] = [
|
||||
0x003366, 0x339966, 0x003300, 0x333300, 0x993300, 0x993366, 0x333399, 0x333333, // 56-63
|
||||
];
|
||||
|
||||
/// Paper sizes (`pageSetup paperSize`) we name.
|
||||
const PAPERS: [(u32, &str); 14] = [
|
||||
/// Paper sizes (`pageSetup paperSize`) we name. ID 12/13 are the JIS B4/B5 that
|
||||
/// `pageLayout.pageSetup` calls `B4`/`B5`; 42 is ISO B4, and 43–88 are the Japanese postcards,
|
||||
/// envelopes and B6. The engine's `known_paper` uses the same names and sizes.
|
||||
const PAPERS: [(u32, &str); 23] = [
|
||||
(1, "Letter"),
|
||||
(3, "Tabloid"),
|
||||
(4, "Ledger"),
|
||||
@@ -80,14 +82,40 @@ const PAPERS: [(u32, &str); 14] = [
|
||||
(20, "Envelope #10"),
|
||||
(27, "Envelope DL"),
|
||||
(34, "Envelope B5"),
|
||||
(42, "B4 (ISO)"),
|
||||
(43, "Japanese Postcard"),
|
||||
(69, "Japanese Double Postcard"),
|
||||
(70, "A6"),
|
||||
(71, "Japanese Envelope Kaku #2"),
|
||||
(72, "Japanese Envelope Kaku #3"),
|
||||
(73, "Japanese Envelope #3"),
|
||||
(74, "Japanese Envelope #4"),
|
||||
(88, "B6"),
|
||||
];
|
||||
|
||||
pub fn paper_name(id: u32) -> Option<&'static str> {
|
||||
PAPERS.iter().find(|(i, _)| *i == id).map(|(_, n)| *n)
|
||||
}
|
||||
|
||||
/// The file ID for a paper name. Spelling is loose like `known_paper`'s — spaces, hyphens,
|
||||
/// underscores and parentheses don't matter, and the JIS/ISO and Japanese-envelope spellings the
|
||||
/// page-setup commands take are folded onto the name in [`PAPERS`].
|
||||
pub fn paper_id(name: &str) -> Option<u32> {
|
||||
PAPERS.iter().find(|(_, n)| n.eq_ignore_ascii_case(name.trim())).map(|(i, _)| *i)
|
||||
let norm = |s: &str| s.trim().to_ascii_lowercase().replace([' ', '-', '_', '(', ')'], "");
|
||||
let normalized = norm(name);
|
||||
let alias = match normalized.as_str() {
|
||||
"b4jis" => "b4",
|
||||
"b5jis" => "b5",
|
||||
"b6jis" => "b6",
|
||||
"postcard" => "japanesepostcard",
|
||||
"doublepostcard" => "japanesedoublepostcard",
|
||||
"chou#3" => "japaneseenvelope#3",
|
||||
"chou#4" => "japaneseenvelope#4",
|
||||
"kaku#2" => "japaneseenvelopekaku#2",
|
||||
"kaku#3" => "japaneseenvelopekaku#3",
|
||||
other => other,
|
||||
};
|
||||
PAPERS.iter().find(|(_, n)| norm(n) == alias).map(|(i, _)| *i)
|
||||
}
|
||||
|
||||
/// Functions introduced after Excel 2007 that files store with the `_xlfn.` prefix. Entries with
|
||||
@@ -339,5 +367,26 @@ mod tests {
|
||||
assert_eq!(builtin_id(builtin_format(5).unwrap()), None);
|
||||
assert_eq!(paper_name(9), Some("A4"));
|
||||
assert_eq!(paper_id("a4"), Some(9));
|
||||
// 12/13 are the JIS B4/B5 Japanese Excel uses; 42 is ISO B4.
|
||||
assert_eq!(paper_name(12), Some("B4"));
|
||||
assert_eq!(paper_id("b5"), Some(13));
|
||||
assert_eq!(paper_id("B5 (JIS)"), Some(13));
|
||||
assert_eq!(paper_id("B4 (ISO)"), Some(42));
|
||||
// The Japanese papers page setup names.
|
||||
assert_eq!(paper_name(43), Some("Japanese Postcard"));
|
||||
assert_eq!(paper_name(88), Some("B6"));
|
||||
for (name, id) in [
|
||||
("Japanese Postcard", 43),
|
||||
("japanese double postcard", 69),
|
||||
("A6", 70),
|
||||
("Japanese Envelope Kaku #2", 71),
|
||||
("kaku #3", 72),
|
||||
("Japanese Envelope #3", 73),
|
||||
("chou #4", 74),
|
||||
("B6 (JIS)", 88),
|
||||
] {
|
||||
assert_eq!(paper_id(name), Some(id), "{name}");
|
||||
}
|
||||
assert_eq!(paper_id("Napkin"), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1165,3 +1165,35 @@ fn doc_props_keep_boundary_whitespace() {
|
||||
let (back, _) = read_xlsx(&write_xlsx(&wb).unwrap()).unwrap();
|
||||
assert_eq!(back.props, wb.props);
|
||||
}
|
||||
|
||||
/// Japanese paper sizes survive an XLSX save and reopen. IDs 12/13 are the JIS B4/B5; the
|
||||
/// Japanese postcards and envelopes have their own IDs, and `B4 (ISO)` keeps ISO B4. The ISO B5/B6
|
||||
/// names only exist in the engine: ECMA-376 has no `paperSize` id for them (only ISO B4), so they
|
||||
/// are not part of the round-trip.
|
||||
#[test]
|
||||
fn japanese_paper_sizes_round_trip() {
|
||||
for paper in [
|
||||
"B4",
|
||||
"B5",
|
||||
"B6",
|
||||
"B4 (ISO)",
|
||||
"A6",
|
||||
"Japanese Postcard",
|
||||
"Japanese Double Postcard",
|
||||
"Japanese Envelope #3",
|
||||
"Japanese Envelope #4",
|
||||
"Japanese Envelope Kaku #2",
|
||||
"Japanese Envelope Kaku #3",
|
||||
] {
|
||||
let mut wb = Workbook::new();
|
||||
wb.sheet_mut(0).unwrap().print.paper = paper.into();
|
||||
let (back, report) = read_xlsx(&write_xlsx(&wb).unwrap()).unwrap();
|
||||
assert!(report.warnings.is_empty(), "{paper}: {:?}", report.warnings);
|
||||
assert_eq!(back.sheet(0).unwrap().print.paper, paper);
|
||||
}
|
||||
// An alias is written as the file's own name and read back as that name.
|
||||
let mut wb = Workbook::new();
|
||||
wb.sheet_mut(0).unwrap().print.paper = "B5 (JIS)".into();
|
||||
let (back, _) = read_xlsx(&write_xlsx(&wb).unwrap()).unwrap();
|
||||
assert_eq!(back.sheet(0).unwrap().print.paper, "B5");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Target-app parity: GridCraft vs Microsoft Excel
|
||||
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-12 · **Change:** minor (rows auto-fit oversized fonts on open and row formatting) · **Target:** Microsoft Excel (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-21 · **Change:** minor (Page Setup paper sizes match Japanese Excel: B4/B5/B6 are the JIS sizes, with ISO B4/B5/B6 by name and the Japanese postcards, envelopes and A6 added; no score change) · **Target:** Microsoft Excel (Microsoft 365)
|
||||
|
||||
This is the authoritative parity assessment. [`ROADMAP.md`](../ROADMAP.md) summarizes it,
|
||||
[`gaps.md`](gaps.md) lists every shortfall, and the area checklists hold the detail:
|
||||
@@ -86,7 +86,7 @@ performance 8, stability 8, localization 4, hardware 2, platforms 2, ecosystem 2
|
||||
| Sort, filter, tables, validation, outline | 8 | ~82% | ~70% | 10–16 | Multi-level sort, AutoFilter, Advanced Filter, tables with totals, validation, outline, subtotals, consolidate. Bugs: subtotal header (#131), validation edits (#129), table style panic caught (#130); no slicers on tables |
|
||||
| PivotTables | 7 | ~65% | ~45% | 25–40 | Insert/Recommended, field list, three layouts, values on rows or columns, date grouping, show-values-as, refresh, calculated fields, XLSX round-trip. Missing: slicers, timelines, calculated items, PivotCharts, value/label filters, number grouping, GETPIVOTDATA, data-model pivots. Round-trip never checked in Excel |
|
||||
| Charts and sparklines | 8 | ~50% | ~30% | 40–60 | 25 chart kinds, all 2-D. The chart model holds title, legend position, a data-labels flag, gridlines flag and axis titles; no axis scaling, trendlines, error bars, per-point formatting, chart sheets, 3-D, surface, map, Pareto. Several kinds don't survive XLSX round trip (open PRs #158, #161). [chart-parity.md](chart-parity.md) |
|
||||
| Page layout, print, PDF | 5 | ~80% | ~55% | 12–20 | Page setup, print titles, breaks, Page Break Preview, headers/footers, PDF export and printing. No true Page Layout view; the PDF writer uses only the 14 standard fonts without embedding (no CJK or other non-Latin text in PDF); web print only exports (#141, #143) |
|
||||
| Page layout, print, PDF | 5 | ~80% | ~55% | 12–20 | Page setup, print titles, breaks, Page Break Preview, headers/footers, PDF export and printing. Paper sizes match Japanese Excel: B4/B5/B6 are the JIS sizes, ISO B4/B5/B6 stay reachable by name, and Japanese Postcard (はがき), Japanese Double Postcard (往復はがき), the chou/kaku envelopes and A6 are available. No true Page Layout view; the PDF writer uses only the 14 standard fonts without embedding (no CJK or other non-Latin text in PDF); web print only exports (#141, #143) |
|
||||
| What-if and analysis | 4 | ~50% | ~40% | 22–35 | Goal Seek, Scenario Manager, data tables, Analysis ToolPak (13 of 19 tools, no charts), Solver (Simplex LP, a local nonlinear method and Evolutionary; Answer and Limits reports; no Sensitivity report or options dialog). Missing: Forecast Sheet, Analyze Data |
|
||||
| Get & Transform, external data, data model | 5 | ~15% | ~10% | 60–100 | Import of CSV/TSV, JSON, HTML. No Power Query editor, no From Web, database or folder sources, no connections or refresh, no relationships or data model, no external workbook links (dropped with a warning) |
|
||||
| Insert objects | 3 | ~55% | ~45% | 25–40 | Pictures, 8 shape kinds (Excel has ~170), text boxes, own icon set, ink with Ink to Shape, hyperlinks, checkboxes. Missing: SmartArt, WordArt, equations, signature line, objects, 3-D models, screenshot, pictures in cells (PR #105) |
|
||||
@@ -332,6 +332,7 @@ The inventory carried over from the 2026-10-07 ROADMAP.md, updated for what land
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-21 | minor | Page Setup paper sizes: `B4`/`B5`/`B6` are the JIS sizes Japanese Excel uses (ISO B4/B5/B6 by name), and Japanese Postcard, Japanese Double Postcard, the chou/kaku envelopes and A6 are added; PDF MediaBox and `file.print` follow, and the Size menu lists them |
|
||||
| 2026-10-12 | minor | Essentials AutoFit evidence: rows auto-fit oversized fonts on open and row formatting (#833) |
|
||||
| 2026-10-12 | minor | Sheet tabs: reveal the selected tab on navigation and resize while preserving manual scrolling (#681); no score change |
|
||||
| 2026-10-12 | minor | PivotTables: values on rows (Σ Values in Rows at any position, all three layouts; `pivot.moveField` and the field pane move it; `dataOnRows` round-trips) (no score change) |
|
||||
|
||||
Reference in New Issue
Block a user