I am using a C# FreeImage wrapper.
I am trying to open a PDF file containing images, and "extract" those images into windows Bitmap objects. I am following the guidelines described in articles on the internet, following loosely the following pattern:
byte[] bytes = GetPdfFile();
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(bytes);
int pageNumber = 0;
for (int i = 0; i <= reader.XrefSize - 1; i++)
{
pdfObj = reader.GetPdfObject(i);
if ((pdfObj != null) && pdfObj.IsStream())
{
pdfStream = (iTextSharp.text.pdf.PdfStream)pdfObj;
iTextSharp.text.pdf.PdfObject subtype = pdfStream.Get(iTextSharp.text.pdf.PdfName.SUBTYPE);
if ((subtype != null) && subtype.ToString().Equals(iTextSharp.text.pdf.PdfName.IMAGE.ToString()))
{
pageNumber++;
byte[] imgBytes = iTextSharp.text.pdf.PdfReader.GetStreamBytesRaw((iTextSharp.text.pdf.PRStream)pdfStream);
if ((imgBytes != null))
{
// in my case images are in TIF Group 4 format
using (MemoryStream ms = new MemoryStream(imgBytes))
{
FreeImageAPI.FIBITMAP bmp = FreeImageAPI.FreeImage.LoadFromStream(ms);
// in my case bmp with IsNull = true is returned
if (!bmp.IsNull)
{
using (MemoryStream msOut = new MemoryStream())
{
FreeImageAPI.FreeImage.SaveToStream(bmp, msOut, ... ); // etc.
}
}
}
}
}
}
}
Does anybody have a suggestion on how to troubleshoot this, given that no exception is returned - some kind of GetLastError FreeImage function?
Thank you
Although it doesn't appear that the SetOutputMessage function was added to the .NET wrapper library, it is possible to call it directly from the FreeImage library.
That is, adding the function to your code using:
[DllImport("FreeImage", EntryPoint = "FreeImage_SetOutputMessage")]
internal static extern void SetOutputMessage(OutputMessageFunction outputMessageFunction);
The following code shows an Exception being thrown from the Save function, rather than returning false (with the following console output):
Test 1... Success
Test 2...
Unhandled Exception: System.Exception: only 24-bit highcolor or 8-bit greyscale/palette bitmaps can be saved as JPEG
at ConsoleApplication1.Program.OutputMessage(FREE_IMAGE_FORMAT format, String message) ...\ConsoleApplication1\Program.cs:line 35
at FreeImageAPI.FreeImage.Save(FREE_IMAGE_FORMAT fif, FIBITMAP dib, String filename, FREE_IMAGE_SAVE_FLAGS flags)
at ConsoleApplication1.Program.Main(String[] args) in ...\ConsoleApplication1\Program.cs:line 28
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using FreeImageAPI;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{ FIBITMAP bitmap;
bool success;
SetOutputMessage(OutputMessage);
Console.Write("Test 1... ");
bitmap = FreeImage.CreateFromBitmap(new Bitmap(320, 240, PixelFormat.Format24bppRgb));
success = FreeImage.Save(FREE_IMAGE_FORMAT.FIF_JPEG, bitmap, "output.jpg", FREE_IMAGE_SAVE_FLAGS.JPEG_QUALITYNORMAL);
FreeImage.Unload(bitmap);
Console.WriteLine("Success");
Console.Write("Test 2... ");
bitmap = FreeImage.CreateFromBitmap(new Bitmap(320, 240));
success = FreeImage.Save(FREE_IMAGE_FORMAT.FIF_JPEG, bitmap, "output.jpg", FREE_IMAGE_SAVE_FLAGS.JPEG_QUALITYNORMAL);
FreeImage.Unload(bitmap);
Console.WriteLine("Success");
}
static void OutputMessage(FREE_IMAGE_FORMAT format, string message)
{ throw new Exception(message);
}
[DllImport("FreeImage", EntryPoint = "FreeImage_SetOutputMessage")]
internal static extern void SetOutputMessage(OutputMessageFunction outputMessageFunction);
}
}
Related
I'm trying to load an asset using Resources.load() but it always returns null.
Here is my folder structure: https://imgur.com/a/z8KObW1
When I use Resources.load() in my unity project, it works without any problem.
But when I create a seperate project in visual studio with the unityengine dll's, and use Resources.load() in one of the .cs files under the Source folder it always seems to return null, no matter what I try.
When I placed the Assets folder inside of the Source folder it returned null and when I placed the .cs files in the Asset folder it also returned null. I can't seem to figure out why. I also tried to fetch the path that the Resources.load() starts at, but I can't seem to figure it out. Does it start from the .dll or from the .cs file under the Source?
public static void Create_Manager() {
GameObject networkManagerObj = new GameObject();
networkManagerObj.name = "_NetworkManager";
networkManager = networkManagerObj.AddComponent<NetworkManager>();
networkManager.playerPrefab = Resources.Load("Player") as GameObject;
networkManager.dontDestroyOnLoad = true;
networkManager.runInBackground = true;
networkManager.autoCreatePlayer = true;
}
All I need is for the Resources.load() to work with my seperate project/dll. Does anyone know how to do so?
Here's the code that a coworker of mine wrote and mostly explained to me. I've omitted a lot of the code that dealt with what this class was for (handling touch input), leaving only the sections related to loading an image (used to display a touch point on screen).
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
using HedgehogTeam.EasyTouch;
namespace TouchControls
{
/// <summary>Helper to control touch-based input.</summary>
public static class Utility
{
const string imageNamespace = "TouchControls.";
const string imageResourceFolder = "TouchControls/Images/";
/// <summary>Static helper to contain texture resources.</summary>
public static class Texture
{
/// <summary>The texture used to represent a second finger when simulating touches.</summary>
public static Texture2D SecondFinger
{
get
{
if (null == secondFinger)
secondFinger = LoadImage(secondFingerFilename);
return secondFinger;
}
}
static Texture2D secondFinger = null;
const string secondFingerFilename = "secondFinger.png";
}
static Assembly ExecutingAssembly
{
get
{
if (null == executingAssembly)
{
executingAssembly = Assembly.GetExecutingAssembly();
}
return executingAssembly;
}
}
static Assembly executingAssembly = null;
static Stream GetImageStream(string imageName) { return ExecutingAssembly.GetManifestResourceStream(imageName); }
static Texture2D LoadImage(string filename, TextureWrapMode wrapMode = TextureWrapMode.Clamp, FilterMode filterMode = FilterMode.Bilinear, bool useMipMaps = true, TextureFormat format = TextureFormat.ARGB32)
{
Texture2D texture = Resources.Load<Texture2D>(imageResourceFolder + Path.GetFileNameWithoutExtension(!string.IsNullOrEmpty(filename) ? filename : string.Empty));
try
{
// Didn't find it in resources in the project so try to find it in the library manifest....
if (null == texture)
{
using (Stream stream = GetImageStream(imageNamespace + filename))
{
texture = new Texture2D(0, 0, format, useMipMaps);
if (!texture.LoadImage(GetImageBuffer(stream)))
throw new NotSupportedException(filename);
texture.wrapMode = wrapMode;
texture.filterMode = filterMode;
}
}
else // ensure it is read/write enabled...
{
Texture2D invertedTexture = new Texture2D(texture.width, texture.height, texture.format, 1 < texture.mipmapCount);
invertedTexture.SetPixels32(texture.GetPixels32());
invertedTexture.Apply(true);
texture = invertedTexture;
}
}
catch
{
// Something went wrong so make a magenta 4 pixel texture.
texture = new Texture2D(2, 2, TextureFormat.ARGB32, false);
texture.SetPixels(0, 0, 2, 2, Enumerable.Repeat(Color.magenta, 4).ToArray());
}
texture.Apply(true);
return texture;
}
static byte[] GetImageBuffer(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
return buffer;
}
}
}
It first attempts to load an image out of the Resources folder (as you're familiar with) at a given location: this allows the image to be overridden by the local project, if desired. If that image isn't found, then it loads one out of the DLL. I am not sure where this image is located, but as the path reference is in the code, I'm sure it works out sensibly enough (the key part he had informed me was how to insure that the file got included in the DLL, rather than where it was located).
The important chunk for loading it out of the DLL is this section:
static Stream GetImageStream(string imageName) { return ExecutingAssembly.GetManifestResourceStream(imageName); }
//...
using (Stream stream = GetImageStream(imageNamespace + filename))
{
texture = new Texture2D(0, 0, format, useMipMaps);
if (!texture.LoadImage(GetImageBuffer(stream)))
throw new NotSupportedException(filename);
texture.wrapMode = wrapMode;
texture.filterMode = filterMode;
}
//...
texture.Apply(true);
return texture;
I'm trying to convert a .tif file to .jpg but I get the following exception:
System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.
I'm running this on a Windows 10 machine.
How can I fix this error?
This is my program:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using Encoder = System.Drawing.Imaging.Encoder;
namespace Test
{
class Program
{
static void Main(string[] args)
{
SaveAsJpeg(#"C:\Temp\Target\test_converted.jpg", 70);
if (!System.Diagnostics.Debugger.IsAttached) return;
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
public static FileInfo SaveAsJpeg(string newFilePath, long quality)
{
// File to convert
var path = #"C:\Temp\test.tif";
using (var stream = new MemoryStream(File.ReadAllBytes(path)))
{
using (var imageToConvert = Image.FromStream(stream))
{
// This line throws the error
imageToConvert.Save(newFilePath, GetDecoder(ImageFormat.Jpeg), GetQualityEncoderParameters(quality));
}
}
return new FileInfo(newFilePath);
}
private static ImageCodecInfo GetDecoder(ImageFormat format)
{
var codecs = ImageCodecInfo.GetImageDecoders();
return codecs.FirstOrDefault(codec => codec.FormatID == format.Guid);
}
private static EncoderParameters GetQualityEncoderParameters(long quality)
{
var qualityParameter = new EncoderParameter(Encoder.Quality, quality);
var encoderParameters = new EncoderParameters(1) { Param = { [0] = qualityParameter } };
return encoderParameters;
}
}
}
So far I have...
Made sure that C:\Temp\Target exists
Made sure that Everyone have full read/write access to C:\Temp\Target
These are the file properties:
Edit:
After trying the same code with a different image, and no errors were thrown, there's probably an issue with the image i'm trying to convert. An image with the following properties is successfully converted:
I don't know how, but this seems to do the trick. No errors are thrown, and the image is converted successfully:
public static FileInfo SaveAsJpeg(string newFilePath, long quality)
{
var path = #"C:\Temp\test.tif";
using (var stream = new MemoryStream(File.ReadAllBytes(path)))
{
var imageToConvert = Image.FromStream(stream);
using (imageToConvert = new Bitmap(imageToConvert))
{
imageToConvert.Save(newFilePath, GetDecoder(ImageFormat.Jpeg), GetQualityEncoderParameters(quality));
}
}
return new FileInfo(newFilePath);
}
If anyone could elaborate on why this works, that would be great.
I am trying to replicate https://ffmpeg.org/doxygen/3.0/doc_2examples_2metadata_8c_source.html in C# using the wrapper from https://github.com/Ruslan-B/FFmpeg.AutoGen
I can open and read some properties of the file just fine, however tag is always null, even after the call to av_dict_get
My code is as follows
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using FFmpeg.AutoGen;
namespace ffmpeg_test
{
class Program
{
static void Main(string[] args)
{
var currentPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
var libPath = Path.Combine(currentPath, "lib");
SetDllDirectory(libPath);
ffmpeg.av_register_all();
ffmpeg.avcodec_register_all();
DoRiskyThings();
}
private static unsafe void DoRiskyThings()
{
var pFormatContext = ffmpeg.avformat_alloc_context();
if (ffmpeg.avformat_open_input(&pFormatContext, "01 - 999,999.opus", null, null) != 0)
throw new ApplicationException(#"Could not open file.");
ffmpeg.avformat_find_stream_info(pFormatContext, null);
AVStream* pStream = null;
pStream = pFormatContext->streams[0];
var codecContext = *pStream->codec;
Console.WriteLine($"codec name: {ffmpeg.avcodec_get_name(codecContext.codec_id)}");
Console.WriteLine($"number of streams: {pFormatContext->nb_streams}");
//attempting to replicate https://ffmpeg.org/doxygen/3.0/doc_2examples_2metadata_8c_source.html
AVDictionaryEntry* tag = null;
tag = ffmpeg.av_dict_get(pFormatContext->metadata, "", null, 2);
while (tag != null)
{
tag = ffmpeg.av_dict_get(pFormatContext->metadata, "", tag, 2);
Console.WriteLine(Encoding.UTF8.GetString(tag->key,100));
//tag->key and //tag->value are byte pointers
}
}
[DllImport("kernel32", SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);
}
}
You need to marshal strings. This is works like a charm:
var url = #"http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4";
var pFormatContext = ffmpeg.avformat_alloc_context();
if (ffmpeg.avformat_open_input(&pFormatContext, url, null, null) != 0)
throw new ApplicationException(#"Could not open file.");
if (ffmpeg.avformat_find_stream_info(pFormatContext, null) != 0)
throw new ApplicationException(#"Could not find stream info");
AVDictionaryEntry* tag = null;
while ((tag = ffmpeg.av_dict_get(pFormatContext->metadata, "", tag, ffmpeg.AV_DICT_IGNORE_SUFFIX)) != null)
{
var key = Marshal.PtrToStringAnsi((IntPtr) tag->key);
var value = Marshal.PtrToStringAnsi((IntPtr) tag->value);
Console.WriteLine($"{key} = {value}");
}
What I am trying to do?
I am trying to create a video streaming website using ASP.net web API.
Problem
The api is working fine when running on local but when uploaded on live server then it does not play on chrome at all and too slow on firefox, opera
Live Demo of Problem
Please open this link in Chrome, firefox and you will see the difference.
Note: Live Demo link will be available till the problem is solved then I will remove it
GitHub link of source code
Please download the code from Github and in its videos folder please put a mp4 file of size at least 700mb or more
Please name it avan.mp4
Here is the code that I am using:
Coding part here
Code Credits: Robert Vandenberg Huang: Codeproject
Those who can not download the code from GitHub, I am providing everything here
Index.html
<video id="mainPlayer" width="640" height="360"
autoplay="autoplay" controls="controls" onloadeddata="onLoad()">
<source src="api/media/play?f=avan.mp4" />
</video>
Web API Code
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Web.Configuration;
using System.Web.Http;
namespace VideoLIb.Controllers
{
//http://www.codeproject.com/Articles/820146/HTTP-Partial-Content-In-ASP-NET-Web-API-Video
public class MediaController : ApiController
{
#region Fields
// This will be used in copying input stream to output stream.
public const int ReadStreamBufferSize = 1024 * 512;//* 1024;
// We have a read-only dictionary for mapping file extensions and MIME names.
public static readonly IReadOnlyDictionary<string, string> MimeNames;
// We will discuss this later.
public static readonly IReadOnlyCollection<char> InvalidFileNameChars;
// Where are your videos located at? Change the value to any folder you want.
public static readonly string InitialDirectory;
#endregion
#region Constructors
static MediaController()
{
var mimeNames = new Dictionary<string, string>();
mimeNames.Add(".mp3", "audio/mpeg"); // List all supported media types;
mimeNames.Add(".mp4", "video/mp4");
mimeNames.Add(".ogg", "application/ogg");
mimeNames.Add(".ogv", "video/ogg");
mimeNames.Add(".oga", "audio/ogg");
mimeNames.Add(".wav", "audio/x-wav");
mimeNames.Add(".webm", "video/webm");
MimeNames = new ReadOnlyDictionary<string, string>(mimeNames);
InvalidFileNameChars = Array.AsReadOnly(Path.GetInvalidFileNameChars());
InitialDirectory = #"~/videos"; /// directory that contains videos
}
#endregion
#region Actions
//http://localhost:58720/
[HttpGet]
public HttpResponseMessage Play(string f)
{
// This can prevent some unnecessary accesses.
// These kind of file names won't be existing at all.
if (string.IsNullOrWhiteSpace(f) || AnyInvalidFileNameChars(f))
throw new HttpResponseException(HttpStatusCode.NotFound);
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath(Path.Combine(InitialDirectory, f));
FileInfo fileInfo = new FileInfo(mappedPath);
if (!fileInfo.Exists)
throw new HttpResponseException(HttpStatusCode.NotFound);
long totalLength = fileInfo.Length;
RangeHeaderValue rangeHeader = base.Request.Headers.Range;
HttpResponseMessage response = new HttpResponseMessage();
response.Headers.AcceptRanges.Add("bytes");
// The request will be treated as normal request if there is no Range header.
if (rangeHeader == null || !rangeHeader.Ranges.Any())
{
response.StatusCode = HttpStatusCode.OK;
response.Content = new PushStreamContent((outputStream, httpContent, transpContext)
=>
{
using (outputStream) // Copy the file to output stream straightforward.
using (Stream inputStream = fileInfo.OpenRead())
{
try
{
inputStream.CopyTo(outputStream, ReadStreamBufferSize);
}
catch (Exception error)
{
Debug.WriteLine(error);
}
}
}, GetMimeNameFromExt(fileInfo.Extension));
response.Content.Headers.ContentLength = totalLength;
return response;
}
long start = 0, end = 0;
// 1. If the unit is not 'bytes'.
// 2. If there are multiple ranges in header value.
// 3. If start or end position is greater than file length.
if (rangeHeader.Unit != "bytes" || rangeHeader.Ranges.Count > 1 ||
!TryReadRangeItem(rangeHeader.Ranges.First(), totalLength, out start, out end))
{
response.StatusCode = HttpStatusCode.RequestedRangeNotSatisfiable;
response.Content = new StreamContent(Stream.Null); // No content for this status.
response.Content.Headers.ContentRange = new ContentRangeHeaderValue(totalLength);
response.Content.Headers.ContentType = GetMimeNameFromExt(fileInfo.Extension);
return response;
}
var contentRange = new ContentRangeHeaderValue(start, end, totalLength);
// We are now ready to produce partial content.
response.StatusCode = HttpStatusCode.PartialContent;
response.Content = new PushStreamContent((outputStream, httpContent, transpContext)
=>
{
using (outputStream) // Copy the file to output stream in indicated range.
using (Stream inputStream = fileInfo.OpenRead())
CreatePartialContent(inputStream, outputStream, start, end);
}, GetMimeNameFromExt(fileInfo.Extension));
response.Content.Headers.ContentLength = end - start + 1;
response.Content.Headers.ContentRange = contentRange;
return response;
}
#endregion
#region Others
private static bool AnyInvalidFileNameChars(string fileName)
{
return InvalidFileNameChars.Intersect(fileName).Any();
}
private static MediaTypeHeaderValue GetMimeNameFromExt(string ext)
{
string value;
if (MimeNames.TryGetValue(ext.ToLowerInvariant(), out value))
return new MediaTypeHeaderValue(value);
else
return new MediaTypeHeaderValue(MediaTypeNames.Application.Octet);
}
private static bool TryReadRangeItem(RangeItemHeaderValue range, long contentLength,
out long start, out long end)
{
if (range.From != null)
{
start = range.From.Value;
if (range.To != null)
end = range.To.Value;
else
end = contentLength - 1;
}
else
{
end = contentLength - 1;
if (range.To != null)
start = contentLength - range.To.Value;
else
start = 0;
}
return (start < contentLength && end < contentLength);
}
private static void CreatePartialContent(Stream inputStream, Stream outputStream,
long start, long end)
{
int count = 0;
long remainingBytes = end - start + 1;
long position = start;
byte[] buffer = new byte[ReadStreamBufferSize];
inputStream.Position = start;
do
{
try
{
if (remainingBytes > ReadStreamBufferSize)
count = inputStream.Read(buffer, 0, ReadStreamBufferSize);
else
count = inputStream.Read(buffer, 0, (int)remainingBytes);
outputStream.Write(buffer, 0, count);
}
catch (Exception error)
{
Debug.WriteLine(error);
break;
}
position = inputStream.Position;
remainingBytes = end - position + 1;
} while (position <= end);
}
#endregion
}
}
I'm using Ghostscript.NET, a handy C# wrapper for Ghostscript functionality. I have a batch of PDFs being sent from the clientside to be converted to images on the ASP .NET WebAPI server and returned to the client.
public static IEnumerable<Image> PdfToImagesGhostscript(byte[] binaryPdfData, int dpi)
{
List<Image> pagesAsImages = new List<Image>();
GhostscriptVersionInfo gvi = new GhostscriptVersionInfo(AppDomain.CurrentDomain.BaseDirectory + #"\bin\gsdll32.dll");
using (var pdfDataStream = new MemoryStream(binaryPdfData))
using (var rasterizer = new Ghostscript.NET.Rasterizer.GhostscriptRasterizer())
{
rasterizer.Open(pdfDataStream, gvi, true);
for (int i = 1; i <= rasterizer.PageCount; i++)
{
Image pageAsImage = rasterizer.GetPage(dpi, dpi, i); // Out of Memory Exception on this line
pagesAsImages.Add(pageAsImage);
}
}
return pagesAsImages;
}
This generally works fine (I generally use 500 dpi, which I know is high, but even dropping to 300 I can reproduce this error). But if I give it many PDFs from the clientside (150 1-page PDFs, for example) it will often hit an Out of Memory Exception in Ghostscript.NET Rasterizer. How can I overcome this? Should this be threaded? If so how would that work? Would it help to use the 64 bit version of GhostScript? Thanks in advance.
I'm new to this myself, on here looking for techniques.
According to the example in the documentation here, they show this:
for (int page = 1; page <= _rasterizer.PageCount; page++)
{
var docName = String.Format("Page-{0}.pdf", page);
var pageFilePath = Path.Combine(outputPath, docName);
var pdf = _rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber);
pdf.Save(pageFilePath);
pagesAsImages.Add(pdf);
}
It looks like you aren't saving your files.
I am still working at getting something similar to this to work on my end as well. Currently, I have 2 methods that I'm going to try, using the GhostscriptProcessor first:
private static void GhostscriptNetProcess(String fileName, String outputPath)
{
var version = Ghostscript.NET.GhostscriptVersionInfo.GetLastInstalledVersion();
var source = (fileName.IndexOf(' ') == -1) ? fileName : String.Format("\"{0}\"", fileName);
var gsArgs = new List<String>();
gsArgs.Add("-q");
gsArgs.Add("-dNOPAUSE");
gsArgs.Add("-dNOPROMPT");
gsArgs.Add("-sDEVICE=pdfwrite");
gsArgs.Add(String.Format(#"-sOutputFile={0}", outputPath));
gsArgs.Add(source);
var processor = new Ghostscript.NET.Processor.GhostscriptProcessor(version, false);
processor.Process(gsArgs.ToArray());
}
This version below is similar to yours, and what I started out using until I started finding other code examples:
private static void GhostscriptNetRaster(String fileName, String outputPath)
{
var version = Ghostscript.NET.GhostscriptVersionInfo.GetLastInstalledVersion();
using (var rasterizer = new Ghostscript.NET.Rasterizer.GhostscriptRasterizer())
{
rasterizer.Open(File.Open(fileName, FileMode.Open, FileAccess.Read), version, false);
for (int page = 0; page < rasterizer.PageCount; page++)
{
var img = rasterizer.GetPage(96, 96, page);
img.Save(outputPath);
}
}
}
Does that get you anywhere?
You don't have to rasterize all pages at the same GhostscriptRasterizer instance. Use disposable rasterizer on each page and collect results in List Image or List byte[] .
Example with results List of Jpeg encoded byte arrays.
List<byte[]> result = new List<byte[]>();
for (int i = 1; i <= pdfPagesCount; i++)
{
using (var pageRasterizer = new GhostscriptRasterizer())
{
pageRasterizer.Open(stream, gsVersion, true);
using (Image tempImage = pageRasterizer.GetPage(dpiX, dpiY, i))
{
var encoder = ImageCodecInfo.GetImageEncoders().First(c => c.FormatID == System.Drawing.Imaging.ImageFormat.Jpeg.Guid);
var encoderParams = new EncoderParameters() { Param = new[] { new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 95L) } };
using (MemoryStream memoryStream = new MemoryStream())
{
tempImage.Save(memoryStream, encoder, encoderParams);
result.Add(memoryStream.ToArray());
}
}
}
}
If you don't know number of pages in PDF you could call rasterizer one time, and get PageCount property.