Task 330: .JPE File Format
Task 330: .JPE File Format
1. List of Properties of the .JPE File Format Intrinsic to Its File Structure
The .JPE file format is an alternative extension for JPEG (Joint Photographic Experts Group) files, specifically conforming to the JPEG File Interchange Format (JFIF) standard (ITU-T T.871 / ISO/IEC 10918-5). It is identical in structure to .JPEG or .JPG files and represents compressed raster images. The "properties intrinsic to its file system" are interpreted here as the structural and metadata properties embedded within the file's binary format (e.g., headers, markers, and fields), rather than external file system attributes like size or timestamps. These can be extracted by parsing the file's segments.
Based on the JFIF and JPEG specifications, the key extractable properties are:
- File Signature (Magic Bytes): The starting bytes, typically
FFD8
(SOI - Start of Image) followed byFFE0
(APP0 marker for JFIF). - JFIF Identifier: The string "JFIF" followed by a null byte (
4A 46 49 46 00
), confirming JFIF compliance. - JFIF Version: Major and minor version numbers (e.g., 1.02).
- Density Units: Units for pixel density (0: no units, aspect ratio only; 1: dots per inch; 2: dots per cm).
- Horizontal Pixel Density (Xdensity): Horizontal resolution value.
- Vertical Pixel Density (Ydensity): Vertical resolution value.
- Thumbnail Width (Xthumbnail): Horizontal pixel count of the embedded thumbnail (0 if none).
- Thumbnail Height (Ythumbnail): Vertical pixel count of the embedded thumbnail (0 if none).
- Has Thumbnail: Boolean indicating if thumbnail data is present (based on non-zero thumbnail dimensions).
- Image Width: The width of the main image in pixels (from the Start of Frame - SOF marker).
- Image Height: The height of the main image in pixels (from the SOF marker).
- Bits per Sample: Sample precision, typically 8 bits per component.
- Number of Color Components: Number of color channels (1 for grayscale, 3 for YCbCr color).
- Compression Mode: Type of JPEG compression (e.g., Baseline DCT if SOF0 marker, Progressive DCT if SOF2).
- Has Extension Thumbnail (JFXX): Boolean indicating if an optional JFIF extension (JFXX APP0) is present for advanced thumbnails.
- Extension Thumbnail Type: If JFXX present, the code (e.g., 10: JPEG-encoded thumbnail, 11: 1-byte/pixel palette, 13: 3-bytes/pixel RGB).
Note: Additional properties like quantization tables, Huffman tables, comments (COM markers), or application-specific data (e.g., EXIF in APP1) may exist but are optional and not core to all .JPE files. The above focuses on intrinsic JFIF/JPEG elements.
2. Two Direct Download Links for .JPE Files
Here are two direct download links for sample .JPE files (verified as valid JPEG images with the .JPE extension):
- https://filesamples.com/samples/image/jpe/sample_640×426.jpe (Size: ~100 KB, 640x426 resolution)
- https://filesamples.com/samples/image/jpe/sample_1280×853.jpe (Size: ~326 KB, 1280x853 resolution)
3. Ghost Blog Embedded HTML/JavaScript for Drag-and-Drop .JPE File Property Dump
Assuming "ghost blog embedded" refers to embeddable HTML/JavaScript code suitable for a Ghost blogging platform (or similar), below is a self-contained HTML snippet with JavaScript. It creates a drag-and-drop area where a user can drop a .JPE file. The script reads the file as an ArrayBuffer, parses the JFIF/JPEG structure, extracts the properties listed in #1, and dumps them to the screen in a readable format. Embed this in a Ghost blog post using their HTML card or code injection.
4. Python Class for .JPE File Handling
Below is a Python class that can open a .JPE file, decode/read its structure, extract and print the properties to console, and write a modified version (e.g., updating density values as an example of write capability).
import struct
import sys
class JPEParser:
def __init__(self, filepath):
self.filepath = filepath
self.data = None
self.properties = {}
def read(self):
with open(self.filepath, 'rb') as f:
self.data = f.read()
self.decode()
def decode(self):
if len(self.data) < 4 or self.data[:2] != b'\xFF\xD8':
print("Not a valid .JPE file")
return
offset = 2
while offset < len(self.data):
marker = struct.unpack('>H', self.data[offset:offset+2])[0]
offset += 2
if marker == 0xFFE0: # APP0 JFIF
length = struct.unpack('>H', self.data[offset:offset+2])[0]
offset += 2
identifier = self.data[offset:offset+4].decode('ascii', errors='ignore')
if identifier != 'JFIF':
continue
self.properties['JFIF Identifier'] = identifier
offset += 5 # JFIF\0
major, minor = self.data[offset], self.data[offset+1]
self.properties['JFIF Version'] = f"{major}.{minor:02d}"
offset += 2
self.properties['Density Units'] = self.data[offset]
offset += 1
self.properties['Horizontal Pixel Density'] = struct.unpack('>H', self.data[offset:offset+2])[0]
offset += 2
self.properties['Vertical Pixel Density'] = struct.unpack('>H', self.data[offset:offset+2])[0]
offset += 2
thumb_w = self.data[offset]
offset += 1
thumb_h = self.data[offset]
offset += 1
self.properties['Thumbnail Width'] = thumb_w
self.properties['Thumbnail Height'] = thumb_h
self.properties['Has Thumbnail'] = 'Yes' if thumb_w > 0 and thumb_h > 0 else 'No'
offset += 3 * thumb_w * thumb_h # Skip thumbnail
elif marker & 0xFFF0 == 0xFFC0 or marker & 0xFFF0 == 0xFFC8: # SOF
self.properties['Compression Mode'] = 'Baseline DCT' if marker == 0xFFC0 else 'Progressive DCT'
length = struct.unpack('>H', self.data[offset:offset+2])[0]
offset += 2
self.properties['Bits per Sample'] = self.data[offset]
offset += 1
self.properties['Image Height'] = struct.unpack('>H', self.data[offset:offset+2])[0]
offset += 2
self.properties['Image Width'] = struct.unpack('>H', self.data[offset:offset+2])[0]
offset += 2
self.properties['Number of Color Components'] = self.data[offset]
offset += 1
offset += (length - 8) # Skip rest
elif marker == 0xFFD9: # EOI
break
else:
if (marker & 0xFF00) != 0xFF00:
break
length = struct.unpack('>H', self.data[offset:offset+2])[0]
offset += length
self.properties['File Signature'] = 'FFD8FFE0 (JFIF)'
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, output_path, modify_example=False):
if not self.data:
print("No data to write")
return
data = bytearray(self.data)
if modify_example:
# Example: Modify horizontal density to 300 (offset hardcoded for simplicity; in real use, track positions)
app0_start = data.find(b'\xFF\xE0')
if app0_start != -1:
density_offset = app0_start + 2 + 2 + 5 + 2 + 1 # Marker + length + JFIF\0 + version + units
struct.pack_into('>H', data, density_offset, 300) # Set Xdensity to 300
with open(output_path, 'wb') as f:
f.write(data)
# Usage example:
# parser = JPEParser('sample.jpe')
# parser.read()
# parser.print_properties()
# parser.write('modified.jpe', modify_example=True)
5. Java Class for .JPE File Handling
Below is a Java class that opens a .JPE file, decodes/reads its structure, extracts and prints the properties to console, and writes a modified version (e.g., updating density).
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;
public class JPEParser {
private String filepath;
private byte[] data;
private Map<String, Object> properties = new HashMap<>();
public JPEParser(String filepath) {
this.filepath = filepath;
}
public void read() throws IOException {
FileInputStream fis = new FileInputStream(filepath);
data = fis.readAllBytes();
fis.close();
decode();
}
private void decode() {
if (data.length < 4 || (data[0] & 0xFF) != 0xFF || (data[1] & 0xFF) != 0xD8) {
System.out.println("Not a valid .JPE file");
return;
}
int offset = 2;
while (offset < data.length) {
int marker = ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
offset += 2;
if (marker == 0xFFE0) { // APP0 JFIF
int length = ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
offset += 2;
String id = new String(data, offset, 4);
if (!id.equals("JFIF")) continue;
properties.put("JFIF Identifier", id);
offset += 5; // JFIF\0
int major = data[offset] & 0xFF;
int minor = data[offset + 1] & 0xFF;
properties.put("JFIF Version", major + "." + String.format("%02d", minor));
offset += 2;
properties.put("Density Units", data[offset] & 0xFF);
offset += 1;
int xDensity = ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
properties.put("Horizontal Pixel Density", xDensity);
offset += 2;
int yDensity = ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
properties.put("Vertical Pixel Density", yDensity);
offset += 2;
int thumbW = data[offset] & 0xFF;
offset += 1;
int thumbH = data[offset] & 0xFF;
offset += 1;
properties.put("Thumbnail Width", thumbW);
properties.put("Thumbnail Height", thumbH);
properties.put("Has Thumbnail", (thumbW > 0 && thumbH > 0) ? "Yes" : "No");
offset += 3 * thumbW * thumbH;
} else if ((marker & 0xFFF0) == 0xFFC0 || (marker & 0xFFF0) == 0xFFC8) { // SOF
properties.put("Compression Mode", (marker == 0xFFC0) ? "Baseline DCT" : "Progressive DCT");
int length = ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
offset += 2;
properties.put("Bits per Sample", data[offset] & 0xFF);
offset += 1;
int height = ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
properties.put("Image Height", height);
offset += 2;
int width = ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
properties.put("Image Width", width);
offset += 2;
properties.put("Number of Color Components", data[offset] & 0xFF);
offset += 1;
offset += (length - 8);
} else if (marker == 0xFFD9) {
break;
} else {
if ((marker & 0xFF00) != 0xFF00) break;
int length = ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
offset += length;
}
}
properties.put("File Signature", "FFD8FFE0 (JFIF)");
}
public void printProperties() {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public void write(String outputPath, boolean modifyExample) throws IOException {
if (data == null) {
System.out.println("No data to write");
return;
}
byte[] modifiedData = data.clone();
if (modifyExample) {
// Example: Set Xdensity to 300
ByteBuffer buffer = ByteBuffer.wrap(modifiedData).order(ByteOrder.BIG_ENDIAN);
int app0Start = -1;
for (int i = 0; i < modifiedData.length - 1; i++) {
if ((modifiedData[i] & 0xFF) == 0xFF && (modifiedData[i+1] & 0xFF) == 0xE0) {
app0Start = i;
break;
}
}
if (app0Start != -1) {
int densityOffset = app0Start + 2 + 2 + 5 + 2 + 1; // Marker + length + JFIF\0 + version + units
buffer.position(densityOffset);
buffer.putShort((short) 300);
}
}
FileOutputStream fos = new FileOutputStream(outputPath);
fos.write(modifiedData);
fos.close();
}
// Usage example:
// public static void main(String[] args) throws IOException {
// JPEParser parser = new JPEParser("sample.jpe");
// parser.read();
// parser.printProperties();
// parser.write("modified.jpe", true);
// }
}
6. JavaScript Class for .JPE File Handling
Below is a JavaScript class (for Node.js) that opens a .JPE file, decodes/reads its structure, extracts and prints the properties to console, and writes a modified version (using Node's fs module).
const fs = require('fs');
class JPEParser {
constructor(filepath) {
this.filepath = filepath;
this.data = null;
this.properties = {};
}
read() {
this.data = fs.readFileSync(this.filepath);
this.decode();
}
decode() {
if (this.data.length < 4 || this.data[0] !== 0xFF || this.data[1] !== 0xD8) {
console.log('Not a valid .JPE file');
return;
}
let offset = 2;
while (offset < this.data.length) {
const marker = (this.data[offset] << 8) | this.data[offset + 1];
offset += 2;
if (marker === 0xFFE0) { // APP0 JFIF
const length = (this.data[offset] << 8) | this.data[offset + 1];
offset += 2;
const id = String.fromCharCode(this.data[offset], this.data[offset+1], this.data[offset+2], this.data[offset+3]);
if (id !== 'JFIF') continue;
this.properties['JFIF Identifier'] = id;
offset += 5; // JFIF\0
const major = this.data[offset];
const minor = this.data[offset + 1];
this.properties['JFIF Version'] = `${major}.${minor.toString().padStart(2, '0')}`;
offset += 2;
this.properties['Density Units'] = this.data[offset];
offset += 1;
this.properties['Horizontal Pixel Density'] = (this.data[offset] << 8) | this.data[offset + 1];
offset += 2;
this.properties['Vertical Pixel Density'] = (this.data[offset] << 8) | this.data[offset + 1];
offset += 2;
const thumbW = this.data[offset];
offset += 1;
const thumbH = this.data[offset];
offset += 1;
this.properties['Thumbnail Width'] = thumbW;
this.properties['Thumbnail Height'] = thumbH;
this.properties['Has Thumbnail'] = (thumbW > 0 && thumbH > 0) ? 'Yes' : 'No';
offset += 3 * thumbW * thumbH;
} else if ((marker & 0xFFF0) === 0xFFC0 || (marker & 0xFFF0) === 0xFFC8) { // SOF
this.properties['Compression Mode'] = (marker === 0xFFC0) ? 'Baseline DCT' : 'Progressive DCT';
const length = (this.data[offset] << 8) | this.data[offset + 1];
offset += 2;
this.properties['Bits per Sample'] = this.data[offset];
offset += 1;
this.properties['Image Height'] = (this.data[offset] << 8) | this.data[offset + 1];
offset += 2;
this.properties['Image Width'] = (this.data[offset] << 8) | this.data[offset + 1];
offset += 2;
this.properties['Number of Color Components'] = this.data[offset];
offset += 1;
offset += (length - 8);
} else if (marker === 0xFFD9) {
break;
} else {
if ((marker & 0xFF00) !== 0xFF00) break;
const length = (this.data[offset] << 8) | this.data[offset + 1];
offset += length;
}
}
this.properties['File Signature'] = 'FFD8FFE0 (JFIF)';
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
write(outputPath, modifyExample = false) {
if (!this.data) {
console.log('No data to write');
return;
}
const modifiedData = Buffer.from(this.data);
if (modifyExample) {
// Example: Set Xdensity to 300
const app0Start = modifiedData.indexOf(Buffer.from([0xFF, 0xE0]));
if (app0Start !== -1) {
const densityOffset = app0Start + 2 + 2 + 5 + 2 + 1; // Marker + length + JFIF\0 + version + units
modifiedData.writeUInt16BE(300, densityOffset);
}
}
fs.writeFileSync(outputPath, modifiedData);
}
}
// Usage example:
// const parser = new JPEParser('sample.jpe');
// parser.read();
// parser.printProperties();
// parser.write('modified.jpe', true);
7. C++ Class for .JPE File Handling
Below is a C++ class that opens a .JPE file, decodes/reads its structure, extracts and prints the properties to console, and writes a modified version.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <iomanip>
#include <map>
class JPEParser {
private:
std::string filepath;
std::vector<unsigned char> data;
std::map<std::string, std::string> properties;
public:
JPEParser(const std::string& fp) : filepath(fp) {}
void read() {
std::ifstream file(filepath, std::ios::binary);
if (!file) {
std::cout << "Cannot open file" << std::endl;
return;
}
data = std::vector<unsigned char>(std::istreambuf_iterator<char>(file), {});
file.close();
decode();
}
void decode() {
if (data.size() < 4 || data[0] != 0xFF || data[1] != 0xD8) {
std::cout << "Not a valid .JPE file" << std::endl;
return;
}
size_t offset = 2;
while (offset < data.size()) {
unsigned short marker = (data[offset] << 8) | data[offset + 1];
offset += 2;
if (marker == 0xFFE0) { // APP0 JFIF
unsigned short length = (data[offset] << 8) | data[offset + 1];
offset += 2;
std::string id = "";
for (int i = 0; i < 4; ++i) id += data[offset + i];
if (id != "JFIF") continue;
properties["JFIF Identifier"] = id;
offset += 5; // JFIF\0
unsigned char major = data[offset];
unsigned char minor = data[offset + 1];
properties["JFIF Version"] = std::to_string(major) + "." + std::to_string(minor);
offset += 2;
properties["Density Units"] = std::to_string(data[offset]);
offset += 1;
unsigned short xDensity = (data[offset] << 8) | data[offset + 1];
properties["Horizontal Pixel Density"] = std::to_string(xDensity);
offset += 2;
unsigned short yDensity = (data[offset] << 8) | data[offset + 1];
properties["Vertical Pixel Density"] = std::to_string(yDensity);
offset += 2;
unsigned char thumbW = data[offset];
offset += 1;
unsigned char thumbH = data[offset];
offset += 1;
properties["Thumbnail Width"] = std::to_string(thumbW);
properties["Thumbnail Height"] = std::to_string(thumbH);
properties["Has Thumbnail"] = (thumbW > 0 && thumbH > 0) ? "Yes" : "No";
offset += 3 * thumbW * thumbH;
} else if ((marker & 0xFFF0) == 0xFFC0 || (marker & 0xFFF0) == 0xFFC8) { // SOF
properties["Compression Mode"] = (marker == 0xFFC0) ? "Baseline DCT" : "Progressive DCT";
unsigned short length = (data[offset] << 8) | data[offset + 1];
offset += 2;
properties["Bits per Sample"] = std::to_string(data[offset]);
offset += 1;
unsigned short height = (data[offset] << 8) | data[offset + 1];
properties["Image Height"] = std::to_string(height);
offset += 2;
unsigned short width = (data[offset] << 8) | data[offset + 1];
properties["Image Width"] = std::to_string(width);
offset += 2;
properties["Number of Color Components"] = std::to_string(data[offset]);
offset += 1;
offset += (length - 8);
} else if (marker == 0xFFD9) {
break;
} else {
if ((marker & 0xFF00) != 0xFF00) break;
unsigned short length = (data[offset] << 8) | data[offset + 1];
offset += length;
}
}
properties["File Signature"] = "FFD8FFE0 (JFIF)";
}
void printProperties() {
for (const auto& prop : properties) {
std::cout << prop.first << ": " << prop.second << std::endl;
}
}
void write(const std::string& outputPath, bool modifyExample = false) {
if (data.empty()) {
std::cout << "No data to write" << std::endl;
return;
}
std::vector<unsigned char> modifiedData = data;
if (modifyExample) {
// Example: Set Xdensity to 300
size_t app0Start = 0;
for (size_t i = 0; i < modifiedData.size() - 1; ++i) {
if (modifiedData[i] == 0xFF && modifiedData[i + 1] == 0xE0) {
app0Start = i;
break;
}
}
if (app0Start != 0) {
size_t densityOffset = app0Start + 2 + 2 + 5 + 2 + 1; // Marker + length + JFIF\0 + version + units
modifiedData[densityOffset] = 300 >> 8;
modifiedData[densityOffset + 1] = 300 & 0xFF;
}
}
std::ofstream outFile(outputPath, std::ios::binary);
outFile.write(reinterpret_cast<const char*>(modifiedData.data()), modifiedData.size());
outFile.close();
}
};
// Usage example:
// int main() {
// JPEParser parser("sample.jpe");
// parser.read();
// parser.printProperties();
// parser.write("modified.jpe", true);
// return 0;
// }