SplashOutputDev.h 16.4 KB
Newer Older
Kristian Høgsberg's avatar
Kristian Høgsberg committed
1 2 3 4 5 6 7 8
//========================================================================
//
// SplashOutputDev.h
//
// Copyright 2003 Glyph & Cog, LLC
//
//========================================================================

9 10 11 12
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
13 14 15
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
16
// Copyright (C) 2005 Takashi Iwai <tiwai@suse.de>
17
// Copyright (C) 2009-2016 Thomas Freitag <Thomas.Freitag@alfa.de>
18
// Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
19
// Copyright (C) 2010 Christian Feuersnger <cfeuersaenger@googlemail.com>
20
// Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com>
21
// Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com>
22
// Copyright (C) 2011, 2017 Adrian Johnson <ajohnson@redneon.com>
23
// Copyright (C) 2012, 2015, 2018 Albert Astals Cid <aacid@kde.org>
Albert Astals Cid's avatar
Albert Astals Cid committed
24
// Copyright (C) 2015, 2016 William Bader <williambader@hotmail.com>
Albert Astals Cid's avatar
Albert Astals Cid committed
25
// Copyright (C) 2018 Stefan Brns <stefan.bruens@rwth-aachen.de>
26 27 28 29 30 31
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================

Kristian Høgsberg's avatar
Kristian Høgsberg committed
32 33 34 35
#ifndef SPLASHOUTPUTDEV_H
#define SPLASHOUTPUTDEV_H

#include "splash/SplashTypes.h"
36
#include "splash/SplashPattern.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
37 38
#include "poppler-config.h"
#include "OutputDev.h"
39
#include "GfxState.h"
Thomas Freitag's avatar
Thomas Freitag committed
40
#include "GlobalParams.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
41

42
class PDFDoc;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
43 44 45 46 47 48 49 50 51
class Gfx8BitFont;
class SplashBitmap;
class Splash;
class SplashPath;
class SplashFontEngine;
class SplashFont;
class T3FontCache;
struct T3FontCacheTag;
struct T3GlyphStack;
52
struct SplashTransparencyGroup;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
53

54 55 56 57
//------------------------------------------------------------------------
// Splash dynamic pattern
//------------------------------------------------------------------------

58 59 60 61 62
class SplashFunctionPattern: public SplashPattern {
public:

  SplashFunctionPattern(SplashColorMode colorMode, GfxState *state, GfxFunctionShading *shading);

Albert Astals Cid's avatar
Albert Astals Cid committed
63
  SplashPattern *copy() override { return new SplashFunctionPattern(colorMode, state, (GfxFunctionShading *) shading); }
64

Albert Astals Cid's avatar
Albert Astals Cid committed
65
  ~SplashFunctionPattern();
66

67
  bool testPosition(int x, int y) override { return true; }
68

69
  bool isStatic() override { return false; }
70

71
  bool getColor(int x, int y, SplashColorPtr c) override;
72 73 74

  virtual GfxFunctionShading *getShading() { return shading; }

75
  bool isCMYK() override { return gfxMode == csDeviceCMYK; }
76 77 78 79 80 81 82 83 84 85

protected:
  Matrix ictm;
  double xMin, yMin, xMax, yMax;
  GfxFunctionShading *shading;
  GfxState *state;
  SplashColorMode colorMode;
  GfxColorSpaceMode gfxMode;
};

86
class SplashUnivariatePattern: public SplashPattern {
87 88
public:

89
  SplashUnivariatePattern(SplashColorMode colorMode, GfxState *state, GfxUnivariateShading *shading);
90

Albert Astals Cid's avatar
Albert Astals Cid committed
91
  ~SplashUnivariatePattern();
92

93
  bool getColor(int x, int y, SplashColorPtr c) override;
94

95
  bool testPosition(int x, int y) override;
96

97
  bool isStatic() override { return false; }
98

99
  virtual bool getParameter(double xs, double ys, double *t) = 0;
100 101 102

  virtual GfxUnivariateShading *getShading() { return shading; }

103
  bool isCMYK() override { return gfxMode == csDeviceCMYK; }
104

105
protected:
106
  Matrix ictm;
107 108
  double t0, t1, dt;
  GfxUnivariateShading *shading;
109 110
  GfxState *state;
  SplashColorMode colorMode;
111
  GfxColorSpaceMode gfxMode;
112 113 114 115 116 117 118
};

class SplashAxialPattern: public SplashUnivariatePattern {
public:

  SplashAxialPattern(SplashColorMode colorMode, GfxState *state, GfxAxialShading *shading);

Albert Astals Cid's avatar
Albert Astals Cid committed
119
  SplashPattern *copy() override { return new SplashAxialPattern(colorMode, state, (GfxAxialShading *) shading); }
120

Albert Astals Cid's avatar
Albert Astals Cid committed
121
  ~SplashAxialPattern();
122

123
  bool getParameter(double xs, double ys, double *t) override;
124 125 126 127

private:
  double x0, y0, x1, y1;
  double dx, dy, mul;
128 129 130 131 132 133
};

// see GfxState.h, GfxGouraudTriangleShading
class SplashGouraudPattern: public SplashGouraudColor {
public:

134
  SplashGouraudPattern(bool bDirectColorTranslation, GfxState *state, GfxGouraudTriangleShading *shading);
135

136
  SplashPattern *copy() override { return new SplashGouraudPattern(bDirectColorTranslation, state, shading); }
137

Albert Astals Cid's avatar
Albert Astals Cid committed
138
  ~SplashGouraudPattern();
139

140
  bool getColor(int x, int y, SplashColorPtr c) override { return false; }
141

142
  bool testPosition(int x, int y) override { return false; }
143

144
  bool isStatic() override { return false; }
145

146
  bool isCMYK() override { return gfxMode == csDeviceCMYK; }
147

148
  bool isParameterized() override { return shading->isParameterized(); }
Albert Astals Cid's avatar
Albert Astals Cid committed
149 150
  int getNTriangles() override { return shading->getNTriangles(); }
   void getTriangle(int i, double *x0, double *y0, double *color0,
151
                            double *x1, double *y1, double *color1,
Albert Astals Cid's avatar
Albert Astals Cid committed
152
                            double *x2, double *y2, double *color2) override
153
  { shading->getTriangle(i, x0, y0, color0, x1, y1, color1, x2, y2, color2); }
154

Albert Astals Cid's avatar
Albert Astals Cid committed
155
  void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c) override;
156 157 158 159

private:
  GfxGouraudTriangleShading *shading;
  GfxState *state;
160
  bool bDirectColorTranslation;
161
  GfxColorSpaceMode gfxMode;
162 163
};

164 165 166 167 168 169
// see GfxState.h, GfxRadialShading
class SplashRadialPattern: public SplashUnivariatePattern {
public:

  SplashRadialPattern(SplashColorMode colorMode, GfxState *state, GfxRadialShading *shading);

Albert Astals Cid's avatar
Albert Astals Cid committed
170
  SplashPattern *copy() override { return new SplashRadialPattern(colorMode, state, (GfxRadialShading *) shading); }
171

Albert Astals Cid's avatar
Albert Astals Cid committed
172
  ~SplashRadialPattern();
173

174
  bool getParameter(double xs, double ys, double *t) override;
175 176 177 178 179 180

private:
  double x0, y0, r0, dx, dy, dr;
  double a, inva;
};

Kristian Høgsberg's avatar
Kristian Høgsberg committed
181 182 183 184 185 186 187 188 189 190 191 192 193
//------------------------------------------------------------------------

// number of Type 3 fonts to cache
#define splashOutT3FontCacheSize 8

//------------------------------------------------------------------------
// SplashOutputDev
//------------------------------------------------------------------------

class SplashOutputDev: public OutputDev {
public:

  // Constructor.
194
  SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,
195 196
		  bool reverseVideoA, SplashColorPtr paperColorA,
		  bool bitmapTopDownA = true,
Thomas Freitag's avatar
Thomas Freitag committed
197
		  SplashThinLineMode thinLineMode = splashThinLineDefault,
198
		  bool overprintPreviewA = globalParams->getOverprintPreview());
Kristian Høgsberg's avatar
Kristian Høgsberg committed
199 200

  // Destructor.
Albert Astals Cid's avatar
Albert Astals Cid committed
201
  ~SplashOutputDev();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
202 203 204

  //----- get info about output device

205 206 207
  // Does this device use tilingPatternFill()?  If this returns false,
  // tiling pattern fills will be reduced to a series of other drawing
  // operations.
208
  bool useTilingPatternFill() override { return true; }
209

210 211 212
  // Does this device use functionShadedFill(), axialShadedFill(), and
  // radialShadedFill()?  If this returns false, these shaded fills
  // will be reduced to a series of other drawing operations.
213 214
  bool useShadedFills(int type) override
  { return (type >= 1 && type <= 5) ? true : false; }
215

Kristian Høgsberg's avatar
Kristian Høgsberg committed
216 217
  // Does this device use upside-down coordinates?
  // (Upside-down means (0,0) is the top left corner of the page.)
218
  bool upsideDown() override { return bitmapTopDown ^ bitmapUpsideDown; }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
219 220

  // Does this device use drawChar() or drawString()?
221
  bool useDrawChar() override { return true; }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
222 223 224

  // Does this device use beginType3Char/endType3Char?  Otherwise,
  // text in Type 3 fonts will be drawn with drawChar/drawString.
225
  bool interpretType3Chars() override { return true; }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
226 227 228 229

  //----- initialization and control

  // Start a page.
Albert Astals Cid's avatar
Albert Astals Cid committed
230
  void startPage(int pageNum, GfxState *state, XRef *xref) override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
231 232

  // End a page.
Albert Astals Cid's avatar
Albert Astals Cid committed
233
  void endPage() override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
234 235

  //----- save/restore graphics state
Albert Astals Cid's avatar
Albert Astals Cid committed
236 237
  void saveState(GfxState *state) override;
  void restoreState(GfxState *state) override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
238 239

  //----- update graphics state
Albert Astals Cid's avatar
Albert Astals Cid committed
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
  void updateAll(GfxState *state) override;
  void updateCTM(GfxState *state, double m11, double m12,
			 double m21, double m22, double m31, double m32) override;
  void updateLineDash(GfxState *state) override;
  void updateFlatness(GfxState *state) override;
  void updateLineJoin(GfxState *state) override;
  void updateLineCap(GfxState *state) override;
  void updateMiterLimit(GfxState *state) override;
  void updateLineWidth(GfxState *state) override;
  void updateStrokeAdjust(GfxState *state) override;
  void updateFillColorSpace(GfxState *state) override;
  void updateStrokeColorSpace(GfxState *state) override;
  void updateFillColor(GfxState *state) override;
  void updateStrokeColor(GfxState *state) override;
  void updateBlendMode(GfxState *state) override;
  void updateFillOpacity(GfxState *state) override;
  void updateStrokeOpacity(GfxState *state) override;
  void updatePatternOpacity(GfxState *state) override;
  void clearPatternOpacity(GfxState *state) override;
  void updateFillOverprint(GfxState *state) override;
  void updateStrokeOverprint(GfxState *state) override;
  void updateOverprintMode(GfxState *state) override;
  void updateTransfer(GfxState *state) override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
263 264

  //----- update text state
Albert Astals Cid's avatar
Albert Astals Cid committed
265
  void updateFont(GfxState *state) override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
266 267

  //----- path painting
Albert Astals Cid's avatar
Albert Astals Cid committed
268 269 270
  void stroke(GfxState *state) override;
  void fill(GfxState *state) override;
  void eoFill(GfxState *state) override;
271
  bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *catalog, Object *str,
Albert Astals Cid's avatar
Albert Astals Cid committed
272 273
				  const double *pmat, int paintType, int tilingType, Dict *resDict,
				  const double *mat, const double *bbox,
274
				  int x0, int y0, int x1, int y1,
Albert Astals Cid's avatar
Albert Astals Cid committed
275
				  double xStep, double yStep) override;
276 277 278 279
  bool functionShadedFill(GfxState *state, GfxFunctionShading *shading) override;
  bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override;
  bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) override;
  bool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
280 281

  //----- path clipping
Albert Astals Cid's avatar
Albert Astals Cid committed
282 283 284
  void clip(GfxState *state) override;
  void eoClip(GfxState *state) override;
  void clipToStrokePath(GfxState *state) override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
285 286

  //----- text drawing
Albert Astals Cid's avatar
Albert Astals Cid committed
287
  void drawChar(GfxState *state, double x, double y,
Kristian Høgsberg's avatar
Kristian Høgsberg committed
288 289
			double dx, double dy,
			double originX, double originY,
Albert Astals Cid's avatar
Albert Astals Cid committed
290
			CharCode code, int nBytes, Unicode *u, int uLen) override;
291
  bool beginType3Char(GfxState *state, double x, double y,
Kristian Høgsberg's avatar
Kristian Høgsberg committed
292
			       double dx, double dy,
Albert Astals Cid's avatar
Albert Astals Cid committed
293 294 295 296
			       CharCode code, Unicode *u, int uLen) override;
  void endType3Char(GfxState *state) override;
  void beginTextObject(GfxState *state) override;
  void endTextObject(GfxState *state) override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
297 298

  //----- image drawing
Albert Astals Cid's avatar
Albert Astals Cid committed
299
  void drawImageMask(GfxState *state, Object *ref, Stream *str,
300 301
			     int width, int height, bool invert,
			     bool interpolate, bool inlineImg) override;
Albert Astals Cid's avatar
Albert Astals Cid committed
302
  void setSoftMaskFromImageMask(GfxState *state,
303
					Object *ref, Stream *str,
304 305
					int width, int height, bool invert,
					bool inlineImg, double *baseMatrix) override;
Albert Astals Cid's avatar
Albert Astals Cid committed
306 307
  void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) override;
  void drawImage(GfxState *state, Object *ref, Stream *str,
Kristian Høgsberg's avatar
Kristian Høgsberg committed
308
			 int width, int height, GfxImageColorMap *colorMap,
309
			 bool interpolate, int *maskColors, bool inlineImg) override;
Albert Astals Cid's avatar
Albert Astals Cid committed
310
  void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
311 312
			       int width, int height,
			       GfxImageColorMap *colorMap,
313
			       bool interpolate,
314
			       Stream *maskStr, int maskWidth, int maskHeight,
315
			       bool maskInvert, bool maskInterpolate) override;
Albert Astals Cid's avatar
Albert Astals Cid committed
316
  void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
317 318
				   int width, int height,
				   GfxImageColorMap *colorMap,
319
				   bool interpolate,
320 321
				   Stream *maskStr,
				   int maskWidth, int maskHeight,
322
				   GfxImageColorMap *maskColorMap,
323
				   bool maskInterpolate) override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
324 325

  //----- Type 3 font operators
Albert Astals Cid's avatar
Albert Astals Cid committed
326 327 328
  void type3D0(GfxState *state, double wx, double wy) override;
  void type3D1(GfxState *state, double wx, double wy,
		       double llx, double lly, double urx, double ury) override;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
329

330
  //----- transparency groups and soft masks
331
  bool checkTransparencyGroup(GfxState *state, bool knockout) override;
Albert Astals Cid's avatar
Albert Astals Cid committed
332
  void beginTransparencyGroup(GfxState *state, const double *bbox,
333
				      GfxColorSpace *blendingColorSpace,
334 335
				      bool isolated, bool knockout,
				      bool forSoftMask) override;
Albert Astals Cid's avatar
Albert Astals Cid committed
336
  void endTransparencyGroup(GfxState *state) override;
Albert Astals Cid's avatar
Albert Astals Cid committed
337
  void paintTransparencyGroup(GfxState *state, const double *bbox) override;
338
  void setSoftMask(GfxState *state, const double *bbox, bool alpha,
Albert Astals Cid's avatar
Albert Astals Cid committed
339 340
			   Function *transferFunc, GfxColor *backdropColor) override;
  void clearSoftMask(GfxState *state) override;
341

Kristian Høgsberg's avatar
Kristian Høgsberg committed
342 343 344
  //----- special access

  // Called to indicate that a new PDF document has been loaded.
345
  void startDoc(PDFDoc *docA);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
346
 
347 348
  void setPaperColor(SplashColorPtr paperColorA);

349 350
  bool isReverseVideo() { return reverseVideo; }
  void setReverseVideo(bool reverseVideoA) { reverseVideo = reverseVideoA; }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
351 352 353 354 355 356

  // Get the bitmap and its size.
  SplashBitmap *getBitmap() { return bitmap; }
  int getBitmapWidth();
  int getBitmapHeight();

357 358 359 360
  // Returns the last rasterized bitmap, transferring ownership to the
  // caller.
  SplashBitmap *takeBitmap();

361 362
  // Set this flag to true to generate an upside-down bitmap (useful
  // for Windows BMP files).
363
  void setBitmapUpsideDown(bool f) { bitmapUpsideDown = f; }
364

Kristian Høgsberg's avatar
Kristian Høgsberg committed
365 366 367
  // Get the Splash object.
  Splash *getSplash() { return splash; }

368 369 370 371 372
  // Get the modified region.
  void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax);

  // Clear the modified region.
  void clearModRegion();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
373

374
  SplashFont *getCurrentFont() { return font; }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
375

376 377
  // If <skipTextA> is true, don't draw horizontal text.
  // If <skipRotatedTextA> is true, don't draw rotated (non-horizontal) text.
378
  void setSkipText(bool skipHorizTextA, bool skipRotatedTextA)
379 380 381 382
    { skipHorizText = skipHorizTextA; skipRotatedText = skipRotatedTextA; }

  int getNestCount() { return nestCount; }

383
#if 1 //~tmp: turn off anti-aliasing temporarily
384 385
  bool getVectorAntialias() override;
  void setVectorAntialias(bool vaa) override;
386 387
#endif

388 389
  bool getFontAntialias() { return fontAntialias; }
  void setFontAntialias(bool anti) { fontAntialias = anti; }
390

391
  void setFreeTypeHinting(bool enable, bool enableSlightHinting);
392

393 394 395
protected:
  void doUpdateFont(GfxState *state);

Kristian Høgsberg's avatar
Kristian Høgsberg committed
396
private:
397
  bool univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
398

399
  void setupScreenParams(double hDPI, double vDPI);
400 401
  SplashPattern *getColor(GfxGray gray);
  SplashPattern *getColor(GfxRGB *rgb);
Adrian Johnson's avatar
Adrian Johnson committed
402
#ifdef SPLASH_CMYK
403
  SplashPattern *getColor(GfxCMYK *cmyk);
404
  SplashPattern *getColor(GfxColor *deviceN);
405
#endif
Albert Astals Cid's avatar
Albert Astals Cid committed
406
  static void getMatteColor( SplashColorMode colorMode, GfxImageColorMap *colorMap, const GfxColor * matteColor, SplashColor splashMatteColor);
407 408
  void setOverprintMask(GfxColorSpace *colorSpace, bool overprintFlag,
			int overprintMode, const GfxColor *singleColor, bool grayIndexed = false);
409
  SplashPath convertPath(GfxState *state, GfxPath *path,
410
			  bool dropEmptySubpaths);
411
  void drawType3Glyph(GfxState *state, T3FontCache *t3Font,
412
		      T3FontCacheTag *tag, unsigned char *data);
413
#ifdef USE_CMS
414
  bool useIccImageSrc(void *data);
415
  static void iccTransform(void *data, SplashBitmap *bitmap);
416
  static bool iccImageSrc(void *data, SplashColorPtr colorLine,
417
			unsigned char *alphaLine);
418
#endif
419 420
  static bool imageMaskSrc(void *data, SplashColorPtr line);
  static bool imageSrc(void *data, SplashColorPtr colorLine,
421
			unsigned char *alphaLine);
422
  static bool alphaImageSrc(void *data, SplashColorPtr line,
423
			     unsigned char *alphaLine);
424
  static bool maskedImageSrc(void *data, SplashColorPtr line,
425
			      unsigned char *alphaLine);
426
  static bool tilingBitmapSrc(void *data, SplashColorPtr line,
427
			     unsigned char *alphaLine);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
428

429
  bool keepAlphaChannel;	// don't fill with paper color, keep alpha channel
430

Kristian Høgsberg's avatar
Kristian Høgsberg committed
431
  SplashColorMode colorMode;
432
  int bitmapRowPad;
433 434 435 436 437 438 439 440
  bool bitmapTopDown;
  bool bitmapUpsideDown;
  bool fontAntialias;
  bool vectorAntialias;
  bool overprintPreview;
  bool enableFreeTypeHinting;
  bool enableSlightHinting;
  bool reverseVideo;		// reverse video mode
Kristian Høgsberg's avatar
Kristian Høgsberg committed
441
  SplashColor paperColor;	// paper color
442
  SplashScreenParams screenParams;
443 444
  bool skipHorizText;
  bool skipRotatedText;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
445

446
  PDFDoc *doc;			// the current document
Thomas Freitag's avatar
Thomas Freitag committed
447
  XRef *xref;       // the xref of the current document
Kristian Høgsberg's avatar
Kristian Høgsberg committed
448 449 450 451 452 453 454 455 456 457 458

  SplashBitmap *bitmap;
  Splash *splash;
  SplashFontEngine *fontEngine;

  T3FontCache *			// Type 3 font cache
    t3FontCache[splashOutT3FontCacheSize];
  int nT3Fonts;			// number of valid entries in t3FontCache
  T3GlyphStack *t3GlyphStack;	// Type 3 glyph context stack

  SplashFont *font;		// current font
459
  bool needFontUpdate;		// set when the font needs to be updated
Kristian Høgsberg's avatar
Kristian Høgsberg committed
460
  SplashPath *textClipPath;	// clipping path built with text object
461 462 463

  SplashTransparencyGroup *	// transparency group stack
    transpGroupStack;
464
  int nestCount;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
465 466 467
};

#endif