Skip to content
This repository has been archived by the owner on Nov 7, 2022. It is now read-only.

feat: mathml #68

Merged
merged 7 commits into from
Mar 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/greetings.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Greetings

on: [pull_request, issues]
on: [issues]

jobs:
greeting:
Expand All @@ -10,4 +10,3 @@ jobs:
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: '[Auto response] Looks like this is your first issue. I will look into it as soon as possible.'
pr-message: '[Auto response] Welcome! Your first pr for this project. I will review it as soon as possible.'
9 changes: 7 additions & 2 deletions README-ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
[![License](https://img.shields.io/github/license/blaisewang/img2latex-mathpix?style=flat-square)](https://github.com/blaisewang/img2latex-mathpix/blob/master/LICENSE)

[Snip](https://mathpix.com/) 是由 Mathpix 打造的一款出色的应用。
它可以帮助你提取文档中的公式及文本将其转换为LaTeX格式,并每月免费提供给所有用户 50 次识别。[邀请](https://accounts.mathpix.com/referrals)拥有 **.edu** 域名电子邮件的朋友,你们都将获得**额外 200 次识别**。
它可以帮助你提取文档中的公式、基本表格、或者文本将其转换为 LaTeX 和 MathML 格式,并每月免费提供给所有用户 50 次识别。[邀请](https://accounts.mathpix.com/referrals)拥有 **.edu** 域名电子邮件的朋友,你们都将获得**额外 200 次识别**。

在 [MathpixOCR API](https://mathpix.com/ocr/) 以及 [Image2LaTeX](https://github.com/blaisewang/img2latex-mathpix/) 这款补充应用的帮助下,你可以每月免费进行 1000 次的 OCR 识别。

Expand All @@ -23,6 +23,7 @@ Image2LaTeX 是一个个人项目,仅保留了 Snip 的核心功能——将

### 新功能

- 新 MathML OCR 格式用于 Microsoft Word 等应用。
- 新 `Improved OCR` 选项支持[多选](https://mathpix.com/blog/mathpix-text-endpoint)、[段落](https://mathpix.com/blog/snip-2.0)、[基础表格](https://mathpix.com/blog/v1-table-recognition)的识别。
- 支持使用 HTTP 代理发出 OCR 请求。

Expand Down Expand Up @@ -59,7 +60,11 @@ Linux 用户可以通过在应用内任意位置右键单击的方式打开 `Pre
```

使用操作系统默认的方式(或者其他工具)对公式或文本进行截取(macOS 默认为 Shift (⇧)-Control (⌃)-Command (⌘)-4)后,
按下`空格`键预览截图,或者直接按下`回车`键发送 OCR 请求。
按下 `退格` 键(macOS 为 `delete` 键)预览截图,或者直接按下 `回车` 键发送 OCR 请求。

### 在 Microsoft Word 中 使用 MathML

要在矩阵或分数周围修复括号或括号,从公式右侧的下拉菜单中单击 `Professional` 选项。

查看下面的[展示](#展示)章节了解使用流程。

Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ English | [中文](https://github.com/blaisewang/img2latex-mathpix/blob/master/R
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?color=ff69b4&style=flat-square)](http://makeapullrequest.com)
[![License](https://img.shields.io/github/license/blaisewang/img2latex-mathpix?style=flat-square)](https://github.com/blaisewang/img2latex-mathpix/blob/master/LICENSE)

[Snip](https://mathpix.com/) is an amazing app built by Mathpix to help you extract LaTeX (also text) from documents.
[Snip](https://mathpix.com/) is an amazing app built by Mathpix to help you extract LaTeX, MathML, basic table, and text from documents.
It gives you the first 50 snips for free every single month. **200 Extra Snips** for both when you [invite](https://accounts.mathpix.com/referrals) a friend with a **.edu** domain email.

With the help of [MathpixOCR API](https://mathpix.com/ocr/) and this supplementary app, you can now make up to 1000 OCR requests per month for free.
Expand All @@ -23,6 +23,7 @@ See the [features](https://mathpix.com/ocr#features) section on MathpixOCR websi

### New Features

- New MathML OCR for Microsoft Word and more.
- New `Improved OCR` option for [multiple choice](https://mathpix.com/blog/mathpix-text-endpoint), [paragraphs](https://mathpix.com/blog/snip-2.0), [basic table](https://mathpix.com/blog/v1-table-recognition) OCRs, and more.
- HTTP proxy support for making OCR request.

Expand Down Expand Up @@ -60,9 +61,13 @@ Run `Image2LaTeX` at `Image2LaTeX-linux/bin/` or with:

Use your operating system's default methods (or other tools) to take a screenshot of equations or text (Shift (⇧)-Control (⌃)-Command (⌘)-4 on macOS by default).

Then, press the `Space` key to preview the screenshot.
Then, press the `Back Space` key (`delete` key on macOS) to preview the screenshot.
Or, press the `Return` or `Enter` key to send the OCR request.

### Microsoft Word with MathML

To fix the brackets and parentheses around matrices or fractions, click the `Professional` option from the right-hand side dropdown menu of the equation.

See [demo](#Demo) section below.

## Demo
Expand Down
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group "blaise.img2latex"
version "0.7.0"
version "0.7.1"

compileJava {
sourceCompatibility = 14
Expand All @@ -22,7 +22,6 @@ javafx {
version = "14"
}


application {
mainClassName = "entry.Main"
applicationName = "Image2LaTeX"
Expand Down
119 changes: 66 additions & 53 deletions src/main/java/io/IOUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences;

Expand All @@ -17,34 +15,36 @@
*/
public class IOUtils {

public final static String UNEXPECTED_ERROR = "Unexpected error";
public final static String INVALID_CREDENTIALS_ERROR = "Invalid credentials";
public final static String INVALID_PROXY_CONFIG_ERROR = "Invalid proxy config";
public final static String NO_IMAGE_FOUND_IN_THE_CLIPBOARD_ERROR = "No image found in the clipboard";
public static final String EXCEPTION_MARK = "Exception:";

public final static String TEXT_API_URL = "https://api.mathpix.com/v3/text";
public final static String LEGACY_API_URL = "https://api.mathpix.com/v3/latex";
public final static String MATHPIX_DASHBOARD_URL = "https://dashboard.mathpix.com/";
public final static String GITHUB_RELEASES_URL = "https://github.com/blaisewang/img2latex-mathpix/releases";
public static final String UNEXPECTED_ERROR = "Unexpected error";
public static final String INVALID_CREDENTIALS_ERROR = "Invalid credentials";
public static final String INVALID_PROXY_CONFIG_ERROR = "Invalid proxy config";
public static final String NO_IMAGE_FOUND_IN_THE_CLIPBOARD_ERROR = "No image found in the clipboard";

private final static String I2L_APP_ID = "I2L_APP_ID";
private final static String I2L_APP_KEY = "I2L_APP_KEY";
private final static String I2L_THIRD_RESULT_FORMATTING_OPTION = "I2L_THIRD_RESULT_FORMATTING_OPTION";
private final static String I2L_FOURTH_RESULT_FORMATTING_OPTION = "I2L_FOURTH_RESULT_FORMATTING_OPTION";
private final static String I2L_PROXY_ENABLE_OPTION = "I2L_PROXY_ENABLE_OPTION";
private final static String I2L_PROXY_HOSTNAME = "I2L_PROXY_HOSTNAME";
private final static String I2L_PROXY_PORT = "I2L_PROXY_PORT";
private final static String I2L_IMPROVED_OCR_ENABLE_OPTION = "I2L_IMPROVED_OCR_ENABLE_OPTION";
public static final String TEXT_API_URL = "https://api.mathpix.com/v3/text";
public static final String LEGACY_API_URL = "https://api.mathpix.com/v3/latex";
public static final String MATHPIX_DASHBOARD_URL = "https://dashboard.mathpix.com/";
public static final String GITHUB_RELEASES_URL = "https://github.com/blaisewang/img2latex-mathpix/releases";

private final static String CONFIG_NODE_PATH = "I2L_API_CREDENTIAL_CONFIG";
private static final Preferences preferences = Preferences.userRoot().node(CONFIG_NODE_PATH);
private static final String I2L_APP_ID = "I2L_APP_ID";
private static final String I2L_APP_KEY = "I2L_APP_KEY";
private static final String I2L_THIRD_RESULT_FORMATTING_OPTION = "I2L_THIRD_RESULT_FORMATTING_OPTION";
private static final String I2L_FOURTH_RESULT_FORMATTING_OPTION = "I2L_FOURTH_RESULT_FORMATTING_OPTION";
private static final String I2L_PROXY_ENABLE_OPTION = "I2L_PROXY_ENABLE_OPTION";
private static final String I2L_PROXY_HOSTNAME = "I2L_PROXY_HOSTNAME";
private static final String I2L_PROXY_PORT = "I2L_PROXY_PORT";
private static final String I2L_IMPROVED_OCR_ENABLE_OPTION = "I2L_IMPROVED_OCR_ENABLE_OPTION";

private static final String CONFIG_NODE_PATH = "I2L_API_CREDENTIAL_CONFIG";
private static final Preferences PREFERENCES = Preferences.userRoot().node(CONFIG_NODE_PATH);

/**
* @return if os is macOS.
*/
public static boolean isOSMacOSX() {

String osName = System.getProperty("os.name");
var osName = System.getProperty("os.name");
if (osName == null) {
return false;
}
Expand All @@ -58,7 +58,7 @@ public static boolean isOSMacOSX() {
*/
public static boolean isOSWindows() {

String osName = System.getProperty("os.name");
var osName = System.getProperty("os.name");
if (osName == null) {
return false;
}
Expand All @@ -76,7 +76,7 @@ public static boolean isMacDarkMode() {

try {
// process will exit with 0 if dark mode enabled
final Process process = Runtime.getRuntime().exec(new String[]{"defaults", "read", "-g", "AppleInterfaceStyle"});
final var process = Runtime.getRuntime().exec(new String[]{"defaults", "read", "-g", "AppleInterfaceStyle"});
process.waitFor(100, TimeUnit.MILLISECONDS);
return process.exitValue() == 0;
} catch (IOException | InterruptedException | IllegalThreadStateException e) {
Expand All @@ -93,10 +93,10 @@ public static boolean isMacDarkMode() {
*/
public static Response concurrentCall(Recognition recognition, Image image) {

ExecutorService executor = Executors.newSingleThreadExecutor();
var executor = Executors.newSingleThreadExecutor();

if (recognition.setSrcParameters(image)) {
Future<Response> result = executor.submit(recognition);
var result = executor.submit(recognition);
try {
return result.get();
} catch (InterruptedException | ExecutionException e) {
Expand All @@ -121,7 +121,7 @@ public static boolean isAPICredentialConfigValid() {
* @param appId App ID to be written.
*/
public static void setAppId(String appId) {
preferences.put(I2L_APP_ID, appId);
PREFERENCES.put(I2L_APP_ID, appId);
}

/**
Expand All @@ -130,7 +130,7 @@ public static void setAppId(String appId) {
* @param appKey App key to be written.
*/
public static void setAppKey(String appKey) {
preferences.put(I2L_APP_KEY, appKey);
PREFERENCES.put(I2L_APP_KEY, appKey);
}

/**
Expand All @@ -139,7 +139,7 @@ public static void setAppKey(String appKey) {
* @return IO.APICredentialConfig object.
*/
public static APICredentialConfig getAPICredentialConfig() {
return new APICredentialConfig(preferences.get(I2L_APP_ID, ""), preferences.get(I2L_APP_KEY, ""));
return new APICredentialConfig(PREFERENCES.get(I2L_APP_ID, ""), PREFERENCES.get(I2L_APP_KEY, ""));
}

/**
Expand All @@ -148,7 +148,7 @@ public static APICredentialConfig getAPICredentialConfig() {
* @param option option to be written.
*/
public static void setThirdResultFormattingOption(int option) {
preferences.putInt(I2L_THIRD_RESULT_FORMATTING_OPTION, option);
PREFERENCES.putInt(I2L_THIRD_RESULT_FORMATTING_OPTION, option);
}

/**
Expand All @@ -157,7 +157,7 @@ public static void setThirdResultFormattingOption(int option) {
* @return third result formatting option.
*/
public static int getThirdResultFormattingOption() {
return preferences.getInt(I2L_THIRD_RESULT_FORMATTING_OPTION, 2);
return PREFERENCES.getInt(I2L_THIRD_RESULT_FORMATTING_OPTION, 2);
}

/**
Expand All @@ -166,7 +166,7 @@ public static int getThirdResultFormattingOption() {
* @param option option to be written.
*/
public static void setFourthResultFormattingOption(int option) {
preferences.putInt(I2L_FOURTH_RESULT_FORMATTING_OPTION, option);
PREFERENCES.putInt(I2L_FOURTH_RESULT_FORMATTING_OPTION, option);
}

/**
Expand All @@ -175,7 +175,20 @@ public static void setFourthResultFormattingOption(int option) {
* @return fourth result formatting option.
*/
public static int getFourthResultFormattingOption() {
return preferences.getInt(I2L_FOURTH_RESULT_FORMATTING_OPTION, 0);
return PREFERENCES.getInt(I2L_FOURTH_RESULT_FORMATTING_OPTION, 0);
}

/**
* @param exception exception message.
* @return formatted exception message.
*/
public static String exceptionFormatter(String exception) {

var splitArray = exception.split(EXCEPTION_MARK, 2);
var result = splitArray[splitArray.length - 1].trim();

return result.substring(0, 1).toUpperCase() + result.substring(1);

}

/**
Expand All @@ -186,13 +199,11 @@ public static int getFourthResultFormattingOption() {
*/
public static String formatHelper(String string, String left_delimiter, String right_delimiter) {

String formatted_left_delimiter = left_delimiter + "\n ";
String formatted_right_delimiter = "\n" + right_delimiter;
var formatted_left_delimiter = left_delimiter + "\n ";
var formatted_right_delimiter = "\n" + right_delimiter;

if (string.startsWith("\\(") && string.split("\\u005C\\u0028").length == 2) {

return string.replace("\\(", formatted_left_delimiter).replace("\\)", formatted_right_delimiter);

}

return string.replace("\\(", "$").replace("\\)", "$").
Expand All @@ -214,15 +225,17 @@ public static String thirdResultFormatter(String result) {
return null;
}

int option = getThirdResultFormattingOption();
var option = getThirdResultFormattingOption();

// default for option 2 and others
return switch (option) {
case 0 -> formatHelper(result, "\\begin{equation*}", "\\end{equation*}");
case 1 -> formatHelper(result, "\\begin{align*}", "\\end{align*}");
case 3 -> formatHelper(result, "\\[", "\\]");
default -> formatHelper(result, "$$", "$$");
};
if (option == 0) {
return formatHelper(result, "\\begin{equation*}", "\\end{equation*}");
} else if (option == 1) {
return formatHelper(result, "\\begin{align*}", "\\end{align*}");
} else if (option == 3) {
return formatHelper(result, "\\[", "\\]");
}
return formatHelper(result, "$$", "$$");

}

Expand All @@ -239,8 +252,7 @@ public static String fourthResultFormatter(String result) {
return null;
}

int option = getFourthResultFormattingOption();

var option = getFourthResultFormattingOption();

if (option == 1) {
return formatHelper(result, "\\begin{align}", "\\end{align}");
Expand All @@ -256,7 +268,7 @@ public static String fourthResultFormatter(String result) {
* @param option option to be written.
*/
public static void setProxyEnableOption(boolean option) {
preferences.putBoolean(I2L_PROXY_ENABLE_OPTION, option);
PREFERENCES.putBoolean(I2L_PROXY_ENABLE_OPTION, option);
}

/**
Expand All @@ -265,7 +277,7 @@ public static void setProxyEnableOption(boolean option) {
* @return proxy enable option.
*/
public static boolean getProxyEnableOption() {
return preferences.getBoolean(I2L_PROXY_ENABLE_OPTION, false);
return PREFERENCES.getBoolean(I2L_PROXY_ENABLE_OPTION, false);
}

/**
Expand All @@ -274,7 +286,7 @@ public static boolean getProxyEnableOption() {
* @param host host to be written.
*/
public static void setProxyHostname(String host) {
preferences.put(I2L_PROXY_HOSTNAME, host);
PREFERENCES.put(I2L_PROXY_HOSTNAME, host);
}

/**
Expand All @@ -283,7 +295,7 @@ public static void setProxyHostname(String host) {
* @param port port to be written.
*/
public static void setProxyPort(String port) {
preferences.put(I2L_PROXY_PORT, port);
PREFERENCES.put(I2L_PROXY_PORT, port);
}

/**
Expand All @@ -295,12 +307,13 @@ public static ProxyConfig getProxyConfig() {

int port;
try {
port = Integer.parseInt(preferences.get(I2L_PROXY_PORT, ""));
port = Integer.parseInt(PREFERENCES.get(I2L_PROXY_PORT, ""));
} catch (NumberFormatException e) {
port = -1;
}

return new ProxyConfig(preferences.get(I2L_PROXY_HOSTNAME, ""), port);
return new ProxyConfig(PREFERENCES.get(I2L_PROXY_HOSTNAME, ""), port);

}

/**
Expand All @@ -309,7 +322,7 @@ public static ProxyConfig getProxyConfig() {
* @param option option to be written.
*/
public static void setImprovedOCREnableOption(Boolean option) {
preferences.putBoolean(I2L_IMPROVED_OCR_ENABLE_OPTION, option);
PREFERENCES.putBoolean(I2L_IMPROVED_OCR_ENABLE_OPTION, option);
}

/**
Expand All @@ -318,7 +331,7 @@ public static void setImprovedOCREnableOption(Boolean option) {
* @return improved OCR enable option.
*/
public static boolean getImprovedOCREnableOption() {
return preferences.getBoolean(I2L_IMPROVED_OCR_ENABLE_OPTION, true);
return PREFERENCES.getBoolean(I2L_IMPROVED_OCR_ENABLE_OPTION, true);
}

}
Loading