JavaFx里是通过Java调用控制台执行的的jupyter和xelatex指令,
这些个指令需要在本地安装Jupyter和MikTeX之后才能正常在电脑上运行
1.1 最近写了一个大数据的小练习,感觉那个有点用,就想导出PDF去打印
然后问题来了:导出的PDF不显示中文!!!(可惜那一块多钱)。
网上教程差不多就是用juypter和xlatex命令进行转换,但是这样一个一个转感觉有点麻烦,
然后就想着写一个Java程序看看能不能自己选择文件进行PDF转换
然后探索的过程就开始了
1.2 一有问题当然是先问度娘了,找了一波后发现可以通过命令提示符(CMD)以命令的方式创建创建出pdf,这就很灵性了。
虽然直接在Jupyter Notebook里可以更简单地直接导出PDF,
但是对于希望在PDF里显示中文的同学来说,能够方便一点导出pdf文件的话,何乐而不为嘞
2.1 我找的解决方案挺简单的,大概分三步:a): 一条命令通过source.ipynb文件生成source.tex文件
b): 用编辑器打开source.tex,在指定位置添加文本
c):一条命令通过source.tex文件生成一系列文件,这里面就包括了source.pdf
d):上面的处理方式都是通过Java实现,在控制台运行指令也是通过Java调用的(真好玩)
嘿嘿,听起来挺简单的,写着写着你会发现还真的挺简单的,还有一点瓜
3.1 win10+IDEA+jdk8+anaconda+Jupyter Notebook+MikTeX+Pandoc+JavaFx(SceneBuilder)
3.2 要通过Jupyter Notebook转PDF的话要用MikTeX和Pandoc,MikTeX需要配置环境
3.3 MikTeX下载地址:https://miktex.org/download
Pandoc下载地址:https://github.com/jgm/pandoc/releases/tag/2.3.1
3.4 把MikTex添加到系统环境变量里
4.1 介绍:最初就是写了一个控制台程序,然后为了能够成功导出PDF就一直在堆代码,最后就是成功导出PDF
4.2 代码:因为一开始就是为了写功能而写代码,所以感觉把这个写死了,因为测试就是针对一个文件来写的,不过问题不大,
后面我又写了一个JavaFx的,相关注释我都写在里面了
1 import java.io.*;
2
3 public class Main {
4
5 private static String path = "D:\\JupyterNotebook";
6 private static File sourceFile = new File("D:\\JupyterNotebook\\pandas_test.tex");
7
8 public static void main(String[] args) throws IOException, InterruptedException {
9 for (int i = 0; i < args.length; i++) {
10 System.out.println(args[i]);
11 }
12 change2Tex();
13 File bufferFile = createTexFile(); //获取创建的文件对象
14 delay() ; //延时
15 modifyTex(bufferFile); //修该Tex文件
16 change2PDF(); //将Tex文件转化成PDF文件
17 }
18
19
20 /**
21 * 延一个时,
22 * java建文件比命令提示符快
23 *
24 * @throws InterruptedException
25 */
26 public static void delay() throws InterruptedException {
27 for (int i = 0; i < 4; i++) {
28 Thread.sleep(1000);
29 }
30 }
31
32 /**
33 * 通过简单的命令
34 * 将文件转化为tex文件
35 * 执行cmd
36 *
37 * jupyter nbconvert --to latex yourNotebookName.ipynb
38 * 将文件里 \documentclass[11pt]{article}后面加上下面这三行
39 * \\usepackage{fontspec, xunicode, xltxtra}
40 * \\setmainfont{Microsoft YaHei}
41 * 将latex转化为pdf
42 * xelatex yourNotebookName.tex
43 */
44 public static void change2Tex() throws IOException {
45
46 Runtime runtime = Runtime.getRuntime();
47 String cmd = "cmd /k start jupyter nbconvert --to latex " + path + "\\pandas_test.ipynb"; //cmd指令,cmd /k start + 指令,运行五玩了指令就关闭cmd
48 System.out.println(cmd);
49 System.out.println(path);
50 runtime.exec(cmd);
51 }
52
53 /**
54 * 将tex文件转化成pdf文件
55 *
56 * @throws IOException
57 */
58 public static void change2PDF() throws IOException {
59 Runtime runtime = Runtime.getRuntime();
60 String cmd = "cmd /k start xelatex afterInsertText.tex";
61 runtime.exec(cmd, null, new File(path));//注意这里:exec里有三个参数,这个方法可以指定在path文件夹打开cmd,然后运行cmd指令
62 }
63
64 /**
65 * 创建Tex文件
66 *
67 * @return
68 * @throws IOException
69 */
70 public static File createTexFile() throws IOException {
71
72 File tempFile = new File("D:\\JupyterNotebook"); //判断这个文件夹在不在
73 if (!tempFile.exists()) {
74 tempFile.mkdir();
75 }
76
77 File bufferTopTex = new File("D:\\JupyterNotebook", "afterInsertText.tex"); //判断这个文件在不在
78 if (!bufferTopTex.exists()) { //不在的话创建文件,
79 bufferTopTex.createNewFile();
80 }
81 //在后面会把源.tex文件要修改的位置前面的数据写到下面这个文件(虽然下面是个对象,意思应该能懂)里面,
82 // 然后接着在后面添加文本,最后把源tex剩下的数据写到这个文件里
83 return bufferTopTex;
84 }
85
86 /**
87 * 文件读写,在文件后面添加需要添加的指令
88 *
89 * @throws IOException
90 */
91 public static void modifyTex(File file) throws IOException {
92
93 RandomAccessFile topTex = new RandomAccessFile(file, "rw"); //在本地创建的afterInsertText.tex文件
94
95 RandomAccessFile raf = new RandomAccessFile(sourceFile, "rw"); //获取源.tex文件
96
97 String line;
98
99 //在下面是对readLine()取到的数据进行转码,这是一个解决乱码的好方式
100 //注意在下面每用一次readLine(),那个指向行号的指针就会向下移动一次,和ResultSet里的rs.next()有点像
101 while ((line = new String(raf.readLine().getBytes("ISO-8859-1"), "utf-8")) != null) {
102 topTex.write(("\n" + line).getBytes());
103 if (line.equals("\\documentclass[11pt]{article}")) {
104 topTex.write((" \n\\usepackage{fontspec, xunicode, xltxtra}").getBytes());
105 topTex.write(("\n\\setmainfont{Microsoft YaHei}").getBytes());
106 topTex.write(("\n\\usepackage{ctex} ").getBytes());
107 break;
108 }
109 }
110
111 while (true) {
112 final String temp;
113 if ((temp = raf.readLine()) == null) {
114 break;
115 } else {
116 line = new String(temp.getBytes("ISO-8859-1"), "utf-8");
117 topTex.write(("\n" + line).getBytes());
118 }
119 }
120 }
121 }
Main.java
4.3 运行结果:emmmmmmm,这个运行完了就自己关掉了,不好截图,看看其他的吧
4.3.1 只有一个.ipynb文件:
然后运行一下程序:
先是Java程序生成的afterInsertText.tex文件
然后接着运行到结束就会生成这些文件,可以看到pdf自动生成了
最后看一眼有没有中文:
-----------------可以看出,上面的pdf是有中文的,成功-----------------
1.jupyter nbconvert --to latex yourNotebookName.ipynb
2.将文件里 \documentclass[11pt]{article}后面加上下面这三行
\usepackage{fontspec, xunicode, xltxtra}
\setmainfont{Microsoft YaHei}
\usepackage{ctex}
3.将latex转化为pdf: xelatex yourNotebookName.tex
Tip_2:
在Java里输出反斜杠要用两个,英文点号 ……要用 \\.
5.1 用的是IDEA开发的,所以开发JavaFx程序挺简单的,只要新建一个JavaFx程序就好了,然后就是下载Scenebuilder了,下载完了之后还要配置一下options.xml和other.xml
路径是这个:other.xml也在这个路径下
配置:other.xml
配置:options.xml,在45行里的value里面加上E:/SceneBuilder/SceneBuilder.exe;你的SceeBuilder程序的路径
重启IDEA,右键FXML文件可以看到有open in scenebuilder那个选项点击之后不会报错了
5.2 程序总体来说还行,但是打包成jar包之后就会出bug,点击Generate不会正常显示提示框,可是在IDEA里可以正常显示提示框,不知道为啥,所以就没处理这个bug,有兴趣的可以改一改咯
5.3 Java调用控制台执行的的jupyter和xelatex指令,这些个指令需要在本地安装Jupyter和MikTeX之后才能正常在电脑上运行
5.4 程序基本骨架
5.5 程序代码:
5.5.1 包含main方法的类
1 package sample;
2
3 import javafx.application.Application;
4 import javafx.fxml.FXMLLoader;
5 import javafx.scene.Parent;
6 import javafx.scene.Scene;
7 import javafx.stage.Stage;
8
9 public class Main extends Application {
10
11 static Stage mainStage = null ;
12
13 public static Stage getMainStage(){
14 return mainStage;
15 }
16 @Override
17 public void start(Stage primaryStage) throws Exception{
18 mainStage = primaryStage;
19 Parent root = FXMLLoader.load(getClass().getResource("view/sample.fxml"));
20 primaryStage.setTitle("ChangeTex2PDF");
21 primaryStage.setScene(new Scene(root));
22 primaryStage.show();
23 }
24
25
26 public static void main(String[] args) {
27 launch(args);
28 }
29 }
Main.java
5.5.2 主页中的控件的控制器
package sample.controller;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import sample.Main;
import sample.Service;
import java.io.File;
import java.io.IOException;
public class Controller {
InputFileDialogController InputFileDialogController = new InputFileDialogController();
private String srcFilePath;
@FXML
private Label filePath\_Label;
@FXML
private void importFile() {
//创建选择文件stage
Stage chooseFileStage = new Stage();
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("选择文件");
//文件过滤
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("ipynb file", "\*.ipynb")
);
//以对话框的形式显示选择文件
File file = fileChooser.showOpenDialog(chooseFileStage);
if (file != null) {
String absolutePath = file.getAbsolutePath();
srcFilePath = absolutePath;
filePath\_Label.setText(absolutePath);
System.out.println("劳资要读文件啦");
}
}
@FXML
/\*\*
\* 生成按钮点击事件处理
\* srcFilePath:文件的绝对路径
\* fullName:名称+后缀
\* filePkgPath:目标文件的包路径,
\* 比如:C:\\test\\notebook\\test.ipynb。
\* 这里filePkgPath就是C:\\test\\notebook
\* path\_arr数组:对文件绝对路径的拆分,按反斜杠 \\ 进行拆分,在java里用\\\\
\* fullName\_arr数组:对fullName按英文句号进行拆分,在java里用"\\\\."表示
\* texFileName:构造一个tex文件名,给后面转化pdf时用
\*
\*/
public void generatePDF() throws IOException, InterruptedException {
if (srcFilePath != null) { //在选择了文件的情况下
String\[\] path\_arr = srcFilePath.split("\\\\\\\\");
String fullName = path\_arr\[path\_arr.length - 1\];
StringBuffer filePkgPath = getStringBuffer(path\_arr);
String\[\] fullName\_arr = fullName.split("\\\\.");
Service.change2Tex(filePkgPath.toString(), fullName\_arr\[0\]);
File bufferFile = Service.createTexFile(filePkgPath.toString());
Service.delay();
String texFileName = fullName\_arr\[0\] + ".tex";
texFileName = filePkgPath + "\\\\" + texFileName;
Service.modifyTex(bufferFile, texFileName);
Service.change2PDF(filePkgPath.toString());
} else {
InputFileDialogController.loadDialog().show(); //显示提示框,请先选择所需文件
Main.getMainStage().close();
}
}
/\*\*
\* 获取目标文件的包路径
\*
\* @param path\_arr
\* @return
\*/
private StringBuffer getStringBuffer(String\[\] path\_arr) {
StringBuffer filePkgPath = new StringBuffer();
for (int i = 0; i < path\_arr.length - 1; i++) {
if (i < path\_arr.length - 2) { //除开绝对路径后面的(文件名+后缀)那一项
filePkgPath.append(path\_arr\[i\]).append("\\\\");
} else {
filePkgPath.append(path\_arr\[i\]);
}
}
return filePkgPath;
}
/\*\*
\* 退出软件
\*
\* @throws IOException
\*/
@FXML
public void exit() throws IOException {
Main.getMainStage().close();
}
}
Controller.java
5.5.3 调用控制台,执行jupyter xlatex指令,以及对文件路径进行处理的方法
package sample;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class Service {
/**
* 延一个时,
* 娘欸java建文件比命令提示符快
*
* @throws InterruptedException
*/
public static void delay() throws InterruptedException {
for (int i = 0; i < 4; i++) {
Thread.sleep(1000);
}
}
/\*\*
\* 通过简单的命令
\* 将文件转化为tex文件
\* \* 执行cmd
\* \* jupyter nbconvert --to latex yourNotebookName.ipynb
\*
\* 将文件里 \\documentclass\[11pt\]{article}后面加上下面这两行
\* \* \\\\usepackage{fontspec, xunicode, xltxtra}
\* \* \\\\setmainfont{Microsoft YaHei}
\*
\* 将latex转化为pdf
\* \* xelatex yourNotebookName.tex
\*/
public static void change2Tex(String filePkgPath, String fileName) throws IOException {
Runtime runtime = Runtime.getRuntime();
String cmd = "cmd /k start jupyter nbconvert --to latex " + filePkgPath + "\\\\" + fileName + ".ipynb";
runtime.exec(cmd);
}
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*这里可以改,改成用户自定义名称\*\*\*\*\*\*\*\*\*\*\*\*\*
\* 将tex文件转化成pdf文件
\*
\* @throws IOException
\*/
public static void change2PDF(String filePkgPath) throws IOException {
Runtime runtime = Runtime.getRuntime();
String cmd = "cmd /k start xelatex Result.tex";
runtime.exec(cmd, null, new File(filePkgPath));
}
/\*\*
\* 创建Tex文件
\*
\* @return 获取生成的tex文件
\* @throws IOException
\*/
public static File createTexFile(String filePkgPath) throws IOException {
File tempFile = new File(filePkgPath);
if (!tempFile.exists()) {
tempFile.mkdir();
}
File bufferTopTex = new File(filePkgPath, "Result.tex");
if (!bufferTopTex.exists()) {
bufferTopTex.createNewFile();
}
return bufferTopTex;
}
/\*\*
\* 文件读写,在文件后面添加需要添加的指令
\*
\* @throws IOException
\*/
public static void modifyTex(File file, String sourceFile) throws IOException {
RandomAccessFile topTex = new RandomAccessFile(file, "rw");
RandomAccessFile raf = new RandomAccessFile(sourceFile, "rw");
String line;
while ((line = new String(raf.readLine().getBytes("ISO-8859-1"), "utf-8")) != null) {
topTex.write(("\\n" + line).getBytes());
if (line.equals("\\\\documentclass\[11pt\]{article}")) {
topTex.write((" \\n\\\\usepackage{fontspec, xunicode, xltxtra}").getBytes());
topTex.write(("\\n\\\\setmainfont{Microsoft YaHei}").getBytes());
topTex.write(("\\n\\\\usepackage{ctex} ").getBytes());
break;
}
}
//接着插入后半部分
while (true) {
final String temp;
if ((temp = raf.readLine()) == null) {
break;
} else {
line = new String(temp.getBytes("ISO-8859-1"), "utf-8");
topTex.write(("\\n" + line).getBytes());
}
}
}
//判断是否成功生成,成功生成对应文件弹出成功框
}
Service.java
5.5.4 控制弹出框动作的控制器
package sample.controller;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import sample.Main;
import java.io.IOException;
public class InputFileDialogController {
static Stage stage = new Stage();
public Stage loadDialog() throws IOException {
Scene dialogScene = new Scene(FXMLLoader.load(getClass().getResource("../view/inputFileDialog.fxml")));
stage.setScene(dialogScene);
return stage;
}
@FXML
public void closeDialog(){
stage.close();
Main.getMainStage().show();
}
}
InputFileDialogController.java
5.5.5 主页的视图
1
2
3
4
5
6
7
8
9
10
11
12
33
38
43
58
59
60
61
sample.fxml
5.5.6 弹出框的视图
1
2
3
4
5
6
7
8
InputFileDialog.fxml
5.6 Tips:
5.6.1 如果你直接导入这个项目的话,那么你在SceneBuilder里需要选定controller,以及绑定控件触发事件和控件id
5.7 最后再上一波运行结果哇
5.7.1首页
5.7.2 没有选择文件点击Generate之后:在开发环境里点击会弹出Warning,但是导出jar包之后就报错了0.0
5.7.3 选择.ipynb文件,然后程序就开始了
5.7.4 还有一些细节可以做,比如说程序运行的进度条呀,自定义导出pdf的名称呀啥的
可能我的解决方式有点瓜,但是这只是我一时兴起,于是就写了这么个东西,觉得挺好玩的
代码已经放到我的Github上了,欢迎大家来看看呀:https://github.com/Shijie1210/ChangeTex2PDF_CN.git
手机扫一扫
移动阅读更方便
你可能感兴趣的文章