// POV-Ray macros to create text objects from geometric fonts // Last edited on 2010-04-03 16:20:45 by stolfi #include "fonts/js_defchar.inc" #macro font_table(fname,ini_seg,fin_seg) // Generates a graphic character table for the // geometric font {fname} for the code range {ini_seg..fin-seg}. // The client must define all parameters required by the font such as // {stemR}, {stemHY}, etc. before calling this macro. // The macro itself requires {stemR} to be defined. // The font must use the {defchar} macro in {js_defchar} to define // each character. // The table is centered on the Z axis and scaled so that // its width is fixed (32) irrespective of the font char size. #debug concat("loading the font from '", fname, "'...\n") // Undefine the font parameters: #declare widthChar = array[65536] #declare heightChar = array[65536] #declare depthChar = array[65536] #declare glyphChar = array[65536] // Load the font: #include fname #debug "obtaining the font metrics...\n" // Get the maximum height, width, and depth of all characters in whole font: #local max_wd = -99999.0; #local max_ht = -99999.0; #local max_dp = -99999.0; #local min_zc = +99999.0; // Min extent in {z} direction (thickness). #local max_zc = -99999.0; // Max extent in {z} direction (thickness). #local ndef_tot = 0; // Number of defined characters in whole font. // Get also character counts in segment only: #local ndef_seg = 0; // Number of defined characters in segment. #local cmin_seg = 999999; // Code of first defined character in segment. #local cmax_seg = -1; // Code of last defined character in segment. #local i = 0; #while (i <= 65535) #if (defined(glyphChar[i])) #local max_wd = max(max_wd, widthChar[i]); #local max_ht = max(max_ht, heightChar[i]); #local max_dp = max(max_dp, depthChar[i]); #local max_zc = max(max_zc, max_extent(glyphChar[i]).z); #local min_zc = min(min_zc, min_extent(glyphChar[i]).z); #local ndef_tot = ndef_tot + 1; #if ((i >= ini_seg) & (i <= fin_seg)) #local ndef_seg = ndef_seg + 1; #local cmin_seg = min(cmin_seg, i); #local cmax_seg = i; #end #end #local i = i + 1; #end #if (ndef_seg = 0) #error "no characters in specified code range" #end #debug "\n" #debug concat("in whole font:\n") #debug concat("max char wd = ", str(max_wd,5,3), "\n") #debug concat("max char ht = ", str(max_ht,5,3), "\n") #debug concat("max char dp = ", str(max_dp,5,3), "\n") #debug concat(str(ndef_tot,1,0)," defined characters\n") #debug concat("in segment", str(ini_seg,-5,0), "..", str(fin_seg,-5,0), ":\n") #debug concat("first char = ", str(cmin_seg,-5,0), "\n") #debug concat("last char = ", str(cmax_seg,5,0), "\n") #debug concat(str(ndef_seg,1,0), "defined characters\n") // Textures for table body: #local fi_glow = finish{ ambient 1.0 diffuse 0.0 } #local tx_chr = texture{ pigment{ color rgb < 1.00, 0.95, 0.00 > } finish{ fi_glow } } #local tx_und = texture{ pigment{ color rgb < 1.00, 0.30, 0.00 > } finish{ fi_glow } } #local tx_hex = texture{ pigment{ color rgb < 1.00, 0.50, 0.00 > } finish{ fi_glow } } #local tx_dec = texture{ pigment{ color rgb < 0.00, 1.00, 0.00 > } finish{ fi_glow } } #local tx_box = texture{ pigment{ color rgb < 0.30, 0.30, 0.30 > } finish{ fi_glow } } // Textures for table header: #local tx_top = texture{ tx_dec } // Top title. #local tx_sub = texture{ tx_hex } // Subtitles. #debug "assembling the table body...\n" // Compute the number of columns (fixed): #local ncols = 32; // Compute the range of rows {rmin,rmax}: #local rmin = div(cmin_seg,ncols); #local rmax = div(cmax_seg,ncols); // Total height of font chars #local char_ht = max_ht + max_dp; #local char_wd = max_wd; // Estimate dimensions of the caption text below the character: #local capt_ht = 0.80*char_wd; // Estimated height of caption text. #debug concat("caption height = ", str(capt_ht,5,3), "\n") // Table cell dimensions: #local cell_mr = 0.15*char_wd; // Margin around char in cell. #local cell_wd = char_wd + 2*cell_mr; // Cell width. #local cell_ht = char_ht + capt_ht + 3*cell_mr; // Cell height. #debug concat("cell wd = ", str(cell_wd,5,3), " ht = ", str(cell_ht,5,3), "\n") #local mrg = 0.1; // Table margin as fraction of cell size. #local tbl = union{ #local iy = rmin; // Index of current row. #local ty = -(1 + mrg)*cell_ht; // Y position of bottom of row. #local nrows = 0; // Number of rows actually shown. #while (iy <= rmax) #local ini = ncols*iy; #local fin = ini + ncols - 1; // Check whether row is empty: #local dec = ini; #local ncr = 0; // Defined chars in row: #while (dec <= fin) #if (defined(glyphChar[dec])) #local ncr = ncr + 1; #end #local dec = dec + 1; #end #if (ncr > 0) // Row is nonemty, put it down: object{ font_table_row( ini, fin, cmax_seg, max_wd, max_ht, max_dp, min_zc, max_zc, cell_wd, cell_ht, tx_chr, tx_und, tx_hex, tx_dec, tx_box ) translate < mrg*cell_wd, ty, 0 > } #local nrows = nrows + 1; #local ty = ty - cell_ht; #end #local iy = iy + 1; #end } // Ensure that the table body is X-centered and has fixed width: #local table_wd_goal = 32; #local table_wd = (ncols + 2*mrg)*cell_wd; #local table_ht = (nrows + 2*mrg)*cell_ht; #local tbl = object{ tbl translate < -table_wd/2, +table_ht/2, 0 > scale table_wd_goal/table_wd } #local head_ht_min = 1.0; // Min font height for header #local head_wd_max = table_wd_goal; // Max width of header #debug "assembling titles...\n" #local tit = object{ font_table_header( fname, ndef_tot, ini_seg, fin_seg, cmin_seg, cmax_seg, ndef_seg, max_wd, max_ht, max_dp, min_zc, max_zc, head_ht_min, head_wd_max, tx_top, tx_sub ) } #debug "assembling everything...\n" object{ font_table_vstack(tit, 0.6, tbl) } #debug "table built.\n" #end #macro font_table_row( ini, fin, fin_seg, max_wd, max_ht, max_dp, min_zc, max_zc, cell_wd, cell_ht, tx_chr, tx_und, tx_hex, tx_dec, tx_box ) union{ #debug "" #local ix = 0; #local tx = 0; #local dec = ini; #while (dec <= fin) object{ font_table_cell( dec, fin_seg, max_wd, max_ht, max_dp, min_zc, max_zc, cell_wd, cell_ht, tx_chr, tx_und, tx_hex, tx_dec, tx_box ) translate tx*x } #local tx = tx + cell_wd; #local ix = ix + 1; #local dec = dec + 1; #end #debug "\n" } #end #macro font_table_cell( dec, fin_seg, max_wd, max_ht, max_dp, min_zc, max_zc, cell_wd, cell_ht, tx_chr, tx_und, tx_hex, tx_dec, tx_box ) // Create a cell object for the character whose code is {dec} #local thinR = 0.05*min(max_wd, max_ht); // Thickness of various things. #local bbox_dz = -2.0*max(thinR,-min_zc); // Depth of outline/baseline decoration. // Placement of character in cell: #local cell_mr = (cell_wd - max_wd)/2; // Margin at sides and top. #local chbx_dy = cell_ht - cell_mr - max_ht; // Y pos of char's baseline. // Space allowed for caption: #local capt_wd = cell_wd - 2*cell_mr; #local capt_ht = cell_ht - 3*cell_mr; // Get the character {ch_obj} and its metrics: #ifdef (glyphChar[dec]) #debug concat("[", str(dec,-3,0), "]") #local ch_wd = widthChar[dec]; #local ch_ht = heightChar[dec]; #local ch_dp = depthChar[dec]; #local ch_obj = object{ glyphChar[dec] rotate -90*x texture{ tx_chr } } #else #local ch_wd = max_wd; #local ch_ht = max_ht; #local ch_dp = max_dp; #local ch_obj = object{ string_obj_undef_char(max_wd, max_ht, thinR) texture{ tx_und } } #end // Put it against a bounding box visualizer: #local chbx_obj = union{ union{ object{ ch_obj } box{ < 0, -ch_dp, -thinR >, < ch_wd, ch_ht, +thinR > translate bbox_dz*z texture{ tx_box } } translate (cell_wd - ch_wd)/2*x } cylinder{ 0.05*cell_wd*x, 0.95*cell_wd*x, 0.9*thinR translate bbox_dz*z texture{ tx_box } } } #local capt_obj = object{ font_table_cell_caption(dec, fin_seg, capt_wd, capt_ht, tx_hex, tx_dec) } #local capt_dy = chbx_dy - max_dp - cell_mr - (max_extent(capt_obj).y); union{ object{ chbx_obj translate chbx_dy*y } object{ capt_obj translate < cell_wd/2, capt_dy, 0> } } #end #macro font_table_cell_caption(cod, fin_seg, capt_wd, capt_ht, tx_hex, tx_dec) // Decide how many digits are needed: #local ndig = 3; #if (fin_seg > 999) #local ndig = 4; #end #if (fin_seg > 9999) #local ndig = 5; #end // Typeset the character code in decimal: #local dec_obj = text{ ttf "tt-fonts/courbd.ttf" str(dec,-ndig,0) 0.2, 0.0 texture{ tx_dec } } #local raw_dec_ht = (max_extent(dec_obj).y) - (min_extent(dec_obj).y); // Typset the character code in hex, scaled by 3/4: #local hex_str = font_table_hexadecimal_code(dec) #local hex_obj = text { ttf "tt-fonts/courbd.ttf" hex_str 0.2, 0.0 texture{ tx_hex } scale 0.75 } #local raw_hex_ht = (max_extent(hex_obj).y) - (min_extent(hex_obj).y); // Estimate the unscaled caption dimensions: #local raw_capt_sp = 0.20*(raw_dec_ht + raw_hex_ht)/2; // Vertical space between codes. #local capt_obj = font_table_vstack( font_table_hcenter(dec_obj), raw_capt_sp, font_table_hcenter(hex_obj) ) #local raw_capt_wd = (max_extent(capt_obj).x) - (min_extent(capt_obj).x); #local raw_capt_ht = (max_extent(capt_obj).y) - (min_extent(capt_obj).y); // Scale the caption code to fit the cell: // #debug concat("raw code wd = ", str(raw_capt_wd,5,3), " ht = ", str(raw_capt_ht,5,3), "\n") #local capt_scale = min(capt_wd/raw_capt_wd, capt_ht/raw_capt_ht); object{ capt_obj scale capt_scale } #end #macro font_table_header( fname, ndef_tot, ini_seg, fin_seg, cmin_seg, cmax_seg, ndef_seg, max_wd, max_ht, max_dp, min_zc, max_zc, ht_min, wd_max tx_top, tx_sub ) // Assembles a set of titles for the current geometric font. // Adjust sizes so that the header it has minimum height {ht_min} and // has maximum width {wd_max} (the latter overriding the former). #local hdy = 0.2; // Vertical displacement between subtitle rows. // Font name: #local title_0a = fname #local tobj_0a = object{ font_table_title_obj(title_0a, 2.0, tx_top) } // Character count and range: #local title_0b = concat( "total ", str(ndef_tot,1,0), " chars" ) #local tobj_0b = object{ font_table_title_obj(title_0b, 1.0, tx_top) } #local tobj_0 = object{ font_table_vstack(tobj_0a, hdy, tobj_0b) } #local tobj_0 = object { font_table_adjust_size(tobj_0, 3*ht_min+hdy, wd_max) } // Character count and range: #local title_1a = concat( "segment ", str(ini_seg,-5,0), "..", str(fin_seg,-5,0), " has ", str(ndef_seg,1,0), " chars ", str(cmin_seg,-5,0), "..", str(cmax_seg,-5,0) ) #local tobj_1a = object{ font_table_title_obj(title_1a, 1.0, tx_sub) } // Usual font design parameters: #local title_1b = concat( "stemR: ", str(stemR,5,3) #if (defined(pointR)) , " pointR: ", str(pointR,5,3) #end #if (defined(stemHY)) , " stemHY: ", str(stemHY,5,3) #end #if (defined(stretchH)) , " stretchH: ", str(stretchH,5,3) #end #if (defined(balanced)) , " balanced: ", str(balanced,1,0) #end ) #local tobj_1b = object{ font_table_title_obj(title_1b, 1.0, tx_sub) } // Max character matrics: #local title_1c = concat( "max char dims wd: ", str(max_wd,5,3), " ht: ", str(max_ht,5,3), " dp: ", str(max_dp,5,3) ) #local tobj_1c = object{ font_table_title_obj(title_1c, 1.0, tx_sub) } // Some important character widths: // Get dimensions of important chars: #local M_wd = font_table_get_char_dim(widthChar,"M"); #local N_wd = font_table_get_char_dim(widthChar,"N"); #local I_wd = font_table_get_char_dim(widthChar,"I"); #local m_wd = font_table_get_char_dim(widthChar,"m"); #local n_wd = font_table_get_char_dim(widthChar,"n"); #local i_wd = font_table_get_char_dim(widthChar,"i"); #local title_1d= concat( "widths M: ", str(M_wd,5,3), " N: ", str(N_wd,5,3), " I: ", str(I_wd,5,3), " m: ", str(m_wd,5,3), " n: ", str(n_wd,5,3), " i: ", str(i_wd,5,3) ) #local tobj_1d = object{ font_table_title_obj(title_1d, 1.0, tx_sub) } // Some important character heights: #local M_ht = font_table_get_char_dim(heightChar,"M"); #local x_ht = font_table_get_char_dim(heightChar,"x"); #local h_ht = font_table_get_char_dim(heightChar,"h"); #local title_1e= concat( "heights M: ", str(M_ht,5,3), " x: ", str(x_ht,5,3), " h: ", str(h_ht,5,3) ) #local tobj_1e = object{ font_table_title_obj(title_1e, 1.0, tx_sub) } // Some important character depths: #local p_dp = font_table_get_char_dim(depthChar,"p"); #local com_dp = font_table_get_char_dim(depthChar,","); #local title_1f= concat( "depths p: ", str(p_dp,5,3), " comma: ", str(com_dp,5,3) ) #local tobj_1f = object{ font_table_title_obj(title_1f, 1.0, tx_sub) } // All subtitles: #local tobj_1 = object{ font_table_vstack( font_table_vstack( font_table_vstack(tobj_1a, hdy, tobj_1b), hdy, font_table_vstack(tobj_1c, hdy, tobj_1d) ), hdy, font_table_vstack(tobj_1e, hdy, tobj_1f) ) } #local tobj_1 = object { font_table_adjust_size(tobj_1, 6*ht_min+5*hdy, wd_max) } #local tobj_all = object{ font_table_vstack(tobj_0, 2*hdy, tobj_1) } object{ tobj_all } #end #macro font_table_adjust_size(obj, ht_min, wd_max) // Scale {obj} so that its width is at most {wd_max} and // its height (if possible) at least {ht_min}: #local ht = max(max_extent(obj).y, -min_extent(obj).y); #if ((ht > 0.001) & (ht < ht_min)) #local obj = object{ obj scale ht_min/ht } #end #local wd = (max_extent(obj).x) - (min_extent(obj).x); #if (wd > wd_max) #local obj = object{ obj scale wd_max/wd } #end object{ obj } #end #macro font_table_hcenter(obj) // Returns the object {obj} centered in X. #local tobj = object{obj} #local tmin = min_extent(tobj); #local tmax = max_extent(tobj); object{ tobj translate -(tmax.x + tmin.x)/2*x } #end #macro font_table_vstack(obj1,vspc,obj2) // The objects {obj1,obj2} stacked in the {y} direction with {vspc} extra // space between. Object {obj2} stays put, {obj1} is displaced in {+y}. #local tobj1 = object{obj1} #local tobj2 = object{obj2} #local tmin1 = min_extent(tobj1); #local tmax2 = max_extent(tobj2); union{ object{ tobj1 translate (tmax2.y - tmin1.y + vspc)*y } object{ tobj2 } } #end #macro font_table_get_char_dim(arr,ch) // Gets the entry {ch} in font metric table {arr}, or returns 0 if not defined. #local dec = asc(ch); #if (defined(arr[dec])) #local dm = arr[dec]; #else #local dm = 0; #end dm #end #macro font_table_title_obj(bla,mag,txu) font_table_hcenter( text{ ttf "tt-fonts/cour.ttf" bla 0.2, 0.0 texture{ txu } scale mag } ) #end #macro font_table_octal_code(cod) #local hi = div(cod,256); #local hioct = 100*div(hi,64) + 10*mod(div(hi,8),8) + mod(hi,8); #local lo = mod(cod,256); #local looct = 100*div(lo,64) + 10*mod(div(lo,8),8) + mod(lo,8); #if (fin_seg <= 255) #local oct_srt = str(looct,-3,0) #else #local oct_str = concat(str(hioct,-3,0), " ", str(looct,-3,0)) #end oct_str #end #macro font_table_hexadecimal_code(cod) #local hi = div(cod,256); #local lo = mod(cod,256); #local digs = "0123456789ABCDEF" #local d3 = substr(digs, div(hi,16)+1,1) #local d2 = substr(digs, mod(hi,16)+1,1) #local d1 = substr(digs, div(lo,16)+1,1) #local d0 = substr(digs, mod(lo,16)+1,1) concat(d3,d2,d1,d0) #end