Doxgen的安装与使用!
Doxgen的安装与使用
背景:使用CCS开发DSP工程时,当随着工程逐渐增大,代码的可读性时逐渐下降的。此时如何能够快速的了解代码工程就显得尤为重要。如果能够解决这个问题,或许也能够约束代码编写时的格式不统一的问题。提升代码的编写质量和可读性。
参考:
- Eclipse Doxygen Installation also Code Composer Studio v5.x (coder-tronics.com) 
- callgraph工具, 函数调用关系 | cflow, egypt 和 doxygen 使用 - whilewell - 博客园 (cnblogs.com) 
- 相关插件工具: 
实现:
- 首先参考了第一条的安装过程,先下载并安装了doxygen(1.12.0)软件,并且在CCS的市场中安装了插件eclox(0.13.0) 
- 然后使用对应插件,并配置对应的doxygen工具的安装目录,使用插件建立得到一份doxygen.file文件 
- 编译boxygen文件得到对应的html文件 
- 安装Graphviz工具,手动配置对应工具的bin文件夹路径,然后指定生成图片的格式,深度等细节,再次编译doxygen文件。 
- 重新查看对应的html文件,可以看到对应的说明文件已经更新了插图。(SVG格式比PNG格式要清晰) 
参考
- 能根据代码给出函数调用关系的工具 - 有一些工具可以分析代码并生成函数调用关系图,帮助你理解代码的结构和函数之间的依赖关系。以下是几种常用的工具: - 1. Doxygen- 支持语言:C/C++、Java、Python 等多种语言。 
- 功能:Doxygen 是一个自动生成文档的工具,可以分析代码并生成函数调用关系图。它可以输出多种格式,包括 HTML、LaTeX 和 XML。如果你启用 - CALL_GRAPH 和- CALLER_GRAPH 选项,它还能为每个函数生成调用者和被调用者的图。
- 使用方法: - 编写配置文件 - Doxyfile。
- 在 - Doxyfile 中启用- CALL_GRAPH = YES 和- CALLER_GRAPH = YES。
- 运行 Doxygen,它会生成包含函数调用关系的文档。 
 
 - 2. Gprof- 支持语言:C/C++。 
- 功能:Gprof 是一个性能分析工具,能生成程序的函数调用图。它主要用于检测程序的性能瓶颈,但也能展示函数之间的调用关系。 
- 使用方法: - 编译程序时加上 - -pg 选项生成可执行文件。
- 运行程序,生成 - gmon.out 文件。
- 使用 - gprof 分析- gmon.out 文件,查看函数调用关系。
 
 - 3. Cscope- 支持语言:C/C++。 
- 功能:Cscope 是一个交互式的代码浏览工具,可以查找函数的定义、调用、全局变量等信息,适合分析大规模的 C/C++ 项目。 
- 使用方法: - 生成索引文件: - cscope -R。
- 在 Cscope 的交互界面中查找函数调用关系。 
 
 - 4. Code2flow- 支持语言:Python、JavaScript、Ruby、PHP 等。 
- 功能:Code2flow 是一个简单的工具,可以从代码中生成流程图和调用图,适用于不需要深度分析的小项目。 
- 使用方法: - 安装 - code2flow。
- 运行 - code2flow your_file.py,工具会生成相应的调用图。
 
 - 5. Callgrind (结合 KCachegrind 使用)- 支持语言:C/C++。 
- 功能:Callgrind 是 Valgrind 的一个工具,专门用于分析函数调用关系和性能。生成的报告可以用 KCachegrind 可视化,展示函数调用的详细图谱。 
- 使用方法: - 使用 - valgrind --tool=callgrind ./your_program 运行程序。
- 生成的 - callgrind.out 文件可以使用- kcachegrind 查看调用关系。
 
 - 这些工具各有特点,适合不同语言和场景的需求。如果你是在做 C/C++ 项目,可以考虑 Doxygen 和 Gprof。如果是其他语言,Code2flow 和 Cscope 也是不错的选择。你可以根据具体情况选择合适的工具。 
 
- Doxygen生成函数调用图 - Doxygen 是一个非常强大的文档生成工具,它不仅可以为代码生成文档,还可以生成函数调用图。要生成函数调用图,必须正确配置 Doxygen。以下是如何使用 Doxygen 生成函数调用图的详细步骤: - 1. 安装 Doxygen- 首先,你需要确保 Doxygen 已安装在你的系统中。你可以通过以下方式进行安装: - Windows:可以从 Doxygen官网 下载可执行安装文件。 
- Linux:通过包管理器安装,例如: - sudo apt-get install doxygen graphviz
- macOS:使用 Homebrew 安装: - brew install doxygen graphviz- Graphviz 是一个用于生成图的工具,Doxygen 生成调用图时依赖于它。因此,你还需要安装 Graphviz。 
 - 2. 配置 Doxygen- Doxygen 通过配置文件 - Doxyfile 来控制生成文档的行为。你可以通过以下方式生成该文件:- doxygen -g- 这将创建一个名为 - Doxyfile 的默认配置文件。- 3. 修改- Doxyfile 配置文件- 打开 - Doxyfile,并根据需要修改以下选项以启用函数调用图的生成:- 启用调用图和调用者图: - CALL_GRAPH = YES # 生成函数的调用图(显示被哪些函数调用) CALLER_GRAPH = YES # 生成函数的调用者图(显示调用了哪些函数)
- 启用图形输出格式: - HAVE_DOT = YES # 确保 Graphviz 的 dot 工具可用- 如果 - HAVE_DOT 被设置为- NO,则需要将其更改为- YES 以支持图形生成。
- 图形显示样式(可选): - DOT_IMAGE_FORMAT = svg # 可以选择生成的图形格式,如 png 或 svg- SVG 格式通常质量更好并支持放大缩小操作。 
- 限制图的深度(可选): 
 如果你的项目很大,可以设置调用图的最大深度以避免生成过于复杂的图:- MAX_DOT_GRAPH_DEPTH = 2 # 控制生成图的最大深度
 - 4. 生成文档- 配置好 - Doxyfile 后,在项目根目录下运行以下命令生成文档:- doxygen Doxyfile- Doxygen 将自动分析你的源代码,并生成包含函数调用图的 HTML 文档。 - 5. 查看生成的函数调用图- 在 Doxygen 生成的文档目录中,打开 - index.html 文件,通过浏览器访问。如果正确配置,点击某个函数时,它的调用图和调用者图将会显示在页面上。- 调用图 (Call Graph):显示当前函数调用了哪些其他函数。 
- 调用者图 (Caller Graph):显示哪些函数调用了当前函数。 
 - 6. 优化图形显示(可选)- 调整图的布局: - DOT_GRAPH_MAX_NODES = 50 # 控制图形中的最大节点数,防止图形过于复杂 DOT_TRANSPARENT = YES # 使图形背景透明(更易嵌入到网页)
- 函数聚类: 
 如果你想让相关函数聚集在一起,可以启用函数聚类选项:- DOT_CLUSTERING = YES
 - 7. 示例调用图- 如果配置成功,生成的调用图可能看起来像这样: - +--------+ +----------+ +---------+ | func A | -->| func B | -->| func C | +--------+ +----------+ +---------+- 8. 调试常见问题- 调用图未生成:确保 - HAVE_DOT 设置为- YES 并且系统中安装了 Graphviz。
- 生成的图为空或不完整:检查 - MAX_DOT_GRAPH_DEPTH 设置,增加图的深度。
- 图形显示不清晰:尝试将 - DOT_IMAGE_FORMAT 设置为- svg 或其他矢量图格式。
 - 通过以上步骤,你可以生成函数的调用关系图,帮助你更好地理解项目中的函数调用逻辑。 
 
实例
enum Colors {
    RED = 1,    /**< Represents the color red */
    GREEN = 2,  /**< Represents the color green */
    BLUE = 3    /**< Represents the color blue */
};
/**
 * @brief Adds two integers.
 *
 * @details This function uses simple addition and assumes the input integers are valid.
 * Special care is taken to avoid overflow by ensuring the sum stays within a valid range.
 *
 * @note This function does not check for integer overflow.
 *
 * @param a First integer
 * @param b Second integer
 * @return The sum of a and b
 */
int add1(int a, int b) {
    //! This variable stores the sum of a and b
    int result = 0; /**< The accumulated sum */
    // Add integers with overflow check
    if ((a > 0 && b > 0 && a > INT_MAX - b) || (a < 0 && b < 0 && a < INT_MIN - b)) {
        // Handle overflow case
        return -1; //!< Return error code if overflow occurs
    }
    result = a + b; //!< Perform the addition
    return result; //!< Return the result
}
int add2(int a, int b) {
    int result = 0; /**< The variable that stores the sum of a and b */
    result += a; /**< Add the value of a to the result */
    result += b; /**< Add the value of b to the result */
    return result; /**< Return the calculated sum */
}
/**
 * @brief Adds two integers.
 *
 * This function takes two integers and returns their sum.
 *
 * @param a First integer
 * @param b Second integer
 * @return The sum of a and b
 */
int add3(int a, int b) {
    //! Initialize the result variable to zero.
    int result = 0;
    //! Add the first integer to the result.
    result += a;
    //! Add the second integer to the result.
    result += b;
    //! Return the final result.
    return result;
}
/**
 * @brief Adds two integers.
 *
 * @details
 * This function uses the following local variables:
 * - `result`: Stores the sum of the two input integers.
 *
 * @param a First integer
 * @param b Second integer
 * @return The sum of a and b
 */
int add4(int a, int b) {
    int result = 0; // Local variable to store the sum
    result += a;
    result += b;
    return result;
}
/**
 * @brief Computes the average of an integer array.
 *
 * This function takes an array of integers and its size as input, and returns
 * the average of the array elements. It handles cases where the array is empty.
 *
 * @details
 * The function iterates over the array to compute the sum of its elements.
 * If the array is empty, it returns 0 to avoid division by zero.
 * 
 * Local variables used in this function:
 * - `sum`: Accumulates the sum of array elements.
 * - `i`: Loop counter for iterating through the array.
 *
 * Example usage:
 * @code
 * int arr[] = {1, 2, 3, 4, 5};
 * double avg = compute_average(arr, 5);
 * printf("Average: %f\n", avg);
 * @endcode
 *
 * @param arr The input array of integers.
 * @param size The number of elements in the array.
 * @return The average of the array elements. If the array is empty, returns 0.
 * 
 * @note The return value is of type `double` to allow for decimal averages.
 */
double compute_average(const int* arr, size_t size) {
    //! Sum of the array elements
    int sum = 0;
    //! Loop counter to iterate through the array
    size_t i;
    // If the array is empty, return 0 to avoid division by zero
    if (size == 0) {
        return 0.0;
    }
    // Sum all elements in the array
    for (i = 0; i < size; ++i) {
        sum += arr[i];
    }
    //! Return the computed average as a double
    return (double)sum / size;
}
/**
 * @brief Adds two integers.
 *
 * @details This function uses simple addition and assumes the input integers are valid.
 * Special care is taken to avoid overflow by ensuring the sum stays within a valid range.
 *
 * @note This function does not check for integer overflow.
 *
 * @param a First integer
 * @param b Second integer
 * @return The sum of a and b
 */
int add6(int a, int b) {
    int result = 0; /**< The accumulated sum */
    // Add integers with overflow check
    if ((a > 0 && b > 0 && a > INT_MAX - b) || (a < 0 && b < 0 && a < INT_MIN - b)) {
        // Handle overflow case
        return -1; //!< Return error code if overflow occurs
    }
    result = a + b; //!< Perform the addition
    return result; //!< Return the result
}
int add7(int a, int b) {
    int result = 0; /**< The variable that stores the sum of a and b */
    result += a; /**< Add the value of a to the result */
    result += b; /**< Add the value of b to the result */
    return result; /**< Return the calculated sum */
}
/**
 * @defgroup GeometricShapes Geometric Shape Calculations
 * @brief This module handles the calculations of areas for geometric shapes like circles, squares, and triangles.
 *
 * The functions in this module compute the areas of various geometric shapes by using their respective formulas.
 * @{
 */
/**
 * @brief Initializes the system to compute geometric areas.
 *
 * This function can be used to set up any necessary initial values before computing the areas of shapes.
 */
void init_area_computation(void) {
    shape_count = 0;  // Initialize shape counter
}
/** @} */  // End of GeometricShapes group
/**
 * @brief Computes the area of a circle.
 *
 * This function takes a pointer to a `Circle` structure and calculates its area using the formula:
 * @f$ Area = \pi \times radius^2 @f$.
 * 
 * @f[
 * Area = \pi \times radius^2
 * @f]
 *
 * @param[in] circle A pointer to the `Circle` structure containing the radius.
 * @return The area of the circle.
 */
double compute_circle_area(const Circle* circle) {
    return PI * circle->radius * circle->radius;
}
/**
 * @file example.h
 * @brief This file contains definitions, function prototypes, and data structures
 *        for a simple system that computes geometric shapes' areas.
 * 
 * This file demonstrates how to use Doxygen to document a C header file.
 * It includes macros, structures, function prototypes, and enumeration types.
 * 
 * @author John Doe
 * @date 2024-10-11
 */
/**
 * @def PI
 * @brief The mathematical constant Pi (3.14159...).
 *
 * This macro defines the value of Pi, which is used in geometric calculations.
 */
#define PI 3.141592653589793
/**
 * @enum ShapeType
 * @brief Defines the types of geometric shapes.
 *
 * This enumeration is used to specify the type of shape, 
 * such as a circle, square, or triangle.
 */
typedef enum {
    SHAPE_CIRCLE,   /**< Circle shape */
    SHAPE_SQUARE,   /**< Square shape */
    SHAPE_TRIANGLE  /**< Triangle shape */
} ShapeType;
/**
 * @struct Triangle
 * @brief Represents a triangle with a base and height.
 *
 * This structure stores the base and height of a triangle for area calculations.
 */
typedef struct {
    double base;   /**< The base length of the triangle */
    double height; /**< The height of the triangle */
} Triangle;
/**
 * @struct Circle
 * @brief Represents a circle with a radius.
 *
 * This structure is used to store the radius of a circle for area calculations.
 */
typedef struct {
    double radius; /**< The radius of the circle */
} Circle;
/**
 * @struct Square
 * @brief Represents a square with a side length.
 *
 * This structure stores the side length of a square for area calculations.
 */
typedef struct {
    double side_length; /**< The length of one side of the square */
} Square;
/**
 * @var shape_count
 * @brief Keeps track of the number of shapes processed.
 *
 * This global variable is incremented every time a new shape's area is computed.
 */
int shape_count = 0;
/**
 * @brief Computes the area of a square.
 *
 * This function calculates the area of a square based on its side length.
 *
 * @param[in] square A pointer to the `Square` structure containing the side length.
 * @return The area of the square.
 */
double compute_square_area(const Square* square) {
    return square->side_length * square->side_length;
}
/**
 * @brief Computes the area of a triangle.
 *
 * This function computes the area of a triangle using the formula:
 * @f$ Area = \frac{1}{2} \times base \times height @f$.
 *
 * @param[in] triangle A pointer to the `Triangle` structure containing the base and height.
 * @return The area of the triangle.
 */
double compute_triangle_area(const Triangle* triangle) {
    return 0.5 * triangle->base * triangle->height;
}
在使用 Doxygen 生成文档时,函数内的注释可以通过特定的标记来实现。Doxygen 支持在函数内部添加详细的说明,解释代码逻辑、变量用途或步骤等。这些注释将显示在生成的文档中,帮助开发者理解函数的实现细节。
以下是几种常见的函数内注释方式:
1. 使用 //! 或 ///
可以在函数体内直接使用 //! 或 /// 注释每个逻辑部分。例如:
好的,下面是一个全面的 C 语言项目注释示例,包含了常见的 Doxygen 注释用法,涵盖文件、函数、变量、结构体、枚举、宏定义、以及组(modules)等内容。这样你可以在实际项目中参考使用,确保所有重要元素都得到充分注释和文档化。
### 文件注释
```c
/**
 * @file example.h
 * @brief This file contains definitions, function prototypes, and data structures
 *        for a simple system that computes geometric shapes' areas.
 * 
 * This file demonstrates how to use Doxygen to document a C header file.
 * It includes macros, structures, function prototypes, and enumeration types.
 * 
 * @author John Doe
 * @date 2024-10-11
 */
宏定义注释
/**
 * @def PI
 * @brief The mathematical constant Pi (3.14159...).
 *
 * This macro defines the value of Pi, which is used in geometric calculations.
 */
#define PI 3.141592653589793
枚举类型注释
/**
 * @enum ShapeType
 * @brief Defines the types of geometric shapes.
 *
 * This enumeration is used to specify the type of shape, 
 * such as a circle, square, or triangle.
 */
typedef enum {
    SHAPE_CIRCLE,   /**< Circle shape */
    SHAPE_SQUARE,   /**< Square shape */
    SHAPE_TRIANGLE  /**< Triangle shape */
} ShapeType;
结构体注释
/**
 * @struct Circle
 * @brief Represents a circle with a radius.
 *
 * This structure is used to store the radius of a circle for area calculations.
 */
typedef struct {
    double radius; /**< The radius of the circle */
} Circle;
/**
 * @struct Square
 * @brief Represents a square with a side length.
 *
 * This structure stores the side length of a square for area calculations.
 */
typedef struct {
    double side_length; /**< The length of one side of the square */
} Square;
/**
 * @struct Triangle
 * @brief Represents a triangle with a base and height.
 *
 * This structure stores the base and height of a triangle for area calculations.
 */
typedef struct {
    double base;   /**< The base length of the triangle */
    double height; /**< The height of the triangle */
} Triangle;
全局变量注释
/**
 * @var shape_count
 * @brief Keeps track of the number of shapes processed.
 *
 * This global variable is incremented every time a new shape's area is computed.
 */
int shape_count = 0;
函数注释
/**
 * @brief Computes the area of a circle.
 *
 * This function takes a pointer to a `Circle` structure and calculates its area using the formula:
 * @f$ Area = \pi \times radius^2 @f$.
 *
 * @param[in] circle A pointer to the `Circle` structure containing the radius.
 * @return The area of the circle.
 */
double compute_circle_area(const Circle* circle) {
    return PI * circle->radius * circle->radius;
}
/**
 * @brief Computes the area of a square.
 *
 * This function calculates the area of a square based on its side length.
 *
 * @param[in] square A pointer to the `Square` structure containing the side length.
 * @return The area of the square.
 */
double compute_square_area(const Square* square) {
    return square->side_length * square->side_length;
}
/**
 * @brief Computes the area of a triangle.
 *
 * This function computes the area of a triangle using the formula:
 * @f$ Area = \frac{1}{2} \times base \times height @f$.
 *
 * @param[in] triangle A pointer to the `Triangle` structure containing the base and height.
 * @return The area of the triangle.
 */
double compute_triangle_area(const Triangle* triangle) {
    return 0.5 * triangle->base * triangle->height;
}
组(Modules)注释
为了组织代码,你可以使用 Doxygen 的 @defgroup 和 @addtogroup 标签将相关函数、结构体等归为一组,便于文档阅读。
/**
 * @defgroup GeometricShapes Geometric Shape Calculations
 * @brief This module handles the calculations of areas for geometric shapes like circles, squares, and triangles.
 *
 * The functions in this module compute the areas of various geometric shapes by using their respective formulas.
 * @{
 */
/**
 * @brief Initializes the system to compute geometric areas.
 *
 * This function can be used to set up any necessary initial values before computing the areas of shapes.
 */
void init_area_computation(void) {
    shape_count = 0;  // Initialize shape counter
}
/** @} */  // End of GeometricShapes group
main.c 中的代码示例
为了结合所有的注释,以下是一个主程序文件中使用这些函数的示例。
#include "example.h"
int main(void) {
    Circle circle = {5.0};       // Circle with radius 5.0
    Square square = {4.0};       // Square with side length 4.0
    Triangle triangle = {3.0, 6.0}; // Triangle with base 3.0 and height 6.0
    init_area_computation();
    double circle_area = compute_circle_area(&circle);
    double square_area = compute_square_area(&square);
    double triangle_area = compute_triangle_area(&triangle);
    printf("Circle Area: %f\n", circle_area);
    printf("Square Area: %f\n", square_area);
    printf("Triangle Area: %f\n", triangle_area);
    return 0;
}
其他注释标签
- @param 和- @return:用于注释函数参数和返回值。
- @file:用于文件级别的注释。
- @struct 和- @typedef:用于结构体和类型定义。
- @brief:简要描述。
- @details:详细描述。
- @note:给出提示或注意事项。
- @code 和- @endcode:用于嵌入代码示例。
- @ingroup:将元素添加到组中。
总结
这是一套完整的 Doxygen 注释示例,展示了如何为 C 语言中常见的元素(如文件、函数、变量、结构体、枚举、宏和组)进行注释。使用这些注释方式,可以确保生成的文档清晰、全面,方便团队协作和代码维护。
### 2. 使用 `/**<` 在变量声明中注释
如果想对函数内部局部变量进行注释的话应该怎么办
最后得到的只是写的注释,并没有给出局部变量的名字
对于函数内的变量,您可以使用 `/**<` 来直接注释变量的声明,帮助读者了解每个变量的作用:
int add(int a, int b) {
 int result = 0; /**< The variable that stores the sum of a and b */
result += a; /**< Add the value of a to the result */  
result += b; /**< Add the value of b to the result */
return result; /**< Return the calculated sum */  
}
### 3. 使用 `@details` 和 `@note`
对于较为复杂的函数,可以使用 `@details` 和 `@note` 来解释函数中的特定细节或提醒:
/**
- @brief Adds two integers. 
-  
- @details This function uses simple addition and assumes the input integers are valid. 
- Special care is taken to avoid overflow by ensuring the sum stays within a valid range. 
-  
- @note This function does not check for integer overflow. 
-  
- @param a First integer 
- @param b Second integer 
- @return The sum of a and b 
 */
 int add(int a, int b) {
 int result = 0; /**< The accumulated sum */- // Add integers with overflow check 
 if ((a > 0 && b > 0 && a > INT_MAX - b) || (a < 0 && b < 0 && a < INT_MIN - b)) {
 // Handle overflow case
 return -1; //!< Return error code if overflow occurs
 }- result = a + b; //!< Perform the addition - return result; //!< Return the result 
 }
### 小结
在函数内部添加详细的注释有助于提升代码的可读性和维护性,Doxygen 支持多种注释格式,可以根据项目需求灵活选择。
如果最后生成的文档显示的公式是 `$ Area = \pi \times radius^2 $` 而不是公式的数学格式,可能是 Doxygen 没有正确识别 LaTeX 公式的标记。为了让 Doxygen 正确地显示数学公式,可以尝试以下几种解决方案:
### 1. 确保使用正确的 `@f$` 标签
Doxygen 支持 LaTeX 公式,但它需要你使用正确的标签格式。如果你想让公式显示为数学格式,应该用 `@f$` 来标记公式的开始和结束。例如:
/**
- @brief Computes the area of a circle. 
-  
- The area of a circle is given by the formula: 
- @f$Area = \pi \times radius^2 @f$. 
-  
- @param[in] radius The radius of the circle. 
- @return The area of the circle. 
 */
 double compute_circle_area(double radius) {
 return PI * radius * radius;
 }
在生成文档时,Doxygen 会识别 `@f$` 标签并将其转换为 LaTeX 格式,显示为:
[\\text{Area} \= \\pi \\times \\text{radius}\^2]
### 2. 使用 `@f[` 和 `@f]` 包围公式
如果公式较长或你想换行,可以使用 `@f[` 和 `@f]` 标签将整个公式包围起来,生成更加清晰的数学公式表示。例如:
/**
- @brief Computes the area of a circle. 
-  
- The area of a circle is calculated as: 
- @f[ 
- Area = \pi \times radius^2 
- @f] 
-  
- @param[in] radius The radius of the circle. 
- @return The area of the circle. 
 */
 double compute_circle_area(double radius) {
 return PI * radius * radius;
 }
这样,公式将会被识别为块状公式,并正确渲染。
### 3. 确保 Doxygen 配置支持 LaTeX
Doxygen 需要特定的配置才能正确处理 LaTeX 公式:
* 打开 `Doxyfile`,确保以下设置启用:
  * `USE_MATHJAX = YES` 让 Doxygen 使用 MathJax 来渲染数学公式。
  * 如果没有 MathJax,你可以设置 `LATEX_OUTPUT = YES`,以确保 LaTeX 能被正确编译成 PDF 或 HTML 格式。
USE_MATHJAX   = YES
 MATHJAX_FORMAT = HTML
### 4. 确保 MathJax 或 LaTeX 环境可用
* 如果生成 HTML 文档并希望公式正确显示,你需要确保 MathJax 脚本能够正常加载。如果是生成 PDF,你需要 LaTeX 环境来正确编译数学公式。
### 结论
总结来说,确保使用 `@f$` 标签正确标记公式,并且检查 Doxygen 的配置文件是否启用了 MathJax 或 LaTeX 支持。
效果图
 
- 感谢你赐予我前进的力量
                        
                        
                            
 
            
        

