mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-06-28 14:37:51 +08:00
some changes
This commit is contained in:
parent
76c6e9436d
commit
8501ae123e
@ -0,0 +1,35 @@
|
|||||||
|
namespace UglyToad.PdfPig.Tests.Integration
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using UglyToad.PdfPig.Tokens;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class DctDecodeFilterTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void LettersHaveCorrectColors()
|
||||||
|
{
|
||||||
|
using (var document = PdfDocument.Open(IntegrationHelpers.GetDocumentPath("Pig Production Handbook.pdf"), ParsingOptions.LenientParsingOff))
|
||||||
|
{
|
||||||
|
for (int p = 1; p <= document.NumberOfPages; p++)
|
||||||
|
{
|
||||||
|
var page = document.GetPage(p);
|
||||||
|
int i = 0;
|
||||||
|
foreach (var image in page.GetImages())
|
||||||
|
{
|
||||||
|
if (image.ImageDictionary.TryGet<NameToken>(NameToken.Filter, out var filter) && filter.Data.Equals(NameToken.DctDecode))
|
||||||
|
{
|
||||||
|
image.TryGetPng(out var png);
|
||||||
|
File.WriteAllBytes($"Pig Production Handbook_{p}_{i}.png", png);
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,46 @@
|
|||||||
namespace UglyToad.PdfPig.Filters
|
namespace UglyToad.PdfPig.Filters
|
||||||
{
|
{
|
||||||
using System;
|
using BigGustave.Jpgs;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using Tokens;
|
using Tokens;
|
||||||
|
using static System.Net.Mime.MediaTypeNames;
|
||||||
|
|
||||||
internal class DctDecodeFilter : IFilter
|
internal class DctDecodeFilter : IFilter
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsSupported { get; } = false;
|
public bool IsSupported { get; } = true;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public byte[] Decode(IReadOnlyList<byte> input, DictionaryToken streamDictionary, int filterIndex)
|
public byte[] Decode(IReadOnlyList<byte> input, DictionaryToken streamDictionary, int filterIndex)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("The DST (Discrete Cosine Transform) Filter indicates data is encoded in JPEG format. " +
|
//throw new NotSupportedException("The DST (Discrete Cosine Transform) Filter indicates data is encoded in JPEG format. " +
|
||||||
"This filter is not currently supported but the raw data can be supplied to JPEG supporting libraries.");
|
// "This filter is not currently supported but the raw data can be supplied to JPEG supporting libraries.");
|
||||||
|
|
||||||
|
var decodeParms = DecodeParameterResolver.GetFilterParameters(streamDictionary, filterIndex);
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream(input.ToArray()))
|
||||||
|
{
|
||||||
|
var jpg = JpgOpener.Open(ms, true);
|
||||||
|
|
||||||
|
byte[] output = new byte[3 * jpg.Width * jpg.Height];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (int col = 0; col < jpg.Height; col++)
|
||||||
|
{
|
||||||
|
for (int row = 0; row < jpg.Width; row++)
|
||||||
|
{
|
||||||
|
var pixel = jpg.GetPixel(row, col);
|
||||||
|
output[i++] = pixel.R;
|
||||||
|
output[i++] = pixel.G;
|
||||||
|
output[i++] = pixel.B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return output; //jpg.rawData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
namespace BigGustave.Jpgs
|
namespace BigGustave.Jpgs
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
internal class BitStream
|
internal class BitStream
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private bool isLittleEndian = BitConverter.IsLittleEndian;
|
||||||
|
|
||||||
|
private readonly BitArray bitArray;
|
||||||
|
|
||||||
private int bitOffset;
|
private int bitOffset;
|
||||||
private readonly IReadOnlyList<byte> data;
|
private readonly IReadOnlyList<byte> data;
|
||||||
|
|
||||||
public BitStream(IReadOnlyList<byte> data)
|
public BitStream(IReadOnlyList<byte> data)
|
||||||
{
|
{
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
bitArray = new BitArray(data.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Read()
|
public int Read()
|
||||||
@ -22,14 +30,14 @@
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var byteVal = data[byteIndex];
|
|
||||||
|
|
||||||
var withinByteIndex = bitOffset - (byteIndex * 8);
|
var withinByteIndex = bitOffset - (byteIndex * 8);
|
||||||
|
|
||||||
bitOffset++;
|
bitOffset++;
|
||||||
|
|
||||||
// TODO: LSB?
|
// TODO: LSB?
|
||||||
return ((1 << (7 - withinByteIndex)) & byteVal) > 0 ? 1 : 0;
|
return bitArray[bitOffset + 7 - withinByteIndex] ? 1 : 0;
|
||||||
|
//var byteVal = data[byteIndex];
|
||||||
|
//return ((1 << (7 - withinByteIndex)) & byteVal) > 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ReadNBits(int length)
|
public int ReadNBits(int length)
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
namespace BigGustave
|
namespace BigGustave
|
||||||
{
|
{
|
||||||
|
using Jpgs;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Jpgs;
|
|
||||||
using UglyToad.PdfPig.Core;
|
using UglyToad.PdfPig.Core;
|
||||||
|
using UglyToad.PdfPig.Images.Png;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A JPEG image.
|
/// A JPEG image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Jpg
|
internal class Jpg
|
||||||
{
|
{
|
||||||
private readonly byte[] rawData;
|
internal readonly byte[] rawData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The width of the image in pixels.
|
/// The width of the image in pixels.
|
||||||
@ -46,7 +47,6 @@
|
|||||||
this.rawData = rawData;
|
this.rawData = rawData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the pixel at the given column and row (x, y).
|
/// Get the pixel at the given column and row (x, y).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -77,7 +77,6 @@
|
|||||||
|
|
||||||
return new Pixel(r, g, b);
|
return new Pixel(r, g, b);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open and parse a JPG file from the stream.
|
/// Open and parse a JPG file from the stream.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
{
|
{
|
||||||
using Content;
|
using Content;
|
||||||
using Graphics.Colors;
|
using Graphics.Colors;
|
||||||
|
using System.Linq;
|
||||||
using UglyToad.PdfPig.Core;
|
using UglyToad.PdfPig.Core;
|
||||||
|
|
||||||
internal static class PngFromPdfImageFactory
|
internal static class PngFromPdfImageFactory
|
||||||
@ -21,61 +22,8 @@
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bytesPure = ColorSpaceDetailsByteConverter.Convert(image.ColorSpaceDetails, bytesPure,
|
|
||||||
image.BitsPerComponent, image.WidthInSamples, image.HeightInSamples);
|
|
||||||
|
|
||||||
var numberOfComponents = image.ColorSpaceDetails.BaseNumberOfColorComponents;
|
|
||||||
|
|
||||||
var is3Byte = numberOfComponents == 3;
|
|
||||||
|
|
||||||
var builder = PngBuilder.Create(image.WidthInSamples, image.HeightInSamples, false);
|
var builder = PngBuilder.Create(image.WidthInSamples, image.HeightInSamples, false);
|
||||||
|
|
||||||
var requiredSize = (image.WidthInSamples * image.HeightInSamples * numberOfComponents);
|
|
||||||
|
|
||||||
var actualSize = bytesPure.Count;
|
|
||||||
var isCorrectlySized = bytesPure.Count == requiredSize ||
|
|
||||||
// Spec, p. 37: "...error if the stream contains too much data, with the exception that
|
|
||||||
// there may be an extra end-of-line marker..."
|
|
||||||
(actualSize == requiredSize + 1 && bytesPure[actualSize - 1] == ReadHelper.AsciiLineFeed) ||
|
|
||||||
(actualSize == requiredSize + 1 && bytesPure[actualSize - 1] == ReadHelper.AsciiCarriageReturn) ||
|
|
||||||
// The combination of a CARRIAGE RETURN followed immediately by a LINE FEED is treated as one EOL marker.
|
|
||||||
(actualSize == requiredSize + 2 &&
|
|
||||||
bytesPure[actualSize - 2] == ReadHelper.AsciiCarriageReturn &&
|
|
||||||
bytesPure[actualSize - 1] == ReadHelper.AsciiLineFeed);
|
|
||||||
|
|
||||||
if (!isCorrectlySized)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (image.ColorSpaceDetails.BaseType == ColorSpace.DeviceCMYK || numberOfComponents == 4)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for (int col = 0; col < image.HeightInSamples; col++)
|
|
||||||
{
|
|
||||||
for (int row = 0; row < image.WidthInSamples; row++)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Where CMYK in 0..1
|
|
||||||
* R = 255 × (1-C) × (1-K)
|
|
||||||
* G = 255 × (1-M) × (1-K)
|
|
||||||
* B = 255 × (1-Y) × (1-K)
|
|
||||||
*/
|
|
||||||
|
|
||||||
double c = (bytesPure[i++] / 255d);
|
|
||||||
double m = (bytesPure[i++] / 255d);
|
|
||||||
double y = (bytesPure[i++] / 255d);
|
|
||||||
double k = (bytesPure[i++] / 255d);
|
|
||||||
var r = (byte)(255 * (1 - c) * (1 - k));
|
|
||||||
var g = (byte)(255 * (1 - m) * (1 - k));
|
|
||||||
var b = (byte)(255 * (1 - y) * (1 - k));
|
|
||||||
|
|
||||||
builder.SetPixel(r, g, b, row, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (is3Byte)
|
|
||||||
{
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (int col = 0; col < image.HeightInSamples; col++)
|
for (int col = 0; col < image.HeightInSamples; col++)
|
||||||
{
|
{
|
||||||
@ -84,22 +32,91 @@
|
|||||||
builder.SetPixel(bytesPure[i++], bytesPure[i++], bytesPure[i++], row, col);
|
builder.SetPixel(bytesPure[i++], bytesPure[i++], bytesPure[i++], row, col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for (int col = 0; col < image.HeightInSamples; col++)
|
|
||||||
{
|
|
||||||
for (int row = 0; row < image.WidthInSamples; row++)
|
|
||||||
{
|
|
||||||
byte pixel = bytesPure[i++];
|
|
||||||
builder.SetPixel(pixel, pixel, pixel, row, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = builder.Save();
|
bytes = builder.Save();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//bytesPure = ColorSpaceDetailsByteConverter.Convert(image.ColorSpaceDetails, bytesPure,
|
||||||
|
// image.BitsPerComponent, image.WidthInSamples, image.HeightInSamples);
|
||||||
|
|
||||||
|
//var numberOfComponents = image.ColorSpaceDetails.BaseNumberOfColorComponents;
|
||||||
|
|
||||||
|
//var is3Byte = numberOfComponents == 3;
|
||||||
|
|
||||||
|
//var builder = PngBuilder.Create(image.WidthInSamples, image.HeightInSamples, false);
|
||||||
|
|
||||||
|
//var requiredSize = (image.WidthInSamples * image.HeightInSamples * numberOfComponents);
|
||||||
|
|
||||||
|
//var actualSize = bytesPure.Count;
|
||||||
|
//var isCorrectlySized = bytesPure.Count == requiredSize ||
|
||||||
|
// // Spec, p. 37: "...error if the stream contains too much data, with the exception that
|
||||||
|
// // there may be an extra end-of-line marker..."
|
||||||
|
// (actualSize == requiredSize + 1 && bytesPure[actualSize - 1] == ReadHelper.AsciiLineFeed) ||
|
||||||
|
// (actualSize == requiredSize + 1 && bytesPure[actualSize - 1] == ReadHelper.AsciiCarriageReturn) ||
|
||||||
|
// // The combination of a CARRIAGE RETURN followed immediately by a LINE FEED is treated as one EOL marker.
|
||||||
|
// (actualSize == requiredSize + 2 &&
|
||||||
|
// bytesPure[actualSize - 2] == ReadHelper.AsciiCarriageReturn &&
|
||||||
|
// bytesPure[actualSize - 1] == ReadHelper.AsciiLineFeed);
|
||||||
|
|
||||||
|
//if (!isCorrectlySized)
|
||||||
|
//{
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if (image.ColorSpaceDetails.BaseType == ColorSpace.DeviceCMYK || numberOfComponents == 4)
|
||||||
|
//{
|
||||||
|
// int i = 0;
|
||||||
|
// for (int col = 0; col < image.HeightInSamples; col++)
|
||||||
|
// {
|
||||||
|
// for (int row = 0; row < image.WidthInSamples; row++)
|
||||||
|
// {
|
||||||
|
// /*
|
||||||
|
// * Where CMYK in 0..1
|
||||||
|
// * R = 255 × (1-C) × (1-K)
|
||||||
|
// * G = 255 × (1-M) × (1-K)
|
||||||
|
// * B = 255 × (1-Y) × (1-K)
|
||||||
|
// */
|
||||||
|
|
||||||
|
// double c = (bytesPure[i++] / 255d);
|
||||||
|
// double m = (bytesPure[i++] / 255d);
|
||||||
|
// double y = (bytesPure[i++] / 255d);
|
||||||
|
// double k = (bytesPure[i++] / 255d);
|
||||||
|
// var r = (byte)(255 * (1 - c) * (1 - k));
|
||||||
|
// var g = (byte)(255 * (1 - m) * (1 - k));
|
||||||
|
// var b = (byte)(255 * (1 - y) * (1 - k));
|
||||||
|
|
||||||
|
// builder.SetPixel(r, g, b, row, col);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//else if (is3Byte)
|
||||||
|
//{
|
||||||
|
// int i = 0;
|
||||||
|
// for (int col = 0; col < image.HeightInSamples; col++)
|
||||||
|
// {
|
||||||
|
// for (int row = 0; row < image.WidthInSamples; row++)
|
||||||
|
// {
|
||||||
|
// builder.SetPixel(bytesPure[i++], bytesPure[i++], bytesPure[i++], row, col);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// int i = 0;
|
||||||
|
// for (int col = 0; col < image.HeightInSamples; col++)
|
||||||
|
// {
|
||||||
|
// for (int row = 0; row < image.WidthInSamples; row++)
|
||||||
|
// {
|
||||||
|
// byte pixel = bytesPure[i++];
|
||||||
|
// builder.SetPixel(pixel, pixel, pixel, row, col);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//bytes = builder.Save();
|
||||||
|
//return true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
namespace UglyToad.PdfPig
|
namespace UglyToad.PdfPig
|
||||||
{
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Core;
|
using Core;
|
||||||
using Filters;
|
using Filters;
|
||||||
using Parser.Parts;
|
using Parser.Parts;
|
||||||
using Tokenization.Scanner;
|
using Tokenization.Scanner;
|
||||||
using Tokens;
|
using Tokens;
|
||||||
|
using UglyToad.PdfPig.Content;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extensions for PDF types.
|
/// Extensions for PDF types.
|
||||||
@ -67,10 +69,21 @@
|
|||||||
{
|
{
|
||||||
var filters = filterProvider.GetFilters(stream.StreamDictionary, scanner);
|
var filters = filterProvider.GetFilters(stream.StreamDictionary, scanner);
|
||||||
|
|
||||||
|
var dico = stream.StreamDictionary;
|
||||||
|
|
||||||
|
|
||||||
|
if (dico.TryGet<StreamToken>(NameToken.Metadata, scanner, out var metadata2))
|
||||||
|
{
|
||||||
|
XmpMetadata xmp = new XmpMetadata(metadata2, filterProvider, scanner);
|
||||||
|
//dico = dico.With(NameToken.Metadata, metadata);
|
||||||
|
string xml = OtherEncodings.BytesAsLatin1String(xmp.GetXmlBytes().ToArray());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var transform = stream.Data;
|
var transform = stream.Data;
|
||||||
for (var i = 0; i < filters.Count; i++)
|
for (var i = 0; i < filters.Count; i++)
|
||||||
{
|
{
|
||||||
transform = filters[i].Decode(transform, stream.StreamDictionary, i);
|
transform = filters[i].Decode(transform, dico, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return transform;
|
return transform;
|
||||||
|
Loading…
Reference in New Issue
Block a user