Task 615: .RMD File Format

Task 615: .RMD File Format

  1. The .RMD file format refers to R Markdown files (.Rmd), which are plain text files combining Markdown syntax, YAML metadata, and embedded code chunks (primarily for R). Based on the specifications, the key properties intrinsic to the file format (primarily from the YAML header, as these are the extractable metadata fields defining the document's structure and behavior) are:
  • title
  • subtitle
  • author
  • date
  • fontsize
  • abstract
  • keywords
  • subject
  • description
  • category
  • lang
  • geometry
  • documentclass
  • classoption
  • linestretch
  • indent
  • papersize
  • header-includes
  • bibliography
  • csl
  • citation_package
  • params
  • toc-title
  • output (including sub-properties like toc, toc_depth, toc_float, number_sections, code_folding, theme, highlight, css, fig_width, fig_height, fig_caption, df_print, self_contained, keep_md, keep_tex, latex_engine, template, md_extensions, includes, pandoc_args)

These properties are part of the YAML header at the top of the file, delimited by --- lines. The rest of the file consists of Markdown content and code chunks, but the properties listed are the metadata fields.

  1. Two direct download links for .RMD files:
  1. Here is an embedded HTML/JavaScript snippet for a simple web page (e.g., for a Ghost blog or any HTML context) that allows drag-and-drop of a .RMD file and dumps the properties to the screen. It uses js-yaml library for YAML parsing (include via CDN). Drop a .RMD file onto the designated area.
RMD Property Dumper
Drag and drop .RMD file here

    

  1. Python class for handling .RMD files:
import yaml
import os

class RMDHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}
        self.content = ""
        self.yaml_header = ""

    def read(self):
        if not os.path.exists(self.filepath):
            raise FileNotFoundError(f"File {self.filepath} not found.")
        with open(self.filepath, 'r', encoding='utf-8') as f:
            self.content = f.read()
        # Extract YAML header
        lines = self.content.splitlines()
        if lines and lines[0].strip() == '---':
            end = next(i for i, line in enumerate(lines[1:], 1) if line.strip() == '---')
            self.yaml_header = '\n'.join(lines[1:end])
            self.properties = yaml.safe_load(self.yaml_header) or {}
        else:
            print("No YAML header found.")

    def print_properties(self):
        if not self.properties:
            print("No properties loaded.")
            return
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write(self, new_properties=None):
        if new_properties:
            self.properties.update(new_properties)
        new_yaml = yaml.safe_dump(self.properties, sort_keys=False)
        new_content = f"---\n{new_yaml}---\n" + self.content.split('---', 2)[2] if '---' in self.content else self.content
        with open(self.filepath, 'w', encoding='utf-8') as f:
            f.write(new_content)
        print(f"File written to {self.filepath}")

# Example usage:
# handler = RMDHandler('sample.Rmd')
# handler.read()
# handler.print_properties()
# handler.write({'new_key': 'new_value'})
  1. Java class for handling .RMD files (using SnakeYAML for YAML parsing; assume it's in classpath):
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RMDHandler {
    private String filepath;
    private Map<String, Object> properties;
    private String content;
    private String yamlHeader;

    public RMDHandler(String filepath) {
        this.filepath = filepath;
    }

    public void read() throws IOException {
        content = new String(Files.readAllBytes(Paths.get(filepath)), "UTF-8");
        Pattern pattern = Pattern.compile("---\\s*([\\s\\S]*?)\\s*---");
        Matcher matcher = pattern.matcher(content);
        if (matcher.find()) {
            yamlHeader = matcher.group(1);
            Yaml yaml = new Yaml();
            properties = yaml.load(yamlHeader);
        } else {
            System.out.println("No YAML header found.");
        }
    }

    public void printProperties() {
        if (properties == null || properties.isEmpty()) {
            System.out.println("No properties loaded.");
            return;
        }
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }

    public void write(Map<String, Object> newProperties) throws IOException {
        if (newProperties != null) {
            properties.putAll(newProperties);
        }
        Yaml yaml = new Yaml();
        String newYaml = yaml.dump(properties);
        String newContent = content.replaceFirst("---\\s*[\\s\\S]*?\\s*---", "---\n" + newYaml + "---");
        try (FileWriter writer = new FileWriter(filepath)) {
            writer.write(newContent);
        }
        System.out.println("File written to " + filepath);
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     RMDHandler handler = new RMDHandler("sample.Rmd");
    //     handler.read();
    //     handler.printProperties();
    //     handler.write(Map.of("new_key", "new_value"));
    // }
}
  1. JavaScript class for handling .RMD files (using js-yaml; for Node.js environment):
const fs = require('fs');
const yaml = require('js-yaml');

class RMDHandler {
    constructor(filepath) {
        this.filepath = filepath;
        this.properties = {};
        this.content = '';
        this.yamlHeader = '';
    }

    read() {
        if (!fs.existsSync(this.filepath)) {
            throw new Error(`File ${this.filepath} not found.`);
        }
        this.content = fs.readFileSync(this.filepath, 'utf-8');
        const match = this.content.match(/---\s*([\s\S]*?)\s*---/);
        if (match && match[1]) {
            this.yamlHeader = match[1];
            this.properties = yaml.load(this.yamlHeader) || {};
        } else {
            console.log('No YAML header found.');
        }
    }

    printProperties() {
        if (Object.keys(this.properties).length === 0) {
            console.log('No properties loaded.');
            return;
        }
        for (const [key, value] of Object.entries(this.properties)) {
            console.log(`${key}: ${JSON.stringify(value)}`);
        }
    }

    write(newProperties = {}) {
        Object.assign(this.properties, newProperties);
        const newYaml = yaml.dump(this.properties);
        const newContent = this.content.replace(/---\s*[\s\S]*?\s*---/, `---\n${newYaml}---`);
        fs.writeFileSync(this.filepath, newContent, 'utf-8');
        console.log(`File written to ${this.filepath}`);
    }
}

// Example usage:
// const handler = new RMDHandler('sample.Rmd');
// handler.read();
// handler.printProperties();
// handler.write({ new_key: 'new_value' });
  1. C++ class for handling .RMD files (simple YAML parsing is manual/regex-based as C++ stdlib lacks YAML; for full parsing, use external lib like yaml-cpp, but here's a basic version assuming key-value lines):
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <regex>

class RMDHandler {
private:
    std::string filepath;
    std::map<std::string, std::string> properties;
    std::string content;
    std::string yamlHeader;

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

    void read() {
        std::ifstream file(filepath);
        if (!file.is_open()) {
            std::cerr << "File " << filepath << " not found." << std::endl;
            return;
        }
        std::string line;
        content = "";
        while (std::getline(file, line)) {
            content += line + "\n";
        }
        file.close();

        std::regex yamlRegex(R"(---\s*([\s\S]*?)\s*---)");
        std::smatch match;
        if (std::regex_search(content, match, yamlRegex) && match.size() > 1) {
            yamlHeader = match[1].str();
            // Simple parsing: assume key: value lines
            std::istringstream iss(yamlHeader);
            while (std::getline(iss, line)) {
                size_t colonPos = line.find(':');
                if (colonPos != std::string::npos) {
                    std::string key = line.substr(0, colonPos);
                    std::string value = line.substr(colonPos + 1);
                    // Trim spaces
                    key.erase(0, key.find_first_not_of(" \t"));
                    key.erase(key.find_last_not_of(" \t") + 1);
                    value.erase(0, value.find_first_not_of(" \t"));
                    value.erase(value.find_last_not_of(" \t") + 1);
                    properties[key] = value;
                }
            }
        } else {
            std::cout << "No YAML header found." << std::endl;
        }
    }

    void printProperties() {
        if (properties.empty()) {
            std::cout << "No properties loaded." << std::endl;
            return;
        }
        for (const auto& pair : properties) {
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
    }

    void write(const std::map<std::string, std::string>& newProperties) {
        for (const auto& pair : newProperties) {
            properties[pair.first] = pair.second;
        }
        std::string newYaml = "";
        for (const auto& pair : properties) {
            newYaml += pair.first + ": " + pair.second + "\n";
        }
        std::regex replaceRegex(R"(---\s*[\s\S]*?\s*---)");
        std::string newContent = std::regex_replace(content, replaceRegex, "---\n" + newYaml + "---");
        std::ofstream outFile(filepath);
        if (outFile.is_open()) {
            outFile << newContent;
            outFile.close();
            std::cout << "File written to " << filepath << std::endl;
        } else {
            std::cerr << "Unable to write file." << std::endl;
        }
    }
};

// Example usage:
// int main() {
//     RMDHandler handler("sample.Rmd");
//     handler.read();
//     handler.printProperties();
//     std::map<std::string, std::string> newProps = {{"new_key", "new_value"}};
//     handler.write(newProps);
//     return 0;
// }