extendr_api/graphics/device_descriptor.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
use super::{color::Color, FontFace, LineType};
// From R internals[^1]:
//
// > There should be a ‘pointsize’ argument which defaults to 12, and it should
// > give the pointsize in big points (1/72 inch). How exactly this is
// > interpreted is font-specific, but it should use a font which works with
// > lines packed 1/6 inch apart, and looks good with lines 1/5 inch apart (that
// > is with 2pt leading).
//
// [^1]: https://cran.r-project.org/doc/manuals/r-release/R-ints.html#Conventions
const POINTSIZE: f64 = 12.0;
const PT: f64 = 1.0 / 72.0;
const PT_PER_INCH: f64 = 72.0;
// From R internals[^1]:
//
// > where ‘fnsize’ is the ‘size’ of the standard font (cex=1) on the device, in
// > device units.
//
// and it seems the Postscript device chooses `pointsize` as this.
//
// [^1]: https://cran.r-project.org/doc/manuals/r-release/R-ints.html#Handling-text
const FONTSIZE: f64 = POINTSIZE;
// From R internals[^1]:
//
// > The default size of a device should be 7 inches square.
//
// [^1]: https://cran.r-project.org/doc/manuals/r-release/R-ints.html#Conventions
const WIDTH_INCH: f64 = 7.0;
const HEIGH_INCH: f64 = 7.0;
#[allow(dead_code)]
pub(crate) enum CanHAdjOption {
NotSupported = 0,
FixedAdjustment = 1,
VariableAdjustment = 2,
}
pub enum DevCapTransparency {
Unset = 0,
No = 1,
Yes = 2,
}
pub enum DevCapTransparentBg {
Unset = 0,
No = 1,
Fully = 2,
Semi = 3,
}
#[allow(dead_code)]
pub(crate) enum DevCapRaster {
Unset = 0,
No = 1,
Yes = 2,
ExceptForMissingValues = 3,
}
#[allow(dead_code)]
pub(crate) enum DevCapCapture {
Unset = 0,
No = 1,
Yes = 2,
}
#[allow(dead_code)]
pub(crate) enum DevCapLocator {
Unset = 0,
No = 1,
Yes = 2,
}
/// A builder of [libR_sys::DevDesc].
///
// # Design notes (which feels a bit too internal to be exposed as an official document)
//
// Compared to the original [DevDesc], `DeviceDescriptor` omits several fields
// that seem not very useful. For example,
//
// - `clipLeft`, `clipRight`, `clipBottom`, and `clipTop`: In most of the cases,
// this should match the device size at first.
// - `xCharOffset`, `yCharOffset`, and `yLineBias`: Because I get [the
// hatred](https://github.com/wch/r-source/blob/9f284035b7e503aebe4a804579e9e80a541311bb/src/include/R_ext/GraphicsDevice.h#L101-L103).
// They are rarely used.
// - `gamma`, and `canChangeGamma`: These fields are now ignored because gamma
// support has been removed.
// - `deviceSpecific`: This can be provided later when we actually create a
// [Device].
// - `canGenMouseDown`, `canGenMouseMove`, `canGenMouseUp`, `canGenKeybd`, and
// `canGenIdle`: These fields are currently not used by R and preserved only
// for backward-compatibility.
// - `gettingEvent`, `getEvent`: This is set true when getGraphicsEvent is
// actively looking for events. Reading the description on ["6.1.6 Graphics
// events" of R
// Internals](https://cran.r-project.org/doc/manuals/r-release/R-ints.html#Graphics-events),
// it seems this flag is not what is controlled by a graphic device.
// - `canHAdj`: it seems this parameter is used only for tweaking the `hadj`
// before passing it to the `text()` function. This tweak probably can be done
// inside `text()` easily, so let's pretend to be able to handle any
// adjustments... c.f.
// <https://github.com/wch/r-source/blob/9f284035b7e503aebe4a804579e9e80a541311bb/src/main/engine.c#L1995-L2000>
#[allow(non_snake_case)]
pub struct DeviceDescriptor {
pub(crate) left: f64,
pub(crate) right: f64,
pub(crate) bottom: f64,
pub(crate) top: f64,
// Note: the header file questions about `ipr` and `cra` [1]. Actually,
// svglite and ragg have `pointsize` and `scaling` parameters instead. But,
// I couldn't be sure if it's enough as an framework (I mean, as a package,
// abstracting these parameters to `pointsize` and `scaling` is definitely a
// good idea), so I chose to expose these parameters as they are.
//
// [1]:
// https://github.com/wch/r-source/blob/9f284035b7e503aebe4a804579e9e80a541311bb/src/include/R_ext/GraphicsDevice.h#L75-L81
pub(crate) ipr: [f64; 2],
pub(crate) cra: [f64; 2],
pub(crate) startps: f64,
pub(crate) startcol: Color,
pub(crate) startfill: Color,
pub(crate) startlty: LineType,
pub(crate) startfont: FontFace,
}
#[allow(non_snake_case)]
impl DeviceDescriptor {
pub fn new() -> Self {
Self {
// The From R internals [1] " The default size of a device should be 7
// inches square."
left: 0.0,
right: WIDTH_INCH * PT_PER_INCH,
bottom: 0.0,
top: HEIGH_INCH * PT_PER_INCH,
ipr: [PT, PT],
// Font size. Not sure why these 0.9 and 1.2 are chosen, but R
// internals says this is "a good choice."
cra: [0.9 * FONTSIZE, 1.2 * FONTSIZE],
startps: POINTSIZE,
startcol: Color::hex(0x000000),
startfill: Color::hex(0xffffff),
startlty: LineType::Solid,
startfont: FontFace::Plain,
}
}
/// Sets the device sizes (unit: point).
///
/// If not specified, the following numbers (7 inches square, following [the
/// R Internals' convetion]) will be used.
///
/// * `left`: 0
/// * `right`: 7 inches * points per inch = `7 * 72`
/// * `bottom`: 0
/// * `top`: 7 inches * points per inch = `7 * 72`
///
/// Please note that, depending on the the coordinate system of the device,
/// `left` might be larger than `right`, or `bottom` larger than `top` (for
/// example, in SVG, the origin is at the top left corner).
///
/// [the R Internals' convetion]:
/// https://cran.r-project.org/doc/manuals/r-release/R-ints.html#Conventions
pub fn device_size(mut self, left: f64, right: f64, bottom: f64, top: f64) -> Self {
self.left = left;
self.right = right;
self.bottom = bottom;
self.top = top;
self
}
/// Sets inches per raster unit (i.e. point). **Note that most of the cases,
/// there's no need to change this value.**
///
/// A point is usually 1/72 (the default value), but another value can be
/// specified here to scale the device. The first element is width, the
/// second is height.
pub fn ipr(mut self, ipr: [f64; 2]) -> Self {
self.ipr = ipr;
self
}
/// Sets the font size (unit: point). **Note that most of the cases, there's
/// no need to change this value.**
///
/// The first element is width, the second is height. If not specified,
/// `[0.9 * 12.0, 1.2 * 12.0]`, which is [suggested by the R Internals as "a
/// good choice"] will be used (12 point is the usual default for graphics
/// devices).
///
/// [suggested by the R Internals as "a good choice"]:
/// https://cran.r-project.org/doc/manuals/r-release/R-ints.html#Handling-text
pub fn cra(mut self, cra: [f64; 2]) -> Self {
self.cra = cra;
self
}
/// Sets the initial value of pointsize.
///
/// If not specified, 12, which is the usual default for graphics devices,
/// will be used.
pub fn startps(mut self, startps: f64) -> Self {
self.startps = startps;
self
}
/// Sets the initial value of colour.
///
/// If not specified, black (`0x000000`) will be used.
pub fn startcol(mut self, startcol: Color) -> Self {
self.startcol = startcol;
self
}
/// Sets the initial value of fill.
///
/// If not specified, white (`0xffffff`) will be used.
pub fn startfill(mut self, startfill: Color) -> Self {
self.startfill = startfill;
self
}
/// Sets the initial value of line type.
///
/// If not specified, [LineType::Solid] will be used.
pub fn startlty(mut self, startlty: LineType) -> Self {
self.startlty = startlty;
self
}
/// Sets the initial value of font face.
///
/// If not specified, [FontFace::Plain] will be used.
pub fn startfont(mut self, startfont: FontFace) -> Self {
self.startfont = startfont;
self
}
}
impl Default for DeviceDescriptor {
fn default() -> Self {
Self::new()
}
}