PDFExportVector.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Drawing.Drawing2D;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. namespace FastReport.Export.Pdf
  9. {
  10. public partial class PDFExport
  11. {
  12. #region Private Fields
  13. private CurvesInterpolationEnum curvesInterpolation = CurvesInterpolationEnum.Curves;
  14. private CurvesInterpolationEnum curvesInterpolationText = CurvesInterpolationEnum.Curves;
  15. private Dictionary<HashableByteArray, long> gradientHashSet;
  16. private GradientInterpolationPointsEnum gradientInterpolationPoints = GradientInterpolationPointsEnum.P128;
  17. private GradientQualityEnum gradientQuality = GradientQualityEnum.Medium;
  18. private List<long> pageAlphaShading;
  19. private bool svgAsPicture;
  20. //private int FPageShadingNumber;
  21. private List<long> pageShadings;
  22. #endregion Private Fields
  23. #region Public Properties
  24. /// <summary>
  25. /// The interpolation of curves (svg)
  26. /// </summary>
  27. public CurvesInterpolationEnum CurvesInterpolation
  28. {
  29. get
  30. {
  31. return curvesInterpolation;
  32. }
  33. set
  34. {
  35. curvesInterpolation = value;
  36. }
  37. }
  38. /// <summary>
  39. /// The interpolation of curves (text)
  40. /// </summary>
  41. public CurvesInterpolationEnum CurvesInterpolationText
  42. {
  43. get
  44. {
  45. return curvesInterpolationText;
  46. }
  47. set
  48. {
  49. curvesInterpolationText = value;
  50. }
  51. }
  52. /// <summary>
  53. /// Export svg object as image, not vector
  54. /// </summary>
  55. public bool SvgAsPicture
  56. {
  57. get { return svgAsPicture; }
  58. set { svgAsPicture = value; }
  59. }
  60. /// <summary>
  61. /// Gradient interpolation, high value will lead beautiful the gradient,
  62. /// but the file size will increase and the speed of work will decrease.
  63. /// </summary>
  64. public GradientInterpolationPointsEnum GradientInterpolationPoints
  65. {
  66. get
  67. {
  68. return gradientInterpolationPoints;
  69. }
  70. set
  71. {
  72. gradientInterpolationPoints = value;
  73. }
  74. }
  75. /// <summary>
  76. /// The quality of gradient, export as image or export as gradient grid
  77. /// </summary>
  78. public GradientQualityEnum GradientQuality
  79. {
  80. get
  81. {
  82. return gradientQuality;
  83. }
  84. set
  85. {
  86. gradientQuality = value;
  87. }
  88. }
  89. #endregion Public Properties
  90. #region Private Methods
  91. private void AddPDFPolylineVector(PolyLineObject obj, StringBuilder sb)
  92. {
  93. GraphicsPath path = obj.GetPath(null, 0, 0, obj.Width, obj.Height, 1, 1);
  94. SizeF rect = new SizeF(obj.Width, obj.Height);
  95. if (obj is PolygonObject)
  96. {
  97. FillPDFGraphicsPath(rect, path, obj.Fill.CreateBrush(new RectangleF(PointF.Empty, rect)), sb, curvesInterpolation,
  98. new System.Drawing.Drawing2D.Matrix(PDF_DIVIDER, 0, 0, -PDF_DIVIDER, GetLeft(obj.AbsLeft), GetTop(obj.AbsTop)));
  99. }
  100. // The obj.Border parameter has been changed to obj for the possibility of using custom lines inside the method
  101. // And also combined the StrokePDFGraphicsPath method call for PolygonObject and PolyLineObject
  102. StrokePDFGraphicsPath(rect, path, obj, obj is PolygonObject, sb, curvesInterpolation,
  103. new System.Drawing.Drawing2D.Matrix(PDF_DIVIDER, 0, 0, -PDF_DIVIDER, GetLeft(obj.AbsLeft), GetTop(obj.AbsTop)));
  104. }
  105. private void AppendPDFGraphicsPath(GraphicsPath path, StringBuilder sb, CurvesInterpolationEnum curvesInterpolation)
  106. {
  107. PointF[] ps = path.PathPoints;
  108. byte[] pt = path.PathTypes;
  109. PointF startPoint = ps.Length > 0 ? ps[0] : new PointF();
  110. int interpolation = (int)curvesInterpolation;
  111. for (int i = 0; i < ps.Length; i++)
  112. {
  113. if ((pt[i] & 1) == 1)
  114. {
  115. if ((pt[i] & 3) == 3 && i < ps.Length - 2)
  116. {
  117. if (interpolation > 0)
  118. {
  119. for (int dt = 1; dt <= interpolation; dt++)
  120. AppendPDFGraphicsPathBezierInterpolate(ps[i - 1], ps[i], ps[i + 1], ps[i + 2], (float)dt / interpolation, sb);
  121. }
  122. else
  123. sb.Append(FloatToString(ps[i].X)).Append(" ").Append(FloatToString(ps[i].Y)).Append(" ")
  124. .Append(FloatToString(ps[i + 1].X)).Append(" ").Append(FloatToString(ps[i + 1].Y)).Append(" ")
  125. .Append(FloatToString(ps[i + 2].X)).Append(" ").Append(FloatToString(ps[i + 2].Y)).Append(" c ");
  126. i += 2;
  127. }
  128. else sb.Append(FloatToString(ps[i].X)).Append(" ").Append(FloatToString(ps[i].Y)).Append(" l ");
  129. }
  130. if (pt[i] == 0)
  131. sb.Append(FloatToString(ps[i].X)).Append(" ").Append(FloatToString(ps[i].Y)).Append(" m ");
  132. // switch (pt[i])
  133. //{
  134. // case 0://start
  135. // //sb.AppendLine();
  136. // i++;
  137. // break;
  138. // case 1://line
  139. // i++;
  140. // break;
  141. // //case 3://interpolate bezier
  142. // // for (float dt = 1; dt < 6; dt++)
  143. // // DrawPDFBezier(rect.Left, rect.Top, ps[i - 1], ps[i], ps[i + 1], ps[i + 2], dt / 5, sb);
  144. // // i += 3;
  145. // // break;
  146. // default:
  147. // i++;
  148. // break;
  149. //}
  150. //fill
  151. }
  152. //DrawPDFBezier
  153. sb.AppendLine();
  154. }
  155. private void AppendPDFGraphicsPathBezierInterpolate(PointF p0, PointF p1, PointF p2, PointF p3, float t, StringBuilder sb)
  156. {
  157. float t1 = 1 - t;
  158. float px = t1 * t1 * t1 * p0.X + 3 * t1 * t1 * t * p1.X + 3 * t * t * t1 * p2.X + t * t * t * p3.X;
  159. float py = t1 * t1 * t1 * p0.Y + 3 * t1 * t1 * t * p1.Y + 3 * t * t * t1 * p2.Y + t * t * t * p3.Y;
  160. sb.Append(FloatToString(px)).Append(" ").Append(FloatToString(py)).Append(" l ");
  161. }
  162. private void DrawPDFVectorGradientFill(float left, float top, float width, float height, FillBase fill, StringBuilder sb)
  163. {
  164. unchecked
  165. {
  166. RectangleF rect = new RectangleF(0, 0, width, height);
  167. Brush brush = fill.CreateBrush(rect);
  168. sb.AppendLine("q");
  169. sb.Append("1 0 0 -1 ").Append(FloatToString(left)).Append(" ").Append(FloatToString(top)).AppendLine(" cm");
  170. DrawPDFVectorGradientFill(new SizeF(width, height), brush, sb);
  171. sb.AppendLine("Q");
  172. }
  173. }
  174. private void DrawPDFVectorGradientFill(SizeF size, Brush brush, StringBuilder sb)
  175. {
  176. if (gradientQuality != GradientQualityEnum.Image && (brush is LinearGradientBrush || brush is PathGradientBrush))
  177. {
  178. string shading = GetShadingBrush(size, brush);
  179. if (shading != null)
  180. {
  181. sb.AppendLine("q");
  182. sb.Append(FloatToString(size.Width)).Append(" 0 0 ").Append(FloatToString(-size.Height)).Append(" ").Append(FloatToString(0)).Append(" ").Append(FloatToString(size.Height)).AppendLine(" cm");
  183. sb.AppendLine(shading);
  184. sb.AppendLine("Q");
  185. }
  186. }
  187. else
  188. {
  189. string image = GetImageBrush(size, brush);
  190. sb.AppendLine("q");
  191. sb.Append(FloatToString(size.Width)).Append(" 0 0 ").Append(FloatToString(-size.Height)).Append(" ").Append(FloatToString(0)).Append(" ").Append(FloatToString(size.Height)).AppendLine(" cm");
  192. sb.AppendLine(image);
  193. sb.AppendLine("Q");
  194. }
  195. }
  196. /// <summary>
  197. /// Added graphics path to pdf,
  198. /// </summary>
  199. /// <param name="size">size of rect for gradient filling</param>
  200. /// <param name="path">path, with positions in pdf scaling</param>
  201. /// <param name="brush">Any brush</param>
  202. /// <param name="curvesInterpolation">Interpolation value</param>
  203. /// <param name="sb"></param>
  204. /// <param name="matrixTransform">matrix for transform to pdf scale</param>
  205. private void FillPDFGraphicsPath(SizeF size, GraphicsPath path, Brush brush, StringBuilder sb, CurvesInterpolationEnum curvesInterpolation,
  206. System.Drawing.Drawing2D.Matrix matrixTransform)
  207. {
  208. if (path.PointCount == 0) return;
  209. sb.AppendLine("q");
  210. if (brush is SolidBrush)
  211. GetPDFFillColor((brush as SolidBrush).Color, sb);
  212. float[] m = matrixTransform.Elements;
  213. sb.Append(FloatToString(m[0])).Append(" ")
  214. .Append(FloatToString(m[1])).Append(" ")
  215. .Append(FloatToString(m[2])).Append(" ")
  216. .Append(FloatToString(m[3])).Append(" ")
  217. .Append(FloatToString(m[4])).Append(" ")
  218. .Append(FloatToString(m[5])).AppendLine(" cm");
  219. AppendPDFGraphicsPath(path, sb, curvesInterpolation);
  220. if (brush is SolidBrush)
  221. sb.AppendLine("f");
  222. else
  223. {
  224. sb.AppendLine("W n");
  225. DrawPDFVectorGradientFill(size, brush, sb);
  226. }
  227. sb.AppendLine("Q");
  228. }
  229. private void ClipPDFGraphicsPath(GraphicsPath path, StringBuilder sb)
  230. {
  231. sb.AppendLine("q");
  232. AppendPDFGraphicsPath(path, sb, curvesInterpolation);
  233. sb.AppendLine("h");
  234. sb.AppendLine("W n");
  235. }
  236. private string GetImageBrush(SizeF size, Brush brush)
  237. {
  238. float printZoom = printOptimized ? 4 : 1;
  239. int bitmapWidth = (int)Math.Round(size.Width * printZoom);
  240. int bitmapHeight = (int)Math.Round(size.Height * printZoom);
  241. // check for max bitmap object size
  242. {
  243. // 2GB (max .net object size) / 4 (Format32bppArgb is 4 bytes)
  244. // see http://stackoverflow.com/a/29175905/4667434
  245. const ulong maxPixels = 536870912;
  246. if ((ulong)bitmapWidth * (ulong)bitmapHeight >= maxPixels)
  247. {
  248. bitmapWidth = (int)size.Width;
  249. bitmapHeight = (int)size.Height;
  250. }
  251. if ((ulong)bitmapWidth * (ulong)bitmapHeight >= maxPixels)
  252. {
  253. return null;
  254. }
  255. }
  256. using (Bitmap bmp = new Bitmap(bitmapWidth, bitmapHeight))
  257. {
  258. using (Graphics g = Graphics.FromImage(bmp))
  259. {
  260. g.Clear(Color.Transparent);
  261. //g.TranslateTransform(0, size.Height);
  262. g.ScaleTransform(bitmapWidth / size.Width, bitmapHeight / size.Height);
  263. //g.TranslateTransform(-rect.Left, -rect.Top);
  264. g.FillRectangle(brush, new RectangleF(PointF.Empty, size));
  265. g.Flush();
  266. }
  267. long imageIndex = AppendPDFImage(bmp, jpegQuality);
  268. if (imageIndex < 0) return null;
  269. AddImageToList(imageIndex);
  270. return ExportUtils.StringFormat("/Im{0} Do", imageIndex);
  271. }
  272. }
  273. private string GetShadingBrush(SizeF size, Brush brush)
  274. {
  275. if (gradientQuality == GradientQualityEnum.Image) return null;
  276. //RectangleF rect;
  277. //if (brush is LinearGradientBrush)
  278. // rect = (brush as LinearGradientBrush).Rectangle;
  279. //else if (brush is PathGradientBrush)
  280. // rect = (brush as PathGradientBrush).Rectangle;
  281. //else return -1;
  282. int points = (int)gradientInterpolationPoints;
  283. int bmpWidth = (int)gradientQuality;
  284. if (points > bmpWidth) points = bmpWidth;
  285. using (Bitmap bmp = new Bitmap(bmpWidth, bmpWidth))
  286. {
  287. using (Graphics g = Graphics.FromImage(bmp))
  288. {
  289. g.Clear(Color.Transparent);
  290. g.ScaleTransform(bmpWidth / size.Width, bmpWidth / size.Height);
  291. g.FillRectangle(brush, new RectangleF(PointF.Empty, size));
  292. g.Flush();
  293. }
  294. bool alpha;
  295. long pos;
  296. switch (ColorSpace)
  297. {
  298. case PdfColorSpace.RGB:
  299. pos = GetShadingBrushRGB(points, bmp, out alpha);
  300. break;
  301. case PdfColorSpace.CMYK:
  302. pos = GetShadingBrushCMYK(points, bmp, out alpha);
  303. break;
  304. default:
  305. throw new NotImplementedException("3a4d1849-8309-4452-ade3-173f21458fe1");
  306. }
  307. pageShadings.Add(pos);
  308. if (alpha)
  309. {
  310. long maskPos = GetShadingBrushAlpha(size, points, bmp);
  311. if (maskPos >= 0)
  312. {
  313. pageAlphaShading.Add(maskPos);
  314. return ExportUtils.StringFormat("/s{0} gs /sh{1} sh", pageAlphaShading.Count, pageShadings.Count);
  315. }
  316. // /s6 << /ca 1 /Type /ExtGState /AIS false /SMask << /Type Mask /G << >> /S /Luminosity >> /CA 1 >>
  317. }
  318. //FPageShadings.Append(" /sh").Append(++FPageShadingNumber).Append(" ").Append(ObjNumberRef(pos));
  319. return ExportUtils.StringFormat("/sh{0} sh", pageShadings.Count);
  320. }
  321. }
  322. private long GetShadingBrushAlpha(SizeF size, int points, Bitmap bmp)
  323. {
  324. long posMask = GetShadingBrushGrayAlpha(points, bmp);
  325. long pos = UpdateXRef();
  326. StringBuilder sb = new StringBuilder();
  327. sb.AppendLine(ObjNumber(pos));
  328. sb.AppendLine("<< /Group << /Type /Group /CS /DeviceGray /S /Transparency /I true >> ");
  329. sb.Append("/Type /XObject /Subtype /Form /FormType 1 /Resources << /Shading << /sh").Append(posMask).Append(" ")
  330. .Append(ObjNumberRef(posMask)).AppendLine(" >> /ExtGState << /a0 << /ca 1 /CA 1 >> >> >>");
  331. sb.Append("/BBox [0 0 ").Append(FloatToString(size.Width)).Append(" ").Append(FloatToString(size.Height)).AppendLine(" ]");
  332. //stream
  333. string stringStream = ExportUtils.StringFormat("/a0 gs /sh{0} sh", posMask);
  334. WriteLn(pdf, sb.ToString());
  335. using (MemoryStream tempContentStream = new MemoryStream())
  336. {
  337. Write(tempContentStream, stringStream);
  338. tempContentStream.Position = 0;
  339. WritePDFStream(pdf, tempContentStream, posMask, compressed, encrypted, false, true);
  340. }
  341. return pos;
  342. }
  343. private long GetShadingBrushCMYK(int size, Bitmap bmp, out bool alpha)
  344. {
  345. alpha = false;
  346. long pos = UpdateXRef();
  347. int bmpWidth = bmp.Width;
  348. int bmpHeight = bmp.Height;
  349. StringBuilder sh = new StringBuilder();
  350. sh.AppendLine(ObjNumber(pos));
  351. sh.Append("<< /ShadingType 5 /ColorSpace");
  352. sh.Append(" /DeviceCMYK");
  353. sh.Append(" /VerticesPerRow ").Append(size);// FGradientInterpolationPoints);
  354. sh.Append(" /BitsPerCoordinate 8");
  355. sh.Append(" /BitsPerComponent 8");
  356. sh.Append(" /Decode [0 1.0 0 1.0 0 1.0 0 1.0 0 1.0 0 1.0]");
  357. using (MemoryStream dataSource = new MemoryStream())
  358. {
  359. using (BinaryWriter writer = new BinaryWriter(dataSource))
  360. {
  361. for (int j = size - 1; j >= 0; j--)
  362. for (int i = 0; i < size; i++)
  363. {
  364. writer.Write((byte)(i * 255f / (size - 1)));
  365. writer.Write((byte)((size - j - 1) * 255f / (size - 1)));
  366. Color c = bmp.GetPixel(i * bmpWidth / size, j * bmpHeight / size);
  367. if (c.A < 0xFF)
  368. alpha = true;
  369. float fred = ((float)c.R) / 255f;
  370. float fgreen = ((float)c.G) / 255f;
  371. float fblue = ((float)c.B) / 255f;
  372. float fblack = 1 - Math.Max(fred, Math.Max(fgreen, fblue));
  373. float fcyan = (1 - fred - fblack) / (1 - fblack);
  374. float fmagenta = (1 - fgreen - fblack) / (1 - fblack);
  375. float fyellow = (1 - fblue - fblack) / (1 - fblack);
  376. byte black = (byte)(fblack * 255);
  377. byte cyan = (byte)(fcyan * 255);
  378. byte magenta = (byte)(fmagenta * 255);
  379. byte yellow = (byte)(fyellow * 255);
  380. writer.Write(cyan);
  381. writer.Write(magenta);
  382. writer.Write(yellow);
  383. writer.Write(black);
  384. }
  385. writer.Flush();
  386. dataSource.Position = 0;
  387. HashableByteArray hashableByteArray = new HashableByteArray(dataSource.ToArray());
  388. long pos2 = 0;
  389. if (gradientHashSet.TryGetValue(hashableByteArray, out pos2))
  390. return pos2;
  391. else gradientHashSet[hashableByteArray] = pos;
  392. WriteLn(pdf, sh.ToString());
  393. WritePDFStream(pdf, dataSource, pageShadings.Count, true, encrypted, false, true);
  394. }
  395. }
  396. return pos;
  397. }
  398. private long GetShadingBrushGrayAlpha(int size, Bitmap bmp)
  399. {
  400. long pos = UpdateXRef();
  401. int bmpWidth = bmp.Width;
  402. int bmpHeight = bmp.Height;
  403. StringBuilder sh = new StringBuilder();
  404. sh.AppendLine(ObjNumber(pos));
  405. sh.Append("<< /ShadingType 5 /ColorSpace");
  406. sh.Append(" /DeviceGray");
  407. sh.Append(" /VerticesPerRow ").Append(size);// FGradientInterpolationPoints);
  408. sh.Append(" /BitsPerCoordinate 8");
  409. sh.Append(" /BitsPerComponent 8");
  410. sh.Append(" /Decode [0 1.0 0 1.0 0 1.0]");
  411. using (MemoryStream dataSource = new MemoryStream())
  412. {
  413. using (BinaryWriter writer = new BinaryWriter(dataSource))
  414. {
  415. for (int j = size - 1; j >= 0; j--)
  416. for (int i = 0; i < size; i++)
  417. {
  418. writer.Write((byte)(i * 255f / (size - 1)));
  419. writer.Write((byte)((size - j - 1) * 255f / (size - 1)));
  420. Color c = bmp.GetPixel(i * bmpWidth / size, j * bmpHeight / size);
  421. writer.Write(c.A);
  422. }
  423. writer.Flush();
  424. dataSource.Position = 0;
  425. HashableByteArray hashableByteArray = new HashableByteArray(dataSource.ToArray());
  426. long pos2 = 0;
  427. if (gradientHashSet.TryGetValue(hashableByteArray, out pos2))
  428. return pos2;
  429. else gradientHashSet[hashableByteArray] = pos;
  430. WriteLn(pdf, sh.ToString());
  431. WritePDFStream(pdf, dataSource, pageAlphaShading.Count, true, encrypted, false, true);
  432. }
  433. }
  434. return pos;
  435. }
  436. private long GetShadingBrushRGB(int size, Bitmap bmp, out bool alpha)
  437. {
  438. alpha = false;
  439. long pos;
  440. int bmpWidth = bmp.Width;
  441. int bmpHeight = bmp.Height;
  442. using (MemoryStream dataSource = new MemoryStream())
  443. {
  444. using (BinaryWriter writer = new BinaryWriter(dataSource))
  445. {
  446. for (int j = size - 1; j >= 0; j--)
  447. for (int i = 0; i < size; i++)
  448. {
  449. writer.Write((byte)(i * 255f / (size - 1)));
  450. writer.Write((byte)((size - j - 1) * 255f / (size - 1)));
  451. Color c = bmp.GetPixel(i * bmpWidth / size, j * bmpHeight / size);
  452. if (c.A < 0xFF)
  453. alpha = true;
  454. writer.Write(c.R);
  455. writer.Write(c.G);
  456. writer.Write(c.B);
  457. }
  458. writer.Flush();
  459. dataSource.Position = 0;
  460. HashableByteArray hashableByteArray = new HashableByteArray(dataSource.ToArray());
  461. long pos2 = 0;
  462. if (gradientHashSet.TryGetValue(hashableByteArray, out pos2))
  463. return pos2;
  464. else
  465. {
  466. pos = UpdateXRef();
  467. gradientHashSet[hashableByteArray] = pos;
  468. //begin init
  469. StringBuilder sh = new StringBuilder();
  470. sh.AppendLine(ObjNumber(pos));
  471. sh.Append("<< /ShadingType 5 /ColorSpace");
  472. sh.Append(" /DeviceRGB");
  473. sh.Append(" /VerticesPerRow ").Append(size);// FGradientInterpolationPoints);
  474. sh.Append(" /BitsPerCoordinate 8");
  475. sh.Append(" /BitsPerComponent 8");
  476. sh.Append(" /Decode [0 1.0 0 1.0 0 1.0 0 1.0 0 1.0]");
  477. //end
  478. WriteLn(pdf, sh.ToString());
  479. WritePDFStream(pdf, dataSource, pos, true, encrypted, false, true);
  480. }
  481. }
  482. }
  483. return pos;
  484. }
  485. /// <summary>
  486. /// returns true if this gradient is fillable by gradient grid
  487. /// </summary>
  488. /// <returns></returns>
  489. private bool IsFillableGradientGrid(FillBase fillBase)
  490. {
  491. if (gradientQuality == GradientQualityEnum.Image)
  492. return false;
  493. if (fillBase is LinearGradientFill || fillBase is PathGradientFill)
  494. return true;
  495. return false;
  496. }
  497. // The obj.Border parameter has been changed to obj for the possibility of using custom lines inside the method
  498. private void StrokePDFGraphicsPath(SizeF rect, GraphicsPath path, PolyLineObject obj, bool closed, StringBuilder sb, CurvesInterpolationEnum curvesInterpolation,
  499. System.Drawing.Drawing2D.Matrix matrixTransform)
  500. {
  501. if (obj.Border.Color.A == 0)
  502. return;
  503. float[] m = matrixTransform.Elements;
  504. sb.AppendLine("q");
  505. sb.Append(FloatToString(m[0])).Append(" ")
  506. .Append(FloatToString(m[1])).Append(" ")
  507. .Append(FloatToString(m[2])).Append(" ")
  508. .Append(FloatToString(m[3])).Append(" ")
  509. .Append(FloatToString(m[4])).Append(" ")
  510. .Append(FloatToString(m[5])).AppendLine(" cm");
  511. GetPDFStrokeColor(obj.Border.Color, sb);
  512. //sb.Append(FloatToString(border.Width * PDF_DIVIDER)).AppendLine(" w").AppendLine("1 J");
  513. sb.Append(FloatToString(obj.Border.Width)).AppendLine(" w").AppendLine("0 J");
  514. if (obj.DashPattern.Count == 1)
  515. obj.Border.Style = LineStyle.Solid;
  516. sb.AppendLine(DrawPDFDash(obj.DashPattern.Count > 1 ? LineStyle.Custom : obj.Border.Style, obj.Border.Width, obj.DashPattern.Cast<float>().ToArray()));
  517. AppendPDFGraphicsPath(path, sb, curvesInterpolation);
  518. if (closed)
  519. sb.AppendLine("s");
  520. else
  521. sb.AppendLine("S");
  522. sb.AppendLine("Q");
  523. }
  524. private void StrokePDFGraphicsPath(GraphicsPath path, Pen pen, bool closed, StringBuilder sb, CurvesInterpolationEnum curvesInterpolation,
  525. System.Drawing.Drawing2D.Matrix matrixTransform)
  526. {
  527. try { if (pen.Color.A == 0) return; }
  528. catch { return; }
  529. float[] m = matrixTransform.Elements;
  530. sb.AppendLine("q");
  531. sb.Append(FloatToString(m[0])).Append(" ")
  532. .Append(FloatToString(m[1])).Append(" ")
  533. .Append(FloatToString(m[2])).Append(" ")
  534. .Append(FloatToString(m[3])).Append(" ")
  535. .Append(FloatToString(m[4])).Append(" ")
  536. .Append(FloatToString(m[5])).AppendLine(" cm");
  537. GetPDFStrokeColor(pen.Color, sb);
  538. sb.Append(FloatToString(pen.Width)).AppendLine(" w").AppendLine("0 J");
  539. LineStyle style;
  540. switch (pen.DashStyle)
  541. {
  542. case DashStyle.Dot: style = LineStyle.Dot; break;
  543. case DashStyle.Dash: style = LineStyle.Dash; break;
  544. case DashStyle.DashDot: style = LineStyle.DashDot; break;
  545. case DashStyle.DashDotDot: style = LineStyle.DashDotDot; break;
  546. case DashStyle.Custom: style = LineStyle.Custom; break;
  547. default: style = LineStyle.Solid; break;
  548. }
  549. float[] dashsWidth = null;
  550. if (pen.DashStyle == DashStyle.Custom)
  551. dashsWidth = pen.DashPattern;
  552. sb.AppendLine(DrawPDFDash(style, pen.Width * PDF_DIVIDER, dashsWidth));
  553. AppendPDFGraphicsPath(path, sb, curvesInterpolation);
  554. if (closed)
  555. sb.AppendLine("s");
  556. else
  557. sb.AppendLine("S");
  558. sb.AppendLine("Q");
  559. }
  560. private void WriteFunctionTypeColor(int num, Color c, StringBuilder sb)
  561. {
  562. sb.Append(" /C").Append(num).Append(" [ ");
  563. switch (ColorSpace)
  564. {
  565. case PdfColorSpace.RGB:
  566. GetPDFColor(c, sb);
  567. break;
  568. case PdfColorSpace.CMYK:
  569. GetCMYKColor(c, sb);
  570. break;
  571. default:
  572. throw new NotImplementedException();
  573. }
  574. sb.Append(" ]");
  575. }
  576. #endregion Private Methods
  577. #region Public Enums
  578. /// <summary>
  579. /// The enum of curves interpolation
  580. /// </summary>
  581. public enum CurvesInterpolationEnum
  582. {
  583. /// <summary>
  584. /// Export as curves, without interpolation
  585. /// </summary>
  586. Curves,
  587. /// <summary>
  588. /// Two points
  589. /// </summary>
  590. P002 = 2,
  591. /// <summary>
  592. /// Four points
  593. /// </summary>
  594. P004 = 4,
  595. /// <summary>
  596. /// Eight points
  597. /// </summary>
  598. P008 = 8,
  599. /// <summary>
  600. /// Sixteen points
  601. /// </summary>
  602. P016 = 16
  603. }
  604. /// <summary>
  605. /// The enum of gradient interpolation points
  606. /// </summary>
  607. public enum GradientInterpolationPointsEnum : int
  608. {
  609. /// <summary>
  610. /// Two points
  611. /// </summary>
  612. P002 = 2,
  613. /// <summary>
  614. /// Four points
  615. /// </summary>
  616. P004 = 4,
  617. /// <summary>
  618. /// Eight points
  619. /// </summary>
  620. P008 = 8,
  621. /// <summary>
  622. /// Sixteen points
  623. /// </summary>
  624. P016 = 16,
  625. /// <summary>
  626. /// Thirty two points
  627. /// </summary>
  628. P032 = 32,
  629. /// <summary>
  630. /// Sixty four points
  631. /// </summary>
  632. P064 = 64,
  633. /// <summary>
  634. /// One hundred and twenty eight points
  635. /// </summary>
  636. P128 = 128,
  637. /// <summary>
  638. /// Two hundred and fifty six points
  639. /// </summary>
  640. P256 = 256,
  641. }
  642. /// <summary>
  643. /// The quality of gradient export
  644. /// </summary>
  645. public enum GradientQualityEnum : int
  646. {
  647. /// <summary>
  648. /// Export as image
  649. /// </summary>
  650. Image = 0,
  651. /// <summary>
  652. /// Export as low quality gradient grid, max size of interpolation points is 32
  653. /// </summary>
  654. Low = 32,
  655. /// <summary>
  656. /// Export as medium quality gradient grid, max size of interpolation points is 128
  657. /// </summary>
  658. Medium = 128,
  659. /// <summary>
  660. /// Export as high quality gradient grid, max size of interpolation points is 256
  661. /// </summary>
  662. High = 256,
  663. }
  664. #endregion Public Enums
  665. #region Private Classes
  666. private sealed class HashableByteArray
  667. {
  668. #region Private Fields
  669. private readonly int hash;
  670. private readonly byte[] innerArray;
  671. #endregion Private Fields
  672. #region Public Properties
  673. public byte[] InnerArray
  674. {
  675. get
  676. {
  677. return innerArray;
  678. }
  679. }
  680. #endregion Public Properties
  681. #region Public Constructors
  682. public HashableByteArray(byte[] arr)
  683. {
  684. hash = -1668713423;
  685. innerArray = arr;
  686. for (int i = 0; i < arr.Length; i++) unchecked
  687. {
  688. hash = hash * -1521134295 + arr[i].GetHashCode();
  689. }
  690. }
  691. #endregion Public Constructors
  692. #region Public Methods
  693. public override bool Equals(object obj)
  694. {
  695. return Equals(obj as HashableByteArray);
  696. }
  697. public bool Equals(HashableByteArray obj)
  698. {
  699. if (obj == null || GetHashCode() != obj.GetHashCode() || innerArray == null || obj.innerArray == null || innerArray.Length != obj.innerArray.Length)
  700. return false;
  701. for (int i = 0; i < innerArray.Length; i++)
  702. if (innerArray[i] != obj.innerArray[i])
  703. return false;
  704. return true;
  705. }
  706. public override int GetHashCode()
  707. {
  708. return hash;
  709. }
  710. #endregion Public Methods
  711. }
  712. #endregion Private Classes
  713. }
  714. }