gt: 		return Bry_.Trim_end(src, Byte_ascii.Backslash, src_len);	
context: package gplx.xowa.xtns.hieros; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*;
  import gplx.langs.htmls.*; import gplx.xowa.htmls.core.htmls.*;
  class Hiero_html_mgr {		
  	private Bry_bfr html_bfr = Bry_bfr_.Reset(Io_mgr.Len_kb), content_bfr = Bry_bfr_.Reset(255), tbl_content_bfr = Bry_bfr_.Reset(Io_mgr.Len_kb), temp_bfr = Bry_bfr_.Reset(255);
  	private boolean cartouche_opened = false;
  	public static int scale = 100;
  	private Hiero_prefab_mgr prefab_mgr; private Hiero_file_mgr file_mgr; private Hiero_phoneme_mgr phoneme_mgr;
  	private Hiero_html_wtr wtr;
  	public Hiero_html_mgr(Hiero_xtn_mgr xtn_mgr) {
  		prefab_mgr = xtn_mgr.Prefab_mgr();
  		file_mgr = xtn_mgr.File_mgr();
  		phoneme_mgr = xtn_mgr.Phoneme_mgr();
  		wtr = new Hiero_html_wtr(this, phoneme_mgr);
  	}
  	public void Render_blocks(Bry_bfr final_bfr, Xoh_wtr_ctx hctx, Hiero_block[] blocks, int scale, boolean hr_enabled) {
  		wtr.Init_for_write(hctx);
  		Hiero_html_mgr.scale = scale;
  		tbl_content_bfr.Clear(); content_bfr.Clear(); temp_bfr.Clear();
  		cartouche_opened = false;
  		if (hr_enabled)
  			wtr.Hr(html_bfr);
  		int blocks_len = blocks.length;
  		for (int i = 0; i < blocks_len; i++) {
  			Hiero_block block = blocks[i];
  			if (block.Len() == 1)
  				Render_block_single(content_bfr, hctx, hr_enabled, block);
  			else
  				Render_block_many(content_bfr, hctx, hr_enabled, block);
  			if (content_bfr.Len_gt_0())
  				tbl_content_bfr.Add_bfr_and_clear(content_bfr);	// $tbl_content = $tbl + $content;
  		}
  		if (tbl_content_bfr.Len_gt_0())
  			wtr.Tbl_inner(html_bfr, tbl_content_bfr);
  		wtr.Tbl_outer(final_bfr, html_bfr);
  	}
  	private void Render_block_single(Bry_bfr content_bfr, Xoh_wtr_ctx hctx, boolean hr_enabled, Hiero_block block) {
  		byte[] code = block.Get_at(0);		// block has only one code (hence the proc name: Render_block_single)
  		byte b_0 = code[0];
  		switch (b_0) {
  			case Byte_ascii.Bang: {			// new_line
  				wtr.Tbl_eol(content_bfr);
  				if (hr_enabled)
  					wtr.Hr(content_bfr);
  				break;
  			}
  			case Byte_ascii.Lt: {			// cartouche bgn
  				wtr.Td(content_bfr, Render_glyph(hctx, Tkn_lt));
  				cartouche_opened = true;
  				wtr.Cartouche_bgn(content_bfr);
  				break;
  			}
  			case Byte_ascii.Gt: {			// cartouche end
  				wtr.Cartouche_end(content_bfr);
  				cartouche_opened = false;
  				wtr.Td(content_bfr, Render_glyph(hctx, Tkn_gt));
  				break;
  			}
  			default: {						// glyph or '.'
  				byte[] td_height = wtr.Td_height(Resize_glyph(code, cartouche_opened));
  				wtr.Td(content_bfr, Render_glyph(hctx, code, td_height));
  				break;
  			}
  		}
  	}
  	private void Render_block_many(Bry_bfr content_bfr, Xoh_wtr_ctx hctx, boolean hr_enabled, Hiero_block block) {			
  		temp_bfr.Clear(); // build prefab_bry: "convert all codes into '&' to test prefabs glyph"
  		int block_len = block.Len();
  		boolean amp = false;
  		for (int i = 0; i < block_len; i++) {
  			byte[] v = block.Get_at(i);
  			int v_len = v.length;
  			amp = false;
  			if (v_len == 1) {
  				switch (v[0]) {
  					case Byte_ascii.Brack_bgn: case Byte_ascii.Brack_end:
  					case Byte_ascii.Paren_bgn: case Byte_ascii.Paren_end:
  					case Byte_ascii.Star: case Byte_ascii.Colon: case Byte_ascii.Bang:
  						amp = true;
  						break;
  				}
  			}
  			if (amp)
  				temp_bfr.Add_byte(Byte_ascii.Amp);
  			else
  				temp_bfr.Add(v);
  		}
  		byte[] prefab_bry = temp_bfr.To_bry_and_clear();
  		Hiero_prefab_itm prefab_itm = prefab_mgr.Get_by_key(prefab_bry);
  		if (prefab_itm != null) {
  			byte[] td_height = wtr.Td_height(Resize_glyph(prefab_bry, cartouche_opened));
  			wtr.Td(content_bfr, Render_glyph(hctx, prefab_bry, td_height));
  		}
  		else {
  			int line_max = 0, total = 0, height = 0; // get block total height
  			byte[] glyph = null;
  			for (int i = 0; i < block_len; i++) {
  				byte[] v = block.Get_at(i);
  				int v_len = v.length;
  				if (v_len == 1) {
  					switch (v[0]) {
  						case Byte_ascii.Colon:
  							if (height > line_max)
  								line_max = height;
  							total += line_max;
  							line_max = 0;
  							continue;
  						case Byte_ascii.Star:
  							if (height > line_max)
  								line_max = height;
  							continue;
  					}
  				}
  				Hiero_phoneme_itm phoneme_itm = phoneme_mgr.Get_by_key(v);
  				if (phoneme_itm != null)
  					glyph = phoneme_itm.Gardiner_code();
  				else
  					glyph = v;
  				Hiero_file_itm file_itm = file_mgr.Get_by_key(glyph);
  				if (file_itm != null)
  					height = 2 + file_itm.File_h();
  			}
  			if (height > line_max)
  				line_max = height;
  			total += line_max;
  
  			// render all glyph into the block
  			for (int i = 0; i < block_len; i++) {
  				byte[] v = block.Get_at(i);
  				int v_len = v.length;
  				if (v_len == 1) {
  					switch (v[0]) {
  						case Byte_ascii.Colon:
  							temp_bfr.Add_str_a7("\n            <br/>");
  							continue;
  						case Byte_ascii.Star:
  							temp_bfr.Add_byte_space();
  							continue;
  					}
  				}
  				// resize the glyph according to the block total height
  				byte[] td_height = wtr.Td_height(Resize_glyph(v, cartouche_opened, total));
  				temp_bfr.Add(Render_glyph(hctx, v, td_height));
  			}
  			wtr.Td(content_bfr, temp_bfr.To_bry_and_clear());
  		}
  	}
  	private byte[] Render_glyph(Xoh_wtr_ctx hctx, byte[] src)					{return Render_glyph(hctx, src, Bry_.Empty);}
  	private byte[] Render_glyph(Xoh_wtr_ctx hctx, byte[] src, byte[] td_height) {
  		int src_len = src.length; if (src_len == 0) return src; // bounds check
  		byte byte_n = src[src_len - 1];
  		byte[] img_cls = byte_n == Byte_ascii.Backslash				// REF.MW:isMirrored
  			? Bry_cls_mirrored										// 'class="mw-mirrored" '
  			: Bry_.Empty;
  		byte[] glyph = Extract_code(src, src_len);					// trim backslashes from end; REF.MW:extractCode
  		if		(Bry_.Eq(glyph, Tkn_dot_dot))						// render void block
  			return wtr.Void(Bool_.N);
  		else if (Bry_.Eq(glyph, Tkn_dot))							// render 1/2 width void block
  			return wtr.Void(Bool_.Y);
  		else if (Bry_.Eq(glyph, Tkn_lt))
  			return wtr.Cartouche_img(hctx, Bool_.Y, glyph);
  		else if (Bry_.Eq(glyph, Tkn_gt))
  			return wtr.Cartouche_img(hctx, Bool_.N, glyph);
  
  		Hiero_phoneme_itm phoneme_itm = phoneme_mgr.Get_by_key(glyph);
  		Hiero_file_itm file_itm = null;
  		byte[] glyph_esc = Gfh_utl.Escape_html_as_bry(glyph);
  		if (phoneme_itm != null) {
  			byte[] code = phoneme_itm.Gardiner_code();
  			file_itm = file_mgr.Get_by_key(code);
  			if (file_itm != null)
  				return wtr.Img_phoneme(hctx, img_cls, td_height, glyph_esc, code);
  			else
  				return glyph_esc;
  		}
  		file_itm = file_mgr.Get_by_key(glyph);
  		return file_itm != null
  			? wtr.Img_file(hctx, img_cls, td_height, glyph_esc)
  			: glyph_esc
  			;
  	}
  	private int Resize_glyph(byte[] item, boolean cartouche_opened)	{return Resize_glyph(item, cartouche_opened, 0);}
  	private int Resize_glyph(byte[] item, boolean cartouche_opened, int total) {
  		item = Extract_code(item, item.length);
  		Hiero_phoneme_itm phoneme_itm = phoneme_mgr.Get_by_key(item);
  		byte[] glyph = phoneme_itm == null ? item : phoneme_itm.Gardiner_code();
  		int margin = 2 * Image_margin;
  		if (cartouche_opened)
  			margin += 2 * (int)((Cartouche_width * scale) / 100);
  		Hiero_file_itm file_itm = file_mgr.Get_by_key(glyph);
  		if (file_itm != null) {
  			int height = margin + file_itm.File_h();
  			if (total > 0) {
  				return total > Max_height
  				? (int)(((((height * Max_height) / total) - margin) * scale) / 100)
  				: (int)(((height - margin) * scale) / 100)
  				;
  			}
  			else {
  				return height > Max_height
  				? (int)(((((Max_height * Max_height) / height) - margin) * scale) / 100)
  				: (int)(((height - margin) * scale) / 100)
  				;
  			}
  		}
  		return (int)(((Max_height - margin) * scale) / 100);
  	}
  	private static byte[] Extract_code(byte[] src, int src_len) { // trim backslashes from end; REF.MW:extractCode
  