// SPDX-License-Identifier: Apache-2.0
// Strict OPC checks used by the documented Core geometry ingestion subset.
///|
fn mf_xml_text(raw : FixedArray[Byte]) -> Result[String, String] {
let bytes = Bytes::from_array(raw)
Ok(@utf8.decode(bytes[:])) catch {
_ => Err("3MF XML 部件必须为有效 UTF-8")
}
}
///|
fn mf_opc_nodes(
text : String,
root_name : String,
ns_uri : String,
) -> Result[Array[@xml.XmlElement], String] {
let reader = @xml.Reader::from_string(text)
let scopes : Array[MfXmlScope] = []
let inherited : Map[String, String] = Map([])
let nodes : Array[@xml.XmlElement] = []
let mut seen = false
while !reader.is_eof() {
let event = reader.read_event() catch {
_ => return Err("3MF OPC XML 结构非法")
}
match event.kind {
@xml.EventKind::Start(e) | @xml.EventKind::Empty(e) => {
let parent = if scopes.is_empty() {
inherited
} else {
scopes[scopes.length() - 1].bindings
}
let bindings = mf_bindings(e, parent)
let uri = match bindings.get(prefix_of(e.name)) {
Some(v) => v
None => ""
}
let name = local_name(e.name)
if scopes.is_empty() {
if seen || name != root_name || uri != ns_uri {
return Err("3MF OPC 根元素或命名空间不正确")
}
seen = true
} else if scopes.length() == 1 && uri == ns_uri {
nodes.push(e)
} else {
return Err("3MF OPC 中存在不支持的嵌套结构")
}
if event.kind is @xml.EventKind::Start(_) {
scopes.push({ name: e.name, tag: name, bindings, })
}
}
@xml.EventKind::End(name) =>
if scopes.is_empty() || scopes.pop().unwrap().name != name {
return Err("3MF OPC XML 标签不匹配")
}
@xml.EventKind::Text(t) | @xml.EventKind::CData(t) =>
if obj_words(t).length() > 0 {
return Err("3MF OPC 中不接受非空文本")
}
@xml.EventKind::DocType(_) => return Err("3MF OPC 不接受 DTD")
@xml.EventKind::Decl(encoding~, ..) =>
match encoding {
Some(e) =>
if e.to_lower() != "utf-8" {
return Err("3MF OPC 必须使用 UTF-8")
}
None => ()
}
_ => ()
}
}
if !seen || !scopes.is_empty() {
return Err("3MF OPC 缺少完整根元素")
}
Ok(nodes)
}
///|
fn mf_package_path(raw : String) -> Result[String, String] {
if raw == "" ||
raw.contains("\\") ||
raw.contains(":") ||
raw.contains("?") ||
raw.contains("#") {
return Err(
"3MF 模型关系 Target 不是当前支持的内部部件路径",
)
}
let name = normalize_part_path(raw)
for segment in name.split("/") {
if segment == "" || segment == "." || segment == ".." {
return Err("3MF 部件路径包含空段或点段")
}
}
Ok(name)
}
///|
fn mf_strict_model_part(
entries : Array[(String, FixedArray[Byte])],
) -> Result[String, String] {
let names : Map[String, Bool] = Map([])
for (name, _) in entries {
if name.contains("\\") {
return Err(
"严格 3MF 包条目必须使用正斜杠;旧导出器需显式 compatibility=true",
)
}
let key = normalize_entry_name(name)
if names.contains(key) {
return Err("3MF 包含重复部件名:\{name}")
}
names.set(key, true)
}
let rels = match find_entry(entries, "_rels/.rels") {
Some(raw) =>
match mf_xml_text(raw) {
Ok(s) => s
Err(e) => return Err(e)
}
None =>
return Err("严格 3MF 缺少 _rels/.rels,不能猜测 .model 文件")
}
let links = match
mf_opc_nodes(
rels, "Relationships", "http://schemas.openxmlformats.org/package/2006/relationships",
) {
Ok(v) => v
Err(e) => return Err(e)
}
let ids : Map[String, Bool] = Map([])
let mut target : String? = None
for e in links {
if local_name(e.name) != "Relationship" {
return Err("3MF 关系表包含未知声明")
}
let id = match e.get("Id") {
Some(v) => v
None => return Err("3MF Relationship 缺少 Id")
}
if id == "" || ids.contains(id) {
return Err("3MF Relationship Id 为空或重复")
}
ids.set(id, true)
let kind = match e.get("Type") {
Some(v) => v
None => return Err("3MF Relationship 缺少 Type")
}
let raw = match e.get("Target") {
Some(v) => v
None => return Err("3MF Relationship 缺少 Target")
}
if kind != "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel" {
continue
}
if target is Some(_) {
return Err("3MF 必须有唯一的 3D model 关系")
}
match e.get("TargetMode") {
Some(mode) =>
if mode != "Internal" {
return Err("3MF 3D model 关系必须为内部 Target")
}
None => ()
}
target = Some(
match mf_package_path(raw) {
Ok(p) => p
Err(e) => return Err(e)
},
)
}
let part = match target {
Some(p) => p
None =>
return Err(
"严格 3MF 缺少唯一 3D model 关系,不回退猜测 .model",
)
}
let types_text = match find_entry(entries, "[Content_Types].xml") {
Some(raw) =>
match mf_xml_text(raw) {
Ok(s) => s
Err(e) => return Err(e)
}
None => return Err("严格 3MF 缺少 [Content_Types].xml")
}
let types = match
mf_opc_nodes(
types_text, "Types", "http://schemas.openxmlformats.org/package/2006/content-types",
) {
Ok(v) => v
Err(e) => return Err(e)
}
let defaults : Map[String, String] = Map([])
let overrides : Map[String, String] = Map([])
for e in types {
let mime = match e.get("ContentType") {
Some(v) => v
None => return Err("3MF 内容类型缺少 ContentType")
}
match local_name(e.name) {
"Default" => {
let ext = match e.get("Extension") {
Some(v) => v.to_lower()
None => return Err("3MF Default 缺少 Extension")
}
if ext == "" || defaults.contains(ext) {
return Err("3MF Default Extension 为空或重复")
}
defaults.set(ext, mime)
}
"Override" => {
let raw = match e.get("PartName") {
Some(v) => v
None => return Err("3MF Override 缺少 PartName")
}
if !raw.has_prefix("/") {
return Err("3MF Override PartName 必须为绝对部件名")
}
let key = match mf_package_path(raw) {
Ok(p) => p.to_lower()
Err(e) => return Err(e)
}
if overrides.contains(key) {
return Err("3MF Override PartName 重复")
}
overrides.set(key, mime)
}
_ => return Err("3MF 内容类型包含未知声明")
}
}
let mut extension = ""
for token in part.split(".") {
extension = token.to_lower().to_owned()
}
let model_mime = match overrides.get(part.to_lower()) {
Some(v) => Some(v)
None => defaults.get(extension)
}
if model_mime !=
Some("application/vnd.ms-package.3dmanufacturing-3dmodel+xml") {
return Err("3MF 模型部件的 ContentType 缺失或错误")
}
let rels_mime = match overrides.get("_rels/.rels") {
Some(v) => Some(v)
None => defaults.get("rels")
}
if rels_mime !=
Some("application/vnd.openxmlformats-package.relationships+xml") {
return Err("3MF 根关系部件的 ContentType 缺失或错误")
}
match find_entry(entries, part) {
Some(raw) => mf_xml_text(raw)
None => Err("3MF 关系表指向的模型部件不存在:\{part}")
}
}