aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp87
-rw-r--r--src/markdown_translator.cpp264
-rw-r--r--src/markdown_translator.h35
3 files changed, 386 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..caee08c
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,87 @@
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <unordered_map>
+#include "markdown_translator.h"
+
+bool parseArguments(int argc, char* argv[], std::unordered_map<std::string, std::string>& params, std::string& inputFile) {
+ if (argc < 2) {
+ std::cerr << "Usage: COMMAND <input_file> [-o <output_file>] [-css <css_file_path>] [other options]\n";
+ return false;
+ }
+ params["-o"] = "index.html";
+ params["-css"] = "styles/ffxiv-style.css";
+ for (int i = 1; i < argc; ++i) {
+ std::string arg = argv[i];
+ if (arg[0] == '-') {
+ if (i + 1 < argc) {
+ params[arg] = argv[i + 1];
+ ++i;
+ } else {
+ std::cerr << "Error: Missing value for option " << arg << "\n";
+ return false;
+ }
+ } else {
+ if (inputFile.empty()) {
+ inputFile = arg;
+ } else {
+ std::cerr << "Error: Multiple input files specified: " << inputFile << " and " << arg << "\n";
+ return false;
+ }
+ }
+ }
+
+ if (inputFile.empty()) {
+ std::cerr << "Error: No input file specified\n";
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ std::unordered_map<std::string, std::string> params;
+ std::string inputFile;
+
+ if (!parseArguments(argc, argv, params, inputFile)) {
+ return 1;
+ }
+
+ std::ifstream file(inputFile);
+ if (!file) {
+ std::cerr << "Error: Could not open file " << inputFile << "\n";
+ return 1;
+ }
+
+ // Read entire file content
+ std::stringstream buffer;
+ buffer << file.rdbuf();
+ std::string markdownContent = buffer.str();
+ file.close();
+
+ // Create translator and convert markdown to HTML
+ MarkdownTranslator translator;
+
+ // Get CSS path from parameters or use default
+ std::string cssPath = "styles/ffxiv-style.css";
+ if (params.find("-css") != params.end()) {
+ cssPath = params["-css"];
+ }
+
+ std::string htmlOutput = translator.translate(markdownContent, cssPath, "XIV Lore");
+
+ // Write to output file
+ std::string outputFile = params["-o"];
+ std::ofstream outFile(outputFile);
+ if (!outFile) {
+ std::cerr << "Error: Could not open output file " << outputFile << "\n";
+ return 1;
+ }
+
+ outFile << htmlOutput;
+ outFile.close();
+
+ std::cout << "HTML output written to " << outputFile << std::endl;
+ return 0;
+}
diff --git a/src/markdown_translator.cpp b/src/markdown_translator.cpp
new file mode 100644
index 0000000..d362f82
--- /dev/null
+++ b/src/markdown_translator.cpp
@@ -0,0 +1,264 @@
+#include "markdown_translator.h"
+#include <regex>
+#include <iostream>
+#include <algorithm>
+
+MarkdownTranslator::MarkdownTranslator(){
+}
+
+MarkdownTranslator::~MarkdownTranslator() {
+}
+
+std::string MarkdownTranslator::translate(const std::string& markdownContent, const std::string& cssPath, const std::string& title) {
+ std::stringstream htmlOutput;
+ std::stringstream markdownStream(markdownContent);
+ std::string line;
+ std::vector<std::string> headers;
+
+ std::string currentLine;
+
+
+ while (std::getline(markdownStream, currentLine)) {
+ std::regex headerRegex("^(#{1,3})\\s+(.*)$");
+ std::smatch matches;
+ if (std::regex_match(currentLine, matches, headerRegex)) {
+ int level = matches[1].length();
+ std::string content = matches[2];
+ if (level <= 3) {
+ headers.push_back(std::to_string(level) + ":" + content);
+ }
+ }
+ }
+
+ // Reset the stream to start over
+ markdownStream.clear();
+ markdownStream.str(markdownContent);
+
+ // Start with basic HTML structure
+ htmlOutput << "<!DOCTYPE html>\n";
+ htmlOutput << "<html lang=\"en\">\n";
+ htmlOutput << "<head>\n";
+ htmlOutput << " <meta charset=\"UTF-8\">\n";
+ htmlOutput << " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n";
+ htmlOutput << " <title>" + title + "</title>\n";
+ htmlOutput << " <link rel=\"stylesheet\" href=\"" << cssPath << "\">\n";
+ htmlOutput << "</head>\n";
+ htmlOutput << "<body>\n";
+
+ // Add navigation sidebar
+ generateSideBar(htmlOutput, headers, title);
+
+ // Main content container
+ htmlOutput << " <div class=\"main-content\">\n";
+ htmlOutput << " <div class=\"container\">\n";
+
+ // Process each line of markdown
+
+ // State of current parse
+ bool inFigureBlock{false};
+ std::vector<std::string> figureLines;
+
+ while (std::getline(markdownStream, line)) {
+ if(line.find(":::figure") == 0){
+ inFigureBlock = true;
+ figureLines.clear();
+ continue;
+ }
+
+ if(inFigureBlock && line == ":::"){
+ inFigureBlock = false;
+ htmlOutput << " " << processFigureBlock(figureLines) << "\n";
+ continue;
+ }
+ else if(inFigureBlock){
+ figureLines.push_back(line);
+ continue;
+ }
+
+ htmlOutput << " " << processLine(line);
+ }
+
+ // Add article meta information section
+ htmlOutput << " <div class=\"article-meta\">\n";
+ htmlOutput << " <p>Last updated: " << getCurrentDateTime() << "</p>\n";
+ htmlOutput << " </div>\n";
+
+ htmlOutput << " </div>\n";
+ htmlOutput << " </div>\n";
+ htmlOutput << "</body>\n";
+ htmlOutput << "</html>\n";
+ return htmlOutput.str();
+}
+
+void MarkdownTranslator::generateSideBar(std::stringstream& output, const std::vector<std::string>& headers, const std::string& title) {
+ output << " <div class=\"nav-sidebar\">\n";
+ output << " <div class=\"nav-logo\">\n";
+ output << " <h3>" + title + "</h3>\n";
+ output << " </div>\n";
+ output << " <ul class=\"nav-menu\">\n";
+
+ output << " <h4>Table of Contents</h4>\n";
+ output << " <li><a href=\"index.html\">Home</a></li>\n";
+ output << " <li><a href=\"#\">Recent Changes</a></li>\n";
+ output << " <li><a href=\"#\">Random Page</a></li>\n";
+ output << " <ul class=\"nav-submenu\">\n";
+
+ // Generate navigation from headers
+ for (const auto& header : headers) {
+ size_t separatorPos = header.find(':');
+ if (separatorPos != std::string::npos) {
+ int level = std::stoi(header.substr(0, separatorPos));
+ std::string content = header.substr(separatorPos + 1);
+
+ // Create anchor ID from header content
+ std::string anchorId = createAnchorId(content);
+
+ // Add indentation based on header level
+ std::string indentation = "";
+ for (int i = 1; i < level; ++i) {
+ indentation += " ";
+ }
+
+ output << " " << indentation << "<li><a href=\"#" << anchorId << "\">" << content << "</a></li>\n";
+ }
+ }
+
+ output << " </ul>\n";
+ output << " </ul>\n";
+ output << " </div>\n";
+}
+
+std::string MarkdownTranslator::createAnchorId(const std::string& text) {
+ std::string id = text;
+ // Convert to lowercase
+ std::transform(id.begin(), id.end(), id.begin(), ::tolower);
+
+ // Replace spaces with hyphens
+ std::replace(id.begin(), id.end(), ' ', '-');
+
+ // Remove any non-alphanumeric characters except hyphens
+ id.erase(
+ std::remove_if(
+ id.begin(),
+ id.end(),
+ [](char c) { return !(std::isalnum(c) || c == '-'); }
+ ),
+ id.end()
+ );
+
+ return id;
+}
+
+std::string MarkdownTranslator::getCurrentDateTime() {
+ std::time_t now = std::time(nullptr);
+ std::tm* localTime = std::localtime(&now);
+
+ char buffer[80];
+ std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localTime);
+
+ return std::string(buffer);
+}
+
+std::string MarkdownTranslator::processLine(const std::string& line) {
+ std::string processed = line;
+
+ // Check for headers first (they start at beginning of line)
+ processed = processHeaders(processed);
+
+ // If it wasn't a header, process inline elements
+ if (processed == line) {
+ processed = processBold(processed);
+ processed = processItalic(processed);
+ processed = processLinks(processed);
+
+ // Wrap in paragraph tags if it's regular text
+ if (!processed.empty() && processed[0] != '<') {
+ processed = processParagraph(processed);
+ }
+ }
+
+ return processed + "\n";
+}
+
+std::string MarkdownTranslator::processHeaders(const std::string& line) {
+ // Check for H1-H6
+ std::regex headerRegex("^(#{1,6})\\s+(.*)$");
+ std::smatch matches;
+ if (std::regex_match(line, matches, headerRegex)) {
+ int level = matches[1].length();
+ std::string content = matches[2];
+ std::string anchorId = createAnchorId(content);
+ return "<h" + std::to_string(level) + " id=\"" + anchorId + "\">" + content + "</h" + std::to_string(level) + ">";
+ }
+
+ return line;
+}
+
+std::string MarkdownTranslator::processBold(const std::string& text) {
+ // Replace **text** or __text__ with <strong>text</strong>
+ std::string result = text;
+ std::regex boldRegex("\\*\\*([^\\*]+)\\*\\*|__([^_]+)__");
+ result = std::regex_replace(result, boldRegex, "<strong>$1$2</strong>");
+ return result;
+}
+
+std::string MarkdownTranslator::processItalic(const std::string& text) {
+ // Replace *text* or _text_ with <em>text</em>
+ std::string result = text;
+ std::regex italicRegex("\\*([^\\*]+)\\*|_([^_]+)_");
+ result = std::regex_replace(result, italicRegex, "<em>$1$2</em>");
+ return result;
+}
+
+std::string MarkdownTranslator::processLinks(const std::string& text) {
+ // Replace [text](url) with <a href="url">text</a>
+ std::string result = text;
+ std::regex linkRegex("\\[([^\\]]+)\\]\\(([^\\)]+)\\)");
+ result = std::regex_replace(result, linkRegex, "<a href=\"$2\">$1</a>");
+ return result;
+}
+
+std::string MarkdownTranslator::processParagraph(const std::string& text) {
+ if (text.empty()) return "";
+ return "<p>" + text + "</p>";
+}
+
+std::string MarkdownTranslator::processSingleFigure(const std::string& text) {
+ // Extract image details using regex
+ std::regex imageRegex("!\\[(.*?)\\]\\(([^\\s\"]+)(\\s+\"(.*?)\")?\\)");
+ std::smatch matches;
+
+ if (std::regex_search(text, matches, imageRegex)) {
+ std::string alt = matches[1].str();
+ std::string src = matches[2].str();
+ std::string title = matches.size() > 4 && matches[4].matched ? " title=\"" + matches[4].str() + "\"" : "";
+ // Create the HTML for the image
+ return "<img src=\"" + src + "\" alt=\"" + alt + "\"" + title + ">";
+ }
+ return text;
+}
+
+std::string MarkdownTranslator::processFigureBlock(const std::vector<std::string>& lines){
+ std::stringstream html;
+ std::string imageHtml;
+ std::string caption;
+ html << "<figure class=\"custom-figure\">\n";
+
+ // Process each line in the figure block
+ for (const auto& line : lines) {
+ if (line.find("![") == 0) {
+ // Process the image
+ imageHtml = processSingleFigure(line);
+ html << " " << imageHtml << "\n";
+ } else if (line.find("Caption:") == 0) {
+ // Extract caption
+ caption = line.substr(8);
+ html << " <figcaption>" << caption << "</figcaption>\n";
+ } else if (!line.empty()) {
+ // Process any other content
+ html << " <p>" << line << "</p>\n";
+ }
+ }
+ html << "</figure>";
+ return html.str();
+}
diff --git a/src/markdown_translator.h b/src/markdown_translator.h
new file mode 100644
index 0000000..6d24ac7
--- /dev/null
+++ b/src/markdown_translator.h
@@ -0,0 +1,35 @@
+#ifndef MARKDOWN_TRANSLATOR_H
+#define MARKDOWN_TRANSLATOR_H
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <ctime>
+
+class MarkdownTranslator {
+public:
+ // Constructor
+ MarkdownTranslator();
+ // Destructor
+ ~MarkdownTranslator();
+ // Main translation function - takes markdown content and returns HTML
+ std::string translate(const std::string& markdownContent, const std::string& cssPath = "styles/ffxiv-style.css", const std::string& title = "Title");
+ std::string processLine(const std::string& line);
+
+private:
+ // Helper functions for different markdown elements
+ std::string processHeaders(const std::string& line);
+ std::string processBold(const std::string& text);
+ std::string processItalic(const std::string& text);
+ std::string processLinks(const std::string& text);
+ std::string processParagraph(const std::string& text);
+ std::string processSingleFigure(const std::string& text);
+ std::string processFigureBlock(const std::vector<std::string>& lines);
+ // Navigation and table of contents
+ void generateSideBar(std::stringstream& output, const std::vector<std::string>& headers, const std::string& title);
+ std::string createAnchorId(const std::string& text);
+ // Utility functions
+ std::string getCurrentDateTime();
+};
+
+#endif // MARKDOWN_TRANSLATOR_H
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage