# # Sawmill Document Description Library # # Rendering subroutines. # # # draw_border() # # Purpose: This draws a border around an SDD object # # Parameters: stylesdd: the SDD object to get the style information from # bordersdd: the SDD object to draw the border around # subroutine(draw_border(node page, node stylesdd, node bordersdd), ( #echo("Save1"); save_graphics_state(page); # Iterate through sides; 1=left, 2=right, 3=top, 4=bottom for (int side = 1; side < 5; side++) ( bool stroke = false; string sidename; if (side == 1) then sidename = "left"; else if (side == 2) then sidename = "right"; else if (side == 3) then sidename = "top"; else if (side == 4) then sidename = "bottom"; string specific_border_name = "border-" . sidename; string specific_width_name = "border-width-" . sidename; # Handle border style string border = ""; if (stylesdd?{"border"} and (@stylesdd{"border"} ne "")) then border = @stylesdd{"border"}; else if (stylesdd?{specific_border_name} and (@stylesdd{specific_border_name} ne "")) then border = @stylesdd{specific_border_name}; if (matches_regular_expression(border, "^1px solid #([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])$")) then ( float red = convert_base($1, 16, 10); float green = convert_base($2, 16, 10); float blue = convert_base($3, 16, 10); set_stroking_color(page, red / 255, green / 255, blue / 255); stroke = true; ); # if 1 pixel border # If there is no border, don't do anything (stroke is already false) else if ((border eq "") or (border eq "0px")) then ( ); # If there is no border, don't do anything (stroke is already false) else if (border eq "none") or (border eq "0px") then ( ); else error("Unknown bottom border format: " . border); # Handle widths. For now, we support only 0 (don't draw at all), or 1 (draw 1-pixel width). if (stylesdd?{specific_width_name}) then ( if (@stylesdd{specific_width_name} eq "0") then stroke = false; else if (@stylesdd{specific_width_name} eq "1") then ( ); # If it's some other integer, just treat it as 1. I.e., we don't currently support wider borders like "6" else if (matches_regular_expression(@stylesdd{specific_width_name}, "^[0-9]+$")) then ( ); else ( error("Internal: Unknown format for " . specific_width_name . " " . @stylesdd{specific_width_name}); ); ); if (stroke) then ( float xmin = get_sdd_value(@bordersdd{"xmin"}); float ymax = get_sdd_value(@bordersdd{"ymax"}); float width = get_sdd_value(@bordersdd{"width"}); float height = get_sdd_value(@bordersdd{"height"}); # Draw the line above the table cell if (side == 3) then ( draw_line(page, xmin, ymax, xmin + width, ymax); ); # Draw the line to the left of the table cell else if (side == 1) then ( draw_line(page, xmin, ymax, xmin, ymax - height); ); # If this is the last column, draw the line to the right of the table cell else if (side == 2) then ( draw_line(page, xmin + width, ymax, xmin + width, ymax - height); ); # If this is the last row, draw the line below the table cell else if (side == 4) then ( draw_line(page, xmin, ymax - height, xmin + width, ymax - height); ); ); # if stroke ); # for side #echo("Restore1"); restore_graphics_state(page); )); # draw_border() # # render_sdd_node_to_pdff_page() # # Purpose: This renders one SDD node to the currentPDF page. # # Parameters: sdd: the SDD node to render # parentscaled: true if the parent is scaled. Ignore any scaling factor on this node, if the parent has already done scaling. # subroutine(render_sdd_node_to_pdf_page(node sdd, bool parentscaled), ( #echo("render_sdd_node_to_pdf_page(); sdd=" . sdd . "; parentscaled=" . parentscaled); node page = v.page; float ymax = get_sdd_value(@sdd{"ymax"}); node page = v.page; bool drew_something = true; # If there is a scaling factor, change the PDF coordinate system to scale it. float saved_scale; bool scaled = false; if (sdd?{"scaling_factor"} and !parentscaled) then ( # ymax and xmin need to end up at the same place as they would be without a scale, so this # also requires a translation. # # newymax = ymax * scale + ytrans # so ymax = ymax * scale + ytrans # so ymax - ymax * scale = ytrans # so ytrans = ymax * (1 - scale) # float xmin = get_sdd_value(@sdd{"xmin"}); float ymax = get_sdd_value(@sdd{"ymax"}); float scale = @sdd{"scaling_factor"}; float ytrans = ymax * (1 - scale); float xtrans = xmin * (1 - scale); #echo("Save2"); #echo("scaling_factor: " . @sdd{"scaling_factor"}); save_graphics_state(page); set_coordinate_matrix(page, scale, 0, 0, scale, xtrans, ytrans); scaled = true; ); # scale if there is a scaling_factor if (false) then ( ); # Draw text else if (@sdd{"type"} eq "text") then ( #echo("Drawing text"); node font = @sdd{"font"}; float font_size = @sdd{"font_size"}; float xmin = get_sdd_value(@sdd{"xmin"}); float ymax = get_sdd_value(@sdd{"ymax"}); float height = get_sdd_value(@sdd{"height"}); string alignment; if (sdd?{"alignment"}) then alignment = @sdd{"alignment"}; else alignment = "left"; #echo("Save3"); save_graphics_state(page); # If there is a background color, use it if (sdd?{"background_color"}) then ( #echo("Drawing background color for text"); set_nonstroking_color(page, @sdd{"background_color"}{"red"}, @sdd{"background_color"}{"green"}, @sdd{"background_color"}{"blue"}); # DEBUG: Make the text background red # set_nonstroking_color(page, 1.0, 0, 0); float xmin = get_sdd_value(@sdd{"xmin"}); float ymax = get_sdd_value(@sdd{"ymax"}); float width = get_sdd_value(@sdd{"width"}); float height = get_sdd_value(@sdd{"height"}); draw_rectangle(page, xmin, ymax-height, width, height, true); ); if (sdd?{"text_color"}) then ( #echo("Setting text color"); set_nonstroking_color(page, @sdd{"text_color"}{"red"}, @sdd{"text_color"}{"green"}, @sdd{"text_color"}{"blue"}); ); else ( #echo("NO TEXT COLOR"); set_nonstroking_color(page, 0, 0, 0); ); bool wrap = @sdd{"wrap"}; #echo("wrap: " . wrap); string text = @sdd{"text"}; float width = get_sdd_value(@sdd{"width"}); #echo("width: " . width); float height; height = draw_wrapped_text(page, font, font_size, xmin, ymax, width, text, alignment, true, wrap, false, false); #echo("Restore2"); restore_graphics_state(page); ); # text # Render an outline marker, at the current location else if (@sdd{"type"} eq "outline_marker") then ( node pdf = @page{"pdf"}; add_outline_entry(pdf, @sdd{"label"}); ); # Render a page break, at the current location, by creating a new page. else if (@sdd{"type"} eq "page_break") then ( if (v.drew_something_on_this_page) then ( v.page = new_page(v.pdf, 0, 0, v.page_width, v.page_height); v.drew_something_on_this_page = true; ); ); # Draw a box else if (@sdd{"type"} eq "box") then ( #echo("box"); drew_something = false; float xmin = get_sdd_value(@sdd{"xmin"}); float ymax = get_sdd_value(@sdd{"ymax"}); # 2013-01-28 - GMF - Do autolayout on the box, so , for instance, will use its own width, rather than the container width. ThreadID:1281632 if (!sdd?{"performed_autlayout"} or (!sdd{"performed_autlayout"})) then ( # echo("Doing box autolayout of " . node_name(sdd)); v.autolayout = true; float discard = get_sdd_value(@sdd{"width"}); # echo("discard: " . discard); v.autolayout = false; sdd{"performed_autlayout"} = true; ); # Use draw_width, if available; otherwise use width. float width; if (sdd?{"draw_width"}) then ( width = get_sdd_value(@sdd{"draw_width"}); ) else width = get_sdd_value(@sdd{"width"}); float height = get_sdd_value(@sdd{"height"}); # If it has a fill_color, fill it with that color if (sdd?{"fill_color"}) then ( #echo("Save4"); save_graphics_state(page); set_nonstroking_color(page, @sdd{"fill_color"}{"red"}, @sdd{"fill_color"}{"green"}, @sdd{"fill_color"}{"blue"}); # DEBUG: change the box fill color # set_nonstroking_color(page, 0, 0, 1.0); draw_rectangle(page, xmin, ymax - height, width, height, true); #echo("Restore3"); restore_graphics_state(page); ); # if fill_color # If it has a stroke_color, stroke it with that color if (sdd?{"stroke_color"}) then ( #echo("Save5"); save_graphics_state(page); set_stroking_color(page, @sdd{"stroke_color"}{"red"}, @sdd{"stroke_color"}{"green"}, @sdd{"stroke_color"}{"blue"}); draw_rectangle(page, xmin, ymax - height, width, height, false); #echo("Restore4"); restore_graphics_state(page); ); # if stroke_color # Draw the CSS border draw_border(page, sdd, sdd); ); # box # Draw a line else if (@sdd{"type"} eq "line") then ( float xmin = get_sdd_value(@sdd{"xmin"}); float ymax = get_sdd_value(@sdd{"ymax"}); float width = get_sdd_value(@sdd{"width"}); float height = get_sdd_value(@sdd{"height"}); #echo("Save6"); save_graphics_state(page); set_stroking_color(page, @sdd{"stroke_color"}{"red"}, @sdd{"stroke_color"}{"green"}, @sdd{"stroke_color"}{"blue"}); draw_line(page, xmin, ymax, xmin + width, ymax - height); #echo("Restore5"); restore_graphics_state(page); ); # line # Draw an image else if (@sdd{"type"} eq "image") then ( float xmin = get_sdd_value(@sdd{"xmin"}); float ymax = get_sdd_value(@sdd{"ymax"}); float height = get_sdd_value(@sdd{"height"}); float ymin = ymax - height; node pdf = @page{"pdf"}; string imageName = load_image_into_pdf(pdf, @sdd{"image_handle"}); draw_image(page, imageName, xmin, ymin, @sdd{"image_width"}, @sdd{"image_height"}); ); # image # 2010-09-21 - GMF - Table gridlines are no longer rendered, and *might* not be needed at all, with the better # box border code. Or maybe they are? Commenting it out for now, in case we need it again later. # # Draw the gridlines of a table # else if (@sdd{"type"} eq "table_gridlines") then ( # # echo("Drawing table gridlines"); # node tbl = @sdd{"tbl"}; # ##echo("Drawing table gridline border"); # draw_border(page, tbl, tbl); # ## if ((@tbl{"border"} eq "") or (@tbl{"border"} eq "1")) then ( ## if ((@tbl{"border"} ne "") or ## (@tbl{"border-bottom"} ne "") or ## (@tbl{"border-top"} ne "") or ## (@tbl{"border-left"} ne "") or ## (@tbl{"border-right"} ne "")) then ( # ##if (tbl?{"border"}) then ## echo("table border: " . @tbl{"border"}); ##if (tbl?{"border-bottom"}) then ## echo("table border-bottom: " . @tbl{"border-bottom"}); # ## save_graphics_state(page); ## set_nonstroking_color(page, 0.8, 0.8, 0.8); ## set_stroking_color(page, 0.2, 0.2, 0.2); # # int rows = @tbl{"rows"}; # int columns = @tbl{"columns"}; # # for (int row = 0; row < rows; row++) ( # # int colspan_countdown = 0; # for (int column = 0; column < columns; column++) ( # # node outercell = sdd_get_table_outercell(tbl, row, column); # node innercell = sdd_get_table_innercell(tbl, row, column); # node cell_contents = @innercell{"items"}[0]; ##DEBUG draw_border(page, cell_contents, outercell); # ## float xmin = get_sdd_value(@outercell{"xmin"}); ## float ymax = get_sdd_value(@outercell{"ymax"}); ## float width = get_sdd_value(@outercell{"width"}); ## float height = get_sdd_value(@outercell{"height"}); ## draw_rectangle(page, xmin, ymax - height, width, height, false); # # # If we're in a colspan area, count down to 0. # if (colspan_countdown != 0) then # colspan_countdown--; # # # If this is a colspan call, skip as many columns as it spans # else if (outercell?{"colspan"}) then ( # colspan_countdown = @outercell{"colspan"}; ##echo("SKIPPING " . @outercell{"colspan"} . " COLSPAN CELLS DURING RENDER"); ## for (int colskip = 1; colskip < @outercell{"colspan"}; colskip++) ( ##echo("colskpi=" . colskip); ## node skip_outercell = sdd_get_table_outercell(tbl, row, column + colskip); ## float skip_xmin = get_sdd_value(@skip_outercell{"xmin"}); ## float skip_ymax = get_sdd_value(@skip_outercell{"ymax"}); ## float skip_width = get_sdd_value(@skip_outercell{"width"}); ## float skip_height = get_sdd_value(@skip_outercell{"height"}); ## ## # Draw the line above the table cell ## draw_line(page, skip_xmin, skip_ymax, skip_xmin + skip_width, skip_ymax); ## ## ); ## column += @outercell{"colspan"}; # ); # if colspan # # ); # for column # # ); # for row # ## ); # if stroke # ## ); # for side # ## draw_rectangle(page, xmin, ymax - height, width, height, false); # ## restore_graphics_state(page); # ## ); # if default border # ## else if ((@tbl{"border"} eq "none") or (@tbl{"border"} eq "")) then ( ###echo("NO BORDER ON TABLE"); ## ); # ## else if (matches_regular_expression(@tbl{"border"}, "^1px solid #([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])$")) then ( ## ## save_graphics_state(page); ## ## float red = convert_base($1, 16, 10); ## float green = convert_base($2, 16, 10); ## float blue = convert_base($3, 16, 10); ## ## set_stroking_color(page, red / 255, green / 255, blue / 255); ## ## float xmin = get_sdd_value(@tbl{"xmin"}); ## float ymax = get_sdd_value(@tbl{"ymax"}); ## float width = get_sdd_value(@tbl{"width"}); ## float height = get_sdd_value(@tbl{"height"}); ## ## draw_rectangle(page, xmin, ymax - height, width, height, false); ## ## restore_graphics_state(page); ## ## ); # if "1px solid" border # ## else ## error("Unable to parse table border description: '" . @tbl{"border"} . "'"); # # ); # table_gridlines_sdd # Allow there to be no type; that makes it a simple group node, which just displays its subsdds. else if (@sdd{"type"} eq "") then ( ); else ( error("Unknown SDD type: \"" . @sdd{"type"} . "\""); ); # Display all subsdds. if (subnode_exists(sdd, "items")) then ( node subsdd; foreach subsdd (sdd{"items"}) ( render_sdd_node_to_pdf_page(@subsdd, scaled or parentscaled); ); ); # group if (scaled) then ( #2D2B#echo("scaling:factor: " . @sdd{"scaling_factor"}); #echo("Restore6"); restore_graphics_state(page); ); # If we drew something, remember that (so we know later to add a page break). if (drew_something) then v.drew_something_on_this_page = true; )); # render_sdd_node_to_pdf_page(); subroutine(sdd_to_pdf(node sdd, node pdf, string pdfPathname), ( # Add a new page v.page = ""; set_node_type('v.page', 'node'); #echo("sdd_to_pdf sdd: " . sdd); # 2013-01-28 - GMF - Do autolayout on the box, so , for instance, will use its own width, rather than the container width. ThreadID:1281632 if (!sdd?{"performed_autlayout"} or (!sdd{"performed_autlayout"})) then ( # echo("Doing box autolayout of " . node_name(sdd)); v.autolayout = true; float discard = get_sdd_value(@sdd{"width"}); # echo("discard: " . discard); v.autolayout = false; sdd{"performed_autlayout"} = true; ); # Compute the actual height of this page (assuming margins of 20) float actual_page_height = get_sdd_value(@sdd{"height"}) + 40; #echo("actual_page_height: " . actual_page_height); # Compute the transformation needed to move this page we just rendered, to the top of this page. float ytrans = actual_page_height - v.page_height; v.page_height = actual_page_height; # v.page = new_page(pdf, 0, 0, v.page_width, v.page_height); v.page = new_page(pdf, 0, 0, v.page_width, actual_page_height); v.drew_something_on_this_page = false; # set_coordinate_matrix(page, scale, 0, 0, scale, xtrans, ytrans); set_coordinate_matrix(v.page, 1.0, 0, 0, 1.0, 0, ytrans); # Render the SDD render_sdd_node_to_pdf_page(sdd, false); # Write it to a file write_pdf(pdf, pdfPathname); )); # sdd_to_pdf