1 //========================================================================
2 //
3 // CairoOutputDev.h
4 //
5 // Copyright 2003 Glyph & Cog, LLC
6 // Copyright 2004 Red Hat, INC
7 //
8 //========================================================================
9 
10 //========================================================================
11 //
12 // Modified under the Poppler project - http://poppler.freedesktop.org
13 //
14 // All changes made under the Poppler project to this file are licensed
15 // under GPL version 2 or later
16 //
17 // Copyright (C) 2005-2008 Jeff Muizelaar <jeff@infidigm.net>
18 // Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com>
19 // Copyright (C) 2005 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
20 // Copyright (C) 2006-2011, 2013 Carlos Garcia Campos <carlosgc@gnome.org>
21 // Copyright (C) 2008, 2009, 2011-2017 Adrian Johnson <ajohnson@redneon.com>
22 // Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
23 // Copyright (C) 2010-2013 Thomas Freitag <Thomas.Freitag@alfa.de>
24 // Copyright (C) 2015 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
25 // Copyright (C) 2016 Jason Crain <jason@aquaticape.us>
26 // Copyright (C) 2018, 2019, 2021 Albert Astals Cid <aacid@kde.org>
27 // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
28 // Copyright (C) 2020 Michal <sudolskym@gmail.com>
29 // Copyright (C) 2021 Christian Persch <chpe@src.gnome.org>
30 //
31 // To see a description of the changes please see the Changelog file that
32 // came with your tarball or type make ChangeLog if you are building from git
33 //
34 //========================================================================
35 
36 #ifndef CAIROOUTPUTDEV_H
37 #define CAIROOUTPUTDEV_H
38 
39 #include <cairo-ft.h>
40 #include "OutputDev.h"
41 #include "TextOutputDev.h"
42 #include "GfxState.h"
43 
44 class PDFDoc;
45 class GfxState;
46 class GfxPath;
47 class Gfx8BitFont;
48 struct GfxRGB;
49 class CairoFontEngine;
50 class CairoFont;
51 
52 //------------------------------------------------------------------------
53 
54 //------------------------------------------------------------------------
55 // CairoImage
56 //------------------------------------------------------------------------
57 class CairoImage
58 {
59 public:
60     // Constructor.
61     CairoImage(double x1, double y1, double x2, double y2);
62 
63     // Destructor.
64     ~CairoImage();
65 
66     CairoImage(const CairoImage &) = delete;
67     CairoImage &operator=(const CairoImage &) = delete;
68 
69     // Set the image cairo surface
70     void setImage(cairo_surface_t *image);
71 
72     // Get the image cairo surface
getImage()73     cairo_surface_t *getImage() const { return image; }
74 
75     // Get the image rectangle
getRect(double * xa1,double * ya1,double * xa2,double * ya2)76     void getRect(double *xa1, double *ya1, double *xa2, double *ya2)
77     {
78         *xa1 = x1;
79         *ya1 = y1;
80         *xa2 = x2;
81         *ya2 = y2;
82     }
83 
84 private:
85     cairo_surface_t *image; // image cairo surface
86     double x1, y1; // upper left corner
87     double x2, y2; // lower right corner
88 };
89 
90 //------------------------------------------------------------------------
91 // CairoOutputDev
92 //------------------------------------------------------------------------
93 
94 class CairoOutputDev : public OutputDev
95 {
96 public:
97     // Constructor.
98     CairoOutputDev();
99 
100     // Destructor.
101     ~CairoOutputDev() override;
102 
103     //----- get info about output device
104 
105     // Does this device use upside-down coordinates?
106     // (Upside-down means (0,0) is the top left corner of the page.)
upsideDown()107     bool upsideDown() override { return true; }
108 
109     // Does this device use drawChar() or drawString()?
useDrawChar()110     bool useDrawChar() override { return true; }
111 
112     // Does this device use tilingPatternFill()?  If this returns false,
113     // tiling pattern fills will be reduced to a series of other drawing
114     // operations.
useTilingPatternFill()115     bool useTilingPatternFill() override { return true; }
116 
117     // Does this device use functionShadedFill(), axialShadedFill(), and
118     // radialShadedFill()?  If this returns false, these shaded fills
119     // will be reduced to a series of other drawing operations.
120 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
useShadedFills(int type)121     bool useShadedFills(int type) override { return type <= 7; }
122 #else
useShadedFills(int type)123     bool useShadedFills(int type) override { return type > 1 && type < 4; }
124 #endif
125 
126     // Does this device use FillColorStop()?
useFillColorStop()127     bool useFillColorStop() override { return true; }
128 
129     // Does this device use beginType3Char/endType3Char?  Otherwise,
130     // text in Type 3 fonts will be drawn with drawChar/drawString.
interpretType3Chars()131     bool interpretType3Chars() override { return false; }
132 
133     // Does this device need to clip pages to the crop box even when the
134     // box is the crop box?
needClipToCropBox()135     bool needClipToCropBox() override { return true; }
136 
137     //----- initialization and control
138 
139     // Start a page.
140     void startPage(int pageNum, GfxState *state, XRef *xref) override;
141 
142     // End a page.
143     void endPage() override;
144 
145     //----- save/restore graphics state
146     void saveState(GfxState *state) override;
147     void restoreState(GfxState *state) override;
148 
149     //----- update graphics state
150     void updateAll(GfxState *state) override;
151     void setDefaultCTM(const double *ctm) override;
152     void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) override;
153     void updateLineDash(GfxState *state) override;
154     void updateFlatness(GfxState *state) override;
155     void updateLineJoin(GfxState *state) override;
156     void updateLineCap(GfxState *state) override;
157     void updateMiterLimit(GfxState *state) override;
158     void updateLineWidth(GfxState *state) override;
159     void updateFillColor(GfxState *state) override;
160     void updateStrokeColor(GfxState *state) override;
161     void updateFillOpacity(GfxState *state) override;
162     void updateStrokeOpacity(GfxState *state) override;
163     void updateFillColorStop(GfxState *state, double offset) override;
164     void updateBlendMode(GfxState *state) override;
165 
166     //----- update text state
167     void updateFont(GfxState *state) override;
168 
169     //----- path painting
170     void stroke(GfxState *state) override;
171     void fill(GfxState *state) override;
172     void eoFill(GfxState *state) override;
173     void clipToStrokePath(GfxState *state) override;
174     bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) override;
175 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
176     bool functionShadedFill(GfxState *state, GfxFunctionShading *shading) override;
177 #endif
178     bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override;
179     bool axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading) override;
180     bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) override;
181     bool radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading) override;
182 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
183     bool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) override;
184     bool patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) override;
185 #endif
186 
187     //----- path clipping
188     void clip(GfxState *state) override;
189     void eoClip(GfxState *state) override;
190 
191     //----- text drawing
192     void beginString(GfxState *state, const GooString *s) override;
193     void endString(GfxState *state) override;
194     void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override;
195     void beginActualText(GfxState *state, const GooString *text) override;
196     void endActualText(GfxState *state) override;
197 
198     bool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) override;
199     void endType3Char(GfxState *state) override;
200     void beginTextObject(GfxState *state) override;
201     void endTextObject(GfxState *state) override;
202 
203     //----- image drawing
204     void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) override;
205     void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) override;
206     void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) override;
207     void drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg);
208     void drawImageMaskRegular(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg);
209 
210     void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override;
211     void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap,
212                              bool maskInterpolate) override;
213 
214     void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) override;
215 
216     //----- transparency groups and soft masks
217     void beginTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/, GfxColorSpace * /*blendingColorSpace*/, bool /*isolated*/, bool /*knockout*/, bool /*forSoftMask*/) override;
218     void endTransparencyGroup(GfxState * /*state*/) override;
219     void popTransparencyGroup();
220     void paintTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/) override;
221     void setSoftMask(GfxState * /*state*/, const double * /*bbox*/, bool /*alpha*/, Function * /*transferFunc*/, GfxColor * /*backdropColor*/) override;
222     void clearSoftMask(GfxState * /*state*/) override;
223 
224     //----- Type 3 font operators
225     void type3D0(GfxState *state, double wx, double wy) override;
226     void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) override;
227 
228     //----- special access
229 
230     // Called to indicate that a new PDF document has been loaded.
231     void startDoc(PDFDoc *docA, CairoFontEngine *fontEngine = nullptr);
232 
isReverseVideo()233     bool isReverseVideo() { return false; }
234 
235     void setCairo(cairo_t *cr);
236     void setTextPage(TextPage *text);
setPrinting(bool printingA)237     void setPrinting(bool printingA)
238     {
239         printing = printingA;
240         needFontUpdate = true;
241     }
242     void copyAntialias(cairo_t *cr, cairo_t *source_cr);
243 
setInType3Char(bool inType3CharA)244     void setInType3Char(bool inType3CharA) { inType3Char = inType3CharA; }
getType3GlyphWidth(double * wx,double * wy)245     void getType3GlyphWidth(double *wx, double *wy)
246     {
247         *wx = t3_glyph_wx;
248         *wy = t3_glyph_wy;
249     }
hasType3GlyphBBox()250     bool hasType3GlyphBBox() { return t3_glyph_has_bbox; }
getType3GlyphBBox()251     double *getType3GlyphBBox() { return t3_glyph_bbox; }
252 
253 protected:
254     void doPath(cairo_t *cairo, GfxState *state, const GfxPath *path);
255     cairo_surface_t *downscaleSurface(cairo_surface_t *orig_surface);
256     void getScaledSize(const cairo_matrix_t *matrix, int orig_width, int orig_height, int *scaledWidth, int *scaledHeight);
257     cairo_filter_t getFilterForSurface(cairo_surface_t *image, bool interpolate);
258     bool getStreamData(Stream *str, char **buffer, int *length);
259     void setMimeData(GfxState *state, Stream *str, Object *ref, GfxImageColorMap *colorMap, cairo_surface_t *image, int height);
260     void fillToStrokePathClip(GfxState *state);
261     void alignStrokeCoords(const GfxSubpath *subpath, int i, double *x, double *y);
262 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
263     bool setMimeDataForJBIG2Globals(Stream *str, cairo_surface_t *image);
264 #endif
265 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10)
266     bool setMimeDataForCCITTParams(Stream *str, cairo_surface_t *image, int height);
267 #endif
268 
269     GfxRGB fill_color, stroke_color;
270     cairo_pattern_t *fill_pattern, *stroke_pattern;
271     double fill_opacity;
272     double stroke_opacity;
273     bool stroke_adjust;
274     bool adjusted_stroke_width;
275     bool align_stroke_coords;
276     CairoFont *currentFont;
277     XRef *xref;
278 
279     struct StrokePathClip
280     {
281         GfxPath *path;
282         cairo_matrix_t ctm;
283         double line_width;
284         double *dashes;
285         int dash_count;
286         double dash_offset;
287         cairo_line_cap_t cap;
288         cairo_line_join_t join;
289         double miter;
290         int ref_count;
291     } * strokePathClip;
292 
293     PDFDoc *doc; // the current document
294 
295     static FT_Library ft_lib;
296     static std::once_flag ft_lib_once_flag;
297 
298     CairoFontEngine *fontEngine;
299     bool fontEngine_owner;
300 
301     cairo_t *cairo;
302     cairo_matrix_t orig_matrix;
303     bool needFontUpdate; // set when the font needs to be updated
304     bool printing;
305     bool use_show_text_glyphs;
306     bool text_matrix_valid;
307     cairo_glyph_t *glyphs;
308     int glyphCount;
309     cairo_text_cluster_t *clusters;
310     int clusterCount;
311     char *utf8;
312     int utf8Count;
313     int utf8Max;
314     cairo_path_t *textClipPath;
315     bool inUncoloredPattern; // inside a uncolored pattern (PaintType = 2)
316     bool inType3Char; // inside a Type 3 CharProc
317     double t3_glyph_wx, t3_glyph_wy;
318     bool t3_glyph_has_bbox;
319     double t3_glyph_bbox[4];
320     bool prescaleImages;
321 
322     TextPage *textPage; // text for the current page
323     ActualText *actualText;
324 
325     cairo_pattern_t *group;
326     cairo_pattern_t *shape;
327     cairo_pattern_t *mask;
328     cairo_matrix_t mask_matrix;
329     cairo_t *cairo_shape;
330     int knockoutCount;
331     struct ColorSpaceStack
332     {
333         bool knockout;
334         GfxColorSpace *cs;
335         cairo_matrix_t group_matrix;
336         struct ColorSpaceStack *next;
337     } * groupColorSpaceStack;
338 
339     struct MaskStack
340     {
341         cairo_pattern_t *mask;
342         cairo_matrix_t mask_matrix;
343         struct MaskStack *next;
344     } * maskStack;
345 };
346 
347 //------------------------------------------------------------------------
348 // CairoImageOutputDev
349 //------------------------------------------------------------------------
350 
351 // XXX: this should ideally not inherit from CairoOutputDev but use it instead perhaps
352 class CairoImageOutputDev : public CairoOutputDev
353 {
354 public:
355     // Constructor.
356     CairoImageOutputDev();
357 
358     // Destructor.
359     ~CairoImageOutputDev() override;
360 
361     //----- get info about output device
362 
363     // Does this device use upside-down coordinates?
364     // (Upside-down means (0,0) is the top left corner of the page.)
upsideDown()365     bool upsideDown() override { return true; }
366 
367     // Does this device use drawChar() or drawString()?
useDrawChar()368     bool useDrawChar() override { return false; }
369 
370     // Does this device use tilingPatternFill()?  If this returns false,
371     // tiling pattern fills will be reduced to a series of other drawing
372     // operations.
useTilingPatternFill()373     bool useTilingPatternFill() override { return true; }
374 
375     // Does this device use functionShadedFill(), axialShadedFill(), and
376     // radialShadedFill()?  If this returns false, these shaded fills
377     // will be reduced to a series of other drawing operations.
378 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2)
useShadedFills(int type)379     bool useShadedFills(int type) override { return type <= 7; }
380 #else
useShadedFills(int type)381     bool useShadedFills(int type) override { return type < 4; }
382 #endif
383 
384     // Does this device use FillColorStop()?
useFillColorStop()385     bool useFillColorStop() override { return false; }
386 
387     // Does this device use beginType3Char/endType3Char?  Otherwise,
388     // text in Type 3 fonts will be drawn with drawChar/drawString.
interpretType3Chars()389     bool interpretType3Chars() override { return false; }
390 
391     // Does this device need non-text content?
needNonText()392     bool needNonText() override { return true; }
393 
394     //----- save/restore graphics state
saveState(GfxState * state)395     void saveState(GfxState *state) override { }
restoreState(GfxState * state)396     void restoreState(GfxState *state) override { }
397 
398     //----- update graphics state
updateAll(GfxState * state)399     void updateAll(GfxState *state) override { }
setDefaultCTM(const double * ctm)400     void setDefaultCTM(const double *ctm) override { }
updateCTM(GfxState * state,double m11,double m12,double m21,double m22,double m31,double m32)401     void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) override { }
updateLineDash(GfxState * state)402     void updateLineDash(GfxState *state) override { }
updateFlatness(GfxState * state)403     void updateFlatness(GfxState *state) override { }
updateLineJoin(GfxState * state)404     void updateLineJoin(GfxState *state) override { }
updateLineCap(GfxState * state)405     void updateLineCap(GfxState *state) override { }
updateMiterLimit(GfxState * state)406     void updateMiterLimit(GfxState *state) override { }
updateLineWidth(GfxState * state)407     void updateLineWidth(GfxState *state) override { }
updateFillColor(GfxState * state)408     void updateFillColor(GfxState *state) override { }
updateStrokeColor(GfxState * state)409     void updateStrokeColor(GfxState *state) override { }
updateFillOpacity(GfxState * state)410     void updateFillOpacity(GfxState *state) override { }
updateStrokeOpacity(GfxState * state)411     void updateStrokeOpacity(GfxState *state) override { }
updateBlendMode(GfxState * state)412     void updateBlendMode(GfxState *state) override { }
413 
414     //----- update text state
updateFont(GfxState * state)415     void updateFont(GfxState *state) override { }
416 
417     //----- path painting
stroke(GfxState * state)418     void stroke(GfxState *state) override { }
fill(GfxState * state)419     void fill(GfxState *state) override { }
eoFill(GfxState * state)420     void eoFill(GfxState *state) override { }
clipToStrokePath(GfxState * state)421     void clipToStrokePath(GfxState *state) override { }
tilingPatternFill(GfxState * state,Gfx * gfx,Catalog * cat,GfxTilingPattern * tPat,const double * mat,int x0,int y0,int x1,int y1,double xStep,double yStep)422     bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) override { return true; }
axialShadedFill(GfxState * state,GfxAxialShading * shading,double tMin,double tMax)423     bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override { return true; }
radialShadedFill(GfxState * state,GfxRadialShading * shading,double sMin,double sMax)424     bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) override { return true; }
425 
426     //----- path clipping
clip(GfxState * state)427     void clip(GfxState *state) override { }
eoClip(GfxState * state)428     void eoClip(GfxState *state) override { }
429 
430     //----- image drawing
431     void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) override;
432     void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override;
433     void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap,
434                              bool maskInterpolate) override;
435     void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) override;
436     void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) override;
unsetSoftMaskFromImageMask(GfxState * state,double * baseMatrix)437     void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) override { }
438 
439     //----- transparency groups and soft masks
beginTransparencyGroup(GfxState *,const double *,GfxColorSpace *,bool,bool,bool)440     void beginTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/, GfxColorSpace * /*blendingColorSpace*/, bool /*isolated*/, bool /*knockout*/, bool /*forSoftMask*/) override { }
endTransparencyGroup(GfxState *)441     void endTransparencyGroup(GfxState * /*state*/) override { }
paintTransparencyGroup(GfxState *,const double *)442     void paintTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/) override { }
setSoftMask(GfxState *,const double *,bool,Function *,GfxColor *)443     void setSoftMask(GfxState * /*state*/, const double * /*bbox*/, bool /*alpha*/, Function * /*transferFunc*/, GfxColor * /*backdropColor*/) override { }
clearSoftMask(GfxState *)444     void clearSoftMask(GfxState * /*state*/) override { }
445 
446     //----- Image list
447     // By default images are not rendred
setImageDrawDecideCbk(bool (* cbk)(int img_id,void * data),void * data)448     void setImageDrawDecideCbk(bool (*cbk)(int img_id, void *data), void *data)
449     {
450         imgDrawCbk = cbk;
451         imgDrawCbkData = data;
452     }
453     // Iterate through list of images.
getNumImages()454     int getNumImages() const { return numImages; }
getImage(int i)455     CairoImage *getImage(int i) const { return images[i]; }
456 
457 private:
458     void saveImage(CairoImage *image);
459     void getBBox(GfxState *state, int width, int height, double *x1, double *y1, double *x2, double *y2);
460 
461     CairoImage **images;
462     int numImages;
463     int size;
464     bool (*imgDrawCbk)(int img_id, void *data);
465     void *imgDrawCbkData;
466 };
467 
468 #endif
469