context: /*! MoonPdfLib - Provides a WPF user control to display PDF files
  Copyright (C) 2013  (see AUTHORS file)
  
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  !*/
  using MoonPdfLib.Helper;
  /*
   * 2013 - Modified and extended version of W. Jordan's code (see AUTHORS file)
   */
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Drawing;
  using System.Drawing.Imaging;
  using System.Linq;
  using System.Runtime.InteropServices;
  using System.Text;
  
  namespace MoonPdfLib.MuPdf
  {
  	public static class MuPdfWrapper
  	{
  		/// <summary>
  		/// Extracts a PDF page as a Bitmap for a given pdf filename and a page number.
  		/// </summary>
  		/// <param name="pageNumber">Page number, starting at 1</param>
  		/// <param name="zoomFactor">Used to get a smaller or bigger Bitmap, depending on the specified value</param>
          /// <param name="password">The password for the pdf file (if required)</param>
          public static Bitmap ExtractPage(IPdfSource source, int pageNumber, float zoomFactor = 1.0f, string password = null)
  		{
  			var pageNumberIndex = Math.Max(0, pageNumber - 1); // pages start at index 0
  
              using (var stream = new PdfFileStream(source))
  			{
                  ValidatePassword(stream.Document, password);
  
  				IntPtr p = NativeMethods.LoadPage(stream.Document, pageNumberIndex); // loads the page
  				var bmp = RenderPage(stream.Context, stream.Document, p, zoomFactor);
  				NativeMethods.FreePage(stream.Document, p); // releases the resources consumed by the page
  
  				return bmp;
  			}
  		}
  
  		/// <summary>
  		/// Gets the page bounds for all pages of the given PDF. If a relevant rotation is supplied, the bounds will
  		/// be rotated accordingly before returning.
  		/// </summary>
  		/// <param name="rotation">The rotation that should be applied</param>
          /// <param name="password">The password for the pdf file (if required)</param>
          /// <returns></returns>
  		public static System.Windows.Size[] GetPageBounds(IPdfSource source, ImageRotation rotation = ImageRotation.None, string password = null)
  		{
  			Func<double, double, System.Windows.Size> sizeCallback = (width, height) => new System.Windows.Size(width, height);
  			
  			if( rotation == ImageRotation.Rotate90 || rotation == ImageRotation.Rotate270 )
  				sizeCallback = (width, height) => new System.Windows.Size(height, width); // switch width and height
  
              using (var stream = new PdfFileStream(source))
  			{
                  ValidatePassword(stream.Document, password);
  
  				var pageCount = NativeMethods.CountPages(stream.Document); // gets the number of pages in the document
                  var resultBounds = new System.Windows.Size[pageCount];
  
  				for (int i = 0; i < pageCount; i++)
  				{
  					IntPtr p = NativeMethods.LoadPage(stream.Document, i); // loads the page
  					Rectangle pageBound = NativeMethods.BoundPage(stream.Document, p);
  
  					resultBounds[i] = sizeCallback(pageBound.Width, pageBound.Height);
  
  					NativeMethods.FreePage(stream.Document, p); // releases the resources consumed by the page
  				}
  
                  return resultBounds;
  			}
  		}
  
  		/// <summary>
  		/// Return the total number of pages for a give PDF.
  		/// </summary>
          public static int CountPages(IPdfSource source, string password = null)
  		{
  			using (var stream = new PdfFileStream(source))
  			{
                  ValidatePassword(stream.Document, password);
  
  				return NativeMethods.CountPages(stream.Document); // gets the number of pages in the document
  			}
  		}
  
          public static bool NeedsPassword(IPdfSource source)
          {
              using (var stream = new PdfFileStream(source))
              {
                  return NeedsPassword(stream.Document);
              }
          }
  
          private static void ValidatePassword(IntPtr doc, string password)
          {
              if (NeedsPassword(doc) && NativeMethods.AuthenticatePassword(doc, password) == 0)
                  throw new MissingOrInvalidPdfPasswordException();
          }
  
          private static bool NeedsPassword(IntPtr doc)
          {
              return NativeMethods.NeedsPassword(doc) != 0;
          }
  
  		static Bitmap RenderPage(IntPtr context, IntPtr document, IntPtr page, float zoomFactor)
  		{
  			Rectangle pageBound = NativeMethods.BoundPage(document, page);
  			Matrix ctm = new Matrix();
  			IntPtr pix = IntPtr.Zero;
  			IntPtr dev = IntPtr.Zero;
  
              var currentDpi = DpiHelper.GetCurrentDpi();
              var zoomX = zoomFactor * (currentDpi.HorizontalDpi / DpiHelper.DEFAULT_DPI);
              var zoomY = zoomFactor * (currentDpi.VerticalDpi / DpiHelper.DEFAULT_DPI);
  
  			// gets the size of the page and multiplies it with zoom factors
              int width = (int)(pageBound.Width * zoomX);
  			int height = (int)(pageBound.Height * zoomY);
  
  			// sets the matrix as a scaling matrix (zoomX,0,0,zoomY,0,0)
              ctm.A = zoomX;
              ctm.D = zoomY;
  
  			// creates a pixmap the same size as the width and height of the page
  			pix = NativeMethods.NewPixmap(context, NativeMethods.FindDeviceColorSpace(context, "DeviceRGB"), width, height);
  			// sets white color as the background color of the pixmap
  			NativeMethods.ClearPixmap(context, pix, 0xFF);
  
  			// creates a drawing device
  			dev = NativeMethods.NewDrawDevice(context, pix);
  			// draws the page on the device created from the pixmap
  			NativeMethods.RunPage(document, page, dev, ctm, IntPtr.Zero);
  
  			NativeMethods.FreeDevice(dev); // frees the resources consumed by the device
  			dev = IntPtr.Zero;
  
  			// creates a colorful bitmap of the same size of the pixmap
  			Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
  			var imageData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bmp.PixelFormat);
  			unsafe
  			{ // converts the pixmap data to Bitmap data
  				byte* ptrSrc = (byte*)NativeMethods.GetSamples(context, pix); // gets the rendered data from the pixmap
  				byte* ptrDest = (byte*)imageData.Scan0;
  				for (int y = 0; y < height; y++)
  				{
  					byte* pl = ptrDest;
  					byte* sl = ptrSrc;
  					for (int x = 0; x < width; x++)
  					{
  						//Swap these here instead of in MuPDF because most pdf images will be rgb or cmyk.
  						//Since we are going through the pixels one by one anyway swap here to save a conversion from rgb to bgr.
  						pl[2] = sl[0]; //b-r
  						pl[1] = sl[1]; //g-g
  						pl[0] = sl[2]; //r-b
  						//sl[3] is the alpha channel, we will skip it here
  						pl += 3;
  						sl += 4;
  					}
  					ptrDest += imageData.Stride;
  					ptrSrc += width * 4;
  				}
  			}
              bmp.UnlockBits(imageData);
  
  			NativeMethods.DropPixmap(context, pix);
              bmp.SetResolution(currentDpi.HorizontalDpi, currentDpi.VerticalDpi);
  
  			return bmp;
  		}
  
  		/// <summary>
  		/// Helper class for an easier disposing of unmanaged resources
  		/// </summary>
  		private sealed class PdfFileStream : IDisposable
  		{
  			const uint FZ_STORE_DEFAULT = 256 << 20;
  
  			public IntPtr Context { get; private set; }
  			public IntPtr Stream { get; private set; }
  			public IntPtr Document { get; private set; }
              
              public PdfFileStream(IPdfSource source)
              {
                  if (source is FileSource)
                  {
  
gt:                     var fs = (FileSource)source;