Search code examples
xaringan

Cannot get consistent font sizes between print and screen


My work's standard presentation format is Google Slides in a ratio 16:9, with width set to 10 inches. But I hate having to manually input plots etc so originally mimicked it using Beamer, but am now trying to use xaringan for all the modern html features.

My problem comes in that I cannot easily get any consistency in font sizes between displaying presentations on screen and printing them. To elaborate. Let's say I have some text in Google Slides that is designed at 12pt (16px). When I measure the size of the text when displaying in presentation mode, this is consistent with the size of the same text when downloaded as a pdf (and then put in presentation mode - using Sumatra). Ok I know this is a weird thing to do but it highlights the point that Google Slides must be doing lots of background stuff to make these consistent - and the fact I can't get the same consistency with xaringan is - maybe - highlighting a problem. Maybe - I think actually it's probably more correct as I assume it's adjusting to the size / res of my monitor (14 in, 1920 x 1080).

Anyway. If I do the same in xaringan, using the example code below, then (provided I add in the css @page code to force the pdf printing to be the correct physical size) then the font size in the pdf that results matches the font size in the pdf produced by Google. Great! Everything is consistent here and I can print my xaringan slides the same as my Google Slides - e.g. for sharing with customers who point blank refuse to use Google and just want a pdf emailed. I know.

My problem comes that, the font size when viewing the xaringan html slides in full screen mode is only about 75 % of the "correct" pdf size. This is a problem because - ok I can fudge the font sizes up a bit so displaying on screen looks equivalent to Google Slides - but then the fonts in the pdf are all too big. So there's no easy way to make it so that I can use the same .Rmd file for online presenting and sending as a pdf in a way that all font sizes look the same as if I had used Google all along (unless I only present the pdf, but then might as well revert to Beamer).

I guess the reason for all this is some way font sizes are handled in the background but maybe it's a bug? If not, is there a way to force this consistent font sizing between screen and pdf?

tl;dr Google Slides seems to have consistent font sizes between viewing on a screen and pdf - but xaringan seems not to.

Here's an example Rmd. You can see that the resulting pdf (when viewed in presentation mode) has noticeably bigger font sizes than the html file in presentation mode. In Google Slides, these fonts would all be the same size. I'm using xaringanthemer for ease.

---
title: "Presentation Ninja"
subtitle: "⚔<br/>with xaringan"
author: "Yihui Xie"
institute: "RStudio, PBC"
date: "2016/12/12 (updated: `r Sys.Date()`)"
output:
  xaringan::moon_reader:
    nature:
      ratio: "16:9"
      highlightStyle: github
      highlightLines: true
      countIncrementalSlides: false
    seal: false
knit: pagedown::chrome_print
---

```{css, echo = FALSE}
@page {
  size: 10in 5.625in; # makes sure pdf comes out same size as Google Slides pdf
}
```

```{r setup, include=FALSE}
options(htmltools.dir.version = FALSE)
library(xaringanthemer)
style_mono_light(
  base_font_size = "16px",
  header_h1_font_size = "1.5rem"
)
```


# Hello World

Install the **xaringan** package from [Github](https://github.com/yihui/xaringan):

Oh and I tried this CSS as well in a code chunk, but although it does fix the size of the html slides, when put in full screen mode the same issue arises:

.remark-container {
    height: 540px!important;
    width: 960px!important;
}

Solution

  • Ok, so there is a fudge around this - although I'm still not entirely happy I understand why xaringan does this. I actually think it's something to do with remark.js itself and the way it seems to handle full screen - or maybe the way browsers do (the problem is manifest with all I've tried) - as it seems to do it for "normal" remark.js presentations, too.

    Anyway, it's rather simple, if you use the @media rule to modify font sizes when viewing on screen. There are a number of ways to do it depended how "automated" you want the scaling. The important point is to make sure you only ever set the base html font size in any non-relative unit, and define everything else relative to that using rem - that way everything else scales appropriately. Otherwise you'll have to manually change everything else, e.g. all h1 etc.

    The first two methods seem equivalent, but I'm new to all this so maybe not, either reset the html font directly:

    ```{css, echo = FALSE}
    @page {
      size: 10in 5.625in; # makes sure pdf comes out right size
    }
    @media only screen {
      html {
        font-size: 22px;
      }
    }
    ```
    

    Or reset the base-font-size variable using:

    ```{css, echo = FALSE}
    @page {
      size: 10in 5.625in; # makes sure pdf comes out right size
    }
    @media only screen {
      :root {
        --base-font-size: 22px;
      }
    }
    ```
    

    And this will be automatically used by the existing html font size call (assuming you're using xaringanthemer - shouldn't be hard to adjust appropriately if not).

    You can actually do better and get everything to scale automatically, including the base html font, from whatever you manually set, if you do:

    ```{css, echo = FALSE}
    @page {
      size: 10in 5.625in; # makes sure pdf comes out right size
    }
    @media only screen {
      html {
        font-size: calc(1.23 * var(--base-font-size));
      }
    }
    ```
    

    This way, if you change your base_font_size option in xaringanthemer, you don't have to manually change anything else (with the first two methods you'd have to go in and increase font sizes accordingly).

    I'm using 1.23 because it works for me on my screen - but, because I don't understand why the fonts are behaving inconsistently, I'm really not confident that this is a robust solution across different screens. In fact I'd bet it isn't. My CSS and JS is basically non-existent so feel free to propose better solution(s) - I have a sneaky suspicion you can use @media in a more targeted way than I have.

    Obviously you'll probably want to put the above in your .css file rather than in code chunks at the top of your .Rmd, just to be neater.

    EDIT: seems like 1.23 is close to the ratio between the "real" physical size of the slides Google Slides defines (10" x 5.625") and my display's physical size (14.1") - so presume that's why. But would have to check multiple different display sizes to confirm.

    EDIT2:

    Update. I went off on a bit of a tangent with the whole screen size vs print out size ratio. In my case the correct scaling size to use for fonts is 1.2611, as in:

    @media only screen {
      html {
        font-size: calc(1.2611 * var(--base-font-size)); 
      }
    

    I could only tell the difference once I started checking enormous fonts.

    The reason being, after getting round to dig through the remark.js source code, it sets a reference 4:3 slide dimension of 908 x 681 (in scaler.js) - and then adjusts the scaling according to the width of the current display device (or printed page). But it never corrects the height accordingly and this gives the inconsistent font scaling.

    In my case my slides are defined as 960 x 540, so I need to correct the font sizes (plus any other objects with a variable height - e.g. hr) by 681/540 and then everything scales consistently.