I've just started using ImageResizer to create thumbnails for my images using the code from their website below.
private void CreateThumbnail()
{
Dictionary<string, string> versions = new Dictionary<string, string>();
//Define the versions to generate
versions.Add("_thumb", "width=100&height=100&crop=auto&format=jpg"); //Crop to square thumbnail
versions.Add("_medium", "maxwidth=100&maxheight=100&format=jpg"); //Fit inside 400x400 area, jpeg
versions.Add("_large", "maxwidth=1900&maxheight=1900&format=jpg"); //Fit inside 1900x1200 area
//Loop through each uploaded file
foreach (string fileKey in HttpContext.Current.Request.Files.Keys)
{
HttpPostedFile file = HttpContext.Current.Request.Files[fileKey];
if (file.ContentLength <= 0) continue; //Skip unused file controls.
//Get the physical path for the uploads folder and make sure it exists
string uploadFolder = MapPath("~/Images");
if (!Directory.Exists(uploadFolder)) Directory.CreateDirectory(uploadFolder);
//Generate each version
foreach (string suffix in versions.Keys)
{
//Generate a filename (GUIDs are best).
string fileName = Path.Combine(uploadFolder, "AssetID" + suffix);
//Let the image builder add the correct extension based on the output file type
fileName = ImageBuilder.Current.Build(file, fileName, new ResizeSettings(versions[suffix]), false, true);
}
}
}
However, when I apply this code to a Pdf it crashes with the error 'File may be corrupted, empty, or may contain a PNG image with a single dimension greater than 65,535 pixels.'
What changes do I need to make to enable sizing a Pdf? I've gone through their documentation and although it seems that it will create a Thumbnail from a Pdf, the examples are all using images.
This is the list of Plugins including PdfRenderer
The PdfiumRenderer or PdfRenderer plugin (whichever you chose) is not installed. Thus, the primary decoder is failing to decode the image.
You must install a PDF plugin for this to work.
PdfiumRenderer is the better of the two.
See http://imageresizing.net/docs/v4/plugins/pdfiumrenderer
Related
I want to check if the file is an actual .png/.jpg file that can be displayed (for example not being a .txt file with a changed file extension to .png). I also want to check if the width and height of the image is in a valid range. How can I achieve this?
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
public async Task<IActionResult> Create(IFormFile image)
{
// validate image
}
First of all, don't try to use System.Drawing in .NET Core applications. It's deprecated and works only on Windows anyway. The MSDN docs themselves suggest using ImageSharp or SkiaSharp instead.
Image files start with bytes that identify the file format. You'll have to read at least some of the file's contents to read image metadata like the image size, resolution etc. You can use ImageSharp's Identify method to read only the format and image properties, without loading the entire image.
You can read an uploaded file's contents using IFormFile.OpenReadStream.
using var stream=image.OpenReadStream();
try
{
var imageInfo=Image.Identify(stream, out var format);
if(imageInfo!=null)
{
var formatName=format.Name;
var width=imageInfo.Width;
var height=imageInfo.Height;
}
}
catch(InvalidImageContentException exc)
{
//Invalid content ?
}
The format parameter is an IImageFormat value that contains information about the image format, including its name and mime types.
The IImageInfo object returned contains the image dimensions, pixel type, resolution etc.
The method documentation explains that the return value will be null if no suitable decoder is found:
The IImageInfo or null if a suitable info detector is not found.
But an exception will be thrown if the content is invalid:
InvalidImageContentException Image contains invalid content.
Without testing this, I assume that a text file will result in a null but a file with just a GIF header without valid content will result in an exception.
You can use ImageSharp to resize the image or convert it to another format. In that case it's not enough to just load the metadata. You can use Load to load the image from the stream, detect its format and then manipulate it.
using var stream=image.OpenReadStream();
var image=Image.Load(stream, out var format);
var formatName=format.Name;
if (notOk(formatName,image.Height,image.Width))
{
using var outStream=new MemoryStream();
image.Mutate(x => x.Resize(desiredWidth, desiredHeight));
image.SaveAsPng(outStream);
outStream.Position=0;
//Store the contents of `outStream`
}
Check the file for a known header. (Info from link also mentioned in this answer)
The first eight bytes of a PNG file always contain the following (decimal) values: 137 80 78 71 13 10 26 10
You can also check Path.GetExtension Method
Here's a simple sample:
public static readonly List<string> ImageExtensions = new List<string> { ".JPG", ".JPE", ".BMP", ".GIF", ".PNG" };
private void button_Click(object sender, RoutedEventArgs e)
{
var folder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var files = Directory.GetFiles(folder);
foreach(var f in files)
{
if (ImageExtensions.Contains(Path.GetExtension(f).ToUpperInvariant()))
{
// process image
}
}
}
I load a multiframe TIFF from a Stream in my C# application, and then save it using the Image.Save method. However, this only saves the TIFF with the first frame - how can I get it to save a multiframe tiff?
Since you don't provide any detailed information... just some general tips:
Multi-Frame TIFF are very complex files - for example every frame can have a different encoding... a single Bitmap/Image can't hold all frames with all relevant information (like encoding and similar) of such a file, only one at a time.
For loading you need to set parameter which tells the class which frame to load, otherwise it just loads the first... for some code see here.
Similar problems arise when saving multi-frame TIFFs - here you need to work with EncoderParameters and use SaveAdd etc. - for some working code see here.
Since the link to code provided by #Yahia is broken I have decided to post the code I ended up using.
In my case, the multi-frame TIFF already exists and all I need to do is to load the image, rotate by EXIF (if necessary) and save. I won't post the EXIF rotation code here, since it does not relate to this question.
using (Image img = System.Drawing.Image.FromStream(sourceStream))
{
using (FileStream fileStream = System.IO.File.Create(filePath))
{
int pages = img.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
if (pages == 1)
{
img.Save(fileStream, img.RawFormat); // if there is just one page, just save the file
}
else
{
var encoder = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders().First(x => x.MimeType == fileInfo.MediaType);
var encoderParams = new System.Drawing.Imaging.EncoderParameters(1);
encoderParams.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, Convert.ToInt32(System.Drawing.Imaging.EncoderValue.MultiFrame));
img.Save(fileStream, encoder, encoderParams); // save the first image with MultiFrame parameter
for (int f = 1; f < pages; f++)
{
img.SelectActiveFrame(FrameDimension.Page, f); // select active page (System.Drawing.Image.FromStream loads the first one by default)
encoderParams.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, Convert.ToInt32(System.Drawing.Imaging.EncoderValue.FrameDimensionPage));
img.SaveAdd(img, encoderParams); // save add with FrameDimensionPage parameter
}
}
}
}
sourceStream is a System.IO.MemoryStream which holds the byte array of the file content
filePath is absolute path to cache directory (something like 'C:/Cache/multiframe.tiff')
fileInfo is a model holding the actual byte array, fileName, mediaType and other data
I'm reading in a .docx file using the Novacode API, and am unable to create or display any images within the file to a WinForm app due to not being able to convert from a Novacode Picture (pic) or Image to a system image. I've noticed that there's very little info inside the pic itself, with no way to get any pixel data that I can see. So I have been unable to utilize any of the usual conversion ideas.
I've also looked up how Word saves images inside the files as well as Novacode source for any hints and I've come up with nothing.
My question then is is there a way to convert a Novacode Picture to a system one, or should I use something different to gather the image data like OpenXML? If so, would Novacode and OpenXML conflict in any way?
There's also this answer that might be another place to start.
Any help is much appreciated.
Okay. This is what I ended up doing. Thanks to gattsbr for the advice. This only works if you can grab all the images in order, and have descending names for all the images.
using System.IO.Compression; // Had to add an assembly for this
using Novacode;
// Have to specify to remove ambiguous error from Novacode
Dictionary<string, System.Drawing.Image> images = new Dictionary<string, System.Drawing.Image>();
void LoadTree()
{
// In case of previous exception
if(File.Exists("Images.zip")) { File.Delete("Images.zip"); }
// Allow the file to be open while parsing
using(FileStream stream = File.Open("Images.docx", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using(DocX doc = DocX.Load(stream))
{
// Work rest of document
// Still parse here to get the names of the images
// Might have to drag and drop images into the file, rather than insert through Word
foreach(Picture pic in doc.Pictures)
{
string name = pic.Description;
if(null == name) { continue; }
name = name.Substring(name.LastIndexOf("\\") + 1);
name = name.Substring(0, name.Length - 4);
images[name] = null;
}
// Save while still open
doc.SaveAs("Images.zip");
}
}
// Use temp zip directory to extract images
using(ZipArchive zip = ZipFile.OpenRead("Images.zip"))
{
// Gather all image names, in order
// They're retrieved from the bottom up, so reverse
string[] keys = images.Keys.OrderByDescending(o => o).Reverse().ToArray();
for(int i = 1; ; i++)
{
// Also had to add an assembly for ZipArchiveEntry
ZipArchiveEntry entry = zip.GetEntry(String.Format("word/media/image{0}.png", i));
if(null == entry) { break; }
Stream stream = entry.Open();
images[keys[i - 1]] = new Bitmap(stream);
}
}
// Remove temp directory
File.Delete("Images.zip");
}
I am building a Gallery for Floors in WPF application, I am reading Image Names from csv file and User will directly copy Images into Resources folder.
StreamReader streamReader = new StreamReader(
(Application.Current as PlayOnSurface.App).ProjectAmenitiesCSVPath);
// browse the csv file line by line until the end of the file
while (!streamReader.EndOfStream)
{
// for each line, split it with the split caractere (that may no be ';')
var splitLine = streamReader.ReadLine().Split('~');
if (int.Parse(splitLine[0].Trim())
== (Application.Current as PlayOnSurface.App).SelectedFloorID)
{
for (int i = 2; i < splitLine.Length; i++)
{
ImageList.Add(splitLine[i].Trim());
}
}
// map the splitted line with an entity
}
streamReader.Close();
btnPrev.IsEnabled = false;
if (ImageList.Count <= 1)
btnNext.IsEnabled = false;
imgGallery.Source = Utilities.LoadBitmapFromResource(
"Resources/Amenities/" + ImageList[0],
null);
Utilities code
public static BitmapImage LoadBitmapFromResource(string pathInApplication, Assembly assembly = null)
{
if (assembly == null)
{
assembly = Assembly.GetCallingAssembly();
}
if (pathInApplication[0] == '/')
{
pathInApplication = pathInApplication.Substring(1);
}
return new BitmapImage(new Uri(#"pack://application:,,,/" + assembly.GetName().Name + ";component/" + pathInApplication, UriKind.Absolute));
}
I can't add it in Resources folder in project and I can't embed resources because I want user to copy their file in Resources folder and update csv file after deployment.
My code will read it and display gallery but in that case WPF throws "Cannot locate resource" exception when loading the image on imgGallery.Source line in above code.
my csv file format:
1~FirstFloor~img1.png~img2.png~img3.png
2~SecondFloor~img1.png~img2.png~img3.png
3~ThirdFloor~img1.png~img2.png~img3.png
4~FourthFloor~img1.png~img2.png~img3.png
Maybe use the file absolute path, simply change the last line of the LoadBitmapFromResource function to:
return new BitmapImage(new Uri(assembly.GetName().CodeBase + pathInApplication, UriKind.Absolute));
this should do the fix.
Also you can try using the UriKind.Relative.
Good day
You're using an incorrect authority in your URI scheme. The application authority is to be used for data files which are known at compile time (aka static resources). You have to replace it with the siteoforigin authority.
I'd also recommend replacing your home-rolled text file format with a standard CSV file. Many programs such as Excel support generating comma-separated fields by default. While custom separators can be put, they usually require additional effort. Reading and parsing comma-separated files is also easier because many libraries are available to do these tasks.
I am a newbie to C#, I have a "Save to File" option in my program which saves the output of a richtextbox in a word document and when the user chooses this option, I have used saveFileDialogue box for the user to chose the filename and the location.
What I want is that every time when the user chooses this option the word document in which the output is saved has a pre-defined header and footer images...
Thanks a lot for your help in advance!
below is my 'Save to File" code.
private void menuItem7_Click(object sender, EventArgs e)
{
// Create a SaveFileDialog to request a path and file name to save to.
SaveFileDialog saveFile1 = new SaveFileDialog();
// Initialize the SaveFileDialog to specify the RTF extension for the file.
saveFile1.DefaultExt = "*.rtf";
saveFile1.Filter = "RTF Files|*.rtf";
// Determine if the user selected a file name from the saveFileDialog.
if (saveFile1.ShowDialog() == System.Windows.Forms.DialogResult.OK &&
saveFile1.FileName.Length > 0)
{
// Save the contents of the RichTextBox into the file.
richTextBox1.SaveFile(saveFile1.FileName,
RichTextBoxStreamType.PlainText);
}
}
First of all, create a function to take an image, width and height and return the rtf:
This is for a png
public string GetImage(string path, int width, int height)
{
var stream = new MemoryStream();
var img = Image.FromFile(path);
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
var bytes = stream.ToArray();
var str = BitConverter.ToString(bytes, 0).Replace("-", string.Empty);
var mpic = #"{\pict\pngblip\picw" + img.Width.ToString() + #"\pich" + img.Height.ToString() +
#"\picwgoa" + width.ToString() + #"\pichgoa" + height.ToString() +
#"\hex " + str + "}";
return mpic;
}
Now you need to insert this 'image' into the right place in the rtf. If you open your rtf file in notepad you should see something like this:
{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fnil\fcharset0
Microsoft Sans Serif;}} \viewkind4\uc1\pard\f0\fs17 MYTEXT\par }
If you wanted a quick and dirty method then get the rtf from the richTextBox into a string,
and insert your header image string after the deflang2057 followed by a '/par' to make a new line. Then insert your footer image string just before the closing '}'
something like this:
// Determine if the user selected a file name from the saveFileDialog.
if (saveFile1.ShowDialog() == System.Windows.Forms.DialogResult.OK &&
saveFile1.FileName.Length > 0)
{
var rtf = richTextBox1.Rtf.Insert(richTextBox1.Rtf.IndexOf("deflang2057") + 11, GetImage(#"c:\a.png", 5, 5) + #"\par");
using (var rtfFile = new StreamWriter(saveFile1.FileName))
{
rtfFile.Write(rtf);
}
}
I hope that gets you started.
Here is an example how to use Open XML SDK ...
Source Code
you will have to plug-in your text where it says "Original Text Here".
You have picked a very difficult task for a "newbie". To mix images and text you will need a complex format like PostScript, PDF, DocX, or RTF. Controlling pagination, for example to specify your header and footer images only once and have them automatically show up on the top and bottom of each page, is an even more difficult task.
You have not given us enough information to tell you where to start. For example, what is "my program"? Is it like a word processor? You will have to use the System.Drawing.Printing.PrintDocument classes to define a print document, draw headers and footers when you reach the appropriate place on each page, perform line layout, breaks and pagination. This is a large job for a professional programmer.
Or do you just want to produce a file that another program can output, with headers and footers? You could output RTF; the specification is here. This is an easier task; you may be able to leverage exisiting RTF interpreters.
Or do you want to display these documents on-screen? Han's suggestion to use an existing application via automation is a good one.
Break your task into smaller requirements and investigate each requirement.