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.
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;
// }