Task 018: .AGR File Format

Task 018: .AGR File Format

File Format Specifications for the .AGR File Format

The .AGR file format serves as the project file for Grace (also known as xmgrace), an open-source WYSIWYG tool for generating two-dimensional plots of numerical data. This format is text-based and human-readable, consisting of batch commands that define plot configurations, graph settings, data sets, and visual elements. Each command typically begins with the "@" symbol, followed by parameters that specify properties such as graph types, axis scales, legends, and defaults. The format supports line-by-line parsing, with comments starting with "#". It is case-insensitive and allows for multiple statements per line separated by semicolons. The structure is hierarchical, encompassing global settings, graph-specific configurations, set definitions, and optional embedded data.

1. List of All Properties Intrinsic to This File Format

The properties represent configurable elements defined by the batch commands in the .AGR file. These are intrinsic to the format's structure and include global defaults, page layout, mappings, graph definitions, axis details, legends, frames, and set parameters. Based on the format specifications from the Grace User's Guide, the comprehensive list of properties (categorized for clarity) is as follows:

Version and Page Properties:

  • version (e.g., @version 50122)
  • page size (width, height)
  • page scroll (percentage)
  • page inout (percentage)
  • link page (on/off)

Font Mapping Properties:

  • map font (ID to "Font Name", "Alias")

Color Mapping Properties:

  • map color (ID to (R, G, B), "Name")

Date and Reference Properties:

  • reference date
  • date wrap (on/off)
  • date wrap year

Default Settings Properties:

  • default linewidth
  • default linestyle
  • default color
  • default pattern
  • default font
  • default char size
  • default symbol size
  • default sformat

Background and Timestamp Properties:

  • background color
  • page background fill (on/off)
  • timestamp (on/off, position, color, rotation, font, char size, default text)

Region Properties (per region, e.g., r0, r1):

  • on/off
  • link to graph
  • type (above, below, left, right, etc.)
  • linestyle
  • linewidth
  • color
  • line (coordinates)

Graph Properties (per graph, e.g., g0, g1):

  • on/off
  • hidden (true/false)
  • type (XY, XY Chart, Polar, Fixed, Pie)
  • stacked (true/false)
  • bar hgap
  • fixedpoint (on/off, type, xy, format, precision)

Graph-Specific World and View Properties:

  • world (xmin, ymin, xmax, ymax)
  • stack world (coordinates)
  • znorm
  • view (xmin, ymin, xmax, ymax)

Title and Subtitle Properties:

  • title (text)
  • title font
  • title size
  • title color
  • subtitle (text)
  • subtitle font
  • subtitle size
  • subtitle color

Axis Scale and Invert Properties:

  • xaxes scale (Normal, Logarithmic, Reciprocal)
  • yaxes scale (Normal, Logarithmic, Reciprocal)
  • xaxes invert (on/off)
  • yaxes invert (on/off)

Axis Properties (for xaxis, yaxis, altxaxis, altyaxis):

  • on/off
  • type zero (true/false)
  • offset (x, y)
  • bar (on/off, color, linestyle, linewidth)
  • label (text, layout, place, char size, font, color)
  • tick (on/off, major interval, minor ticks, default count, place rounded, in/out, major/minor size, color, linewidth, linestyle, grid on/off)
  • ticklabel (on/off, format, precision, formula, append, prepend, angle, skip, stagger, place, offset, start/stop type/value, char size, font, color)
  • tick place (both, normal, opposite)
  • tick spec type (none, both, labels, marks)

Legend Properties:

  • legend (on/off)
  • legend loctype (world/view)
  • legend (position)
  • legend box (color, pattern, linewidth, linestyle, fill color, fill pattern)
  • legend font
  • legend char size
  • legend color
  • legend length
  • legend vgap
  • legend hgap
  • legend invert (true/false)

Frame Properties:

  • frame type
  • frame linestyle
  • frame linewidth
  • frame color
  • frame pattern
  • frame background color
  • frame background pattern

Set Properties (per set, e.g., s0, s1):

  • type (XY, XYDX, XYDY, etc.)
  • active (on/off)
  • hidden (true/false)
  • symbol (type, size, color, pattern, fill color, fill pattern, linewidth, linestyle, char, char font, skip)
  • line (type, linestyle, linewidth, color, pattern)
  • baseline (type, on/off, color, pattern, linewidth, linestyle)
  • dropline (on/off)
  • fill (type, rule, color, pattern)
  • avalue (on/off, type, char size, font, color, rotation, format, precision, prepend, append, offset)
  • errorbar (on/off, place, size, color, pattern, linewidth, linestyle, riser clip, riser linewidth)
  • legend (text)

Data and Input/Output Properties:

  • target (graph.set)
  • data (embedded data lines)
  • read (file, settype)
  • read nxy
  • read block
  • block (data)
  • write (set, format, file)

Other Properties:

  • autoscale (onread, xaxes, yaxes, set)
  • autoticks
  • redraw
  • sleep
  • updateall
  • saveall
  • load
  • device (hardcopy settings)
  • objects (lines, boxes, ellipses, text strings with position, font, color, etc.)

These properties are not fixed in order and can be extended with custom commands or modules.

3. HTML/JavaScript for Drag-and-Drop .AGR File Dump

The following is a self-contained HTML page with embedded JavaScript that enables drag-and-drop functionality for a .AGR file. Upon dropping the file, it reads the content as text, parses lines starting with "@" as properties, and dumps them to the screen in a structured list.

.AGR File Property Dumper
Drag and drop a .AGR file here

4. Python Class for .AGR File Handling

The following Python class opens a .AGR file, reads and decodes (parses) the properties, prints them to the console, and supports writing a new or modified .AGR file.

import os

class AGRFileHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = []  # List of (command, value) tuples

    def read_and_decode(self):
        if not os.path.exists(self.filepath):
            raise FileNotFoundError(f"File {self.filepath} not found.")
        with open(self.filepath, 'r') as f:
            lines = f.readlines()
        self.properties = []
        for line in lines:
            line = line.strip()
            if line.startswith('@'):
                parts = line[1:].strip().split(maxsplit=1)
                command = parts[0]
                value = parts[1] if len(parts) > 1 else ''
                self.properties.append((command, value))

    def print_properties(self):
        if not self.properties:
            print("No properties loaded. Call read_and_decode() first.")
            return
        print("Properties from .AGR file:")
        for command, value in self.properties:
            print(f"{command}: {value}")

    def write(self, new_filepath=None):
        filepath = new_filepath or self.filepath
        with open(filepath, 'w') as f:
            for command, value in self.properties:
                f.write(f"@{command} {value}\n")
        print(f"File written to {filepath}")

# Example usage:
# handler = AGRFileHandler('example.agr')
# handler.read_and_decode()
# handler.print_properties()
# handler.write('modified.agr')

5. Java Class for .AGR File Handling

The following Java class opens a .AGR file, reads and decodes the properties, prints them to the console, and supports writing a new or modified .AGR file.

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class AGRFileHandler {
    private String filepath;
    private List<String[]> properties;  // List of [command, value]

    public AGRFileHandler(String filepath) {
        this.filepath = filepath;
        this.properties = new ArrayList<>();
    }

    public void readAndDecode() throws IOException {
        File file = new File(filepath);
        if (!file.exists()) {
            throw new FileNotFoundException("File " + filepath + " not found.");
        }
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            properties.clear();
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.startsWith("@")) {
                    String[] parts = line.substring(1).trim().split("\\s+", 2);
                    String command = parts[0];
                    String value = (parts.length > 1) ? parts[1] : "";
                    properties.add(new String[]{command, value});
                }
            }
        }
    }

    public void printProperties() {
        if (properties.isEmpty()) {
            System.out.println("No properties loaded. Call readAndDecode() first.");
            return;
        }
        System.out.println("Properties from .AGR file:");
        for (String[] prop : properties) {
            System.out.println(prop[0] + ": " + prop[1]);
        }
    }

    public void write(String newFilepath) throws IOException {
        String targetPath = (newFilepath != null) ? newFilepath : filepath;
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(targetPath))) {
            for (String[] prop : properties) {
                writer.write("@" + prop[0] + " " + prop[1] + "\n");
            }
        }
        System.out.println("File written to " + targetPath);
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     AGRFileHandler handler = new AGRFileHandler("example.agr");
    //     handler.readAndDecode();
    //     handler.printProperties();
    //     handler.write("modified.agr");
    // }
}

6. JavaScript Class for .AGR File Handling

The following JavaScript class (for Node.js) opens a .AGR file, reads and decodes the properties, prints them to the console, and supports writing a new or modified .AGR file.

const fs = require('fs');

class AGRFileHandler {
    constructor(filepath) {
        this.filepath = filepath;
        this.properties = [];  // Array of {command, value}
    }

    readAndDecode() {
        if (!fs.existsSync(this.filepath)) {
            throw new Error(`File ${this.filepath} not found.`);
        }
        const content = fs.readFileSync(this.filepath, 'utf8');
        const lines = content.split('\n');
        this.properties = [];
        lines.forEach(line => {
            line = line.trim();
            if (line.startsWith('@')) {
                const parts = line.slice(1).trim().split(/\s+/, 2);
                const command = parts[0];
                const value = parts[1] || '';
                this.properties.push({command, value});
            }
        });
    }

    printProperties() {
        if (this.properties.length === 0) {
            console.log('No properties loaded. Call readAndDecode() first.');
            return;
        }
        console.log('Properties from .AGR file:');
        this.properties.forEach(prop => {
            console.log(`${prop.command}: ${prop.value}`);
        });
    }

    write(newFilepath = null) {
        const targetPath = newFilepath || this.filepath;
        let output = '';
        this.properties.forEach(prop => {
            output += `@${prop.command} ${prop.value}\n`;
        });
        fs.writeFileSync(targetPath, output, 'utf8');
        console.log(`File written to ${targetPath}`);
    }
}

// Example usage:
// const handler = new AGRFileHandler('example.agr');
// handler.readAndDecode();
// handler.printProperties();
// handler.write('modified.agr');

7. C++ Class for .AGR File Handling

The following C++ class opens a .AGR file, reads and decodes the properties, prints them to the console, and supports writing a new or modified .AGR file.

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>

class AGRFileHandler {
private:
    std::string filepath;
    std::vector<std::pair<std::string, std::string>> properties;

public:
    AGRFileHandler(const std::string& fp) : filepath(fp) {}

    void readAndDecode() {
        std::ifstream file(filepath);
        if (!file.is_open()) {
            throw std::runtime_error("File " + filepath + " not found.");
        }
        properties.clear();
        std::string line;
        while (std::getline(file, line)) {
            std::istringstream iss(line);
            std::string token;
            iss >> token;
            if (token.starts_with('@')) {
                std::string command = token.substr(1);
                std::string value;
                std::getline(iss, value);
                value.erase(0, value.find_first_not_of(" \t"));  // Trim leading whitespace
                properties.emplace_back(command, value);
            }
        }
        file.close();
    }

    void printProperties() const {
        if (properties.empty()) {
            std::cout << "No properties loaded. Call readAndDecode() first." << std::endl;
            return;
        }
        std::cout << "Properties from .AGR file:" << std::endl;
        for (const auto& prop : properties) {
            std::cout << prop.first << ": " << prop.second << std::endl;
        }
    }

    void write(const std::string& newFilepath = "") const {
        std::string targetPath = newFilepath.empty() ? filepath : newFilepath;
        std::ofstream outFile(targetPath);
        if (!outFile.is_open()) {
            throw std::runtime_error("Unable to write to " + targetPath);
        }
        for (const auto& prop : properties) {
            outFile << "@" << prop.first << " " << prop.second << std::endl;
        }
        outFile.close();
        std::cout << "File written to " << targetPath << std::endl;
    }
};

// Example usage:
// int main() {
//     try {
//         AGRFileHandler handler("example.agr");
//         handler.readAndDecode();
//         handler.printProperties();
//         handler.write("modified.agr");
//     } catch (const std::exception& e) {
//         std::cerr << e.what() << std::endl;
//     }
//     return 0;
// }