10
0
mirror of https://github.com/LCPQ/QUESTDB_website.git synced 2024-12-26 14:23:42 +01:00
QUESTDB_website/tools/lib/LaTeX.py

158 lines
4.5 KiB
Python

import re
from TexSoup import TexSoup,TexCmd,TexNode
from .utils import *
import itertools
from enum import Enum
from abc import ABCMeta
class commandBase(metaclass=ABCMeta):
def __init__(self,source,supportedTexType):
if not (source.name==supportedTexType):
raise ValueError(source+" is not a "+supportedTexType)
self.source=source
def __str__(self):
return self.source.__str__
def __repr__(self):
return self.source.__repr__()
class newCommand(commandBase):
def __init__(self,source):
super(newCommand, self).__init__(source,"newcommand")
@property
def commandName(self):
return self.source.args[0].value[1:]
@property
def argNum(self):
return 0 if self.source.args[1].type!='optional' else self.source.args[1].value
@property
def result(self):
return nodify(list(self.source.args[len(self.source.args)-1].contents))
def exist(self,tex):
exist=tex.find(self.commandName)!=None
return exist
def run(self,tex):
cmds=list(tex.find_all(self.commandName))
if len(cmds)==0:
raise ValueError("Command not found in tex")
else:
for cmd in cmds:
if self.argNum==0:
fres=self.result
else:
resultstr=str(self.result)
res=TexSoup(re.sub(f'\#([1-{self.argNum}])',lambda m: cmd.args[int(m.group(1))-1].value,resultstr))
fres=TexSoup(res)
if str(tex)==str(cmd):
tex.expr=fres.expr
else:
try:
tex.replace(cmd,fres)
except:
print(f"Unable to replace {str(cmd)}",file=sys.stderr)
return False
return True
def tryRun(self,tex):
cmds=list(tex.find_all(self.commandName))
if len(cmds)!=0:
for cmd in cmds:
if self.argNum==0:
res=self.result
else:
res=re.sub('#[1-{}]'.format(self.argNum),lambda m: cmd.args[int(m.group(1))-1],self.result)
soup=TexSoup(res)
tex.replace(cmd,soup)
@staticmethod
def runAll(tex,collection):
cmds=[cmd for cmd in collection if cmd.exist(tex)]
if(len(cmds)>0):
for cmd in cmds:
if not cmd.run(tex):
cmds.remove(cmd)
collection.remove(cmd)
newCommand.runAll(tex,collection)
class columnAlignment(Enum):
Left = "l"
Right = "r"
Center = "c"
class multiColumn(commandBase):
def __init__(self,source):
super(multiColumn,self).__init__(source,"multicolumn")
@property
def cols(self):
return int(self.source.args[0].value)
@property
def align(self):
return columnAlignment(self.source.args[1].value)
@property
def contents(self):
return nodify(list(self.source.args[2].contents))
def tabularToData(table,commands=None,excludeColumn=set()):
if table.name=="tabular":
ctable=str(table)
ctable=ctable.split("\n")
ctable=ctable[1:len(ctable)-1]
rows=[x.strip() for x in ''.join(ctable).split(r'\\') if x.strip()!='']
ltable=[]
ltable=[[c.strip() for c in r.split("&")] for r in rows]
lnewtable=[]
for row in ltable:
r=[]
for item in row:
texitem=TexSoup(item)
child=list(texitem.children)
if(len(child)==1 and child[0].name=="multicolumn"):
mcolel=child[0]
mcol=multiColumn(mcolel)
el=mcol.contents
el=desarg(el)
if commands!=None:
newCommand.runAll(el,commands)
for i in range(int(mcol.cols)):
r.append(el)
else:
if type(item) is str:
el=TexSoup(item)
else:
el=item
el=desarg(el)
if commands!=None:
newCommand.runAll(el,commands)
r.append(el)
lnewtable.append(r)
lens=[len(x) for x in lnewtable]
#Check if all rows have the same dimension
slens=set(lens)
if(len(slens)!=1):
raise ValueError("This tabular is not supported because lines have not the same column numbers for each row the coulumns numbers are {}".format(lens))
import numpy as np
table=np.array(lnewtable,TexNode)
table=np.delete(table,list(excludeColumn),1)
return table
else:
raise ValueError("Only tabular LaTeX environment is supported")
def extractMath(TexMath,Soup=False,commands=[]):
if not Soup and len(commands)>0:
raise ValueError("Commandw are only usable when Soup is True")
math=TexMath.find("$")
lst=list(math.contents)
mystr=str(lst[0])
if not Soup:
return mystr
mathsoup=None
try:
mathsoup=TexSoup(mystr)
except:
raise ValueError(f"Error when parsing latex math: {mystr}")
newCommand.runAll(mathsoup,commands)
return mathsoup