// POV-Ray macros to create text objects from geometric fonts // Last edited on 2010-04-05 13:07:00 by stolfi // See "js_defchar.inc" // In all these procedures, right is {+x} and objects // generally have the left edge at {x = 0}. // The {x}-axis is the baseline (if {vctr = false}) // or the medial line (if {vctr = true}). // For the glyphs in the {glyphChar} array, // top is {+z} and the observer is assumed to be on the {-y} side. // The range in the {y} direction (thickness) is font-dependent. // The objects assembled by these macros have the standard // orientation for POV-Ray {text} objects; that is, top is {+y} // and the observer is assumed to be on the {+z} side. // The range in the {z} direction (thickness) is font-dependent. #declare string_obj_dim_undef = -999999; // Means "undefined dimension". #macro string_obj_undef_char(wd,ht,rd) // A standard representation for an "undefined" character. // A ring that fits in the specified width{wd} and height {ht} // with minor radius {rd}. // The glyph has the standard POV-Ray text orientation. #local sz = min(wd,ht); #local totR = 0.48*sz; #local R = totR - rd; torus{ R, rd translate < wd/2, 0, sz/2 > rotate -90*x } #end #macro string_obj_get_raw_glyph(cod,wspc,vctr,ch,wd,ht,dp) // Assigns to {ch} the glyph with code {cod} from the {glyphChar} array. // Also assign s its nominal width, height and depth to {wd,ht,dp}. // The glyph is rotated to the standard POV-Ray text orientation. // If the character is undefined, sets {ch} to a conventional symbol // with width and height {wspc} and zero depth. // If {vctr} is true shifts the glyph vertically so that the center // of its nominal box is on the baseline. #if (defined(glyphChar[cod])) #declare ch = object{ glyphChar[cod] rotate -90*x } #declare wd = widthChar[cod]; #declare ht = heightChar[cod]; #declare dp = depthChar[cod]; #else #declare ch = object{ string_obj_undef_char(wspc,wspc,0.02*wspc) } #declare wd = wspc; #declare ht = wspc; #declare dp = 0; #end // Compute the centering displacement {dy}. #if (vctr) #local dy = -(ht - dp)/2; #else #local dy = 0; #end #declare ch = object{ ch translate dy*y } #end #macro string_obj_char(cod,wspc,vctr) // Returns the glyph with code {cod} from the {glyphChar} array. // The glyph will be in the standard POV-Ray text orientation. // If {cod} is not in the font, returns a default character // glyph with size {wspc}. // If {vctr} is true shifts the glyph vertically so that the center // of its nominal box is on the baseline. // Get the character {ch} and metrics {wd,ht,dp}: #local ch = 0; #local wd = 0; #local ht = 0; #local dp = 0; string_obj_get_raw_glyph(cod,wspc,vctr,ch,wd,ht,dp) object{ ch } #end #macro string_obj_accented_char(bs,ac,wspc,vctr,top,dh,dv) // Returns an object that is the glyph with code {bs} // modified by the accent glyph whose code is {ac}. // The accent is placed above {bs} if {top} is true, else below. // Assumes the standard POV-Ray text orientation throughout. // The accent glyph is shifted horizontally so that // the middle {x} of its box coincides with the middle {x} // of the base's box, plus the horizontal displacement {dh}. // Then it is shifted vertically so that its baseline // coincides with the top or bottom of the base glyph's // nominal box, plus the vertical displacement {dv}. // The client must figure out the result's bounding box. // Get the base object {bs_ch} and its metrics {bs_wd,bs_ht,bs_dp}: #local bs_ch = 0; #local bs_wd = 0; #local bs_ht = 0; #local bs_dp = 0; string_obj_get_raw_glyph(bs,wspc,vctr,bs_ch,bs_wd,bs_ht,bs_dp) // Get the accent object {ac_ch} and its metrics {ac_wd,ac_ht,ac_dp}: #local ac_ch = 0; #local ac_wd = 0; #local ac_ht = 0; #local ac_dp = 0; string_obj_get_raw_glyph(ac,wspc,vctr,ac_ch,ac_wd,ac_ht,ac_dp) // Put one above the other: merge{ object{ bs_ch } object{ ac_ch #if (top) translate bs_ht*y #else translate bs_dp*y #end translate (bs_wd - ac_wd)/2*x translate dh*x + dv*y } } #end #macro string_obj(txt,cspc,wspc,vctr) // Formats the string {txt} as an object using the // characters in the {glyphChar} array. // The result will be in the standard POV-Ra text orientation. // Adds {cspc} space between characters. Renders the space character // " " as a gap of width {wspc} (in addition to {cspc}). If {vctr} // is true centers each symbol vertically on the baseline. // Honors backspaces {chr(008)}. // Honors accent-over {chr(009)}. // Honors accent-under {chr(011)}. // Get the length of the string {n}: #local dim_undef = string_obj_dim_undef; #local n = strlen(txt); // Scan the characters and save them in arrays: #local i = 1; // Index of next char in string {txt}. // The following variables take backspaces into account: #local k = 0; // Output characters up to current X pos. #local nt = 0; // Number of glyphs in text. #local tgl = array[n]; // {tgl[0..nt-1]} are the glyphs in the text. #local twd = array[n]; // {twd[0..nt-1]} are their widths. #local tht = array[n]; // {tht[0..nt-1]} are their heights, or {dim_undef}. #local tdp = array[n]; // {tdp[0..nt-1]} are their depths, or {dim_undef}. #local tac = array[n]; // {tac[0..nt-1]} is accent position {-1,0,+1}. // The width {twd[k]} is always defined. // If the height {tht[k]} is {dim_undef}, then {tgl[k]} is to be ignored. #while (i <= n) // Gets the code of the next character: #local cod = asc(substr(txt,i,1)); #debug concat("typesetting character ", str(cod,1,0), "\n") // If defined in font, get the glyph {gl}, set {wd,ht,dp}, leave {ac} undef. // Else if space, set {wd = wspc}, leave {ht,dp,ac} undef. // Else if backspace, backup and leave {wd,ht,dp} undef, {ac=0}. // Else if accent-over, backup and leave {wd,ht,dp} undef, {ac=+1}. // Else if accent-under, backup and leave {wd,ht,dp} undef, {ac=-1}. // Else set {gl,wd,ht,dp} to a default symbol. #local gl = sphere{ <0.3,0.3,0.0>,0.3 }; // Should be {obj_undef}. #local wd = dim_undef; #local ht = dim_undef; #local dp = dim_undef; #local ac = dim_undef; #if (defined(glyphChar[cod])) // Use that character: #debug " font has a glyph with this code\n" string_obj_get_raw_glyph(cod,wspc,vctr,gl,wd,ht,dp) // Leave {ac} undef: #else // Check for control characters: #if (cod = 32) // Blank character, leave a gap: #debug " this is a space\n" #local wd = wspc; // Leave {ht,dp,ac} undefined. #end #if (cod = 8) // Backspace: move back to previous char if any. #debug " this is backspace\n" #if (k > 0) #local k = k - 1; // Leave {wd,ht,dp} undefined, {ac = 0}. #local ac = 0; #end #end #if (cod = 9) // Accent-over: move back to previous char, set {ac=+1} . #debug " this is accent-over\n" #if (k > 0) #local k = k - 1; // Leave {wd,ht,dp} undefined, {ac = +1}. #local ac = +1; #end #end #if (cod = 11) // Accent-under: move back to previous char, set {ac=-1} . #debug " this is accent-under\n" #if (k > 0) #local k = k - 1; // Leave {wd,ht,dp} undefined, {ac = -1}. #local ac = -1; #end #end #if ((cod != 8) & (cod != 9) & (cod != 11) & (cod != 32)) // Use standard "no such char" glyph: #debug " font has no such char, using placeholder\n" #local undefR = 0.075*wspc; #local gl = object{ string_obj_undef_char(wspc,wspc,undefR) } // Set {wd,ht,dp}, leave {ac} undef: #local wd = wspc; #local ht = wspc; #local dp = 0; #end #end #debug concat(" metrics wd = ", str(wd,5,3), " ht = ", str(ht,5,3), " dp = ", str(dp,5,3), " ac = ", str(ac,-1,0), "\n") // Now store the glyph if any: #if (wd != dim_undef) // Space or glyph. #if (k < nt) // Merge with pre-existing glyph: #debug concat(" merging with character[", str(k,1,0), "] tac = ", str(tac[k],-1,0), "\n") string_obj_overprint( tgl[k], twd[k], tht[k], tdp[k], gl, wd, ht, dp tac[k] ) #else // Extend array: #debug concat(" saving as character[", str(k,1,0), "]\n") #local tgl[k] = object{ gl } #local twd[k] = wd; #local tht[k] = ht; #local tdp[k] = dp; #local nt = k + 1; #end // The accent code should be 0 but: #local tac[k] = ac; #local k = k + 1; #else // Backspace or other control char. // Set the accent code of the char that was backspaced over. #debug concat(" back to character[", str(k,1,0), "] set tac = ", str(ac,-1,0), "\n") #local tac[k] = ac; #end // Go to the next character: #local i = i + 1; #end // Now concatenate the saved composite characters: merge{ #local k = 0; // Index of next char in string {txt}. #local dx = 0; // Current X position in output text. #local txp = array[n]; // {txp[0..nt-1]} are the X posns of text glyphs. #while (k < nt) // Adds the inter-character space: #if (k != 0) #local dx = dx + cspc; #end #if (tht[k] != dim_undef) object{ tgl[k] translate dx*x } #end #local dx = dx + twd[k]; #local k = k + 1; #end } #end #macro string_obj_overprint(bgl,bwd,bht,bdp, mgl,mwd,mht,mdp, ac) // Merges {bgl} with {mgl} and updates {bwd,bht,bdp} accordingly. // This macro returns nothing, it merely modifies {bgl,bwd,bht,bdp}. // Puts {mbl} at same baseline as {bgl} if {ac=0}, // over {bgl} if {ac=+1}, // under {bgl} if {ac=-1}. // The widths {bwd,mwd} must both be defined. // If {bht} is {dim_undef} then ignores {bgl} and treats it like // a blank space. Ditto for {mht} and {mgl}. #local dim_undef = string_obj_dim_undef; #if (mht = dim_undef) // Modifier is blank, merely update the width: #declare bwd = max(bwd,mwd); #else #if (bht = dim_undef) // Base only is blank, replace it by the modifier: #declare bgl = object{ mgl } #declare bwd = max(bwd,mwd); #declare bht = mht; #declare bdp = mdp; #else // Both are non-blank: #local bdx = max(0, (mwd - bwd)/2); #local mdx = max(0, (bwd - mwd)/2); // Define the vertical displacement {mdy} of the modifier: #if (ac = 0) #local mdy = 0; #end #if (ac = -1) #local mdy = 0; // Ignore descenders for under-accent. #end #if (ac = +1) #local mdy = bht; // Should consider core height but ignore ascenders. #end #declare bgl = merge{ object{ bgl translate bdx*x } object{ mgl translate mdx*x + mdy*y } } #declare bwd = max(bwd,mwd); #declare bht = max(bht,mht+mdy); #declare bdp = max(bdp,mdp-mdy); #end #end #end