blob: 6c56c76f4264f3a70b75526f646d55634b0da62a [file] [log] [blame]
# File src/library/grDevices/R/postscript.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1995-2016 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# https://www.R-project.org/Licenses/
## An environment not exported from namespace:graphics used to pass
## .PostScript.Options and .PDF.options to the windows() device for
## use in its menus, and also to hide the variables.
.PSenv <- new.env()
check.options <-
function(new, name.opt, reset = FALSE, assign.opt = FALSE,
envir = .GlobalEnv, check.attributes = c("mode", "length"),
override.check = FALSE)
{
lnew <- length(new)
if(lnew != length(newnames <- names(new)))
stop(gettextf("invalid arguments in '%s' (need named args)",
deparse(sys.call(sys.parent()))), domain = NA)
if(!is.character(name.opt))
stop("'name.opt' must be character, name of an existing list")
if(reset) {
if(exists(name.opt, envir=envir, inherits=FALSE)) {
if(length(utils::find(name.opt)) > 1)
rm(list=name.opt, envir=envir)
} else stop(gettextf("cannot reset non-existent '%s'", name.opt),
domain = NA)
}
old <- get(name.opt, envir=envir, inherits=FALSE)
if(!is.list(old))
stop(gettextf("invalid options in '%s'", name.opt), domain = NA)
oldnames <- names(old)
if(lnew > 0) {
matches <- pmatch(newnames, oldnames)
if(any(is.na(matches)))
stop(sprintf(ngettext(as.integer(sum(is.na(matches))),
"invalid argument name %s in '%s'",
"invalid argument names %s in '%s'"),
paste(sQuote(newnames[is.na(matches)]),
collapse=", "),
deparse(sys.call(sys.parent()))),
domain = NA)
else { #- match(es) found: substitute if appropriate
i.match <- oldnames[matches]
prev <- old[i.match]
doubt <- rep.int(FALSE, length(prev))
for(fn in check.attributes)
if(any(ii <- sapply(prev, fn) != sapply(new, fn))) {
## skip 'fonts';
ii <- ii & (names(prev) != "fonts")
if(!any(ii)) next
doubt <- doubt | ii
do.keep <- ii & !override.check
warning(paste(sQuote(paste0(fn, "(", names(prev[ii]), ")" )),
collapse = " and "), " ",
ngettext(as.integer(sum(ii)),
"differs between new and previous",
"differ between new and previous"),
if(any(do.keep)) {
paste0("\n\t ==> ",
gettextf("NOT changing %s",
paste(sQuote(names(prev[do.keep])),
collapse=" & ")))
} else "",
domain = NA, call. = FALSE)
}
names(new) <- NULL
if(any(doubt)) {
ii <- !doubt | override.check
old[i.match[ii]] <- new[ii]
} else old[i.match] <- new
}
if(assign.opt) assign(name.opt, old, envir=envir)
}
old
}
ps.options <- function(..., reset = FALSE, override.check = FALSE)
{
## do initialization if needed
initPSandPDFfonts()
old <- get(".PostScript.Options", envir = .PSenv)
if(reset) {
assign(".PostScript.Options",
get(".PostScript.Options.default", envir = .PSenv),
envir = .PSenv)
}
l... <- length(new <- list(...))
if(m <- match("append", names(new), 0L)) {
warning("argument 'append' is for back-compatibility and will be ignored",
immediate. = TRUE)
new <- new[-m]
}
check.options(new, name.opt = ".PostScript.Options", envir = .PSenv,
assign.opt = l... > 0, override.check = override.check)
if(reset || l... > 0) invisible(old) else old
}
setEPS <- function(...)
{
dots <- list(...)
args <- list(width = 7, height = 7)
args[names(dots)] <- dots
force <- list(onefile = FALSE, horizontal = FALSE, paper = "special")
args[names(force)] <- force
do.call("ps.options", args)
}
setPS <- function(...)
{
dots <- list(...)
args <- list(width = 0, height = 0)
args[names(dots)] <- dots
force <- list(onefile = TRUE, horizontal = TRUE, paper = "default")
args[names(force)] <- force
do.call("ps.options", args)
}
pdf.options <- function(..., reset=FALSE)
{
## do initialization if needed
initPSandPDFfonts()
old <- get(".PDF.Options", envir = .PSenv)
if(reset) {
assign(".PDF.Options",
get(".PDF.Options.default", envir = .PSenv),
envir = .PSenv)
}
l... <- length(new <- list(...))
check.options(new, name.opt = ".PDF.Options", envir = .PSenv,
assign.opt = l... > 0)
if(reset || l... > 0) invisible(old) else old
}
guessEncoding <- function(family)
{
# Three special families have special encodings, regardless of locale
if (!missing(family) &&
family %in% c("ComputerModern", "ComputerModernItalic")) {
switch(family,
"ComputerModern" = "TeXtext.enc",
"ComputerModernItalic" = "TeXtext.enc")
} else {
switch(.Platform$OS.type,
"windows" = {
switch(utils::localeToCharset()[1L],
"ISO8859-2" = "CP1250.enc",
"ISO8859-7" = "CP1253.enc", # Greek
"ISO8859-13" = "CP1257.enc",
"CP1251" = "CP1251.enc", # Cyrillic
"WinAnsi.enc")
},
{ lc <- utils::localeToCharset()
if(length(lc) == 1L)
switch(lc,
"ISO8859-1" = "ISOLatin1.enc",
"ISO8859-2" = "ISOLatin2.enc",
"ISO8859-5" = "Cyrillic.enc",
"ISO8859-7" = "Greek.enc",
"ISO8859-13" = "ISOLatin7.enc",
"ISO8859-15" = "ISOLatin9.enc",
"KOI8-R" = "KOI8-R.enc",
"KOI8-U" = "KOI8-U.enc",
"ISOLatin1.enc")
else if(lc[1L] == "UTF-8")
switch(lc[2L],
"ISO8859-1" = "ISOLatin1.enc", # what about Euro?
"ISO8859-2" = "ISOLatin2.enc",
"ISO8859-5" = "Cyrillic.enc",
"ISO8859-7" = "Greek.enc",
"ISO8859-13" = "ISOLatin7.enc",
"ISOLatin1.enc")
else "ISOLatin1.enc"})
}
}
##--> source in devPS.c :
postscript <- function(file = if(onefile) "Rplots.ps" else "Rplot%03d.ps",
onefile, family, title , fonts, encoding, bg, fg,
width, height, horizontal, pointsize,
paper, pagecentre, print.it, command, colormodel,
useKerning, fillOddEven)
{
## do initialization if needed
initPSandPDFfonts()
new <- list()
if(!missing(onefile)) new$onefile <- onefile
## 'family' is handled separately
if(!missing(title)) new$title <- title
if(!missing(fonts)) new$fonts <- fonts
if(!missing(encoding)) new$encoding <- encoding
if(!missing(bg)) new$bg <- bg
if(!missing(fg)) new$fg <- fg
if(!missing(width)) new$width <- width
if(!missing(height)) new$height <- height
if(!missing(horizontal)) new$horizontal <- horizontal
if(!missing(pointsize)) new$pointsize <- pointsize
if(!missing(paper)) new$paper <- paper
if(!missing(pagecentre)) new$pagecentre <- pagecentre
if(!missing(print.it)) new$print.it <- print.it
if(!missing(command)) new$command <- command
if(!missing(colormodel)) new$colormodel <- colormodel
if(!missing(useKerning)) new$useKerning <- useKerning
if(!missing(fillOddEven)) new$fillOddEven <- fillOddEven
old <- check.options(new, name.opt = ".PostScript.Options", envir = .PSenv)
if(is.null(old$command) || old$command == "default")
old$command <- if(!is.null(cmd <- getOption("printcmd"))) cmd else ""
# need to handle this case before encoding
if(!missing(family) &&
(inherits(family, "Type1Font") || inherits(family, "CIDFont"))) {
enc <- family$encoding
if(inherits(family, "Type1Font") &&!is.null(enc) && enc != "default"
&& (is.null(old$encoding) || old$encoding == "default"))
old$encoding <- enc
family <- family$metrics
}
if(is.null(old$encoding) || old$encoding == "default")
old$encoding <- guessEncoding(family)
## handle family separately as length can be 1, 4, or 5
if(!missing(family)) {
# Case where family is a set of AFMs
if(length(family) == 4L) {
family <- c(family, "Symbol.afm")
} else if (length(family) == 5L) {
## nothing to do
} else if (length(family) == 1L) {
## If family has been specified, match with a font in the
## font database (see postscriptFonts())
## and pass in a device-independent font name.
## NOTE that in order to match, we need both family name
## and encoding to match.
pf <- postscriptFonts(family)[[1L]]
if(is.null(pf))
stop(gettextf("unknown family '%s'", family), domain = NA)
matchFont(pf, old$encoding)
} else
stop("invalid 'family' argument")
old$family <- family
}
onefile <- old$onefile # for 'file'
if(!checkIntFormat(file))
stop(gettextf("invalid 'file' argument '%s'", file), domain = NA)
.External(C_PostScript,
file, old$paper, old$family, old$encoding, old$bg, old$fg,
old$width, old$height, old$horizontal, old$pointsize,
onefile, old$pagecentre, old$print.it, old$command,
old$title, old$fonts, old$colormodel, old$useKerning,
old$fillOddEven)
# if .ps.prolog is searched for and fails, NULL got returned.
invisible()
}
xfig <- function (file = if(onefile) "Rplots.fig" else "Rplot%03d.fig",
onefile = FALSE, encoding = "none",
paper = "default", horizontal = TRUE,
width = 0, height = 0, family = "Helvetica",
pointsize = 12, bg = "transparent", fg = "black",
pagecentre = TRUE,
defaultfont = FALSE, textspecial = FALSE)
{
## do initialization if needed
initPSandPDFfonts()
if(!checkIntFormat(file))
stop(gettextf("invalid 'file' argument '%s'", file), domain = NA)
.External(C_XFig, file, paper, family, bg, fg,
width, height, horizontal, pointsize,
onefile, pagecentre, defaultfont, textspecial, encoding)
invisible()
}
pdf <- function(file = if(onefile) "Rplots.pdf" else "Rplot%03d.pdf",
width, height, onefile, family, title, fonts, version,
paper, encoding, bg, fg, pointsize, pagecentre, colormodel,
useDingbats, useKerning, fillOddEven, compress)
{
## do initialization if needed
initPSandPDFfonts()
new <- list()
if(!missing(width)) new$width <- width
if(!missing(height)) new$height <- height
if(!missing(onefile)) new$onefile <- onefile
## 'family' is handled separately
if(!missing(title)) new$title <- title
if(!missing(fonts)) new$fonts <- fonts
if(!missing(version)) new$version <- version
if(!missing(paper)) new$paper <- paper
if(!missing(encoding)) new$encoding <- encoding
if(!missing(bg)) new$bg <- bg
if(!missing(fg)) new$fg <- fg
if(!missing(pointsize)) new$pointsize <- pointsize
if(!missing(pagecentre)) new$pagecentre <- pagecentre
if(!missing(colormodel)) new$colormodel <- colormodel
if(!missing(useDingbats)) new$useDingbats <- useDingbats
if(!missing(useKerning)) new$useKerning <- useKerning
if(!missing(fillOddEven)) new$fillOddEven <- fillOddEven
if(!missing(compress)) new$compress <- compress
old <- check.options(new, name.opt = ".PDF.Options", envir = .PSenv)
## need to handle this before encoding
if(!missing(family) &&
(inherits(family, "Type1Font") || inherits(family, "CIDFont"))) {
enc <- family$encoding
if(inherits(family, "Type1Font") &&!is.null(enc) && enc != "default"
&& (is.null(old$encoding) || old$encoding == "default"))
old$encoding <- enc
family <- family$metrics
}
if(is.null(old$encoding) || old$encoding == "default")
old$encoding <- guessEncoding()
## handle family separately as length can be 1, 4, or 5
if(!missing(family)) {
# Case where family is a set of AFMs
if(length(family) == 4L) {
family <- c(family, "Symbol.afm")
} else if (length(family) == 5L) {
## nothing to do
} else if (length(family) == 1L) {
## If family has been specified, match with a font in the
## font database (see postscriptFonts())
## and pass in a device-independent font name.
## NOTE that in order to match, we need both family name
## and encoding to match.
pf <- pdfFonts(family)[[1L]]
if(is.null(pf))
stop(gettextf("unknown family '%s'", family), domain = NA)
matchFont(pf, old$encoding)
} else
stop("invalid 'family' argument")
old$family <- family
}
# Extract version
version <- old$version
versions <- c("1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "2.0")
if (version %in% versions)
version <- as.integer(strsplit(version, "[.]")[[1L]])
else
stop("invalid PDF version")
onefile <- old$onefile # needed to set 'file'
if(!checkIntFormat(file))
stop(gettextf("invalid 'file' argument '%s'", file), domain = NA)
.External(C_PDF,
file, old$paper, old$family, old$encoding, old$bg, old$fg,
old$width, old$height, old$pointsize, onefile, old$pagecentre,
old$title, old$fonts, version[1L], version[2L],
old$colormodel, old$useDingbats, old$useKerning,
old$fillOddEven, old$compress)
invisible()
}
.ps.prolog <- c(
"/gs { gsave } bind def",
"/gr { grestore } bind def",
"/ep { showpage gr gr } bind def",
"/m { moveto } bind def",
"/l { rlineto } bind def",
"/np { newpath } bind def",
"/cp { closepath } bind def",
"/f { fill } bind def",
"/o { stroke } bind def",
"/c { newpath 0 360 arc } bind def",
"/r { 4 2 roll moveto 1 copy 3 -1 roll exch 0 exch rlineto 0 rlineto -1 mul 0 exch rlineto closepath } bind def",
"/p1 { stroke } bind def",
"/p2 { gsave bg fill grestore newpath } bind def",
"/p3 { gsave bg fill grestore stroke } bind def",
"/p6 { gsave bg eofill grestore newpath } bind def",
"/p7 { gsave bg eofill grestore stroke } bind def",
"/t { 5 -2 roll moveto gsave rotate",
" 1 index stringwidth pop",
" mul neg 0 rmoveto show grestore } bind def",
"/ta { 4 -2 roll moveto gsave rotate show } bind def",
"/tb { 2 -1 roll 0 rmoveto show } bind def",
"/cl { grestore gsave newpath 3 index 3 index moveto 1 index",
" 4 -1 roll lineto exch 1 index lineto lineto",
" closepath clip newpath } bind def",
"/rgb { setrgbcolor } bind def",
"/s { scalefont setfont } bind def")
.ps.prolog.srgb <- c(## From PLRM 3rd Ed pg 225
"/sRGB { [ /CIEBasedABC",
" << /DecodeLMN",
" [ { dup 0.03928 le",
" {12.92321 div}",
" {0.055 add 1.055 div 2.4 exp }",
" ifelse",
" } bind dup dup",
" ]",
" /MatrixLMN [0.412457 0.212673 0.019334",
" 0.357576 0.715152 0.119192",
" 0.180437 0.072175 0.950301]",
" /WhitePoint [0.9505 1.0 1.0890]",
" >>",
" ] setcolorspace } bind def"
)
####################
# PostScript font database
#
# PostScript fonts may be either Type1 or CID-keyed fonts
# (the latter provides support for CJK fonts)
####################
assign(".PostScript.Fonts", list(), envir = .PSenv)
checkFont <- function(font) UseMethod("checkFont")
checkFont.default <- function(font) stop("Invalid font type")
# A Type1 font family has a name, plus a vector of 4 or 5 directories
# for font metric afm files, plus an encoding file
# Check that the font has the correct structure and information
# Already checked that it had a name
checkFont.Type1Font <- function(font) {
if (is.null(font$family) || !is.character(font$family))
stop("invalid family name in font specification")
if (is.null(font$metrics) || !is.character(font$metrics) ||
length(font$metrics) < 4L)
stop("invalid metric information in font specification")
## Add default symbol font metric if none provided
if (length(font$metrics) == 4L)
font$metrics <- c(font$metrics, "Symbol.afm")
if (is.null(font$encoding) || !is.character(font$encoding))
stop("invalid encoding in font specification")
font
}
# A CID-keyed font family has a name, four afm files,
# a CMap name, a CMap encoding, and (for now at least) a
# PDF chunk
# (I really hope we can dispense with the latter!)
checkFont.CIDFont <- function(font) {
if (!inherits(font, "CIDFont"))
stop("Not a CID font")
if (is.null(font$family) || !is.character(font$family))
stop("invalid family name in font specification")
if (is.null(font$metrics) || !is.character(font$metrics) ||
length(font$metrics) < 4L)
stop("invalid metric information in font specification")
## Add default symbol font metric if none provided
if (length(font$metrics) == 4L)
font$metrics <- c(font$metrics, "Symbol.afm")
if (is.null(font$cmap) || !is.character(font$cmap))
stop("invalid CMap name in font specification")
if (is.null(font$cmapEncoding) || !is.character(font$cmapEncoding))
stop("invalid 'cmapEncoding' in font specification")
if (is.null(font$pdfresource) || !is.character(font$pdfresource))
stop("invalid PDF resource in font specification")
font
}
isPDF <- function(fontDBname) {
switch(fontDBname,
.PostScript.Fonts=FALSE,
.PDF.Fonts=TRUE,
stop("Invalid font database name"))
}
checkFontInUse <- function(names, fontDBname) {
for (i in names)
if (.Call(C_Type1FontInUse, i, isPDF(fontDBname))
|| .Call(C_CIDFontInUse, i, isPDF(fontDBname)))
stop(gettextf("font %s already in use", i), domain = NA)
invisible()
}
setFonts <- function(fonts, fontNames, fontDBname) {
fonts <- lapply(fonts, checkFont)
fontDB <- get(fontDBname, envir=.PSenv)
existingFonts <- fontNames %in% names(fontDB)
if (sum(existingFonts) > 0) {
checkFontInUse(fontNames[existingFonts], fontDBname)
fontDB[fontNames[existingFonts]] <- fonts[existingFonts]
}
if (sum(existingFonts) < length(fontNames))
fontDB <- c(fontDB, fonts[!existingFonts])
assign(fontDBname, fontDB, envir=.PSenv)
}
printFont <- function(font) UseMethod("printFont")
printFont.Type1Font <- function(font)
paste0(font$family, "\n (", paste(font$metrics, collapse = " "),
"\n ", font$encoding, "\n")
printFont.CIDFont <- function(font)
paste0(font$family, "\n (", paste(font$metrics, collapse = " "),
")\n ", font$CMap, "\n ", font$encoding, "\n")
printFonts <- function(fonts)
cat(paste0(names(fonts), ": ", unlist(lapply(fonts, printFont)),
collapse = ""))
# If no arguments specified, return entire font database
# If no named arguments specified, all args should be font names
# on which to get info from the database
# Else, must specify new fonts to enter into database (all
# of which must be valid PostScript font descriptions and
# all of which must be named args)
postscriptFonts <- function(...)
{
## do initialization if needed: not recursive
initPSandPDFfonts()
ndots <- length(fonts <- list(...))
if (ndots == 0L)
get(".PostScript.Fonts", envir=.PSenv)
else {
fontNames <- names(fonts)
nnames <- length(fontNames)
if (nnames == 0L) {
if (!all(sapply(fonts, is.character)))
stop(gettextf("invalid arguments in '%s' (must be font names)",
"postscriptFonts"), domain = NA)
else
get(".PostScript.Fonts", envir=.PSenv)[unlist(fonts)]
} else {
if (ndots != nnames)
stop(gettextf("invalid arguments in '%s' (need named args)",
"postscriptFonts"), domain = NA)
setFonts(fonts, fontNames, ".PostScript.Fonts")
}
}
}
# Create a valid postscript font description
Type1Font <- function(family, metrics, encoding="default")
{
font <- list(family=family, metrics=metrics, encoding=encoding)
class(font) <- "Type1Font"
checkFont(font)
}
CIDFont <- function(family, cmap, cmapEncoding, pdfresource="")
{
font <- list(family=family, metrics=c("", "", "", ""), cmap=cmap,
cmapEncoding=cmapEncoding, pdfresource=pdfresource)
class(font) <- "CIDFont"
checkFont(font)
}
####################
# PDF font database
#
# PDF fonts may be either Type1 or CID-keyed fonts
# (the latter provides support for CJK fonts)
#
# PDF font database separate from PostScript one because
# some standard CID fonts are different
####################
assign(".PDF.Fonts", list(), envir = .PSenv)
pdfFonts <- function(...)
{
## do initialization if needed: not recursive
initPSandPDFfonts()
ndots <- length(fonts <- list(...))
if (ndots == 0L)
get(".PDF.Fonts", envir=.PSenv)
else {
fontNames <- names(fonts)
nnames <- length(fontNames)
if (nnames == 0L) {
if (!all(sapply(fonts, is.character)))
stop(gettextf("invalid arguments in '%s' (must be font names)",
"pdfFonts"), domain = NA)
else
get(".PDF.Fonts", envir=.PSenv)[unlist(fonts)]
} else {
if (ndots != nnames)
stop(gettextf("invalid arguments in '%s' (need named args)",
"pdfFonts"), domain = NA)
setFonts(fonts, fontNames, ".PDF.Fonts")
}
}
}
# Match an encoding
# NOTE that if encoding in font database is "default", that is a match
matchEncoding <- function(font, encoding) UseMethod("matchEncoding")
matchEncoding.Type1Font <- function(font, encoding) {
## the trailing .enc is optional
font$encoding %in% c("default", encoding, paste0(encoding, ".enc"))
}
# Users should not be specifying a CID font AND an encoding
# when starting a new device
matchEncoding.CIDFont <- function(font, encoding) TRUE
# Match a font name (and an encoding)
matchFont <- function(font, encoding) {
if (is.null(font))
stop("unknown font")
if (!matchEncoding(font, encoding))
stop(gettextf("font encoding mismatch '%s'/'%s'",
font$encoding, encoding), domain=NA)
}
# Function to initialise default PostScript and PDF fonts
# Called at first use
# a) because that's a sensible place to do initialisation of package globals
# b) because it does not work to do it BEFORE then. In particular,
# if the body of this function is evaluated when the R code of the
# package is sourced, then the method dispatch on checkFont() does
# not work because when the R code is sourced, the S3 methods in
# this package have not yet been registered.
# Also, we want the run-time locale not the install-time locale.
initPSandPDFfonts <- function() {
if(exists(".PostScript.Options", envir = .PSenv, inherits=FALSE)) return()
assign(".PostScript.Options",
list(onefile = TRUE,
family = "Helvetica",
title = "R Graphics Output",
fonts = NULL,
encoding = "default",
bg = "transparent",
fg = "black",
width = 0,
height = 0,
horizontal = TRUE,
pointsize = 12,
paper = "default",
pagecentre = TRUE,
print.it = FALSE,
command = "default",
colormodel = "srgb",
useKerning = TRUE,
fillOddEven = FALSE), envir = .PSenv)
assign(".PostScript.Options.default",
get(".PostScript.Options", envir = .PSenv),
envir = .PSenv)
assign(".PDF.Options",
list(width = 7,
height = 7,
onefile = TRUE,
family = "Helvetica",
title = "R Graphics Output",
fonts = NULL,
version = "1.4",
paper = "special",
encoding = "default",
bg = "transparent",
fg = "black",
pointsize = 12,
pagecentre = TRUE,
colormodel = "srgb",
useDingbats = TRUE,
useKerning = TRUE,
fillOddEven = FALSE,
compress = TRUE), envir = .PSenv)
assign(".PDF.Options.default",
get(".PDF.Options", envir = .PSenv),
envir = .PSenv)
postscriptFonts(# Default Serif font is Times
serif = Type1Font("Times",
c("Times-Roman.afm", "Times-Bold.afm",
"Times-Italic.afm", "Times-BoldItalic.afm",
"Symbol.afm")),
# Default Sans Serif font is Helvetica
sans = Type1Font("Helvetica",
c("Helvetica.afm", "Helvetica-Bold.afm",
"Helvetica-Oblique.afm", "Helvetica-BoldOblique.afm",
"Symbol.afm")),
# Default Monospace font is Courier
mono = Type1Font("Courier",
c("Courier.afm", "Courier-Bold.afm",
"Courier-Oblique.afm", "Courier-BoldOblique.afm",
"Symbol.afm")),
# Remainder are standard Adobe fonts that
# should be present on PostScript devices
AvantGarde = Type1Font("AvantGarde",
c("agw_____.afm", "agd_____.afm",
"agwo____.afm", "agdo____.afm",
"Symbol.afm")),
Bookman = Type1Font("Bookman",
c("bkl_____.afm", "bkd_____.afm",
"bkli____.afm", "bkdi____.afm",
"Symbol.afm")),
Courier = Type1Font("Courier",
c("Courier.afm", "Courier-Bold.afm",
"Courier-Oblique.afm", "Courier-BoldOblique.afm",
"Symbol.afm")),
Helvetica = Type1Font("Helvetica",
c("Helvetica.afm", "Helvetica-Bold.afm",
"Helvetica-Oblique.afm", "Helvetica-BoldOblique.afm",
"Symbol.afm")),
"Helvetica-Narrow" = Type1Font("Helvetica-Narrow",
c("hvn_____.afm", "hvnb____.afm",
"hvno____.afm", "hvnbo___.afm",
"Symbol.afm")),
NewCenturySchoolbook = Type1Font("NewCenturySchoolbook",
c("ncr_____.afm", "ncb_____.afm",
"nci_____.afm", "ncbi____.afm",
"Symbol.afm")),
Palatino = Type1Font("Palatino",
c("por_____.afm", "pob_____.afm",
"poi_____.afm", "pobi____.afm",
"Symbol.afm")),
Times = Type1Font("Times",
c("Times-Roman.afm", "Times-Bold.afm",
"Times-Italic.afm", "Times-BoldItalic.afm",
"Symbol.afm")),
# URW equivalents
URWGothic = Type1Font("URWGothic",
c("a010013l.afm", "a010015l.afm",
"a010033l.afm", "a010035l.afm",
"s050000l.afm")),
URWBookman = Type1Font("URWBookman",
c("b018012l.afm", "b018015l.afm",
"b018032l.afm", "b018035l.afm",
"s050000l.afm")),
NimbusMon = Type1Font("NimbusMon",
c("n022003l.afm", "n022004l.afm",
"n022023l.afm", "n022024l.afm",
"s050000l.afm")),
NimbusSan = Type1Font("NimbusSan",
c("n019003l.afm", "n019004l.afm",
"n019023l.afm", "n019024l.afm",
"s050000l.afm")),
URWHelvetica = Type1Font("URWHelvetica",
c("n019003l.afm", "n019004l.afm",
"n019023l.afm", "n019024l.afm",
"s050000l.afm")),
NimbusSanCond = Type1Font("NimbusSanCond",
c("n019043l.afm", "n019044l.afm",
"n019063l.afm", "n019064l.afm",
"s050000l.afm")),
CenturySch = Type1Font("CenturySch",
c("c059013l.afm", "c059016l.afm",
"c059033l.afm", "c059036l.afm",
"s050000l.afm")),
URWPalladio = Type1Font("URWPalladio",
c("p052003l.afm", "p052004l.afm",
"p052023l.afm", "p052024l.afm",
"s050000l.afm")),
NimbusRom = Type1Font("NimbusRom",
c("n021003l.afm", "n021004l.afm",
"n021023l.afm", "n021024l.afm",
"s050000l.afm")),
URWTimes = Type1Font("URWTimes",
c("n021003l.afm", "n021004l.afm",
"n021023l.afm", "n021024l.afm",
"s050000l.afm")),
## And Monotype Arial
ArialMT = Type1Font("ArialMT",
c("ArialMT.afm", "ArialMT-Bold.afm",
"ArialMT-Italic.afm", "ArialMT-BoldItalic.afm",
"Symbol.afm"))
)
## All of the above Type1 fonts are the same for PostScript and PDF
do.call("pdfFonts", postscriptFonts())
## add ComputerModern to postscript only
postscriptFonts(# Computer Modern as recoded by Brian D'Urso
ComputerModern = Type1Font("ComputerModern",
c("CM_regular_10.afm", "CM_boldx_10.afm",
"CM_italic_10.afm", "CM_boldx_italic_10.afm",
"CM_symbol_10.afm"), encoding = "TeXtext.enc"),
ComputerModernItalic = Type1Font("ComputerModernItalic",
c("CM_regular_10.afm", "CM_boldx_10.afm", "cmti10.afm",
"cmbxti10.afm", "CM_symbol_10.afm"),
encoding = "TeXtext.enc")
)
# CJK fonts
postscriptFonts(Japan1 = CIDFont("HeiseiKakuGo-W5", "EUC-H", "EUC-JP"),
Japan1HeiMin = CIDFont("HeiseiMin-W3", "EUC-H", "EUC-JP"),
Japan1GothicBBB =
CIDFont("GothicBBB-Medium", "EUC-H", "EUC-JP"),
Japan1Ryumin = CIDFont("Ryumin-Light", "EUC-H", "EUC-JP"),
Korea1 = CIDFont("Baekmuk-Batang", "KSCms-UHC-H", "CP949"),
Korea1deb = CIDFont("Batang-Regular", "KSCms-UHC-H", "CP949"),
CNS1 = CIDFont("MOESung-Regular", "B5pc-H", "CP950"),
GB1 = CIDFont("BousungEG-Light-GB", "GBK-EUC-H", "GBK"))
pdfFonts(Japan1 = CIDFont("KozMinPro-Regular-Acro", "EUC-H", "EUC-JP",
paste("/FontDescriptor",
"<<",
" /Type /FontDescriptor",
" /CapHeight 740 /Ascent 1075 /Descent -272 /StemV 72",
" /FontBBox [-195 -272 1110 1075]",
" /ItalicAngle 0 /Flags 6 /XHeight 502",
" /Style << /Panose <000001000500000000000000> >>",
">>",
"/CIDSystemInfo << /Registry(Adobe) /Ordering(Japan1) /Supplement 2 >>",
"/DW 1000",
"/W [",
" 1 632 500 ",
" 8718 [500 500] ",
"]\n",
sep = "\n ")),
Japan1HeiMin = CIDFont("HeiseiMin-W3-Acro", "EUC-H", "EUC-JP",
paste("/FontDescriptor",
"<<",
" /Type /FontDescriptor",
" /CapHeight 709 /Ascent 723 /Descent -241 /StemV 69",
" /FontBBox [-123 -257 1001 910]",
" /ItalicAngle 0 /Flags 6 /XHeight 450",
" /Style << /Panose <000002020500000000000000> >>",
">>",
"/CIDSystemInfo << /Registry(Adobe) /Ordering(Japan1) /Supplement 2 >>",
"/DW 1000",
"/W [",
" 1 632 500 ",
" 8718 [500 500] ",
"]\n",
sep = "\n ")),
Japan1GothicBBB = CIDFont("GothicBBB-Medium", "EUC-H", "EUC-JP",
paste("/FontDescriptor",
"<<",
" /Type /FontDescriptor",
" /CapHeight 737 /Ascent 752 /Descent -271 /StemV 99",
" /FontBBox [-22 -252 1000 892]",
" /ItalicAngle 0 /Flags 4",
" /Style << /Panose <0801020b0500000000000000> >>",
">>",
"/CIDSystemInfo << /Registry(Adobe) /Ordering(Japan1) /Supplement 2 >>",
"/DW 1000",
"/W [",
" 1 632 500",
" 8718 [500 500]",
"]\n",
sep = "\n ")),
Japan1Ryumin = CIDFont("Ryumin-Light", "EUC-H", "EUC-JP",
paste("/FontDescriptor",
"<<",
" /Type /FontDescriptor",
" /CapHeight 709 /Ascent 723 /Descent -241 /StemV 69",
" /FontBBox [-54 -305 1000 903]",
" /ItalicAngle 0 /Flags 6",
" /Style << /Panose <010502020300000000000000> >>",
">>",
"/CIDSystemInfo << /Registry(Adobe) /Ordering(Japan1) /Supplement 2 >>",
"/DW 1000",
"/W [",
" 1 632 500",
" 8718 [500 500]",
"]\n",
sep = "\n ")),
Korea1 = CIDFont("HYSMyeongJoStd-Medium-Acro", "KSCms-UHC-H", "CP949",
paste("/FontDescriptor",
"<<",
" /Type /FontDescriptor",
" /CapHeight 720 /Ascent 880 /Descent -148 /StemV 59",
" /FontBBox [-28 -148 1001 880]",
" /ItalicAngle 0 /Flags 6 /XHeight 468",
" /Style << /Panose <000001000600000000000000> >>",
">>",
"/CIDSystemInfo << /Registry(Adobe) /Ordering(Korea1) /Supplement 1 >>",
"/DW 1000",
"/W [",
" 1 94 500",
" 97 [500] ",
" 8094 8190 500",
"]\n",
sep = "\n ")),
Korea1deb = CIDFont("HYGothic-Medium-Acro", "KSCms-UHC-H", "CP949",
paste("/FontDescriptor",
"<<",
" /Type /FontDescriptor",
" /CapHeight 737 /Ascent 752 /Descent -271 /StemV 58",
" /FontBBox [-6 -145 1003 880]",
" /ItalicAngle 0 /Flags 4 /XHeight 553",
" /Style << /Panose <000001000600000000000000> >>",
">>",
"/CIDSystemInfo << /Registry(Adobe) /Ordering(Korea1) /Supplement 1 >>",
"/DW 1000",
"/W [",
" 1 94 500",
" 97 [500] ",
" 8094 8190 500",
"]\n",
sep = "\n ")),
CNS1 = CIDFont("MSungStd-Light-Acro", "B5pc-H", "CP950",
paste("/FontDescriptor",
"<<",
" /Type /FontDescriptor",
" /CapHeight 662 /Ascent 1071 /Descent -249 /StemV 66",
" /FontBBox [-160 -249 1015 1071]",
" /ItalicAngle 0 /Flags 6 /XHeight 400",
" /Style << /Panose <000001000600000000000000> >>",
">>",
"/CIDSystemInfo << /Registry(Adobe) /Ordering(CNS1) /Supplement 0 >>",
"/DW 1000",
"/W [",
" 1 33 500",
" 34 [749 673 679 679 685 671 738 736 333 494 739 696 902 720 750 674 746 672 627 769 707 777 887 709 616]",
" 60 65 500",
" 66 [500 511 502 549 494 356 516 550 321 321 510 317 738 533 535 545 533 376 443 261 529 742 534 576 439]",
" 92 95 500",
" 13648 13742 500",
" 17603 [500]",
"]\n",
sep = "\n ")),
GB1 = CIDFont("STSong-Light-Acro", "GBK-EUC-H", "GBK",
paste("/FontDescriptor",
"<<",
" /Type /FontDescriptor",
" /CapHeight 626 /Ascent 905 /Descent -254 /StemV 48",
" /FontBBox [-134 -254 1001 905]",
" /ItalicAngle 0 /Flags 6 /XHeight 416",
" /Style << /Panose <000000000400000000000000> >>",
">>",
"/CIDSystemInfo << /Registry(Adobe) /Ordering(GB1) /Supplement 2 >>",
"/DW 1000",
"/W [",
" 1 95 500",
" 814 939 500",
" 7712 7716 500",
" 22127 22357 500",
"]\n",
sep = "\n ")))
}
# Call ghostscript to process postscript or pdf file to embed fonts
# (could also be used to convert ps or pdf to any supported format)
embedFonts <- function(file, # The ps or pdf file to convert
format, # Default guessed from file suffix
outfile = file, # By default overwrite file
fontpaths = character(),
options = character() # Additional options to ghostscript
)
{
if(!is.character(file) || length(file) != 1L || !nzchar(file))
stop("'file' must be a non-empty character string")
gsexe <- tools::find_gs_cmd()
if(!nzchar(gsexe)) stop("GhostScript was not found")
if(.Platform$OS.type == "windows") gsexe <- shortPathName(gsexe)
suffix <- gsub(".+[.]", "", file)
if (missing(format))
format <- switch(suffix,
ps = , eps = "ps2write",
pdf = "pdfwrite")
if (!is.character(format)) stop("invalid output format")
check_gs_type(gsexe, format)
tmpfile <- tempfile("Rembed")
if (length(fontpaths))
fontpaths <-
paste0("-sFONTPATH=",
shQuote(paste(fontpaths, collapse = .Platform$path.sep)))
args <- c(paste0("-dNOPAUSE -dBATCH -q -dAutoRotatePages=/None -sDEVICE=", format),
paste0(" -sOutputFile=", tmpfile),
fontpaths, options, shQuote(file))
ret <- system2(gsexe, args)
if(ret != 0)
stop(gettextf("status %d in running command '%s'", ret, cmd),
domain = NA)
if(outfile != file) args[2] <- paste0(" -sOutputFile=", shQuote(outfile))
cmd <- paste(c(shQuote(gsexe), args), collapse = " ")
file.copy(tmpfile, outfile, overwrite = TRUE)
invisible(cmd)
}