Task 184: .EPS File Format
Task 184: .EPS File Format
File Format Specifications for .EPS
The .EPS (Encapsulated PostScript) file format is a subset of the PostScript language, designed for embedding graphics in other documents. It conforms to Adobe's Document Structuring Conventions (DSC) version 3.0. EPS files are typically ASCII-based but can include binary previews for compatibility with systems like Windows or Macintosh. The format ensures portability for printing and graphics, restricting certain PostScript operators to prevent device-specific behavior. Key specifications are drawn from Adobe's official documentation. EPS files must be self-contained, with a maximum of one page, and include a bounding box for proper scaling and clipping.
List of all the properties of this file format intrinsic to its file system:
- Binary Header Presence: Boolean indicating if the optional 30-byte DOS/Windows binary header exists (starts with magic bytes \xC5\xD0\xD3\xC6).
- Binary Header Magic: 4 bytes (\xC5\xD0\xD3\xC6 if present).
- PostScript Section Offset: 4-byte unsigned integer (bytes 4-7 if binary header present).
- PostScript Section Length: 4-byte unsigned integer (bytes 8-11 if binary header present).
- Metafile Preview Offset: 4-byte unsigned integer (bytes 12-15 if binary header present; 0 if absent).
- Metafile Preview Length: 4-byte unsigned integer (bytes 16-19 if binary header present; 0 if absent).
- TIFF Preview Offset: 4-byte unsigned integer (bytes 20-23 if binary header present; 0 if absent).
- TIFF Preview Length: 4-byte unsigned integer (bytes 24-27 if binary header present; 0 if absent).
- Header Checksum: 2-byte unsigned integer (bytes 28-29 if binary header present; \xFF\xFF to ignore).
- Version Header: String starting with "%!PS-Adobe-3.0 EPSF-3.0" (required).
- Title: String from "%%Title:" comment (recommended).
- Creator: String from "%%Creator:" comment (recommended).
- Creation Date: String from "%%CreationDate:" comment (recommended).
- Bounding Box: Four values (llx, lly, urx, ury) from "%%BoundingBox:" comment (required; integers or "(atend)").
- Language Level: Integer from "%%LanguageLevel:" comment (conditionally required if >1).
- Extensions: List of strings from "%%Extensions:" comment (conditionally required if extensions used).
- Document Needed Resources: List from "%%DocumentNeededResources:" and related comments (conditionally required if resources like fonts are needed).
- Preview Type: "None", "EPSI" (if "%%BeginPreview" present), "TIFF", or "Metafile" (based on binary header).
- File Portability Flags: Indicators for 7-bit ASCII compliance, line length <=255 chars, and restricted operators (intrinsic constraints).
Two direct download links for files of format .EPS:
- https://example-files.online-convert.com/vector image/eps/example.eps
- https://example-files.online-convert.com/vector image/eps/example_small.eps
Ghost blog embedded HTML JavaScript for drag and drop .EPS file to dump properties:
- Python class for .EPS handling:
import struct
import os
class EPSHandler:
def __init__(self, filepath):
self.filepath = filepath
self.properties = {}
self.content = None
self.has_binary_header = False
self.binary_header = {}
self.read_and_decode()
def read_and_decode(self):
with open(self.filepath, 'rb') as f:
self.content = f.read()
# Check for binary header
if len(self.content) >= 30 and self.content[0:4] == b'\xC5\xD0\xD3\xC6':
self.has_binary_header = True
(magic, ps_offset, ps_length, mf_offset, mf_length, tiff_offset, tiff_length, checksum) = struct.unpack('<I I I I I I I H', self.content[0:30])
self.binary_header = {
'magic': hex(magic),
'ps_offset': ps_offset,
'ps_length': ps_length,
'mf_offset': mf_offset,
'mf_length': mf_length,
'tiff_offset': tiff_offset,
'tiff_length': tiff_length,
'checksum': hex(checksum)
}
ascii_start = ps_offset
ascii_content = self.content[ascii_start:ascii_start + ps_length].decode('utf-8', errors='ignore')
else:
ascii_content = self.content.decode('utf-8', errors='ignore')
# Parse DSC
lines = ascii_content.splitlines()
self.properties = {
'has_binary_header': 'Yes' if self.has_binary_header else 'No',
**self.binary_header,
'version': '',
'title': '',
'creator': '',
'creation_date': '',
'bounding_box': '',
'language_level': '',
'extensions': '',
'document_needed_resources': '',
'preview_type': 'None'
}
for line in lines:
if line.startswith('%!PS-Adobe-3.0 EPSF-3.0'):
self.properties['version'] = line
elif line.startswith('%%Title:'):
self.properties['title'] = line[8:].strip()
elif line.startswith('%%Creator:'):
self.properties['creator'] = line[10:].strip()
elif line.startswith('%%CreationDate:'):
self.properties['creation_date'] = line[15:].strip()
elif line.startswith('%%BoundingBox:'):
self.properties['bounding_box'] = line[14:].strip()
elif line.startswith('%%LanguageLevel:'):
self.properties['language_level'] = line[16:].strip()
elif line.startswith('%%Extensions:'):
self.properties['extensions'] = line[13:].strip()
elif line.startswith('%%DocumentNeededResources:'):
self.properties['document_needed_resources'] = line[26:].strip()
elif line.startswith('%%BeginPreview'):
self.properties['preview_type'] = 'EPSI'
if self.has_binary_header:
if self.binary_header['mf_length'] > 0:
self.properties['preview_type'] = 'Metafile'
elif self.binary_header['tiff_length'] > 0:
self.properties['preview_type'] = 'TIFF'
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, new_filepath, modify_props=None):
if modify_props:
# Simple modify: replace in ASCII content (not handling binary mods fully)
ascii_content = self.content[30:] if self.has_binary_header else self.content
lines = ascii_content.decode('utf-8', errors='ignore').splitlines()
for i, line in enumerate(lines):
for key, value in modify_props.items():
if key == 'title' and line.startswith('%%Title:'):
lines[i] = f'%%Title: {value}'
# Add similar for other props...
new_ascii = '\n'.join(lines).encode('utf-8')
new_content = self.content[:30] + new_ascii if self.has_binary_header else new_ascii
else:
new_content = self.content
with open(new_filepath, 'wb') as f:
f.write(new_content)
# Example usage:
# handler = EPSHandler('example.eps')
# handler.print_properties()
# handler.write('modified.eps', {'title': 'New Title'})
- Java class for .EPS handling:
import java.io.*;
import java.nio.*;
import java.util.*;
public class EPSHandler {
private String filepath;
private Map<String, String> properties = new HashMap<>();
private byte[] content;
private boolean hasBinaryHeader = false;
private Map<String, Object> binaryHeader = new HashMap<>();
public EPSHandler(String filepath) {
this.filepath = filepath;
readAndDecode();
}
private void readAndDecode() {
try (FileInputStream fis = new FileInputStream(filepath)) {
content = fis.readAllBytes();
} catch (IOException e) {
e.printStackTrace();
return;
}
ByteBuffer bb = ByteBuffer.wrap(content).order(ByteOrder.LITTLE_ENDIAN);
// Check binary header
if (content.length >= 30 && bb.getInt(0) == 0xC6D3D0C5) { // Adjusted for little-endian
hasBinaryHeader = true;
binaryHeader.put("magic", "0xC5D0D3C6");
binaryHeader.put("ps_offset", bb.getInt(4));
binaryHeader.put("ps_length", bb.getInt(8));
binaryHeader.put("mf_offset", bb.getInt(12));
binaryHeader.put("mf_length", bb.getInt(16));
binaryHeader.put("tiff_offset", bb.getInt(20));
binaryHeader.put("tiff_length", bb.getInt(24));
binaryHeader.put("checksum", Integer.toHexString(bb.getShort(28) & 0xFFFF).toUpperCase());
int asciiStart = (int) binaryHeader.get("ps_offset");
String asciiContent = new String(content, asciiStart, (int) binaryHeader.get("ps_length"));
parseDSC(asciiContent);
} else {
parseDSC(new String(content));
}
}
private void parseDSC(String asciiContent) {
properties.put("has_binary_header", hasBinaryHeader ? "Yes" : "No");
properties.putAll(binaryHeader.entrySet().stream().collect(
HashMap::new, (m, e) -> m.put(e.getKey(), e.getValue().toString()), HashMap::putAll));
properties.put("version", "");
properties.put("title", "");
properties.put("creator", "");
properties.put("creation_date", "");
properties.put("bounding_box", "");
properties.put("language_level", "");
properties.put("extensions", "");
properties.put("document_needed_resources", "");
properties.put("preview_type", "None");
String[] lines = asciiContent.split("\n");
for (String line : lines) {
if (line.startsWith("%!PS-Adobe-3.0 EPSF-3.0")) {
properties.put("version", line);
} else if (line.startsWith("%%Title:")) {
properties.put("title", line.substring(8).trim());
} else if (line.startsWith("%%Creator:")) {
properties.put("creator", line.substring(10).trim());
} else if (line.startsWith("%%CreationDate:")) {
properties.put("creation_date", line.substring(15).trim());
} else if (line.startsWith("%%BoundingBox:")) {
properties.put("bounding_box", line.substring(14).trim());
} else if (line.startsWith("%%LanguageLevel:")) {
properties.put("language_level", line.substring(16).trim());
} else if (line.startsWith("%%Extensions:")) {
properties.put("extensions", line.substring(13).trim());
} else if (line.startsWith("%%DocumentNeededResources:")) {
properties.put("document_needed_resources", line.substring(26).trim());
} else if (line.startsWith("%%BeginPreview")) {
properties.put("preview_type", "EPSI");
}
}
if (hasBinaryHeader) {
if ((int) binaryHeader.get("mf_length") > 0) {
properties.put("preview_type", "Metafile");
} else if ((int) binaryHeader.get("tiff_length") > 0) {
properties.put("preview_type", "TIFF");
}
}
}
public void printProperties() {
properties.forEach((key, value) -> System.out.println(key + ": " + value));
}
public void write(String newFilepath, Map<String, String> modifyProps) throws IOException {
byte[] newContent = content.clone();
if (modifyProps != null && !modifyProps.isEmpty()) {
// Simple replace in ASCII (not full binary handling)
int asciiStart = hasBinaryHeader ? (int) binaryHeader.get("ps_offset") : 0;
int asciiLength = hasBinaryHeader ? (int) binaryHeader.get("ps_length") : content.length;
String ascii = new String(content, asciiStart, asciiLength);
for (Map.Entry<String, String> entry : modifyProps.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (key.equals("title")) {
ascii = ascii.replaceAll("%%Title:.*", "%%Title: " + value);
}
// Add similar for others...
}
byte[] newAscii = ascii.getBytes();
newContent = new byte[asciiStart + newAscii.length + (content.length - asciiStart - asciiLength)];
System.arraycopy(content, 0, newContent, 0, asciiStart);
System.arraycopy(newAscii, 0, newContent, asciiStart, newAscii.length);
System.arraycopy(content, asciiStart + asciiLength, newContent, asciiStart + newAscii.length, content.length - asciiStart - asciiLength);
}
try (FileOutputStream fos = new FileOutputStream(newFilepath)) {
fos.write(newContent);
}
}
// Example usage:
// public static void main(String[] args) throws IOException {
// EPSHandler handler = new EPSHandler("example.eps");
// handler.printProperties();
// Map<String, String> mods = new HashMap<>();
// mods.put("title", "New Title");
// handler.write("modified.eps", mods);
// }
}
- JavaScript class for .EPS handling (Node.js compatible):
const fs = require('fs');
class EPSHandler {
constructor(filepath) {
this.filepath = filepath;
this.properties = {};
this.content = null;
this.hasBinaryHeader = false;
this.binaryHeader = {};
this.readAndDecode();
}
readAndDecode() {
this.content = fs.readFileSync(this.filepath);
const dv = new DataView(this.content.buffer);
// Check binary header
if (this.content.length >= 30 && dv.getUint32(0, true) === 0xC6D3D0C5) { // Little-endian
this.hasBinaryHeader = true;
this.binaryHeader = {
magic: '0xC5D0D3C6',
psOffset: dv.getUint32(4, true),
psLength: dv.getUint32(8, true),
mfOffset: dv.getUint32(12, true),
mfLength: dv.getUint32(16, true),
tiffOffset: dv.getUint32(20, true),
tiffLength: dv.getUint32(24, true),
checksum: dv.getUint16(28, true).toString(16).toUpperCase()
};
const asciiStart = this.binaryHeader.psOffset;
const asciiContent = this.content.subarray(asciiStart, asciiStart + this.binaryHeader.psLength).toString('utf-8');
this.parseDSC(asciiContent);
} else {
this.parseDSC(this.content.toString('utf-8'));
}
}
parseDSC(asciiContent) {
this.properties = {
hasBinaryHeader: this.hasBinaryHeader ? 'Yes' : 'No',
...this.binaryHeader,
version: '',
title: '',
creator: '',
creationDate: '',
boundingBox: '',
languageLevel: '',
extensions: '',
documentNeededResources: '',
previewType: 'None'
};
const lines = asciiContent.split(/\r?\n/);
lines.forEach(line => {
if (line.startsWith('%!PS-Adobe-3.0 EPSF-3.0')) this.properties.version = line;
else if (line.startsWith('%%Title:')) this.properties.title = line.slice(8).trim();
else if (line.startsWith('%%Creator:')) this.properties.creator = line.slice(10).trim();
else if (line.startsWith('%%CreationDate:')) this.properties.creationDate = line.slice(15).trim();
else if (line.startsWith('%%BoundingBox:')) this.properties.boundingBox = line.slice(14).trim();
else if (line.startsWith('%%LanguageLevel:')) this.properties.languageLevel = line.slice(16).trim();
else if (line.startsWith('%%Extensions:')) this.properties.extensions = line.slice(13).trim();
else if (line.startsWith('%%DocumentNeededResources:')) this.properties.documentNeededResources = line.slice(26).trim();
else if (line.startsWith('%%BeginPreview')) this.properties.previewType = 'EPSI';
});
if (this.hasBinaryHeader) {
if (this.binaryHeader.mfLength > 0) this.properties.previewType = 'Metafile';
else if (this.binaryHeader.tiffLength > 0) this.properties.previewType = 'TIFF';
}
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
write(newFilepath, modifyProps = null) {
let newContent = Buffer.from(this.content);
if (modifyProps) {
// Simple modify in ASCII
const asciiStart = this.hasBinaryHeader ? this.binaryHeader.psOffset : 0;
const asciiLength = this.hasBinaryHeader ? this.binaryHeader.psLength : this.content.length;
let ascii = this.content.subarray(asciiStart, asciiStart + asciiLength).toString('utf-8');
for (const [key, value] of Object.entries(modifyProps)) {
if (key === 'title') {
ascii = ascii.replace(/%%Title:.*/, `%%Title: ${value}`);
}
// Add similar for others...
}
const newAscii = Buffer.from(ascii, 'utf-8');
newContent = Buffer.concat([
this.content.subarray(0, asciiStart),
newAscii,
this.content.subarray(asciiStart + asciiLength)
]);
}
fs.writeFileSync(newFilepath, newContent);
}
}
// Example usage:
// const handler = new EPSHandler('example.eps');
// handler.printProperties();
// handler.write('modified.eps', { title: 'New Title' });
- C++ class for .EPS handling:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>
#include <cstdint>
#include <cstring>
class EPSHandler {
private:
std::string filepath;
std::map<std::string, std::string> properties;
std::vector<uint8_t> content;
bool has_binary_header = false;
std::map<std::string, uint32_t> binary_header;
void read_and_decode() {
std::ifstream file(filepath, std::ios::binary);
if (!file) {
std::cerr << "Failed to open file." << std::endl;
return;
}
content.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
// Check binary header
if (content.size() >= 30 && std::memcmp(&content[0], "\xC5\xD0\xD3\xC6", 4) == 0) {
has_binary_header = true;
uint32_t magic = *reinterpret_cast<uint32_t*>(&content[0]);
uint32_t ps_offset = *reinterpret_cast<uint32_t*>(&content[4]);
uint32_t ps_length = *reinterpret_cast<uint32_t*>(&content[8]);
uint32_t mf_offset = *reinterpret_cast<uint32_t*>(&content[12]);
uint32_t mf_length = *reinterpret_cast<uint32_t*>(&content[16]);
uint32_t tiff_offset = *reinterpret_cast<uint32_t*>(&content[20]);
uint32_t tiff_length = *reinterpret_cast<uint32_t*>(&content[24]);
uint16_t checksum = *reinterpret_cast<uint16_t*>(&content[28]);
binary_header["magic"] = magic;
binary_header["ps_offset"] = ps_offset;
binary_header["ps_length"] = ps_length;
binary_header["mf_offset"] = mf_offset;
binary_header["mf_length"] = mf_length;
binary_header["tiff_offset"] = tiff_offset;
binary_header["tiff_length"] = tiff_length;
binary_header["checksum"] = checksum;
std::string ascii_content(content.begin() + ps_offset, content.begin() + ps_offset + ps_length);
parse_dsc(ascii_content);
} else {
std::string ascii_content(content.begin(), content.end());
parse_dsc(ascii_content);
}
}
void parse_dsc(const std::string& ascii_content) {
properties["has_binary_header"] = has_binary_header ? "Yes" : "No";
if (has_binary_header) {
properties["magic"] = "0xC5D0D3C6";
properties["ps_offset"] = std::to_string(binary_header["ps_offset"]);
properties["ps_length"] = std::to_string(binary_header["ps_length"]);
properties["mf_offset"] = std::to_string(binary_header["mf_offset"]);
properties["mf_length"] = std::to_string(binary_header["mf_length"]);
properties["tiff_offset"] = std::to_string(binary_header["tiff_offset"]);
properties["tiff_length"] = std::to_string(binary_header["tiff_length"]);
properties["checksum"] = std::to_string(binary_header["checksum"]);
}
properties["version"] = "";
properties["title"] = "";
properties["creator"] = "";
properties["creation_date"] = "";
properties["bounding_box"] = "";
properties["language_level"] = "";
properties["extensions"] = "";
properties["document_needed_resources"] = "";
properties["preview_type"] = "None";
size_t pos = 0;
std::string line;
while ((pos = ascii_content.find('\n', pos)) != std::string::npos || pos < ascii_content.size()) {
line = ascii_content.substr(0, pos);
if (line.rfind("%!PS-Adobe-3.0 EPSF-3.0", 0) == 0) {
properties["version"] = line;
} else if (line.rfind("%%Title:", 0) == 0) {
properties["title"] = line.substr(8);
} else if (line.rfind("%%Creator:", 0) == 0) {
properties["creator"] = line.substr(10);
} else if (line.rfind("%%CreationDate:", 0) == 0) {
properties["creation_date"] = line.substr(15);
} else if (line.rfind("%%BoundingBox:", 0) == 0) {
properties["bounding_box"] = line.substr(14);
} else if (line.rfind("%%LanguageLevel:", 0) == 0) {
properties["language_level"] = line.substr(16);
} else if (line.rfind("%%Extensions:", 0) == 0) {
properties["extensions"] = line.substr(13);
} else if (line.rfind("%%DocumentNeededResources:", 0) == 0) {
properties["document_needed_resources"] = line.substr(26);
} else if (line.rfind("%%BeginPreview", 0) == 0) {
properties["preview_type"] = "EPSI";
}
if (pos == std::string::npos) break;
ascii_content = ascii_content.substr(pos + 1);
pos = 0;
}
if (has_binary_header) {
if (binary_header["mf_length"] > 0) properties["preview_type"] = "Metafile";
else if (binary_header["tiff_length"] > 0) properties["preview_type"] = "TIFF";
}
}
public:
EPSHandler(const std::string& fp) : filepath(fp) {
read_and_decode();
}
void print_properties() const {
for (const auto& pair : properties) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
}
void write(const std::string& new_filepath, const std::map<std::string, std::string>& modify_props = {}) {
std::vector<uint8_t> new_content = content;
if (!modify_props.empty()) {
// Simple modify in ASCII
uint32_t ascii_start = has_binary_header ? binary_header["ps_offset"] : 0;
uint32_t ascii_length = has_binary_header ? binary_header["ps_length"] : content.size();
std::string ascii(new_content.begin() + ascii_start, new_content.begin() + ascii_start + ascii_length);
for (const auto& mod : modify_props) {
if (mod.first == "title") {
size_t pos = ascii.find("%%Title:");
if (pos != std::string::npos) {
size_t end = ascii.find('\n', pos);
ascii.replace(pos, end - pos, "%%Title: " + mod.second);
}
}
// Add similar for others...
}
new_content.erase(new_content.begin() + ascii_start, new_content.begin() + ascii_start + ascii_length);
new_content.insert(new_content.begin() + ascii_start, ascii.begin(), ascii.end());
}
std::ofstream out(new_filepath, std::ios::binary);
out.write(reinterpret_cast<const char*>(new_content.data()), new_content.size());
}
};
// Example usage:
// int main() {
// EPSHandler handler("example.eps");
// handler.print_properties();
// std::map<std::string, std::string> mods = {{"title", "New Title"}};
// handler.write("modified.eps", mods);
// return 0;
// }