| /* |
| * R : A Computer Language for Statistical Data Analysis |
| * Copyright (C) 1995, 1996, 1997 Robert Gentleman and Ross Ihaka |
| * Copyright (C) 1998-2015 The R Core Team |
| * |
| * This source code module: |
| * Copyright (C) 1997, 1998 Paul Murrell and Ross Ihaka |
| * Copyright (C) 1998-2015 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, a copy is available at |
| * https://www.R-project.org/Licenses/ |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| #include <Defn.h> |
| |
| #include <ctype.h> |
| #include <rlocale.h> |
| |
| |
| #include <Rmath.h> // provides M_2PI |
| #include <R_ext/GraphicsEngine.h> |
| |
| |
| /* |
| * TeX Math Styles |
| * |
| * The TeXBook, Appendix G, Page 441. |
| * |
| */ |
| |
| typedef enum { |
| STYLE_SS1 = 1, |
| STYLE_SS = 2, |
| STYLE_S1 = 3, |
| STYLE_S = 4, |
| STYLE_T1 = 5, |
| STYLE_T = 6, |
| STYLE_D1 = 7, |
| STYLE_D = 8 |
| } STYLE; |
| |
| typedef struct { |
| unsigned int BoxColor; |
| double BaseCex; |
| double ReferenceX; |
| double ReferenceY; |
| double CurrentX; |
| double CurrentY; |
| double CurrentAngle; |
| double CosAngle; |
| double SinAngle; |
| STYLE CurrentStyle; |
| } mathContext; |
| |
| static GEUnit MetricUnit = GE_INCHES; |
| |
| /* Font Definitions */ |
| |
| typedef enum { |
| PlainFont = 1, |
| BoldFont = 2, |
| ItalicFont = 3, |
| BoldItalicFont = 4, |
| SymbolFont = 5 |
| } FontType; |
| |
| /* |
| * Italic Correction Factor |
| * |
| * The correction for a character is computed as ItalicFactor |
| * times the height (above the baseline) of the character's |
| * bounding box. |
| * |
| */ |
| |
| static double ItalicFactor = 0.15; |
| |
| /* Drawing basics */ |
| |
| |
| /* Convert CurrentX and CurrentY from */ |
| /* 0 angle to and CurrentAngle */ |
| |
| static double ConvertedX(mathContext *mc, pGEDevDesc dd) |
| { |
| double rotatedX = mc->ReferenceX + |
| (mc->CurrentX - mc->ReferenceX) * mc->CosAngle - |
| (mc->CurrentY - mc->ReferenceY) * mc->SinAngle; |
| return toDeviceX(rotatedX, MetricUnit, dd); |
| } |
| |
| static double ConvertedY(mathContext *mc, pGEDevDesc dd) |
| { |
| double rotatedY = mc->ReferenceY + |
| (mc->CurrentY - mc->ReferenceY) * mc->CosAngle + |
| (mc->CurrentX - mc->ReferenceX) * mc->SinAngle; |
| return toDeviceY(rotatedY, MetricUnit, dd); |
| } |
| |
| static void PMoveAcross(double xamount, mathContext *mc) |
| { |
| mc->CurrentX += xamount; |
| } |
| |
| static void PMoveUp(double yamount, mathContext *mc) |
| { |
| mc->CurrentY += yamount; |
| } |
| |
| static void PMoveTo(double x, double y, mathContext *mc) |
| { |
| mc->CurrentX = x; |
| mc->CurrentY = y; |
| } |
| |
| /* Basic Font Properties */ |
| |
| static double xHeight(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| GEMetricInfo('x', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(height, MetricUnit, dd); |
| } |
| |
| static double XHeight(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| GEMetricInfo('X', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(height, MetricUnit, dd); |
| } |
| |
| static double AxisHeight(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| GEMetricInfo('+', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(0.5 * height, MetricUnit, dd); |
| } |
| |
| static double Quad(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| GEMetricInfo('M', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(width, MetricUnit, dd); |
| } |
| |
| /* The height of digits */ |
| static double FigHeight(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| GEMetricInfo('0', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(height, MetricUnit, dd); |
| } |
| |
| /* Depth of lower case descenders */ |
| static double DescDepth(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| GEMetricInfo('g', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(depth, MetricUnit, dd); |
| } |
| |
| /* Thickness of rules */ |
| static double RuleThickness(void) |
| { |
| return 0.015; |
| } |
| |
| static double ThinSpace(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| static double OneSixth = 0.16666666666666666666; |
| GEMetricInfo('M', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(OneSixth * width, MetricUnit, dd); |
| } |
| |
| static double MediumSpace(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| static double TwoNinths = 0.22222222222222222222; |
| GEMetricInfo('M', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(TwoNinths * width, MetricUnit, dd); |
| } |
| |
| static double ThickSpace(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| static double FiveEighteenths = 0.27777777777777777777; |
| GEMetricInfo('M', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(FiveEighteenths * width, MetricUnit, dd); |
| } |
| |
| static double MuSpace(pGEcontext gc, pGEDevDesc dd) |
| { |
| double height, depth, width; |
| static double OneEighteenth = 0.05555555555555555555; |
| GEMetricInfo('M', gc, &height, &depth, &width, dd); |
| return fromDeviceHeight(OneEighteenth * width, MetricUnit, dd); |
| } |
| |
| |
| /* |
| * Mathematics Layout Parameters |
| * |
| * The TeXBook, Appendix G, Page 447. |
| * |
| * These values are based on an inspection of TeX metafont files |
| * together with some visual simplification. |
| * |
| * Note : The values are ``optimised'' for PostScript. |
| * |
| */ |
| |
| typedef enum { |
| sigma2, sigma5, sigma6, sigma8, sigma9, sigma10, sigma11, |
| sigma12, sigma13, sigma14, sigma15, sigma16, sigma17, sigma18, |
| sigma19, sigma20, sigma21, sigma22, xi8, xi9, xi10, xi11, xi12, xi13 |
| } |
| TEXPAR; |
| |
| #define SUBS 0.7 |
| |
| static double TeX(TEXPAR which, pGEcontext gc, pGEDevDesc dd) |
| { |
| switch(which) { |
| case sigma2: /* space */ |
| case sigma5: /* x_height */ |
| return xHeight(gc, dd); |
| |
| case sigma6: /* quad */ |
| return Quad(gc, dd); |
| |
| case sigma8: /* num1 */ |
| return AxisHeight(gc, dd) |
| + 3.51 * RuleThickness() |
| + 0.15 * XHeight(gc, dd) /* 54/36 * 0.1 */ |
| + SUBS * DescDepth(gc, dd); |
| case sigma9: /* num2 */ |
| return AxisHeight(gc, dd) |
| + 1.51 * RuleThickness() |
| + 0.08333333 * XHeight(gc, dd); /* 30/36 * 0.1 */ |
| case sigma10: /* num3 */ |
| return AxisHeight(gc, dd) |
| + 1.51 * RuleThickness() |
| + 0.1333333 * XHeight(gc, dd); /* 48/36 * 0.1 */ |
| case sigma11: /* denom1 */ |
| return - AxisHeight(gc, dd) |
| + 3.51 * RuleThickness() |
| + SUBS * FigHeight(gc, dd) |
| + 0.344444 * XHeight(gc, dd); /* 124/36 * 0.1 */ |
| case sigma12: /* denom2 */ |
| return - AxisHeight(gc, dd) |
| + 1.51 * RuleThickness() |
| + SUBS * FigHeight(gc, dd) |
| + 0.08333333 * XHeight(gc, dd); /* 30/36 * 0.1 */ |
| |
| case sigma13: /* sup1 */ |
| return 0.95 * xHeight(gc, dd); |
| case sigma14: /* sup2 */ |
| return 0.825 * xHeight(gc, dd); |
| case sigma15: /* sup3 */ |
| return 0.7 * xHeight(gc, dd); |
| |
| case sigma16: /* sub1 */ |
| return 0.35 * xHeight(gc, dd); |
| case sigma17: /* sub2 */ |
| return 0.45 * XHeight(gc, dd); |
| |
| case sigma18: /* sup_drop */ |
| return 0.3861111 * XHeight(gc, dd); |
| |
| case sigma19: /* sub_drop */ |
| return 0.05 * XHeight(gc, dd); |
| |
| case sigma20: /* delim1 */ |
| return 2.39 * XHeight(gc, dd); |
| case sigma21: /* delim2 */ |
| return 1.01 *XHeight(gc, dd); |
| |
| case sigma22: /* axis_height */ |
| return AxisHeight(gc, dd); |
| |
| case xi8: /* default_rule_thickness */ |
| return RuleThickness(); |
| |
| case xi9: /* big_op_spacing1 */ |
| case xi10: /* big_op_spacing2 */ |
| case xi11: /* big_op_spacing3 */ |
| case xi12: /* big_op_spacing4 */ |
| case xi13: /* big_op_spacing5 */ |
| return 0.15 * XHeight(gc, dd); |
| default:/* never happens (enum type) */ |
| error("invalid `which' in C function TeX"); return 0;/*-Wall*/ |
| } |
| } |
| |
| static STYLE GetStyle(mathContext *mc) |
| { |
| return mc->CurrentStyle; |
| } |
| |
| static void SetStyle(STYLE newstyle, mathContext *mc, pGEcontext gc) |
| { |
| switch (newstyle) { |
| case STYLE_D: |
| case STYLE_T: |
| case STYLE_D1: |
| case STYLE_T1: |
| gc->cex = 1.0 * mc->BaseCex; |
| break; |
| case STYLE_S: |
| case STYLE_S1: |
| gc->cex = 0.7 * mc->BaseCex; |
| break; |
| case STYLE_SS: |
| case STYLE_SS1: |
| gc->cex = 0.5 * mc->BaseCex; |
| break; |
| default: |
| error(_("invalid math style encountered")); |
| } |
| mc->CurrentStyle = newstyle; |
| } |
| |
| static void SetPrimeStyle(STYLE style, mathContext *mc, pGEcontext gc) |
| { |
| switch (style) { |
| case STYLE_D: |
| case STYLE_D1: |
| SetStyle(STYLE_D1, mc, gc); |
| break; |
| case STYLE_T: |
| case STYLE_T1: |
| SetStyle(STYLE_T1, mc, gc); |
| break; |
| case STYLE_S: |
| case STYLE_S1: |
| SetStyle(STYLE_S1, mc, gc); |
| break; |
| case STYLE_SS: |
| case STYLE_SS1: |
| SetStyle(STYLE_SS1, mc, gc); |
| break; |
| } |
| } |
| |
| static void SetSupStyle(STYLE style, mathContext *mc, pGEcontext gc) |
| { |
| switch (style) { |
| case STYLE_D: |
| case STYLE_T: |
| SetStyle(STYLE_S, mc, gc); |
| break; |
| case STYLE_D1: |
| case STYLE_T1: |
| SetStyle(STYLE_S1, mc, gc); |
| break; |
| case STYLE_S: |
| case STYLE_SS: |
| SetStyle(STYLE_SS, mc, gc); |
| break; |
| case STYLE_S1: |
| case STYLE_SS1: |
| SetStyle(STYLE_SS1, mc, gc); |
| break; |
| } |
| } |
| |
| static void SetSubStyle(STYLE style, mathContext *mc, pGEcontext gc) |
| { |
| switch (style) { |
| case STYLE_D: |
| case STYLE_T: |
| case STYLE_D1: |
| case STYLE_T1: |
| SetStyle(STYLE_S1, mc, gc); |
| break; |
| case STYLE_S: |
| case STYLE_SS: |
| case STYLE_S1: |
| case STYLE_SS1: |
| SetStyle(STYLE_SS1, mc, gc); |
| break; |
| } |
| } |
| |
| static void SetNumStyle(STYLE style, mathContext *mc, pGEcontext gc) |
| { |
| switch (style) { |
| case STYLE_D: |
| SetStyle(STYLE_T, mc, gc); |
| break; |
| case STYLE_D1: |
| SetStyle(STYLE_T1, mc, gc); |
| break; |
| default: |
| SetSupStyle(style, mc, gc); |
| } |
| } |
| |
| static void SetDenomStyle(STYLE style, mathContext *mc, pGEcontext gc) |
| { |
| if (style > STYLE_T) |
| SetStyle(STYLE_T1, mc, gc); |
| else |
| SetSubStyle(style, mc, gc); |
| } |
| |
| static int IsCompactStyle(STYLE style, mathContext *mc, pGEcontext gc) |
| { |
| switch (style) { |
| case STYLE_D1: |
| case STYLE_T1: |
| case STYLE_S1: |
| case STYLE_SS1: |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| |
| #ifdef max |
| #undef max |
| #endif |
| /* Return maximum of two doubles. */ |
| static double max(double x, double y) |
| { |
| if (x > y) return x; |
| else return y; |
| } |
| |
| |
| /* Bounding Boxes */ |
| /* These including italic corrections and an */ |
| /* indication of whether the nucleus was simple. */ |
| |
| typedef struct { |
| double height; |
| double depth; |
| double width; |
| double italic; |
| int simple; |
| } BBOX; |
| |
| |
| #define bboxHeight(bbox) bbox.height |
| #define bboxDepth(bbox) bbox.depth |
| #define bboxWidth(bbox) bbox.width |
| #define bboxItalic(bbox) bbox.italic |
| #define bboxSimple(bbox) bbox.simple |
| |
| |
| static BBOX MakeBBox(double height, double depth, double width) |
| { |
| BBOX bbox; |
| bboxHeight(bbox) = height; |
| bboxDepth(bbox) = depth; |
| bboxWidth(bbox) = width; |
| bboxItalic(bbox) = 0; |
| bboxSimple(bbox) = 0; |
| return bbox; |
| } |
| |
| static BBOX NullBBox(void) |
| { |
| BBOX bbox; |
| bboxHeight(bbox) = 0; |
| bboxDepth(bbox) = 0; |
| bboxWidth(bbox) = 0; |
| bboxItalic(bbox) = 0; |
| bboxSimple(bbox) = 0; |
| return bbox; |
| } |
| |
| static BBOX ShiftBBox(BBOX bbox1, double shiftV) |
| { |
| bboxHeight(bbox1) = bboxHeight(bbox1) + shiftV; |
| bboxDepth(bbox1) = bboxDepth(bbox1) - shiftV; |
| bboxWidth(bbox1) = bboxWidth(bbox1); |
| bboxItalic(bbox1) = bboxItalic(bbox1); |
| bboxSimple(bbox1) = bboxSimple(bbox1); |
| return bbox1; |
| } |
| |
| static BBOX EnlargeBBox(BBOX bbox, double deltaHeight, double deltaDepth, |
| double deltaWidth) |
| { |
| bboxHeight(bbox) += deltaHeight; |
| bboxDepth(bbox) += deltaDepth; |
| bboxWidth(bbox) += deltaWidth; |
| return bbox; |
| } |
| |
| static BBOX CombineBBoxes(BBOX bbox1, BBOX bbox2) |
| { |
| bboxHeight(bbox1) = max(bboxHeight(bbox1), bboxHeight(bbox2)); |
| bboxDepth(bbox1) = max(bboxDepth(bbox1), bboxDepth(bbox2)); |
| bboxWidth(bbox1) = bboxWidth(bbox1) + bboxWidth(bbox2); |
| bboxItalic(bbox1) = bboxItalic(bbox2); |
| bboxSimple(bbox1) = bboxSimple(bbox2); |
| return bbox1; |
| } |
| |
| static BBOX CombineAlignedBBoxes(BBOX bbox1, BBOX bbox2) |
| { |
| bboxHeight(bbox1) = max(bboxHeight(bbox1), bboxHeight(bbox2)); |
| bboxDepth(bbox1) = max(bboxDepth(bbox1), bboxDepth(bbox2)); |
| bboxWidth(bbox1) = max(bboxWidth(bbox1), bboxWidth(bbox2)); |
| bboxItalic(bbox1) = 0; |
| bboxSimple(bbox1) = 0; |
| return bbox1; |
| } |
| |
| static BBOX CombineOffsetBBoxes(BBOX bbox1, int italic1, |
| BBOX bbox2, int italic2, |
| double xoffset, |
| double yoffset) |
| { |
| double width1 = bboxWidth(bbox1) + (italic1 ? bboxItalic(bbox1) : 0); |
| double width2 = bboxWidth(bbox2) + (italic2 ? bboxItalic(bbox2) : 0); |
| bboxWidth(bbox1) = max(width1, width2 + xoffset); |
| bboxHeight(bbox1) = max(bboxHeight(bbox1), bboxHeight(bbox2) + yoffset); |
| bboxDepth(bbox1) = max(bboxDepth(bbox1), bboxDepth(bbox2) - yoffset); |
| bboxItalic(bbox1) = 0; |
| bboxSimple(bbox1) = 0; |
| return bbox1; |
| } |
| |
| static double CenterShift(BBOX bbox) |
| { |
| return 0.5 * (bboxHeight(bbox) - bboxDepth(bbox)); |
| } |
| |
| |
| typedef struct { |
| char *name; |
| int code; |
| } SymTab; |
| |
| /* Determine a match between symbol name and string. */ |
| |
| static int NameMatch(SEXP expr, const char *aString) |
| { |
| if (!isSymbol(expr)) return 0; |
| return !strcmp(CHAR(PRINTNAME(expr)), aString); |
| } |
| |
| static int StringMatch(SEXP expr, const char *aString) |
| { |
| return !strcmp(translateChar(STRING_ELT(expr, 0)), aString); |
| } |
| /* Code to determine the ascii code corresponding */ |
| /* to an element of a mathematical expression. */ |
| |
| #define A_HAT 94 |
| #define A_TILDE 126 |
| |
| #define S_SPACE 32 |
| #define S_PARENLEFT 40 |
| #define S_PARENRIGHT 41 |
| #define S_ASTERISKMATH 42 |
| #define S_COMMA 44 |
| #define S_SLASH 47 |
| #define S_RADICALEX 96 |
| #define S_FRACTION 164 |
| #define S_ELLIPSIS 188 |
| #define S_INTERSECTION 199 |
| #define S_UNION 200 |
| #define S_PRODUCT 213 |
| #define S_RADICAL 214 |
| #define S_SUM 229 |
| #define S_INTEGRAL 242 |
| #define S_BRACKETLEFTTP 233 |
| #define S_BRACKETLEFTBT 235 |
| #define S_BRACKETRIGHTTP 249 |
| #define S_BRACKETRIGHTBT 251 |
| |
| #define N_LIM 1001 |
| #define N_LIMINF 1002 |
| #define N_LIMSUP 1003 |
| #define N_INF 1004 |
| #define N_SUP 1005 |
| #define N_MIN 1006 |
| #define N_MAX 1007 |
| |
| |
| /* The Full Adobe Symbol Font */ |
| |
| static SymTab |
| SymbolTable[] = { |
| { "space", 32 }, |
| { "exclam", 33 }, |
| { "universal", 34 }, |
| { "numbersign", 35 }, |
| { "existential", 36 }, |
| { "percent", 37 }, |
| { "ampersand", 38 }, |
| { "suchthat", 39 }, |
| { "parenleft", 40 }, |
| { "parenright", 41 }, |
| { "asteriskmath", 42 }, |
| { "plus", 43 }, |
| { "comma", 44 }, |
| { "minus", 45 }, |
| { "period", 46 }, |
| { "slash", 47 }, |
| { "0", 48 }, |
| { "1", 49 }, |
| { "2", 50 }, |
| { "3", 51 }, |
| { "4", 52 }, |
| { "5", 53 }, |
| { "6", 54 }, |
| { "7", 55 }, |
| { "8", 56 }, |
| { "9", 57 }, |
| { "colon", 58 }, |
| { "semicolon", 59 }, |
| { "less", 60 }, |
| { "equal", 61 }, |
| { "greater", 62 }, |
| { "question", 63 }, |
| { "congruent", 64 }, |
| |
| { "Alpha",/* 0101= */65 }, /* Upper Case Greek Characters */ |
| { "Beta", 66 }, |
| { "Chi", 67 }, |
| { "Delta", 68 }, |
| { "Epsilon", 69 }, |
| { "Phi", 70 }, |
| { "Gamma", 71 }, |
| { "Eta", 72 }, |
| { "Iota", 73 }, |
| { "theta1", 74 }, |
| { "vartheta", 74 }, |
| { "Kappa", 75 }, |
| { "Lambda", 76 }, |
| { "Mu", 77 }, |
| { "Nu", 78 }, |
| { "Omicron", 79 }, |
| { "Pi", 80 }, |
| { "Theta", 81 }, |
| { "Rho", 82 }, |
| { "Sigma", 83 }, |
| { "Tau", 84 }, |
| { "Upsilon", 85 }, |
| { "sigma1", 86 }, |
| { "varsigma", 86 }, |
| { "stigma", 86 }, |
| { "Omega", 87 }, |
| { "Xi", 88 }, |
| { "Psi", 89 }, |
| { "Zeta",/* 0132 = */90 }, |
| |
| { "bracketleft", 91 }, /* Miscellaneous Special Characters */ |
| { "therefore", 92 }, |
| { "bracketright", 93 }, |
| { "perpendicular", 94 }, |
| { "underscore", 95 }, |
| { "radicalex", 96 }, |
| |
| { "alpha",/* 0141= */97 }, /* Lower Case Greek Characters */ |
| { "beta", 98 }, |
| { "chi", 99 }, |
| { "delta", 100 }, |
| { "epsilon", 101 }, |
| { "phi", 102 }, |
| { "gamma", 103 }, |
| { "eta", 104 }, |
| { "iota", 105 }, |
| { "phi1", 106 }, |
| { "varphi", 106 }, |
| { "kappa", 107 }, |
| { "lambda", 108 }, |
| { "mu", 109 }, |
| { "nu", 110 }, |
| { "omicron", 111 }, |
| { "pi", 112 }, |
| { "theta", 113 }, |
| { "rho", 114 }, |
| { "sigma", 115 }, |
| { "tau", 116 }, |
| { "upsilon", 117 }, |
| { "omega1", 118 }, |
| { "omega", 119 }, |
| { "xi", 120 }, |
| { "psi", 121 }, |
| { "zeta",/* 0172= */122 }, |
| |
| { "braceleft", 123 }, /* Miscellaneous Special Characters */ |
| { "bar", 124 }, |
| { "braceright", 125 }, |
| { "similar", 126 }, |
| |
| { "Upsilon1", 161 }, /* Lone Greek */ |
| { "minute", 162 }, |
| { "lessequal", 163 }, |
| { "fraction", 164 }, |
| { "infinity", 165 }, |
| { "florin", 166 }, |
| { "club", 167 }, |
| { "diamond", 168 }, |
| { "heart", 169 }, |
| { "spade", 170 }, |
| { "arrowboth", 171 }, |
| { "arrowleft", 172 }, |
| { "arrowup", 173 }, |
| { "arrowright", 174 }, |
| { "arrowdown", 175 }, |
| { "degree", 176 }, |
| { "plusminus", 177 }, |
| { "second", 178 }, |
| { "greaterequal", 179 }, |
| { "multiply", 180 }, |
| { "proportional", 181 }, |
| { "partialdiff", 182 }, |
| { "bullet", 183 }, |
| { "divide", 184 }, |
| { "notequal", 185 }, |
| { "equivalence", 186 }, |
| { "approxequal", 187 }, |
| { "ellipsis", 188 }, |
| { "arrowvertex", 189 }, |
| { "arrowhorizex", 190 }, |
| { "carriagereturn", 191 }, |
| { "aleph", 192 }, |
| { "Ifraktur", 193 }, |
| { "Rfraktur", 194 }, |
| { "weierstrass", 195 }, |
| { "circlemultiply", 196 }, |
| { "circleplus", 197 }, |
| { "emptyset", 198 }, |
| { "intersection", 199 },/* = 0307 */ |
| { "union", 200 },/* = 0310 */ |
| { "propersuperset", 201 }, |
| { "reflexsuperset", 202 }, |
| { "notsubset", 203 }, |
| { "propersubset", 204 }, |
| { "reflexsubset", 205 }, |
| { "element", 206 }, |
| { "notelement", 207 }, |
| { "angle", 208 }, |
| { "nabla", 209 },/* = 0321, Adobe name 'gradient' */ |
| { "registerserif", 210 }, |
| { "copyrightserif", 211 }, |
| { "trademarkserif", 212 }, |
| { "product", 213 }, |
| { "radical", 214 }, |
| { "dotmath", 215 }, |
| { "logicaland", 217 }, |
| { "logicalor", 218 }, |
| { "arrowdblboth", 219 }, |
| { "arrowdblleft", 220 }, |
| { "arrowdblup", 221 }, |
| { "arrowdblright", 222 }, |
| { "arrowdbldown", 223 }, |
| { "lozenge", 224 }, |
| { "angleleft", 225 }, |
| { "registersans", 226 }, |
| { "copyrightsans", 227 }, |
| { "trademarksans", 228 }, |
| { "summation", 229 }, |
| { "parenlefttp", 230 }, |
| { "parenleftex", 231 }, |
| { "parenleftbt", 232 }, |
| { "bracketlefttp", 233 }, |
| { "bracketleftex", 234 }, |
| { "bracketleftbt", 235 }, |
| { "bracelefttp", 236 }, |
| { "braceleftmid", 237 }, |
| { "braceleftbt", 238 }, |
| { "braceex", 239 }, |
| { "angleright", 241 }, |
| { "integral", 242 }, |
| { "integraltp", 243 }, |
| { "integralex", 244 }, |
| { "integralbt", 245 }, |
| { "parenrighttp", 246 }, |
| { "parenrightex", 247 }, |
| { "parenrightbt", 248 }, |
| { "bracketrighttp", 249 }, |
| { "bracketrightex", 250 }, |
| { "bracketrightbt", 251 }, |
| { "bracerighttp", 252 }, |
| { "bracerightmid", 253 }, |
| { "bracerightbt", 254 }, |
| |
| { NULL, 0 }, |
| }; |
| |
| static int SymbolCode(SEXP expr) |
| { |
| int i; |
| for (i = 0; SymbolTable[i].code; i++) |
| if (NameMatch(expr, SymbolTable[i].name)) |
| return SymbolTable[i].code; |
| return 0; |
| } |
| |
| /* this is the one really used: */ |
| static int TranslatedSymbol(SEXP expr) |
| { |
| int code = SymbolCode(expr); |
| if ((0101 <= code && code <= 0132) || /* l/c Greek */ |
| (0141 <= code && code <= 0172) || /* u/c Greek */ |
| code == 0300 || /* aleph */ |
| code == 0241 || /* Upsilon1 */ |
| code == 0242 || /* minute */ |
| code == 0245 || /* infinity */ |
| code == 0260 || /* degree */ |
| code == 0262 || /* second */ |
| code == 0266 || /* partialdiff */ |
| code == 0321 || /* nabla */ |
| 0) |
| return code; |
| else // not translated |
| return 0; |
| } |
| |
| /* Code to determine the nature of an expression. */ |
| |
| static int FormulaExpression(SEXP expr) |
| { |
| return (TYPEOF(expr) == LANGSXP); |
| } |
| |
| static int NameAtom(SEXP expr) |
| { |
| return (TYPEOF(expr) == SYMSXP); |
| } |
| |
| static int NumberAtom(SEXP expr) |
| { |
| return ((TYPEOF(expr) == REALSXP) || |
| (TYPEOF(expr) == INTSXP) || |
| (TYPEOF(expr) == CPLXSXP)); |
| } |
| |
| static int StringAtom(SEXP expr) |
| { |
| return (TYPEOF(expr) == STRSXP); |
| } |
| |
| /* Code to determine a font from the */ |
| /* nature of the expression */ |
| |
| static FontType GetFont(pGEcontext gc) |
| { |
| return gc->fontface; |
| } |
| |
| static FontType SetFont(FontType font, pGEcontext gc) |
| { |
| FontType prevfont = gc->fontface; |
| gc->fontface = font; |
| return prevfont; |
| } |
| |
| static int UsingItalics(pGEcontext gc) |
| { |
| return (gc->fontface == ItalicFont || |
| gc->fontface == BoldItalicFont); |
| } |
| |
| static BBOX GlyphBBox(int chr, pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| double height, depth, width; |
| int chr1 = chr; |
| if(dd->dev->wantSymbolUTF8 && gc->fontface == 5) |
| chr1 = -Rf_AdobeSymbol2ucs2(chr); |
| GEMetricInfo(chr1, gc, &height, &depth, &width, dd); |
| bboxHeight(bbox) = fromDeviceHeight(height, MetricUnit, dd); |
| bboxDepth(bbox) = fromDeviceHeight(depth, MetricUnit, dd); |
| bboxWidth(bbox) = fromDeviceHeight(width, MetricUnit, dd); |
| bboxItalic(bbox) = 0; |
| bboxSimple(bbox) = 1; |
| return bbox; |
| } |
| |
| static BBOX RenderElement(SEXP, int, mathContext*, pGEcontext , pGEDevDesc); |
| static BBOX RenderOffsetElement(SEXP, double, double, int, |
| mathContext*, pGEcontext , pGEDevDesc); |
| static BBOX RenderExpression(SEXP, int, mathContext*, pGEcontext , pGEDevDesc); |
| static BBOX RenderSymbolChar(int, int, mathContext*, pGEcontext , pGEDevDesc); |
| |
| |
| /* Code to Generate Bounding Boxes and Draw Formulae. */ |
| |
| static BBOX RenderItalicCorr(BBOX bbox, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| if (bboxItalic(bbox) > 0) { |
| if (draw) |
| PMoveAcross(bboxItalic(bbox), mc); |
| bboxWidth(bbox) += bboxItalic(bbox); |
| bboxItalic(bbox) = 0; |
| } |
| return bbox; |
| } |
| |
| static BBOX RenderGap(double gap, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| if (draw) |
| PMoveAcross(gap, mc); |
| return MakeBBox(0, 0, gap); |
| } |
| |
| /* Draw a Symbol from the Special Font: |
| this is assumed to be 8-bit encoded in Adobe Symbol. |
| */ |
| |
| static BBOX RenderSymbolChar(int ascii, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| FontType prev; |
| BBOX bbox; |
| char asciiStr[2]; |
| if (ascii == A_HAT || ascii == A_TILDE) |
| prev = SetFont(PlainFont, gc); |
| else |
| prev = SetFont(SymbolFont, gc); |
| bbox = GlyphBBox(ascii, gc, dd); |
| if (draw) { |
| asciiStr[0] = (char) ascii; |
| asciiStr[1] = '\0'; |
| GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), asciiStr, |
| CE_SYMBOL, |
| 0.0, 0.0, mc->CurrentAngle, gc, |
| dd); |
| PMoveAcross(bboxWidth(bbox), mc); |
| } |
| SetFont(prev, gc); |
| return bbox; |
| } |
| |
| /* Draw a Symbol String in "Math Mode" */ |
| /* This code inserts italic corrections after */ |
| /* every character. */ |
| |
| static BBOX RenderSymbolStr(const char *str, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| char chr[7] = ""; |
| const char *s = str; |
| BBOX glyphBBox; |
| BBOX resultBBox = NullBBox(); |
| double lastItalicCorr = 0; |
| FontType prevfont = GetFont(gc); |
| FontType font = prevfont; |
| |
| if (str) { |
| /* Need to advance by character, not byte, except in the symbol font. |
| The latter would be hard to achieve, but perhaps not impossible. |
| */ |
| if(mbcslocale && gc->fontface != 5) { |
| wchar_t wc; |
| mbstate_t mb_st; |
| size_t res; |
| |
| mbs_init(&mb_st); |
| while (*s) { |
| wc = 0; |
| res = mbrtowc(&wc, s, MB_LEN_MAX, &mb_st); |
| if(res == -1) error("invalid multibyte string '%s'", s); |
| if (iswdigit(wc) && font != PlainFont) { |
| font = PlainFont; |
| SetFont(PlainFont, gc); |
| } |
| else if (font != prevfont) { |
| font = prevfont; |
| SetFont(prevfont, gc); |
| } |
| glyphBBox = GlyphBBox((unsigned int) wc, gc, dd); |
| if (UsingItalics(gc)) |
| bboxItalic(glyphBBox) = |
| ItalicFactor * bboxHeight(glyphBBox); |
| else |
| bboxItalic(glyphBBox) = 0; |
| if (draw) { |
| memset(chr, 0, sizeof(chr)); |
| /* should not be possible, as we just converted to wc */ |
| if(wcrtomb(chr, wc, &mb_st) == -1) |
| error("invalid multibyte string"); |
| PMoveAcross(lastItalicCorr, mc); |
| GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), chr, |
| CE_NATIVE, |
| 0.0, 0.0, mc->CurrentAngle, gc, dd); |
| PMoveAcross(bboxWidth(glyphBBox), mc); |
| } |
| bboxWidth(resultBBox) += lastItalicCorr; |
| resultBBox = CombineBBoxes(resultBBox, glyphBBox); |
| lastItalicCorr = bboxItalic(glyphBBox); |
| s += res; |
| } |
| } else { |
| while (*s) { |
| if (isdigit((int)*s) && font != PlainFont) { |
| font = PlainFont; |
| SetFont(PlainFont, gc); |
| } |
| else if (font != prevfont) { |
| font = prevfont; |
| SetFont(prevfont, gc); |
| } |
| glyphBBox = GlyphBBox((unsigned char) *s, gc, dd); |
| if (UsingItalics(gc)) |
| bboxItalic(glyphBBox) = |
| ItalicFactor * bboxHeight(glyphBBox); |
| else |
| bboxItalic(glyphBBox) = 0; |
| if (draw) { |
| chr[0] = *s; |
| PMoveAcross(lastItalicCorr, mc); |
| GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), chr, |
| CE_NATIVE, |
| 0.0, 0.0, mc->CurrentAngle, gc, dd); |
| PMoveAcross(bboxWidth(glyphBBox), mc); |
| } |
| bboxWidth(resultBBox) += lastItalicCorr; |
| resultBBox = CombineBBoxes(resultBBox, glyphBBox); |
| lastItalicCorr = bboxItalic(glyphBBox); |
| s++; |
| } |
| } |
| if (font != prevfont) |
| SetFont(prevfont, gc); |
| } |
| bboxSimple(resultBBox) = 1; |
| return resultBBox; |
| } |
| |
| /* Code for Character String Atoms. */ |
| |
| /* This only gets called from RenderAccent */ |
| static BBOX RenderChar(int ascii, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| char asciiStr[7]; |
| |
| bbox = GlyphBBox(ascii, gc, dd); |
| if (draw) { |
| memset(asciiStr, 0, sizeof(asciiStr)); |
| if(mbcslocale) { |
| size_t res = wcrtomb(asciiStr, ascii, NULL); |
| if(res == -1) |
| error("invalid character in current multibyte locale"); |
| } else |
| asciiStr[0] = (char) ascii; |
| GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), asciiStr, CE_NATIVE, |
| 0.0, 0.0, mc->CurrentAngle, gc, |
| dd); |
| PMoveAcross(bboxWidth(bbox), mc); |
| } |
| return bbox; |
| } |
| |
| /* This gets called on strings and PRINTNAMES */ |
| static BBOX RenderStr(const char *str, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX glyphBBox = NullBBox(); /* might be use do italic corr on str="" */ |
| BBOX resultBBox = NullBBox(); |
| int nc = 0; |
| cetype_t enc = (gc->fontface == 5) ? CE_SYMBOL : CE_NATIVE; |
| |
| if (str) { |
| /* need to advance by character, not byte, except in the symbol font */ |
| if(mbcslocale && gc->fontface != 5) { |
| size_t n = strlen(str), used; |
| wchar_t wc; |
| const char *p = str; |
| mbstate_t mb_st; |
| mbs_init(&mb_st); |
| while ((used = Mbrtowc(&wc, p, n, &mb_st)) > 0) { |
| /* On Windows could have sign extension here */ |
| glyphBBox = GlyphBBox((unsigned int) wc, gc, dd); |
| resultBBox = CombineBBoxes(resultBBox, glyphBBox); |
| p += used; n -= used; nc++; |
| } |
| } else { |
| const char *s = str; |
| while (*s) { |
| /* Watch for sign extension here - fixed > 2.7.1 */ |
| glyphBBox = GlyphBBox((unsigned char) *s, gc, dd); |
| resultBBox = CombineBBoxes(resultBBox, glyphBBox); |
| s++; nc++; |
| } |
| } |
| if(nc > 1) { |
| /* Finding the width by adding up boxes is incorrect (kerning) */ |
| double wd = GEStrWidth(str, enc, gc, dd); |
| bboxWidth(resultBBox) = fromDeviceHeight(wd, MetricUnit, dd); |
| } |
| if (draw) { |
| GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), str, enc, |
| 0.0, 0.0, mc->CurrentAngle, gc, dd); |
| PMoveAcross(bboxWidth(resultBBox), mc); |
| } |
| if (UsingItalics(gc)) |
| bboxItalic(resultBBox) = ItalicFactor * bboxHeight(glyphBBox); |
| else |
| bboxItalic(resultBBox) = 0; |
| } |
| bboxSimple(resultBBox) = 1; |
| return resultBBox; |
| } |
| |
| |
| /* Code for Symbol Font Atoms */ |
| |
| static BBOX RenderSymbol(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| int code; |
| if ((code = TranslatedSymbol(expr))) |
| return RenderSymbolChar(code, draw, mc, gc, dd); |
| else |
| return RenderSymbolStr(CHAR(PRINTNAME(expr)), draw, mc, gc, dd); |
| } |
| |
| static BBOX RenderSymbolString(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| int code; |
| if ((code = TranslatedSymbol(expr))) |
| return RenderSymbolChar(code, draw, mc, gc, dd); |
| else |
| return RenderStr(CHAR(PRINTNAME(expr)), draw, mc, gc, dd); |
| } |
| |
| |
| /* Code for Numeric Atoms */ |
| |
| static BBOX RenderNumber(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| FontType prevfont = SetFont(PlainFont, gc); |
| PrintDefaults(); |
| bbox = RenderStr(CHAR(asChar(expr)), draw, mc, gc, dd); |
| SetFont(prevfont, gc); |
| return bbox; |
| } |
| |
| /* Code for String Atoms */ |
| |
| static BBOX RenderString(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| return RenderStr(translateChar(STRING_ELT(expr, 0)), draw, mc, gc, dd); |
| } |
| |
| /* Code for Ellipsis (ldots, cdots, ...) */ |
| |
| static int DotsAtom(SEXP expr) |
| { |
| if (NameMatch(expr, "cdots") || |
| NameMatch(expr, "...") || |
| NameMatch(expr, "ldots")) |
| return 1; |
| return 0; |
| } |
| |
| static BBOX RenderDots(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox = RenderSymbolChar(S_ELLIPSIS, 0, mc, gc, dd); |
| if (NameMatch(expr, "cdots") || NameMatch(expr, "...")) { |
| double shift = AxisHeight(gc, dd) - 0.5 * bboxHeight(bbox); |
| if (draw) { |
| PMoveUp(shift, mc); |
| RenderSymbolChar(S_ELLIPSIS, 1, mc, gc, dd); |
| PMoveUp(-shift, mc); |
| } |
| return ShiftBBox(bbox, shift); |
| } |
| else { |
| if (draw) |
| RenderSymbolChar(S_ELLIPSIS, 1, mc, gc, dd); |
| return bbox; |
| } |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Atoms |
| * |
| */ |
| |
| static BBOX RenderAtom(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| if (NameAtom(expr)) { |
| if (DotsAtom(expr)) |
| return RenderDots(expr, draw, mc, gc, dd); |
| else |
| return RenderSymbol(expr, draw, mc, gc, dd); |
| } |
| else if (NumberAtom(expr)) |
| return RenderNumber(expr, draw, mc, gc, dd); |
| else if (StringAtom(expr)) |
| return RenderString(expr, draw, mc, gc, dd); |
| |
| return NullBBox(); /* -Wall */ |
| } |
| |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Binary / Unary Operators (~, +, -, ... ) |
| * |
| * Note that there are unary and binary ~ s. |
| * |
| */ |
| |
| static int SpaceAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "~"); |
| } |
| |
| |
| static BBOX RenderSpace(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| |
| BBOX opBBox, arg1BBox, arg2BBox; |
| int nexpr = length(expr); |
| |
| if (nexpr == 2) { |
| opBBox = RenderSymbolChar(' ', draw, mc, gc, dd); |
| arg1BBox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| return CombineBBoxes(opBBox, arg1BBox); |
| } |
| else if (nexpr == 3) { |
| arg1BBox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| opBBox = RenderSymbolChar(' ', draw, mc, gc, dd); |
| arg2BBox = RenderElement(CADDR(expr), draw, mc, gc, dd); |
| opBBox = CombineBBoxes(arg1BBox, opBBox); |
| opBBox = CombineBBoxes(opBBox, arg2BBox); |
| return opBBox; |
| } |
| else |
| error(_("invalid mathematical annotation")); |
| |
| return NullBBox(); /* -Wall */ |
| } |
| |
| static SymTab BinTable[] = { |
| { "!", 041 }, |
| { "*", 052 }, /* Binary Operators */ |
| { "+", 053 }, |
| { "-", 055 }, |
| { "/", 057 }, |
| { ":", 072 }, |
| { "%+-%", 0261 }, |
| { "%*%", 0264 }, |
| { "%/%", 0270 }, |
| { "%intersection%", 0307 }, |
| { "%union%", 0310 }, |
| { "%.%", 0327 }, /* cdot or dotmath */ |
| { NULL, 0 } |
| }; |
| |
| static int BinAtom(SEXP expr) |
| { |
| int i; |
| |
| for (i = 0; BinTable[i].code; i++) |
| if (NameMatch(expr, BinTable[i].name)) |
| return BinTable[i].code; |
| return 0; |
| } |
| |
| static BBOX RenderSlash(int draw, mathContext *mc, pGEcontext gc, |
| pGEDevDesc dd) |
| { |
| /* Line Drawing Version */ |
| double x[2], y[2]; |
| double depth = 0.5 * TeX(sigma22, gc, dd); |
| double height = XHeight(gc, dd) + 0.5 * TeX(sigma22, gc, dd); |
| double width = 0.5 * xHeight(gc, dd); |
| if (draw) { |
| int savedlty = gc->lty; |
| double savedlwd = gc->lwd; |
| PMoveAcross(0.5 * width, mc); |
| PMoveUp(-depth, mc); |
| x[0] = ConvertedX(mc, dd); |
| y[0] = ConvertedY(mc, dd); |
| PMoveAcross(width, mc); |
| PMoveUp(depth + height, mc); |
| x[1] = ConvertedX(mc, dd); |
| y[1] = ConvertedY(mc, dd); |
| PMoveUp(-height, mc); |
| gc->lty = LTY_SOLID; |
| if (gc->lwd > 1) |
| gc->lwd = 1; |
| GEPolyline(2, x, y, gc, dd); |
| PMoveAcross(0.5 * width, mc); |
| gc->lty = savedlty; |
| gc->lwd = savedlwd; |
| } |
| return MakeBBox(height, depth, 2 * width); |
| } |
| |
| static BBOX RenderBin(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| int op = BinAtom(CAR(expr)); |
| int nexpr = length(expr); |
| BBOX bbox; |
| double gap; |
| |
| if(nexpr == 3) { |
| if (op == S_ASTERISKMATH) { |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw, |
| mc, gc, dd)); |
| } |
| else if (op == S_SLASH) { |
| gap = 0; |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderSlash(draw, mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd)); |
| return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw, |
| mc, gc, dd)); |
| } |
| else { |
| gap = (mc->CurrentStyle > STYLE_S) ? MediumSpace(gc, dd) : 0; |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderSymbolChar(op, draw, mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd)); |
| return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw, |
| mc, gc, dd)); |
| } |
| } |
| else if(nexpr == 2) { |
| gap = (mc->CurrentStyle > STYLE_S) ? ThinSpace(gc, dd) : 0; |
| bbox = RenderSymbolChar(op, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd)); |
| return CombineBBoxes(bbox, RenderElement(CADR(expr), draw, mc, |
| gc, dd)); |
| } |
| else |
| error(_("invalid mathematical annotation")); |
| |
| return NullBBox(); /* -Wall */ |
| |
| } |
| |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Subscript and Superscipt Expressions |
| * |
| * Rules 18, 18a, ..., 18f of the TeXBook. |
| * |
| */ |
| |
| static int SuperAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "^"); |
| } |
| |
| static int SubAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "["); |
| } |
| |
| /* Note : If all computations are correct */ |
| /* We do not need to save and restore the */ |
| /* current location here. This is paranoia. */ |
| static BBOX RenderSub(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bodyBBox, subBBox; |
| SEXP body = CADR(expr); |
| SEXP sub = CADDR(expr); |
| STYLE style = GetStyle(mc); |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| double v, s16; |
| bodyBBox = RenderElement(body, draw, mc, gc, dd); |
| bodyBBox = RenderItalicCorr(bodyBBox, draw, mc, gc, dd); |
| v = bboxSimple(bodyBBox) ? 0 : bboxDepth(bodyBBox) + TeX(sigma19, gc, dd); |
| s16 = TeX(sigma16, gc, dd); |
| SetSubStyle(style, mc, gc); |
| subBBox = RenderElement(sub, 0, mc, gc, dd); |
| v = max(max(v, s16), bboxHeight(subBBox) - 0.8 * sigma5); |
| subBBox = RenderOffsetElement(sub, 0, -v, draw, mc, gc, dd); |
| bodyBBox = CombineBBoxes(bodyBBox, subBBox); |
| SetStyle(style, mc, gc); |
| if (draw) |
| PMoveTo(savedX + bboxWidth(bodyBBox), savedY, mc); |
| return bodyBBox; |
| } |
| |
| static BBOX RenderSup(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bodyBBox, subBBox, supBBox; |
| SEXP body = CADR(expr); |
| SEXP sup = CADDR(expr); |
| SEXP sub = R_NilValue; /* -Wall */ |
| STYLE style = GetStyle(mc); |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| double theta, delta, width; |
| double u, p; |
| double v, s5, s17; |
| int haveSub; |
| if (FormulaExpression(body) && SubAtom(CAR(body))) { |
| sub = CADDR(body); |
| body = CADR(body); |
| haveSub = 1; |
| } |
| else haveSub = 0; |
| bodyBBox = RenderElement(body, draw, mc, gc, dd); |
| delta = bboxItalic(bodyBBox); |
| bodyBBox = RenderItalicCorr(bodyBBox, draw, mc, gc, dd); |
| width = bboxWidth(bodyBBox); |
| if (bboxSimple(bodyBBox)) { |
| u = 0; |
| v = 0; |
| } |
| else { |
| u = bboxHeight(bodyBBox) - TeX(sigma18, gc, dd); |
| v = bboxDepth(bodyBBox) + TeX(sigma19, gc, dd); |
| } |
| theta = TeX(xi8, gc, dd); |
| s5 = TeX(sigma5, gc, dd); |
| s17 = TeX(sigma17, gc, dd); |
| if (style == STYLE_D) |
| p = TeX(sigma13, gc, dd); |
| else if (IsCompactStyle(style, mc, gc)) |
| p = TeX(sigma15, gc, dd); |
| else |
| p = TeX(sigma14, gc, dd); |
| SetSupStyle(style, mc, gc); |
| supBBox = RenderElement(sup, 0, mc, gc, dd); |
| u = max(max(u, p), bboxDepth(supBBox) + 0.25 * s5); |
| |
| if (haveSub) { |
| SetSubStyle(style, mc, gc); |
| subBBox = RenderElement(sub, 0, mc, gc, dd); |
| v = max(v, s17); |
| if ((u - bboxDepth(supBBox)) - (bboxHeight(subBBox) - v) < 4 * theta) { |
| double psi = 0.8 * s5 - (u - bboxDepth(supBBox)); |
| if (psi > 0) { |
| u += psi; |
| v -= psi; |
| } |
| } |
| if (draw) |
| PMoveTo(savedX, savedY, mc); |
| subBBox = RenderOffsetElement(sub, width, -v, draw, mc, gc, dd); |
| if (draw) |
| PMoveTo(savedX, savedY, mc); |
| SetSupStyle(style, mc, gc); |
| supBBox = RenderOffsetElement(sup, width + delta, u, draw, mc, gc, dd); |
| bodyBBox = CombineAlignedBBoxes(bodyBBox, subBBox); |
| bodyBBox = CombineAlignedBBoxes(bodyBBox, supBBox); |
| } |
| else { |
| supBBox = RenderOffsetElement(sup, 0, u, draw, mc, gc, dd); |
| bodyBBox = CombineBBoxes(bodyBBox, supBBox); |
| } |
| if (draw) |
| PMoveTo(savedX + bboxWidth(bodyBBox), savedY, mc); |
| SetStyle(style, mc, gc); |
| return bodyBBox; |
| } |
| |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Accented Expressions (widehat, bar, widetilde, ...) |
| * |
| */ |
| |
| #define ACCENT_GAP 0.2 |
| #define HAT_HEIGHT 0.3 |
| |
| #define NTILDE 8 |
| #define DELTA 0.05 |
| |
| static int WideTildeAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "widetilde"); |
| } |
| |
| static BBOX RenderWideTilde(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| BBOX bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| double height = bboxHeight(bbox); |
| /*double width = bboxWidth(bbox);*/ |
| double totalwidth = bboxWidth(bbox) + bboxItalic(bbox); |
| double delta = totalwidth * (1 - 2 * DELTA) / NTILDE; |
| double start = DELTA * totalwidth; |
| double accentGap = ACCENT_GAP * XHeight(gc, dd); |
| double hatHeight = 0.5 * HAT_HEIGHT * XHeight(gc, dd); |
| double c = M_2PI / NTILDE; |
| double x[NTILDE + 3], y[NTILDE + 3]; |
| double baseX, baseY, xval, yval; |
| int i; |
| |
| if (draw) { |
| int savedlty = gc->lty; |
| double savedlwd = gc->lwd; |
| baseX = savedX; |
| baseY = savedY + height + accentGap; |
| PMoveTo(baseX, baseY, mc); |
| x[0] = ConvertedX(mc, dd); |
| y[0] = ConvertedY(mc, dd); |
| for (i = 0; i <= NTILDE; i++) { |
| xval = start + i * delta; |
| yval = 0.5 * hatHeight * (sin(c * i) + 1); |
| PMoveTo(baseX + xval, baseY + yval, mc); |
| x[i + 1] = ConvertedX(mc, dd); |
| y[i + 1] = ConvertedY(mc, dd); |
| } |
| PMoveTo(baseX + totalwidth, baseY + hatHeight, mc); |
| x[NTILDE + 2] = ConvertedX(mc, dd); |
| y[NTILDE + 2] = ConvertedY(mc, dd); |
| gc->lty = LTY_SOLID; |
| if (gc->lwd > 1) |
| gc->lwd = 1; |
| GEPolyline(NTILDE + 3, x, y, gc, dd); |
| PMoveTo(savedX + totalwidth, savedY, mc); |
| gc->lty = savedlty; |
| gc->lwd = savedlwd; |
| } |
| return MakeBBox(height + accentGap + hatHeight, |
| bboxDepth(bbox), totalwidth); |
| } |
| |
| static int WideHatAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "widehat"); |
| } |
| |
| static BBOX RenderWideHat(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| BBOX bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| double accentGap = ACCENT_GAP * XHeight(gc, dd); |
| double hatHeight = HAT_HEIGHT * XHeight(gc, dd); |
| double totalwidth = bboxWidth(bbox) + bboxItalic(bbox); |
| double height = bboxHeight(bbox); |
| double width = bboxWidth(bbox); |
| double x[3], y[3]; |
| |
| if (draw) { |
| int savedlty = gc->lty; |
| double savedlwd = gc->lwd; |
| PMoveTo(savedX, savedY + height + accentGap, mc); |
| x[0] = ConvertedX(mc, dd); |
| y[0] = ConvertedY(mc, dd); |
| PMoveAcross(0.5 * totalwidth, mc); |
| PMoveUp(hatHeight, mc); |
| x[1] = ConvertedX(mc, dd); |
| y[1] = ConvertedY(mc, dd); |
| PMoveAcross(0.5 * totalwidth, mc); |
| PMoveUp(-hatHeight, mc); |
| x[2] = ConvertedX(mc, dd); |
| y[2] = ConvertedY(mc, dd); |
| gc->lty = LTY_SOLID; |
| if (gc->lwd > 1) |
| gc->lwd = 1; |
| GEPolyline(3, x, y, gc, dd); |
| PMoveTo(savedX + width, savedY, mc); |
| gc->lty = savedlty; |
| gc->lwd = savedlwd; |
| } |
| return EnlargeBBox(bbox, accentGap + hatHeight, 0, 0); |
| } |
| |
| static int BarAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "bar"); |
| } |
| |
| static BBOX RenderBar(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| BBOX bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| double accentGap = ACCENT_GAP * XHeight(gc, dd); |
| /*double hatHeight = HAT_HEIGHT * XHeight(gc, dd);*/ |
| double height = bboxHeight(bbox); |
| double width = bboxWidth(bbox); |
| double offset = bboxItalic(bbox); |
| double x[2], y[2]; |
| |
| if (draw) { |
| int savedlty = gc->lty; |
| double savedlwd = gc->lwd; |
| PMoveTo(savedX + offset, savedY + height + accentGap, mc); |
| x[0] = ConvertedX(mc, dd); |
| y[0] = ConvertedY(mc, dd); |
| PMoveAcross(width, mc); |
| x[1] = ConvertedX(mc, dd); |
| y[1] = ConvertedY(mc, dd); |
| gc->lty = LTY_SOLID; |
| if (gc->lwd > 1) |
| gc->lwd = 1; |
| GEPolyline(2, x, y, gc, dd); |
| PMoveTo(savedX + width, savedY, mc); |
| gc->lty = savedlty; |
| gc->lwd = savedlwd; |
| } |
| return EnlargeBBox(bbox, accentGap, 0, 0); |
| } |
| |
| static struct { |
| char *name; |
| int code; |
| } |
| AccentTable[] = { |
| { "hat", 94 }, |
| { "ring", 176 }, |
| { "tilde", 126 }, |
| { "dot", 215 }, |
| { NULL, 0 }, |
| }; |
| |
| static int AccentCode(SEXP expr) |
| { |
| int i; |
| for (i = 0; AccentTable[i].code; i++) |
| if (NameMatch(expr, AccentTable[i].name)) |
| return AccentTable[i].code; |
| return 0; |
| } |
| |
| static int AccentAtom(SEXP expr) |
| { |
| return NameAtom(expr) && (AccentCode(expr) != 0); |
| } |
| |
| static void NORET InvalidAccent(SEXP expr) |
| { |
| errorcall(expr, _("invalid accent")); |
| } |
| |
| static BBOX RenderAccent(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| SEXP body, accent; |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| BBOX bodyBBox, accentBBox; |
| double xoffset, yoffset, width, italic; |
| int code; |
| if (length(expr) != 2) |
| InvalidAccent(expr); |
| accent = CAR(expr); |
| body = CADR(expr); |
| code = AccentCode(accent); |
| if (code == 0) |
| InvalidAccent(expr); |
| bodyBBox = RenderElement(body, 0, mc, gc, dd); |
| italic = bboxItalic(bodyBBox); |
| if (code == 176 || /* ring (as degree) */ |
| code == 215) /* dotmath */ |
| accentBBox = RenderSymbolChar(code, 0, mc, gc, dd); |
| else |
| accentBBox = RenderChar(code, 0, mc, gc, dd); |
| width = max(bboxWidth(bodyBBox) + bboxItalic(bodyBBox), |
| bboxWidth(accentBBox)); |
| xoffset = 0.5 *(width - bboxWidth(bodyBBox)); |
| bodyBBox = RenderGap(xoffset, draw, mc, gc, dd); |
| bodyBBox = CombineBBoxes(bodyBBox, RenderElement(body, draw, mc, gc, dd)); |
| bodyBBox = CombineBBoxes(bodyBBox, RenderGap(xoffset, draw, mc, gc, dd)); |
| PMoveTo(savedX, savedY, mc); |
| xoffset = 0.5 *(width - bboxWidth(accentBBox)) |
| + 0.9 * italic; |
| yoffset = bboxHeight(bodyBBox) + bboxDepth(accentBBox) + |
| 0.1 * XHeight(gc, dd); |
| if (draw) { |
| PMoveTo(savedX + xoffset, savedY + yoffset, mc); |
| if (code == 176 || /* ring (as degree) */ |
| code == 215) /* dotmath */ |
| RenderSymbolChar(code, draw, mc, gc, dd); |
| else |
| RenderChar(code, draw, mc, gc, dd); |
| } |
| bodyBBox = CombineOffsetBBoxes(bodyBBox, 0, accentBBox, 0, |
| xoffset, yoffset); |
| if (draw) |
| PMoveTo(savedX + width, savedY, mc); |
| return bodyBBox; |
| } |
| |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Fraction Expressions (over, atop) |
| * |
| * Rules 15, 15a, ..., 15e of the TeXBook |
| * |
| */ |
| |
| static void NumDenomVShift(BBOX numBBox, BBOX denomBBox, |
| double *u, double *v, |
| mathContext *mc, pGEcontext gc, pGEDevDesc dd) |
| { |
| double a, delta, phi, theta; |
| a = TeX(sigma22, gc, dd); |
| theta = TeX(xi8, gc, dd); |
| if(mc->CurrentStyle > STYLE_T) { |
| *u = TeX(sigma8, gc, dd); |
| *v = TeX(sigma11, gc, dd); |
| phi = 3 * theta; |
| } |
| else { |
| *u = TeX(sigma9, gc, dd); |
| *v = TeX(sigma12, gc, dd); |
| phi = theta; |
| } |
| delta = (*u - bboxDepth(numBBox)) - (a + 0.5 * theta); |
| /* |
| * Numerators and denominators on fractions appear too far from |
| * horizontal bar. |
| * Reread of Knuth suggests removing "+ theta" components below. |
| */ |
| if (delta < phi) |
| *u += (phi - delta); /* + theta; */ |
| delta = (a + 0.5 * theta) - (bboxHeight(denomBBox) - *v); |
| if (delta < phi) |
| *v += (phi - delta); /* + theta; */ |
| } |
| |
| static void NumDenomHShift(BBOX numBBox, BBOX denomBBox, |
| double *numShift, double *denomShift) |
| { |
| double numWidth = bboxWidth(numBBox); |
| double denomWidth = bboxWidth(denomBBox); |
| if (numWidth > denomWidth) { |
| *numShift = 0; |
| *denomShift = (numWidth - denomWidth) / 2; |
| } |
| else { |
| *numShift = (denomWidth - numWidth) / 2; |
| *denomShift = 0; |
| } |
| } |
| |
| static BBOX RenderFraction(SEXP expr, int rule, int draw, |
| mathContext *mc, pGEcontext gc, pGEDevDesc dd) |
| { |
| SEXP numerator = CADR(expr); |
| SEXP denominator = CADDR(expr); |
| BBOX numBBox, denomBBox; |
| double nHShift, dHShift; |
| double nVShift, dVShift; |
| double width, x[2], y[2]; |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| STYLE style; |
| |
| style = GetStyle(mc); |
| SetNumStyle(style, mc, gc); |
| numBBox = RenderItalicCorr(RenderElement(numerator, 0, mc, gc, dd), 0, |
| mc, gc, dd); |
| SetDenomStyle(style, mc, gc); |
| denomBBox = RenderItalicCorr(RenderElement(denominator, 0, mc, gc, dd), 0, |
| mc, gc, dd); |
| SetStyle(style, mc, gc); |
| |
| width = max(bboxWidth(numBBox), bboxWidth(denomBBox)); |
| NumDenomHShift(numBBox, denomBBox, &nHShift, &dHShift); |
| NumDenomVShift(numBBox, denomBBox, &nVShift, &dVShift, mc, gc, dd); |
| |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| SetNumStyle(style, mc, gc); |
| numBBox = RenderOffsetElement(numerator, nHShift, nVShift, draw, mc, |
| gc, dd); |
| |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| SetDenomStyle(style, mc, gc); |
| denomBBox = RenderOffsetElement(denominator, dHShift, -dVShift, draw, |
| mc, gc, dd); |
| |
| SetStyle(style, mc, gc); |
| |
| if (draw) { |
| if (rule) { |
| int savedlty = gc->lty; |
| double savedlwd = gc->lwd; |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| PMoveUp(AxisHeight(gc, dd), mc); |
| x[0] = ConvertedX(mc, dd); |
| y[0] = ConvertedY(mc, dd); |
| PMoveAcross(width, mc); |
| x[1] = ConvertedX(mc, dd); |
| y[1] = ConvertedY(mc, dd); |
| gc->lty = LTY_SOLID; |
| if (gc->lwd > 1) |
| gc->lwd = 1; |
| GEPolyline(2, x, y, gc, dd); |
| PMoveUp(-AxisHeight(gc, dd), mc); |
| gc->lty = savedlty; |
| gc->lwd = savedlwd; |
| } |
| PMoveTo(savedX + width, savedY, mc); |
| } |
| return CombineAlignedBBoxes(numBBox, denomBBox); |
| } |
| |
| static BBOX RenderUnderline(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| SEXP body = CADR(expr); |
| BBOX BBox; |
| double width, adepth, depth, x[2], y[2]; |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| |
| BBox = RenderItalicCorr(RenderElement(body, 0, mc, gc, dd), 0, mc, gc, dd); |
| width = bboxWidth(BBox); |
| |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| BBox = RenderElement(body, draw, mc, gc, dd); |
| adepth = 0.1 * XHeight(gc, dd); |
| depth = bboxDepth(BBox) + adepth; |
| |
| if (draw) { |
| int savedlty = gc->lty; |
| double savedlwd = gc->lwd; |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| PMoveUp(-depth, mc); |
| x[0] = ConvertedX(mc, dd); |
| y[0] = ConvertedY(mc, dd); |
| PMoveAcross(width, mc); |
| x[1] = ConvertedX(mc, dd); |
| y[1] = ConvertedY(mc, dd); |
| gc->lty = LTY_SOLID; |
| if (gc->lwd > 1) |
| gc->lwd = 1; |
| GEPolyline(2, x, y, gc, dd); |
| PMoveUp(depth, mc); |
| gc->lty = savedlty; |
| gc->lwd = savedlwd; |
| PMoveTo(savedX + width, savedY, mc); |
| } |
| return EnlargeBBox(BBox, 0.0, adepth, 0.0); |
| } |
| |
| |
| static int OverAtom(SEXP expr) |
| { |
| return NameAtom(expr) && |
| (NameMatch(expr, "over") || NameMatch(expr, "frac")); |
| } |
| |
| static BBOX RenderOver(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| return RenderFraction(expr, 1, draw, mc, gc, dd); |
| } |
| |
| static int UnderlAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "underline"); |
| } |
| |
| static BBOX RenderUnderl(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| return RenderUnderline(expr, draw, mc, gc, dd); |
| } |
| |
| |
| static int AtopAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "atop"); |
| } |
| |
| static BBOX RenderAtop(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| return RenderFraction(expr, 0, draw, mc, gc, dd); |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Grouped Expressions (e.g. ( ... )) |
| * |
| * group(ldelim, body, rdelim) |
| * |
| * bgroup(ldelim, body, rdelim) |
| * |
| */ |
| |
| #define DelimSymbolMag 1.25 |
| |
| static int DelimCode(SEXP expr, SEXP head) |
| { |
| int code = 0; |
| if (NameAtom(head)) { |
| if (NameMatch(head, "lfloor")) |
| code = S_BRACKETLEFTBT; |
| else if (NameMatch(head, "rfloor")) |
| code = S_BRACKETRIGHTBT; |
| if (NameMatch(head, "lceil")) |
| code = S_BRACKETLEFTTP; |
| else if (NameMatch(head, "rceil")) |
| code = S_BRACKETRIGHTTP; |
| } |
| else if (StringAtom(head) && length(head) > 0) { |
| if (StringMatch(head, "|")) |
| code = '|'; |
| else if (StringMatch(head, "||")) // historical anomaly |
| code = '|'; |
| else if (StringMatch(head, "(")) |
| code = '('; |
| else if (StringMatch(head, ")")) |
| code = ')'; |
| else if (StringMatch(head, "[")) |
| code = '['; |
| else if (StringMatch(head, "]")) |
| code = ']'; |
| else if (StringMatch(head, "{")) |
| code = '{'; |
| else if (StringMatch(head, "}")) |
| code = '}'; |
| else if (StringMatch(head, "") || StringMatch(head, ".")) |
| code = '.'; |
| } |
| if (code == 0) |
| errorcall(expr, _("invalid group delimiter")); |
| return code; |
| } |
| |
| static BBOX RenderDelimiter(int delim, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| double savecex = gc->cex; |
| gc->cex = DelimSymbolMag * gc->cex; |
| bbox = RenderSymbolChar(delim, draw, mc, gc, dd); |
| gc->cex = savecex; |
| return bbox; |
| } |
| |
| static int GroupAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "group"); |
| } |
| |
| static BBOX RenderGroup(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| double cexSaved = gc->cex; |
| BBOX bbox; |
| int code; |
| if (length(expr) != 4) |
| errorcall(expr, _("invalid group specification")); |
| bbox = NullBBox(); |
| code = DelimCode(expr, CADR(expr)); |
| gc->cex = DelimSymbolMag * gc->cex; |
| if (code != '.') |
| bbox = RenderSymbolChar(code, draw, mc, gc, dd); |
| gc->cex = cexSaved; |
| bbox = CombineBBoxes(bbox, RenderElement(CADDR(expr), draw, mc, gc, dd)); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| code = DelimCode(expr, CADDDR(expr)); |
| gc->cex = DelimSymbolMag * gc->cex; |
| if (code != '.') |
| bbox = CombineBBoxes(bbox, RenderSymbolChar(code, draw, mc, gc, dd)); |
| gc->cex = cexSaved; |
| return bbox; |
| } |
| |
| static int BGroupAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "bgroup"); |
| } |
| |
| static BBOX RenderDelim(int which, double dist, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| FontType prev = SetFont(SymbolFont, gc); |
| BBOX ansBBox, topBBox, botBBox, extBBox, midBBox; |
| int top, bot, ext, mid; |
| int i, n; |
| double topShift, botShift, extShift, midShift; |
| double ytop, ybot, extHeight, delta; |
| double axisHeight = TeX(sigma22, gc, dd); |
| |
| switch(which) { |
| case '.': |
| SetFont(prev, gc); |
| return NullBBox(); |
| break; |
| case '|': |
| top = 239; ext = 239; bot = 239; mid = 0; |
| break; |
| case '(': |
| top = 230; ext = 231; bot = 232; mid = 0; |
| break; |
| case ')': |
| top = 246; ext = 247; bot = 248; mid = 0; |
| break; |
| case '[': |
| top = 233; ext = 234; bot = 235; mid = 0; |
| break; |
| case ']': |
| top = 249; ext = 250; bot = 251; mid = 0; |
| break; |
| case '{': |
| top = 236; ext = 239; bot = 238; mid = 237; |
| break; |
| case '}': |
| top = 252; ext = 239; bot = 254; mid = 253; |
| break; |
| default: |
| error(_("group is incomplete")); |
| return NullBBox();/*never reached*/ |
| } |
| topBBox = GlyphBBox(top, gc, dd); |
| extBBox = GlyphBBox(ext, gc, dd); |
| botBBox = GlyphBBox(bot, gc, dd); |
| if (which == '{' || which == '}') { |
| if (1.2 * (bboxHeight(topBBox) + bboxDepth(topBBox)) > dist) |
| dist = 1.2 * (bboxHeight(topBBox) + bboxDepth(botBBox)); |
| } |
| else { |
| if (0.8 * (bboxHeight(topBBox) + bboxDepth(topBBox)) > dist) |
| dist = 0.8 * (bboxHeight(topBBox) + bboxDepth(topBBox)); |
| } |
| extHeight = bboxHeight(extBBox) + bboxDepth(extBBox); |
| topShift = dist - bboxHeight(topBBox) + axisHeight; |
| botShift = dist - bboxDepth(botBBox) - axisHeight; |
| extShift = 0.5 * (bboxHeight(extBBox) - bboxDepth(extBBox)); |
| topBBox = ShiftBBox(topBBox, topShift); |
| botBBox = ShiftBBox(botBBox, -botShift); |
| ansBBox = CombineAlignedBBoxes(topBBox, botBBox); |
| if (which == '{' || which == '}') { |
| midBBox = GlyphBBox(mid, gc, dd); |
| midShift = axisHeight |
| - 0.5 * (bboxHeight(midBBox) - bboxDepth(midBBox)); |
| midBBox = ShiftBBox(midBBox, midShift); |
| ansBBox = CombineAlignedBBoxes(ansBBox, midBBox); |
| if (draw) { |
| PMoveTo(savedX, savedY + topShift, mc); |
| RenderSymbolChar(top, draw, mc, gc, dd); |
| PMoveTo(savedX, savedY + midShift, mc); |
| RenderSymbolChar(mid, draw, mc, gc, dd); |
| PMoveTo(savedX, savedY - botShift, mc); |
| RenderSymbolChar(bot, draw, mc, gc, dd); |
| PMoveTo(savedX + bboxWidth(ansBBox), savedY, mc); |
| } |
| } |
| else { |
| if (draw) { |
| /* draw the top and bottom elements */ |
| PMoveTo(savedX, savedY + topShift, mc); |
| RenderSymbolChar(top, draw, mc, gc, dd); |
| PMoveTo(savedX, savedY - botShift, mc); |
| RenderSymbolChar(bot, draw, mc, gc, dd); |
| /* now join with extenders */ |
| ytop = axisHeight + dist |
| - (bboxHeight(topBBox) + bboxDepth(topBBox)); |
| ybot = axisHeight - dist |
| + (bboxHeight(botBBox) + bboxDepth(botBBox)); |
| n = (int) ceil((ytop - ybot) / (0.99 * extHeight)); |
| if (n > 0) { |
| delta = (ytop - ybot) / n; |
| for (i = 0; i < n; i++) { |
| PMoveTo(savedX, savedY + ybot + |
| (i + 0.5) * delta - extShift, mc); |
| RenderSymbolChar(ext, draw, mc, gc, dd); |
| } |
| } |
| PMoveTo(savedX + bboxWidth(ansBBox), savedY, mc); |
| |
| } |
| } |
| SetFont(prev, gc); |
| return ansBBox; |
| } |
| |
| static BBOX RenderBGroup(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| double dist; |
| BBOX bbox; |
| double axisHeight = TeX(sigma22, gc, dd); |
| double extra = 0.2 * xHeight(gc, dd); |
| int delim1, delim2; |
| if (length(expr) != 4) |
| errorcall(expr, _("invalid group specification")); |
| bbox = NullBBox(); |
| delim1 = DelimCode(expr, CADR(expr)); |
| delim2 = DelimCode(expr, CADDDR(expr)); |
| bbox = RenderElement(CADDR(expr), 0, mc, gc, dd); |
| dist = max(bboxHeight(bbox) - axisHeight, bboxDepth(bbox) + axisHeight); |
| bbox = RenderDelim(delim1, dist + extra, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderElement(CADDR(expr), draw, mc, gc, dd)); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderDelim(delim2, dist + extra, draw, mc, |
| gc, dd)); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Parenthetic Expressions (i.e. ( ... )) |
| * |
| */ |
| |
| static int ParenAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "("); |
| } |
| |
| static BBOX RenderParen(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| bbox = RenderDelimiter(S_PARENLEFT, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderElement(CADR(expr), draw, mc, gc, dd)); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| return CombineBBoxes(bbox, RenderDelimiter(S_PARENRIGHT, draw, mc, gc, dd)); |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Integral Operators. |
| * |
| */ |
| |
| static int IntAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "integral"); |
| } |
| |
| |
| static BBOX RenderIntSymbol(int draw, mathContext *mc, pGEcontext gc, |
| pGEDevDesc dd) |
| { |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| if (GetStyle(mc) > STYLE_T) { |
| BBOX bbox1 = RenderSymbolChar(243, 0, mc, gc, dd); |
| BBOX bbox2 = RenderSymbolChar(245, 0, mc, gc, dd); |
| double shift; |
| shift = TeX(sigma22, gc, dd) + 0.99 * bboxDepth(bbox1); |
| PMoveUp(shift, mc); |
| bbox1 = ShiftBBox(RenderSymbolChar(243, draw, mc, gc, dd), shift); |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| shift = TeX(sigma22, gc, dd) - 0.99 * bboxHeight(bbox2); |
| PMoveUp(shift, mc); |
| bbox2 = ShiftBBox(RenderSymbolChar(245, draw, mc, gc, dd), shift); |
| if (draw) |
| PMoveTo(savedX + max(bboxWidth(bbox1), bboxWidth(bbox2)), |
| savedY, mc); |
| else |
| PMoveTo(savedX, savedY, mc); |
| return CombineAlignedBBoxes(bbox1, bbox2); |
| } |
| else { |
| return RenderSymbolChar(0362, draw, mc, gc, dd); |
| } |
| } |
| |
| static BBOX RenderInt(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX opBBox, lowerBBox, upperBBox, bodyBBox; |
| int nexpr = length(expr); |
| STYLE style = GetStyle(mc); |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| double hshift, vshift, width; |
| |
| opBBox = RenderIntSymbol(draw, mc, gc, dd); |
| width = bboxWidth(opBBox); |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| if (nexpr > 2) { |
| hshift = 0.5 * width + ThinSpace(gc, dd); |
| SetSubStyle(style, mc, gc); |
| lowerBBox = RenderElement(CADDR(expr), 0, mc, gc, dd); |
| vshift = bboxDepth(opBBox) + CenterShift(lowerBBox); |
| lowerBBox = RenderOffsetElement(CADDR(expr), hshift, -vshift, draw, |
| mc, gc, dd); |
| opBBox = CombineAlignedBBoxes(opBBox, lowerBBox); |
| SetStyle(style, mc, gc); |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| } |
| if (nexpr > 3) { |
| hshift = width + ThinSpace(gc, dd); |
| SetSupStyle(style, mc, gc); |
| upperBBox = RenderElement(CADDDR(expr), 0, mc, gc, dd); |
| vshift = bboxHeight(opBBox) - CenterShift(upperBBox); |
| upperBBox = RenderOffsetElement(CADDDR(expr), hshift, vshift, draw, |
| mc, gc, dd); |
| opBBox = CombineAlignedBBoxes(opBBox, upperBBox); |
| SetStyle(style, mc, gc); |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| } |
| PMoveAcross(bboxWidth(opBBox), mc); |
| if (nexpr > 1) { |
| bodyBBox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| opBBox = CombineBBoxes(opBBox, bodyBBox); |
| } |
| return opBBox; |
| } |
| |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Operator Expressions (sum, product, lim, inf, sup, ...) |
| * |
| */ |
| |
| #define OperatorSymbolMag 1.25 |
| |
| static SymTab OpTable[] = { |
| { "prod", S_PRODUCT }, |
| { "sum", S_SUM }, |
| { "union", S_UNION }, |
| { "intersect", S_INTERSECTION }, |
| { "lim", N_LIM }, |
| { "liminf", N_LIMINF }, |
| { "limsup", N_LIMINF }, |
| { "inf", N_INF }, |
| { "sup", N_SUP }, |
| { "min", N_MIN }, |
| { "max", N_MAX }, |
| { NULL, 0 } |
| }; |
| |
| static int OpAtom(SEXP expr) |
| { |
| int i; |
| for (i = 0; OpTable[i].code; i++) |
| if (NameMatch(expr, OpTable[i].name)) |
| return OpTable[i].code; |
| return 0; |
| } |
| |
| static BBOX RenderOpSymbol(SEXP op, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| double cexSaved = gc->cex; |
| /*double savedX = mc->CurrentX;*/ |
| /*double savedY = mc->CurrentY;*/ |
| double shift; |
| int display = (GetStyle(mc) > STYLE_T); |
| int opId = OpAtom(op); |
| |
| if (opId == S_SUM || opId == S_PRODUCT || |
| opId == S_UNION || opId == S_INTERSECTION) { |
| if (display) { |
| gc->cex = OperatorSymbolMag * gc->cex; |
| bbox = RenderSymbolChar(OpAtom(op), 0, mc, gc, dd); |
| shift = 0.5 * (bboxHeight(bbox) - bboxDepth(bbox)) - |
| TeX(sigma22, gc, dd); |
| if (draw) { |
| PMoveUp(-shift, mc); |
| bbox = RenderSymbolChar(opId, 1, mc, gc, dd); |
| PMoveUp(shift, mc); |
| } |
| gc->cex = cexSaved; |
| return ShiftBBox(bbox, -shift); |
| } |
| else return RenderSymbolChar(opId, draw, mc, gc, dd); |
| } |
| else { |
| FontType prevfont = SetFont(PlainFont, gc); |
| bbox = RenderStr(CHAR(PRINTNAME(op)), draw, mc, gc, dd); |
| SetFont(prevfont, gc); |
| return bbox; |
| } |
| } |
| |
| static BBOX RenderOp(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX lowerBBox = NullBBox() /* -Wall */, upperBBox = NullBBox(), bodyBBox; |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| int nexpr = length(expr); |
| STYLE style = GetStyle(mc); |
| BBOX opBBox = RenderOpSymbol(CAR(expr), 0, mc, gc, dd); |
| double width = bboxWidth(opBBox); |
| double hshift, lvshift, uvshift; |
| lvshift = uvshift = 0; /* -Wall */ |
| if (nexpr > 2) { |
| SetSubStyle(style, mc, gc); |
| lowerBBox = RenderElement(CADDR(expr), 0, mc, gc, dd); |
| SetStyle(style, mc, gc); |
| width = max(width, bboxWidth(lowerBBox)); |
| lvshift = max(TeX(xi10, gc, dd), TeX(xi12, gc, dd) - |
| bboxHeight(lowerBBox)); |
| lvshift = bboxDepth(opBBox) + bboxHeight(lowerBBox) + lvshift; |
| } |
| if (nexpr > 3) { |
| SetSupStyle(style, mc, gc); |
| upperBBox = RenderElement(CADDDR(expr), 0, mc, gc, dd); |
| SetStyle(style, mc, gc); |
| width = max(width, bboxWidth(upperBBox)); |
| uvshift = max(TeX(xi9, gc, dd), TeX(xi11, gc, dd) - |
| bboxDepth(upperBBox)); |
| uvshift = bboxHeight(opBBox) + bboxDepth(upperBBox) + uvshift; |
| } |
| hshift = 0.5 * (width - bboxWidth(opBBox)); |
| opBBox = RenderGap(hshift, draw, mc, gc, dd); |
| opBBox = CombineBBoxes(opBBox, |
| RenderOpSymbol(CAR(expr), draw, mc, gc, dd)); |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| if (nexpr > 2) { |
| SetSubStyle(style, mc, gc); |
| hshift = 0.5 * (width - bboxWidth(lowerBBox)); |
| lowerBBox = RenderOffsetElement(CADDR(expr), hshift, -lvshift, draw, |
| mc, gc, dd); |
| SetStyle(style, mc, gc); |
| opBBox = CombineAlignedBBoxes(opBBox, lowerBBox); |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| } |
| if (nexpr > 3) { |
| SetSupStyle(style, mc, gc); |
| hshift = 0.5 * (width - bboxWidth(upperBBox)); |
| upperBBox = RenderOffsetElement(CADDDR(expr), hshift, uvshift, draw, |
| mc, gc, dd); |
| SetStyle(style, mc, gc); |
| opBBox = CombineAlignedBBoxes(opBBox, upperBBox); |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| } |
| opBBox = EnlargeBBox(opBBox, TeX(xi13, gc, dd), TeX(xi13, gc, dd), 0); |
| if (draw) |
| PMoveAcross(width, mc); |
| opBBox = CombineBBoxes(opBBox, |
| RenderGap(ThinSpace(gc, dd), draw, mc, gc, dd)); |
| bodyBBox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| return CombineBBoxes(opBBox, bodyBBox); |
| } |
| |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for radical expressions (root, sqrt) |
| * |
| * Tunable parameteters : |
| * |
| * RADICAL_GAP The gap between the nucleus and the radical extension. |
| * RADICAL_SPACE Extra space to the left and right of the nucleus. |
| * |
| */ |
| |
| #define RADICAL_GAP 0.4 |
| #define RADICAL_SPACE 0.2 |
| |
| static int RadicalAtom(SEXP expr) |
| { |
| return NameAtom(expr) && |
| (NameMatch(expr, "root") || |
| NameMatch(expr, "sqrt")); |
| } |
| |
| static BBOX RenderScript(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| STYLE style = GetStyle(mc); |
| SetSupStyle(style, mc, gc); |
| bbox = RenderElement(expr, draw, mc, gc, dd); |
| SetStyle(style, mc, gc); |
| return bbox; |
| } |
| |
| static BBOX RenderRadical(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| SEXP body = CADR(expr); |
| SEXP order = CADDR(expr); |
| BBOX bodyBBox, orderBBox; |
| double radWidth, radHeight; |
| double leadWidth, leadHeight, twiddleHeight; |
| double hshift, vshift; |
| double radGap, radSpace, radTrail; |
| STYLE style = GetStyle(mc); |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| double x[5], y[5]; |
| |
| radGap = RADICAL_GAP * xHeight(gc, dd); |
| radSpace = RADICAL_SPACE * xHeight(gc, dd); |
| radTrail = MuSpace(gc, dd); |
| SetPrimeStyle(style, mc, gc); |
| bodyBBox = RenderElement(body, 0, mc, gc, dd); |
| bodyBBox = RenderItalicCorr(bodyBBox, 0, mc, gc, dd); |
| |
| radWidth = 0.6 *XHeight(gc, dd); |
| radHeight = bboxHeight(bodyBBox) + radGap; |
| twiddleHeight = CenterShift(bodyBBox); |
| |
| leadWidth = radWidth; |
| leadHeight = radHeight; |
| if (order != R_NilValue) { |
| SetSupStyle(style, mc, gc); |
| orderBBox = RenderScript(order, 0, mc, gc, dd); |
| leadWidth = max(leadWidth, bboxWidth(orderBBox) + 0.4 * radWidth); |
| hshift = leadWidth - bboxWidth(orderBBox) - 0.4 * radWidth; |
| vshift = leadHeight - bboxHeight(orderBBox); |
| if (vshift - bboxDepth(orderBBox) < twiddleHeight + radGap) |
| vshift = twiddleHeight + bboxDepth(orderBBox) + radGap; |
| if (draw) { |
| PMoveTo(savedX + hshift, savedY + vshift, mc); |
| orderBBox = RenderScript(order, draw, mc, gc, dd); |
| } |
| orderBBox = EnlargeBBox(orderBBox, vshift, 0, hshift); |
| } |
| else |
| orderBBox = NullBBox(); |
| if (draw) { |
| int savedlty = gc->lty; |
| double savedlwd = gc->lwd; |
| PMoveTo(savedX + leadWidth - radWidth, savedY, mc); |
| PMoveUp(0.8 * twiddleHeight, mc); |
| x[0] = ConvertedX(mc, dd); |
| y[0] = ConvertedY(mc, dd); |
| PMoveUp(0.2 * twiddleHeight, mc); |
| PMoveAcross(0.3 * radWidth, mc); |
| x[1] = ConvertedX(mc, dd); |
| y[1] = ConvertedY(mc, dd); |
| PMoveUp(-(twiddleHeight + bboxDepth(bodyBBox)), mc); |
| PMoveAcross(0.3 * radWidth, mc); |
| x[2] = ConvertedX(mc, dd); |
| y[2] = ConvertedY(mc, dd); |
| PMoveUp(bboxDepth(bodyBBox) + bboxHeight(bodyBBox) + radGap, mc); |
| PMoveAcross(0.4 * radWidth, mc); |
| x[3] = ConvertedX(mc, dd); |
| y[3] = ConvertedY(mc, dd); |
| PMoveAcross(radSpace + bboxWidth(bodyBBox) + radTrail, mc); |
| x[4] = ConvertedX(mc, dd); |
| y[4] = ConvertedY(mc, dd); |
| gc->lty = LTY_SOLID; |
| if (gc->lwd > 1) |
| gc->lwd = 1; |
| GEPolyline(5, x, y, gc, dd); |
| PMoveTo(savedX, savedY, mc); |
| gc->lty = savedlty; |
| gc->lwd = savedlwd; |
| } |
| orderBBox = |
| CombineAlignedBBoxes(orderBBox, |
| RenderGap(leadWidth + radSpace, draw, mc, gc, dd)); |
| SetPrimeStyle(style, mc, gc); |
| orderBBox = CombineBBoxes(orderBBox, |
| RenderElement(body, draw, mc, gc, dd)); |
| orderBBox = CombineBBoxes(orderBBox, |
| RenderGap(2 * radTrail, draw, mc, gc, dd)); |
| orderBBox = EnlargeBBox(orderBBox, radGap, 0, 0);/* << fixes PR#1101 */ |
| SetStyle(style, mc, gc); |
| return orderBBox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Absolute Value Expressions (abs) |
| * |
| */ |
| |
| static int AbsAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "abs"); |
| } |
| |
| static BBOX RenderAbs(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox = RenderElement(CADR(expr), 0, mc, gc, dd); |
| double height = bboxHeight(bbox); |
| double depth = bboxDepth(bbox); |
| double x[2], y[2]; |
| |
| bbox= RenderGap(MuSpace(gc, dd), draw, mc, gc, dd); |
| if (draw) { |
| int savedlty = gc->lty; |
| double savedlwd = gc->lwd; |
| PMoveUp(-depth, mc); |
| x[0] = ConvertedX(mc, dd); |
| y[0] = ConvertedY(mc, dd); |
| PMoveUp(depth + height, mc); |
| x[1] = ConvertedX(mc, dd); |
| y[1] = ConvertedY(mc, dd); |
| gc->lty = LTY_SOLID; |
| if (gc->lwd > 1) |
| gc->lwd = 1; |
| GEPolyline(2, x, y, gc, dd); |
| PMoveUp(-height, mc); |
| gc->lty = savedlty; |
| gc->lwd = savedlwd; |
| } |
| bbox = CombineBBoxes(bbox, RenderGap(MuSpace(gc, dd), draw, mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderElement(CADR(expr), draw, mc, gc, dd)); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderGap(MuSpace(gc, dd), draw, mc, gc, dd)); |
| if (draw) { |
| int savedlty = gc->lty; |
| double savedlwd = gc->lwd; |
| PMoveUp(-depth, mc); |
| x[0] = ConvertedX(mc, dd); |
| y[0] = ConvertedY(mc, dd); |
| PMoveUp(depth + height, mc); |
| x[1] = ConvertedX(mc, dd); |
| y[1] = ConvertedY(mc, dd); |
| gc->lty = LTY_SOLID; |
| if (gc->lwd > 1) |
| gc->lwd = 1; |
| GEPolyline(2, x, y, gc, dd); |
| PMoveUp(-height, mc); |
| gc->lty = savedlty; |
| gc->lwd = savedlwd; |
| } |
| bbox = CombineBBoxes(bbox, RenderGap(MuSpace(gc, dd), draw, mc, gc, dd)); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Grouped Expressions (i.e. { ... } ) |
| * |
| */ |
| |
| static int CurlyAtom(SEXP expr) |
| { |
| return NameAtom(expr) && |
| NameMatch(expr, "{"); |
| } |
| |
| static BBOX RenderCurly(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| return RenderElement(CADR(expr), draw, mc, gc, dd); |
| } |
| |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Relation Expressions (i.e. ... ==, !=, ...) |
| * |
| */ |
| |
| /* Binary Relationships */ |
| static |
| SymTab RelTable[] = { |
| { "<", 60 }, /* less */ |
| { "==", 61 }, /* equal */ |
| { ">", 62 }, /* greater */ |
| { "%=~%", 64 }, /* congruent */ |
| { "!=", 185 }, /* not equal */ |
| { "<=", 163 }, /* less or equal */ |
| { ">=", 179 }, /* greater or equal */ |
| { "%==%", 186 }, /* equivalence */ |
| { "%~~%", 187 }, /* approxequal */ |
| { "%prop%", 181 }, /* proportional to */ |
| { "%~%", 126 }, /* distributed as */ |
| |
| { "%<->%", 171 }, /* Arrows */ |
| { "%<-%", 172 }, |
| { "%up%", 173 }, |
| { "%->%", 174 }, |
| { "%down%", 175 }, |
| { "%<=>%", 219 }, |
| { "%<=%", 220 }, |
| { "%dblup%", 221 }, |
| { "%=>%", 222 }, |
| { "%dbldown%", 223 }, |
| |
| { "%supset%", 201 }, /* Sets (TeX Names) */ |
| { "%supseteq%", 202 }, |
| { "%notsubset%", 203 }, |
| { "%subset%", 204 }, |
| { "%subseteq%", 205 }, |
| { "%in%", 206 }, |
| { "%notin%", 207 }, |
| |
| { NULL, 0 }, |
| }; |
| |
| static int RelAtom(SEXP expr) |
| { |
| int i; |
| for (i = 0; RelTable[i].code; i++) |
| if (NameMatch(expr, RelTable[i].name)) |
| return RelTable[i].code; |
| return 0; |
| } |
| |
| static BBOX RenderRel(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| int op = RelAtom(CAR(expr)); |
| int nexpr = length(expr); |
| BBOX bbox; |
| double gap; |
| |
| if(nexpr == 3) { |
| gap = (mc->CurrentStyle > STYLE_S) ? ThickSpace(gc, dd) : 0; |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderSymbolChar(op, draw, mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd)); |
| return |
| CombineBBoxes(bbox, RenderElement(CADDR(expr), draw, mc, gc, dd)); |
| } |
| else error(_("invalid mathematical annotation")); |
| |
| return NullBBox(); /* -Wall */ |
| } |
| |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Boldface Expressions |
| * |
| */ |
| |
| static int BoldAtom(SEXP expr) |
| { |
| return NameAtom(expr) && |
| NameMatch(expr, "bold"); |
| } |
| |
| static BBOX RenderBold(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| FontType prevfont = SetFont(BoldFont, gc); |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| SetFont(prevfont, gc); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Italic Expressions |
| * |
| */ |
| |
| static int ItalicAtom(SEXP expr) |
| { |
| return NameAtom(expr) && |
| (NameMatch(expr, "italic") || NameMatch(expr, "math")); |
| } |
| |
| static BBOX RenderItalic(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| FontType prevfont = SetFont(ItalicFont, gc); |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| SetFont(prevfont, gc); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Plain (i.e. Roman) Expressions |
| * |
| */ |
| |
| static int PlainAtom(SEXP expr) |
| { |
| return NameAtom(expr) && |
| NameMatch(expr, "plain"); |
| } |
| |
| static BBOX RenderPlain(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| int prevfont = SetFont(PlainFont, gc); |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| SetFont(prevfont, gc); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for SymbolFace (i.e. font = 5) Expressions |
| * |
| * This makes the default font an Adobe Symbol Encoded font |
| * (provides access to any character in the Adobe Symbol Font |
| * encoding via strings like "\042" for the universal ["for all"] |
| * symbol, without the need for separate special names for each |
| * of these symbols). |
| * |
| */ |
| |
| static int SymbolFaceAtom(SEXP expr) |
| { |
| return NameAtom(expr) && |
| NameMatch(expr, "symbol"); |
| } |
| |
| static BBOX RenderSymbolFace(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| int prevfont = SetFont(SymbolFont, gc); |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| SetFont(prevfont, gc); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Bold Italic Expressions |
| * |
| */ |
| |
| static int BoldItalicAtom(SEXP expr) |
| { |
| return NameAtom(expr) && |
| (NameMatch(expr, "bolditalic") || NameMatch(expr, "boldmath")); |
| } |
| |
| static BBOX RenderBoldItalic(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| int prevfont = SetFont(BoldItalicFont, gc); |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| SetFont(prevfont, gc); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Styles |
| * |
| */ |
| |
| static int StyleAtom(SEXP expr) |
| { |
| return (NameAtom(expr) && |
| (NameMatch(expr, "displaystyle") || |
| NameMatch(expr, "textstyle") || |
| NameMatch(expr, "scriptstyle") || |
| NameMatch(expr, "scriptscriptstyle"))); |
| } |
| |
| static BBOX RenderStyle(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| STYLE prevstyle = GetStyle(mc); |
| BBOX bbox; |
| if (NameMatch(CAR(expr), "displaystyle")) |
| SetStyle(STYLE_D, mc, gc); |
| else if (NameMatch(CAR(expr), "textstyle")) |
| SetStyle(STYLE_T, mc, gc); |
| else if (NameMatch(CAR(expr), "scriptstyle")) |
| SetStyle(STYLE_S, mc, gc); |
| else if (NameMatch(CAR(expr), "scriptscriptstyle")) |
| SetStyle(STYLE_SS, mc, gc); |
| bbox = RenderElement(CADR(expr), draw, mc, gc, dd); |
| SetStyle(prevstyle, mc, gc); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Phantom Expressions |
| * |
| */ |
| |
| static int PhantomAtom(SEXP expr) |
| { |
| return (NameAtom(expr) && |
| (NameMatch(expr, "phantom") || |
| NameMatch(expr, "vphantom"))); |
| } |
| |
| static BBOX RenderPhantom(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox = RenderElement(CADR(expr), 0, mc, gc, dd); |
| if (NameMatch(CAR(expr), "vphantom")) { |
| bboxWidth(bbox) = 0; |
| bboxItalic(bbox) = 0; |
| } |
| else RenderGap(bboxWidth(bbox), draw, mc, gc, dd); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Concatenate Expressions |
| * |
| */ |
| |
| static int ConcatenateAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "paste"); |
| } |
| |
| static BBOX RenderConcatenate(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox = NullBBox(); |
| int i, n; |
| |
| expr = CDR(expr); |
| n = length(expr); |
| |
| for (i = 0; i < n; i++) { |
| bbox = CombineBBoxes(bbox, RenderElement(CAR(expr), draw, mc, gc, dd)); |
| if (i != n - 1) |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| expr = CDR(expr); |
| } |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Comma-Separated Lists |
| * |
| */ |
| |
| static BBOX RenderCommaList(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox = NullBBox(); |
| double small = 0.4 * ThinSpace(gc, dd); |
| int i, n; |
| n = length(expr); |
| for (i = 0; i < n; i++) { |
| if (NameAtom(CAR(expr)) && NameMatch(CAR(expr), "...")) { |
| if (i > 0) { |
| bbox = CombineBBoxes(bbox, RenderSymbolChar(S_COMMA, draw, |
| mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderSymbolChar(S_SPACE, draw, |
| mc, gc, dd)); |
| } |
| bbox = CombineBBoxes(bbox, RenderSymbolChar(S_ELLIPSIS, draw, |
| mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderGap(small, draw, mc, gc, dd)); |
| } |
| else { |
| if (i > 0) { |
| bbox = CombineBBoxes(bbox, RenderSymbolChar(S_COMMA, draw, |
| mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderSymbolChar(S_SPACE, draw, |
| mc, gc, dd)); |
| } |
| bbox = CombineBBoxes(bbox, RenderElement(CAR(expr), draw, mc, |
| gc, dd)); |
| } |
| expr = CDR(expr); |
| } |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for General Expressions |
| * |
| */ |
| |
| static BBOX RenderExpression(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| BBOX bbox; |
| if (NameAtom(CAR(expr))) |
| bbox = RenderSymbolString(CAR(expr), draw, mc, gc, dd); |
| else |
| bbox = RenderElement(CAR(expr), draw, mc, gc, dd); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderDelimiter(S_PARENLEFT, draw, mc, gc, dd)); |
| bbox = CombineBBoxes(bbox, RenderCommaList(CDR(expr), draw, mc, gc, dd)); |
| bbox = RenderItalicCorr(bbox, draw, mc, gc, dd); |
| bbox = CombineBBoxes(bbox, RenderDelimiter(S_PARENRIGHT, draw, mc, gc, dd)); |
| return bbox; |
| } |
| |
| /*---------------------------------------------------------------------- |
| * |
| * Code for Comma Separated List Expressions |
| * |
| */ |
| |
| static int ListAtom(SEXP expr) |
| { |
| return NameAtom(expr) && NameMatch(expr, "list"); |
| } |
| |
| static BBOX RenderList(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| return RenderCommaList(CDR(expr), draw, mc, gc, dd); |
| } |
| |
| /* Dispatching procedure which determines nature of expression. */ |
| |
| |
| static BBOX RenderFormula(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| SEXP head = CAR(expr); |
| |
| if (SpaceAtom(head)) |
| return RenderSpace(expr, draw, mc, gc, dd); |
| else if (BinAtom(head)) |
| return RenderBin(expr, draw, mc, gc, dd); |
| else if (SuperAtom(head)) |
| return RenderSup(expr, draw, mc, gc, dd); |
| else if (SubAtom(head)) |
| return RenderSub(expr, draw, mc, gc, dd); |
| else if (WideTildeAtom(head)) |
| return RenderWideTilde(expr, draw, mc, gc, dd); |
| else if (WideHatAtom(head)) |
| return RenderWideHat(expr, draw, mc, gc, dd); |
| else if (BarAtom(head)) |
| return RenderBar(expr, draw, mc, gc, dd); |
| else if (AccentAtom(head)) |
| return RenderAccent(expr, draw, mc, gc, dd); |
| else if (OverAtom(head)) |
| return RenderOver(expr, draw, mc, gc, dd); |
| else if (UnderlAtom(head)) |
| return RenderUnderl(expr, draw, mc, gc, dd); |
| else if (AtopAtom(head)) |
| return RenderAtop(expr, draw, mc, gc, dd); |
| else if (ParenAtom(head)) |
| return RenderParen(expr, draw, mc, gc, dd); |
| else if (BGroupAtom(head)) |
| return RenderBGroup(expr, draw, mc, gc, dd); |
| else if (GroupAtom(head)) |
| return RenderGroup(expr, draw, mc, gc, dd); |
| else if (IntAtom(head)) |
| return RenderInt(expr, draw, mc, gc, dd); |
| else if (OpAtom(head)) |
| return RenderOp(expr, draw, mc, gc, dd); |
| else if (RadicalAtom(head)) |
| return RenderRadical(expr, draw, mc, gc, dd); |
| else if (AbsAtom(head)) |
| return RenderAbs(expr, draw, mc, gc, dd); |
| else if (CurlyAtom(head)) |
| return RenderCurly(expr, draw, mc, gc, dd); |
| else if (RelAtom(head)) |
| return RenderRel(expr, draw, mc, gc, dd); |
| else if (BoldAtom(head)) |
| return RenderBold(expr, draw, mc, gc, dd); |
| else if (ItalicAtom(head)) |
| return RenderItalic(expr, draw, mc, gc, dd); |
| else if (PlainAtom(head)) |
| return RenderPlain(expr, draw, mc, gc, dd); |
| else if (SymbolFaceAtom(head)) |
| return RenderSymbolFace(expr, draw, mc, gc, dd); |
| else if (BoldItalicAtom(head)) |
| return RenderBoldItalic(expr, draw, mc, gc, dd); |
| else if (StyleAtom(head)) |
| return RenderStyle(expr, draw, mc, gc, dd); |
| else if (PhantomAtom(head)) |
| return RenderPhantom(expr, draw, mc, gc, dd); |
| else if (ConcatenateAtom(head)) |
| return RenderConcatenate(expr, draw, mc, gc, dd); |
| else if (ListAtom(head)) |
| return RenderList(expr, draw, mc, gc, dd); |
| else |
| return RenderExpression(expr, draw, mc, gc, dd); |
| } |
| |
| |
| /* Dispatch on whether atom (symbol, string, number, ...) */ |
| /* or formula (some sort of expression) */ |
| |
| static BBOX RenderElement(SEXP expr, int draw, mathContext *mc, |
| pGEcontext gc, pGEDevDesc dd) |
| { |
| if (FormulaExpression(expr)) |
| return RenderFormula(expr, draw, mc, gc, dd); |
| else |
| return RenderAtom(expr, draw, mc, gc, dd); |
| } |
| |
| static BBOX RenderOffsetElement(SEXP expr, double x, double y, int draw, |
| mathContext *mc, pGEcontext gc, |
| pGEDevDesc dd) |
| { |
| BBOX bbox; |
| double savedX = mc->CurrentX; |
| double savedY = mc->CurrentY; |
| if (draw) { |
| mc->CurrentX += x; |
| mc->CurrentY += y; |
| } |
| bbox = RenderElement(expr, draw, mc, gc, dd); |
| bboxWidth(bbox) += x; |
| bboxHeight(bbox) += y; |
| bboxDepth(bbox) -= y; |
| mc->CurrentX = savedX; |
| mc->CurrentY = savedY; |
| return bbox; |
| |
| } |
| |
| /* Functions forming the R API */ |
| |
| /* Calculate width of expression */ |
| /* BBOXes are in INCHES (see MetricUnit) */ |
| |
| double GEExpressionWidth(SEXP expr, |
| pGEcontext gc, |
| pGEDevDesc dd) |
| { |
| BBOX bbox; |
| double width; |
| |
| /* |
| * Build a "drawing context" for the current expression |
| */ |
| mathContext mc; |
| mc.BaseCex = gc->cex; |
| mc.BoxColor = 4291543295U; // name2col("pink"); |
| mc.CurrentStyle = STYLE_D; |
| /* |
| * Some "empty" values. Will be filled in after BBox is calc'ed |
| */ |
| mc.ReferenceX = 0; |
| mc.ReferenceY = 0; |
| mc.CurrentX = 0; |
| mc.CurrentY = 0; |
| mc.CurrentAngle = 0; |
| mc.CosAngle = 0; |
| mc.SinAngle = 0; |
| |
| SetFont(PlainFont, gc); |
| bbox = RenderElement(expr, 0, &mc, gc, dd); |
| width = bboxWidth(bbox); |
| /* |
| * NOTE that we do fabs() here in case the device |
| * runs right-to-left. |
| * This is so that these calculations match those |
| * for string widths and heights, where the width |
| * and height of text is positive no matter how |
| * the device drawing is oriented. |
| */ |
| return fabs(toDeviceWidth(width, GE_INCHES, dd)); |
| } |
| |
| double GEExpressionHeight(SEXP expr, |
| pGEcontext gc, |
| pGEDevDesc dd) |
| { |
| BBOX bbox; |
| double height; |
| |
| /* |
| * Build a "drawing context" for the current expression |
| */ |
| mathContext mc; |
| mc.BaseCex = gc->cex; |
| mc.BoxColor = 4291543295U; // name2col("pink"); |
| mc.CurrentStyle = STYLE_D; |
| /* |
| * Some "empty" values. Will be filled in after BBox is calc'ed |
| */ |
| mc.ReferenceX = 0; |
| mc.ReferenceY = 0; |
| mc.CurrentX = 0; |
| mc.CurrentY = 0; |
| mc.CurrentAngle = 0; |
| mc.CosAngle = 0; |
| mc.SinAngle = 0; |
| |
| SetFont(PlainFont, gc); |
| bbox = RenderElement(expr, 0, &mc, gc, dd); |
| height = bboxHeight(bbox) + bboxDepth(bbox); |
| /* NOTE that we do fabs() here in case the device |
| * draws top-to-bottom (like an X11 window). |
| * This is so that these calculations match those |
| * for string widths and heights, where the width |
| * and height of text is positive no matter how |
| * the device drawing is oriented. |
| */ |
| return fabs(toDeviceHeight(height, GE_INCHES, dd)); |
| } |
| |
| void GEExpressionMetric(SEXP expr, |
| const pGEcontext gc, |
| double *ascent, double *descent, double *width, |
| pGEDevDesc dd) |
| { |
| BBOX bbox; |
| |
| /* |
| * Build a "drawing context" for the current expression |
| */ |
| mathContext mc; |
| mc.BaseCex = gc->cex; |
| mc.BoxColor = 4291543295U; // name2col("pink"); |
| mc.CurrentStyle = STYLE_D; |
| /* |
| * Some "empty" values. Will be filled in after BBox is calc'ed |
| */ |
| mc.ReferenceX = 0; |
| mc.ReferenceY = 0; |
| mc.CurrentX = 0; |
| mc.CurrentY = 0; |
| mc.CurrentAngle = 0; |
| mc.CosAngle = 0; |
| mc.SinAngle = 0; |
| |
| SetFont(PlainFont, gc); |
| bbox = RenderElement(expr, 0, &mc, gc, dd); |
| /* NOTE that we do fabs() here in case the device |
| * draws top-to-bottom (like an X11 window). |
| * This is so that these calculations match those |
| * for string widths and heights, where the width |
| * and height of text is positive no matter how |
| * the device drawing is oriented. |
| */ |
| *width = fabs(toDeviceWidth(bboxWidth(bbox), GE_INCHES, dd)); |
| *ascent = fabs(toDeviceHeight(bboxHeight(bbox), GE_INCHES, dd)); |
| *descent = fabs(toDeviceHeight(bboxDepth(bbox), GE_INCHES, dd)); |
| } |
| |
| void GEMathText(double x, double y, SEXP expr, |
| double xc, double yc, double rot, |
| pGEcontext gc, |
| pGEDevDesc dd) |
| { |
| BBOX bbox; |
| mathContext mc; |
| |
| /* If font metric information is not available for device |
| then bail out */ |
| double ascent, descent, width; |
| GEMetricInfo('M', gc, &ascent, &descent, &width, dd); |
| if ((ascent == 0.0) && (descent == 0.0) && (width == 0.0)) |
| error(_("Metric information not available for this family/device")); |
| |
| /* |
| * Build a "drawing context" for the current expression |
| */ |
| mc.BaseCex = gc->cex; |
| mc.BoxColor = 4291543295U; // name2col("pink"); |
| mc.CurrentStyle = STYLE_D; |
| |
| /* |
| * Some "empty" values. Will be filled in after BBox is calc'ed |
| */ |
| mc.ReferenceX = 0; |
| mc.ReferenceY = 0; |
| mc.CurrentX = 0; |
| mc.CurrentY = 0; |
| mc.CurrentAngle = 0; |
| mc.CosAngle = 0; |
| mc.SinAngle = 0; |
| |
| SetFont(PlainFont, gc); |
| bbox = RenderElement(expr, 0, &mc, gc, dd); |
| mc.ReferenceX = fromDeviceX(x, GE_INCHES, dd); |
| mc.ReferenceY = fromDeviceY(y, GE_INCHES, dd); |
| if (R_FINITE(xc)) |
| mc.CurrentX = mc.ReferenceX - xc * bboxWidth(bbox); |
| else |
| /* Paul 2002-02-11 |
| * If xc == NA then should centre horizontally. |
| * Used to left-adjust. |
| */ |
| mc.CurrentX = mc.ReferenceX - 0.5 * bboxWidth(bbox); |
| if (R_FINITE(yc)) |
| mc.CurrentY = mc.ReferenceY + bboxDepth(bbox) |
| - yc * (bboxHeight(bbox) + bboxDepth(bbox)); |
| else |
| /* Paul 11/2/02 |
| * If xc == NA then should centre vertically. |
| * Used to bottom-adjust. |
| */ |
| mc.CurrentY = mc.ReferenceY + bboxDepth(bbox) |
| - 0.5 * (bboxHeight(bbox) + bboxDepth(bbox)); |
| mc.CurrentAngle = rot; |
| rot *= M_PI_2 / 90 ;/* radians */ |
| mc.CosAngle = cos(rot); |
| mc.SinAngle = sin(rot); |
| RenderElement(expr, 1, &mc, gc, dd); |
| }/* GEMathText */ |