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("") {
|
||||
"new" => {
|
||||
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].current_path = None;
|
||||
// The headless session starts on the welcome (Start) tab, which
|
||||
|
||||
+1
-19
@@ -1,5 +1,4 @@
|
||||
use crate::command::CadCommand;
|
||||
use crate::io::linetypes;
|
||||
use crate::modules::draw::modify::block_edit::BlockEditSession;
|
||||
use crate::modules::draw::modify::refedit::RefEditSession;
|
||||
use crate::scene::pick::grip::GripEdit;
|
||||
@@ -567,24 +566,7 @@ impl DocumentTab {
|
||||
|
||||
pub(super) fn new_drawing(n: usize) -> Self {
|
||||
let mut scene = Scene::new();
|
||||
linetypes::populate_document(&mut scene.document);
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
scene.populate_new_drawing_defaults();
|
||||
Self {
|
||||
id: NEXT_DOCUMENT_TAB_ID.fetch_add(1, Ordering::Relaxed),
|
||||
scene,
|
||||
|
||||
+716
-230
File diff suppressed because it is too large.
Load diff
@@ -1508,8 +1508,7 @@ impl OpenCADStudio {
|
||||
Message::ClearScene => {
|
||||
let i = self.active_tab;
|
||||
self.push_undo_snapshot(i, "CLEAR");
|
||||
self.tabs[i].scene.clear();
|
||||
crate::io::linetypes::populate_document(&mut self.tabs[i].scene.document);
|
||||
self.tabs[i].scene.reset_to_new_drawing();
|
||||
self.tabs[i].properties = PropertiesPanel::empty();
|
||||
let doc_layers = self.tabs[i].scene.document.layers.clone();
|
||||
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 l.name == new_name {
|
||||
l.flags = layout_flags;
|
||||
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.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();
|
||||
crate::scene::apply_default_page_setup(l, &plot_style);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2709,4 +2709,25 @@ impl Scene {
|
||||
self.named_parameters = crate::scene::named_parameters::ParameterTable::new();
|
||||
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_chain;
|
||||
pub use dimension_assoc::{ReferenceStatus, ResolvedReference};
|
||||
pub use page_setup::{apply_default_page_setup, rotated_margins};
|
||||
mod dwg_native_constraints;
|
||||
mod entity;
|
||||
#[cfg(test)]
|
||||
@@ -4378,13 +4379,8 @@ impl Scene {
|
||||
None
|
||||
})?;
|
||||
// `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
|
||||
// to the displayed side that side rotates onto.
|
||||
let (ml, mb, mr, mt) = match rot {
|
||||
1 | 3 => (bottom, left, top, right),
|
||||
2 => (right, top, left, bottom),
|
||||
_ => (left, bottom, right, top),
|
||||
};
|
||||
// margins must rotate to the same edges.
|
||||
let (ml, mb, mr, mt) = page_setup::rotated_margins((left, bottom, right, top), rot);
|
||||
// Plot margins are millimetres like the paper size; scale them into the
|
||||
// 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
|
||||
@@ -4970,6 +4966,78 @@ impl Scene {
|
||||
.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.
|
||||
/// Scales are matched by their `name` field, not the dictionary key — some
|
||||
/// 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.
|
||||
|
||||
use super::Scene;
|
||||
use acadrust::objects::{Dictionary, ObjectType, PlotSettings};
|
||||
use acadrust::objects::{Dictionary, Layout, ObjectType, PlotSettings};
|
||||
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 {
|
||||
/// 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