indexing description: "Implementation of objects that draw to an SVG STRING or file" keywords: SVG, Vision2, drawable, "vector graphics" author: "Colin Adams" copyright: "Copyright (c) Barr Rosenberg Reasearch Center LLC 2008" license: "GNU General Public License version 3 (see gpl-3.0.txt)" class SV_DRAWABLE_IMP inherit EV_DRAWABLE_I EXCEPTIONS export {NONE} all end KL_IMPORTED_STRING_ROUTINES export {NONE} all end UC_IMPORTED_UTF8_ROUTINES export {NONE} all end EV_FONT_CONSTANTS export {NONE} all end create make feature {NONE} -- Initialize make (a_interface: SV_DRAWABLE) is -- Initialize `Current'. do base_make (a_interface) end feature {EV_ANY} -- Initialization initialize is -- Initialize `Current'. do line_width := 1 dashed_line_style := False set_default_font set_default_colors drawing_mode := drawing_mode_copy clip_area := Void tile := Void set_is_initialized (True) end feature -- Access svg_text (a_serializer: SV_XML_SERIALIZER): STRING is -- Serialized version of `document' using `a_serializer'; -- Consider calling `optimize_svg_file_size' prior to calling this query. require a_serializer_not_void: a_serializer /= Void local l_output: XM_OUTPUT do create l_output l_output.set_output_to_string a_serializer.set_indent (indenting) a_serializer.set_omit_xml_declaration (omitting_xml_declaration) if wants_doctype then a_serializer.set_doctype_system (Svg_system_identifier) a_serializer.set_doctype_public (Svg_public_identifier) end a_serializer.set_method_xml a_serializer.set_encoding ("UTF-8") a_serializer.set_version ("1.0") a_serializer.set_standalone ("yes") a_serializer.serialize (document, l_output) Result := l_output.last_output ensure svg_text_not_void: Result /= Void svg_text_not_empty: not Result.is_empty end document: XM_DOCUMENT -- Tree for SVG document to be generated svg_prefix: STRING -- Optional prefix for elements in SVG namespace width: INTEGER_32 -- Horizontal size in pixels. height: INTEGER_32 -- Vertical size in pixels. line_width: INTEGER -- Line thickness. drawing_mode: INTEGER -- Logical operation on pixels when drawing. -- Not used (effectively `drawing_mode_copy' is used throughout). SVG has opacity and masking, but I don't think they are equivalent. tile: EV_PIXMAP -- Pixmap that is used to instead of background_color. -- If set to Void, `background_color' is used to fill. -- TODO: Not currently used. To do so is difficult, as we will have to -- write a PNG file for the pixmap to disk, and specify the URL of theis file. -- This means the files are of a semi-permanent nature (they cannot be deleted -- until the generated SVG file is deleted). dashed_line_style: BOOLEAN -- Are lines drawn dashed? clip_area: EV_RECTANGLE -- Clip area used to clip drawing; -- If set to Void, no clipping is applied. foreground_color: EV_COLOR -- Color of foreground features like text. background_color: EV_COLOR -- Color displayed behind foreground features. fill_color: EV_COLOR -- Color used for filling figures font: EV_FONT -- Typeface appearance for `Current' indenting: BOOLEAN -- Will the serializer indent the XML? omitting_xml_declaration: BOOLEAN -- Will the serializer omit the XML declaration? wants_doctype: BOOLEAN -- Does the user want a DOCTYPE to be emitted? uses_default_aspect_ratio: BOOLEAN -- Does the user want the "preserveAspectRatio" attribute to be omitted? aspect_ratio: STRING -- Value of "preserveAspectRatio" attribute to be generated; -- Only applicable if `uses_default_aspect_ratio' is `False'. -- If `Void', then a value of "none" is used. Svg_system_identifier: STRING is "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" -- SYSTEM identifier for SVG 1.1 Svg_public_identifier: STRING is "-//W3C//DTD SVG 1.1//EN" -- PUBLIC identifier for SVG 1.1 feature -- Setting set_svg_prefix (a_prefix: STRING) is -- Set prefix to use for SVG namespace. -- WARNING: call zero or one times, and do not call after `set_size' or any drawing operation. require a_prefix_not_void: a_prefix /= Void a_prefix_not_empty: not a_prefix.is_empty do cached_svg_namespace := Void svg_prefix := a_prefix ensure svg_prefix_set: svg_prefix = a_prefix svg_namespace_not_cached: cached_svg_namespace = Void end set_indenting (a_indenting: BOOLEAN) is -- Set `indenting' to `a_indenting'. do indenting := a_indenting ensure indenting_set: indenting = a_indenting end set_omitting_xml_declaration (a_omitting: BOOLEAN) is -- Set `omitting_xml_declaration' to `a_omitting'. do omitting_xml_declaration := a_omitting ensure omitting_xml_declaration_set: omitting_xml_declaration = a_omitting end set_wants_doctype (a_status: BOOLEAN) is -- Set `wants_doctype' to `a_status'. do wants_doctype := a_status ensure wants_doctype_set: wants_doctype = a_status end use_default_aspect_ratio (a_status: BOOLEAN) is -- Do not generate a "preserveAspectRatio" attribute if `a_status' = `True'. -- By default, a preserveAspectRatio="none" attribute is generated. -- Useless if called after `set_size'. do uses_default_aspect_ratio := a_status ensure default_aspect_ratio_used: uses_default_aspect_ratio = a_status end set_aspect_ratio (a_aspect_ratio: STRING) is -- Generate a preserveAspectRatio="`a_aspect_ratio'" attribute. -- Useless if called after `set_size'. require a_aspect_ratio_not_void: a_aspect_ratio /= Void do uses_default_aspect_ratio := False aspect_ratio := a_aspect_ratio ensure aspect_ratio_set: aspect_ratio = a_aspect_ratio default_aspect_ratio_not_used: not uses_default_aspect_ratio end set_line_width (a_width: INTEGER) is -- Assign `a_width' to `line_width'. do if a_width /= line_width then line_width := a_width assure_current_group set_painting_attributes (current_group) end end set_drawing_mode (a_mode: INTEGER) is -- Set drawing mode to `a_logical_mode'. do drawing_mode := a_mode end set_clip_area (an_area: EV_RECTANGLE) is -- Set area which will be refreshed. do clip_area := an_area new_svg_element set_painting_attributes (current_group) end set_clip_region (a_region: EV_REGION) is -- Assign a_region to the area which will be refreshed. do -- do nothing -- Not relevant to producing offline graphics? In any case, EV_REGION is opaque, so unusable. Nor is it used within integration. end remove_clipping is -- Do not apply any clipping. do clip_area := Void new_svg_element set_painting_attributes (current_group) end set_tile (a_pixmap: EV_PIXMAP) is -- Set tile used to fill figures. -- Set to Void to use `background_color' to fill. do tile := a_pixmap -- TODO: currently ignored. See comments to `tile'. end remove_tile is -- Do not apply a tile when filling. do tile := Void end enable_dashed_line_style is -- Draw lines dashed. do if not dashed_line_style then dashed_line_style := True assure_current_group set_painting_attributes (current_group) end end disable_dashed_line_style is -- Draw lines solid. do if dashed_line_style then dashed_line_style := False assure_current_group set_painting_attributes (current_group) end end set_default_font is -- Set the `font' to the default font. do create font end set_font (a_font: EV_FONT) is -- Assign `a_font' to `font'. do font := a_font.twin end set_foreground_color (a_color: like foreground_color) is -- Assign `a_color' to foreground_color. do if not a_color.is_equal (foreground_color) then foreground_color := a_color fill_color := foreground_color assure_current_group set_painting_attributes (current_group) end end set_background_color (a_color: like background_color) is -- Assign `a_color' to foreground_color. do if not a_color.is_equal (background_color) then background_color := a_color assure_current_group set_painting_attributes (current_group) end end set_default_colors is -- Set foreground and background color to their default values. do create foreground_color.make_with_rgb (0, 0, 0) create background_color.make_with_rgb (1, 1, 1) end feature {SV_DRAWABLE} -- Setting set_size (a_width, a_height: INTEGER_32) is -- Set width to `a_width' and height to `a_height'. -- Must not be called after any drawing commands. require positive: a_width >= 0 and a_height >= 0 local l_aspect_ratio: STRING do width := a_width height := a_height ensure_document set_painting_attributes (current_group) document.root_element.add_attribute (Svg_width_attribute, Null_namespace, "100%%") document.root_element.add_attribute (Svg_height_attribute, Null_namespace, "100%%") document.root_element.add_attribute (Svg_viewbox_attribute, Null_namespace, "0 0 " + width.out + " " + height.out) if not uses_default_aspect_ratio then if aspect_ratio = Void then l_aspect_ratio := "none" else l_aspect_ratio := aspect_ratio end document.root_element.add_attribute (Svg_preserve_aspect_ratio_attribute, Null_namespace, l_aspect_ratio) end ensure set: width = a_width and height = a_height width_attribute_present: document.root_element.has_attribute_by_name (Svg_width_attribute) height_attribute_present: document.root_element.has_attribute_by_name (Svg_height_attribute) viewbox_attribute_present: document.root_element.has_attribute_by_name (Svg_viewbox_attribute) end feature -- Basic operation optimize_svg_file_size is -- Eliminate empty "g" elements, to minimize SVG file size. -- Best called (if at all) immediately prior to `svg_text'. local l_element: XM_ELEMENT l_cursor: DS_LINKED_LIST_CURSOR [XM_NODE] do ensure_document from l_cursor := document.root_element.new_cursor l_cursor.start until l_cursor.after loop l_element ?= l_cursor.item if l_element /= Void and then l_element.name.same_string (Svg_g_element_name) and l_element.namespace = svg_namespace and l_element.elements.is_empty then l_cursor.remove else l_cursor.forth end end end feature -- Clearing and drawing operations redraw is -- Force `Current' to redraw itself. do -- Not relevant to offline graphics production. end clear is -- Erase `Current' with `background_color'. do clear_rectangle (0, 0, width, height) end clear_rectangle (x1, y1, a_width, a_height: INTEGER) is -- Draw rectangle with upper-left corner on (`x', `y') -- with size `a_width' and `a_height' in `background_color'. do fill_color := background_color fill_rectangle (x1, y1, a_width, a_height) fill_color := foreground_color end feature -- Duplication sub_pixmap (area: EV_RECTANGLE): EV_PIXMAP is -- Pixmap region of `Current' represented by rectangle `area' do raise ("SVG rasterization not implemented (and won't be!).") end feature -- Drawing operations draw_point (x, y: INTEGER) is -- Draw point at (`x', 'y'). do draw_segment (x, y, x, y) end draw_text (x, y: INTEGER; a_text: STRING_GENERAL) is -- Draw `a_text' with left of baseline at (`x', `y') using `font'. local l_element: XM_ELEMENT l_text: XM_CHARACTER_DATA do ensure_document create l_element.make_last (current_group, Svg_text_element_name, svg_namespace) l_element.add_attribute (Svg_x_attribute, Null_namespace, x.out) l_element.add_attribute (Svg_y_attribute, Null_namespace, y.out) add_font_attributes (l_element) if a_text.is_string_8 then create l_text.make_last (l_element, a_text.as_string_8) else create l_text.make_last (l_element, utf8_string (a_text)) end ensure then last_child_is_element: current_group.last /= Void -- and then is an XM_ELEMENT end draw_rotated_text (x, y: INTEGER; angle: REAL; a_text: STRING_GENERAL) is -- Draw rotated text `a_text' with left of baseline at (`x', `y') using `font'. -- Rotation is number of radians counter-clockwise from horizontal plane. local l_node: XM_NODE l_element: XM_ELEMENT do draw_text (x, y, a_text) l_node := current_group.last l_element ?= l_node check l_element_not_void: l_element /= Void -- postcondition of `draw_text' end l_element.add_attribute (Svg_transform_attribute, Null_namespace, "rotate(" + (-angle).out + "rad," + x.out + "," + y.out + ")") end draw_text_top_left (x, y: INTEGER; a_text: STRING_GENERAL) is -- Draw `a_text' with top left corner at (`x', `y') using `font'. local l_node: XM_NODE l_element: XM_ELEMENT do draw_text (x, y, a_text) l_node := current_group.last l_element ?= l_node check l_element_not_void: l_element /= Void -- from postcondition of `draw_text' end l_element.add_attribute (Svg_dy_attribute, Null_namespace, font.height.out) end draw_ellipsed_text (x, y: INTEGER; a_text: STRING_GENERAL; clipping_width: INTEGER) is -- Draw `a_text' with left of baseline at (`x', `y') using `font'. -- Text is clipped to `clipping_width' in pixels and ellipses are displayed -- to show truncated characters if any. do raise ("Ellipsed text not supported") end draw_ellipsed_text_top_left (x, y: INTEGER; a_text: STRING_GENERAL; clipping_width: INTEGER) is -- Draw `a_text' with top left corner at (`x', `y') using `font'. -- Text is clipped to `clipping_width' in pixels and ellipses are displayed -- to show truncated characters if any. do raise ("Ellipsed text not supported") end draw_segment (x1, y1, x2, y2: INTEGER) is -- Draw line segment from (`x1', 'y1') to (`x2', 'y2'). local l_element: XM_ELEMENT do ensure_document create l_element.make_last (current_group, Svg_line_element_name, svg_namespace) l_element.add_attribute (Svg_x1_attribute, Null_namespace, x1.out) l_element.add_attribute (Svg_y1_attribute, Null_namespace, y1.out) l_element.add_attribute (Svg_x2_attribute, Null_namespace, x2.out) l_element.add_attribute (Svg_y2_attribute, Null_namespace, y2.out) end draw_pixmap (x, y: INTEGER; a_pixmap: EV_PIXMAP) is -- Draw `a_pixmap' with upper-left corner on (`x', 'y'). do -- see postscript implementation for clues raise ("Pixmap drawing not yet implemented") end draw_sub_pixmap (x, y: INTEGER; a_pixmap: EV_PIXMAP; area: EV_RECTANGLE) is -- Draw `area' of `a_pixmap' with upper-left corner on (`x', `y'). local l_pixmap: EV_PIXMAP do create l_pixmap.make_with_size (area.width, area.height) l_pixmap.draw_sub_pixmap (0, 0, a_pixmap, area) draw_pixmap (x, y, l_pixmap) end draw_sub_pixel_buffer (a_x, a_y: INTEGER; a_pixel_buffer: EV_PIXEL_BUFFER; area: EV_RECTANGLE) is -- Draw `area' of `a_pixel_buffer' with upper-left corner on (`a_x', `a_y'). do raise ("Pixmap drawing not yet implemented") end draw_arc (x, y, a_bounding_width, a_bounding_height: INTEGER; a_start_angle, a_aperture: REAL) is -- Draw a part of an ellipse defined by a rectangular area with an -- upper left corner at `x',`y', width `a_bounding_width' and height -- `a_bounding_height'. -- Start at `a_start_angle' and stop at `a_start_angle' + `a_aperture'. -- Angles are measured in radians. local l_element: XM_ELEMENT l_path_data: STRING l_cx, l_cy, l_rx, l_ry, l_x1, l_y1, l_x2, l_y2: DOUBLE l_fa, l_fs: BOOLEAN do ensure_document l_cx := x + a_bounding_width / 2 l_cy := y + a_bounding_height / 2 l_rx := a_bounding_width / 2 l_ry := a_bounding_height / 2 -- for the following formulae, see appendix F.6.4 in http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes -- (the formulae simplify greatly as we have a rotation angle of zero) l_x1 := l_rx * cosine (a_start_angle) + l_cx l_y1 := l_ry * sine (a_start_angle) + l_cy l_x2 := l_rx * cosine (a_start_angle + a_aperture) + l_cx l_y2 := l_ry * sine (a_start_angle + a_aperture) + l_cy l_fa := (a_aperture.abs > pi) l_fs := (a_aperture > 0) l_path_data := "M" + l_x1.out + "," + l_y1.out + "A" + l_rx.out + "," + l_ry.out + " 0 " if l_fa then l_path_data := l_path_data + " 0," else l_path_data := l_path_data + " 1," end if l_fs then l_path_data := l_path_data + "0 " else l_path_data := l_path_data + "1 " end l_path_data := l_path_data + l_x2.out + "," + l_y2.out create l_element.make_last (current_group, Svg_path_element_name, svg_namespace) l_element.add_attribute (Svg_d_attribute, Null_namespace, l_path_data) end draw_rectangle (x, y, a_width, a_height: INTEGER) is -- Draw rectangle with upper-left corner on (`x', 'y') -- with size `a_width' and `a_height'. local l_element: XM_ELEMENT do ensure_document create l_element.make_last (current_group, Svg_rect_element_name, svg_namespace) l_element.add_attribute (Svg_x_attribute, Null_namespace, x.out) l_element.add_attribute (Svg_y_attribute, Null_namespace, y.out) l_element.add_attribute (Svg_width_attribute, Null_namespace, a_width.out) l_element.add_attribute (Svg_height_attribute, Null_namespace, a_height.out) ensure then last_child_is_element: current_group.last /= Void -- and then is an XM_ELEMENT end draw_ellipse (x, y, a_bounding_width, a_bounding_height: INTEGER) is -- Draw an ellipse defined by a rectangular area with an -- upper left corner at `x',`y', width `a_bounding_width' and height -- `a_bounding_height'. local l_cx, l_cy, l_rx, l_ry: DOUBLE l_element: XM_ELEMENT do ensure_document l_cx := x + a_bounding_width / 2 l_cy := y + a_bounding_height / 2 l_rx := a_bounding_width / 2 l_ry := a_bounding_height / 2 create l_element.make_last (current_group, Svg_ellipse_element_name, svg_namespace) l_element.add_attribute (Svg_cx_attribute, Null_namespace, l_cx.out) l_element.add_attribute (Svg_cy_attribute, Null_namespace, l_cy.out) l_element.add_attribute (Svg_rx_attribute, Null_namespace, l_rx.out) l_element.add_attribute (Svg_ry_attribute, Null_namespace, l_ry.out) ensure then last_child_is_element: current_group.last /= Void -- and then is an XM_ELEMENT end draw_polyline (points: ARRAY [EV_COORDINATE]; is_closed: BOOLEAN) is -- Draw line segments between subsequent points in -- `points'. If `is_closed' draw line segment between first -- and last point in `points'. local l_points: STRING l_element: XM_ELEMENT do ensure_document if is_closed then create l_element.make_last (current_group, Svg_polygon_element_name, svg_namespace) else create l_element.make_last (current_group, Svg_polyline_element_name, svg_namespace) end l_points := points_list (points) l_element.add_attribute (Svg_points_attribute, Null_namespace, l_points) ensure then last_child_is_element: current_group.last /= Void -- and then is an XM_ELEMENT end draw_pie_slice (x, y, a_bounding_width, a_bounding_height: INTEGER; a_start_angle, a_aperture: REAL) is -- Draw part of an ellipse defined by a rectangular area with an -- upper left corner at `x',`y', width `a_bounding_width' and height -- `a_bounding_height'. -- Start at `a_start_angle' and stop at `a_start_angle' + `a_aperture'. -- The arc is then closed by two segments through (`x', 'y'). -- Angles are measured in radians. local l_element: XM_ELEMENT l_path_data: STRING l_cx, l_cy, l_rx, l_ry, l_x1, l_y1, l_x2, l_y2: DOUBLE l_fa, l_fs: BOOLEAN do ensure_document l_cx := x + a_bounding_width / 2 l_cy := y + a_bounding_height / 2 l_rx := a_bounding_width / 2 l_ry := a_bounding_height / 2 -- for the following formulae, see appendix F.6.4 in http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes -- (the formulae simplify greatly as we have a rotation angle of zero) l_x1 := l_rx * cosine (a_start_angle) + l_cx l_y1 := l_ry * sine (a_start_angle) + l_cy l_x2 := l_rx * cosine (a_start_angle + a_aperture) + l_cx l_y2 := l_ry * sine (a_start_angle + a_aperture) + l_cy l_fa := (a_aperture.abs > pi) l_fs := (a_aperture > 0) l_path_data := "M" + l_cx.out + "," + l_cy.out + "L" + l_x1.out + "," + l_y1.out + "A" + l_rx.out + "," + l_ry.out + " 0 " if l_fa then l_path_data := l_path_data + " 0," else l_path_data := l_path_data + " 1," end if l_fs then l_path_data := l_path_data + "0 " else l_path_data := l_path_data + "1 " end l_path_data := l_path_data + l_x2.out + "," + l_y2.out + "Z" create l_element.make_last (current_group, Svg_path_element_name, svg_namespace) l_element.add_attribute (Svg_d_attribute, Null_namespace, l_path_data) ensure then last_child_is_element: current_group.last /= Void -- and then is an XM_ELEMENT end feature -- Drawing operations (filled) fill_rectangle (x, y, a_width, a_height: INTEGER) is -- Draw rectangle with upper-left corner on (`x', 'y') -- with size `a_width' and `a_height'. do draw_rectangle (x, y, a_width, a_height) fill_last_element end fill_ellipse (x, y, a_bounding_width, a_bounding_height: INTEGER) is -- Fill an ellipse defined by a rectangular area with an -- upper left corner at `x',`y', width `a_bounding_width' and height -- `a_bounding_height'. do draw_ellipse (x, y, a_bounding_width, a_bounding_height) fill_last_element end fill_polygon (points: ARRAY [EV_COORDINATE]) is -- Draw line segments between subsequent points in `points'. -- Fill with `background_color'. do draw_polyline (points, True) fill_last_element end fill_pie_slice (x, y, a_bounding_width, a_bounding_height: INTEGER; a_start_angle, an_aperture: REAL) is -- Fill part of an ellipse defined by a rectangular area with an -- upper left corner at `x',`y', width `a_bounding_width' and height -- `a_bounding_height'. -- Start at `a_start_angle' and stop at `a_start_angle' + `an_aperture'. -- The arc is then closed by two segments through (`x', 'y'). -- Angles are measured in radians. do draw_pie_slice (x, y, a_bounding_width, a_bounding_height, a_start_angle, an_aperture) fill_last_element end feature -- Commands save_to_named_file (a_file_name: FILE_NAME; a_serializer: SV_XML_SERIALIZER; a_compress: BOOLEAN) is -- Save `Current' to `a_file_name' with optional optimization of SVG file size. require a_file_name_not_void: a_file_name /= Void a_serializer_not_void: a_serializer /= Void local l_file: PLAIN_TEXT_FILE do if a_compress then optimize_svg_file_size end create l_file.make_open_write (a_file_name) l_file.put_string (svg_text (a_serializer)) l_file.close end feature {EV_ANY, EV_ANY_I} -- Command destroy is -- Destroy underlying native toolkit objects. -- Render `Current' unusable. -- Any feature calls after a call to destroy are -- invalid. do set_is_destroyed (True) end feature {NONE} -- XML names Svg_element_name: STRING is "svg" -- Local name for SVG document element cached_svg_namespace: like svg_namespace -- Cache for `svg_namespace' svg_namespace: XM_NAMESPACE is -- Namespace URI for SVG elements do if cached_svg_namespace = Void then create cached_svg_namespace.make (svg_prefix, "http://www.w3.org/2000/svg") end Result := cached_svg_namespace ensure svg_namespace_not_void: Result /= Void end Null_namespace: XM_NAMESPACE is -- Namespace for global attributes once create Result.make (Void, "") ensure Null_namespace_not_void: Result /= Void end Svg_g_element_name: STRING is "g" -- Local name for SVG g element Svg_text_element_name: STRING is "text" -- Local name for SVG text element Svg_line_element_name: STRING is "line" -- Local name for SVG line element Svg_ellipse_element_name: STRING is "ellipse" -- Local name for SVG ellipse element Svg_rect_element_name: STRING is "rect" -- Local name for SVG rect element Svg_path_element_name: STRING is "path" -- Local name for SVG path element Svg_polygon_element_name: STRING is "polygon" -- Local name for SVG polygon element Svg_polyline_element_name: STRING is "polyline" -- Local name for SVG polyline element Svg_d_attribute: STRING is "d" -- Name for "d" attribute Svg_x_attribute: STRING is "x" -- Name for "x" attribute Svg_y_attribute: STRING is "y" -- Name for "y" attribute Svg_x1_attribute: STRING is "x1" -- Name for "x1" attribute Svg_y1_attribute: STRING is "y1" -- Name for "y1" attribute Svg_x2_attribute: STRING is "x2" -- Name for "x2" attribute Svg_y2_attribute: STRING is "y2" -- Name for "y2" attribute Svg_cx_attribute: STRING is "cx" -- Name for "cx" attribute Svg_cy_attribute: STRING is "cy" -- Name for "cy" attribute Svg_rx_attribute: STRING is "rx" -- Name for "rx" attribute Svg_ry_attribute: STRING is "ry" -- Name for "ry" attribute Svg_dy_attribute: STRING is "dy" -- Name for "dy" attribute Svg_width_attribute: STRING is "width" -- Name for "width" attribute Svg_height_attribute: STRING is "height" -- Name for "height" attribute Svg_points_attribute: STRING is "points" -- Name for "points" attribute Svg_font_family_attribute: STRING is "font-family" -- Name for "font-family" attribute Svg_font_size_attribute: STRING is "font-size" -- Name for "font-size" attribute Svg_font_style_attribute: STRING is "font-style" -- Name for "font-style" attribute Svg_font_weight_attribute: STRING is "font-weight" -- Name for "font-weight" attribute Svg_transform_attribute: STRING is "tranform" -- Name for "transform" attribute Svg_stroke_attribute: STRING is "stroke" -- Name for "stroke" attribute Svg_stroke_width_attribute: STRING is "stroke-width" -- Name for "stroke-width" attribute Svg_stroke_dasharray_attribute: STRING is "stroke-dasharray" -- Name for "stroke-dasharray" attribute Svg_fill_attribute: STRING is "fill" -- Name for "fill" attribute Svg_overflow_attribute: STRING is "overflow" -- Name for "overflow" attribute Svg_viewbox_attribute: STRING is "viewBox" -- Name for "viewbox" attribute Svg_preserve_aspect_ratio_attribute: STRING is "preserveAspectRatio" -- Name for "preserveAspectRatio" attribute feature {NONE} -- Implementation current_group: XM_ELEMENT -- Current grouping element (svg or g) to which -- drawing commands are added assure_current_group is -- Create new g `current_group' as child of document element. do ensure_document create current_group.make_last (document.root_element, Svg_g_element_name, svg_namespace) current_group.add_attribute (Svg_fill_attribute, Null_namespace, "none") ensure current_group_changed: current_group /= old current_group current_group_is_g: True -- Not exported: document.root_element.named_same_name (current_group, Svg_g_element_name) current_group_owned_by_document_element: document.root_element.elements.has (current_group) fill_attribute_present: current_group.has_attribute_by_name (Svg_fill_attribute) end new_svg_element is -- Create new svg `current_group' as child of document element. do ensure_document create current_group.make_last (document.root_element, Svg_element_name, svg_namespace) if clip_area = Void then current_group.add_attribute (Svg_overflow_attribute, Null_namespace, "visible") current_group.add_attribute (Svg_width_attribute, Null_namespace, "100%%") current_group.add_attribute (Svg_height_attribute, Null_namespace, "100%%") else current_group.add_attribute (Svg_overflow_attribute, Null_namespace, "hidden") current_group.add_attribute (Svg_x_attribute, Null_namespace, clip_area.x.out) current_group.add_attribute (Svg_y_attribute, Null_namespace, clip_area.y.out) current_group.add_attribute (Svg_width_attribute, Null_namespace, clip_area.width.out) current_group.add_attribute (Svg_height_attribute, Null_namespace, clip_area.height.out) end current_group.add_attribute (Svg_fill_attribute, Null_namespace, "none") ensure current_group_changed: current_group /= old current_group current_group_is_g: True -- Not exported: document.root_element.named_same_name (current_group, Svg_element_name) current_group_owned_by_document_element: document.root_element.elements.has (current_group) overflow_attribute_present: current_group.has_attribute_by_name (Svg_overflow_attribute) x_attribute_present: clip_area /= Void implies current_group.has_attribute_by_name (Svg_x_attribute) y_attribute_present: clip_area /= Void implies current_group.has_attribute_by_name (Svg_y_attribute) width_attribute_present: current_group.has_attribute_by_name (Svg_width_attribute) height_attribute_present: current_group.has_attribute_by_name (Svg_height_attribute) fill_attribute_present: current_group.has_attribute_by_name (Svg_fill_attribute) end set_painting_attributes (a_element: XM_ELEMENT) is -- Add or replace painting attributes to `a_element'. require a_element_not_void: a_element /= Void local l_text: STRING do l_text := "rgb(" + foreground_color.red_8_bit.out + "," + foreground_color.green_8_bit.out + "," + foreground_color.blue_8_bit.out + ")" a_element.add_attribute (Svg_stroke_attribute, Null_namespace, l_text) a_element.add_attribute (Svg_stroke_width_attribute, Null_namespace, line_width.out) if dashed_line_style then a_element.add_attribute (Svg_stroke_dasharray_attribute, Null_namespace, "3,3") else a_element.add_attribute (Svg_stroke_dasharray_attribute, Null_namespace, "none") end ensure stroke_attribute_present: a_element.has_attribute_by_name (Svg_stroke_attribute) stroke_width_attribute_present: a_element.has_attribute_by_name (Svg_stroke_width_attribute) stroke_dasharray_attribute_present: a_element.has_attribute_by_name (Svg_stroke_dasharray_attribute) end add_font_attributes (a_element: XM_ELEMENT) is -- Add attributes describing `font' to `a_element'. require font_not_void: font /= Void a_element_not_void: a_element /= Void local l_text: STRING do add_font_family_attribute (a_element) add_font_size_attribute (a_element) add_font_style_attribute (a_element) add_font_weight_attribute (a_element) l_text := "rgb(" + fill_color.red_8_bit.out + "," + fill_color.green_8_bit.out + "," + fill_color.blue_8_bit.out + ")" a_element.add_attribute (Svg_fill_attribute, Null_namespace, l_text) a_element.add_attribute (Svg_stroke_attribute, Null_namespace, "none") ensure font_family_attribute_present: a_element.has_attribute_by_name (Svg_font_family_attribute) font_size_attribute_present: a_element.has_attribute_by_name (Svg_font_size_attribute) font_style_attribute_present: a_element.has_attribute_by_name (Svg_font_style_attribute) font_weight_attribute_present: a_element.has_attribute_by_name (Svg_font_weight_attribute) fill_attribute_present: a_element.has_attribute_by_name (Svg_fill_attribute) stroke_attribute_present: a_element.has_attribute_by_name (Svg_stroke_attribute) end add_font_family_name (a_text: DS_CELL [STRING]; a_name: STRING_32) is -- Append puntuated version of `a_name' to `a_text'. require a_text_not_void: a_text /= Void a_text_not_empty: a_text.item /= Void a_name_not_void: a_name /= Void local l_text: STRING do l_text := STRING_.concat ("'", utf8_string (a_name)) l_text := STRING_.appended_string (l_text, "', ") a_text.put (l_text) ensure a_text_not_empty: a_text.item /= Void text_appended: a_text.item.count > old a_text.item.count end add_font_family_attribute (a_element: XM_ELEMENT) is -- Add "font-family" attribute to `a_element'. require font_not_void: font /= Void a_element_not_void: a_element /= Void local l_text: STRING l_cell: DS_CELL [STRING] do create l_text.make_from_string (font.name + ", ") create l_cell.make (l_text) font.preferred_families.do_all (agent add_font_family_name (l_cell, ?)) l_text := l_cell.item inspect font.family when Family_screen, Family_sans then l_text := STRING_.appended_string (l_text, "sans-serif") when Family_roman then l_text := STRING_.appended_string (l_text, "serif") when Family_typewriter then l_text := STRING_.appended_string (l_text, "monospace") when Family_modern then l_text := STRING_.appended_string (l_text, "serif") end a_element.add_attribute (Svg_font_family_attribute, Null_namespace, l_text) ensure font_family_attribute_present: a_element.has_attribute_by_name (Svg_font_family_attribute) end add_font_size_attribute (a_element: XM_ELEMENT) is -- Add "font-size" attribute to `a_element'. require font_not_void: font /= Void a_element_not_void: a_element /= Void do a_element.add_attribute (Svg_font_size_attribute, Null_namespace, font.height.out) ensure font_size_attribute_present: a_element.has_attribute_by_name (Svg_font_size_attribute) end add_font_style_attribute (a_element: XM_ELEMENT) is -- Add "font-style" attribute to `a_element'. require font_not_void: font /= Void a_element_not_void: a_element /= Void do if font.shape = shape_italic then a_element.add_attribute (Svg_font_style_attribute, Null_namespace, "italic") else a_element.add_attribute (Svg_font_style_attribute, Null_namespace, "normal") end ensure font_style_attribute_present: a_element.has_attribute_by_name (Svg_font_style_attribute) end add_font_weight_attribute (a_element: XM_ELEMENT) is -- Add "font-weight" attribute to `a_element'. require font_not_void: font /= Void a_element_not_void: a_element /= Void do inspect font.weight when weight_thin then a_element.add_attribute (Svg_font_weight_attribute, Null_namespace, "100") when weight_regular then a_element.add_attribute (Svg_font_weight_attribute, Null_namespace, "normal") when weight_bold then a_element.add_attribute (Svg_font_weight_attribute, Null_namespace, "bold") when weight_black then a_element.add_attribute (Svg_font_weight_attribute, Null_namespace, "900") end ensure font_weight_attribute_present: a_element.has_attribute_by_name (Svg_font_weight_attribute) end utf8_string (a_string: STRING_GENERAL): UC_UTF8_STRING is -- UTF-8 encoded version of `a_string' require a_string_not_void: a_string /= Void local i: INTEGER_32 l_bytes: STRING do create l_bytes.make (a_string.count * 4) from i := 1 until i > a_string.count loop utf8.append_code_to_utf8 (l_bytes, a_string.code (i).as_integer_32) i := i + 1 end create Result.make_from_utf8 (l_bytes) ensure utf8_string_not_void: Result /= Void correct_count: Result.count = a_string.count strings_equal: True -- can't be checked as STRING_GENERAL and UC_STRING lack the facilities (`for_all') end points_list (a_points: ARRAY [EV_COORDINATE]): STRING is -- SVG "points" attribute value for `a_points' require a_points_not_void: a_points /= Void do Result := "" a_points.do_all (agent add_point (Result, ?)) ensure points_list_not_void: Result /= Void end add_point (a_result: STRING; a_point: EV_COORDINATE) is -- Add representation for `a_point' to `a_result'. require a_result_not_void: a_result /= Void a_point_not_void: a_point /= Void do a_result.append_string (a_point.x.out) a_result.append_character (',') a_result.append_string (a_point.y.out) a_result.append_character (' ') ensure longer: a_result.count > old a_result.count + 3 end fill_last_element is -- Add fill attribute to last element in `current_group' with `background_color'. require last_child_is_element: current_group.last /= Void -- and then is an XM_ELEMENT local l_node: XM_NODE l_element: XM_ELEMENT l_text: STRING do l_node := current_group.last l_element ?= l_node check l_element_not_void: l_element /= Void -- from precondition end l_text := "rgb(" + fill_color.red_8_bit.out + "," + fill_color.green_8_bit.out + "," + fill_color.blue_8_bit.out + ")" l_element.add_attribute (Svg_fill_attribute, Null_namespace, l_text) end ensure_document is -- Ensure `document' is non-Void. do if document = Void then create document.make_with_root_named (Svg_element_name, svg_namespace) current_group := document.root_element end ensure document_not_void: document /= Void end invariant font_not_void: is_initialized implies font /= Void current_group_not_void: document /= void implies current_group /= void end