use super::parser::{EmfParser, EmfRecord};
use crate::common::error::{Error, Result};
use crate::images::svg::*;
use rayon::prelude::*;
use zerocopy::FromBytes;
pub struct EmfSvgConverter {
parser: EmfParser,
}
#[derive(Debug, Clone, FromBytes)]
#[repr(C)]
struct EmfRect {
left: i32,
top: i32,
right: i32,
bottom: i32,
}
#[derive(Debug, Clone, FromBytes)]
#[repr(C)]
struct EmfPoint {
x: i32,
y: i32,
}
#[derive(Debug, Clone, FromBytes)]
#[repr(C)]
#[allow(dead_code)]
struct EmfSize {
cx: i32,
cy: i32,
}
#[derive(Debug, Clone, FromBytes)]
#[repr(C)]
struct EmfStretchDibits {
bounds: EmfRect,
x_dest: i32,
y_dest: i32,
x_src: i32,
y_src: i32,
cx_src: i32,
cy_src: i32,
off_bmi_src: u32,
cb_bmi_src: u32,
off_bits_src: u32,
cb_bits_src: u32,
usage_src: u32,
dw_rop: u32,
cx_dest: i32,
cy_dest: i32,
}
#[derive(Debug, Clone, FromBytes)]
#[repr(C)]
struct EmfPolygonHeader {
bounds: EmfRect,
count: u32,
}
impl EmfSvgConverter {
pub fn new(parser: EmfParser) -> Self {
Self { parser }
}
pub fn convert_to_svg(&self) -> Result<String> {
let mut builder = SvgBuilder::new(
self.parser.width() as f64,
self.parser.height() as f64,
);
let (x1, y1, x2, y2) = self.parser.header.bounds;
builder = builder.with_viewbox(
x1 as f64,
y1 as f64,
(x2 - x1) as f64,
(y2 - y1) as f64,
);
let elements: Vec<SvgElement> = self
.parser
.records
.par_iter()
.filter_map(|record| self.process_record(record).ok().flatten())
.collect();
for element in elements {
builder.add_element(element);
}
Ok(builder.build())
}
pub fn convert_to_svg_bytes(&self) -> Result<Vec<u8>> {
Ok(self.convert_to_svg()?.into_bytes())
}
fn process_record(&self, record: &EmfRecord) -> Result<Option<SvgElement>> {
match record.record_type {
0x0000002B => self.parse_rectangle(record),
0x0000002A => self.parse_ellipse(record),
0x00000003 => self.parse_polygon(record),
0x00000004 => self.parse_polyline(record),
0x00000002 => self.parse_polybezier(record),
0x00000036 => self.parse_lineto(record),
0x00000051 => self.parse_stretchdibits(record),
0x0000002D => self.parse_arc(record),
0x0000002F => self.parse_pie(record),
0x0000002E => self.parse_chord(record),
_ => Ok(None), }
}
fn parse_rectangle(&self, record: &EmfRecord) -> Result<Option<SvgElement>> {
let rect = EmfRect::read_from_bytes(&record.data)
.map_err(|_| Error::ParseError("Invalid RECT data in EMR_RECTANGLE".into()))?;
Ok(Some(SvgElement::Rect(SvgRect {
x: rect.left as f64,
y: rect.top as f64,
width: (rect.right - rect.left) as f64,
height: (rect.bottom - rect.top) as f64,
fill: None,
stroke: Some("#000000".to_string()),
stroke_width: 1.0,
})))
}
fn parse_ellipse(&self, record: &EmfRecord) -> Result<Option<SvgElement>> {
let rect = EmfRect::read_from_bytes(&record.data)
.map_err(|_| Error::ParseError("Invalid RECT data in EMR_ELLIPSE".into()))?;
let left = rect.left as f64;
let top = rect.top as f64;
let right = rect.right as f64;
let bottom = rect.bottom as f64;
let cx = (left + right) / 2.0;
let cy = (top + bottom) / 2.0;
let rx = (right - left) / 2.0;
let ry = (bottom - top) / 2.0;
Ok(Some(SvgElement::Ellipse(SvgEllipse {
cx,
cy,
rx,
ry,
fill: None,
stroke: Some("#000000".to_string()),
stroke_width: 1.0,
})))
}
fn parse_polygon(&self, record: &EmfRecord) -> Result<Option<SvgElement>> {
if record.data.len() < 20 {
return Ok(None);
}
let header = EmfPolygonHeader::read_from_bytes(&record.data)
.map_err(|_| Error::ParseError("Invalid header data in EMR_POLYGON".into()))?;
let count = header.count as usize;
if record.data.len() < 20 + count * 8 {
return Ok(None);
}
let mut commands = Vec::with_capacity(count + 1);
let points_data = &record.data[20..20 + count * 8];
for i in 0..count {
let point_data = &points_data[i * 8..(i + 1) * 8];
let point = EmfPoint::read_from_bytes(point_data)
.map_err(|_| Error::ParseError("Invalid point data in EMR_POLYGON".into()))?;
let x = point.x as f64;
let y = point.y as f64;
if i == 0 {
commands.push(PathCommand::MoveTo { x, y });
} else {
commands.push(PathCommand::LineTo { x, y });
}
}
commands.push(PathCommand::ClosePath);
Ok(Some(SvgElement::Path(
SvgPath::new(commands)
.with_stroke("#000000".to_string())
.with_fill("none".to_string()),
)))
}
fn parse_polyline(&self, record: &EmfRecord) -> Result<Option<SvgElement>> {
if record.data.len() < 20 {
return Ok(None);
}
let header = EmfPolygonHeader::read_from_bytes(&record.data)
.map_err(|_| Error::ParseError("Invalid header data in EMR_POLYLINE".into()))?;
let count = header.count as usize;
if record.data.len() < 20 + count * 8 {
return Ok(None);
}
let mut commands = Vec::with_capacity(count);
let points_data = &record.data[20..20 + count * 8];
for i in 0..count {
let point_data = &points_data[i * 8..(i + 1) * 8];
let point = EmfPoint::read_from_bytes(point_data)
.map_err(|_| Error::ParseError("Invalid point data in EMR_POLYLINE".into()))?;
let x = point.x as f64;
let y = point.y as f64;
if i == 0 {
commands.push(PathCommand::MoveTo { x, y });
} else {
commands.push(PathCommand::LineTo { x, y });
}
}
Ok(Some(SvgElement::Path(
SvgPath::new(commands)
.with_stroke("#000000".to_string())
.with_fill("none".to_string()),
)))
}
fn parse_polybezier(&self, record: &EmfRecord) -> Result<Option<SvgElement>> {
if record.data.len() < 20 {
return Ok(None);
}
let header = EmfPolygonHeader::read_from_bytes(&record.data)
.map_err(|_| Error::ParseError("Invalid header data in EMR_POLYBEZIER".into()))?;
let count = header.count as usize;
if record.data.len() < 20 + count * 8 || count < 4 {
return Ok(None);
}
let mut commands = Vec::new();
let points_data = &record.data[20..20 + count * 8];
let mut points = Vec::with_capacity(count);
for i in 0..count {
let point_data = &points_data[i * 8..(i + 1) * 8];
let point = EmfPoint::read_from_bytes(point_data)
.map_err(|_| Error::ParseError("Invalid point data in EMR_POLYBEZIER".into()))?;
points.push(point);
}
commands.push(PathCommand::MoveTo {
x: points[0].x as f64,
y: points[0].y as f64,
});
let mut i = 1;
while i + 2 < count {
commands.push(PathCommand::CubicBezier {
x1: points[i].x as f64,
y1: points[i].y as f64,
x2: points[i + 1].x as f64,
y2: points[i + 1].y as f64,
x: points[i + 2].x as f64,
y: points[i + 2].y as f64,
});
i += 3;
}
Ok(Some(SvgElement::Path(
SvgPath::new(commands)
.with_stroke("#000000".to_string())
.with_fill("none".to_string()),
)))
}
fn parse_lineto(&self, record: &EmfRecord) -> Result<Option<SvgElement>> {
if record.data.len() < 8 {
return Ok(None);
}
let point = EmfPoint::read_from_bytes(&record.data)
.map_err(|_| Error::ParseError("Invalid point data in EMR_LINETO".into()))?;
let x = point.x as f64;
let y = point.y as f64;
Ok(Some(SvgElement::Path(
SvgPath::new(vec![
PathCommand::MoveTo { x: 0.0, y: 0.0 },
PathCommand::LineTo { x, y },
])
.with_stroke("#000000".to_string()),
)))
}
fn parse_stretchdibits(&self, record: &EmfRecord) -> Result<Option<SvgElement>> {
if record.data.len() < std::mem::size_of::<EmfStretchDibits>() {
return Ok(None);
}
let stretch = EmfStretchDibits::read_from_bytes(&record.data)
.map_err(|_| Error::ParseError("Invalid EMR_STRETCHDIBITS data".into()))?;
if let Ok(png_data) = self.extract_and_convert_dib(&record.data[std::mem::size_of::<EmfStretchDibits>()..]) {
return Ok(Some(SvgElement::Image(SvgImage::from_png_data(
stretch.x_dest as f64,
stretch.y_dest as f64,
stretch.cx_dest as f64,
stretch.cy_dest as f64,
&png_data,
))));
}
Ok(None)
}
fn parse_arc(&self, _record: &EmfRecord) -> Result<Option<SvgElement>> {
Ok(None)
}
fn parse_pie(&self, _record: &EmfRecord) -> Result<Option<SvgElement>> {
Ok(None)
}
fn parse_chord(&self, _record: &EmfRecord) -> Result<Option<SvgElement>> {
Ok(None)
}
fn extract_and_convert_dib(&self, dib_data: &[u8]) -> Result<Vec<u8>> {
if dib_data.len() < 40 {
return Err(Error::ParseError("DIB data too small".into()));
}
let file_size = 14u32 + dib_data.len() as u32;
let pixel_data_offset = 14u32 + 40u32;
let mut bmp_data = Vec::with_capacity(file_size as usize);
bmp_data.extend_from_slice(b"BM");
bmp_data.extend_from_slice(&file_size.to_le_bytes());
bmp_data.extend_from_slice(&[0u8; 4]);
bmp_data.extend_from_slice(&pixel_data_offset.to_le_bytes());
bmp_data.extend_from_slice(dib_data);
let img = image::load_from_memory(&bmp_data)
.map_err(|e| Error::ParseError(format!("Failed to load DIB: {}", e)))?;
let mut png_data = Vec::new();
let mut cursor = std::io::Cursor::new(&mut png_data);
img.write_to(&mut cursor, image::ImageFormat::Png)
.map_err(|e| Error::ParseError(format!("Failed to encode PNG: {}", e)))?;
Ok(png_data)
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_emf_svg_converter_creation() {
}
}