Program.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Drawing;
  6. using System.Drawing.Imaging;
  7. using System.IO;
  8. using System.Runtime.InteropServices;
  9. using System.Text;
  10. using MuPDF;
  11. namespace Demo
  12. {
  13. static class Program
  14. {
  15. static void Main (string[] args) {
  16. HashSet<PdfObject> visited = new HashSet<PdfObject>(10);
  17. using (var cookie = new Cookie())
  18. using (var pageInfo = new StreamWriter("pages.txt", false)) { // Creates the context
  19. try {
  20. using (var doc = Context.Instance.OpenDocument("test.pdf")) {
  21. Console.WriteLine("Page count: " + doc.PageCount);
  22. Console.WriteLine("Object count: " + doc.ObjectCount);
  23. // test dict modification with Unicode (Chinese) PDF string
  24. doc.Info.Set(PdfNames.Subject, "MuPDF \u4E2D\u6587 Test Document");
  25. doc.Info.Set(PdfNames.Producer, "MuPDF#");
  26. doc.Info.Set(PdfNames.ModDate, DateTime.Now);
  27. PrintPdfDictionary(doc.Info, pageInfo, visited);
  28. var pn = doc.PageCount;
  29. for (int i = 0; i < pn; i++) {
  30. Console.WriteLine("Rendering page " + (i + 1));
  31. using (var p = doc.LoadPage(i)) {
  32. File.WriteAllBytes($"Contents{i + 1}.bin.txt", p.GetContentBytes());
  33. var b = p.Bound;
  34. pageInfo.WriteLine("Page bound: " + b);
  35. PrintPdfDictionary(p.PdfObject, pageInfo, visited);
  36. using (var resources = p.PdfObject.Locate(PdfNames.Resources, PdfNames.XObject) as PdfDictionary) {
  37. if (resources != null) {
  38. foreach (var item in resources) {
  39. if (item.Value.UnderlyingObject is PdfDictionary d) {
  40. pageInfo.WriteLine(item.Key + " is image");
  41. }
  42. }
  43. }
  44. else {
  45. pageInfo.WriteLine("No XObject detected");
  46. }
  47. }
  48. pageInfo.WriteLine("Text in page:");
  49. using (var tp = p.TextPage) {
  50. foreach (var block in tp) {
  51. foreach (var line in block) {
  52. pageInfo.WriteLine($"{line}({line.FirstCharacter.Font.Name}, {line.FirstCharacter.Size} {line.FirstCharacter.Font.Flags})");
  53. foreach (var span in line.GetSpans()) {
  54. pageInfo.WriteLine(span.ToString());
  55. }
  56. }
  57. }
  58. }
  59. using (var bmp = RenderPage(doc, cookie, p, b)) {
  60. Console.WriteLine("Saving picture: " + (i + 1) + ".png");
  61. bmp.Save((i + 1) + ".png"); // saves the bitmap to a file
  62. }
  63. }
  64. }
  65. }
  66. }
  67. catch (MuException ex) {
  68. Console.Error.WriteLine("Error occurred while rendering document!");
  69. Console.Error.WriteLine("Error code: " + ex.Code);
  70. Console.Error.WriteLine(ex.ToString());
  71. }
  72. }
  73. Console.WriteLine ("Program finished. Press any key to quit.");
  74. Console.ReadKey (true);
  75. }
  76. static void PrintPdfDictionary(PdfDictionary dict, StreamWriter writer, HashSet<PdfObject> visited, int indent = 0) {
  77. int count = dict.Count;
  78. writer.WriteLine(dict.Type + ": " + count + " items");
  79. for (int i = 0; i < count; i++) {
  80. var item = dict[i];
  81. using (PdfObject o = item.Value.UnderlyingObject) {
  82. writer.Write(new string(' ', indent << 1));
  83. writer.Write(item.Key + ": ");
  84. if (visited.Add(o) == false) {
  85. writer.WriteLine(item.Value.ToString());
  86. continue;
  87. }
  88. switch (o.TypeKind) {
  89. case Kind.Array:
  90. PrintPdfArray(writer, o as PdfArray);
  91. continue;
  92. case Kind.Dictionary:
  93. PrintPdfDictionary(o as PdfDictionary, writer, visited, indent + 1);
  94. continue;
  95. }
  96. writer.WriteLine(o);
  97. }
  98. }
  99. }
  100. static void PrintPdfArray(StreamWriter writer, PdfArray array) {
  101. int count = array.Count;
  102. StringBuilder sb = new StringBuilder(20)
  103. .Append('[');
  104. for (int i = 0; i < count; i++) {
  105. var item = array[i];
  106. if (i != 0) {
  107. sb.Append(' ');
  108. }
  109. sb.Append(item);
  110. }
  111. writer.WriteLine(sb.Append(']').ToString());
  112. }
  113. static Bitmap RenderPage (Document document, Cookie cookie, Page page, Box pageBound) {
  114. int width = (int)(pageBound.Width); // gets the size of the page
  115. int height = (int)(pageBound.Height);
  116. // creates a pixmap the same size as the width and height of the page
  117. using (Pixmap
  118. #if UNSAFE
  119. pix = Context.Instance.CreatePixmap(ColorspaceKind.Rgb, width, height)
  120. #else
  121. // use BGR color space to save byte conversions
  122. pix = Context.Instance.CreatePixmap(ColorspaceKind.Bgr, width, height)
  123. #endif
  124. ) {
  125. // sets white color as the background color of the pixmap
  126. pix.SetBackgroundWhite();
  127. // creates a drawing device
  128. using (var dev = Device.NewDraw(pix)) {
  129. // draws the page on the device created from the pixmap
  130. page.Run(dev, cookie);
  131. // ends the drawing procedure
  132. dev.Close();
  133. }
  134. // creates a colorful bitmap of the same size of the pixmap
  135. Bitmap bmp = new Bitmap (width, height, PixelFormat.Format24bppRgb);
  136. var imageData = bmp.LockBits (new System.Drawing.Rectangle (0, 0, width, height), ImageLockMode.ReadWrite, bmp.PixelFormat);
  137. #if UNSAFE
  138. // note: unsafe conversion from pixmap to bitmap
  139. // without the overhead of P/Invokes, the following code can run faster than the safe-conversion code below
  140. unsafe { // converts the pixmap data to Bitmap data
  141. byte* ptrSrc = (byte*)pix.Samples; // gets the rendered data from the pixmap
  142. byte* ptrDest = (byte*)imageData.Scan0;
  143. for (int y = 0; y < height; y++) {
  144. byte* pl = ptrDest;
  145. byte* sl = ptrSrc;
  146. for (int x = 0; x < width; x++) {
  147. //Swap these here instead of in MuPDF because most pdf images will be rgb or cmyk.
  148. //Since we are going through the pixels one by one anyway swap here to save a conversion from rgb to bgr.
  149. pl[2] = sl[0]; //b-r
  150. pl[1] = sl[1]; //g-g
  151. pl[0] = sl[2]; //r-b
  152. pl += 3;
  153. sl += 3;
  154. }
  155. ptrDest += imageData.Stride;
  156. ptrSrc += width * 3;
  157. }
  158. }
  159. #else
  160. // note: Safe-conversion from pixmap to bitmap
  161. var source = pix.Samples;
  162. var target = imageData.Scan0;
  163. for (int y = 0; y < height; y++) {
  164. // copy memory line by line
  165. NativeMethods.RtlMoveMemory(target, source, width * 3);
  166. target = (IntPtr)(target.ToInt64() + imageData.Stride);
  167. source = (IntPtr)(source.ToInt64() + width * 3);
  168. }
  169. #endif
  170. bmp.UnlockBits(imageData);
  171. return bmp;
  172. }
  173. }
  174. static class NativeMethods
  175. {
  176. [DllImport("kernel32.dll")]
  177. public static extern void RtlMoveMemory(IntPtr dest, IntPtr src, int byteCount);
  178. }
  179. }
  180. }