rparallel-processing

Measuring the on-screen displayed width of strings in a parallel set-up


In order to adjust the columns' widths of a spreadheet document to their content, I need to compute the 'real' width in 'characters' (defined by Microsoft as the ratio of the real width divided by the real width of the corresponding string of the same length composed only of '0's) of strings for a specific font, say, Calibri (that I import and load in case it is not already registered in the system). While this works fine in a sequential set-up (I'm running on Windows) with the functions strwdith and par, when moving to a parallel set-up, the graphical parameters of the related function par are being slightly changed (and cannot be reversed back to the values I want) and the calculated width is then no longer correct. This behaviour is really surprising and unexpected to me as I thought that starting socket clusters on Windows would be equivalent to just open new identical sessions of R, esp. with identical graphical parameters.

Is there is an alternative way to reach the same outcome in a parallel set-up as in the sequential set-up?

Here is a reprex:

library(parallel)
library(extrafont)

if(is.null(fonts()))
{
    ttf_import(paths = file.path("...", "Input", "Fonts"))
    loadfonts()
}

par(family = "Calibri", ps = 11, font = 1)
ReferenceWidth <- strwidth('0', units = 'in')
MainWidth <- strwidth('One simple tentative', units = 'in')
print(c(MainWidth / ReferenceWidth, ReferenceWidth))

clusterMassiveExport <- makePSOCKcluster(names = 6)
ReferenceWidth_Vector <- unlist(parLapply(cl = clusterMassiveExport, X = 1, fun = function(x){
                    par(family = "Calibri", ps = 11, font = 1)
                    ReferenceWidth <- strwidth('0', units = 'in')
                    MainWidth <- strwidth('One simple tentative', units = 'in')
                    c(MainWidth / ReferenceWidth, ReferenceWidth)
                }))
stopCluster(clusterMassiveExport)
print(ReferenceWidth_Vector)

Results:

> print(c(MainWidth / ReferenceWidth, ReferenceWidth))
[1] 18.71428571  0.07291667

> print(ReferenceWidth_Vector)
[1] 20.00000000  0.07638889

Here are some addresses to download the Calibri font:

Regular

Bold

Italic

Bold italic


Solution

  • I found a solution with the package systemfonts.

    library(parallel)
    library(systemfonts)
    
    if(any(match_fonts("Calibri") == "sans"))   add_fonts(Sys.glob(file.path("...", "Input", "Fonts", "*.ttf")))
    ResolutionPPI <- sqrt(prod(dev.size(units = c("px"))) / prod(dev.size(units = c("in"))))
    dev.off()
    
    ReferenceWidth <- string_width(strings = c('0'), family = "Calibri", size = 11, res = ResolutionPPI)
    MainWidth <- string_width(strings = c('One simple tentative'), family = "Calibri", size = 11, res = ResolutionPPI)
    print(c(MainWidth / ReferenceWidth, ReferenceWidth))
    
    clusterMassiveExport <- makePSOCKcluster(names = 6)
    clusterEvalQ(clusterMassiveExport, {
        lapply(c("systemfonts"), library, character.only = TRUE)
    })
    clusterExport(clusterMassiveExport, c("ResolutionPPI"))
    print("After clusterExport in the socket approach")
    
    ReferenceWidth_Vector <- unlist(parLapply(cl = clusterMassiveExport, X = 1:10, fun = function(x){
        ReferenceWidth <- string_width(strings = c('0'), family = "Calibri", size = 11, res = ResolutionPPI)
        MainWidth <- string_width(strings = c('One simple tentative'), family = "Calibri", size = 11, res = ResolutionPPI)
        c(MainWidth / ReferenceWidth, ReferenceWidth)
    }))
    stopCluster(clusterMassiveExport)
    print(ReferenceWidth_Vector)