forked from 1b-agent/opencadstudio-upstream
feat: embed images (OLE2FRAME), print pipeline fixes, plot prefs, community fonts
- IMAGEEMBED: pack raster images into OLE2FRAME entities so a .dwg is self-contained; 'Embed Image' entry in the XREF attach menu; control/MCP op embed_image for external tools; dxf_name for OLE2FRAME queries. - Scene: populate_images_from_document now rebuilds OLE2FRAME and PDF underlay models too (they used to vanish after any XREF op). - Printing: add PRINTER_ENUM_NETWORK pass when listing printers; GDI spooler fallback (hayro raster at 300 DPI + StretchDIBits) for hosts whose PDF handler registers no print verb; DEVMODE orientation with a 90-degree raster rotation fallback for v4 drivers; fix the fit-scale precedence bug that printed blank pages. - PDF export dialogs on Windows use the async file dialog so the native overwrite confirmation no longer deadlocks the UI thread. - Plot dialog: persist the chosen printer (saved on selection) and the picked plot window across sessions; page setups with no device no longer reset the output device. - Community fonts: detect unresolvable .shx references on open and offer a download from the repository's fonts/ folder (contribution guide in fonts/README.md); custom font source URL setting for intranet font servers; downloaded fonts resolve from the per-user fonts directory.
This commit is contained in:
1 parent
1450fdeedf
commit
28efbffd57
27 files changed
+1767
-66
No files matched your search
@@ -1,6 +1,10 @@
|
||||
// IMAGE / IMAGEATTACH command — place a raster image in the drawing.
|
||||
// IMAGE / IMAGEEMBED commands — place a raster image in the drawing.
|
||||
//
|
||||
// Workflow:
|
||||
// IMAGE references the picture by path (RasterImage + ImageDefinition, like
|
||||
// AutoCAD's IMAGEATTACH); IMAGEEMBED packs the encoded raster inside the
|
||||
// drawing as an OLE2FRAME so the .dwg travels alone.
|
||||
//
|
||||
// Workflow (both commands):
|
||||
// 1. File dialog opens (async, handled in update.rs).
|
||||
// 2. User picks insertion point (first click).
|
||||
// 3. User drags to pick width; height is computed from the image's aspect ratio.
|
||||
@@ -13,10 +17,22 @@ use glam::DVec3;
|
||||
use crate::t;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::io::ole_embed::{build_embedded_ole, corners_from_placement, EmbeddedImage};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
|
||||
/// Where the picture's pixels come from.
|
||||
#[derive(Clone)]
|
||||
pub enum ImageSource {
|
||||
/// Reference by path — the drawing stores the file location.
|
||||
File(String),
|
||||
/// Embedded — the encoded raster travels inside the drawing.
|
||||
Embedded(EmbeddedImage),
|
||||
}
|
||||
|
||||
pub struct ImageCommand {
|
||||
file_path: String,
|
||||
source: ImageSource,
|
||||
/// CLI name: "IMAGE" for a path reference, "IMAGEEMBED" when embedded.
|
||||
name: &'static str,
|
||||
pixel_width: u32,
|
||||
pixel_height: u32,
|
||||
origin: Option<DVec3>,
|
||||
@@ -26,7 +42,8 @@ pub struct ImageCommand {
|
||||
impl ImageCommand {
|
||||
pub fn new(file_path: String, pixel_width: u32, pixel_height: u32) -> Self {
|
||||
Self {
|
||||
file_path,
|
||||
source: ImageSource::File(file_path),
|
||||
name: "IMAGE",
|
||||
pixel_width,
|
||||
pixel_height,
|
||||
origin: None,
|
||||
@@ -34,6 +51,25 @@ impl ImageCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_embedded(image: EmbeddedImage) -> Self {
|
||||
let (pixel_width, pixel_height) = (image.pixel_width, image.pixel_height);
|
||||
Self {
|
||||
source: ImageSource::Embedded(image),
|
||||
name: "IMAGEEMBED",
|
||||
pixel_width,
|
||||
pixel_height,
|
||||
origin: None,
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn source_name(&self) -> std::borrow::Cow<'_, str> {
|
||||
match &self.source {
|
||||
ImageSource::File(path) => std::borrow::Cow::Borrowed(short_name(path)),
|
||||
ImageSource::Embedded(image) => std::borrow::Cow::Owned(image.name.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn aspect(&self) -> f64 {
|
||||
if self.pixel_height == 0 {
|
||||
1.0
|
||||
@@ -46,21 +82,30 @@ impl ImageCommand {
|
||||
let origin = self.plane.to_local(origin);
|
||||
let width_pt = self.plane.to_local(width_pt);
|
||||
let world_width = (width_pt.x - origin.x).abs().max(0.001);
|
||||
let world_height = world_width / self.aspect();
|
||||
|
||||
let ins = Vector3::new(origin.x, origin.y, origin.z);
|
||||
|
||||
let mut img = RasterImage::with_size(
|
||||
&self.file_path,
|
||||
ins,
|
||||
self.pixel_width as f64,
|
||||
self.pixel_height as f64,
|
||||
world_width,
|
||||
world_height,
|
||||
);
|
||||
img.flags = acadrust::entities::ImageDisplayFlags::SHOW_IMAGE
|
||||
| acadrust::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY;
|
||||
self.plane.place_entity(EntityType::RasterImage(img))
|
||||
match &self.source {
|
||||
ImageSource::File(path) => {
|
||||
let world_height = world_width / self.aspect();
|
||||
let mut img = RasterImage::with_size(
|
||||
path,
|
||||
ins,
|
||||
self.pixel_width as f64,
|
||||
self.pixel_height as f64,
|
||||
world_width,
|
||||
world_height,
|
||||
);
|
||||
img.flags = acadrust::entities::ImageDisplayFlags::SHOW_IMAGE
|
||||
| acadrust::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY;
|
||||
self.plane.place_entity(EntityType::RasterImage(img))
|
||||
}
|
||||
ImageSource::Embedded(image) => {
|
||||
let (upper_left, lower_right) =
|
||||
corners_from_placement(ins, world_width, self.aspect());
|
||||
let ole = build_embedded_ole(image, upper_left, lower_right);
|
||||
self.plane.place_entity(EntityType::Ole2Frame(ole))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,14 +115,14 @@ impl CadCommand for ImageCommand {
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"IMAGE"
|
||||
self.name
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
if self.origin.is_none() {
|
||||
t!(
|
||||
"IMAGE Specify insertion point (%{name}): ",
|
||||
name = short_name(&self.file_path)
|
||||
name = self.source_name()
|
||||
)
|
||||
.into_owned()
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user