Task 816: .WMF File Format

Task 816: .WMF File Format

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .WMF Property Dump

The following updated HTML snippet with embedded JavaScript now parses and displays both the standard metafile header properties and the optional placeable header properties if present.

Drag and drop a .WMF file here

4. Python Class for .WMF File Handling

The updated Python class now extracts and prints the optional placeable header properties if present. The write method includes an optional parameter to include a placeable header with default values.

import struct
import os

class WMFHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}

    def read_and_decode(self):
        with open(self.filepath, 'rb') as f:
            data = f.read()
        offset = 0
        # Check and parse optional placeable header
        if len(data) >= 22 and struct.unpack('<I', data[offset:offset+4])[0] == 0x9AC6CDD7:
            self.properties['Key'] = struct.unpack('<I', data[offset:offset+4])[0]
            self.properties['Handle'] = struct.unpack('<H', data[offset+4:offset+6])[0]
            self.properties['Left'] = struct.unpack('<h', data[offset+6:offset+8])[0]
            self.properties['Top'] = struct.unpack('<h', data[offset+8:offset+10])[0]
            self.properties['Right'] = struct.unpack('<h', data[offset+10:offset+12])[0]
            self.properties['Bottom'] = struct.unpack('<h', data[offset+12:offset+14])[0]
            self.properties['Inch'] = struct.unpack('<H', data[offset+14:offset+16])[0]
            self.properties['Reserved'] = struct.unpack('<I', data[offset+16:offset+20])[0]
            self.properties['Checksum'] = struct.unpack('<H', data[offset+20:offset+22])[0]
            offset += 22
        # Standard header
        self.properties['File Type'] = struct.unpack('<H', data[offset:offset+2])[0]
        self.properties['Header Size'] = struct.unpack('<H', data[offset+2:offset+4])[0]
        self.properties['Version'] = struct.unpack('<H', data[offset+4:offset+6])[0]
        self.properties['File Size'] = struct.unpack('<I', data[offset+6:offset+10])[0]
        self.properties['Number of Objects'] = struct.unpack('<H', data[offset+10:offset+12])[0]
        self.properties['Maximum Record Size'] = struct.unpack('<I', data[offset+12:offset+16])[0]
        self.properties['Number of Parameters'] = struct.unpack('<H', data[offset+16:offset+18])[0]

    def print_properties(self):
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write_header(self, output_path, include_placeable=False):
        header = b''
        if include_placeable:
            # Default placeable header values
            header += struct.pack('<IHHhhhhIH', 0x9AC6CDD7, 0, 0, 0, 100, 100, 1440, 0, 0)  # Checksum 0 for simplicity
        # Standard header (minimal values)
        header += struct.pack('<HHIHIIH', 1, 9, 0x0300, 9, 0, 0, 0)
        with open(output_path, 'wb') as f:
            f.write(header)

# Example usage:
# handler = WMFHandler('example.wmf')
# handler.read_and_decode()
# handler.print_properties()
# handler.write_header('new.wmf', include_placeable=True)

5. Java Class for .WMF File Handling

The updated Java class now extracts and prints the optional placeable header properties if present. The write method includes an optional boolean to include a placeable header with default values.

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class WMFHandler {
    private String filepath;
    private int key, handle, left, top, right, bottom, inch, reserved, checksum;
    private int fileType, headerSize, version, fileSize, numObjects, maxRecord, numParams;
    private boolean hasPlaceable = false;

    public WMFHandler(String filepath) {
        this.filepath = filepath;
    }

    public void readAndDecode() throws IOException {
        File file = new File(filepath);
        byte[] data = new byte[(int) file.length()];
        try (FileInputStream fis = new FileInputStream(file)) {
            fis.read(data);
        }
        ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
        int offset = 0;
        // Check and parse optional placeable header
        if (data.length >= 22 && buffer.getInt(offset) == 0x9AC6CDD7) {
            hasPlaceable = true;
            key = buffer.getInt(offset);
            handle = buffer.getShort(offset + 4) & 0xFFFF;
            left = buffer.getShort(offset + 6);
            top = buffer.getShort(offset + 8);
            right = buffer.getShort(offset + 10);
            bottom = buffer.getShort(offset + 12);
            inch = buffer.getShort(offset + 14) & 0xFFFF;
            reserved = buffer.getInt(offset + 16);
            checksum = buffer.getShort(offset + 20) & 0xFFFF;
            offset += 22;
        }
        // Standard header
        fileType = buffer.getShort(offset) & 0xFFFF;
        headerSize = buffer.getShort(offset + 2) & 0xFFFF;
        version = buffer.getShort(offset + 4) & 0xFFFF;
        fileSize = buffer.getInt(offset + 6);
        numObjects = buffer.getShort(offset + 10) & 0xFFFF;
        maxRecord = buffer.getInt(offset + 12);
        numParams = buffer.getShort(offset + 16) & 0xFFFF;
    }

    public void printProperties() {
        if (hasPlaceable) {
            System.out.println("Key: " + key);
            System.out.println("Handle: " + handle);
            System.out.println("Left: " + left);
            System.out.println("Top: " + top);
            System.out.println("Right: " + right);
            System.out.println("Bottom: " + bottom);
            System.out.println("Inch: " + inch);
            System.out.println("Reserved: " + reserved);
            System.out.println("Checksum: " + checksum);
        }
        System.out.println("File Type: " + fileType);
        System.out.println("Header Size: " + headerSize);
        System.out.println("Version: " + version);
        System.out.println("File Size: " + fileSize);
        System.out.println("Number of Objects: " + numObjects);
        System.out.println("Maximum Record Size: " + maxRecord);
        System.out.println("Number of Parameters: " + numParams);
    }

    public void writeHeader(String outputPath, boolean includePlaceable) throws IOException {
        int totalSize = includePlaceable ? 40 : 18;
        ByteBuffer buffer = ByteBuffer.allocate(totalSize).order(ByteOrder.LITTLE_ENDIAN);
        int pos = 0;
        if (includePlaceable) {
            buffer.putInt(0x9AC6CDD7);  // Key
            buffer.putShort((short) 0);  // Handle
            buffer.putShort((short) 0);  // Left
            buffer.putShort((short) 0);  // Top
            buffer.putShort((short) 100);  // Right
            buffer.putShort((short) 100);  // Bottom
            buffer.putShort((short) 1440);  // Inch
            buffer.putInt(0);  // Reserved
            buffer.putShort((short) 0);  // Checksum (simplified)
            pos += 22;
        }
        buffer.position(pos);
        buffer.putShort((short) 1);  // File Type
        buffer.putShort((short) 9);  // Header Size
        buffer.putShort((short) 0x0300);  // Version
        buffer.putInt(9);  // File Size
        buffer.putShort((short) 0);  // Num Objects
        buffer.putInt(0);  // Max Record
        buffer.putShort((short) 0);  // Num Params
        try (FileOutputStream fos = new FileOutputStream(outputPath)) {
            fos.write(buffer.array());
        }
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     WMFHandler handler = new WMFHandler("example.wmf");
    //     handler.readAndDecode();
    //     handler.printProperties();
    //     handler.writeHeader("new.wmf", true);
    // }
}

6. JavaScript Class for .WMF File Handling

The updated JavaScript class (Node.js compatible) now extracts and prints the optional placeable header properties if present. The write method includes an optional parameter to include a placeable header with default values.

const fs = require('fs');

class WMFHandler {
  constructor(filepath) {
    this.filepath = filepath;
    this.properties = {};
  }

  readAndDecode() {
    const data = fs.readFileSync(this.filepath);
    let offset = 0;
    // Check and parse optional placeable header
    if (data.length >= 22 && data.readUInt32LE(offset) === 0x9AC6CDD7) {
      this.properties['Key'] = data.readUInt32LE(offset);
      this.properties['Handle'] = data.readUInt16LE(offset + 4);
      this.properties['Left'] = data.readInt16LE(offset + 6);
      this.properties['Top'] = data.readInt16LE(offset + 8);
      this.properties['Right'] = data.readInt16LE(offset + 10);
      this.properties['Bottom'] = data.readInt16LE(offset + 12);
      this.properties['Inch'] = data.readUInt16LE(offset + 14);
      this.properties['Reserved'] = data.readUInt32LE(offset + 16);
      this.properties['Checksum'] = data.readUInt16LE(offset + 20);
      offset += 22;
    }
    // Standard header
    this.properties['File Type'] = data.readUInt16LE(offset);
    this.properties['Header Size'] = data.readUInt16LE(offset + 2);
    this.properties['Version'] = data.readUInt16LE(offset + 4);
    this.properties['File Size'] = data.readUInt32LE(offset + 6);
    this.properties['Number of Objects'] = data.readUInt16LE(offset + 10);
    this.properties['Maximum Record Size'] = data.readUInt32LE(offset + 12);
    this.properties['Number of Parameters'] = data.readUInt16LE(offset + 16);
  }

  printProperties() {
    for (const [key, value] of Object.entries(this.properties)) {
      console.log(`${key}: ${value}`);
    }
  }

  writeHeader(outputPath, includePlaceable = false) {
    const bufferSize = includePlaceable ? 40 : 18;
    const buffer = Buffer.alloc(bufferSize);
    let offset = 0;
    if (includePlaceable) {
      buffer.writeUInt32LE(0x9AC6CDD7, offset);  // Key
      buffer.writeUInt16LE(0, offset + 4);  // Handle
      buffer.writeInt16LE(0, offset + 6);  // Left
      buffer.writeInt16LE(0, offset + 8);  // Top
      buffer.writeInt16LE(100, offset + 10);  // Right
      buffer.writeInt16LE(100, offset + 12);  // Bottom
      buffer.writeUInt16LE(1440, offset + 14);  // Inch
      buffer.writeUInt32LE(0, offset + 16);  // Reserved
      buffer.writeUInt16LE(0, offset + 20);  // Checksum (simplified)
      offset += 22;
    }
    buffer.writeUInt16LE(1, offset);  // File Type
    buffer.writeUInt16LE(9, offset + 2);  // Header Size
    buffer.writeUInt16LE(0x0300, offset + 4);  // Version
    buffer.writeUInt32LE(9, offset + 6);  // File Size
    buffer.writeUInt16LE(0, offset + 10);  // Num Objects
    buffer.writeUInt32LE(0, offset + 12);  // Max Record
    buffer.writeUInt16LE(0, offset + 16);  // Num Params
    fs.writeFileSync(outputPath, buffer);
  }
}

// Example usage:
// const handler = new WMFHandler('example.wmf');
// handler.readAndDecode();
// handler.printProperties();
// handler.writeHeader('new.wmf', true);

7. C Class for .WMF File Handling

The updated C++ class now extracts and prints the optional placeable header properties if present. The write method includes an optional boolean to include a placeable header with default values.

#include <iostream>
#include <fstream>
#include <cstdint>
#include <vector>

class WMFHandler {
private:
    std::string filepath;
    uint32_t key, reserved;
    uint16_t handle, inch, checksum;
    int16_t left, top, right, bottom;
    uint16_t fileType, headerSize, version, numObjects, numParams;
    uint32_t fileSize, maxRecord;
    bool hasPlaceable = false;

public:
    WMFHandler(const std::string& filepath) : filepath(filepath) {}

    void readAndDecode() {
        std::ifstream file(filepath, std::ios::binary);
        if (!file) {
            std::cerr << "Error opening file." << std::endl;
            return;
        }
        std::vector<char> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        size_t offset = 0;
        // Check and parse optional placeable header
        if (data.size() >= 22) {
            key = *reinterpret_cast<uint32_t*>(&data[offset]);
            if (key == 0x9AC6CDD7) {
                hasPlaceable = true;
                handle = *reinterpret_cast<uint16_t*>(&data[offset + 4]);
                left = *reinterpret_cast<int16_t*>(&data[offset + 6]);
                top = *reinterpret_cast<int16_t*>(&data[offset + 8]);
                right = *reinterpret_cast<int16_t*>(&data[offset + 10]);
                bottom = *reinterpret_cast<int16_t*>(&data[offset + 12]);
                inch = *reinterpret_cast<uint16_t*>(&data[offset + 14]);
                reserved = *reinterpret_cast<uint32_t*>(&data[offset + 16]);
                checksum = *reinterpret_cast<uint16_t*>(&data[offset + 20]);
                offset += 22;
            }
        }
        // Standard header
        fileType = *reinterpret_cast<uint16_t*>(&data[offset]);
        headerSize = *reinterpret_cast<uint16_t*>(&data[offset + 2]);
        version = *reinterpret_cast<uint16_t*>(&data[offset + 4]);
        fileSize = *reinterpret_cast<uint32_t*>(&data[offset + 6]);
        numObjects = *reinterpret_cast<uint16_t*>(&data[offset + 10]);
        maxRecord = *reinterpret_cast<uint32_t*>(&data[offset + 12]);
        numParams = *reinterpret_cast<uint16_t*>(&data[offset + 16]);
    }

    void printProperties() {
        if (hasPlaceable) {
            std::cout << "Key: " << key << std::endl;
            std::cout << "Handle: " << handle << std::endl;
            std::cout << "Left: " << left << std::endl;
            std::cout << "Top: " << top << std::endl;
            std::cout << "Right: " << right << std::endl;
            std::cout << "Bottom: " << bottom << std::endl;
            std::cout << "Inch: " << inch << std::endl;
            std::cout << "Reserved: " << reserved << std::endl;
            std::cout << "Checksum: " << checksum << std::endl;
        }
        std::cout << "File Type: " << fileType << std::endl;
        std::cout << "Header Size: " << headerSize << std::endl;
        std::cout << "Version: " << version << std::endl;
        std::cout << "File Size: " << fileSize << std::endl;
        std::cout << "Number of Objects: " << numObjects << std::endl;
        std::cout << "Maximum Record Size: " << maxRecord << std::endl;
        std::cout << "Number of Parameters: " << numParams << std::endl;
    }

    void writeHeader(const std::string& outputPath, bool includePlaceable = false) {
        std::ofstream file(outputPath, std::ios::binary);
        if (!file) {
            std::cerr << "Error creating file." << std::endl;
            return;
        }
        if (includePlaceable) {
            uint32_t temp32 = 0x9AC6CDD7; file.write(reinterpret_cast<char*>(&temp32), 4);  // Key
            uint16_t temp16 = 0; file.write(reinterpret_cast<char*>(&temp16), 2);  // Handle
            int16_t tempS16 = 0; file.write(reinterpret_cast<char*>(&tempS16), 2);  // Left
            tempS16 = 0; file.write(reinterpret_cast<char*>(&tempS16), 2);  // Top
            tempS16 = 100; file.write(reinterpret_cast<char*>(&tempS16), 2);  // Right
            tempS16 = 100; file.write(reinterpret_cast<char*>(&tempS16), 2);  // Bottom
            temp16 = 1440; file.write(reinterpret_cast<char*>(&temp16), 2);  // Inch
            temp32 = 0; file.write(reinterpret_cast<char*>(&temp32), 4);  // Reserved
            temp16 = 0; file.write(reinterpret_cast<char*>(&temp16), 2);  // Checksum
        }
        uint16_t temp16;
        uint32_t temp32;
        temp16 = 1; file.write(reinterpret_cast<char*>(&temp16), 2);  // File Type
        temp16 = 9; file.write(reinterpret_cast<char*>(&temp16), 2);  // Header Size
        temp16 = 0x0300; file.write(reinterpret_cast<char*>(&temp16), 2);  // Version
        temp32 = 9; file.write(reinterpret_cast<char*>(&temp32), 4);  // File Size
        temp16 = 0; file.write(reinterpret_cast<char*>(&temp16), 2);  // Num Objects
        temp32 = 0; file.write(reinterpret_cast<char*>(&temp32), 4);  // Max Record
        temp16 = 0; file.write(reinterpret_cast<char*>(&temp16), 2);  // Num Params
    }
};

// Example usage:
// int main() {
//     WMFHandler handler("example.wmf");
//     handler.readAndDecode();
//     handler.printProperties();
//     handler.writeHeader("new.wmf", true);
//     return 0;
// }