''' Syntax-highlight POV-Ray Scene Description Language 2 HTML Auth: Ingo Janssen 2019-01-03 updated to support code up to POV-Ray 3.8 beta xx ''' import re, sys, string color_dic={'mlcom': '{color: #009922; font-style: italic;} /*Multi-line comment*/', 'slcom': '{color: #009922; font-style: italic;} /*Single line comment*/', 'cond': '{color: #880088; font-weight: bold;} /*Conditionals*/', 'dirc': '{color: #880088; font-weight: bold;} /*Directives*/', 'io': '{color: #880088; font-weight: bold;} /*File I/O*/', 'bin': '{color: #880088;} /*Binary Word Type*/', 'msg': '{color: #880088; font-weight: bold;} /*Message streams*/', 'atmo': '{color: #880088; font-weight: bold;} /*Atmospheric objects*/', 'atmod': '{color: #880088;} /*Atmospheric modifiers*/', 'cam': '{color: #880088; font-weight: bold;} /*Camera*/', 'cammod': '{color: #880088;} /*Camera modifiers*/', 'glob': '{color: #880088; font-weight: bold;} /*Global*/', 'globmod': '{color: #880088;} /*Global modifiers*/', 'inter': '{color: #880088; font-weight: bold;} /*Interior*/', 'lite': '{color: #880088; font-weight: bold;} /*Light source*/', 'litmod': '{color: #880088;} /*Light modifiers*/', 'mat': '{color: #880088; font-weight: bold;} /*Materials*/', 'matmod': '{color: #880088;} /*Material modifiers*/', 'med': '{color: #880088; font-weight: bold;} /*Media*/', 'medmod': '{color: #880088;} /*Media modifiers*/', 'obj': '{color: #880088; font-weight: bold;} /*Objects*/', 'obmod': '{color: #880088;} /*Object modifiers*/', 'mod': '{color: #880088;} /*Modifiers (general)*/', 'dot': '{color: #880088;} /*Dot operators*/', 'func': '{color: #880088;} /*Functions (float, vector, string)*/', 'ident': '{font-weight: bold;} /*Identifiers*/', 'math': '{color: #ff0000;} /*Mathematical operators*/', 'num': '{color: #009999;} /*Numbers*/', 'ptrn': '{color: #880088;} /*Patterns*/', 'str': '{color: #ff0000;} /*Strings*/', 'squig': '{color: #0000dd;} /*{}*/', } keywords={ "bin" :['sint8','sint32be','uint8','sint32le','sint16le','uint16be', 'sint16be','uint16le'], "obj" :['bicubic_patch','blob', 'box', 'cone', 'cylinder', 'cubic', 'difference', 'disc', 'height_field', 'intersection', 'isosurface', 'julia_fractal', 'lathe', 'merge', 'mesh', 'mesh2', 'parametric', 'plane', 'poly', 'polygon', 'prism', 'quadric', 'quartic', 'smooth_triangle','sor','sphere','sphere_sweep','superellipsoid', 'text','torus','triangle','union','light_group','object', 'clipped_by','bounded_by','contained_by','normal_indices', 'normal_vectors','uv_indices','face_indices','uv_vectors', 'vertex_vectors', 'ovus','lemon','polynomial'], "obmod" :['no_shadow','no_image','no_reflection','no_radiosity', 'inverse','sturm','hierarchy','hollow','double_illuminate', 'target','pass_through','split_union','photons', 'uv_mapping','all_intersections','b_spline', 'bezier_spline','component','composite', 'conic_sweep','cubic_spline','evaluate', 'flatness','global_lights','hypercomplex', 'linear_spline','linear_sweep','major_radius','max_gradient', 'max_iteration','natural_spline','open', 'precision','precompute','quadratic_spline','quaternion', 'reciprocal','slice','smooth','threshold','tolerance', 'ttf','u_steps','v_steps','projected_through', 'water_level','max_trace','all_intersections','polarity', 'importance','inside_vector'], "mod" :['scale','translate','rotate','matrix','transform'], "func" :['abs','acos','acosh','asc','asin','asinh','atan','atanh','atan2', 'ceil','cos','cosh','defined','degrees','div','exp','function', 'file_exists','floor','int','ln','log','max','min','mod','pow', 'radians','select','sin','sinh','sqrt','strcmp','strlen','tan', 'tanh','vdot','vlength','seed','rand','val','dimensions','cube', 'dimension_size','clock','pi','version','true','yes','on', 'false','no','off','clock_delta','initial_clock','final_clock', 'initial_frame','inside','final_frame','frame_number','clock_on', 'image_width','image_height','str','concat','chr','substr', 'strupr','strlwr','vstr','vaxis_rotate','vcross','vrotate', 'vnormalize','vturbulence','internal','pwr','max_extent','sqr', 'min_extent','spline','trace','append','read','write','array', 'sum','prod','datetime','now', 'global', 'mixed', 'dictionary', 'bitwise_and','bitwise_or','bitwise_xor','tau','optional'], "mat" :['material','texture','pigment','finish','normal','interior', 'interior_texture','material_map','texture_map','image_map', 'color_map','colour_map','pigment_map','bump_map','slope_map', 'normal_map','reflection','texture_list','warp'], "ptrn" :['agate','aoi','average','boxed','bozo','brick','bumps','cells', 'checker','crackle','form','metric','offset','solid', 'cylindrical','density_file','dents','facets','mandel','julia', 'magnet','gradient','granite','hexagon','image_pattern','leopard', 'marble','onion','pattern','pigment_pattern','planar','quilted', 'radial','ripples','slope','spherical','spiral1','spiral2', 'spotted','waves','wood','wrinkles','tile2','tiles','square', 'tiling','triangular','potential','pavement'], "matmod" :['gif','tga','iff','ppm','pgm','png','jpeg','tiff', 'sys','map_type','once','interpolate','all','quick_color', 'quick_colour','color','colour','no_bump_scale','accuracy', 'map_type','use_color','use_colour','ambient','pot', 'diffuse','brilliance','phong','phong_size','specular', 'roughness','metallic','crand','conserve_energy', 'irid','fresnel','albedo','falloff','exponent','thickness','turbulence', 'uv_mapping','cutaway_textures','reflection_exponent', 'alpha','agate_turb','brick_size','mortar','df3','coords', 'size','exterior','use_alpha','use_color','refraction', 'use_index','control0','control1','frequency','phase', 'ramp_wave','triangle_wave','sine_wave','scallop_wave', 'cubic_wave','poly_wave','bump_size','turbulence', 'octaves','omega','lambda','repeat','black_hole', 'toroidal','spherical','cylindrical','planar','offset','flip', 'strength','falloff','type','repeat','dist_exp', 'orientation','altitude','rgb','rgbt','rgbf','rgbft', 'gamma','premultiplied','srgb','srgbf','srgbft','srgbt','blend_gamma', 'blend_mode','bt709','bt2020','translucency','mm_per_unit'], "cam" :['camera'], "cammod":['perspective','orthographic','fisheye', 'ultra_wide_angle','omnimax','panoramic','cylinder','spherical', 'location','right','up','direction','sky','angle','look_at', 'blur_samples','aperture','focal_point','confidence','variance', 'bokeh'], "glob" :['global_settings','radiosity','photons','subsurface'], "globmod":['global_settings','adc_bailout','ambient_light','assumed_gamma', 'hf_gray_16','irid_wavelength','max_intersections', 'max_trace_level','number_of_waves','noise_generator','charset', 'ascii','utf8','sys','always_sample','brightness', 'count','error_bound','gray_threshold','load_file', 'low_error_factor','max_sample','media','minimum_reuse', 'nearest_count','normal','pretrace_end','pretrace_start', 'recursion_limit','save_file','spacing','count', 'gather','jitter','autostop','expand_thresholds','radius', 'distance_maximum','collect','range_divider', 'maximum_reuse', 'minimum_reuse'], "lite" :['light_source'], "litmod":['spotlight','parallel','area_light','radius','falloff', 'tightness','point_at','adaptive','circular','orient', 'looks_like','fade_power','fade_distance','media_attenuation', 'media_interaction','shadowless', 'area_illumination'], "inter" :['ior','caustics','dispersion','dispersion_samples','fade_colour', 'fade_color','fade_distance','fade_power'], "med" :['media','scattering','absorption','emission','density', 'density_map'], "medmod":['method','intervals','samples','confidence','variance', 'ratio','aa_threshold','aa_level','eccentricity','extinction'], "atmo" :['background','fog','sky_sphere','rainbow'], "atmod" :['fog_type','distance','turb_depth','fog_offset','fog_alt', 'arc_angle','falloff_angle','width'], "dot" :['t','u','v','x','y','z','red','green','blue', 'gray','transmit','filter','hf'] } directives={ "dirc" :['declare','default','include','local','macro','switch','undef', 'version'], "cond" :['break','case','else','end','if','ifdef','ifndef','range', 'while','for','elseif'], "io" :['fclose','fopen','read','write'], "msg" :['debug','error','render','statistics','warning'] } #build the keywords pattern string keypat = [r"(?P<%s>" % name + '|'.join(keywords[name])+")" for name in list(keywords.keys())] keypat = r"\b(?:"+'|'.join(keypat)+r")\b" #build the directives pattern string dirpat = [r"#\s*(?P<%s>" % name + '|'.join(directives[name])+")" for name in list(directives.keys())] dirpat = r"(?:"+'|'.join(dirpat)+r")\b" #patterns not coverd by the above mlcompat = r'(?P/\*)' #match start of multiline comment slcompat = r'(?P//[^\n]*)' #single line comment strpat = r'(?s)(?P(?}|{)' # { } numpat = r'(?P[.\d]+\d*([eE][+-]\d+)?)' #numbers mathpat = r'(?P\*|/|\+|-|=|!|>|<|&|\||\?)' #math-symbols identpat = r"(?P[A-Za-z_][A-Za-z_0-9]*)" #identifier #join it all to one string and compile to regular expression patlist=[strpat, mlcompat, slcompat, squigpat, dirpat, keypat, identpat, numpat, mathpat] pat='|'.join(patlist) pattern=re.compile(pat) #match all keywords #some patterns for separate matching steps mlcompattern=re.compile(r'(?P\*/|/\*)') #multi-line comment def find_end_comment(str, start): end=start end_comment=0 while end_comment ==0: p=mlcompattern.search(str, end) if not p: print("\n#warning: no closing comment (*/) found") end=len(str) end_comment=1 elif p.group() == '*/': end=p.end() end_comment=1 else: end=p.end() end=find_end_comment(str, end) return end def tag_it(str, Link): ident_flag = 0 taglist=[] link_dic={} start=0 while 1: p=pattern.search(str, start) try: p.group() except: break #no more patterns that match. start=p.end() if p.lastgroup == 'mlcom': #find end of comment and lastgr=p.lastgroup #deal with nested comments or s=p.start() #missing end of comment. end=find_end_comment(str, start) taglist.append((s,end,lastgr,None,None)) start=end elif p.lastgroup == 'ident': if Link: if ident_flag == 1: if p.group() in link_dic: link_dic[p.group()]=link_dic[p.group()]+'+' else: link_dic[p.group()]=p.group() Target=link_dic[p.group()] taglist.append((p.start(),p.end(),p.lastgroup,Target,None)) ident_flag=0 else: if p.group() in link_dic: Link=link_dic[p.group()] taglist.append((p.start(),p.end(),None,None,Link)) else: if ident_flag == 1: taglist.append((p.start(),p.end(),p.lastgroup,None,None)) ident_flag=0 else: taglist.append((p.start(),p.end(),p.lastgroup,None,None)) if p.lastgroup == 'dirc': if p.group() == '#declare' or p.group() == '#macro' or p.group() == '#local': ident_flag=1 return taglist def ad_tag(str,taglist): s=list(str) s=HTMLesc(s) for tag in taglist: if tag[2]!=None: s[tag[0]]=r'' %tag[2] +s[tag[0]] s[tag[1]-1]=s[tag[1]-1]+r'' if tag[3]!=None: s[tag[0]]=r'' %tag[3] +s[tag[0]] s[tag[1]-1]=s[tag[1]-1]+r'' if tag[4]!=None: s[tag[0]]=r'' %tag[4] +s[tag[0]] s[tag[1]-1]=s[tag[1]-1]+r'' return ''.join(s) def HTMLesc(strlist): escdic={"&":'&',"<":'<',">":'>','"':'"'} for char in range(len(strlist)): try: strlist[char]=escdic[strlist[char]] except: pass return strlist def build_page (Script, FileName, Link=None): taglist=tag_it(Script, Link) formatted=ad_tag(Script,taglist) css="" for key in list(color_dic.keys()): css=css+".%s %s\n" %(key,color_dic[key]) title=FileName page=r''' %s

%s :

%s
''' %(title,css,title,formatted) return page def use(): print(""" USE: povsdl2html.py [options] filename(s)\n OPTIONS: -h, --help : shows this message and quits povsdl2html.py -l, --link : creates links from identifiers and macro names to the place where they are declared. """) def convert(FileName, Link=None): try: f_in=open(FileName,'r') except IOError as xxx_todo_changeme: (errno, strerror) = xxx_todo_changeme.args print('I/O error(%s): "%s", %s' % (errno, FileName, strerror)) else: Script=f_in.read() f_in.close() HTMLpage=build_page(Script, FileName, Link) outfile=FileName.split('.') outfile=outfile[0]+'.html' f_out=open(outfile,'w') f_out.write(HTMLpage) f_out.close() def main(): import getopt, glob try: options, remainder=getopt.getopt(sys.argv[1:],"hl",["help","link"]) except getopt.GetoptError: sys.stderr.write("\n Unknown option specified") use() sys.exit(2) Link=None for (option, val) in options: if option in ("-h","--help"): use() sys.exit(2) if option in ("-l","--link"): Link=1 if len(remainder) == 0: sys.stderr.write("\n No filename specified\n") use() sys.exit(2) for FileNames in remainder: for FileName in glob.glob(FileNames): convert(FileName, Link) if __name__ == "__main__": main()