forked from 1b-agent/opencadstudio-upstream
Merge remote-tracking branch 'origin/main' into codex/pdf-attach-parity
# Conflicts: # locales/ar-SA/opencadstudio.ftl # locales/bg-BG/opencadstudio.ftl # locales/cs-CZ/opencadstudio.ftl # locales/de-DE/opencadstudio.ftl # locales/el-GR/opencadstudio.ftl # locales/en-US/opencadstudio.ftl # locales/es-ES/opencadstudio.ftl # locales/fi-FI/opencadstudio.ftl # locales/fr-FR/opencadstudio.ftl # locales/hi-IN/opencadstudio.ftl # locales/hu-HU/opencadstudio.ftl # locales/it-IT/opencadstudio.ftl # locales/ja-JP/opencadstudio.ftl # locales/ko-KR/opencadstudio.ftl # locales/nl-NL/opencadstudio.ftl # locales/pl-PL/opencadstudio.ftl # locales/pt-BR/opencadstudio.ftl # locales/ru-RU/opencadstudio.ftl # locales/tr-TR/opencadstudio.ftl # locales/zh-CN/opencadstudio.ftl # locales/zh-TW/opencadstudio.ftl # src/app/control/actions.rs # src/app/mod.rs # src/app/update/mod.rs # src/app/view/modal.rs # src/io/xref.rs # src/ui/window/mod.rs
This commit is contained in:
commit
c1ecb0edaf
77 files changed
+5695
-606
No files matched your search
@@ -42,6 +42,15 @@ pub enum FontSource {
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// Stable key for comparing bare font names without path or case differences.
|
||||
pub fn font_key(name: &str) -> String {
|
||||
name.rsplit(['/', '\\'])
|
||||
.next()
|
||||
.unwrap_or(name)
|
||||
.trim()
|
||||
.to_ascii_lowercase()
|
||||
}
|
||||
|
||||
impl FontSource {
|
||||
/// Empty / whitespace input selects the community repository.
|
||||
pub fn from_url(url: &str) -> Self {
|
||||
@@ -265,6 +274,12 @@ mod tests {
|
||||
assert_eq!(percent_encode_path("vn 30f.shx"), "vn%2030f.shx");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn font_keys_are_bare_trimmed_and_case_insensitive() {
|
||||
assert_eq!(font_key(r#" C:\Fonts\ROMANS.SHX "#), "romans.shx");
|
||||
assert_eq!(font_key("folder/simplex.shx"), "simplex.shx");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_a_non_listing_response() {
|
||||
assert!(parse_contents("{\"message\":\"Not Found\"}").is_err());
|
||||
|
||||
@@ -1361,6 +1361,25 @@ pub fn parse_save_format(format: &str) -> (&'static str, acadrust::DxfVersion) {
|
||||
(ext, version)
|
||||
}
|
||||
|
||||
/// Parse an explicit automation/CLI target version without silently falling
|
||||
/// back to a newer format. Accepts either release names (`R14`, `2000`, …)
|
||||
/// or their DXF codes (`AC1014`, `AC1015`, …).
|
||||
pub fn parse_target_version(value: &str) -> Result<acadrust::DxfVersion, String> {
|
||||
use acadrust::DxfVersion;
|
||||
match value.trim().to_ascii_uppercase().as_str() {
|
||||
"R14" | "14" | "AC1014" => Ok(DxfVersion::AC1014),
|
||||
"2000" | "AC1015" => Ok(DxfVersion::AC1015),
|
||||
"2004" | "AC1018" => Ok(DxfVersion::AC1018),
|
||||
"2007" | "AC1021" => Ok(DxfVersion::AC1021),
|
||||
"2010" | "AC1024" => Ok(DxfVersion::AC1024),
|
||||
"2013" | "AC1027" => Ok(DxfVersion::AC1027),
|
||||
"2018" | "AC1032" => Ok(DxfVersion::AC1032),
|
||||
_ => Err(format!(
|
||||
"unsupported target version {value:?}; use R14, 2000, 2004, 2007, 2010, 2013 or 2018"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reverse of [`parse_save_format`]: the Save-dialog format string for a
|
||||
/// version + DXF/DWG choice (e.g. `AC1018, is_dxf=false` -> `"DWG 2004"`).
|
||||
/// Used to default the Save-As dropdown to the loaded file's version so a
|
||||
|
||||
@@ -1073,7 +1073,18 @@ fn gdi_raster_print(
|
||||
}
|
||||
|
||||
/// Open a file with the OS default application (used for print preview).
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(all(not(target_arch = "wasm32"), test))]
|
||||
pub fn open_in_viewer(path: &std::path::Path) -> Result<(), String> {
|
||||
// Unit tests exercise the complete preview pipeline, but must not launch
|
||||
// the user's PDF application or leave generated previews in the temp
|
||||
// directory. Production viewers need the file after this call returns;
|
||||
// the test path has no external consumer, so it can be removed now.
|
||||
std::fs::remove_file(path)
|
||||
.map_err(|error| format!("Could not consume test preview: {error}"))
|
||||
}
|
||||
|
||||
/// Open a file with the OS default application (used for print preview).
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
|
||||
pub fn open_in_viewer(path: &std::path::Path) -> Result<(), String> {
|
||||
let p = path.to_string_lossy().to_string();
|
||||
#[cfg(target_os = "windows")]
|
||||
@@ -1099,6 +1110,19 @@ pub fn open_in_viewer(path: &std::path::Path) -> Result<(), String> {
|
||||
.map_err(|e| format!("Could not open preview: {e}"))
|
||||
}
|
||||
|
||||
#[cfg(all(test, not(target_arch = "wasm32")))]
|
||||
mod preview_isolation_tests {
|
||||
#[test]
|
||||
fn a_test_preview_is_consumed_without_leaving_a_temp_file() {
|
||||
let path = super::temp_pdf_path("preview_test");
|
||||
std::fs::write(&path, b"test preview").unwrap();
|
||||
|
||||
super::open_in_viewer(&path).unwrap();
|
||||
|
||||
assert!(!path.exists(), "test preview was not cleaned up: {}", path.display());
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask the registered PDF application to print `path` on `printer` through
|
||||
/// its `printto` verb, once per copy. The printer name is quoted: some
|
||||
/// `printto` command templates do not quote their `%2`.
|
||||
|
||||
+260
-26
@@ -324,6 +324,82 @@ pub fn register_underlay_sources(doc: &CadDocument, base_dir: &Path) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Layer properties an override can change, as compared for the
|
||||
/// "Layer property overrides" row.
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct LayerLook {
|
||||
color: acadrust::types::Color,
|
||||
line_type: String,
|
||||
line_weight: acadrust::types::LineWeight,
|
||||
off: bool,
|
||||
frozen: bool,
|
||||
locked: bool,
|
||||
plottable: bool,
|
||||
}
|
||||
|
||||
impl LayerLook {
|
||||
fn of(layer: &acadrust::tables::Layer) -> Self {
|
||||
Self {
|
||||
color: layer.color.clone(),
|
||||
line_type: layer.line_type.to_uppercase(),
|
||||
line_weight: layer.line_weight,
|
||||
off: layer.flags.off,
|
||||
frozen: layer.flags.frozen,
|
||||
locked: layer.flags.locked,
|
||||
plottable: layer.is_plottable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The source file's layers by upper-case name, read once per file version.
|
||||
fn source_layers(path: &Path) -> Option<HashMap<String, LayerLook>> {
|
||||
type Cache = std::sync::Mutex<HashMap<PathBuf, (std::time::SystemTime, HashMap<String, LayerLook>)>>;
|
||||
static CACHE: std::sync::OnceLock<Cache> = std::sync::OnceLock::new();
|
||||
let modified = std::fs::metadata(path).and_then(|m| m.modified()).ok()?;
|
||||
let cache = CACHE.get_or_init(Default::default);
|
||||
if let Some((stamp, layers)) = cache.lock().ok()?.get(path) {
|
||||
if *stamp == modified {
|
||||
return Some(layers.clone());
|
||||
}
|
||||
}
|
||||
let doc = super::load_file(path).ok()?;
|
||||
let layers: HashMap<String, LayerLook> = doc
|
||||
.layers
|
||||
.iter()
|
||||
.map(|layer| (layer.name.to_uppercase(), LayerLook::of(layer)))
|
||||
.collect();
|
||||
cache
|
||||
.lock()
|
||||
.ok()?
|
||||
.insert(path.to_path_buf(), (modified, layers.clone()));
|
||||
Some(layers)
|
||||
}
|
||||
|
||||
/// Whether any of reference `name`'s layers was changed in the host — the
|
||||
/// Properties "Layer property overrides" row. Compares the host's dependent
|
||||
/// layers with the source file's own.
|
||||
pub fn layer_overrides(doc: &CadDocument, name: &str, raw_path: &str, base_dir: &Path) -> bool {
|
||||
let Some(source) = resolve_path(raw_path, base_dir).and_then(|p| source_layers(&p)) else {
|
||||
return false;
|
||||
};
|
||||
let prefix = format!("{}|", name.to_uppercase());
|
||||
doc.layers.iter().any(|layer| {
|
||||
let upper = layer.name.to_uppercase();
|
||||
let Some(own) = upper.strip_prefix(&prefix) else {
|
||||
return false;
|
||||
};
|
||||
let Some(original) = source.get(own) else {
|
||||
return false;
|
||||
};
|
||||
let mut here = LayerLook::of(layer);
|
||||
// Dependent linetypes carry the reference prefix.
|
||||
if let Some(stripped) = here.line_type.strip_prefix(&prefix) {
|
||||
here.line_type = stripped.to_string();
|
||||
}
|
||||
here != *original
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to build an absolute path from a raw xref path string.
|
||||
/// Handles absolute paths, relative paths, and Windows-style separators.
|
||||
fn resolve_path(raw: &str, base_dir: &Path) -> Option<PathBuf> {
|
||||
@@ -361,18 +437,44 @@ fn resolve_path(raw: &str, base_dir: &Path) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Make sure `doc` has BLOCK + ENDBLK entities for `block_name`.
|
||||
/// These are required so renderers can find the block content.
|
||||
fn ensure_block_entities(doc: &mut CadDocument, block_name: &str) {
|
||||
let has_block = doc
|
||||
.entities()
|
||||
.any(|e| matches!(e, EntityType::Block(b) if b.name == block_name));
|
||||
if has_block {
|
||||
/// Give a block record its BLOCK / ENDBLK markers, linked through the
|
||||
/// record's marker handles. The DWG writer emits the markers the record
|
||||
/// points at; a record left pointing at a null handle is written with
|
||||
/// handle 0, which makes the whole drawing unreadable elsewhere.
|
||||
pub(crate) fn ensure_block_entities(doc: &mut CadDocument, block_name: &str) {
|
||||
let Some((owner, begin, end, path)) = doc.block_records.get(block_name).map(|br| {
|
||||
(
|
||||
br.handle,
|
||||
br.block_entity_handle,
|
||||
br.block_end_handle,
|
||||
br.xref_path.clone(),
|
||||
)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
if begin.is_null() || !matches!(doc.get_entity(begin), Some(EntityType::Block(_))) {
|
||||
let handle = doc.allocate_handle();
|
||||
let mut block = Block::new(block_name, Vector3::zero());
|
||||
if !path.is_empty() {
|
||||
block = block.with_xref_path(&path);
|
||||
}
|
||||
block.common.handle = handle;
|
||||
block.common.owner_handle = owner;
|
||||
let _ = doc.add_entity(EntityType::Block(block));
|
||||
if let Some(br) = doc.block_records.get_mut(block_name) {
|
||||
br.block_entity_handle = handle;
|
||||
}
|
||||
}
|
||||
if end.is_null() || !matches!(doc.get_entity(end), Some(EntityType::BlockEnd(_))) {
|
||||
let handle = doc.allocate_handle();
|
||||
let mut block_end = BlockEnd::new();
|
||||
block_end.common.handle = handle;
|
||||
block_end.common.owner_handle = owner;
|
||||
let _ = doc.add_entity(EntityType::BlockEnd(block_end));
|
||||
if let Some(br) = doc.block_records.get_mut(block_name) {
|
||||
br.block_end_handle = handle;
|
||||
}
|
||||
}
|
||||
let b = Block::new(block_name, Vector3::zero());
|
||||
let _ = doc.add_entity(EntityType::Block(b));
|
||||
let _ = doc.add_entity(EntityType::BlockEnd(BlockEnd::new()));
|
||||
}
|
||||
|
||||
/// Shared symbol-remap core for XREF load-merge and BIND (SPIKE3 REFACTOR
|
||||
@@ -490,6 +592,7 @@ struct XrefSymbolMaps {
|
||||
layers: HashMap<String, String>,
|
||||
linetypes: HashMap<String, String>,
|
||||
text_styles: HashMap<String, String>,
|
||||
text_style_handles: HashMap<Handle, Handle>,
|
||||
dim_styles: HashMap<String, String>,
|
||||
blocks: HashMap<String, String>,
|
||||
br_handles: HashMap<Handle, Handle>,
|
||||
@@ -512,16 +615,55 @@ fn import_xref_symbols(
|
||||
) -> XrefSymbolMaps {
|
||||
let mut maps = XrefSymbolMaps::default();
|
||||
|
||||
// A layer's plot style and material point at objects of the file it came
|
||||
// from; carried over verbatim they point at whatever the host keeps under
|
||||
// those handles. Take the host's defaults (its layer 0) instead.
|
||||
// ponytail: every imported layer gets the default plot style and material;
|
||||
// map them by name if a reference ever carries non-default ones.
|
||||
let (host_plotstyle, host_material) = doc
|
||||
.layers
|
||||
.get("0")
|
||||
.map(|layer| (layer.plotstyle_handle, layer.material))
|
||||
.unwrap_or((Handle::NULL, Handle::NULL));
|
||||
|
||||
for layer in xref_doc.layers.iter() {
|
||||
let old = layer.name.clone();
|
||||
let new = naming.name_for(SymbolKind::Layer, &old);
|
||||
let mut cloned = layer.clone();
|
||||
cloned.name = new.clone();
|
||||
cloned.plotstyle_handle = host_plotstyle;
|
||||
cloned.material = host_material;
|
||||
cloned.set_handle(doc.allocate_handle());
|
||||
doc.layers.add_or_replace(cloned);
|
||||
maps.layers.insert(old.to_uppercase(), new);
|
||||
}
|
||||
|
||||
for ts in xref_doc.text_styles.iter() {
|
||||
// A shape-file style has no name of its own; the host's style for the
|
||||
// same shape file serves the imported linetypes.
|
||||
if ts.is_shape_file {
|
||||
let same_file = doc
|
||||
.text_styles
|
||||
.iter()
|
||||
.find(|host| host.is_shape_file && host.font_file.eq_ignore_ascii_case(&ts.font_file))
|
||||
.map(|host| host.handle);
|
||||
if let Some(host_handle) = same_file {
|
||||
maps.text_style_handles.insert(ts.handle, host_handle);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let old = ts.name.clone();
|
||||
let new = naming.name_for(SymbolKind::TextStyle, &old);
|
||||
let mut cloned = ts.clone();
|
||||
cloned.name = new.clone();
|
||||
let new_handle = doc.allocate_handle();
|
||||
cloned.set_handle(new_handle);
|
||||
cloned.xref_dependent = false;
|
||||
doc.text_styles.add_or_replace(cloned);
|
||||
maps.text_style_handles.insert(ts.handle, new_handle);
|
||||
maps.text_styles.insert(old.to_uppercase(), new);
|
||||
}
|
||||
|
||||
for lt in xref_doc.line_types.iter() {
|
||||
let old = lt.name.clone();
|
||||
if is_sentinel_linetype(&old) {
|
||||
@@ -530,22 +672,20 @@ fn import_xref_symbols(
|
||||
let new = naming.name_for(SymbolKind::Linetype, &old);
|
||||
let mut cloned = lt.clone();
|
||||
cloned.name = new.clone();
|
||||
// Shape and text segments name their style by handle: point them at
|
||||
// the imported copy, not at the source file's record.
|
||||
for element in &mut cloned.elements {
|
||||
if let Some(complex) = element.complex.as_mut() {
|
||||
if let Some(new_style) = maps.text_style_handles.get(&complex.style_handle) {
|
||||
complex.style_handle = *new_style;
|
||||
}
|
||||
}
|
||||
}
|
||||
cloned.set_handle(doc.allocate_handle());
|
||||
doc.line_types.add_or_replace(cloned);
|
||||
maps.linetypes.insert(old.to_uppercase(), new);
|
||||
}
|
||||
|
||||
for ts in xref_doc.text_styles.iter() {
|
||||
let old = ts.name.clone();
|
||||
let new = naming.name_for(SymbolKind::TextStyle, &old);
|
||||
let mut cloned = ts.clone();
|
||||
cloned.name = new.clone();
|
||||
cloned.set_handle(doc.allocate_handle());
|
||||
cloned.xref_dependent = false;
|
||||
doc.text_styles.add_or_replace(cloned);
|
||||
maps.text_styles.insert(old.to_uppercase(), new);
|
||||
}
|
||||
|
||||
for ds in xref_doc.dim_styles.iter() {
|
||||
let old = ds.name.clone();
|
||||
let new = naming.name_for(SymbolKind::DimStyle, &old);
|
||||
@@ -817,6 +957,21 @@ fn merge_xref_into_block(
|
||||
prefix: prefix.to_string(),
|
||||
};
|
||||
let maps = import_xref_symbols(doc, &xref_doc, &mut naming);
|
||||
// Dependent layers and linetypes carry the xref flag and their owning
|
||||
// reference, the way the file stores them; without it the host writes
|
||||
// plain records with a "|" in the name, which other readers reject.
|
||||
for name in maps.layers.values() {
|
||||
if let Some(layer) = doc.layers.get_mut(name) {
|
||||
layer.flags.xref_dependent = true;
|
||||
layer.xref_block_record_handle = br_handle;
|
||||
}
|
||||
}
|
||||
for name in maps.linetypes.values() {
|
||||
if let Some(linetype) = doc.line_types.get_mut(name) {
|
||||
linetype.xref_dependent = true;
|
||||
linetype.xref_block_record_handle = br_handle;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Entities (shared helper) ────────────────────────────────────────
|
||||
// The unremapped-handle count is ignored on the load path (BIND reports
|
||||
@@ -1730,6 +1885,64 @@ fn same_file(a: &Path, b: &Path) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the named references' paths relative to `host` (their drawing's
|
||||
/// first save); a reference that cannot be relative keeps its full path.
|
||||
pub fn make_relative(
|
||||
doc: &mut CadDocument,
|
||||
names: &rustc_hash::FxHashSet<String>,
|
||||
host: &Path,
|
||||
) {
|
||||
for name in names {
|
||||
let Some(key) = doc.block_records.get(name).map(|br| br.handle.value()) else {
|
||||
continue;
|
||||
};
|
||||
let _ = apply_pathtype(doc, key, crate::io::xref_model::Pathtype::Relative, host);
|
||||
}
|
||||
}
|
||||
|
||||
/// Drop from a save copy what resolving each xref merged into the host but
|
||||
/// the file never stores: the reference's own geometry, its nested block
|
||||
/// definitions and its text and dimension styles. Dependent layers and
|
||||
/// linetypes stay (flagged), so layer overrides kept under VISRETAIN persist.
|
||||
pub fn strip_resolved_xref_content(doc: &mut CadDocument) {
|
||||
let xrefs: Vec<(String, Handle)> = doc
|
||||
.block_records
|
||||
.iter()
|
||||
.filter(|br| br.flags.is_xref || br.flags.is_xref_overlay)
|
||||
.map(|br| (br.name.clone(), br.handle))
|
||||
.collect();
|
||||
for (name, handle) in xrefs {
|
||||
let (begin, end) = doc
|
||||
.block_records
|
||||
.get(&name)
|
||||
.map(|br| (br.block_entity_handle, br.block_end_handle))
|
||||
.unwrap_or((Handle::NULL, Handle::NULL));
|
||||
let owned: Vec<Handle> = doc
|
||||
.entities()
|
||||
.filter(|e| e.common().owner_handle == handle)
|
||||
.map(|e| e.common().handle)
|
||||
.filter(|h| *h != begin && *h != end)
|
||||
.collect();
|
||||
for h in owned {
|
||||
doc.remove_entity(h);
|
||||
}
|
||||
if let Some(br) = doc.block_records.get_mut(&name) {
|
||||
br.entity_handles.clear();
|
||||
}
|
||||
remove_dependent_symbols(doc, &name, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Text styles a kept linetype draws its shapes or text with.
|
||||
fn linetype_styles(doc: &CadDocument) -> HashSet<Handle> {
|
||||
doc.line_types
|
||||
.iter()
|
||||
.flat_map(|linetype| linetype.elements.iter())
|
||||
.filter_map(|element| element.complex.as_ref().map(|complex| complex.style_handle))
|
||||
.filter(|handle| !handle.is_null())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Remove one xref's `name|*` dependent symbols (case-insensitive): layers,
|
||||
/// linetypes, text styles, dim styles, and nested block records — plus every
|
||||
/// entity and draw-order table owned by those dependent blocks.
|
||||
@@ -1738,6 +1951,10 @@ fn same_file(a: &Path, b: &Path) -> bool {
|
||||
/// (which drops the previous merge before re-merging under `$N$` names). The
|
||||
/// xref BlockRecord itself, its INSERTs, and non-prefixed symbols stay.
|
||||
fn remove_pipe_symbols(doc: &mut CadDocument, name: &str) {
|
||||
remove_dependent_symbols(doc, name, true);
|
||||
}
|
||||
|
||||
fn remove_dependent_symbols(doc: &mut CadDocument, name: &str, layers_too: bool) {
|
||||
let prefix = format!("{}|", name.to_uppercase());
|
||||
let is_dep = |n: &str| n.to_uppercase().starts_with(&prefix);
|
||||
// Dependent blocks first — their handles route the owned-entity cleanup.
|
||||
@@ -1774,7 +1991,7 @@ fn remove_pipe_symbols(doc: &mut CadDocument, name: &str) {
|
||||
let layers: Vec<String> = doc
|
||||
.layers
|
||||
.names()
|
||||
.filter(|n| is_dep(n))
|
||||
.filter(|n| layers_too && is_dep(n))
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
for n in layers {
|
||||
@@ -1783,21 +2000,38 @@ fn remove_pipe_symbols(doc: &mut CadDocument, name: &str) {
|
||||
let lts: Vec<String> = doc
|
||||
.line_types
|
||||
.names()
|
||||
.filter(|n| is_dep(n))
|
||||
.filter(|n| layers_too && is_dep(n))
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
for n in lts {
|
||||
doc.line_types.remove(&n);
|
||||
}
|
||||
// Styles a kept dependent linetype still draws with stay, flagged.
|
||||
let used = if layers_too {
|
||||
HashSet::default()
|
||||
} else {
|
||||
linetype_styles(doc)
|
||||
};
|
||||
let styles: Vec<String> = doc
|
||||
.text_styles
|
||||
.names()
|
||||
.filter(|n| is_dep(n))
|
||||
.map(|s| s.to_string())
|
||||
.iter()
|
||||
.filter(|style| is_dep(&style.name) && !used.contains(&style.handle))
|
||||
.map(|style| style.name.clone())
|
||||
.collect();
|
||||
for n in styles {
|
||||
doc.text_styles.remove(&n);
|
||||
}
|
||||
let owner = doc
|
||||
.block_records
|
||||
.get(name)
|
||||
.map(|br| br.handle)
|
||||
.unwrap_or(Handle::NULL);
|
||||
for style in doc.text_styles.iter_mut() {
|
||||
if is_dep(&style.name) {
|
||||
style.xref_dependent = true;
|
||||
style.xref_block_record_handle = owner;
|
||||
}
|
||||
}
|
||||
let dims: Vec<String> = doc
|
||||
.dim_styles
|
||||
.names()
|
||||
|
||||
@@ -247,6 +247,11 @@ pub fn to_pathtype_result(
|
||||
if parts.is_empty() {
|
||||
return Ok(file_name_only(path));
|
||||
}
|
||||
// A path at or below the host folder starts "./" ("./plan.dwg",
|
||||
// "./refs/plan.dwg"), as the reference writes it; one above starts "../".
|
||||
if parts[0] != ".." {
|
||||
parts.insert(0, ".".to_string());
|
||||
}
|
||||
Ok(parts.join("/"))
|
||||
}
|
||||
}
|
||||
@@ -846,7 +851,7 @@ mod tests {
|
||||
let full = to_pathtype("C:/Drawings/refs/plan.dwg", host, Pathtype::Full);
|
||||
assert_eq!(full, "C:/Drawings/refs/plan.dwg");
|
||||
let rel = to_pathtype("C:/Drawings/refs/plan.dwg", host, Pathtype::Relative);
|
||||
assert_eq!(rel, "refs/plan.dwg");
|
||||
assert_eq!(rel, "./refs/plan.dwg");
|
||||
let none = to_pathtype("C:/Drawings/refs/plan.dwg", host, Pathtype::None);
|
||||
assert_eq!(none, "plan.dwg");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user