ASCU_ALL/PdfSharp/Drawing.Internal/ImageImporterJpeg.cs

364 lines
12 KiB
C#
Raw Normal View History

2020-09-04 12:49:15 +05:00
#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Thomas Hövel
//
// Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion
using System;
using PdfSharp.Pdf;
namespace PdfSharp.Drawing.Internal
{
// ReSharper disable once InconsistentNaming
internal class ImageImporterJpeg : ImageImporterRoot, IImageImporter
{
// TODO Find information about JPEG2000.
// Notes: JFIF is big-endian.
public ImportedImage ImportImage(StreamReaderHelper stream, PdfDocument document)
{
try
{
stream.CurrentOffset = 0;
// Test 2 magic bytes.
if (TestFileHeader(stream))
{
// Skip over 2 magic bytes.
stream.CurrentOffset += 2;
ImagePrivateDataDct ipd = new ImagePrivateDataDct(stream.Data, stream.Length);
ImportedImage ii = new ImportedImageJpeg(this, ipd, document);
if (TestJfifHeader(stream, ii))
{
bool colorHeader = false, infoHeader = false;
while (MoveToNextHeader(stream))
{
if (TestColorFormatHeader(stream, ii))
{
colorHeader = true;
}
else if (TestInfoHeader(stream, ii))
{
infoHeader = true;
}
}
if (colorHeader && infoHeader)
return ii;
}
}
}
// ReSharper disable once EmptyGeneralCatchClause
catch (Exception)
{
}
return null;
}
private bool TestFileHeader(StreamReaderHelper stream)
{
// File must start with 0xffd8.
return stream.GetWord(0, true) == 0xffd8;
}
private bool TestJfifHeader(StreamReaderHelper stream, ImportedImage ii)
{
// The App0 header should be the first header in every JFIF file.
if (stream.GetWord(0, true) == 0xffe0)
{
// Now check for text "JFIF".
if (stream.GetDWord(4, true) == 0x4a464946)
{
int blockLength = stream.GetWord(2, true);
if (blockLength >= 16)
{
int version = stream.GetWord(9, true);
int units = stream.GetByte(11);
int densityX = stream.GetWord(12, true);
int densityY = stream.GetWord(14, true);
switch (units)
{
case 0: // Aspect ratio only.
ii.Information.HorizontalAspectRatio = densityX;
ii.Information.VerticalAspectRatio = densityY;
break;
case 1: // DPI.
ii.Information.HorizontalDPI = densityX;
ii.Information.VerticalDPI = densityY;
break;
case 2: // DPCM.
ii.Information.HorizontalDPM = densityX * 100;
ii.Information.VerticalDPM = densityY * 100;
break;
}
// More information here? More tests?
return true;
}
}
}
return false;
}
private bool TestColorFormatHeader(StreamReaderHelper stream, ImportedImage ii)
{
// The SOS header (start of scan).
if (stream.GetWord(0, true) == 0xffda)
{
int components = stream.GetByte(4);
if (components < 1 || components > 4 || components == 2)
return false;
// 1 for grayscale, 3 for RGB, 4 for CMYK.
int blockLength = stream.GetWord(2, true);
// Integrity check: correct size?
if (blockLength != 6 + 2 * components)
return false;
// Eventually do more tests here.
// Magic: we assume that all JPEG files with 4 components are RGBW (inverted CMYK) and not CMYK.
// We add a test to tell CMYK from RGBW when we encounter a test file in CMYK format.
ii.Information.ImageFormat = components == 3 ? ImageInformation.ImageFormats.JPEG :
(components == 1 ? ImageInformation.ImageFormats.JPEGGRAY : ImageInformation.ImageFormats.JPEGRGBW);
return true;
}
return false;
}
private bool TestInfoHeader(StreamReaderHelper stream, ImportedImage ii)
{
// The SOF header (start of frame).
int header = stream.GetWord(0, true);
if (header >= 0xffc0 && header <= 0xffc3 ||
header >= 0xffc9 && header <= 0xffcb)
{
// Lines in image.
int sizeY = stream.GetWord(5, true);
// Samples per line.
int sizeX = stream.GetWord(7, true);
// $THHO TODO: Check if we always get useful information here.
ii.Information.Width = (uint)sizeX;
ii.Information.Height = (uint)sizeY;
return true;
}
return false;
}
private bool MoveToNextHeader(StreamReaderHelper stream)
{
int blockLength = stream.GetWord(2, true);
int headerMagic = stream.GetByte(0);
int headerType = stream.GetByte(1);
if (headerMagic == 0xff)
{
// EOI: last header.
if (headerType == 0xd9)
return false;
// Check for standalone markers.
if (headerType == 0x01 || headerType >= 0xd0 && headerType <= 0xd7)
{
stream.CurrentOffset += 2;
return true;
}
// Now assume header with block size.
stream.CurrentOffset += 2 + blockLength;
return true;
}
return false;
}
public ImageData PrepareImage(ImagePrivateData data)
{
throw new NotImplementedException();
}
//int GetJpgSizeTestCode(byte[] pData, uint FileSizeLow, out int pWidth, out int pHeight)
//{
// pWidth = -1;
// pHeight = -1;
// int i = 0;
// if ((pData[i] == 0xFF) && (pData[i + 1] == 0xD8) && (pData[i + 2] == 0xFF) && (pData[i + 3] == 0xE0))
// {
// i += 4;
// // Check for valid JPEG header (null terminated JFIF)
// if ((pData[i + 2] == 'J') && (pData[i + 3] == 'F') && (pData[i + 4] == 'I') && (pData[i + 5] == 'F')
// && (pData[i + 6] == 0x00))
// {
// //Retrieve the block length of the first block since the first block will not contain the size of file
// int block_length = pData[i] * 256 + pData[i + 1];
// while (i < FileSizeLow)
// {
// //Increase the file index to get to the next block
// i += block_length;
// if (i >= FileSizeLow)
// {
// //Check to protect against segmentation faults
// return -1;
// }
// if (pData[i] != 0xFF)
// {
// return -2;
// }
// if (pData[i + 1] == 0xC0)
// {
// //0xFFC0 is the "Start of frame" marker which contains the file size
// //The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y]
// pHeight = pData[i + 5] * 256 + pData[i + 6];
// pWidth = pData[i + 7] * 256 + pData[i + 8];
// return 0;
// }
// else
// {
// i += 2; //Skip the block marker
// //Go to the next block
// block_length = pData[i] * 256 + pData[i + 1];
// }
// }
// //If this point is reached then no size was found
// return -3;
// }
// else
// {
// return -4;
// } //Not a valid JFIF string
// }
// else
// {
// return -5;
// } //Not a valid SOI header
// //return -6;
//} // GetJpgSize
}
/// <summary>
/// Imported JPEG image.
/// </summary>
internal class ImportedImageJpeg : ImportedImage
{
/// <summary>
/// Initializes a new instance of the <see cref="ImportedImageJpeg"/> class.
/// </summary>
public ImportedImageJpeg(IImageImporter importer, ImagePrivateDataDct data, PdfDocument document)
: base(importer, data, document)
{ }
internal override ImageData PrepareImageData()
{
ImagePrivateDataDct data = (ImagePrivateDataDct)Data;
ImageDataDct imageData = new ImageDataDct();
imageData.Data = data.Data;
imageData.Length = data.Length;
return imageData;
}
}
/// <summary>
/// Contains data needed for PDF. Will be prepared when needed.
/// </summary>
internal class ImageDataDct : ImageData
{
/// <summary>
/// Gets the data.
/// </summary>
public byte[] Data
{
get { return _data; }
internal set { _data = value; }
}
private byte[] _data;
/// <summary>
/// Gets the length.
/// </summary>
public int Length
{
get { return _length; }
internal set { _length = value; }
}
private int _length;
}
/*internal*/
/// <summary>
/// Private data for JPEG images.
/// </summary>
internal class ImagePrivateDataDct : ImagePrivateData
{
/// <summary>
/// Initializes a new instance of the <see cref="ImagePrivateDataDct"/> class.
/// </summary>
public ImagePrivateDataDct(byte[] data, int length)
{
_data = data;
_length = length;
}
/// <summary>
/// Gets the data.
/// </summary>
public byte[] Data
{
get { return _data; }
//internal set { _data = value; }
}
private readonly byte[] _data;
/// <summary>
/// Gets the length.
/// </summary>
public int Length
{
get { return _length; }
//internal set { _length = value; }
}
private readonly int _length;
}
}