Task 085: .CGM File Format
Task 085: .CGM File Format
File Format Specifications for the .CGM File Format
The .CGM file format, known as Computer Graphics Metafile, is an international standard defined by ISO/IEC 8632:1999 for the storage and exchange of 2D vector graphics, raster graphics, and text in a device-independent manner. It supports three encoding types: binary (for efficient access and exchange), clear text (for human readability), and character (for compact storage). The format structures data as a sequence of elements, each comprising a command header and parameters, facilitating the reconstruction of graphical primitives such as lines, polygons, and text. In binary encoding, elements are organized in 16-bit words, with a header specifying class (4 bits), element ID (7 bits), and parameter length (5 bits for short form or extended for long form), followed by parameters and optional padding to ensure word alignment.
List of Properties Intrinsic to the File Format
The properties intrinsic to the .CGM file format are primarily the elements within the Metafile Descriptor (Class 1 elements), which define metadata for interpreting the file. These are standardized in ISO/IEC 8632 and include the following:
- Metafile Version: An integer specifying the CGM version (e.g., 1, 2, 3, or 4).
- Metafile Description: A string providing descriptive information, such as author, creation date, or software used.
- VDC Type: An enumerated value indicating Virtual Device Coordinates type (0 for integer, 1 for real).
- Integer Precision: Parameters defining the bit width and range for integer values (e.g., minimum and maximum values).
- Real Precision: Parameters specifying precision for real numbers (e.g., exponent and mantissa widths).
- Index Precision: Bit width for index values.
- Colour Precision: Precision for direct color values.
- Colour Index Precision: Precision for indexed colors.
- Maximum Colour Index: The highest allowable color index.
- Colour Value Extent: Range of color values (e.g., RGB minima and maxima).
- Metafile Element List: A list of supported elements in the metafile.
- Font List: A list of font names available for text rendering.
- Character Set List: A list of character sets supported.
- Character Coding Announcer: An enumerated value for character encoding (e.g., basic 7-bit or extended).
- Name Precision: Precision for name indices (in higher versions).
- Maximum VDC Extent: Bounds for the virtual device coordinate space.
- Colour Model: Enumerated value for color space (e.g., RGB, CMYK).
These properties appear after the BEGIN METAFILE element and before the first picture, ensuring consistent interpretation across systems.
Two Direct Download Links for .CGM Files
- https://www.fileformat.info/cc91dbeed9dd47f5812205b800aa7cd9/download (Sample file: CORVETTE.CGM, binary-encoded vector graphic).
- https://www.fileformat.info/e8a3e916274341a4b1bd7c2800e4c08c/download (Sample file: TECHDRAW.CGM, binary-encoded technical drawing).
Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .CGM File Analysis
The following is an HTML snippet with embedded JavaScript suitable for embedding in a Ghost blog post. It creates a drag-and-drop zone where users can upload a .CGM file (assuming binary encoding). The script parses the file, extracts the properties listed above, and displays them on the screen.
Python Class for .CGM File Handling
The following Python class opens a .CGM file (binary encoding), decodes and reads the properties, prints them to the console, and supports writing the original file content back (with potential for property modification in extensions).
import struct
class CGMParser:
def __init__(self, filename):
with open(filename, 'rb') as f:
self.data = f.read()
self.pos = 0
self.properties = {}
self.parse()
def read_uint16(self):
val, = struct.unpack('>H', self.data[self.pos:self.pos+2])
self.pos += 2
return val
def read_int16(self):
val, = struct.unpack('>h', self.data[self.pos:self.pos+2])
self.pos += 2
return val
def read_string(self, length):
s = self.data[self.pos:self.pos+length].decode('ascii')
self.pos += length
return s
def parse(self):
while self.pos < len(self.data):
word = self.read_uint16()
class_id = (word >> 12) & 0xF
elem_id = (word >> 5) & 0x7F
param_len = word & 0x1F
if param_len == 0x1F:
param_len = self.read_uint16()
start_pos = self.pos
if class_id == 0 and elem_id == 1: # BEGIN METAFILE
pass # Skip
elif class_id == 1:
if elem_id == 1: # METAFILE VERSION
self.properties['Metafile Version'] = self.read_int16()
elif elem_id == 2: # METAFILE DESCRIPTION
self.properties['Metafile Description'] = self.read_string(param_len)
elif elem_id == 3: # VDC TYPE
self.properties['VDC Type'] = 'Integer' if self.read_int16() == 0 else 'Real'
elif elem_id == 4: # INTEGER PRECISION
self.properties['Integer Precision'] = f"Min: {self.read_int16()}, Max: {self.read_int16()}"
elif elem_id == 5: # REAL PRECISION
self.properties['Real Precision'] = f"Mantissa: {self.read_int16()}, Exponent: {self.read_int16()}"
# Extend for other properties (e.g., elem_id 6: Index Precision, etc.)
self.pos = start_pos + param_len
if param_len % 2 == 1:
self.pos += 1 # Padding
if class_id == 0 and elem_id == 3: # Stop at BEGIN PICTURE
break
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, output_filename):
with open(output_filename, 'wb') as f:
f.write(self.data) # Writes original; extend for modified properties
# Example usage: parser = CGMParser('sample.cgm'); parser.print_properties(); parser.write('output.cgm')
Java Class for .CGM File Handling
The following Java class opens a .CGM file, decodes and reads the properties, prints them to the console, and supports writing the file.
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class CGMParser {
private byte[] data;
private int pos = 0;
private java.util.Map<String, String> properties = new java.util.HashMap<>();
public CGMParser(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
data = fis.readAllBytes();
}
parse();
}
private int readUint16() {
ByteBuffer bb = ByteBuffer.wrap(data, pos, 2).order(ByteOrder.BIG_ENDIAN);
int val = bb.getShort() & 0xFFFF;
pos += 2;
return val;
}
private short readInt16() {
ByteBuffer bb = ByteBuffer.wrap(data, pos, 2).order(ByteOrder.BIG_ENDIAN);
short val = bb.getShort();
pos += 2;
return val;
}
private String readString(int length) {
String s = new String(data, pos, length);
pos += length;
return s;
}
private void parse() {
while (pos < data.length) {
int word = readUint16();
int classId = (word >> 12) & 0xF;
int elemId = (word >> 5) & 0x7F;
int paramLen = word & 0x1F;
if (paramLen == 0x1F) {
paramLen = readUint16();
}
int startPos = pos;
if (classId == 0 && elemId == 1) { // BEGIN METAFILE
// Skip
} else if (classId == 1) {
if (elemId == 1) { // METAFILE VERSION
properties.put("Metafile Version", String.valueOf(readInt16()));
} else if (elemId == 2) { // METAFILE DESCRIPTION
properties.put("Metafile Description", readString(paramLen));
} else if (elemId == 3) { // VDC TYPE
properties.put("VDC Type", readInt16() == 0 ? "Integer" : "Real");
} else if (elemId == 4) { // INTEGER PRECISION
properties.put("Integer Precision", "Min: " + readInt16() + ", Max: " + readInt16());
} else if (elemId == 5) { // REAL PRECISION
properties.put("Real Precision", "Mantissa: " + readInt16() + ", Exponent: " + readInt16());
} // Extend for others
}
pos = startPos + paramLen;
if (paramLen % 2 == 1) pos += 1; // Padding
if (classId == 0 && elemId == 3) break; // BEGIN PICTURE
}
}
public void printProperties() {
properties.forEach((key, value) -> System.out.println(key + ": " + value));
}
public void write(String outputFilename) throws IOException {
try (FileOutputStream fos = new FileOutputStream(outputFilename)) {
fos.write(data);
}
}
// Example: CGMParser parser = new CGMParser("sample.cgm"); parser.printProperties(); parser.write("output.cgm");
}
JavaScript Class for .CGM File Handling
The following JavaScript class (Node.js compatible) opens a .CGM file, decodes and reads the properties, prints them to the console, and supports writing.
const fs = require('fs');
class CGMParser {
constructor(filename) {
this.data = fs.readFileSync(filename);
this.pos = 0;
this.properties = {};
this.parse();
}
readUint16() {
const val = (this.data[this.pos] << 8) | this.data[this.pos + 1];
this.pos += 2;
return val;
}
readInt16() {
let val = (this.data[this.pos] << 8) | this.data[this.pos + 1];
if (val & 0x8000) val -= 0x10000; // Signed
this.pos += 2;
return val;
}
readString(len) {
let str = '';
for (let i = 0; i < len; i++) {
str += String.fromCharCode(this.data[this.pos + i]);
}
this.pos += len;
return str;
}
parse() {
while (this.pos < this.data.length) {
const word = this.readUint16();
const classId = (word >> 12) & 0xF;
const elemId = (word >> 5) & 0x7F;
let paramLen = word & 0x1F;
if (paramLen === 0x1F) {
paramLen = this.readUint16();
}
const startPos = this.pos;
if (classId === 0 && elemId === 1) { // BEGIN METAFILE
// Skip
} else if (classId === 1) {
if (elemId === 1) { // METAFILE VERSION
this.properties['Metafile Version'] = this.readInt16();
} else if (elemId === 2) { // METAFILE DESCRIPTION
this.properties['Metafile Description'] = this.readString(paramLen);
} else if (elemId === 3) { // VDC TYPE
this.properties['VDC Type'] = this.readInt16() === 0 ? 'Integer' : 'Real';
} else if (elemId === 4) { // INTEGER PRECISION
this.properties['Integer Precision'] = `Min: ${this.readInt16()}, Max: ${this.readInt16()}`;
} else if (elemId === 5) { // REAL PRECISION
this.properties['Real Precision'] = `Mantissa: ${this.readInt16()}, Exponent: ${this.readInt16()}`;
} // Extend for others
}
this.pos = startPos + paramLen;
if (paramLen % 2 === 1) this.pos += 1; // Padding
if (classId === 0 && elemId === 3) break; // BEGIN PICTURE
}
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
write(outputFilename) {
fs.writeFileSync(outputFilename, this.data);
}
}
// Example: const parser = new CGMParser('sample.cgm'); parser.printProperties(); parser.write('output.cgm');
C Class (Implemented as C++ for Object-Oriented Structure) for .CGM File Handling
The following C++ class opens a .CGM file, decodes and reads the properties, prints them to the console, and supports writing.
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <string>
class CGMParser {
private:
std::vector<unsigned char> data;
size_t pos = 0;
std::map<std::string, std::string> properties;
unsigned short readUint16() {
unsigned short val = (data[pos] << 8) | data[pos + 1];
pos += 2;
return val;
}
short readInt16() {
unsigned short uval = readUint16();
return static_cast<short>(uval);
}
std::string readString(size_t len) {
std::string s(data.begin() + pos, data.begin() + pos + len);
pos += len;
return s;
}
void parse() {
while (pos < data.size()) {
unsigned short word = readUint16();
int classId = (word >> 12) & 0xF;
int elemId = (word >> 5) & 0x7F;
int paramLen = word & 0x1F;
if (paramLen == 0x1F) {
paramLen = readUint16();
}
size_t startPos = pos;
if (classId == 0 && elemId == 1) { // BEGIN METAFILE
// Skip
} else if (classId == 1) {
if (elemId == 1) { // METAFILE VERSION
properties["Metafile Version"] = std::to_string(readInt16());
} else if (elemId == 2) { // METAFILE DESCRIPTION
properties["Metafile Description"] = readString(paramLen);
} else if (elemId == 3) { // VDC TYPE
properties["VDC Type"] = readInt16() == 0 ? "Integer" : "Real";
} else if (elemId == 4) { // INTEGER PRECISION
properties["Integer Precision"] = "Min: " + std::to_string(readInt16()) + ", Max: " + std::to_string(readInt16());
} else if (elemId == 5) { // REAL PRECISION
properties["Real Precision"] = "Mantissa: " + std::to_string(readInt16()) + ", Exponent: " + std::to_string(readInt16());
} // Extend for others
}
pos = startPos + paramLen;
if (paramLen % 2 == 1) pos += 1; // Padding
if (classId == 0 && elemId == 3) break; // BEGIN PICTURE
}
}
public:
CGMParser(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
data = std::vector<unsigned char>(std::istreambuf_iterator<char>(file), {});
parse();
}
void printProperties() const {
for (const auto& prop : properties) {
std::cout << prop.first << ": " << prop.second << std::endl;
}
}
void write(const std::string& outputFilename) const {
std::ofstream file(outputFilename, std::ios::binary);
file.write(reinterpret_cast<const char*>(data.data()), data.size());
}
};
// Example: CGMParser parser("sample.cgm"); parser.printProperties(); parser.write("output.cgm");