2025/11/10

JavaFX Markdown Viewer

在 Java 處理 Markdown 文件,可使用 flexmark 套件,而 JavaFX,是使用 openjfx,因為 jfx 並不在 openjdk 裡面。

這邊因為使用舊版的 macos,所以使用很舊版本的openjfx

        <!-- markdown -->
        <dependency>
            <groupId>com.vladsch.flexmark</groupId>
            <artifactId>flexmark-all</artifactId>
            <version>0.64.8</version>
        </dependency>

        <!-- java FX -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>17.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>17.0.2</version>
        </dependency>

pom.xml下面的 plugin 這樣寫

    <build>
        <plugins>
            <!-- JavaFX Maven Plugin -->
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <configuration>
                    <mainClass>MarkdownViewerApp</mainClass>
                </configuration>
            </plugin>

            <!-- Build fat JAR -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.7.1</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>${main.class}</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <!-- Java compiler -->
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.14.0</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

MarkdownTable

在 flexmark,要處理 table 時,需要加入 extension,以下是一個處理 Markdown Table 的 sample

import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.util.data.MutableDataSet;
import com.vladsch.flexmark.ext.tables.TablesExtension;

import java.util.Arrays;

public class MarkdownTableExample {
    public static void main(String[] args) {
        String markdown =
                "| Name  | Age |\n" +
                "|-------|-----|\n" +
                "| John  | 30  |\n" +
                "| Alice | 25  |";

        MutableDataSet options = new MutableDataSet();
        options.set(Parser.EXTENSIONS, Arrays.asList(TablesExtension.create()));

        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();

        String html = renderer.render(parser.parse(markdown));
        System.out.println(html);
    }
}

執行結果

<table>
<thead>
<tr><th>Name</th><th>Age</th></tr>
</thead>
<tbody>
<tr><td>John</td><td>30</td></tr>
<tr><td>Alice</td><td>25</td></tr>
</tbody>
</table>


Process finished with exit code 0

MarkdownViewer

html table 加上 css

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.util.data.MutableDataSet;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Scanner;

public class MarkdownViewerApp extends Application {

    private MutableDataSet options;
    private Parser parser;
    private HtmlRenderer renderer;

    @Override
    public void start(Stage primaryStage) {
        options = new MutableDataSet();
        options.set(Parser.EXTENSIONS, Arrays.asList(TablesExtension.create()));
        parser = Parser.builder(options).build();
        renderer = HtmlRenderer.builder(options).build();

        // CSS
        String css = """
                body {
                    font-family: sans-serif;
                    margin: 20px;
                    line-height: 1.6;
                }
                table {
                    border-collapse: collapse;
                    width: 100%;
                }
                th, td {
                    border: 1px solid #ccc;
                    padding: 6px 10px;
                }
                th {
                    background-color: #f0f0f0;
                }
                """;

        TextArea markdownInput = new TextArea();
        WebView htmlView = new WebView();

        // Default text
//        markdownInput.setText("# Hello Markdown\n\nThis is **bold** and this is _italic_.");
        try (InputStream in = getClass().getResourceAsStream("/test.md")) {
            if (in != null) {
                Scanner scanner = new Scanner(in, StandardCharsets.UTF_8).useDelimiter("\\A");
                String htmlContent = scanner.hasNext() ? scanner.next() : "";

                markdownInput.setText(htmlContent);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Convert and display Markdown as HTML
        markdownInput.textProperty().addListener((obs, oldVal, newVal) -> {
            Node document = parser.parse(newVal);
            String htmlBody = renderer.render(document);
            String html = """
                <html>
                <head>
                <style>%s</style>
                </head>
                <body>
                    %s
                </body>
                </html>
            """.formatted(css, htmlBody);
            htmlView.getEngine().loadContent(html);
        });

        // Initial render
        Node document = parser.parse(markdownInput.getText());
        String htmlBody = renderer.render(document);
        String html = """
                <html>
                <head>
                <style>%s</style>
                </head>
                <body>
                    %s
                </body>
                </html>
            """.formatted(css, htmlBody);
        htmlView.getEngine().loadContent(html);

        BorderPane root = new BorderPane();
        root.setLeft(markdownInput);
        root.setCenter(htmlView);

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setTitle("JavaFX Markdown Viewer");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

可直接用 mvn 啟動

mvn javafx:run

獨立執行需下載 openjfx sdk,可到 JavaFX - Gluon 下載,openjfx-17.0.2_osx-x64_bin-sdk.zip

執行 mvn package 可產生 jar,然後就能這樣啟動

java --module-path ~/Downloads/javafx-sdk-17.0.2/lib/ --add-modules javafx.controls,javafx.web -jar target/markdownviewer-1.0-SNAPSHOT-jar-with-dependencies.jar MarkdownViewerApp

這是執行結果

沒有留言:

張貼留言