field_eq2latex.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. #!/usr/bin/env/python
  2. # -*- coding:utf-8 -*-
  3. """
  4. 域公式转latex
  5. """
  6. import re, os
  7. import configs
  8. from pprint import pprint
  9. from func_timeout import func_set_timeout
  10. SUB = {"A":"Ⓐ",
  11. "V":"Ⓥ",
  12. "W":"Ⓦ",
  13. "X":"Ⓧ",
  14. "G":"Ⓖ",
  15. }
  16. def latex_wash(ltx, is_danti=0):
  17. """
  18. latex格式调整, 为了渲染效果
  19. :param ltx:
  20. :return:
  21. """
  22. # ltx = re.sub(r"(?<!\\)%", "\%", ltx)
  23. # word中phi和varphi的渲染与mathjax相反,需换一下
  24. if not is_danti:
  25. ltx = re.sub(r"\\phi(?!up)", "@#@\\varphi", ltx)
  26. ltx = re.sub(r"(?<!@#@)\\varphi(?!up)", "\\phi", ltx)
  27. ltx = ltx.replace("@#@\\varphi", "\\varphi")
  28. # 处理\left和\right单独出现的情况
  29. ltx = re.sub(r"\$\\left\s*[((]\$", "(", ltx)
  30. ltx = re.sub(r"\$\\right\s*[))]\$", ")", ltx)
  31. def sub1(ss):
  32. if re.search(r"\\left.*?\\right|\\right.*?\\left", ss.group(1)) is None:
  33. # res = re.sub(r"\\left(?!right|arrow)|\\right(?!left|arrow)", "", ss.group(1))
  34. res = re.sub(r"\\left(?![a-z])|\\right(?![a-z])", "", ss.group(1))
  35. return res
  36. return ss.group(1)
  37. ltx = re.sub(r"(\$.*?(\\left(?![a-z])|\\right(?![a-z])).*?\$)", sub1, ltx)
  38. # -----------------------------------------------------------
  39. # \text{}中的 "<" 不换为"\lt"
  40. def get_posi(item, start): # 获取}的正确位置
  41. sign_stack = ["{"]
  42. for n, s in enumerate(item[start:]):
  43. if s == "{":
  44. sign_stack.append("{")
  45. elif s == "}":
  46. sign_stack.pop()
  47. if not sign_stack:
  48. return start + n
  49. return None
  50. res_p = ""
  51. tmp_item = ltx
  52. # 前端也会将小于号<替换成&lt;
  53. while "\\text" in tmp_item and (" \lt " in tmp_item or "\%" in tmp_item):
  54. st = re.search(r"\\text\s*{", tmp_item).end()
  55. ed = get_posi(tmp_item, st)
  56. if ed:
  57. res_p += tmp_item[:st] + tmp_item[st: ed + 1].replace(" \lt ", " &lt; ")\
  58. .replace("\%", "%")
  59. tmp_item = tmp_item[ed + 1:]
  60. else:
  61. break
  62. if res_p:
  63. ltx = res_p
  64. if tmp_item:
  65. ltx += tmp_item
  66. return ltx
  67. # def get_latex(item):
  68. # if r"$eq \\f(" in item:
  69. # item = re.sub(r"\$eq \\\\f\((.+?),(.+?)\)", r"$\\frac{\1}{\2}", item)
  70. #
  71. # if r"$eq \\r(" in item:
  72. # item = re.sub(r"\$eq \\\\r\((.+?)\)", r"$\sqrt{\1}", item)
  73. #
  74. # if "$eq \\\\o\\\\" in item:
  75. # while re.search(r"\$eq \\\\o\\\\al\((.+?),(.*?)\)", item):
  76. # ss = re.search(r"\$eq \\\\o\\\\al\((.+?),(.*?)\)", item)
  77. # # 将非变量的{}修改成{{}}
  78. # s1 = "$_{{{sub}}}^{{{sup}}}".format(sub=ss.group(2), sup=ss.group(1))
  79. # s1 = re.sub("</?su[bp]>|\s", "", s1)
  80. # if not ss.group(2):
  81. # eq_info = re.match(r"\$\s*<sub>(.+?)</sub>", item[ss.end():])
  82. # if eq_info:
  83. # s1 = "$_{{{sub}}}^{{{sup}}}$".format(sub=eq_info.group(1), sup=ss.group(1))
  84. # s1 = re.sub("</?su[bp]>|\s", "", s1)
  85. # item = item[:ss.start()] + s1 + item[ss.end()+eq_info.end():]
  86. # # return re.sub("</?su[bp]>|\s", "", s1)
  87. # else:
  88. # item = item[:ss.start()] + s1 + item[ss.end():]
  89. # else:
  90. # item = item[:ss.start()] + s1 + item[ss.end():]
  91. #
  92. # # s1 = "$_{{{sub}}}^{{{sup}}}".format(sub=ss.group(2), sup=ss.group(1))
  93. # # return re.sub("</?su[bp]>|\s", "", s1)
  94. # # item = re.sub(r"\$eq \\\\o\\\\al\((.+?),(.*?)\)", sub1, item)
  95. #
  96. # ac_info = re.search(r"\$eq \\\\o\\\\ac\(○,\s*([A-Z])\)", item)
  97. # if ac_info:
  98. # if ac_info.group(1) in SUB.keys():
  99. # item = item.replace(ac_info.group(0), SUB.get(ac_info.group(1)))
  100. #
  101. # return item
  102. # @func_set_timeout(3)
  103. def get_latex0(item):
  104. while "$eq \\\\f(" in item or "$eq \\\\r(" in item or re.search("【域公式】.*?\\\\o\\\\", item):
  105. if "$eq \\\\f(" in item:
  106. # item = re.sub(r"\$eq \\\\f\((((?!\\\\[fr]).)+?),(.+?)\)", r"$\\frac{\1}{\2}", item)
  107. item = re.sub(r"(【域公式】.*?)\\\\f\((((?!\\\\[fr]\().)+?),(((?!\\\\[fr]\().)+?)\)",
  108. r"\1\\frac{\2}{\4}", item)
  109. if "$eq \\\\r(" in item:
  110. item = re.sub(r"(【域公式】.*?)\\\\r\((((?!\\\\[fr]\().)+?)\)", r"\1\sqrt{\2}", item)
  111. if re.search("【域公式】.*?\\\\o\\\\", item): # if "$eq \\\\o\\\\" in item:
  112. while re.search(r"【域公式】.*?\\\\o\\\\al\((.+?),(.*?)\)", item):
  113. ss = re.search(r"(【域公式】.*?)\\\\o\\\\al\((.+?),(.*?)\)", item)
  114. # 将非变量的{}修改成{{}}
  115. s1 = "_{{{sub}}}^{{{sup}}}".format(sub=ss.group(3), sup=ss.group(2))
  116. s1 = re.sub("</?su[bp]>|\s", "", s1)
  117. if not ss.group(3):
  118. eq_info = re.match(r"\$\s*<sub>(.+?)</sub>", item[ss.end():])
  119. if eq_info:
  120. s1 = "_{{{sub}}}^{{{sup}}}$".format(sub=eq_info.group(1), sup=ss.group(2))
  121. s1 = re.sub("</?su[bp]>|\s", "", s1)
  122. item = item[:ss.start()] + ss.group(1) + s1 + item[ss.end()+eq_info.end():]
  123. # return re.sub("</?su[bp]>|\s", "", s1)
  124. else:
  125. item = item[:ss.start()] + ss.group(1) + s1 + item[ss.end():]
  126. else:
  127. item = item[:ss.start()] + ss.group(1) + s1 + item[ss.end():]
  128. # s1 = "$_{{{sub}}}^{{{sup}}}".format(sub=ss.group(2), sup=ss.group(1))
  129. # return re.sub("</?su[bp]>|\s", "", s1)
  130. # item = re.sub(r"\$eq \\\\o\\\\al\((.+?),(.*?)\)", sub1, item)
  131. ac_info = re.search(r"\$eq \\\\o\\\\ac\(○,\s*([A-Z])\)", item)
  132. if ac_info:
  133. if ac_info.group(1) in SUB.keys():
  134. item = item.replace(ac_info.group(0), SUB.get(ac_info.group(1)))
  135. return item.replace("【域公式】$eq ", "$")
  136. # @func_set_timeout(5)
  137. def zifu_match_combine(split_eq):
  138. """
  139. 递归函数,将成对括号进行组合,目前先按成对的括号进行转化
  140. :param split_eq:
  141. :return:
  142. """
  143. if len(split_eq) < 4 or ")" not in split_eq or "(" not in split_eq:
  144. return split_eq
  145. for k, i in enumerate(split_eq):
  146. if i == ")":
  147. for subk, j in enumerate(split_eq[:k][::-1]):
  148. if j == "(":
  149. # print(split_eq[k - subk - 1 - 1])
  150. bef_left_kuohao = split_eq[k - subk - 1 - 1]
  151. if bef_left_kuohao == "\\f":
  152. # dou_index = split_eq[k-subk-1-1:k+1].index(',')+k-subk-2
  153. # bb = split_eq[k - subk - 1 - 1:k + 1]
  154. info1 = re.search(r"\\f\((.*?),(.*?)\)$", "".join(split_eq[k - subk - 1 - 1:k + 1]))
  155. if info1:
  156. new_s = "\\frac{{{one}}}{{{two}}}".format(one=info1.group(1), two=info1.group(2))
  157. new_split_eq = split_eq[:k - subk - 1 - 1]
  158. if new_split_eq and new_split_eq[-1] == "(":
  159. new_s = "{" + new_s + "}"
  160. new_split_eq.append(new_s)
  161. new_split_eq.extend(split_eq[k + 1:])
  162. return zifu_match_combine(new_split_eq)
  163. elif bef_left_kuohao == "\\r":
  164. print(':::', "".join(split_eq[k - subk - 1 - 1:k + 1]))
  165. info1 = re.search(r"\\r\((.*?)\)$", "".join(split_eq[k - subk - 1 - 1:k + 1]))
  166. if info1:
  167. if re.search("<sup>\d</sup>\s*,", info1.group(1)):
  168. lft, right = re.split("(?<=</sup>)\s*,", info1.group(1))
  169. new_s = "\sqrt[{}]{{{}}}".format(re.search("<sup>(\d)</sup>", lft).group(1), right)
  170. else:
  171. new_s = "\sqrt{{{}}}".format(re.sub("^\s*,", "", info1.group(1)))
  172. new_split_eq = split_eq[:k - subk - 1 - 1]
  173. if new_split_eq and new_split_eq[-1] == "(":
  174. new_s = "{" + new_s + "}"
  175. new_split_eq.append(new_s)
  176. new_split_eq.extend(split_eq[k + 1:])
  177. return zifu_match_combine(new_split_eq)
  178. elif bef_left_kuohao in ['\\o\\al', '\\s']:
  179. info1 = re.search(r"(\\o\\al|\\s)\((.*?),(.*?)\)$", "".join(split_eq[k - subk - 1 - 1: k + 1]))
  180. if info1:
  181. new_s = "_{{{sub}}}^{{{sup}}}".format(sub=info1.group(3), sup=info1.group(2))
  182. new_s = re.sub("</?su[bp]>|\s", "", new_s)
  183. new_split_eq = split_eq[:k - subk - 1 - 1]
  184. new_split_eq.append(new_s)
  185. new_split_eq.extend(split_eq[k + 1:])
  186. return zifu_match_combine(new_split_eq)
  187. elif bef_left_kuohao == '\\x\\to':
  188. info1 = re.search(r"\\x\\to\((.*?)\)$", "".join(split_eq[k - subk - 1 - 1:k + 1]))
  189. if info1:
  190. new_s = "\\bar{{{}}}".format(info1.group(1))
  191. new_split_eq = split_eq[:k - subk - 1 - 1]
  192. if new_split_eq and new_split_eq[-1] == "(":
  193. new_s = "{" + new_s + "}"
  194. new_split_eq.append(new_s)
  195. new_split_eq.extend(split_eq[k + 1:])
  196. return zifu_match_combine(new_split_eq)
  197. else:
  198. new_s = "".join(split_eq[k - subk - 1 - 1: k + 1])
  199. new_split_eq = split_eq[:k - subk - 1 - 1]
  200. new_split_eq.append(new_s)
  201. new_split_eq.extend(split_eq[k + 1:])
  202. return zifu_match_combine(new_split_eq)
  203. # @func_set_timeout(36)
  204. def get_latex(item, is_reparse=0, wordid="123456", must_latex=0):
  205. """
  206. 第一通道:
  207. 将文本中的域代码字符串能转化latex的先转化,不能转化的就暂时用域代码格式
  208. 第二通道:
  209. 再解析时,遇到域代码,将域代码转图片处理
  210. 考虑先转化:根式、分式、上下标、to、\s
  211. :param item:
  212. :return:
  213. """
  214. is_first = 1
  215. item = item.replace("\\uf028", "(").replace("\\uf029", ")") # 2020-6-21
  216. new_item = item
  217. # semi_succ_dict = {}
  218. while re.findall("(【域公式:[^【]*?】)", item):
  219. all_eqs1 = re.findall("(【域公式:[^【]*?】)", item) # 遇到嵌套的域公式,无法获取完整,故加【
  220. all_eqs = list(set(all_eqs1))
  221. all_eqs.sort(key=all_eqs1.index)
  222. print(all_eqs)
  223. new_eqs = []
  224. fail_n = 0
  225. for eq in all_eqs:
  226. raw_eq = eq.replace("\\\\", "\\").replace(" \R", " \\r")
  227. eq = raw_eq.replace("eq ", "").replace("【域公式:", "").replace("】", "")
  228. split_eq = re.split(r"(\\f|\(|\)|\\r|\\o\\al|\\x\\to|\\s|,)", eq)
  229. split_eq = [i for i in split_eq if i]
  230. res_eq = zifu_match_combine(split_eq)
  231. # print(res_eq, split_eq)
  232. try:
  233. if "".join(res_eq) == "".join(split_eq): # 转失败
  234. fail_n += 1
  235. new_eqs.append(raw_eq)
  236. elif re.search(r"\\[a-zA-Z\d]{1,5}\(", "".join(res_eq)): # 没有完全转成功
  237. fail_n += 1
  238. new_eqs.append(raw_eq)
  239. # semi_succ_dict[raw_eq] = "【域公式:eq {}】".format("".join(res_eq))
  240. # new_eqs.append("【域公式:eq {}】".format("".join(res_eq)))
  241. else:
  242. # mathjax不能渲染sub和sup
  243. new_eq = "".join(res_eq)
  244. def deal2(yy):
  245. new_y = yy.group(2)
  246. if yy.group(1) == "<sub>":
  247. new_y = "_{" + yy.group(2) + "}"
  248. if yy.group(1) == "<sup>":
  249. new_y = "^{" + yy.group(2) + "}"
  250. return new_y
  251. new_eq = re.sub("(<sub>)(.+?)</sub>", deal2, new_eq)
  252. new_eq = re.sub("(<sup>)(.+?)</sup>", deal2, new_eq).strip()
  253. if not is_first: # 如果不是第一轮转化,则将前面转化后的$去掉
  254. new_eq = re.sub(r"(?<!\\)\$", "", new_eq)
  255. new_eqs.append("${}$".format(new_eq))
  256. except:
  257. fail_n += 1
  258. new_eqs.append(raw_eq)
  259. if fail_n == len(all_eqs): # 防止死循环
  260. break
  261. eq_repl_dict = dict(zip(all_eqs, new_eqs))
  262. print('-------------',eq_repl_dict)
  263. for k, v in eq_repl_dict.items():
  264. # v = latex_wash(v)
  265. # print(v)
  266. item = item.replace(k, v)
  267. is_first = 0
  268. # 对于转latex失败的域公式走第二通道:转图片
  269. # 嵌套的情况,里层域公式转latex成功,外层转失败,怎么办
  270. if must_latex:
  271. return item, ""
  272. if is_reparse and "【域公式" in item:
  273. file_path = configs.IMG_FOLDER + '/' + str(wordid) + '/' + "field_eq"
  274. if not os.path.exists(file_path):
  275. os.makedirs(file_path)
  276. new_eqs2raw = {} # 域代码_原始文本
  277. for i in re.finditer("【域公式:(.*?)】", item):
  278. if re.search(r"\\sqrt|\\frac|\\bar", i.group(1)) is None: # 不能包含latex命令
  279. if "【" in i.group(1): # 嵌套,则按上面提取的域公式不完整
  280. cout = i.group(1).count("【") # 统计【个数
  281. try: # 根据嵌套的“【”找到最外层的“】”
  282. raw_eq = i.group(0)+"】".join(item[i.end():].split("】")[:cout])+"】" # 拿到完整样式
  283. eqs = i.group(1) + "".join(item[i.end():].split("】")[:cout])
  284. eqs = "eq " + eqs.replace("【域公式:", "").replace("【", "").replace("eq ", "")
  285. eqs = re.sub("<sub>(.+?)</sub>", r"\s(,\1)", eqs)
  286. eqs = re.sub("<sup>(.+?)</sup>", r"\s(\1,)", eqs)
  287. new_eqs2raw[eqs]=raw_eq
  288. except:
  289. pass
  290. else:
  291. eqs = re.sub("<sub>(.+?)</sub>", r"\s(,\1)", i.group(1))
  292. eqs = re.sub("<sup>(.+?)</sup>", r"\s(\1,)", eqs)
  293. new_eqs2raw[eqs] = i.group(0)
  294. else:
  295. print("域公式中含latex表达式!!!")
  296. new_eqs = list(new_eqs2raw.keys())
  297. new_eqs.append(file_path)
  298. eqcode = "】【".join(new_eqs)
  299. try:
  300. requests.get(r"http://localhost:9001/FieldEq/Eq2Png/?eqcode=" + eqcode, timeout=3)
  301. except:
  302. pass
  303. # 在生成图片的文件夹中对应判断图片再进行替换
  304. eq_imgs = os.listdir(file_path)
  305. if eq_imgs:
  306. raw_eqs2img = {}
  307. for img in eq_imgs:
  308. w_h_info = str(img.replace(".png", "").split("__")[-1]).split("_")
  309. w = int(int(w_h_info[0])/1.27+1)
  310. h = int(int(w_h_info[1])/1.27+1)
  311. name = str(img.replace(".png", "").split("__")[0])
  312. idn = int(name.split("_")[-1])
  313. new_name = name + ".png"
  314. os.rename(file_path + "/" + img, file_path + "/" + new_name)
  315. eq_img = '<img src="{}/{}/field_eq/{}" width="{}px" height="{}px" eq-code="{}" />'\
  316. .format(configs.new_img_ip, wordid, new_name, w, h, new_eqs[idn-1])
  317. raw_eqs2img[new_eqs2raw[new_eqs[idn-1]]] = eq_img
  318. if raw_eqs2img:
  319. for k, v in raw_eqs2img.items():
  320. item = item.replace(k, v)
  321. new_item = new_item.replace(k, v)
  322. else:
  323. new_item = ""
  324. else:
  325. new_item = ""
  326. return item, new_item
  327. if __name__ == '__main__':
  328. import requests,json
  329. # f = "t=【域公式】$eq \\\\f(v<sub>0</sub>,a)$=【域公式】$eq \\\\f(6,1)$ s=6s, $eq \\\\r(6)$ "
  330. # print(re.sub(r"\\\\o\\\\al\((.+?),.+?\)", r"\1",f))
  331. # p1 = r"C:\Users\Python\Desktop\test\24\25.html"
  332. # html = open(p1, 'r', encoding='utf-8').read()
  333. # # print(html)
  334. # print(get_latex(html))
  335. # f = "eq \\f(\\f(1,2)×0.82,0.2×10)】【eq \\f(6,1)】【eq \\f(\\x\\to(OC)-\\x\\to(OA),2T)】【C:/Users/Python/Desktop/test/temp"
  336. # res = requests.get(r"http://localhost:9001/FieldEq/Eq2Png/?eqcode=" + f, timeout=30).text
  337. # print(json.loads(res).replace("\r\n", ""))
  338. # f = "【解】解析 (1)因OB绳处于竖直方向,所以B球处于平衡状态,AB绳上的拉力为零,OB绳对小球的拉力F<sub>OB</sub>=mg. (3分)<br/>(2)A球在重力mg、水平拉力F和OA绳的拉力F<sub>OA</sub>三力作用下平衡,所以OA绳对小球的拉力F<sub>OA</sub>=【域公式:eq \\\\f(mg,cos 60°)】=2mg. (3分)<br/>(3)作用力F=mgtan 60°=【域公式:eq \\\\r(3)】mg. (3分)<br/>答案 (1)mg (2)2mg (3)【域公式:eq \\\\r(3)】mg"
  339. f = "B.【域公式:eq \\r(<sup>3</sup>,\\f(1,4))】"
  340. aa = get_latex(f, 1)
  341. print(aa)
  342. # tt = r"${ } _ { n H C H O } \rightarrow f H _ { 2 } C - O _ { n }$"
  343. # def sub1(ss):
  344. # if re.search(r"\\left.*?\\right|\\right.*?\\left", ss.group(1)) is None:
  345. # # res = re.sub(r"\\left(?!right|arrow)|\\right(?!left|arrow)", "", ss.group(1))
  346. # res = re.sub(r"\\left(?![a-z])|\\right(?![a-z])", "", ss.group(1))
  347. # return res
  348. # return ss.group(1)
  349. # tt = re.sub(r"(\$.*?(\\left(?![a-z])|\\right(?![a-z])).*?\$)", sub1, tt)
  350. # # re1 = latex_wash(tt)
  351. # print(tt)
  352. # item = "【域公式:eq \\f(【域公式:eq \\f(6,1)】,3)】geeghe】threthtrh"
  353. # new_eqs2raw = {} # 域代码_原始文本
  354. # for i in re.finditer("【域公式:(.*?)】", item):
  355. # print(i.group(0))
  356. # if re.search(r"\\sqrt|\\frac|\\bar", i.group(1)) is None: # 不能包含latex命令
  357. # if "【" in i.group(1): # 嵌套
  358. # cout = i.group(1).count("【")
  359. # try: # 根据嵌套的【找到最外层的】
  360. # raw_eq = i.group(0) + "】".join(item[i.end():].split("】")[:cout]) + "】"
  361. # eqs = i.group(1) + "".join(item[i.end():].split("】")[:cout])
  362. # eqs = "eq " + eqs.replace("【域公式:", "").replace("【", "").replace("eq ", "")
  363. # eqs = re.sub("<sub>(.+?)</sub>", r"\s(,\1)", eqs)
  364. # eqs = re.sub("<sup>(.+?)</sup>", r"\s(\1,)", eqs)
  365. # new_eqs2raw[eqs] = raw_eq
  366. # except:
  367. # pass
  368. # else:
  369. # eqs = re.sub("<sub>(.+?)</sub>", r"\s(,\1)", i.group(1))
  370. # eqs = re.sub("<sup>(.+?)</sup>", r"\s(\1,)", eqs)
  371. # new_eqs2raw[eqs] = i.group(0)