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) 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