diff --git a/R/data.R b/R/data.R index 674f5cd..022b5cd 100644 --- a/R/data.R +++ b/R/data.R @@ -65,3 +65,26 @@ #' \item{`series_status`}{Series status (select USGS map series are "abandoned")} #' } "standard_scales" + +#' Standard paper and image sizes +#' +#' Reference table of standard paper, postcard, photo print, and social media image +#' sizes, for [get_paper] and [get_social_image] functions. Derived from +#' [visioguy/PaperSizes](https://github.com/visioguy/PaperSizes/) repo, [Adobe UK +#' guide to photo +#' sizes](https://www.adobe.com/uk/creativecloud/photography/discover/standard-photo-sizes.html) +#' and other sources. +#' +#' @format A data frame with 85 rows and 9 variables: +#' \describe{ +#' \item{`name`}{Name of paper} +#' \item{`series`}{Series} +#' \item{`standard`}{Standard} +#' \item{`size`}{Size in series} +#' \item{`units`}{Units ("in", "mm", or "px") for dimensions} +#' \item{`width`}{Width in units} +#' \item{`height`}{Height in units} +#' \item{`orientation`}{Portrait (width less than height), landscape, or square} +#' \item{`type`}{Type (paper, postcard, print, or social)} +#' } +"paper_sizes" diff --git a/R/get_paper.R b/R/get_paper.R new file mode 100644 index 0000000..4e738a0 --- /dev/null +++ b/R/get_paper.R @@ -0,0 +1,229 @@ + +#' Get standard paper and image sizes +#' +#' Use the "paper" parameter (matching name from [paper_sizes]), standard +#' (optionally including series and size) parameter, or width, height and units. +#' May return multiple paper sizes depending on parameters. +#' +#' If margin is provided, a block_width, block_height, and block_asp are +#' calculated and included as columns in the returned data frame. +#' +#' Paper can also be a data frame with "width", "height", "orientation", and +#' "units" columns. +#' +#' @param paper Paper, Default: 'letter'. +#' @param orientation Orientation "portrait", "landscape", or "square", Default: +#' 'portrait'. +#' @param standard Size standard, "ANSI", "ISO", "British Imperial", "JIS", +#' "USPS", "Facebook", "Instagram", or "Twitter". +#' @param series Size series (e.g. A), Default: `NULL` +#' @param size Size number (only used for "ISO" and "JIS" series). Standard, +#' series, and size may all be required to return a single paper when using +#' these parameters. +#' @param width,height Width and height in units, Default: `NULL`. +#' @param units Paper size units, either "in", "mm", or "px"; defaults to `NULL` +#' (using "in" if width or height are provided). +#' @param ncol,nrow Number of expected columns and rows in paper; used to +#' determine row_height and section_asp in paper data frame returned by +#' get_paper if nrow or ncol is greater than 1; defaults to `NULL`. +#' @param gutter Gutter distance in units. Gutter is used as the spacing between +#' nrow and columns (variable spacing is not currently supported); defaults to 0. +#' @param margin A numeric vector or ggplot2 margin object. +#' @param bbox A bounding box to use to get orientation using [sf_bbox_asp()] with orientation = TRUE. +#' @param ... Additional parameters passed to get_margin. plot_width can only be +#' passed in these parameters if paper has only a single row. margin is returned as a list column. +#' @return Data frame with one or more paper/image sizes. +#' @example examples/get_paper.R +#' @rdname get_paper +#' @export +#' @importFrom dplyr filter select mutate +#' @importFrom rlang .data +get_paper <- function(paper = "letter", + orientation = "portrait", + standard = NULL, + series = NULL, + size = NULL, + width = NULL, + height = NULL, + units = NULL, + ncol = 1, + nrow = 1, + gutter = 0, + bbox = NULL, + margin = NULL, + ...) { + type <- + dplyr::case_when( + is.data.frame(paper) && is_df_paper(paper, ext = FALSE) ~ "paper", + is.character(paper) ~ "name", + !is.null(standard) ~ "standard", + !is.null(width) | !is.null(height) | !is.null(units) ~ "dims" + ) + + paper <- + switch(type, + "paper" = paper, + "name" = get_paper_name(paper), + "standard" = get_paper_standard(standard = standard, series = series, size = size), + "dims" = get_paper_dims(width = width, height = height, units = units) + ) + + paper <- set_paper_orientation(paper, orientation = orientation, bbox = bbox) + + ncol <- ncol %||% 1 + nrow <- nrow %||% 1 + gutter <- gutter %||% 0 + + paper <- + dplyr::mutate( + paper, + ncol = ncol, + nrow = nrow, + col_width = (width - gutter) / ncol, + row_height = (height - gutter) / nrow, + gutter = gutter, + section_asp = col_width / row_height, + .after = asp + ) + + if (is.null(margin)) { + return(paper) + } + + margin <- get_margin(margin = margin, ...) + margin_num <- as.numeric(margin) + margin_width <- margin_num[[2]] + margin_num[[4]] + margin_height <- margin_num[[1]] + margin_num[[3]] + + dplyr::mutate( + paper, + block_width = width - margin_width, + block_height = height - margin_height, + block_asp = block_width / block_height, + col_width = (block_width - gutter) / ncol, + row_height = (block_height - gutter) / nrow, + section_asp = col_width / row_height, + margin = list(margin) + ) +} + +#' Set paper orientation and swap width/height if needed +#' +#' @noRd +set_paper_orientation <- function(paper, orientation = NULL, bbox = NULL) { + if (!is.null(bbox)) { + orientation <- sf_bbox_asp(bbox = bbox, orientation = TRUE) + } else { + orientation <- + match.arg( + orientation, + c("portrait", "landscape", "square"), + several.ok = TRUE + ) + } + + paper_orientation <- unique(paper$orientation) + + # Save width and height before checking orientation + if (!is.null(orientation) && (length(paper_orientation) == 1)) { + paper_width <- paper$width + paper_height <- paper$height + + if ((paper_orientation == "portrait") && (orientation == "landscape")) { + # width and height for most papers are assumed to be in a portrait format + paper$width <- paper_height + paper$height <- paper_width + paper$orientation <- orientation + } else if ((paper_orientation == "landscape") && (orientation == "portrait")) { + # width and height for most papers are assumed to be in a portrait format + paper$width <- paper_height + paper$height <- paper_width + paper$orientation <- orientation + } + } + + dplyr::mutate( + paper, + asp = width / height, + .after = height + ) +} + +#' Get paper using name +#' +#' @noRd +#' @importFrom dplyr filter +get_paper_name <- function(paper) { + paper_sizes[tolower(paper_sizes[["name"]]) %in% tolower(paper), ] +} + +#' Get paper using standard and optional series or size +#' +#' @noRd +#' @importFrom dplyr filter +get_paper_standard <- function(standard, series = NULL, size = NULL) { + standard <- match.arg(standard, c("ANSI", "ISO", "British Imperial", "JIS", "USPS", "Facebook", "Instagram", "Twitter"), several.ok = TRUE) + paper <- paper_sizes[paper_sizes[["standard"]] %in% standard, ] + + if (!is.null(series)) { + series <- match.arg(series, c("A", "B", "C", "Engineering", "Architecture", "EDDM"), several.ok = TRUE) + paper <- paper[paper[["series"]] %in% series, ] + } + + if (!is.null(size)) { + paper_series <- match.arg(series, c("A", "B", "C", "Engineering", "Architecture", "EDDM"), several.ok = TRUE) + paper <- paper[paper[["size"]] %in% size, ] + } + + return(paper) +} + +#' Get paper using width, height, or both +#' +#' @noRd +#' @importFrom dplyr filter +get_paper_dims <- function(width = NULL, height = NULL, units = NULL) { + units <- match.arg(tolower(units), c("in", "mm", "px")) + paper <- paper_sizes[paper_sizes[["units"]] %in% units, ] + + if (!is.null(width)) { + paper <- paper[paper[["width"]] %in% width, ] + } + + if (!is.null(height)) { + paper <- paper[paper[["height"]] %in% height, ] + } + + paper +} + +#' Get social media image size to match platform and format +#' +#' See `paper_sizes[paper_sizes$type == "social",]$name` for support image +#' options. +#' +#' @param image Image size name, Default: `NULL` +#' @param platform Social media platform, "Instagram", "Facebook", or "Twitter", +#' Default: `NULL` +#' @param format Image format, "post", "story", or "cover", Default: `NULL` +#' @param orientation Image orientation, Default: `NULL`. +#' @rdname get_social_image +#' @export +#' @importFrom glue glue +get_social_image <- function(image = NULL, platform = NULL, format = NULL, orientation = NULL) { + social_image_sizes <- paper_sizes[paper_sizes$type == "social", ]$name + + if (!is.null(image)) { + image <- arg_match(image, social_image_sizes) + } else { + platform <- arg_match(platform, c("Instagram", "Twitter", "Facebook")) + format <- arg_match(format, c("post", "story", "cover")) + platform_image_sizes <- grep(glue("^{platform}.+{format}"), social_image_sizes, value = TRUE) + image <- arg_match(image, platform_image_sizes) + } + + get_paper( + paper = image, + orientation = orientation + ) +} diff --git a/data-raw/paper.R b/data-raw/paper.R new file mode 100644 index 0000000..a3c766b --- /dev/null +++ b/data-raw/paper.R @@ -0,0 +1,9 @@ +## code to prepare `paper` dataset goes here + +# Derived from https://raw.githubusercontent.com/visioguy/PaperSizes/master/PaperSizes.json +ss <- "https://docs.google.com/spreadsheets/d/1P-WHYSIhVu0TR-_023hmNbNrcYY0dA4_957shbTC6Bc/edit?usp=sharing" + +paper_sizes <- + googlesheets4::read_sheet(ss) + +usethis::use_data(paper_sizes, overwrite = TRUE) diff --git a/data/paper_sizes.rda b/data/paper_sizes.rda new file mode 100644 index 0000000..c29b7dc Binary files /dev/null and b/data/paper_sizes.rda differ diff --git a/examples/get_paper.R b/examples/get_paper.R new file mode 100644 index 0000000..9a4d2a0 --- /dev/null +++ b/examples/get_paper.R @@ -0,0 +1,5 @@ +get_paper("letter") + +get_paper(paper = NULL, standard = "ISO", series = "A", size = 4) + +get_paper(width = 11, height = 17) diff --git a/man/get_paper.Rd b/man/get_paper.Rd new file mode 100644 index 0000000..c2e21a6 --- /dev/null +++ b/man/get_paper.Rd @@ -0,0 +1,79 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_paper.R +\name{get_paper} +\alias{get_paper} +\title{Get standard paper and image sizes} +\usage{ +get_paper( + paper = "letter", + orientation = "portrait", + standard = NULL, + series = NULL, + size = NULL, + width = NULL, + height = NULL, + units = NULL, + ncol = 1, + nrow = 1, + gutter = 0, + bbox = NULL, + margin = NULL, + ... +) +} +\arguments{ +\item{paper}{Paper, Default: 'letter'.} + +\item{orientation}{Orientation "portrait", "landscape", or "square", Default: +'portrait'.} + +\item{standard}{Size standard, "ANSI", "ISO", "British Imperial", "JIS", +"USPS", "Facebook", "Instagram", or "Twitter".} + +\item{series}{Size series (e.g. A), Default: \code{NULL}} + +\item{size}{Size number (only used for "ISO" and "JIS" series). Standard, +series, and size may all be required to return a single paper when using +these parameters.} + +\item{width, height}{Width and height in units, Default: \code{NULL}.} + +\item{units}{Paper size units, either "in", "mm", or "px"; defaults to \code{NULL} +(using "in" if width or height are provided).} + +\item{ncol, nrow}{Number of expected columns and rows in paper; used to +determine row_height and section_asp in paper data frame returned by +get_paper if nrow or ncol is greater than 1; defaults to \code{NULL}.} + +\item{gutter}{Gutter distance in units. Gutter is used as the spacing between +nrow and columns (variable spacing is not currently supported); defaults to 0.} + +\item{bbox}{A bounding box to use to get orientation using \code{\link[=sf_bbox_asp]{sf_bbox_asp()}} with orientation = TRUE.} + +\item{margin}{A numeric vector or ggplot2 margin object.} + +\item{...}{Additional parameters passed to get_margin. plot_width can only be +passed in these parameters if paper has only a single row. margin is returned as a list column.} +} +\value{ +Data frame with one or more paper/image sizes. +} +\description{ +Use the "paper" parameter (matching name from \link{paper_sizes}), standard +(optionally including series and size) parameter, or width, height and units. +May return multiple paper sizes depending on parameters. +} +\details{ +If margin is provided, a block_width, block_height, and block_asp are +calculated and included as columns in the returned data frame. + +Paper can also be a data frame with "width", "height", "orientation", and +"units" columns. +} +\examples{ +get_paper("letter") + +get_paper(paper = NULL, standard = "ISO", series = "A", size = 4) + +get_paper(width = 11, height = 17) +} diff --git a/man/get_social_image.Rd b/man/get_social_image.Rd new file mode 100644 index 0000000..3df5c4b --- /dev/null +++ b/man/get_social_image.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_paper.R +\name{get_social_image} +\alias{get_social_image} +\title{Get social media image size to match platform and format} +\usage{ +get_social_image( + image = NULL, + platform = NULL, + format = NULL, + orientation = NULL +) +} +\arguments{ +\item{image}{Image size name, Default: \code{NULL}} + +\item{platform}{Social media platform, "Instagram", "Facebook", or "Twitter", +Default: \code{NULL}} + +\item{format}{Image format, "post", "story", or "cover", Default: \code{NULL}} + +\item{orientation}{Image orientation, Default: \code{NULL}.} +} +\description{ +See \code{paper_sizes[paper_sizes$type == "social",]$name} for support image +options. +} diff --git a/man/paper_sizes.Rd b/man/paper_sizes.Rd new file mode 100644 index 0000000..dd4a478 --- /dev/null +++ b/man/paper_sizes.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{paper_sizes} +\alias{paper_sizes} +\title{Standard paper and image sizes} +\format{ +A data frame with 85 rows and 9 variables: +\describe{ +\item{\code{name}}{Name of paper} +\item{\code{series}}{Series} +\item{\code{standard}}{Standard} +\item{\code{size}}{Size in series} +\item{\code{units}}{Units ("in", "mm", or "px") for dimensions} +\item{\code{width}}{Width in units} +\item{\code{height}}{Height in units} +\item{\code{orientation}}{Portrait (width less than height), landscape, or square} +\item{\code{type}}{Type (paper, postcard, print, or social)} +} +} +\usage{ +paper_sizes +} +\description{ +Reference table of standard paper, postcard, photo print, and social media image +sizes, for \link{get_paper} and \link{get_social_image} functions. Derived from +\href{https://github.com/visioguy/PaperSizes/}{visioguy/PaperSizes} repo, \href{https://www.adobe.com/uk/creativecloud/photography/discover/standard-photo-sizes.html}{Adobe UK guide to photo sizes} +and other sources. +} +\keyword{datasets} diff --git a/tests/testthat/test-get_paper.R b/tests/testthat/test-get_paper.R new file mode 100644 index 0000000..56e3d0f --- /dev/null +++ b/tests/testthat/test-get_paper.R @@ -0,0 +1,32 @@ +test_that("get_paper works", { + expect_identical( + get_paper("letter")$name, + "Letter" + ) + + expect_identical( + get_paper(paper = NULL, standard = "ISO", series = "A", size = 4)$width, + 210 + ) + + expect_identical( + nrow(get_paper(width = 11, height = 17)), + 1L + ) + + expect_identical( + get_paper(ncol = 2)$ncol, + 2 + ) + + expect_identical( + get_paper(paper = "letter", ncol = 2)$col_width, + 4.25 + ) + + # Checks get_margins + expect_identical( + get_paper(paper = "letter", orientation = "landscape", margin = "extrawide")$block_asp, + 7 / 4.5 + ) +})