From 44178e8f68df1df4e932aecaec2672d1eeb89149 Mon Sep 17 00:00:00 2001 From: Thomas Lin Pedersen Date: Fri, 6 Dec 2024 14:25:52 +0100 Subject: [PATCH] updated text_width to match arguments for shape_text --- NEWS.md | 1 + R/cpp11.R | 4 ++-- R/shape_text.R | 54 +++++++++++++++++++++++++----------------- man/text_width.Rd | 38 ++++++++++++++++++++++------- src/cpp11.cpp | 8 +++---- src/string_metrics.cpp | 33 +++++++++++++------------- src/string_metrics.h | 2 +- 7 files changed, 86 insertions(+), 54 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4fe5077..5b4a9e9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,7 @@ width (#45) * Fixed a mismatch between the default values of the `width` argument in `shape_text()` and `systemfonts::match_fonts()` (#44) +* Updated `text_width()` to take the same inputs as `shape_text()` # textshaping 0.4.0 diff --git a/R/cpp11.R b/R/cpp11.R index f10c8e8..6cc080d 100644 --- a/R/cpp11.R +++ b/R/cpp11.R @@ -8,6 +8,6 @@ get_string_shape_c <- function(string, id, path, index, features, size, res, lin .Call(`_textshaping_get_string_shape_c`, string, id, path, index, features, size, res, lineheight, align, hjust, vjust, width, tracking, indent, hanging, space_before, space_after) } -get_line_width_c <- function(string, path, index, size, res, include_bearing) { - .Call(`_textshaping_get_line_width_c`, string, path, index, size, res, include_bearing) +get_line_width_c <- function(string, path, index, size, res, include_bearing, features) { + .Call(`_textshaping_get_line_width_c`, string, path, index, size, res, include_bearing, features) } diff --git a/R/shape_text.R b/R/shape_text.R index 3125e52..bfcf964 100644 --- a/R/shape_text.R +++ b/R/shape_text.R @@ -170,12 +170,13 @@ shape_text <- function(strings, id = NULL, family = '', italic = FALSE, } #' Calculate the width of a string, ignoring new-lines #' -#' This is a very simple alternative to [shape_string()] that simply calculates -#' the width of strings without taking any newline into account. As such it is -#' suitable to calculate the width of words or lines that has already been -#' splitted by `\n`. Input is recycled to the length of `strings`. +#' This is a very simple alternative to [systemfonts::shape_string()] that +#' simply calculates the width of strings without taking any newline into +#' account. As such it is suitable to calculate the width of words or lines that +#' has already been splitted by `\n`. Input is recycled to the length of +#' `strings`. #' -#' @inheritParams systemfonts::font_info +#' @inheritParams shape_text #' @param strings A character vector of strings #' @param include_bearing Logical, should left and right bearing be included in #' the string width? @@ -190,30 +191,39 @@ shape_text <- function(strings, id = NULL, family = '', italic = FALSE, #' strings <- c('A short string', 'A very very looong string') #' text_width(strings) #' -text_width <- function(strings, family = '', italic = FALSE, bold = FALSE, +text_width <- function(strings, family = '', italic = FALSE, weight = 'normal', + width = 'undefined', features = font_feature(), size = 12, res = 72, include_bearing = TRUE, path = NULL, - index = 0) { + index = 0, bold = deprecated()) { n_strings <- length(strings) + + if (lifecycle::is_present(bold)) { + lifecycle::deprecate_soft("0.4.1", "text_width(bold)", "text_width(weight='bold')") + weight <- ifelse(bold, "bold", "normal") + } + + if (inherits(features, 'font_feature')) features <- list(features) + features <- rep_len(features, n_strings) + if (is.null(path)) { - fonts <- match_fonts( - rep_len(family, n_strings), - rep_len(italic, n_strings), - ifelse(rep_len(bold, n_strings), "bold", "normal") - ) - path <- fonts$path - index <- fonts$index + family <- rep_len(family, n_strings) + italic <- rep_len(italic, n_strings) + weight <- rep_len(weight, n_strings) + width <- rep_len(width, n_strings) + loc <- match_fonts(family, italic, weight, width) + path <- loc$path + index <- loc$index + features <- Map(c, loc$features, features) } else { - if (!all(c(length(path), length(index)) == 1)) { - path <- rep_len(path, n_strings) - index <- rep_len(index, n_strings) - } + path <- rep_len(path, n_strings) + index <- rep_len(index, n_strings) } - if (length(size) != 1) size <- rep_len(size, n_strings) - if (length(res) != 1) res <- rep_len(res, n_strings) - if (length(include_bearing) != 1) include_bearing <- rep_len(include_bearing, n_strings) + size <- rep_len(size, n_strings) + res <- rep_len(res, n_strings) + include_bearing <- rep_len(include_bearing, n_strings) if (!all(file.exists(path))) stop("path must point to a valid file", call. = FALSE) get_line_width_c( as.character(strings), path, as.integer(index), as.numeric(size), - as.numeric(res), as.logical(include_bearing) + as.numeric(res), as.logical(include_bearing), features ) } diff --git a/man/text_width.Rd b/man/text_width.Rd index 6e7b042..d6876c3 100644 --- a/man/text_width.Rd +++ b/man/text_width.Rd @@ -8,12 +8,15 @@ text_width( strings, family = "", italic = FALSE, - bold = FALSE, + weight = "normal", + width = "undefined", + features = font_feature(), size = 12, res = 72, include_bearing = TRUE, path = NULL, - index = 0 + index = 0, + bold = deprecated() ) } \arguments{ @@ -23,27 +26,44 @@ text_width( \item{italic}{logical indicating the font slant} -\item{bold}{logical indicating whether the font weight} +\item{weight}{The weight to query for, either in numbers (\code{0}, \code{100}, \code{200}, +\code{300}, \code{400}, \code{500}, \code{600}, \code{700}, \code{800}, or \code{900}) or strings (\code{"undefined"}, +\code{"thin"}, \code{"ultralight"}, \code{"light"}, \code{"normal"}, \code{"medium"}, \code{"semibold"}, +\code{"bold"}, \code{"ultrabold"}, or \code{"heavy"}). \code{NA} will be interpreted as +\code{"undefined"}/\code{0}} + +\item{width}{The width to query for either in numbers (\code{0}, \code{1}, \code{2}, +\code{3}, \code{4}, \code{5}, \code{6}, \code{7}, \code{8}, or \code{9}) or strings (\code{"undefined"}, +\code{"ultracondensed"}, \code{"extracondensed"}, \code{"condensed"}, \code{"semicondensed"}, +\code{"normal"}, \code{"semiexpanded"}, \code{"expanded"}, \code{"extraexpanded"}, or +\code{"ultraexpanded"}). \code{NA} will be interpreted as \code{"undefined"}/\code{0}} + +\item{features}{A \code{\link[systemfonts:font_feature]{systemfonts::font_feature()}} object or a list of them, +giving the OpenType font features to set} -\item{size}{The pointsize of the font to use for size related measures} +\item{size}{The size in points to use for the font} -\item{res}{The ppi of the size related mesures} +\item{res}{The resolution to use when doing the shaping. Should optimally +match the resolution used when rendering the glyphs.} \item{include_bearing}{Logical, should left and right bearing be included in the string width?} \item{path, index}{path an index of a font file to circumvent lookup based on family and style} + +\item{bold}{logical indicating whether the font weight} } \value{ A numeric vector giving the width of the strings in pixels. Use the provided \code{res} value to convert it into absolute values. } \description{ -This is a very simple alternative to \code{\link[=shape_string]{shape_string()}} that simply calculates -the width of strings without taking any newline into account. As such it is -suitable to calculate the width of words or lines that has already been -splitted by \verb{\\n}. Input is recycled to the length of \code{strings}. +This is a very simple alternative to \code{\link[systemfonts:shape_string]{systemfonts::shape_string()}} that +simply calculates the width of strings without taking any newline into +account. As such it is suitable to calculate the width of words or lines that +has already been splitted by \verb{\\n}. Input is recycled to the length of +\code{strings}. } \examples{ strings <- c('A short string', 'A very very looong string') diff --git a/src/cpp11.cpp b/src/cpp11.cpp index 6d22ea2..bc9f665 100644 --- a/src/cpp11.cpp +++ b/src/cpp11.cpp @@ -20,17 +20,17 @@ extern "C" SEXP _textshaping_get_string_shape_c(SEXP string, SEXP id, SEXP path, END_CPP11 } // string_metrics.h -doubles get_line_width_c(strings string, strings path, integers index, doubles size, doubles res, logicals include_bearing); -extern "C" SEXP _textshaping_get_line_width_c(SEXP string, SEXP path, SEXP index, SEXP size, SEXP res, SEXP include_bearing) { +doubles get_line_width_c(strings string, strings path, integers index, doubles size, doubles res, logicals include_bearing, list_of features); +extern "C" SEXP _textshaping_get_line_width_c(SEXP string, SEXP path, SEXP index, SEXP size, SEXP res, SEXP include_bearing, SEXP features) { BEGIN_CPP11 - return cpp11::as_sexp(get_line_width_c(cpp11::as_cpp>(string), cpp11::as_cpp>(path), cpp11::as_cpp>(index), cpp11::as_cpp>(size), cpp11::as_cpp>(res), cpp11::as_cpp>(include_bearing))); + return cpp11::as_sexp(get_line_width_c(cpp11::as_cpp>(string), cpp11::as_cpp>(path), cpp11::as_cpp>(index), cpp11::as_cpp>(size), cpp11::as_cpp>(res), cpp11::as_cpp>(include_bearing), cpp11::as_cpp>>(features))); END_CPP11 } extern "C" { static const R_CallMethodDef CallEntries[] = { {"_textshaping_get_face_features_c", (DL_FUNC) &_textshaping_get_face_features_c, 2}, - {"_textshaping_get_line_width_c", (DL_FUNC) &_textshaping_get_line_width_c, 6}, + {"_textshaping_get_line_width_c", (DL_FUNC) &_textshaping_get_line_width_c, 7}, {"_textshaping_get_string_shape_c", (DL_FUNC) &_textshaping_get_string_shape_c, 17}, {NULL, NULL, 0} }; diff --git a/src/string_metrics.cpp b/src/string_metrics.cpp index ffae233..18d36b5 100644 --- a/src/string_metrics.cpp +++ b/src/string_metrics.cpp @@ -266,32 +266,33 @@ list get_string_shape_c(strings string, integers id, strings path, integers inde } doubles get_line_width_c(strings string, strings path, integers index, doubles size, - doubles res, logicals include_bearing) { + doubles res, logicals include_bearing, list_of features) { int n_strings = string.size(); writable::doubles widths; if (n_strings != 0) { - bool one_path = path.size() == 1; - const char* first_path = Rf_translateCharUTF8(path[0]); - int first_index = index[0]; - bool one_size = size.size() == 1; - double first_size = size[0]; - bool one_res = res.size() == 1; - double first_res = res[0]; - bool one_bear = include_bearing.size() == 1; - int first_bear = include_bearing[0]; + if (n_strings != path.size() || + n_strings != index.size() || + n_strings != features.size() || + n_strings != size.size() || + n_strings != res.size() || + n_strings != include_bearing.size() + ) { + cpp11::stop("All input must be the same size"); + } + auto all_features = create_font_features(features); + auto fonts = create_font_settings(path, index, all_features); int error = 1; double width = 0; for (int i = 0; i < n_strings; ++i) { - error = string_width( + error = ts_string_width( Rf_translateCharUTF8(string[i]), - one_path ? first_path : Rf_translateCharUTF8(path[i]), - one_path ? first_index : index[i], - one_size ? first_size : size[i], - one_res ? first_res : res[i], - one_bear ? first_bear : static_cast(include_bearing[0]), + fonts[i], + size[i], + res[i], + static_cast(include_bearing[0]), &width ); if (error) { diff --git a/src/string_metrics.h b/src/string_metrics.h index 2f77fd7..d9a5046 100644 --- a/src/string_metrics.h +++ b/src/string_metrics.h @@ -31,7 +31,7 @@ list get_string_shape_c(strings string, integers id, strings path, integers inde [[cpp11::register]] doubles get_line_width_c(strings string, strings path, integers index, doubles size, - doubles res, logicals include_bearing); + doubles res, logicals include_bearing, list_of features); int ts_string_width(const char* string, FontSettings font_info, double size, double res, int include_bearing, double* width); int ts_string_shape(const char* string, FontSettings font_info, double size,