Task 576: .PSDC File Format
Task 576: .PSDC File Format
File Format Specifications for .PSDC
The .PSDC file format is the Adobe Photoshop Cloud Document format, introduced in November 2019. It is designed for cloud syncing, offline editing, versioning, and incremental backups within Adobe's ecosystem. According to multiple sources, the internal structure of .PSDC files is the same as the .PSD (Photoshop Document) format, with additional cloud-specific features like caching and syncing metadata. However, the core file structure, headers, and data sections follow the Adobe Photoshop file format specification. The official Adobe PSD specification does not explicitly cover PSDC, but since the content is identical to PSD, the PSD spec applies. The format is proprietary, and no separate public specification for PSDC structure is available beyond its relation to PSD.
The PSD/PSDC format is a layered image file supporting multiple channels, layers, masks, and metadata. It is structured into five main sections: File Header, Color Mode Data, Image Resources, Layer and Mask Information, and Image Data. Data is stored in big-endian format, with variable-length sections.
- List of all the properties of this file format intrinsic to its file system:
Based on the PSD specification (which applies to PSDC), the intrinsic properties are the fields in the file structure, focusing on the header and key elements. "Intrinsic to its file system" is interpreted as the core structural properties that define the file's layout and metadata, such as headers, dimensions, and modes. Here is the comprehensive list:
- Signature (4 bytes: always '8BPS' ASCII)
- Version (2 bytes: 1 for standard, 2 for large document PSB variant)
- Reserved (6 bytes: must be zeros)
- Number of channels (2 bytes: 1 to 56, including alpha channels)
- Image height (4 bytes: 1 to 30,000 pixels for standard; up to 300,000 for large)
- Image width (4 bytes: same as height)
- Bits per channel (depth) (2 bytes: 1, 8, 16, or 32)
- Color mode (2 bytes: 0=Bitmap, 1=Grayscale, 2=Indexed, 3=RGB, 4=CMYK, 7=Multichannel, 8=Duotone, 9=Lab)
- Color mode data length (4 bytes: length of color data section; 0 for most modes, 768 for indexed)
- Color data (variable: color table for indexed/duotone modes)
- Image resources length (4 bytes: length of resources section)
- Image resource blocks (variable: series of blocks with signature '8BIM', ID, name, size, data; e.g., resolution info, thumbnails, ICC profile)
- Layer info length (4 bytes: length of layer section; 8 bytes for large format)
- Layer count (2 bytes: number of layers; negative if transparency data is merged)
- Layer records (variable: for each layer - bounding rectangle, channel count, channel info, blend mode, opacity, clipping, flags, layer mask, blending ranges, name)
- Channel image data (variable: pixel data for each layer channel, with compression method)
- Global layer mask info (variable: overlay color space, opacity, kind)
- Additional layer info (variable: tagged blocks for effects, adjustments, etc.)
- Image data compression (2 bytes: 0=raw, 1=RLE, 2/3=ZIP)
- Image pixel data (variable: planar order, compressed or raw)
These properties define the file's structure and are extracted from the Adobe Photoshop File Formats Specification.
- Two direct download links for files of format .PSDC:
No public direct download links for .PSDC files were found, as they are primarily cloud-based and not commonly shared publicly due to their proprietary nature and integration with Adobe Creative Cloud. Attempts to search for samples yielded no results. Since .PSDC uses the same internal format as .PSD, here are two direct download links for sample .PSD files for demonstration purposes:
- https://www.sample-videos.com/psd/Sample-psd-file.psd
- https://filesamples.com/samples/document/psd/sample.psd
- Ghost blog embedded HTML JavaScript for drag and drop .PSDC file dump:
Here is a complete HTML page with embedded JavaScript that can be embedded in a Ghost blog post. It allows dragging and dropping a .PSDC (or .PSD) file and dumps the properties to the screen by parsing the header.
- Python class for .PSDC file:
import struct
class PSDCFile:
def __init__(self, filename=None):
self.properties = {}
if filename:
self.read(filename)
def read(self, filename):
with open(filename, 'rb') as f:
data = f.read(26) # Header is 26 bytes
if len(data) < 26:
raise ValueError("File too small")
self.properties = {
'Signature': data[0:4].decode('ascii'),
'Version': struct.unpack('>H', data[4:6])[0],
'Reserved': list(data[6:12]),
'Channels': struct.unpack('>H', data[12:14])[0],
'Height': struct.unpack('>I', data[14:18])[0],
'Width': struct.unpack('>I', data[18:22])[0],
'Depth': struct.unpack('>H', data[22:24])[0],
'ColorMode': struct.unpack('>H', data[24:26])[0]
}
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, filename, properties=None):
if properties:
self.properties = properties
if not all(key in self.properties for key in ['Signature', 'Version', 'Reserved', 'Channels', 'Height', 'Width', 'Depth', 'ColorMode']):
raise ValueError("Missing properties for writing")
data = (
self.properties['Signature'].encode('ascii') +
struct.pack('>H', self.properties['Version']) +
bytes(self.properties['Reserved']) +
struct.pack('>H', self.properties['Channels']) +
struct.pack('>I', self.properties['Height']) +
struct.pack('>I', self.properties['Width']) +
struct.pack('>H', self.properties['Depth']) +
struct.pack('>H', self.properties['ColorMode'])
)
with open(filename, 'wb') as f:
f.write(data) # Note: This writes only the header; full write requires more data
# Example usage:
# psdc = PSDCFile('example.psdc')
# psdc.print_properties()
# psdc.write('new.psdc')
Note: The write method writes only the header for simplicity; a full PSDC write would require implementing the entire structure, which is complex.
- Java class for .PSDC file:
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class PSDCFile {
private String signature;
private short version;
private byte[] reserved = new byte[6];
private short channels;
private int height;
private int width;
private short depth;
private short colorMode;
public PSDCFile(String filename) throws IOException {
read(filename);
}
public void read(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename);
DataInputStream dis = new DataInputStream(fis)) {
byte[] sigBytes = new byte[4];
dis.readFully(sigBytes);
signature = new String(sigBytes, "ASCII");
version = dis.readShort();
dis.readFully(reserved);
channels = dis.readShort();
height = dis.readInt();
width = dis.readInt();
depth = dis.readShort();
colorMode = dis.readShort();
}
}
public void printProperties() {
System.out.println("Signature: " + signature);
System.out.println("Version: " + version);
System.out.print("Reserved: ");
for (byte b : reserved) System.out.print(b + " ");
System.out.println();
System.out.println("Channels: " + channels);
System.out.println("Height: " + height);
System.out.println("Width: " + width);
System.out.println("Depth: " + depth);
System.out.println("ColorMode: " + colorMode);
}
public void write(String filename) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename);
DataOutputStream dos = new DataOutputStream(fos)) {
dos.writeBytes(signature);
dos.writeShort(version);
dos.write(reserved);
dos.writeShort(channels);
dos.writeInt(height);
dos.writeInt(width);
dos.writeShort(depth);
dos.writeShort(colorMode);
}
}
// Example usage:
// public static void main(String[] args) throws IOException {
// PSDCFile psdc = new PSDCFile("example.psdc");
// psdc.printProperties();
// psdc.write("new.psdc");
// }
}
Note: Write method only handles the header.
- JavaScript class for .PSDC file:
class PSDCFile {
constructor(arrayBuffer = null) {
this.properties = {};
if (arrayBuffer) {
this.read(arrayBuffer);
}
}
read(arrayBuffer) {
const dataView = new DataView(arrayBuffer);
this.properties = {
Signature: String.fromCharCode(...new Uint8Array(arrayBuffer.slice(0, 4))),
Version: dataView.getUint16(4, false),
Reserved: Array.from(new Uint8Array(arrayBuffer.slice(6, 12))),
Channels: dataView.getUint16(12, false),
Height: dataView.getUint32(14, false),
Width: dataView.getUint32(18, false),
Depth: dataView.getUint16(22, false),
ColorMode: dataView.getUint16(24, false)
};
}
printProperties() {
console.log(this.properties);
}
write() {
const buffer = new ArrayBuffer(26);
const dataView = new DataView(buffer);
const sigBytes = new TextEncoder().encode(this.properties.Signature);
for (let i = 0; i < 4; i++) dataView.setUint8(i, sigBytes[i]);
dataView.setUint16(4, this.properties.Version, false);
this.properties.Reserved.forEach((b, i) => dataView.setUint8(6 + i, b));
dataView.setUint16(12, this.properties.Channels, false);
dataView.setUint32(14, this.properties.Height, false);
dataView.setUint32(18, this.properties.Width, false);
dataView.setUint16(22, this.properties.Depth, false);
dataView.setUint16(24, this.properties.ColorMode, false);
return buffer; // Return ArrayBuffer; can be saved as blob
}
}
// Example usage:
// const reader = new FileReader();
// reader.onload = (e) => {
// const psdc = new PSDCFile(e.target.result);
// psdc.printProperties();
// const newBuffer = psdc.write();
// // Save newBuffer as file
// };
// reader.readAsArrayBuffer(file);
Note: Write returns an ArrayBuffer for the header.
- C class for .PSDC file (using C++ for class support):
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <cstring>
class PSDCFile {
private:
char signature[5];
uint16_t version;
uint8_t reserved[6];
uint16_t channels;
uint32_t height;
uint32_t width;
uint16_t depth;
uint16_t colorMode;
public:
PSDCFile(const std::string& filename) {
read(filename);
}
void read(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file) {
throw std::runtime_error("Cannot open file");
}
char header[26];
file.read(header, 26);
if (file.gcount() < 26) {
throw std::runtime_error("File too small");
}
memcpy(signature, header, 4);
signature[4] = '\0';
version = *reinterpret_cast<uint16_t*>(header + 4);
memcpy(reserved, header + 6, 6);
channels = *reinterpret_cast<uint16_t*>(header + 12);
height = *reinterpret_cast<uint32_t*>(header + 14);
width = *reinterpret_cast<uint32_t*>(header + 18);
depth = *reinterpret_cast<uint16_t*>(header + 22);
colorMode = *reinterpret_cast<uint16_t*>(header + 24);
// Note: Big-endian swap if needed, assuming big-endian input
}
void printProperties() {
std::cout << "Signature: " << signature << std::endl;
std::cout << "Version: " << version << std::endl;
std::cout << "Reserved: ";
for (int i = 0; i < 6; ++i) std::cout << static_cast<int>(reserved[i]) << " ";
std::cout << std::endl;
std::cout << "Channels: " << channels << std::endl;
std::cout << "Height: " << height << std::endl;
std::cout << "Width: " << width << std::endl;
std::cout << "Depth: " << depth << std::endl;
std::cout << "ColorMode: " << colorMode << std::endl;
}
void write(const std::string& filename) {
std::ofstream file(filename, std::ios::binary);
if (!file) {
throw std::runtime_error("Cannot write file");
}
char header[26];
memcpy(header, signature, 4);
*reinterpret_cast<uint16_t*>(header + 4) = version;
memcpy(header + 6, reserved, 6);
*reinterpret_cast<uint16_t*>(header + 12) = channels;
*reinterpret_cast<uint32_t*>(header + 14) = height;
*reinterpret_cast<uint32_t*>(header + 18) = width;
*reinterpret_cast<uint16_t*>(header + 22) = depth;
*reinterpret_cast<uint16_t*>(header + 24) = colorMode;
file.write(header, 26);
}
};
// Example usage:
// int main() {
// try {
// PSDCFile psdc("example.psdc");
// psdc.printProperties();
// psdc.write("new.psdc");
// } catch (const std::exception& e) {
// std::cerr << e.what() << std::endl;
// }
// return 0;
// }
Note: Write method only handles the header; endianness assumed big-endian. Full implementation would require swapping if on little-endian systems.