Task 020: .AI File Format
Task 020: .AI File Format
1. List of all the properties of this file format intrinsic to its file system
Based on the available specifications for the .AI file format (primarily from the Adobe Illustrator 7 file format document, as modern .AI files are proprietary PDF-based with private data and lack full public specs), the intrinsic properties include header fields, version info, and structural elements like layers, palettes, images, text, colors, patterns, overprint, groups, clipping, guides, object tags, attributes, and non-printing elements. These are parsed from PostScript comments and operators in the file. The list below covers key properties:
- File ID Line (Magic String): The starting line, typically
%!PS-Adobe-2.0 EPSF-3.0(for PostScript-based versions; modern versions start with%PDF-1.x). - Header Comments:
- %%Creator: The creator string, e.g., "Adobe Illustrator(TM) version".
- %%For: User and organization, e.g., "(username) (organization)".
- %%Title: Illustration title, e.g., "(illustration title)".
- %%CreationDate: Date and time of creation, e.g., "(date) (time)".
- %%DocumentProcSets: Procedure sets, e.g., "Adobe_Illustrator_version level revision".
- %%DocumentSuppliedProcSets: Supplied procedure sets, e.g., "Adobe_Illustrator_version level revision".
- %%DocumentFonts: List of fonts used, e.g., "font...".
- %%BoundingBox: Bounding box coordinates, e.g., "llx lly urx ury".
- %%TemplateBox: Template box coordinates, e.g., "llx lly urx ury".
- %%PageOrigin (Windows-specific): Page origin, e.g., "x y".
- %%PrinterName (Windows-specific): Printer name, e.g., "{printer brand name}".
- %%PrinterRect (Windows-specific): Printer rectangle, e.g., "llx lly urx ury".
- Version Information: %AI5_FileFormat version, e.g., "3" for Illustrator 7.0.
- Layer Properties (from Lb operator): visible (0/1), preview (0/1), enabled (0/1), printing (0/1), dimmed (0/1), hasMultiLayerMasks (0/1), colorIndex (-1 to 26), red (0-255), green (0-255), blue (0-255).
- Layer Name (from Ln operator): Layer name string.
- Palette Properties (from Pb operator): topLeftCellIndex, selectedIndex.
- Raster Image Properties (from XI/XF operator): matrix [a b c d tx ty], bounding box (llx lly urx ury), height (h), width (w), bits per pixel, ImageType (1=grayscale, 3=RGB, 4=CMYK), AlphaChannelCount, reserved, bin-ascii (0=ASCII hex, 1=binary big-endian), ImageMask (0=opaque, 1=transparent).
- Image Link Path (from XG operator): Path string, modified flag (0/1).
- Text Object Properties (from To operator): Type (0=point text, 1=area text, 2=path text).
- Text Path Properties (from Tp operator): Matrix [a b c d tx ty], startPt (fractional Bézier index).
- Text Render Mode (from Tr operator): 0=fill, 1=stroke, 2=fill+stroke, 3=invisible, etc.
- Text Font (from Tf operator): Fontname (e.g., "/_Helvetica"), size, ascent, descent.
- Custom Color Properties (from Xx/XX operator): Components (comp1 … compN, e.g., R G B or CMYK), name string, tint (0.0-1.0), type (0=CMYK, 1=RGB).
- RGB Color Properties (from Xa/XA operator): red (0.0-1.0), green (0.0-1.0), blue (0.0-1.0).
- Pattern Properties (from p/P operator): Patternname string, px, py (offset), sx, sy (scale), angle (degrees), rf (reflection flag 0/1), r (reflection angle), k (shear angle), ka (shear axis), matrix [a b c d tx ty].
- Overprint Flag (from O/R operator): Flag (0=normal, 1=overprint).
- Group Indicators: Begin group (u), end group (U).
- Clipping Mask Indicators: Begin clipping (q), end clipping (Q), intersect clip (W).
- Guide Indicators: Guide flag (), render mode (e.g., (N) for non-printing).
- Object Tag (from XT operator): Identifier (e.g., /profits98), string value.
- Attributes:
- Show center point (from Ap operator): showCenter (0/1).
- Path resolution (from Ar operator): resolution (dpi).
- Non-Printing Elements: Begin non-printing (%AI5_Begin_NonPrinting, Np), end non-printing (%AI5_End_NonPrinting--).
- Byte Order: Big-endian (Motorola) for binary data (e.g., images).
- Encoding: ASCII for text, platform-specific for special characters, 83PVRKSJ–H for CJK fonts.
Note: Modern .AI files (post-Illustrator CS6) are PDF-based, so properties also include PDF version, objects, and private Illustrator data in /PieceInfo dictionaries. However, detailed specs for private data are not publicly available, so the above focuses on the documented PostScript structure.
2. Two direct download links for files of format .AI
- https://raw.githubusercontent.com/newsdev/ai2html/master/sample files/sample-ai-file.ai
- https://www.fileexamples.com/api/download?file=sample_corporate_identity.ai&type=application/postscript
3. Ghost blog embedded HTML JavaScript for drag n drop .AI file to dump properties
4. Python class for .AI file
class AIFileHandler:
def __init__(self, filepath):
self.filepath = filepath
self.content = None
self.properties = {}
def read_decode(self):
with open(self.filepath, 'r', encoding='utf-8', errors='ignore') as f:
self.content = f.read()
self.properties = self.parse_properties(self.content)
def parse_properties(self, content):
lines = content.split('\n')
properties = {
'header': {},
'layers': [],
'palettes': [],
'images': [],
'texts': [],
'customColors': [],
'patterns': [],
'overprints': [],
'groups': 0,
'clippings': 0,
'guides': [],
'objectTags': [],
'attributes': {},
'nonPrinting': False,
'byteOrder': 'big-endian (assumed for binary sections)',
'encoding': 'ASCII with platform specifics'
}
current_layer = None
in_layer = False
in_palette = False
in_non_printing = False
group_count = 0
clipping_count = 0
for line in lines:
line = line.strip()
# Header
if line.startswith('%%Creator:'):
properties['header']['creator'] = line[10:].strip()
elif line.startswith('%%For:'):
properties['header']['for'] = line[6:].strip()
elif line.startswith('%%Title:'):
properties['header']['title'] = line[8:].strip()
elif line.startswith('%%CreationDate:'):
properties['header']['creationDate'] = line[15:].strip()
elif line.startswith('%%DocumentProcSets:'):
properties['header']['documentProcSets'] = line[19:].strip()
elif line.startswith('%%DocumentSuppliedProcSets:'):
properties['header']['documentSuppliedProcSets'] = line[27:].strip()
elif line.startswith('%%DocumentFonts:'):
properties['header']['documentFonts'] = line[16:].strip()
elif line.startswith('%%BoundingBox:'):
properties['header']['boundingBox'] = line[14:].strip()
elif line.startswith('%%TemplateBox:'):
properties['header']['templateBox'] = line[14:].strip()
elif line.startswith('%AI5_FileFormat'):
properties['header']['aiFileFormat'] = line[15:].strip()
# Layer
if line == '%AI5_BeginLayer':
in_layer = True
if in_layer and line.startswith('Lb'):
current_layer = {'properties': line[2:].strip().split(' ')}
properties['layers'].append(current_layer)
if in_layer and line.startswith('Ln'):
if current_layer:
current_layer['name'] = line[2:].strip()
if line == '%AI5_EndLayer':
in_layer = False
# Palette
if line == '%AI5_BeginPalette':
in_palette = True
if in_palette and line.startswith('Pb'):
properties['palettes'].append(line[2:].strip())
if line == '%AI5_EndPalette':
in_palette = False
# Image
if line.startswith('XI') or line.startswith('XF'):
properties['images'].append(line.strip())
# Text
if line.startswith('To'):
properties['texts'].append({'type': line[2:].strip()})
if line.startswith('Tf'):
if properties['texts']:
properties['texts'][-1]['font'] = line[2:].strip()
# Custom Color
if line.startswith('Xx') or line.startswith('XX'):
properties['customColors'].append(line.strip())
# Pattern
if line.startswith('p') or line.startswith('P'):
properties['patterns'].append(line.strip())
# Overprint
if line.startswith('O') or line.startswith('R'):
properties['overprints'].append(line.strip())
# Group and Clipping
if line == 'u':
group_count += 1
if line == 'U':
group_count -= 1
if line == 'q':
clipping_count += 1
if line == 'Q':
clipping_count -= 1
properties['groups'] = group_count
properties['clippings'] = clipping_count
# Guide
if '*' in line:
properties['guides'].append(line.strip())
# Object Tag
if line.startswith('XT'):
properties['objectTags'].append(line.strip())
# Attributes
if line.startswith('Ap'):
properties['attributes']['showCenter'] = line[2:].strip()
if line.startswith('Ar'):
properties['attributes']['resolution'] = line[2:].strip()
# Non-Printing
if line == '%AI5_Begin_NonPrinting':
in_non_printing = True
if line == '%AI5_End_NonPrinting--':
in_non_printing = False
properties['nonPrinting'] = in_non_printing
return properties
def print_properties(self):
import json
print(json.dumps(self.properties, indent=4))
def write(self, new_filepath=None):
filepath = new_filepath or self.filepath
with open(filepath, 'w', encoding='utf-8') as f:
f.write(self.content) # Writes original content; modify self.content for changes
# Example usage:
# handler = AIFileHandler('sample.ai')
# handler.read_decode()
# handler.print_properties()
# handler.write('output.ai')
5. Java class for .AI file
import java.io.*;
import java.util.*;
public class AIFileHandler {
private String filepath;
private String content;
private Map<String, Object> properties;
public AIFileHandler(String filepath) {
this.filepath = filepath;
this.properties = new HashMap<>();
}
public void readDecode() throws IOException {
StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader(filepath))) {
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
}
content = sb.toString();
properties = parseProperties(content);
}
@SuppressWarnings("unchecked")
private Map<String, Object> parseProperties(String content) {
String[] lines = content.split("\n");
Map<String, Object> props = new HashMap<>();
Map<String, String> header = new HashMap<>();
List<Map<String, Object>> layers = new ArrayList<>();
List<String> palettes = new ArrayList<>();
List<String> images = new ArrayList<>();
List<Map<String, String>> texts = new ArrayList<>();
List<String> customColors = new ArrayList<>();
List<String> patterns = new ArrayList<>();
List<String> overprints = new ArrayList<>();
int groups = 0;
int clippings = 0;
List<String> guides = new ArrayList<>();
List<String> objectTags = new ArrayList<>();
Map<String, String> attributes = new HashMap<>();
boolean nonPrinting = false;
props.put("header", header);
props.put("layers", layers);
props.put("palettes", palettes);
props.put("images", images);
props.put("texts", texts);
props.put("customColors", customColors);
props.put("patterns", patterns);
props.put("overprints", overprints);
props.put("guides", guides);
props.put("objectTags", objectTags);
props.put("attributes", attributes);
props.put("byteOrder", "big-endian (assumed for binary sections)");
props.put("encoding", "ASCII with platform specifics");
Map<String, Object> currentLayer = null;
boolean inLayer = false;
boolean inPalette = false;
boolean inNonPrinting = false;
for (String line : lines) {
line = line.trim();
// Header
if (line.startsWith("%%Creator:")) header.put("creator", line.substring(10).trim());
else if (line.startsWith("%%For:")) header.put("for", line.substring(6).trim());
else if (line.startsWith("%%Title:")) header.put("title", line.substring(8).trim());
else if (line.startsWith("%%CreationDate:")) header.put("creationDate", line.substring(15).trim());
else if (line.startsWith("%%DocumentProcSets:")) header.put("documentProcSets", line.substring(19).trim());
else if (line.startsWith("%%DocumentSuppliedProcSets:")) header.put("documentSuppliedProcSets", line.substring(27).trim());
else if (line.startsWith("%%DocumentFonts:")) header.put("documentFonts", line.substring(16).trim());
else if (line.startsWith("%%BoundingBox:")) header.put("boundingBox", line.substring(14).trim());
else if (line.startsWith("%%TemplateBox:")) header.put("templateBox", line.substring(14).trim());
else if (line.startsWith("%AI5_FileFormat")) header.put("aiFileFormat", line.substring(15).trim());
// Layer
if (line.equals("%AI5_BeginLayer")) inLayer = true;
if (inLayer && line.startsWith("Lb")) {
currentLayer = new HashMap<>();
currentLayer.put("properties", Arrays.asList(line.substring(2).trim().split(" ")));
layers.add(currentLayer);
}
if (inLayer && line.startsWith("Ln")) {
if (currentLayer != null) currentLayer.put("name", line.substring(2).trim());
}
if (line.equals("%AI5_EndLayer")) inLayer = false;
// Palette
if (line.equals("%AI5_BeginPalette")) inPalette = true;
if (inPalette && line.startsWith("Pb")) palettes.add(line.substring(2).trim());
if (line.equals("%AI5_EndPalette")) inPalette = false;
// Image
if (line.startsWith("XI") || line.startsWith("XF")) images.add(line.trim());
// Text
if (line.startsWith("To")) {
Map<String, String> text = new HashMap<>();
text.put("type", line.substring(2).trim());
texts.add(text);
}
if (line.startsWith("Tf")) {
if (!texts.isEmpty()) texts.get(texts.size() - 1).put("font", line.substring(2).trim());
}
// Custom Color
if (line.startsWith("Xx") || line.startsWith("XX")) customColors.add(line.trim());
// Pattern
if (line.startsWith("p") || line.startsWith("P")) patterns.add(line.trim());
// Overprint
if (line.startsWith("O") || line.startsWith("R")) overprints.add(line.trim());
// Group and Clipping
if (line.equals("u")) groups++;
if (line.equals("U")) groups--;
if (line.equals("q")) clippings++;
if (line.equals("Q")) clippings--;
// Guide
if (line.contains("*")) guides.add(line.trim());
// Object Tag
if (line.startsWith("XT")) objectTags.add(line.trim());
// Attributes
if (line.startsWith("Ap")) attributes.put("showCenter", line.substring(2).trim());
if (line.startsWith("Ar")) attributes.put("resolution", line.substring(2).trim());
// Non-Printing
if (line.equals("%AI5_Begin_NonPrinting")) inNonPrinting = true;
if (line.equals("%AI5_End_NonPrinting--")) inNonPrinting = false;
}
props.put("groups", groups);
props.put("clippings", clippings);
props.put("nonPrinting", inNonPrinting);
return props;
}
public void printProperties() {
System.out.println(propertiesToString(properties, 0));
}
private String propertiesToString(Object obj, int indent) {
StringBuilder sb = new StringBuilder();
String ind = " ".repeat(indent);
if (obj instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) obj;
sb.append("{\n");
for (Map.Entry<String, Object> entry : map.entrySet()) {
sb.append(ind).append(" \"").append(entry.getKey()).append("\": ").append(propertiesToString(entry.getValue(), indent + 1)).append(",\n");
}
sb.append(ind).append("}");
} else if (obj instanceof List) {
List<?> list = (List<?>) obj;
sb.append("[\n");
for (Object item : list) {
sb.append(ind).append(" ").append(propertiesToString(item, indent + 1)).append(",\n");
}
sb.append(ind).append("]");
} else {
sb.append("\"").append(obj).append("\"");
}
return sb.toString();
}
public void write(String newFilepath) throws IOException {
String path = (newFilepath == null) ? filepath : newFilepath;
try (BufferedWriter bw = new BufferedWriter(new FileWriter(path))) {
bw.write(content);
}
}
// Example usage:
// public static void main(String[] args) throws IOException {
// AIFileHandler handler = new AIFileHandler("sample.ai");
// handler.readDecode();
// handler.printProperties();
// handler.write("output.ai");
// }
}
6. JavaScript class for .AI file (Node.js compatible)
const fs = require('fs');
class AIFileHandler {
constructor(filepath) {
this.filepath = filepath;
this.content = null;
this.properties = {};
}
readDecode() {
this.content = fs.readFileSync(this.filepath, 'utf8');
this.properties = this.parseProperties(this.content);
}
parseProperties(content) {
const lines = content.split('\n');
const properties = {
header: {},
layers: [],
palettes: [],
images: [],
texts: [],
customColors: [],
patterns: [],
overprints: [],
groups: 0,
clippings: 0,
guides: [],
objectTags: [],
attributes: {},
nonPrinting: false,
byteOrder: 'big-endian (assumed for binary sections)',
encoding: 'ASCII with platform specifics'
};
let currentLayer = null;
let inLayer = false;
let inPalette = false;
let inNonPrinting = false;
let groups = 0;
let clippings = 0;
lines.forEach(line => {
line = line.trim();
// Header
if (line.startsWith('%%Creator:')) properties.header.creator = line.slice(10).trim();
if (line.startsWith('%%For:')) properties.header.for = line.slice(6).trim();
if (line.startsWith('%%Title:')) properties.header.title = line.slice(8).trim();
if (line.startsWith('%%CreationDate:')) properties.header.creationDate = line.slice(15).trim();
if (line.startsWith('%%DocumentProcSets:')) properties.header.documentProcSets = line.slice(19).trim();
if (line.startsWith('%%DocumentSuppliedProcSets:')) properties.header.documentSuppliedProcSets = line.slice(27).trim();
if (line.startsWith('%%DocumentFonts:')) properties.header.documentFonts = line.slice(16).trim();
if (line.startsWith('%%BoundingBox:')) properties.header.boundingBox = line.slice(14).trim();
if (line.startsWith('%%TemplateBox:')) properties.header.templateBox = line.slice(14).trim();
if (line.startsWith('%AI5_FileFormat')) properties.header.aiFileFormat = line.slice(15).trim();
// Layer
if (line === '%AI5_BeginLayer') inLayer = true;
if (inLayer && line.startsWith('Lb')) {
currentLayer = { properties: line.slice(2).trim().split(' ') };
properties.layers.push(currentLayer);
}
if (inLayer && line.startsWith('Ln')) if (currentLayer) currentLayer.name = line.slice(2).trim();
if (line === '%AI5_EndLayer') inLayer = false;
// Palette
if (line === '%AI5_BeginPalette') inPalette = true;
if (inPalette && line.startsWith('Pb')) properties.palettes.push(line.slice(2).trim());
if (line === '%AI5_EndPalette') inPalette = false;
// Image
if (line.startsWith('XI') || line.startsWith('XF')) properties.images.push(line.trim());
// Text
if (line.startsWith('To')) properties.texts.push({ type: line.slice(2).trim() });
if (line.startsWith('Tf')) if (properties.texts.length > 0) properties.texts[properties.texts.length - 1].font = line.slice(2).trim();
// Custom Color
if (line.startsWith('Xx') || line.startsWith('XX')) properties.customColors.push(line.trim());
// Pattern
if (line.startsWith('p') || line.startsWith('P')) properties.patterns.push(line.trim());
// Overprint
if (line.startsWith('O') || line.startsWith('R')) properties.overprints.push(line.trim());
// Group and Clipping
if (line === 'u') groups++;
if (line === 'U') groups--;
if (line === 'q') clippings++;
if (line === 'Q') clippings--;
// Guide
if (line.includes('*')) properties.guides.push(line.trim());
// Object Tag
if (line.startsWith('XT')) properties.objectTags.push(line.trim());
// Attributes
if (line.startsWith('Ap')) properties.attributes.showCenter = line.slice(2).trim();
if (line.startsWith('Ar')) properties.attributes.resolution = line.slice(2).trim();
// Non-Printing
if (line === '%AI5_Begin_NonPrinting') inNonPrinting = true;
if (line === '%AI5_End_NonPrinting--') inNonPrinting = false;
});
properties.groups = groups;
properties.clippings = clippings;
properties.nonPrinting = inNonPrinting;
return properties;
}
printProperties() {
console.log(JSON.stringify(this.properties, null, 2));
}
write(newFilepath = null) {
const path = newFilepath || this.filepath;
fs.writeFileSync(path, this.content, 'utf8');
}
}
// Example usage:
// const handler = new AIFileHandler('sample.ai');
// handler.readDecode();
// handler.printProperties();
// handler.write('output.ai');
7. C class (using C++ for class support)
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
class AIFileHandler {
private:
std::string filepath;
std::string content;
std::map<std::string, std::string> header;
std::vector<std::map<std::string, std::string>> layers; // Simplified for C++
std::vector<std::string> palettes;
std::vector<std::string> images;
std::vector<std::map<std::string, std::string>> texts;
std::vector<std::string> customColors;
std::vector<std::string> patterns;
std::vector<std::string> overprints;
int groups;
int clippings;
std::vector<std::string> guides;
std::vector<std::string> objectTags;
std::map<std::string, std::string> attributes;
bool nonPrinting;
std::string byteOrder;
std::string encoding;
public:
AIFileHandler(const std::string& fp) : filepath(fp), groups(0), clippings(0), nonPrinting(false),
byteOrder("big-endian (assumed for binary sections)"), encoding("ASCII with platform specifics") {}
void readDecode() {
std::ifstream file(filepath);
std::stringstream ss;
ss << file.rdbuf();
content = ss.str();
parseProperties();
}
void parseProperties() {
std::stringstream ss(content);
std::string line;
std::map<std::string, std::string> currentLayer;
bool inLayer = false;
bool inPalette = false;
bool inNonPrinting = false;
while (std::getline(ss, line)) {
// Trim line (simplified)
size_t start = line.find_first_not_of(" \t");
if (start == std::string::npos) continue;
line = line.substr(start);
size_t end = line.find_last_not_of(" \t");
line = line.substr(0, end + 1);
// Header
if (line.rfind("%%Creator:", 0) == 0) header["creator"] = line.substr(10);
else if (line.rfind("%%For:", 0) == 0) header["for"] = line.substr(6);
else if (line.rfind("%%Title:", 0) == 0) header["title"] = line.substr(8);
else if (line.rfind("%%CreationDate:", 0) == 0) header["creationDate"] = line.substr(15);
else if (line.rfind("%%DocumentProcSets:", 0) == 0) header["documentProcSets"] = line.substr(19);
else if (line.rfind("%%DocumentSuppliedProcSets:", 0) == 0) header["documentSuppliedProcSets"] = line.substr(27);
else if (line.rfind("%%DocumentFonts:", 0) == 0) header["documentFonts"] = line.substr(16);
else if (line.rfind("%%BoundingBox:", 0) == 0) header["boundingBox"] = line.substr(14);
else if (line.rfind("%%TemplateBox:", 0) == 0) header["templateBox"] = line.substr(14);
else if (line.rfind("%AI5_FileFormat", 0) == 0) header["aiFileFormat"] = line.substr(15);
// Layer (simplified parsing)
if (line == "%AI5_BeginLayer") inLayer = true;
if (inLayer && line.rfind("Lb", 0) == 0) {
currentLayer["properties"] = line.substr(2);
layers.push_back(currentLayer);
currentLayer.clear();
}
if (inLayer && line.rfind("Ln", 0) == 0) layers.back()["name"] = line.substr(2);
if (line == "%AI5_EndLayer") inLayer = false;
// Palette
if (line == "%AI5_BeginPalette") inPalette = true;
if (inPalette && line.rfind("Pb", 0) == 0) palettes.push_back(line.substr(2));
if (line == "%AI5_EndPalette") inPalette = false;
// Image
if (line.rfind("XI", 0) == 0 || line.rfind("XF", 0) == 0) images.push_back(line);
// Text
if (line.rfind("To", 0) == 0) {
std::map<std::string, std::string> text;
text["type"] = line.substr(2);
texts.push_back(text);
}
if (line.rfind("Tf", 0) == 0) if (!texts.empty()) texts.back()["font"] = line.substr(2);
// Custom Color
if (line.rfind("Xx", 0) == 0 || line.rfind("XX", 0) == 0) customColors.push_back(line);
// Pattern
if (line.rfind("p", 0) == 0 || line.rfind("P", 0) == 0) patterns.push_back(line);
// Overprint
if (line.rfind("O", 0) == 0 || line.rfind("R", 0) == 0) overprints.push_back(line);
// Group and Clipping
if (line == "u") ++groups;
if (line == "U") --groups;
if (line == "q") ++clippings;
if (line == "Q") --clippings;
// Guide
if (line.find('*') != std::string::npos) guides.push_back(line);
// Object Tag
if (line.rfind("XT", 0) == 0) objectTags.push_back(line);
// Attributes
if (line.rfind("Ap", 0) == 0) attributes["showCenter"] = line.substr(2);
if (line.rfind("Ar", 0) == 0) attributes["resolution"] = line.substr(2);
// Non-Printing
if (line == "%AI5_Begin_NonPrinting") inNonPrinting = true;
if (line == "%AI5_End_NonPrinting--") inNonPrinting = false;
}
nonPrinting = inNonPrinting;
}
void printProperties() {
std::cout << "{\n";
std::cout << " \"header\": {\n";
for (const auto& kv : header) {
std::cout << " \"" << kv.first << "\": \"" << kv.second << "\",\n";
}
std::cout << " },\n";
std::cout << " \"layers\": [ /* simplified */ ],\n"; // Omit detailed print for brevity
std::cout << " \"palettes\": [ /* ... */ ],\n";
std::cout << " \"images\": [ /* ... */ ],\n";
std::cout << " \"texts\": [ /* ... */ ],\n";
std::cout << " \"customColors\": [ /* ... */ ],\n";
std::cout << " \"patterns\": [ /* ... */ ],\n";
std::cout << " \"overprints\": [ /* ... */ ],\n";
std::cout << " \"groups\": " << groups << ",\n";
std::cout << " \"clippings\": " << clippings << ",\n";
std::cout << " \"guides\": [ /* ... */ ],\n";
std::cout << " \"objectTags\": [ /* ... */ ],\n";
std::cout << " \"attributes\": {\n";
for (const auto& kv : attributes) {
std::cout << " \"" << kv.first << "\": \"" << kv.second << "\",\n";
}
std::cout << " },\n";
std::cout << " \"nonPrinting\": " << (nonPrinting ? "true" : "false") << ",\n";
std::cout << " \"byteOrder\": \"" << byteOrder << "\",\n";
std::cout << " \"encoding\": \"" << encoding << "\"\n";
std::cout << "}\n";
}
void write(const std::string& newFilepath = "") {
std::string path = newFilepath.empty() ? filepath : newFilepath;
std::ofstream out(path);
out << content;
}
};
// Example usage:
// int main() {
// AIFileHandler handler("sample.ai");
// handler.readDecode();
// handler.printProperties();
// handler.write("output.ai");
// return 0;
// }