forked from 1b-agent/opencadstudio-upstream
Merge pull request #1337 from HakanSeven12/plot/b1-plot-settings-compat
fix(plot): write page setups in the file format's canonical form
This commit is contained in:
11 files changed
+16779
-278
No files matched your search
@@ -294,7 +294,7 @@ impl OpenCADStudio {
|
|||||||
match req["op"].as_str().unwrap_or("") {
|
match req["op"].as_str().unwrap_or("") {
|
||||||
"new" => {
|
"new" => {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
self.tabs[i].scene.clear();
|
self.tabs[i].scene.reset_to_new_drawing();
|
||||||
self.tabs[i].scene.material_base_dir = None;
|
self.tabs[i].scene.material_base_dir = None;
|
||||||
self.tabs[i].current_path = None;
|
self.tabs[i].current_path = None;
|
||||||
// The headless session starts on the welcome (Start) tab, which
|
// The headless session starts on the welcome (Start) tab, which
|
||||||
|
|||||||
+1
-19
@@ -1,5 +1,4 @@
|
|||||||
use crate::command::CadCommand;
|
use crate::command::CadCommand;
|
||||||
use crate::io::linetypes;
|
|
||||||
use crate::modules::draw::modify::block_edit::BlockEditSession;
|
use crate::modules::draw::modify::block_edit::BlockEditSession;
|
||||||
use crate::modules::draw::modify::refedit::RefEditSession;
|
use crate::modules::draw::modify::refedit::RefEditSession;
|
||||||
use crate::scene::pick::grip::GripEdit;
|
use crate::scene::pick::grip::GripEdit;
|
||||||
@@ -567,24 +566,7 @@ impl DocumentTab {
|
|||||||
|
|
||||||
pub(super) fn new_drawing(n: usize) -> Self {
|
pub(super) fn new_drawing(n: usize) -> Self {
|
||||||
let mut scene = Scene::new();
|
let mut scene = Scene::new();
|
||||||
linetypes::populate_document(&mut scene.document);
|
scene.populate_new_drawing_defaults();
|
||||||
// Paper layouts start as A4 landscape.
|
|
||||||
for obj in scene.document.objects.values_mut() {
|
|
||||||
if let acadrust::objects::ObjectType::Layout(l) = obj {
|
|
||||||
if l.name != "Model" {
|
|
||||||
l.min_limits = (0.0, 0.0);
|
|
||||||
l.max_limits = (297.0, 210.0);
|
|
||||||
l.min_extents = (0.0, 0.0, 0.0);
|
|
||||||
l.max_extents = (297.0, 210.0, 0.0);
|
|
||||||
l.paper_width = 297.0;
|
|
||||||
l.paper_height = 210.0;
|
|
||||||
l.plot_paper_units = 1;
|
|
||||||
l.plot_scale_numerator = 1.0;
|
|
||||||
l.plot_scale_denominator = 1.0;
|
|
||||||
l.paper_size = "ISO_A4_(297.00_x_210.00_MM)".into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
id: NEXT_DOCUMENT_TAB_ID.fetch_add(1, Ordering::Relaxed),
|
id: NEXT_DOCUMENT_TAB_ID.fetch_add(1, Ordering::Relaxed),
|
||||||
scene,
|
scene,
|
||||||
|
|||||||
+716
-230
File diff suppressed because it is too large.
Load diff
@@ -1508,8 +1508,7 @@ impl OpenCADStudio {
|
|||||||
Message::ClearScene => {
|
Message::ClearScene => {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
self.push_undo_snapshot(i, "CLEAR");
|
self.push_undo_snapshot(i, "CLEAR");
|
||||||
self.tabs[i].scene.clear();
|
self.tabs[i].scene.reset_to_new_drawing();
|
||||||
crate::io::linetypes::populate_document(&mut self.tabs[i].scene.document);
|
|
||||||
self.tabs[i].properties = PropertiesPanel::empty();
|
self.tabs[i].properties = PropertiesPanel::empty();
|
||||||
let doc_layers = self.tabs[i].scene.document.layers.clone();
|
let doc_layers = self.tabs[i].scene.document.layers.clone();
|
||||||
let vp_info = self.tabs[i].scene.viewport_list();
|
let vp_info = self.tabs[i].scene.viewport_list();
|
||||||
|
|||||||
@@ -6033,24 +6033,7 @@ properties={:.1}ms picked={}",
|
|||||||
if let acadrust::objects::ObjectType::Layout(l) = obj {
|
if let acadrust::objects::ObjectType::Layout(l) = obj {
|
||||||
if l.name == new_name {
|
if l.name == new_name {
|
||||||
l.flags = layout_flags;
|
l.flags = layout_flags;
|
||||||
l.min_limits = (0.0, 0.0);
|
crate::scene::apply_default_page_setup(l, &plot_style);
|
||||||
l.max_limits = (297.0, 210.0);
|
|
||||||
l.min_extents = (0.0, 0.0, 0.0);
|
|
||||||
l.max_extents = (297.0, 210.0, 0.0);
|
|
||||||
l.paper_width = 297.0;
|
|
||||||
l.paper_height = 210.0;
|
|
||||||
l.plot_paper_units = 1;
|
|
||||||
l.plot_scale_numerator = 1.0;
|
|
||||||
l.plot_scale_denominator = 1.0;
|
|
||||||
l.plot_scale_type = 16;
|
|
||||||
l.plot_scale_factor = 1.0;
|
|
||||||
l.plot_type = 5;
|
|
||||||
l.plot_flags.use_standard_scale = true;
|
|
||||||
l.plot_flags.print_lineweights = true;
|
|
||||||
l.plot_flags.plot_plot_styles = !plot_style.is_empty();
|
|
||||||
l.plot_flags.show_plot_styles = !plot_style.is_empty();
|
|
||||||
l.plot_style_sheet = plot_style;
|
|
||||||
l.paper_size = "ISO_A4_(297.00_x_210.00_MM)".into();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2709,4 +2709,25 @@ impl Scene {
|
|||||||
self.named_parameters = crate::scene::named_parameters::ParameterTable::new();
|
self.named_parameters = crate::scene::named_parameters::ParameterTable::new();
|
||||||
self.bump_geometry();
|
self.bump_geometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset to the drawing File → New produces. Every path that starts a
|
||||||
|
/// fresh drawing (the New tab, CLEAR, the automation `new` op) goes
|
||||||
|
/// through here so they all agree on what a blank drawing contains.
|
||||||
|
pub fn reset_to_new_drawing(&mut self) {
|
||||||
|
self.clear();
|
||||||
|
self.populate_new_drawing_defaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What a blank document gets on top of the codec's defaults: the
|
||||||
|
/// standard linetypes and a compatible page setup on every paper layout.
|
||||||
|
pub fn populate_new_drawing_defaults(&mut self) {
|
||||||
|
crate::io::linetypes::populate_document(&mut self.document);
|
||||||
|
for obj in self.document.objects.values_mut() {
|
||||||
|
if let acadrust::objects::ObjectType::Layout(l) = obj {
|
||||||
|
if l.name != "Model" {
|
||||||
|
crate::scene::apply_default_page_setup(l, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+75
-7
@@ -33,6 +33,7 @@ pub(crate) mod centermark;
|
|||||||
pub(crate) mod dimension_assoc;
|
pub(crate) mod dimension_assoc;
|
||||||
pub(crate) mod dimension_assoc_chain;
|
pub(crate) mod dimension_assoc_chain;
|
||||||
pub use dimension_assoc::{ReferenceStatus, ResolvedReference};
|
pub use dimension_assoc::{ReferenceStatus, ResolvedReference};
|
||||||
|
pub use page_setup::{apply_default_page_setup, rotated_margins};
|
||||||
mod dwg_native_constraints;
|
mod dwg_native_constraints;
|
||||||
mod entity;
|
mod entity;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -4378,13 +4379,8 @@ impl Scene {
|
|||||||
None
|
None
|
||||||
})?;
|
})?;
|
||||||
// `paper_limits()` already swaps the sheet for a 90°/270° rotation, so the
|
// `paper_limits()` already swaps the sheet for a 90°/270° rotation, so the
|
||||||
// margins must rotate to the same edges: a margin on a physical side moves
|
// margins must rotate to the same edges.
|
||||||
// to the displayed side that side rotates onto.
|
let (ml, mb, mr, mt) = page_setup::rotated_margins((left, bottom, right, top), rot);
|
||||||
let (ml, mb, mr, mt) = match rot {
|
|
||||||
1 | 3 => (bottom, left, top, right),
|
|
||||||
2 => (right, top, left, bottom),
|
|
||||||
_ => (left, bottom, right, top),
|
|
||||||
};
|
|
||||||
// Plot margins are millimetres like the paper size; scale them into the
|
// Plot margins are millimetres like the paper size; scale them into the
|
||||||
// layout's paper-space units so the inset matches the (already scaled)
|
// layout's paper-space units so the inset matches the (already scaled)
|
||||||
// sheet rect. Without this an inch paper space insets an ~8-inch sheet
|
// sheet rect. Without this an inch paper space insets an ~8-inch sheet
|
||||||
@@ -4970,6 +4966,78 @@ impl Scene {
|
|||||||
.then_some(owner)
|
.then_some(owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Millimetres in one unit of the paper side of this drawing's scale
|
||||||
|
/// family: 25.4 for an imperial drawing (whose `1/8" = 1'-0"` style scales
|
||||||
|
/// measure paper in inches), 1 for a metric one. Plot-scale factors built
|
||||||
|
/// from [`Scene::scale_list`] are expressed per this unit; page setups
|
||||||
|
/// store theirs per the layout's own paper unit, so the two are converted
|
||||||
|
/// through this value.
|
||||||
|
pub(crate) fn scale_family_unit_mm(&self) -> f64 {
|
||||||
|
if self.prefers_imperial_scales() == Some(true) {
|
||||||
|
25.4
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `paper : drawing` ratio behind a scale-list name, from the
|
||||||
|
/// drawing's own `Scale` object when it has one, otherwise from the
|
||||||
|
/// built-in family. A ratio keeps the plot scale readable in a page setup
|
||||||
|
/// (`1 : 250` rather than `0.004 : 1`).
|
||||||
|
pub(crate) fn scale_ratio(&self, name: &str) -> Option<(f64, f64)> {
|
||||||
|
use acadrust::objects::ObjectType;
|
||||||
|
let stored = self.document.objects.values().find_map(|o| match o {
|
||||||
|
ObjectType::Scale(s) if !s.is_temporary && s.name.eq_ignore_ascii_case(name) => {
|
||||||
|
Some((s.paper_units, s.drawing_units))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
stored
|
||||||
|
.or_else(|| Self::parse_scale_name_ratio(name))
|
||||||
|
.filter(|(paper, drawing)| *paper > 0.0 && *drawing > 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `paper : drawing` ratio spelled by a scale name: `1:250` gives
|
||||||
|
/// `(1, 250)`, an architectural `1/8" = 1'-0"` gives `(0.125, 12)` in
|
||||||
|
/// inches. Anything else is not a scale name.
|
||||||
|
pub(crate) fn parse_scale_name_ratio(name: &str) -> Option<(f64, f64)> {
|
||||||
|
if let Some((paper, drawing)) = name.split_once(':') {
|
||||||
|
let paper: f64 = paper.trim().parse().ok()?;
|
||||||
|
let drawing: f64 = drawing.trim().parse().ok()?;
|
||||||
|
return (paper > 0.0 && drawing > 0.0).then_some((paper, drawing));
|
||||||
|
}
|
||||||
|
let (paper, drawing) = name.split_once('=')?;
|
||||||
|
let paper = Self::parse_feet_inches(paper)?;
|
||||||
|
let drawing = Self::parse_feet_inches(drawing)?;
|
||||||
|
(paper > 0.0 && drawing > 0.0).then_some((paper, drawing))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inches in a `F'-I"` / `I"` / `F'` length, where the inch part may be a
|
||||||
|
/// fraction (`3/32"`).
|
||||||
|
fn parse_feet_inches(text: &str) -> Option<f64> {
|
||||||
|
let text = text.trim();
|
||||||
|
let (feet, inches) = match text.split_once('\'') {
|
||||||
|
Some((feet, rest)) => (
|
||||||
|
feet.trim().parse::<f64>().ok()?,
|
||||||
|
rest.trim_start_matches('-').trim(),
|
||||||
|
),
|
||||||
|
None => (0.0, text),
|
||||||
|
};
|
||||||
|
let inches = inches.trim_end_matches('"').trim();
|
||||||
|
let inches = if inches.is_empty() {
|
||||||
|
0.0
|
||||||
|
} else if let Some((num, den)) = inches.split_once('/') {
|
||||||
|
let den: f64 = den.trim().parse().ok()?;
|
||||||
|
if den <= 0.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
num.trim().parse::<f64>().ok()? / den
|
||||||
|
} else {
|
||||||
|
inches.parse::<f64>().ok()?
|
||||||
|
};
|
||||||
|
Some(feet * 12.0 + inches)
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle of the real (non-temporary) `Scale` object with this display name.
|
/// Handle of the real (non-temporary) `Scale` object with this display name.
|
||||||
/// Scales are matched by their `name` field, not the dictionary key — some
|
/// Scales are matched by their `name` field, not the dictionary key — some
|
||||||
/// files key the entries by an internal identifier (`*A1`, `A0`, …) that
|
/// files key the entries by an internal identifier (`*A1`, `A0`, …) that
|
||||||
|
|||||||
+52
-1
@@ -5,9 +5,60 @@
|
|||||||
//! dictionary + objects consistent so the result round-trips through DWG/DXF.
|
//! dictionary + objects consistent so the result round-trips through DWG/DXF.
|
||||||
|
|
||||||
use super::Scene;
|
use super::Scene;
|
||||||
use acadrust::objects::{Dictionary, ObjectType, PlotSettings};
|
use acadrust::objects::{Dictionary, Layout, ObjectType, PlotSettings};
|
||||||
use acadrust::Handle;
|
use acadrust::Handle;
|
||||||
|
|
||||||
|
/// Give a freshly created paper layout the page setup a new drawing starts
|
||||||
|
/// with — ISO A4 landscape on no plotter, plotted 1:1 as a layout — stored the
|
||||||
|
/// way the file format expects it: the portrait medium under its canonical
|
||||||
|
/// name with the orientation in the rotation code. `plot_style` names the
|
||||||
|
/// plot style table to attach, or is empty.
|
||||||
|
pub fn apply_default_page_setup(layout: &mut Layout, plot_style: &str) {
|
||||||
|
use acadrust::objects::{PlotPaperUnits, PlotRotation, PlotType, ScaledType};
|
||||||
|
let a4 = crate::io::paper_catalog::default_paper();
|
||||||
|
let (paper_width, paper_height) = a4.portrait_mm();
|
||||||
|
layout.min_limits = (0.0, 0.0);
|
||||||
|
layout.max_limits = (paper_height, paper_width);
|
||||||
|
layout.min_extents = (0.0, 0.0, 0.0);
|
||||||
|
layout.max_extents = (paper_height, paper_width, 0.0);
|
||||||
|
layout.paper_width = paper_width;
|
||||||
|
layout.paper_height = paper_height;
|
||||||
|
layout.plot_rotation = PlotRotation::Degrees90.to_code();
|
||||||
|
layout.plot_paper_units = PlotPaperUnits::Millimeters.to_code();
|
||||||
|
layout.plot_type = PlotType::Layout.to_code();
|
||||||
|
layout.plot_scale_type = ScaledType::OneToOne.to_code();
|
||||||
|
layout.plot_scale_numerator = 1.0;
|
||||||
|
layout.plot_scale_denominator = 1.0;
|
||||||
|
layout.plot_scale_factor = 1.0;
|
||||||
|
layout.plot_flags.use_standard_scale = true;
|
||||||
|
layout.plot_flags.print_lineweights = true;
|
||||||
|
layout.plot_flags.draw_viewports_first = true;
|
||||||
|
layout.plot_flags.plot_plot_styles = !plot_style.is_empty();
|
||||||
|
layout.plot_flags.show_plot_styles = !plot_style.is_empty();
|
||||||
|
layout.plot_style_sheet = plot_style.to_string();
|
||||||
|
layout.paper_size = a4.canonical.to_string();
|
||||||
|
layout.plot_printer_name = crate::io::plot_device::PlotDevice::None.canonical_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The unprintable margins of a sheet as they appear on the rotated layout,
|
||||||
|
/// `(left, bottom, right, top)`. Stored margins belong to the medium's own
|
||||||
|
/// edges; a plot rotated by a quarter turn (`rotation` is the file's 0–3
|
||||||
|
/// code) shows the medium turned on the layout, so each margin moves to the
|
||||||
|
/// edge its side lands on — verified against limits written by the
|
||||||
|
/// commercial application: at 90° the medium's top margin is the layout's
|
||||||
|
/// left one and its left margin the layout's bottom one.
|
||||||
|
pub fn rotated_margins(
|
||||||
|
(left, bottom, right, top): (f64, f64, f64, f64),
|
||||||
|
rotation: i16,
|
||||||
|
) -> (f64, f64, f64, f64) {
|
||||||
|
match rotation {
|
||||||
|
1 => (top, left, bottom, right),
|
||||||
|
2 => (right, top, left, bottom),
|
||||||
|
3 => (bottom, right, top, left),
|
||||||
|
_ => (left, bottom, right, top),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
/// Handle of the `ACAD_PLOTSETTINGS` dictionary, located robustly.
|
/// Handle of the `ACAD_PLOTSETTINGS` dictionary, located robustly.
|
||||||
///
|
///
|
||||||
|
|||||||
Vendored
+29
@@ -0,0 +1,29 @@
|
|||||||
|
Page-setup fixtures saved by a commercial CAD application (2016 release, DXF
|
||||||
|
AC1027 and the matching DWG). Provided by Panos for the plotting parity track;
|
||||||
|
the drawing holds one 10 m line (`$INSUNITS` = 6, metres) and three paper
|
||||||
|
layouts, each with a viewport of that line and its dimension:
|
||||||
|
|
||||||
|
| Layout | Device | Media (`4`) | `44`×`45` | `73` | `72` | `142`/`143` | `147` | `75` |
|
||||||
|
|---|---|---|---|---|---|---|---|---|
|
||||||
|
| `A4 1-100` | `DWG To PDF.pc3` | `ISO_A4_(297.00_x_210.00_MM)` | 297 × 210 | 0 | mm | 1 / 1 | 25.4 | 16 |
|
||||||
|
| `A3 - plotter` | system printer `HPB602DE (HP OfficeJet Pro 7740 series)` | `A3` | 297.01 × 419.99 | 1 | mm | 1 / 1 | 25.4 | 16 |
|
||||||
|
| `ARCH D - PLOTTER` | the application's second PDF plotter configuration (`… PDF (High Quality Print).pc3`) | `ARCH_D_(36.00_x_24.00_Inches)` | 914.4 × 609.6 | 0 | in | 1 / 25.4 | 0.03937 | 16 |
|
||||||
|
|
||||||
|
What the file establishes (see `src/app/update/file.rs`, `plot_settings_from_dialog`):
|
||||||
|
|
||||||
|
- A landscape sheet is written either as the driver's own landscape medium
|
||||||
|
with rotation 0 (the PDF driver lists both orientations) or as a portrait
|
||||||
|
medium with rotation 90 (system printers) — rotation is relative to the
|
||||||
|
medium's orientation, and the medium's exact driver dimensions are kept.
|
||||||
|
- Margins (`40`–`43`, mm) belong to the medium's own edges: the PDF driver's
|
||||||
|
A4 has 5.79375 left/right and 17.79375 bottom/top. Layout limits (`10`/`20`)
|
||||||
|
put the sheet at minus the *displayed* margins: at rotation 90 the medium's
|
||||||
|
top margin (20.02) becomes the layout's left one, its left margin (5.00) the
|
||||||
|
layout's bottom one.
|
||||||
|
- Plot flags `70` = 672 on every layout: draw viewports first, print
|
||||||
|
lineweights, plot with plot styles — the "use standard scale" bit stays clear
|
||||||
|
even with a standard code in `75`; the model tab (fit to paper) sets it.
|
||||||
|
- `147` is `142/143` × 25.4 on millimetre page setups and `142/143` itself on
|
||||||
|
inch ones.
|
||||||
|
- An inch page setup over a millimetre paper space stores "1:1" as
|
||||||
|
`1 in = 25.4 units` (`75` = 16 kept).
|
||||||
BIN
Binary file not shown.
+15882
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user