feat(ole): render EMF/WMF OLE presentations

OLE2FRAME entities whose compound file carries a metafile presentation
(Excel/Word previews, scanned WMF pages) drew only their border frame —
the old decoder scanned the raw blob for a contiguous BMP, which the
CFB sector layout almost never provides. acadrust now extracts the
picture bytes tagged by kind; the new scene/model/gdi player rasterizes
EMF and WMF record streams on the CPU (pens/brushes, polygons, paths,
DIB blits, clipping, transforms) with text drawn through the existing
system-font glyph cache, and image_model feeds the result into the
raster pipeline. Rasters (BMP/PNG/JPEG/GIF, CF_DIB) decode as before
via the image crate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Fable 5 committed 2026-07-21 01:23:31 +03:00
1 parent 1cfb856224
commit e8411c1b6d
10 files changed
+2088 -42

No files matched your search

+6 -41
View File
@@ -182,12 +182,13 @@ impl ImageModel {
}
impl ImageModel {
/// Build an ImageModel from an OLE2FRAME's embedded presentation bitmap.
/// Returns `None` when the frame is degenerate or carries no decodable
/// bitmap (e.g. a metafile-only OLE), so the caller falls back to the frame
/// placeholder.
/// Build an ImageModel from an OLE2FRAME's embedded presentation.
/// The blob's compound file is parsed for the native raster or the cached
/// metafile picture (rasterized by the `gdi` player); returns `None` when
/// the frame is degenerate or nothing in the blob decodes, so the caller
/// falls back to the frame placeholder.
pub fn from_ole2frame(ole: &acadrust::entities::Ole2Frame) -> Option<Self> {
let (pixels, width, height) = decode_ole_bitmap(&ole.binary_data)?;
let (pixels, width, height) = super::ole_pres::decode(&ole.binary_data)?;
// Frame rectangle in WCS. `upper_left`/`lower_right` name the diagonal;
// normalise to left/right/top/bottom so the bitmap sits upright.
@@ -235,42 +236,6 @@ impl ImageModel {
}
}
/// Extract and decode the presentation BMP embedded in an OLE2FRAME data blob.
/// The blob carries the OLE object's cached bitmap (a `BITMAPFILEHEADER` "BM"
/// followed by a DIB); scan for a self-consistent one and decode it. Returns
/// `None` when no valid BMP is present (a metafile/other-format OLE).
fn decode_ole_bitmap(data: &[u8]) -> Option<(Vec<u8>, u32, u32)> {
let mut i = 0usize;
while i + 54 <= data.len() {
if &data[i..i + 2] != b"BM" {
i += 1;
continue;
}
let file_size = u32::from_le_bytes(data[i + 2..i + 6].try_into().unwrap()) as usize;
let dib_size = u32::from_le_bytes(data[i + 14..i + 18].try_into().unwrap());
// A plausible BITMAPFILEHEADER points at a known DIB-header size.
if !matches!(dib_size, 12 | 40 | 52 | 56 | 64 | 108 | 124) {
i += 1;
continue;
}
let end = if file_size >= 54 && i + file_size <= data.len() {
i + file_size
} else {
data.len()
};
if let Ok(img) = image::load_from_memory_with_format(&data[i..end], image::ImageFormat::Bmp)
{
let rgba = img.to_rgba8();
let (w, h) = rgba.dimensions();
if w > 0 && h > 0 {
return Some((rgba.into_raw(), w, h));
}
}
i += 1;
}
None
}
/// Decoded RGBA image shared between the raster pipeline and the
/// unresolved-reference probe. Cheap to clone — the pixels are `Arc`-shared.
#[derive(Clone)]