Repository: bokeh/bokeh

Issue:
[FEATURE] SVG export for gridplot
Currently export of a gridplot works for export png but not for svg. 

With svg you get a series of plots back and have to manually compile.

User jcacbelos on discourse seems to have a start at something that works: https://discourse.bokeh.org/t/generate-svg-images-from-bokeh-plots-and-include-them-in-a-pdf-report-with-using-svglib-and-reportlab/3889


Discussion:
I am "jcacabelos"

Actually I export the plots as SVG files individually, with the `export_svgs` function. Then I create a PDF file including all of them, not a SVG file.

But what you want is to export an unique SVG file with all the plots in a gridplot right? Or you just want to export them one by one (the same you do with the PNG files)? I think having both options would be a nice feature
> Currently export of a gridplot works for export png but not for svg.

The "export" part should be emphasized, because otherwise bokeh can't save a gridplot as a PNG, only as a sequence of PNGs. This is because gridplot's implementation doesn't live up to its name and produces a composite layout. We can export such layout as a single PNG, because export utility captures a screenshot of an entire page where a gridplot was rendered.

When issue https://github.com/bokeh/bokeh/issues/3821 is fixed, which is currently work in progress scheduled for bokeh 1.4, it will be possible to put multiple plots on a single canvas, which in turn will allow to save multiple plots a single file (PNG or SVG). At bokeh 2.0, `gridplot()` will produce an instance of a single canvas `GridPlot` model. The old behavior will still be possible with an existing `GridBox` layout.
>  At bokeh 2.0, gridplot() will produce an instance of a single canvas GridPlot model.

We will have to conduct some experiments to validate that single-canvas performance is acceptable, first. 
>> At bokeh 2.0, gridplot() will produce an instance of a single canvas GridPlot model.
> We will have to conduct some experiments to validate that single-canvas performance is acceptable, first.

This is very exciting. 

Regarding performance I would be happy to accept poor performance in the case where I was trying to do an svg export.

Happy to help test / chip in on that!

Thanks for the clarification @chesucr. the pdf arrangement is useful to me while other solutions are coming along but it sounds like @mattpap is working up a proper solution.

Resolving patch:
diff --git a/bokeh/io/export.py b/bokeh/io/export.py
--- a/bokeh/io/export.py
+++ b/bokeh/io/export.py
@@ -42,6 +42,7 @@
 
 __all__ = (
     'export_png',
+    'export_svg',
     'export_svgs',
     'get_layout_html',
     'get_screenshot_as_png',
@@ -106,6 +107,42 @@ def export_png(obj: Union[LayoutDOM, Document], *, filename: Optional[str] = Non
 
     return abspath(filename)
 
+def export_svg(obj: Union[LayoutDOM, Document], *, filename: Optional[str] = None, width: Optional[int] = None,
+        height: Optional[int] = None, webdriver: "Optional[WebDriver]" = None, timeout: int = 5) -> List[str]:
+    ''' Export a layout as SVG file or a document as a set of SVG files.
+
+    If the filename is not given, it is derived from the script name
+    (e.g. ``/foo/myplot.py`` will create ``/foo/myplot.svg``)
+
+    Args:
+        obj (LayoutDOM object) : a Layout (Row/Column), Plot or Widget object to display
+
+        filename (str, optional) : filename to save document under (default: None)
+            If None, infer from the filename.
+
+        width (int) : the desired width of the exported layout obj only if
+            it's a Plot instance. Otherwise the width kwarg is ignored.
+
+        height (int) : the desired height of the exported layout obj only if
+            it's a Plot instance. Otherwise the height kwarg is ignored.
+
+        webdriver (selenium.webdriver) : a selenium webdriver instance to use
+            to export the image.
+
+        timeout (int) : the maximum amount of time (in seconds) to wait for
+            Bokeh to initialize (default: 5)
+
+    Returns:
+        filenames (list(str)) : the list of filenames where the SVGs files are saved.
+
+    .. warning::
+        Responsive sizing_modes may generate layouts with unexpected size and
+        aspect ratios. It is recommended to use the default ``fixed`` sizing mode.
+
+    '''
+    svgs = get_svg(obj, width=width, height=height, driver=webdriver, timeout=timeout)
+    return _write_collection(svgs, filename, "svg")
+
 def export_svgs(obj: Union[LayoutDOM, Document], *, filename: Optional[str] = None, width: Optional[int] = None,
         height: Optional[int] = None, webdriver: "Optional[WebDriver]" = None, timeout: int = 5) -> List[str]:
     ''' Export the SVG-enabled plots within a layout. Each plot will result
@@ -133,8 +170,7 @@ def export_svgs(obj: Union[LayoutDOM, Document], *, filename: Optional[str] = No
             Bokeh to initialize (default: 5) (Added in 1.1.1).
 
     Returns:
-        filenames (list(str)) : the list of filenames where the SVGs files are
-        saved.
+        filenames (list(str)) : the list of filenames where the SVGs files are saved.
 
     .. warning::
         Responsive sizing_modes may generate layouts with unexpected size and
@@ -147,22 +183,7 @@ def export_svgs(obj: Union[LayoutDOM, Document], *, filename: Optional[str] = No
         log.warning("No SVG Plots were found.")
         return []
 
-    if filename is None:
-        filename = default_filename("svg")
-
-    filenames = []
-
-    for i, svg in enumerate(svgs):
-        if i > 0:
-            idx = filename.find(".svg")
-            filename = filename[:idx] + f"_{i}" + filename[idx:]
-
-        with open(filename, mode="w", encoding="utf-8") as f:
-            f.write(svg)
-
-        filenames.append(filename)
-
-    return filenames
+    return _write_collection(svgs, filename, "svg")
 
 #-----------------------------------------------------------------------------
 # Dev API
@@ -210,11 +231,24 @@ def get_screenshot_as_png(obj: Union[LayoutDOM, Document], *, driver: "Optional[
                  .crop((0, 0, width*dpr, height*dpr))
                  .resize((width, height)))
 
-def get_svgs(obj: Union[LayoutDOM, Document], *, driver: "Optional[WebDriver]" = None, timeout: int = 5,
+def get_svg(obj: Union[LayoutDOM, Document], *, driver: "Optional[WebDriver]" = None, timeout: int = 5,
         resources: Resources = INLINE, width: Optional[int] = None, height: Optional[int] = None) -> List[str]:
-    '''
+    from .webdriver import webdriver_control
 
-    '''
+    with _tmp_html() as tmp:
+        html = get_layout_html(obj, resources=resources, width=width, height=height)
+        with open(tmp.path, mode="w", encoding="utf-8") as file:
+            file.write(html)
+
+        web_driver = driver if driver is not None else webdriver_control.get()
+        web_driver.get("file:///" + tmp.path)
+        wait_until_render_complete(web_driver, timeout)
+        svgs = cast(List[str], web_driver.execute_script(_SVG_SCRIPT))
+
+    return svgs
+
+def get_svgs(obj: Union[LayoutDOM, Document], *, driver: "Optional[WebDriver]" = None, timeout: int = 5,
+        resources: Resources = INLINE, width: Optional[int] = None, height: Optional[int] = None) -> List[str]:
     from .webdriver import webdriver_control
 
     with _tmp_html() as tmp:
@@ -225,9 +259,9 @@ def get_svgs(obj: Union[LayoutDOM, Document], *, driver: "Optional[WebDriver]" =
         web_driver = driver if driver is not None else webdriver_control.get()
         web_driver.get("file:///" + tmp.path)
         wait_until_render_complete(web_driver, timeout)
-        svgs = web_driver.execute_script(_SVG_SCRIPT)
+        svgs = cast(List[str], web_driver.execute_script(_SVGS_SCRIPT))
 
-    return cast(List[str], svgs)
+    return svgs
 
 def get_layout_html(obj: Union[LayoutDOM, Document], *, resources: Resources = INLINE,
         width: Optional[int] = None, height: Optional[int] = None) -> str:
@@ -313,6 +347,24 @@ def is_bokeh_render_complete(driver: "WebDriver") -> bool:
 # Private API
 #-----------------------------------------------------------------------------
 
+def _write_collection(items: List[str], filename: Union[str, None], ext: str) -> List[str]:
+    if filename is None:
+        filename = default_filename(ext)
+
+    filenames = []
+
+    for i, item in enumerate(items):
+        if i > 0:
+            idx = filename.find(f".{ext}")
+            filename = filename[:idx] + f"_{i}" + filename[idx:]
+
+        with open(filename, mode="w", encoding="utf-8") as f:
+            f.write(item)
+
+        filenames.append(filename)
+
+    return filenames
+
 def _log_console(driver: "WebDriver") -> None:
     levels = {'WARNING', 'ERROR', 'SEVERE'}
     try:
@@ -345,7 +397,7 @@ def _maximize_viewport(web_driver: "WebDriver") -> Tuple[int, int, int]:
     web_driver.set_window_size(width + eps, height + eps)
     return viewport_size
 
-_SVG_SCRIPT = """
+_SVGS_SCRIPT = """
 const {LayoutDOMView} = Bokeh.require("models/layouts/layout_dom")
 const {PlotView} = Bokeh.require("models/plots/plot")
 
@@ -365,6 +417,19 @@ def _maximize_viewport(web_driver: "WebDriver") -> Tuple[int, int, int]:
 return [...collect_svgs(root_views)]
 """
 
+_SVG_SCRIPT = """\
+function* export_svgs(views) {
+  for (const view of views) {
+    // TODO: use to_blob() API in future
+    const {ctx} = view.export("svg")
+    yield ctx.get_serialized_svg(true)
+  }
+}
+
+const root_views = Object.values(Bokeh.index)
+return [...export_svgs(root_views)]
+"""
+
 _WAIT_SCRIPT = """
 // add private window prop to check that render is complete
 window._bokeh_render_complete = false;


Test patch:
diff --git a/bokehjs/test/baselines/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads b/bokehjs/test/baselines/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads
@@ -0,0 +1,5 @@
+Row bbox=[0, 0, 400, 600]
+  Plot bbox=[0, 0, 200, 600]
+    Title bbox=[5, 0, 190, 28]
+  Plot bbox=[200, 0, 200, 600]
+    Title bbox=[5, 0, 190, 28]
diff --git a/bokehjs/test/baselines/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads b/bokehjs/test/baselines/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads
@@ -0,0 +1,5 @@
+Column bbox=[0, 0, 600, 400]
+  Plot bbox=[0, 0, 600, 200]
+    Title bbox=[5, 0, 590, 28]
+  Plot bbox=[0, 200, 600, 200]
+    Title bbox=[5, 0, 590, 28]
diff --git a/bokehjs/test/baselines/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend b/bokehjs/test/baselines/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 200]
+  Plot bbox=[0, 0, 200, 200]
+    LinearAxis bbox=[28, 175, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 147]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 200]
+    LinearAxis bbox=[28, 175, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 147]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend b/bokehjs/test/baselines/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 200]
+  Plot bbox=[0, 0, 200, 200]
+    LinearAxis bbox=[18, 175, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 147]
+    Title bbox=[18, 0, 177, 28]
+  Plot bbox=[200, 0, 200, 200]
+    LinearAxis bbox=[18, 175, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 147]
+    Title bbox=[18, 0, 177, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_AnnularWedge b/bokehjs/test/baselines/Glyph_models__should_support_AnnularWedge
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_AnnularWedge
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Annulus b/bokehjs/test/baselines/Glyph_models__should_support_Annulus
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Annulus
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Arc b/bokehjs/test/baselines/Glyph_models__should_support_Arc
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Arc
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Bezier b/bokehjs/test/baselines/Glyph_models__should_support_Bezier
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Bezier
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Circle b/bokehjs/test/baselines/Glyph_models__should_support_Circle
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Circle
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Ellipse b/bokehjs/test/baselines/Glyph_models__should_support_Ellipse
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Ellipse
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_HArea b/bokehjs/test/baselines/Glyph_models__should_support_HArea
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_HArea
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_HBar b/bokehjs/test/baselines/Glyph_models__should_support_HBar
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_HBar
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[31, 275, 164, 25]
+    LinearAxis bbox=[0, 28, 31, 247]
+    Title bbox=[31, 0, 164, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[31, 275, 164, 25]
+    LinearAxis bbox=[0, 28, 31, 247]
+    Title bbox=[31, 0, 164, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Line b/bokehjs/test/baselines/Glyph_models__should_support_Line
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Line
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_MultiLine b/bokehjs/test/baselines/Glyph_models__should_support_MultiLine
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_MultiLine
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_MultiPolygon b/bokehjs/test/baselines/Glyph_models__should_support_MultiPolygon
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_MultiPolygon
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Patch b/bokehjs/test/baselines/Glyph_models__should_support_Patch
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Patch
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Patches b/bokehjs/test/baselines/Glyph_models__should_support_Patches
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Patches
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Quad b/bokehjs/test/baselines/Glyph_models__should_support_Quad
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Quad
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Quadratic b/bokehjs/test/baselines/Glyph_models__should_support_Quadratic
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Quadratic
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Ray b/bokehjs/test/baselines/Glyph_models__should_support_Ray
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Ray
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Rect b/bokehjs/test/baselines/Glyph_models__should_support_Rect
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Rect
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Segment b/bokehjs/test/baselines/Glyph_models__should_support_Segment
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Segment
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Step b/bokehjs/test/baselines/Glyph_models__should_support_Step
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Step
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Text b/bokehjs/test/baselines/Glyph_models__should_support_Text
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Text
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_VArea b/bokehjs/test/baselines/Glyph_models__should_support_VArea
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_VArea
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_VBar b/bokehjs/test/baselines/Glyph_models__should_support_VBar
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_VBar
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[18, 275, 177, 25]
+    LinearAxis bbox=[0, 28, 18, 247]
+    Title bbox=[18, 0, 177, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_Wedge b/bokehjs/test/baselines/Glyph_models__should_support_Wedge
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_Wedge
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 400, 300]
+  Plot bbox=[0, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
+  Plot bbox=[200, 0, 200, 300]
+    LinearAxis bbox=[28, 275, 167, 25]
+    LinearAxis bbox=[0, 28, 28, 247]
+    Title bbox=[28, 0, 167, 28]
diff --git a/bokehjs/test/baselines/Glyph_models__should_support_fill_with_hatch_patterns b/bokehjs/test/baselines/Glyph_models__should_support_fill_with_hatch_patterns
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Glyph_models__should_support_fill_with_hatch_patterns
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 800, 800]
+  Plot bbox=[0, 0, 400, 800]
+    LinearAxis bbox=[25, 775, 370, 25]
+    LinearAxis bbox=[0, 28, 25, 747]
+    Title bbox=[25, 0, 370, 28]
+  Plot bbox=[400, 0, 400, 800]
+    LinearAxis bbox=[25, 775, 370, 25]
+    LinearAxis bbox=[0, 28, 25, 747]
+    Title bbox=[25, 0, 370, 28]
diff --git a/bokehjs/test/baselines/Marker_glyph__should_support_glyph_methods_and_rotation b/bokehjs/test/baselines/Marker_glyph__should_support_glyph_methods_and_rotation
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/baselines/Marker_glyph__should_support_glyph_methods_and_rotation
@@ -0,0 +1,9 @@
+Row bbox=[0, 0, 600, 600]
+  Plot bbox=[0, 0, 300, 600]
+    LinearAxis bbox=[25, 575, 270, 25]
+    LinearAxis bbox=[0, 28, 25, 547]
+    Title bbox=[25, 0, 270, 28]
+  Plot bbox=[300, 0, 300, 600]
+    LinearAxis bbox=[25, 575, 270, 25]
+    LinearAxis bbox=[0, 28, 25, 547]
+    Title bbox=[25, 0, 270, 28]
diff --git a/bokehjs/test/baselines/linux/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads.png b/bokehjs/test/baselines/linux/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads.png differ
diff --git a/bokehjs/test/baselines/linux/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads.png b/bokehjs/test/baselines/linux/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads.png differ
diff --git a/bokehjs/test/baselines/linux/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend.png b/bokehjs/test/baselines/linux/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend.png differ
diff --git a/bokehjs/test/baselines/linux/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend.png b/bokehjs/test/baselines/linux/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_AnnularWedge.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_AnnularWedge.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_AnnularWedge.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Annulus.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Annulus.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Annulus.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Arc.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Arc.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Arc.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Bezier.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Bezier.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Bezier.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Circle.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Circle.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Circle.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Ellipse.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Ellipse.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Ellipse.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_HArea.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_HArea.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_HArea.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_HBar.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_HBar.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_HBar.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Line.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Line.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Line.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_MultiLine.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_MultiLine.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_MultiLine.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_MultiPolygon.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_MultiPolygon.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_MultiPolygon.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Patch.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Patch.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Patch.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Patches.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Patches.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Patches.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Quad.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Quad.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Quad.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Quadratic.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Quadratic.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Quadratic.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Ray.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Ray.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Ray.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Rect.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Rect.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Rect.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Segment.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Segment.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Segment.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Step.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Step.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Step.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Text.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Text.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Text.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_VArea.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_VArea.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_VArea.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_VBar.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_VBar.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_VBar.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_Wedge.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_Wedge.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_Wedge.png differ
diff --git a/bokehjs/test/baselines/linux/Glyph_models__should_support_fill_with_hatch_patterns.png b/bokehjs/test/baselines/linux/Glyph_models__should_support_fill_with_hatch_patterns.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Glyph_models__should_support_fill_with_hatch_patterns.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'asterisk'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'asterisk'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'asterisk'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'asterisk'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_cross'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_cross'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_cross'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_dot'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_dot'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_x'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_x'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_x'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_x'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_y'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_y'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_y'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'circle_y'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'cross'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'cross'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'cross'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'dash'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'dash'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'dash'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'dash'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond_cross'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond_cross'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond_cross'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond_cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond_dot'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond_dot'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'diamond_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'dot'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'dot'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'dot'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'hex'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'hex'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'hex'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'hex'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'hex_dot'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'hex_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'hex_dot'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'hex_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'inverted_triangle'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'inverted_triangle'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'inverted_triangle'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'inverted_triangle'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'plus'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'plus'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'plus'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'plus'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_cross'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_cross'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_cross'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_dot'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_dot'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_pin'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_pin'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_pin'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_pin'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_x'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_x'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_x'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'square_x'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle_dot'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle_dot'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle_pin'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle_pin'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle_pin'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'triangle_pin'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'x'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'x'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'x'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'x'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'y'_marker_type.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'y'_marker_type.png
Binary files a/bokehjs/test/baselines/linux/Marker_glyph__should_support_'y'_marker_type.png and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_'y'_marker_type.png differ
diff --git a/bokehjs/test/baselines/linux/Marker_glyph__should_support_glyph_methods_and_rotation.png b/bokehjs/test/baselines/linux/Marker_glyph__should_support_glyph_methods_and_rotation.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/linux/Marker_glyph__should_support_glyph_methods_and_rotation.png differ
diff --git a/bokehjs/test/baselines/macos/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads.png b/bokehjs/test/baselines/macos/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads.png differ
diff --git a/bokehjs/test/baselines/macos/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads.png b/bokehjs/test/baselines/macos/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads.png differ
diff --git a/bokehjs/test/baselines/macos/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend.png b/bokehjs/test/baselines/macos/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend.png differ
diff --git a/bokehjs/test/baselines/macos/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend.png b/bokehjs/test/baselines/macos/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_AnnularWedge.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_AnnularWedge.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_AnnularWedge.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Annulus.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Annulus.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Annulus.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Arc.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Arc.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Arc.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Bezier.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Bezier.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Bezier.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Circle.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Circle.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Circle.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Ellipse.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Ellipse.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Ellipse.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_HArea.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_HArea.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_HArea.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_HBar.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_HBar.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_HBar.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Line.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Line.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Line.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_MultiLine.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_MultiLine.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_MultiLine.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_MultiPolygon.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_MultiPolygon.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_MultiPolygon.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Patch.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Patch.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Patch.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Patches.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Patches.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Patches.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Quad.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Quad.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Quad.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Quadratic.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Quadratic.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Quadratic.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Ray.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Ray.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Ray.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Rect.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Rect.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Rect.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Segment.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Segment.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Segment.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Step.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Step.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Step.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Text.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Text.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Text.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_VArea.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_VArea.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_VArea.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_VBar.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_VBar.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_VBar.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_Wedge.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_Wedge.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_Wedge.png differ
diff --git a/bokehjs/test/baselines/macos/Glyph_models__should_support_fill_with_hatch_patterns.png b/bokehjs/test/baselines/macos/Glyph_models__should_support_fill_with_hatch_patterns.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Glyph_models__should_support_fill_with_hatch_patterns.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'asterisk'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'asterisk'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'asterisk'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'asterisk'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_cross'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_cross'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_cross'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_dot'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_dot'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_x'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_x'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_x'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_x'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_y'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_y'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_y'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'circle_y'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'cross'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'cross'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'cross'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'dash'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'dash'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'dash'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'dash'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond_cross'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond_cross'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond_cross'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond_cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond_dot'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond_dot'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'diamond_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'dot'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'dot'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'dot'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'hex'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'hex'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'hex'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'hex'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'hex_dot'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'hex_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'hex_dot'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'hex_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'inverted_triangle'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'inverted_triangle'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'inverted_triangle'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'inverted_triangle'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'plus'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'plus'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'plus'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'plus'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_cross'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_cross'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_cross'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_dot'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_dot'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_pin'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_pin'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_pin'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_pin'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_x'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_x'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_x'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'square_x'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle_dot'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle_dot'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle_pin'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle_pin'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle_pin'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'triangle_pin'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'x'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'x'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'x'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'x'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'y'_marker_type.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'y'_marker_type.png
Binary files a/bokehjs/test/baselines/macos/Marker_glyph__should_support_'y'_marker_type.png and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_'y'_marker_type.png differ
diff --git a/bokehjs/test/baselines/macos/Marker_glyph__should_support_glyph_methods_and_rotation.png b/bokehjs/test/baselines/macos/Marker_glyph__should_support_glyph_methods_and_rotation.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/macos/Marker_glyph__should_support_glyph_methods_and_rotation.png differ
diff --git a/bokehjs/test/baselines/windows/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads.png b/bokehjs/test/baselines/windows/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Arrow_annotation__should_support_support_horizontal_arrows_with_all_arrow_heads.png differ
diff --git a/bokehjs/test/baselines/windows/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads.png b/bokehjs/test/baselines/windows/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Arrow_annotation__should_support_support_vertical_arrows_with_all_arrow_heads.png differ
diff --git a/bokehjs/test/baselines/windows/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend.png b/bokehjs/test/baselines/windows/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Bug__in_issue_#6775__disallows_correct_rendering_of_legends_with_SVG_backend.png differ
diff --git a/bokehjs/test/baselines/windows/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend.png b/bokehjs/test/baselines/windows/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Bug__in_issue_#8446__disallows_correct_rendering_circle_scatter_plots_with_SVG_backend.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_AnnularWedge.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_AnnularWedge.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_AnnularWedge.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Annulus.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Annulus.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Annulus.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Arc.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Arc.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Arc.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Bezier.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Bezier.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Bezier.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Circle.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Circle.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Circle.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Ellipse.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Ellipse.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Ellipse.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_HArea.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_HArea.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_HArea.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_HBar.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_HBar.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_HBar.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Line.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Line.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Line.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_MultiLine.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_MultiLine.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_MultiLine.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_MultiPolygon.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_MultiPolygon.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_MultiPolygon.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Patch.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Patch.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Patch.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Patches.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Patches.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Patches.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Quad.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Quad.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Quad.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Quadratic.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Quadratic.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Quadratic.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Ray.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Ray.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Ray.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Rect.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Rect.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Rect.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Segment.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Segment.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Segment.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Step.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Step.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Step.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Text.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Text.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Text.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_VArea.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_VArea.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_VArea.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_VBar.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_VBar.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_VBar.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_Wedge.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_Wedge.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_Wedge.png differ
diff --git a/bokehjs/test/baselines/windows/Glyph_models__should_support_fill_with_hatch_patterns.png b/bokehjs/test/baselines/windows/Glyph_models__should_support_fill_with_hatch_patterns.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Glyph_models__should_support_fill_with_hatch_patterns.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'asterisk'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'asterisk'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'asterisk'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'asterisk'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_cross'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_cross'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_cross'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_dot'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_dot'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_x'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_x'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_x'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_x'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_y'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_y'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_y'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'circle_y'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'cross'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'cross'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'cross'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'dash'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'dash'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'dash'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'dash'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond_cross'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond_cross'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond_cross'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond_cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond_dot'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond_dot'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'diamond_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'dot'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'dot'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'dot'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'hex'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'hex'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'hex'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'hex'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'hex_dot'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'hex_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'hex_dot'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'hex_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'inverted_triangle'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'inverted_triangle'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'inverted_triangle'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'inverted_triangle'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'plus'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'plus'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'plus'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'plus'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_cross'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_cross'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_cross'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_cross'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_dot'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_dot'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_pin'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_pin'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_pin'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_pin'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_x'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_x'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_x'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'square_x'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle_dot'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle_dot'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle_dot'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle_dot'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle_pin'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle_pin'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle_pin'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'triangle_pin'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'x'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'x'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'x'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'x'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'y'_marker_type.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'y'_marker_type.png
Binary files a/bokehjs/test/baselines/windows/Marker_glyph__should_support_'y'_marker_type.png and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_'y'_marker_type.png differ
diff --git a/bokehjs/test/baselines/windows/Marker_glyph__should_support_glyph_methods_and_rotation.png b/bokehjs/test/baselines/windows/Marker_glyph__should_support_glyph_methods_and_rotation.png
new file mode 100644
Binary files /dev/null and b/bokehjs/test/baselines/windows/Marker_glyph__should_support_glyph_methods_and_rotation.png differ
diff --git a/bokehjs/test/integration/annotations/arrows.ts b/bokehjs/test/integration/annotations/arrows.ts
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/integration/annotations/arrows.ts
@@ -0,0 +1,100 @@
+import {display, fig, row, column} from "../utils"
+
+import {Arrow, NormalHead, OpenHead, TeeHead, VeeHead, Range1d} from "@bokehjs/models"
+import {OutputBackend} from "@bokehjs/core/enums"
+
+describe("Arrow annotation", () => {
+  const make_arrow_heads = (size: number) => [
+    {start: null, end: null},
+
+    {start: null, end: new NormalHead({size})},
+    {start: null, end: new OpenHead({size})},
+    {start: null, end: new TeeHead({size})},
+    {start: null, end: new VeeHead({size})},
+
+    {start: new NormalHead({size}), end: null},
+    {start: new OpenHead({size}), end: null},
+    {start: new TeeHead({size}), end: null},
+    {start: new VeeHead({size}), end: null},
+
+    {start: new NormalHead({size}), end: new NormalHead({size})},
+    {start: new NormalHead({size}), end: new OpenHead({size})},
+    {start: new NormalHead({size}), end: new TeeHead({size})},
+    {start: new NormalHead({size}), end: new VeeHead({size})},
+
+    {start: new OpenHead({size}), end: new NormalHead({size})},
+    {start: new OpenHead({size}), end: new OpenHead({size})},
+    {start: new OpenHead({size}), end: new TeeHead({size})},
+    {start: new OpenHead({size}), end: new VeeHead({size})},
+
+    {start: new TeeHead({size}), end: new NormalHead({size})},
+    {start: new TeeHead({size}), end: new OpenHead({size})},
+    {start: new TeeHead({size}), end: new TeeHead({size})},
+    {start: new TeeHead({size}), end: new VeeHead({size})},
+
+    {start: new VeeHead({size}), end: new NormalHead({size})},
+    {start: new VeeHead({size}), end: new OpenHead({size})},
+    {start: new VeeHead({size}), end: new TeeHead({size})},
+    {start: new VeeHead({size}), end: new VeeHead({size})},
+  ]
+
+  it(`should support support horizontal arrows with all arrow heads`, async () => {
+    function make_plot(output_backend: OutputBackend) {
+      const arrow_heads = make_arrow_heads(12)
+
+      const x_range = new Range1d({start: 0, end: 8})
+      const y_range = new Range1d({start: 0, end: arrow_heads.length + 1})
+      const p = fig([200, 600], {
+        x_range, y_range,
+        x_axis_type: null, y_axis_type: null,
+        output_backend, title: output_backend,
+      })
+
+      const x_start = 1.3
+      const x_end = 6.7
+      const width = 2
+
+      let y = 0
+      for (const {start, end} of arrow_heads) {
+        p.add_layout(new Arrow({x_start, y_start: ++y, x_end, y_end: y, line_width: width, start, end}))
+      }
+
+      return p
+    }
+
+    const p0 = make_plot("canvas")
+    const p1 = make_plot("svg")
+
+    await display(row([p0, p1]), [450, 650])
+  })
+
+  it(`should support support vertical arrows with all arrow heads`, async () => {
+    function make_plot(output_backend: OutputBackend) {
+      const arrow_heads = make_arrow_heads(12)
+
+      const x_range = new Range1d({start: 0, end: arrow_heads.length + 1})
+      const y_range = new Range1d({start: 0, end: 8})
+      const p = fig([600, 200], {
+        x_range, y_range,
+        x_axis_type: null, y_axis_type: null,
+        output_backend, title: output_backend,
+      })
+
+      const y_start = 1.3
+      const y_end = 6.7
+      const width = 2
+
+      let x = 0
+      for (const {start, end} of arrow_heads) {
+        p.add_layout(new Arrow({y_start, x_start: ++x, y_end, x_end: x, line_width: width, start, end}))
+      }
+
+      return p
+    }
+
+    const p0 = make_plot("canvas")
+    const p1 = make_plot("svg")
+
+    await display(column([p0, p1]), [650, 450])
+  })
+})
diff --git a/bokehjs/test/integration/annotations/index.ts b/bokehjs/test/integration/annotations/index.ts
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/integration/annotations/index.ts
@@ -0,0 +1 @@
+import "./arrows"
diff --git a/bokehjs/test/integration/glyphs/glyphs.ts b/bokehjs/test/integration/glyphs/glyphs.ts
new file mode 100644
--- /dev/null
+++ b/bokehjs/test/integration/glyphs/glyphs.ts
@@ -0,0 +1,277 @@
+import {display, fig, row} from "../utils"
+
+import {OutputBackend} from "@bokehjs/core/enums"
+import {hatch_aliases} from "@bokehjs/core/visuals"
+import {entries} from "@bokehjs/core/util/object"
+// import {radians} from "@bokehjs/core/util/math"
+
+describe("Glyph models", () => {
+  const x = [1, 2, 3]
+  const y = [1, 2, 3]
+
+  it("should support AnnularWedge", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.annular_wedge({x, y, inner_radius: 0.1, outer_radius: 0.25, start_angle: 0.4, end_angle: 4.8, color: "green", alpha: 0.6})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Annulus", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.annulus({x, y, inner_radius: 0.1, outer_radius: 0.25, color: "green", alpha: 0.6})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Arc", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.arc({x, y, radius: 0.25, start_angle: 0.4, end_angle: 4.8, color: "green", alpha: 0.6, line_width: 5})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Bezier", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.bezier({
+        x0: [1, 2, 3], y0: [1, 2, 3],
+        x1: [4, 5, 6], y1: [4, 5, 6],
+        cx0: [1, 2, 3], cy0: [2, 3, 4],
+        cx1: [4, 5, 6], cy1: [3, 4, 5],
+        line_width: 5,
+      })
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Circle", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.circle({x, y, /*size,*/ radius: 0.25})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Ellipse", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.ellipse({x, y, width: 0.5, height: 1})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support HArea", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.harea({x1: [1, 2, 3], x2: [2, 3, 4], y: [1, 2, 3]})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support HBar", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.hbar({y: 1, height: [1, 2, 3], left: [1, 2, 3], right: [4, 5, 6]})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  /*
+  it("should support HexTile", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      //p.hex_tile({q, r})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Image", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      //p.image({image, x, y, dw, dh})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support ImageRGBA", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      //p.image_rgba({image, x, y, dw, dh})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support ImageURL", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      //p.image_url({url, x, y, w, h})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+  */
+
+  it("should support Line", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.line({x, y})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support MultiLine", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.multi_line({xs: [x], ys: [y]})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support MultiPolygon", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.multi_polygons({xs: [[[x]]], ys: [[[y]]]})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Patch", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.patch({x, y})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Patches", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.patches({xs: [x], ys: [y]})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Quad", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.quad({left: x, right: 1, bottom: y, top: 1})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Quadratic", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.quadratic({x0: [1, 2, 3], y0: [1, 2, 3], x1: [4, 5, 6], y1: [4, 5, 6], cx: 1, cy: 1})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Ray", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.ray({x, y})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Rect", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.rect({x, y, width: 1, height: 2})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Segment", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.segment({x0: [1, 2, 3], y0: [1, 2, 3], x1: [4, 5, 6], y1: [4, 5, 6]})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Step", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.step({x, y, mode: "center"})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Text", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.text({x, y, text: "Some"})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support VArea", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.varea({x, y1: [1, 2, 3], y2: [4, 5, 6]})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support VBar", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.vbar({x, width: [1, 2, 3], bottom: [1, 2, 3], top: [4, 5, 6]})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+  it("should support Wedge", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([200, 300], {output_backend, title: output_backend})
+      p.wedge({x, y, radius: 0.25, start_angle: 0.4, end_angle: 4.8})
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [450, 350])
+  })
+
+
+  it("should support fill with hatch patterns", async () => {
+    function p(output_backend: OutputBackend) {
+      const p = fig([400, 800], {output_backend, title: output_backend})
+      let y = 0
+      for (const [alias, name] of entries(hatch_aliases)) {
+        p.quad({left: 0, bottom: y, right: 1.95, top: y + 0.75, hatch_pattern: alias, hatch_scale: 12})
+        p.quad({left: 2.05, bottom: y, right: 4, top: y + 0.75, hatch_pattern: name, hatch_scale: 12})
+        y++
+      }
+      return p
+    }
+    await display(row([p("canvas"), p("svg")]), [850, 850])
+  })
+})
diff --git a/bokehjs/test/integration/glyphs/index.ts b/bokehjs/test/integration/glyphs/index.ts
--- a/bokehjs/test/integration/glyphs/index.ts
+++ b/bokehjs/test/integration/glyphs/index.ts
@@ -1,3 +1,4 @@
 import "./hex_tile"
+import "./glyphs"
 import "./markers"
 import "./text"
diff --git a/bokehjs/test/integration/glyphs/markers.ts b/bokehjs/test/integration/glyphs/markers.ts
--- a/bokehjs/test/integration/glyphs/markers.ts
+++ b/bokehjs/test/integration/glyphs/markers.ts
@@ -2,6 +2,8 @@ import {display, fig, row} from "../utils"
 
 import {MarkerType, OutputBackend} from "@bokehjs/core/enums"
 import {Random} from "@bokehjs/core/util/random"
+import {radians} from "@bokehjs/core/util/math"
+import {assert} from "@bokehjs/core/util/assert"
 
 describe("Marker glyph", () => {
   const random = new Random(1)
@@ -30,4 +32,59 @@ describe("Marker glyph", () => {
       await display(row([...plots()]), [3*150 + 50, 150 + 50])
     })
   }
+
+  it("should support glyph methods and rotation", async () => {
+    function make_plot(output_backend: OutputBackend) {
+      const p = fig([300, 600], {output_backend, title: output_backend})
+
+      let y = 0
+      const X = [1, 2, 3, 4, 5]
+      const Y = () => [++y + 0, y + 1, y + 2, y + 3, y + 4]
+
+      const attrs = {
+        angle: [0, 30, 45, 60, 90].map((a) => radians(a)),
+        size: 12,
+        line_color: "navy",
+        fill_color: "orange",
+        alpha: 0.7,
+      }
+
+      p.asterisk(X, Y(), attrs)
+      p.circle(X, Y(), attrs)
+      p.circle_cross(X, Y(), attrs)
+      p.circle_dot(X, Y(), attrs)
+      p.circle_x(X, Y(), attrs)
+      p.circle_y(X, Y(), attrs)
+      p.cross(X, Y(), attrs)
+      p.dash(X, Y(), attrs)
+      p.diamond(X, Y(), attrs)
+      p.diamond_cross(X, Y(), attrs)
+      p.diamond_dot(X, Y(), attrs)
+      p.dot(X, Y(), attrs)
+      p.hex(X, Y(), attrs)
+      p.hex_dot(X, Y(), attrs)
+      p.inverted_triangle(X, Y(), attrs)
+      p.plus(X, Y(), attrs)
+      p.square(X, Y(), attrs)
+      p.square_cross(X, Y(), attrs)
+      p.square_dot(X, Y(), attrs)
+      p.square_pin(X, Y(), attrs)
+      p.square_x(X, Y(), attrs)
+      p.triangle(X, Y(), attrs)
+      p.triangle_dot(X, Y(), attrs)
+      p.triangle_pin(X, Y(), attrs)
+      p.x(X, Y(), attrs)
+      p.y(X, Y(), attrs)
+
+      const N = [...MarkerType].length
+      assert(p.renderers.length == N)
+
+      return p
+    }
+
+    const p0 = make_plot("canvas")
+    const p1 = make_plot("svg")
+
+    await display(row([p0, p1]), [650, 650])
+  })
 })
diff --git a/bokehjs/test/integration/index.ts b/bokehjs/test/integration/index.ts
--- a/bokehjs/test/integration/index.ts
+++ b/bokehjs/test/integration/index.ts
@@ -1,9 +1,10 @@
 export * from "../framework"
 
+import "./regressions"
+import "./annotations"
+import "./glyphs"
 import "./layouts"
 import "./plots"
-import "./glyphs"
+import "./color_mapping"
 import "./tiles"
-import "./regressions"
 import "./widgets"
-import "./color_mapping"
diff --git a/bokehjs/test/integration/regressions.ts b/bokehjs/test/integration/regressions.ts
--- a/bokehjs/test/integration/regressions.ts
+++ b/bokehjs/test/integration/regressions.ts
@@ -1,7 +1,7 @@
 import {display, fig, row, column, grid} from "./utils"
 
 import {
-  Arrow, ArrowHead, NormalHead, BoxAnnotation,
+  Arrow, ArrowHead, NormalHead, BoxAnnotation, Legend, LegendItem,
   Range1d, DataRange1d, FactorRange,
   ColumnDataSource, CDSView, BooleanFilter, Selection,
   LinearAxis, CategoricalAxis,
@@ -318,4 +318,42 @@ describe("Bug", () => {
       await display(r, [650, 650])
     })
   })
+
+  describe("in issue #8446", () => {
+    it("disallows correct rendering circle scatter plots with SVG backend", async () => {
+      function make_plot(output_backend: OutputBackend) {
+        const p = fig([200, 200], {output_backend, title: output_backend})
+        p.scatter([-1, -2, -3, -4, -5], [6, 7, 2, 4, 5], {size: 20, color: "navy", alpha: 0.5, marker: "circle"})
+        p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], {size: 20, color: "red", alpha: 0.5})
+        return p
+      }
+
+      const p0 = make_plot("canvas")
+      const p1 = make_plot("svg")
+
+      await display(row([p0, p1]), [450, 250])
+    })
+  })
+
+  describe("in issue #6775", () => {
+    it("disallows correct rendering of legends with SVG backend", async () => {
+      function make_plot(output_backend: OutputBackend) {
+        const p = fig([200, 200], {output_backend, title: output_backend})
+        const cross = p.diamond({x: 1, y: 1, color: "red", size: 30})
+        const square = p.square({x: 2, y: 1, size: 30})
+        const items = [
+          new LegendItem({label: "circle", renderers: [cross]}),
+          new LegendItem({label: "square", renderers: [square]}),
+        ]
+        const legend = new Legend({items, location: "top_center"})
+        p.add_layout(legend)
+        return p
+      }
+
+      const p0 = make_plot("canvas")
+      const p1 = make_plot("svg")
+
+      await display(row([p0, p1]), [450, 250])
+    })
+  })
 })
diff --git a/bokehjs/test/unit/models/canvas/canvas.ts b/bokehjs/test/unit/models/canvas/canvas.ts
--- a/bokehjs/test/unit/models/canvas/canvas.ts
+++ b/bokehjs/test/unit/models/canvas/canvas.ts
@@ -54,9 +54,7 @@ describe("Canvas", () => {
       expect(primary_svg).to.be.equal(`\
 <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="600">\
 <defs/>\
-<g transform="scale(1,1) translate(0.5,0.5)">\
-<rect fill="blue" stroke="none" x="100" y="200" width="300" height="400"/>\
-</g>\
+<path fill="blue" stroke="none" paint-order="stroke" d="M 100.5 200.5 L 400.5 200.5 L 400.5 600.5 L 100.5 600.5 L 100.5 200.5"/>\
 </svg>\
 `)
 
@@ -64,9 +62,7 @@ describe("Canvas", () => {
       expect(overlays_svg).to.be.equal(`\
 <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="600">\
 <defs/>\
-<g transform="scale(1,1) translate(0.5,0.5)">\
-<rect fill="green" stroke="none" x="300" y="400" width="200" height="100"/>\
-</g>\
+<path fill="green" stroke="none" paint-order="stroke" d="M 300.5 400.5 L 500.5 400.5 L 500.5 500.5 L 300.5 500.5 L 300.5 400.5"/>\
 </svg>\
 `)
 
@@ -75,14 +71,8 @@ describe("Canvas", () => {
       expect(composite_svg).to.be.equal(`\
 <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="600">\
 <defs/>\
-<g>\
-<g transform="scale(1,1) translate(0.5,0.5) translate(0, 0)">\
-<rect fill="blue" stroke="none" x="100" y="200" width="300" height="400"/>\
-</g>\
-<g transform="scale(1,1) translate(0.5,0.5) translate(0, 0)">\
-<rect fill="green" stroke="none" x="300" y="400" width="200" height="100"/>\
-</g>\
-</g>\
+<path fill="blue" stroke="none" paint-order="stroke" d="M 100.5 200.5 L 400.5 200.5 L 400.5 600.5 L 100.5 600.5 L 100.5 200.5"/>\
+<path fill="green" stroke="none" paint-order="stroke" d="M 300.5 400.5 L 500.5 400.5 L 500.5 500.5 L 300.5 500.5 L 300.5 400.5"/>\
 </svg>\
 `)
     })
diff --git a/tests/unit/bokeh/io/test_export.py b/tests/unit/bokeh/io/test_export.py
--- a/tests/unit/bokeh/io/test_export.py
+++ b/tests/unit/bokeh/io/test_export.py
@@ -15,7 +15,6 @@
 #-----------------------------------------------------------------------------
 
 # Standard library imports
-import re
 from typing import Tuple
 
 # External imports
@@ -132,11 +131,57 @@ def test_get_screenshot_as_png_with_unicode_unminified(webdriver) -> None:
 
     assert len(png.tobytes()) > 0
 
+@flaky(max_runs=10)
+@pytest.mark.selenium
+def test_get_svg_no_svg_present() -> None:
+    layout = Plot(x_range=Range1d(), y_range=Range1d(), plot_height=20, plot_width=20, toolbar_location=None)
+
+    with silenced(MISSING_RENDERERS):
+        svgs = bie.get_svg(layout)
+
+    assert svgs == [
+        '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20">'
+            '<defs/>'
+            '<image width="20" height="20" preserveAspectRatio="none" transform="translate(0, 0)" xlink:href="'
+            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAbElEQVQ4T2P8//+/AwMDAwhTBTD+//+/gYGBoZ4'
+            'qpjEwMIwaCAnJN2/eMPz69YtgsLKxsTGIiIigqMMahs+ePWOQkpIiaCA2daMGQoJtNAxxJp+BSzbE5hRmZuYL4uLiBsheGC1tCJYHBBUAAA7h'
+            'kkaBfwzpAAAAAElFTkSuQmCC"/>'
+        '</svg>',
+    ]
+
+@flaky(max_runs=10)
+@pytest.mark.selenium
+def test_get_svg_with_svg_present(webdriver) -> None:
+    plot = lambda color: Plot(
+        x_range=Range1d(), y_range=Range1d(),
+        plot_height=20, plot_width=20, toolbar_location=None,
+        outline_line_color=None, border_fill_color=None,
+        background_fill_color=color, output_backend="svg",
+    )
+
+    layout = row([plot("red"), plot("blue")])
+
+    with silenced(MISSING_RENDERERS):
+        svgs0 = bie.get_svg(layout, driver=webdriver)
+        svgs1 = bie.get_svg(layout, driver=webdriver)
+
+    svgs2 = [
+        '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="20">'
+            '<defs/>'
+            '<path fill="rgb(255,0,0)" stroke="none" paint-order="stroke" d="M 5.5 5.5 L 15.5 5.5 L 15.5 15.5 L 5.5 15.5 L 5.5 5.5" fill-opacity="1"/>'
+            '<g transform="matrix(1, 0, 0, 1, 20, 0)">'
+                '<path fill="rgb(0,0,255)" stroke="none" paint-order="stroke" d="M 5.5 5.5 L 15.5 5.5 L 15.5 15.5 L 5.5 15.5 L 5.5 5.5" fill-opacity="1"/>'
+            '</g>'
+        '</svg>',
+    ]
+
+    assert svgs0 == svgs2
+    assert svgs1 == svgs2
+
 @flaky(max_runs=10)
 @pytest.mark.selenium
 def test_get_svgs_no_svg_present() -> None:
-    layout = Plot(x_range=Range1d(), y_range=Range1d(),
-              plot_height=20, plot_width=20, toolbar_location=None)
+    layout = Plot(x_range=Range1d(), y_range=Range1d(), plot_height=20, plot_width=20, toolbar_location=None)
 
     with silenced(MISSING_RENDERERS):
         svgs = bie.get_svgs(layout)
@@ -146,36 +191,32 @@ def test_get_svgs_no_svg_present() -> None:
 @flaky(max_runs=10)
 @pytest.mark.selenium
 def test_get_svgs_with_svg_present(webdriver) -> None:
+    plot = lambda color: Plot(
+        x_range=Range1d(), y_range=Range1d(),
+        plot_height=20, plot_width=20, toolbar_location=None,
+        outline_line_color=None, border_fill_color=None,
+        background_fill_color=color, output_backend="svg",
+    )
 
-    def fix_ids(svg):
-        svg = re.sub(r'id="\w{12}"', 'id="X"', svg)
-        svg = re.sub(r'url\(#\w{12}\)', 'url(#X)', svg)
-        return svg
-
-    layout = Plot(x_range=Range1d(), y_range=Range1d(),
-                  plot_height=20, plot_width=20, toolbar_location=None,
-                  outline_line_color=None, border_fill_color=None,
-                  background_fill_color="red", output_backend="svg")
+    layout = row([plot("red"), plot("blue")])
 
     with silenced(MISSING_RENDERERS):
-        svg0 = fix_ids(bie.get_svgs(layout, driver=webdriver)[0])
-        svg1 = fix_ids(bie.get_svgs(layout, driver=webdriver)[0])
+        svgs0 = bie.get_svgs(layout, driver=webdriver)
+        svgs1 = bie.get_svgs(layout, driver=webdriver)
 
-    svg2 = (
+    svgs2 = [
         '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20">'
-        '<defs/>'
-        '<g>'
-            '<g transform="scale(1,1) translate(0.5,0.5) translate(0, 0)">'
-                '<rect fill="rgb(255,0,0)" stroke="none" x="5" y="5" width="10" height="10" fill-opacity="1"/>'
-                '<g/>'
-            '</g>'
-            '<g transform="scale(1,1) translate(0.5,0.5) translate(0, 0)"/>'
-        '</g>'
-        '</svg>'
-    )
+            '<defs/>'
+            '<path fill="rgb(255,0,0)" stroke="none" paint-order="stroke" d="M 5.5 5.5 L 15.5 5.5 L 15.5 15.5 L 5.5 15.5 L 5.5 5.5" fill-opacity="1"/>'
+        '</svg>',
+        '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20">'
+            '<defs/>'
+            '<path fill="rgb(0,0,255)" stroke="none" paint-order="stroke" d="M 5.5 5.5 L 15.5 5.5 L 15.5 15.5 L 5.5 15.5 L 5.5 5.5" fill-opacity="1"/>'
+        '</svg>',
+    ]
 
-    assert svg0 == svg2
-    assert svg1 == svg2
+    assert svgs0 == svgs2
+    assert svgs1 == svgs2
 
 def test_get_layout_html_resets_plot_dims() -> None:
     initial_height, initial_width = 200, 250