bokeh / bokeh

Interactive Data Visualization in the browser, from Python
https://bokeh.org
BSD 3-Clause "New" or "Revised" License
19.4k stars 4.19k forks source link

Scalable Text #9407

Open bryevdv opened 5 years ago

bryevdv commented 5 years ago

Users who make PNG exports at very high resolutions (e.g. for publication) run in to the issue that text font sizes are fixed and do not scale, leading to very tiny titles, labels, etc in the larger image. AFAIK units like rem, etc do not work on Canvas, but this should be verified. If not, it would be helpful to introduce our own unit that we manually scale according to canvas height.

Tangentially related to https://github.com/bokeh/bokeh/issues/6840

cc @bokeh/dev for thoughts, input.

birdsarah commented 5 years ago

This would be a phenomenal feature. I had to make a special theme to make my publication plots look right.

That said, it wasn't the biggest hurdle I faced in making publication plots and we could publish a theme that helps with this.

mattpap commented 5 years ago

units like rem, etc do not work on Canvas, but this should be verified.

They actually seem to work. According to spec, ctx.font accepts a CSS font specification, so everything that CSS supports, should be supported in canvas as well.

bryevdv commented 5 years ago

They actually seem to work. According to spec, ctx.font accepts a CSS font specification, so everything that CSS supports, should be supported in canvas as well.

Thank goodness for small mercies. I think we should document/demonstrate this and add a section to the exporting user guide section talking about how to use it.

bryevdv commented 4 years ago

Just noting that a user on SO claimed no success with "rem" but that "vh" units did work

https://stackoverflow.com/questions/61954539/bokeh-label-font-size-responsive-to-figure-size

tcmetzger commented 4 years ago

I tried working on this to add information about relative scaling to the exporting section in the User Guide. However, I was not able to make all text fully scalable, neither with rem nor with vh (definition) or the related vw, vmin or vmax. These are my steps for testing, in case this is helpful in this discussion:

I created a very simple graph with some text in vh units in a Jupyter Notebook (Bokeh 2.1.1):

from bokeh.models import Label
from bokeh.plotting import figure
from bokeh.io import output_notebook, show

x = [1,2,3,4,5]
y = [1,2,3,4,5]
output_notebook()

# Create figure object
f = figure()

# Add headline
f.title.text = "Headline"
f.title.text_font_size = "2.5vh"
f.title.align = "center"

# Add label
test_label = Label(x=0, y=0, x_units="screen", y_units="screen", text="Test", render_mode="css", text_font_size="3vh", text_color="white", background_fill_color="green")
f.add_layout(test_label)

# Add axis label
f.xaxis.axis_label = "Test axis label"
f.xaxis.axis_label_text_font_size = "2vh"

# Draw
f.line(x, y, legend_label="Label")

# Add legend
f.legend.label_text_font_size="2vh"

show(f)

I noticed two things:

I also tried exporting a rather high-resolution PNG:

from bokeh.io import export_png
export_png(f, filename="plot.png", width=3000, height=3000)

As expected, all text in the PNG was tiny - only the Label object got scaled (again, only if the render_mode="css" argument is included).

The example from SO mentioned by @bryevdv also uses a Label object with vh units. Interestingly, this Label does scale with the browser window, even without the render_mode="css" argument. To further test this behavior, I added some more text elements with vh units to the sample code from SO. In this case, even text like headline and axis labels did scale with the browser window. I suspect that this behavior is related to the fact that the plot is continually updated by the server (add_periodic_callback()). Unfortunately, I was not able to reproduce the scaling for a static plot not served by bokeh.server.

Conclusion: I'm afraid that using relative CSS units like 'vh' and 'vw' does not easily solve the problem of non-scaling text in high-res PNG exports. More research seems to be required.

bryevdv commented 4 years ago

At this point all I can really think of is to allow the units to also be specified in "data units", which is then converted by one of the range scales to px units for display (by default, use the default y-range scale). Certainly doable, but to make this applicable generally we will probably need to abstract text-rendering at a low level rather than special-casing this computation everywhere text is drawn.

There would certainly be additional details to figure out, e.g.

tcmetzger commented 4 years ago

Just a thought: The User Guide could recommend using SVG export for high-res publications, instead of PNG. SVG export seems to work well in most cases, and it is easy to scale an SVG file to a very high resolution without any text going out of proportion. A recommendation like this might help to point new users in the right direction and save them the hassle of trying to figure out scaling text sizes for PNG export.

I'd be happy to create a PR for the Exporting Plots page in the User Guide (and possibly also in the binder.org notebooks) to include this recommendation.

It might also be worthwhile to consider an additional export_highres_png() function as a more advanced workaround. This function would automatically export an SVG, scale the SVG, convert the SVG to PNG and return the PNG. The export_highres_png() function could take a multiplier as an argument. This multiplier would be used to proportionally scale the plot to x times the original pixel dimensions (similar to Datawapper's high-res export functionality, for example).

jbednar commented 4 years ago

Last I checked SVG output was only usable for individual plots, as it doesn't integrate separate plots in a layout into an SVG. If so, SVG output isn't often going to be a usable substitute for PNG.

bryevdv commented 4 years ago

It's also not entirely clear to me that this approach would actually work even for one plot, at least without some changes. Looking just now the current output of canvas2svg specifies actual width and height attributes and CSS styles in the CVG tag itself. This presently makes scaling somewhat painful. It would probably be better or the output to specify a view box instead:

viewBox="0 0 900 400"

Changing that allows for actual responsive scaling of the SVGand I expect this is actually what people would like with an SVG output (both images below span my entire desktop):

Screen Shot 2020-07-02 at 3 46 00 PM Screen Shot 2020-07-02 at 3 46 19 PM

(Perhaps a separate issue should be opened for view box)

Of course, people want this not only for exports but simply for real bokeh plots in responsive scenarios in browsers that can range widely in size, and none of this would help for that.

FlorisCalkoen commented 7 months ago

Any updates or workarounds on this issue?