~ubuntu-branches/ubuntu/trusty/pdfmod/trusty

« back to all changes in this revision

Viewing changes to lib/PdfSharp/PdfSharp.Drawing.Pdf/PdfGraphicsState.cs

  • Committer: Bazaar Package Importer
  • Author(s): Chow Loong Jin
  • Date: 2010-06-18 03:44:46 UTC
  • Revision ID: james.westby@ubuntu.com-20100618034446-bogifrsscpayp361
Tags: upstream-0.8.3
ImportĀ upstreamĀ versionĀ 0.8.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#region PDFsharp - A .NET library for processing PDF
 
2
//
 
3
// Authors:
 
4
//   Stefan Lange (mailto:Stefan.Lange@pdfsharp.com)
 
5
//
 
6
// Copyright (c) 2005-2008 empira Software GmbH, Cologne (Germany)
 
7
//
 
8
// http://www.pdfsharp.com
 
9
// http://sourceforge.net/projects/pdfsharp
 
10
//
 
11
// Permission is hereby granted, free of charge, to any person obtaining a
 
12
// copy of this software and associated documentation files (the "Software"),
 
13
// to deal in the Software without restriction, including without limitation
 
14
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
 
15
// and/or sell copies of the Software, and to permit persons to whom the
 
16
// Software is furnished to do so, subject to the following conditions:
 
17
//
 
18
// The above copyright notice and this permission notice shall be included
 
19
// in all copies or substantial portions of the Software.
 
20
//
 
21
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
22
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
23
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 
24
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
25
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 
26
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 
27
// DEALINGS IN THE SOFTWARE.
 
28
#endregion
 
29
 
 
30
using System;
 
31
using System.Diagnostics;
 
32
using System.Globalization;
 
33
using System.Text;
 
34
using System.IO;
 
35
#if GDI
 
36
using System.Drawing;
 
37
using System.Drawing.Drawing2D;
 
38
#endif
 
39
#if WPF
 
40
using System.Windows.Media;
 
41
#endif
 
42
using PdfSharp.Internal;
 
43
using PdfSharp.Pdf;
 
44
using PdfSharp.Pdf.Advanced;
 
45
using PdfSharp.Pdf.Internal;
 
46
 
 
47
namespace PdfSharp.Drawing.Pdf
 
48
{
 
49
  // TODO: update the following text
 
50
  //
 
51
  // In PDF the current transformation matrix (CTM) can only be modified, but not set. The XGraphics
 
52
  // object allows to set the transformation matrix, which leads to a problem. In PDF the only way
 
53
  // to reset the CTM to its original value is to save and restore the PDF graphics state. Don't try
 
54
  // to keep track of every modification and then reset the CTM by multiplying with the inverse matrix
 
55
  // of the product of all modifications. PDFlib uses this 'trick', but it does not work. Because of
 
56
  // rounding errors everything on the PDF page looks sloping after some resets. Saving and restoring
 
57
  // the graphics state is the only possible way to reset the CTM, but because the PDF restore operator
 
58
  // 'Q' resets not only the CTM but all other graphics state values, we have to implement our own 
 
59
  // graphics state management. This is apparently the only safe way to give the XGrahics users the 
 
60
  // illusion that they can arbitrarily set the transformation matrix.
 
61
  // 
 
62
  // The current implementation is just a draft. Save/Restore works only once and clipping is not
 
63
  // correctly restored in some cases.
 
64
 
 
65
  /// <summary>
 
66
  /// Represents the current PDF graphics state.
 
67
  /// </summary>
 
68
  internal sealed class PdfGraphicsState : ICloneable
 
69
  {
 
70
    public PdfGraphicsState(XGraphicsPdfRenderer renderer)
 
71
    {
 
72
      this.renderer = renderer;
 
73
    }
 
74
    XGraphicsPdfRenderer renderer;
 
75
 
 
76
    public PdfGraphicsState Clone()
 
77
    {
 
78
      PdfGraphicsState state = (PdfGraphicsState)MemberwiseClone();
 
79
      return state;
 
80
    }
 
81
 
 
82
    object ICloneable.Clone()
 
83
    {
 
84
      return Clone();
 
85
    }
 
86
 
 
87
    internal int Level;
 
88
 
 
89
    internal InternalGraphicsState InternalState;
 
90
 
 
91
    public void PushState()
 
92
    {
 
93
      //BeginGraphic
 
94
      this.renderer.Append("q/n");
 
95
    }
 
96
 
 
97
    public void PopState()
 
98
    {
 
99
      //BeginGraphic
 
100
      this.renderer.Append("Q/n");
 
101
    }
 
102
 
 
103
    #region Stroke
 
104
 
 
105
    double realizedLineWith = -1;
 
106
    int realizedLineCap = -1;
 
107
    int realizedLineJoin = -1;
 
108
    double realizedMiterLimit = -1;
 
109
    XDashStyle realizedDashStyle = (XDashStyle)(-1);
 
110
    string realizedDashPattern;
 
111
    XColor realizedStrokeColor = XColor.Empty;
 
112
 
 
113
    public void RealizePen(XPen pen, PdfColorMode colorMode)
 
114
    {
 
115
      XColor color = pen.Color;
 
116
      color = ColorSpaceHelper.EnsureColorMode(colorMode, color);
 
117
 
 
118
      if (this.realizedLineWith != pen.width)
 
119
      {
 
120
        this.renderer.AppendFormat("{0:0.###} w\n", pen.width);
 
121
        this.realizedLineWith = pen.width;
 
122
      }
 
123
 
 
124
      if (this.realizedLineCap != (int)pen.lineCap)
 
125
      {
 
126
        this.renderer.AppendFormat("{0} J\n", (int)pen.lineCap);
 
127
        this.realizedLineCap = (int)pen.lineCap;
 
128
      }
 
129
 
 
130
      if (this.realizedLineJoin != (int)pen.lineJoin)
 
131
      {
 
132
        this.renderer.AppendFormat("{0} j\n", (int)pen.lineJoin);
 
133
        this.realizedLineJoin = (int)pen.lineJoin;
 
134
      }
 
135
 
 
136
      if (this.realizedLineCap == (int)XLineJoin.Miter)
 
137
      {
 
138
        if (this.realizedMiterLimit != (int)pen.miterLimit && (int)pen.miterLimit != 0)
 
139
        {
 
140
          this.renderer.AppendFormat("{0} M\n", (int)pen.miterLimit);
 
141
          this.realizedMiterLimit = (int)pen.miterLimit;
 
142
        }
 
143
      }
 
144
 
 
145
      if (this.realizedDashStyle != pen.dashStyle || pen.dashStyle == XDashStyle.Custom)
 
146
      {
 
147
        double dot = pen.Width;
 
148
        double dash = 3 * dot;
 
149
 
 
150
        // Line width 0 is not recommended but valid
 
151
        XDashStyle dashStyle = pen.DashStyle;
 
152
        if (dot == 0)
 
153
          dashStyle = XDashStyle.Solid;
 
154
 
 
155
        switch (dashStyle)
 
156
        {
 
157
          case XDashStyle.Solid:
 
158
            this.renderer.Append("[]0 d\n");
 
159
            break;
 
160
 
 
161
          case XDashStyle.Dash:
 
162
            this.renderer.AppendFormat("[{0:0.##} {1:0.##}]0 d\n", dash, dot);
 
163
            break;
 
164
 
 
165
          case XDashStyle.Dot:
 
166
            this.renderer.AppendFormat("[{0:0.##}]0 d\n", dot);
 
167
            break;
 
168
 
 
169
          case XDashStyle.DashDot:
 
170
            this.renderer.AppendFormat("[{0:0.##} {1:0.##} {1:0.##} {1:0.##}]0 d\n", dash, dot);
 
171
            break;
 
172
 
 
173
          case XDashStyle.DashDotDot:
 
174
            this.renderer.AppendFormat("[{0:0.##} {1:0.##} {1:0.##} {1:0.##} {1:0.##} {1:0.##}]0 d\n", dash, dot);
 
175
            break;
 
176
 
 
177
          case XDashStyle.Custom:
 
178
            {
 
179
              StringBuilder pdf = new StringBuilder("[", 256);
 
180
              int len = pen.dashPattern == null ? 0 : pen.dashPattern.Length;
 
181
              for (int idx = 0; idx < len; idx++)
 
182
              {
 
183
                if (idx > 0)
 
184
                  pdf.Append(' ');
 
185
                pdf.Append(PdfEncoders.ToString(pen.dashPattern[idx] * pen.width));
 
186
              }
 
187
              // Make an even number of values look like in GDI+
 
188
              if (len > 0 && len % 2 == 1)
 
189
              {
 
190
                pdf.Append(' ');
 
191
                pdf.Append(PdfEncoders.ToString(0.2 * pen.width));
 
192
              }
 
193
              pdf.AppendFormat(CultureInfo.InvariantCulture, "]{0:0.###} d\n", pen.dashOffset * pen.width);
 
194
              string pattern = pdf.ToString();
 
195
 
 
196
              // BUG: drice2@ageone.de reported a realizing problem
 
197
              // HACK: I romove the if clause
 
198
              //if (this.realizedDashPattern != pattern)
 
199
              {
 
200
                this.realizedDashPattern = pattern;
 
201
                this.renderer.Append(pattern);
 
202
              }
 
203
            }
 
204
            break;
 
205
        }
 
206
        this.realizedDashStyle = dashStyle;
 
207
      }
 
208
 
 
209
      if (colorMode != PdfColorMode.Cmyk)
 
210
      {
 
211
        if (this.realizedStrokeColor.Rgb != color.Rgb)
 
212
        {
 
213
          this.renderer.Append(PdfEncoders.ToString(color, PdfColorMode.Rgb));
 
214
          this.renderer.Append(" RG\n");
 
215
        }
 
216
      }
 
217
      else
 
218
      {
 
219
        if (!ColorSpaceHelper.IsEqualCmyk(this.realizedStrokeColor, color))
 
220
        {
 
221
          this.renderer.Append(PdfEncoders.ToString(color, PdfColorMode.Cmyk));
 
222
          this.renderer.Append(" K\n");
 
223
        }
 
224
      }
 
225
 
 
226
      if (this.renderer.Owner.Version >= 14 && this.realizedStrokeColor.A != color.A)
 
227
      {
 
228
        PdfExtGState extGState = this.renderer.Owner.ExtGStateTable.GetExtGStateStroke(color.A);
 
229
        string gs = this.renderer.Resources.AddExtGState(extGState);
 
230
        this.renderer.AppendFormat("{0} gs\n", gs);
 
231
 
 
232
        // Must create transparany group
 
233
        if (this.renderer.page != null && color.A < 1)
 
234
          this.renderer.page.transparencyUsed = true;
 
235
      }
 
236
      this.realizedStrokeColor = color;
 
237
    }
 
238
 
 
239
    #endregion
 
240
 
 
241
    #region Fill
 
242
 
 
243
    XColor realizedFillColor = XColor.Empty;
 
244
 
 
245
    public void RealizeBrush(XBrush brush, PdfColorMode colorMode)
 
246
    {
 
247
      if (brush is XSolidBrush)
 
248
      {
 
249
        XColor color = ((XSolidBrush)brush).Color;
 
250
        color = ColorSpaceHelper.EnsureColorMode(colorMode, color);
 
251
 
 
252
        if (colorMode != PdfColorMode.Cmyk)
 
253
        {
 
254
          if (this.realizedFillColor.Rgb != color.Rgb)
 
255
          {
 
256
            this.renderer.Append(PdfEncoders.ToString(color, PdfColorMode.Rgb));
 
257
            this.renderer.Append(" rg\n");
 
258
          }
 
259
        }
 
260
        else
 
261
        {
 
262
          if (!ColorSpaceHelper.IsEqualCmyk(this.realizedFillColor, color))
 
263
          {
 
264
            this.renderer.Append(PdfEncoders.ToString(color, PdfColorMode.Cmyk));
 
265
            this.renderer.Append(" k\n");
 
266
          }
 
267
        }
 
268
 
 
269
        if (this.renderer.Owner.Version >= 14 && this.realizedFillColor.A != color.A)
 
270
        {
 
271
          PdfExtGState extGState = this.renderer.Owner.ExtGStateTable.GetExtGStateNonStroke(color.A);
 
272
          string gs = this.renderer.Resources.AddExtGState(extGState);
 
273
          this.renderer.AppendFormat("{0} gs\n", gs);
 
274
 
 
275
          // Must create transparany group
 
276
          if (this.renderer.page != null && color.A < 1)
 
277
            this.renderer.page.transparencyUsed = true;
 
278
        }
 
279
        this.realizedFillColor = color;
 
280
      }
 
281
      else if (brush is XLinearGradientBrush)
 
282
      {
 
283
        XMatrix matrix = this.renderer.defaultViewMatrix;
 
284
        matrix.Prepend(this.Transform);
 
285
        PdfShadingPattern pattern = new PdfShadingPattern(this.renderer.Owner);
 
286
        pattern.SetupFromBrush((XLinearGradientBrush)brush, matrix);
 
287
        string name = this.renderer.Resources.AddPattern(pattern);
 
288
        this.renderer.AppendFormat("/Pattern cs\n", name);
 
289
        this.renderer.AppendFormat("{0} scn\n", name);
 
290
 
 
291
        // Invalidate fill color
 
292
        this.realizedFillColor = XColor.Empty;
 
293
      }
 
294
    }
 
295
    #endregion
 
296
 
 
297
    #region Text
 
298
 
 
299
    internal PdfFont realizedFont;
 
300
    string realizedFontName = String.Empty;
 
301
    double realizedFontSize = 0;
 
302
 
 
303
    public void RealizeFont(XFont font, XBrush brush, int renderMode)
 
304
    {
 
305
      // So far rendering mode 0 only
 
306
      RealizeBrush(brush, this.renderer.colorMode); // this.renderer.page.document.Options.ColorMode);
 
307
 
 
308
      this.realizedFont = null;
 
309
      string fontName = this.renderer.GetFontName(font, out this.realizedFont);
 
310
      if (fontName != this.realizedFontName || this.realizedFontSize != font.Size)
 
311
      {
 
312
        if (this.renderer.Gfx.PageDirection == XPageDirection.Downwards)
 
313
          this.renderer.AppendFormat("{0} {1:0.###} Tf\n", fontName, -font.Size);
 
314
        else
 
315
          this.renderer.AppendFormat("{0} {1:0.###} Tf\n", fontName, font.Size);
 
316
 
 
317
        this.realizedFontName = fontName;
 
318
        this.realizedFontSize = font.Size;
 
319
      }
 
320
    }
 
321
 
 
322
    public XPoint realizedTextPosition = new XPoint();
 
323
 
 
324
    #endregion
 
325
 
 
326
    #region Transformation
 
327
 
 
328
    /// <summary>
 
329
    /// The realized current transformation matrix.
 
330
    /// </summary>
 
331
    XMatrix realizedCtm = XMatrix.Identity;
 
332
 
 
333
    /// <summary>
 
334
    /// The unrealized current transformation matrix.
 
335
    /// </summary>
 
336
    XMatrix unrealizedCtm = XMatrix.Identity;
 
337
 
 
338
    /// <summary>
 
339
    /// A flag indicating whether the CTM must be realized.
 
340
    /// </summary>
 
341
    public bool MustRealizeCtm;
 
342
 
 
343
    public XMatrix Transform
 
344
    {
 
345
      get
 
346
      {
 
347
        if (this.MustRealizeCtm)
 
348
        {
 
349
          XMatrix matrix = this.realizedCtm;
 
350
          matrix.Prepend(this.unrealizedCtm);
 
351
          return matrix;
 
352
        }
 
353
        return this.realizedCtm;
 
354
      }
 
355
      set
 
356
      {
 
357
        XMatrix matrix = this.realizedCtm;
 
358
        matrix.Invert();
 
359
        matrix.Prepend(value);
 
360
        this.unrealizedCtm = matrix;
 
361
        this.MustRealizeCtm = !this.unrealizedCtm.IsIdentity;
 
362
      }
 
363
    }
 
364
 
 
365
    /// <summary>
 
366
    /// Modifies the current transformation matrix.
 
367
    /// </summary>
 
368
    public void MultiplyTransform(XMatrix matrix, XMatrixOrder order)
 
369
    {
 
370
      if (!matrix.IsIdentity)
 
371
      {
 
372
        this.MustRealizeCtm = true;
 
373
        this.unrealizedCtm.Multiply(matrix, order);
 
374
      }
 
375
    }
 
376
 
 
377
    /// <summary>
 
378
    /// Realizes the CTM.
 
379
    /// </summary>
 
380
    public void RealizeCtm()
 
381
    {
 
382
      if (this.MustRealizeCtm)
 
383
      {
 
384
        Debug.Assert(!this.unrealizedCtm.IsIdentity, "mrCtm is unnecessarily set.");
 
385
 
 
386
        double[] matrix = this.unrealizedCtm.GetElements();
 
387
        // Up to six decimal digits to prevent round up problems
 
388
        this.renderer.AppendFormat("{0:0.######} {1:0.######} {2:0.######} {3:0.######} {4:0.######} {5:0.######} cm\n",
 
389
          matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
 
390
 
 
391
        this.realizedCtm.Prepend(this.unrealizedCtm);
 
392
        this.unrealizedCtm = XMatrix.Identity;
 
393
        this.MustRealizeCtm = false;
 
394
      }
 
395
    }
 
396
 
 
397
    #endregion
 
398
 
 
399
    #region Clip Path
 
400
 
 
401
    public void SetAndRealizeClipRect(XRect clipRect)
 
402
    {
 
403
      XGraphicsPath clipPath = new XGraphicsPath();
 
404
      clipPath.AddRectangle(clipRect);
 
405
      RealizeClipPath(clipPath);
 
406
    }
 
407
 
 
408
    public void SetAndRealizeClipPath(XGraphicsPath clipPath)
 
409
    {
 
410
      RealizeClipPath(clipPath);
 
411
    }
 
412
 
 
413
    void RealizeClipPath(XGraphicsPath clipPath)
 
414
    {
 
415
      this.renderer.BeginGraphic();
 
416
      RealizeCtm();
 
417
#if GDI && !WPF
 
418
      this.renderer.AppendPath(clipPath.gdipPath);
 
419
#endif
 
420
#if WPF &&!GDI
 
421
      this.renderer.AppendPath(clipPath.pathGeometry);
 
422
#endif
 
423
#if WPF && GDI
 
424
      if (this.renderer.Gfx.targetContext == XGraphicTargetContext.GDI)
 
425
      {
 
426
        this.renderer.AppendPath(clipPath.gdipPath);
 
427
      }
 
428
      else
 
429
      {
 
430
        this.renderer.AppendPath(clipPath.pathGeometry);
 
431
      }
 
432
#endif
 
433
      if (clipPath.FillMode == XFillMode.Winding)
 
434
        this.renderer.Append("W n\n");
 
435
      else
 
436
        this.renderer.Append("W* n\n");
 
437
    }
 
438
 
 
439
    #endregion
 
440
  }
 
441
}