ASCU_ALL/PrintPDF/PdfSharp/Pdf.Internal/PdfEncoders.cs

663 lines
25 KiB
C#
Raw Normal View History

2020-09-04 12:49:15 +05:00
#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Stefan Lange
//
// 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 System.Diagnostics;
using System.Globalization;
using System.Text;
using PdfSharp.Drawing;
using PdfSharp.Pdf.Security;
namespace PdfSharp.Pdf.Internal
{
/// <summary>
/// Groups a set of static encoding helper functions.
/// </summary>
internal static class PdfEncoders
{
/// <summary>
/// Gets the raw encoding.
/// </summary>
public static Encoding RawEncoding
{
get { return _rawEncoding ?? (_rawEncoding = new RawEncoding()); }
}
static Encoding _rawEncoding;
/// <summary>
/// Gets the raw Unicode encoding.
/// </summary>
public static Encoding RawUnicodeEncoding
{
get { return _rawUnicodeEncoding ?? (_rawUnicodeEncoding = new RawUnicodeEncoding()); }
}
static Encoding _rawUnicodeEncoding;
/// <summary>
/// Gets the Windows 1252 (ANSI) encoding.
/// </summary>
public static Encoding WinAnsiEncoding
{
get
{
if (_winAnsiEncoding == null)
{
#if !SILVERLIGHT && !NETFX_CORE && !UWP && !CORE
// Use .net encoder if available.
_winAnsiEncoding = Encoding.GetEncoding(1252);
#else
// Use own implementation in Silverlight and WinRT
_winAnsiEncoding = new AnsiEncoding();
#endif
}
return _winAnsiEncoding;
}
}
static Encoding _winAnsiEncoding;
/// <summary>
/// Gets the PDF DocEncoding encoding.
/// </summary>
public static Encoding DocEncoding
{
get { return _docEncoding ?? (_docEncoding = new DocEncoding()); }
}
static Encoding _docEncoding;
/// <summary>
/// Gets the UNICODE little-endian encoding.
/// </summary>
public static Encoding UnicodeEncoding
{
get { return _unicodeEncoding ?? (_unicodeEncoding = Encoding.Unicode); }
}
static Encoding _unicodeEncoding;
///// <summary>
///// Encodes a string from a byte array. Each character gets the code of the corresponding byte.
///// </summary>
//public static string RawString(byte[] bytes, int offset, int length)
//{
// char[] chars = new char[length];
// for (int idx = offset, ch = 0; idx < offset + length; idx++, ch++)
// chars[ch] = (char)bytes[idx];
// return new string(chars, 0, length);
//}
//
//public static string RawString(byte[] bytes)
//{
// return RawString(bytes, 0, bytes.Length);
//}
#if true_
public static string EncodeAsLiteral(string text, bool unicode)
{
if (text == null || text == "")
return "<>";
StringBuilder pdf = new StringBuilder("");
if (!unicode)
{
byte[] bytes = WinAnsiEncoding.GetBytes(text);
int count = bytes.Length;
pdf.Append("(");
for (int idx = 0; idx < count; idx++)
{
char ch = (char)bytes[idx];
if (ch < 32)
{
switch (ch)
{
case '\n':
pdf.Append("\\n");
break;
case '\r':
pdf.Append("\\r");
break;
case '\t':
pdf.Append("\\t");
break;
case '\f':
pdf.Append("\\f");
break;
default:
pdf.Append(InvalidChar); // TODO
break;
}
}
else
{
switch (ch)
{
case '(':
pdf.Append("\\(");
break;
case ')':
pdf.Append("\\)");
break;
case '\\':
pdf.Append("\\\\");
break;
default:
pdf.Append(ch);
break;
}
}
}
pdf.Append(')');
}
else
{
pdf.Append("<");
byte[] bytes = UnicodeEncoding.GetBytes(text);
int count = bytes.Length;
for (int idx = 0; idx < count; idx += 2)
{
pdf.AppendFormat("{0:X2}{1:X2}", bytes[idx + 1], bytes[idx]);
if (idx != 0 && (idx % 48) == 0)
pdf.Append("\n");
}
pdf.Append(">");
}
return pdf.ToString();
}
#endif
//public static string EncodeAsLiteral(string text)
//{
// return EncodeAsLiteral(text, false);
//}
/// <summary>
/// Converts a raw string into a raw string literal, possibly encrypted.
/// </summary>
public static string ToStringLiteral(string text, PdfStringEncoding encoding, PdfStandardSecurityHandler securityHandler)
{
if (String.IsNullOrEmpty(text))
return "()";
byte[] bytes;
switch (encoding)
{
case PdfStringEncoding.RawEncoding:
bytes = RawEncoding.GetBytes(text);
break;
case PdfStringEncoding.WinAnsiEncoding:
bytes = WinAnsiEncoding.GetBytes(text);
break;
case PdfStringEncoding.PDFDocEncoding:
bytes = DocEncoding.GetBytes(text);
break;
case PdfStringEncoding.Unicode:
bytes = RawUnicodeEncoding.GetBytes(text);
break;
default:
throw new NotImplementedException(encoding.ToString());
}
byte[] temp = FormatStringLiteral(bytes, encoding == PdfStringEncoding.Unicode, true, false, securityHandler);
return RawEncoding.GetString(temp, 0, temp.Length);
}
/// <summary>
/// Converts a raw string into a raw string literal, possibly encrypted.
/// </summary>
public static string ToStringLiteral(byte[] bytes, bool unicode, PdfStandardSecurityHandler securityHandler)
{
if (bytes == null || bytes.Length == 0)
return "()";
byte[] temp = FormatStringLiteral(bytes, unicode, true, false, securityHandler);
return RawEncoding.GetString(temp, 0, temp.Length);
}
/// <summary>
/// Converts a raw string into a raw hexadecimal string literal, possibly encrypted.
/// </summary>
public static string ToHexStringLiteral(string text, PdfStringEncoding encoding, PdfStandardSecurityHandler securityHandler)
{
if (String.IsNullOrEmpty(text))
return "<>";
byte[] bytes;
switch (encoding)
{
case PdfStringEncoding.RawEncoding:
bytes = RawEncoding.GetBytes(text);
break;
case PdfStringEncoding.WinAnsiEncoding:
bytes = WinAnsiEncoding.GetBytes(text);
break;
case PdfStringEncoding.PDFDocEncoding:
bytes = DocEncoding.GetBytes(text);
break;
case PdfStringEncoding.Unicode:
//bytes = UnicodeEncoding.GetBytes(text);
bytes = RawUnicodeEncoding.GetBytes(text);
break;
default:
throw new NotImplementedException(encoding.ToString());
}
byte[] agTemp = FormatStringLiteral(bytes, encoding == PdfStringEncoding.Unicode, true, true, securityHandler);
return RawEncoding.GetString(agTemp, 0, agTemp.Length);
}
/// <summary>
/// Converts a raw string into a raw hexadecimal string literal, possibly encrypted.
/// </summary>
public static string ToHexStringLiteral(byte[] bytes, bool unicode, PdfStandardSecurityHandler securityHandler)
{
if (bytes == null || bytes.Length == 0)
return "<>";
byte[] agTemp = FormatStringLiteral(bytes, unicode, true, true, securityHandler);
return RawEncoding.GetString(agTemp, 0, agTemp.Length);
}
/// <summary>
/// Converts the specified byte array into a byte array representing a string literal.
/// </summary>
/// <param name="bytes">The bytes of the string.</param>
/// <param name="unicode">Indicates whether one or two bytes are one character.</param>
/// <param name="prefix">Indicates whether to use Unicode prefix.</param>
/// <param name="hex">Indicates whether to create a hexadecimal string literal.</param>
/// <param name="securityHandler">Encrypts the bytes if specified.</param>
/// <returns>The PDF bytes.</returns>
public static byte[] FormatStringLiteral(byte[] bytes, bool unicode, bool prefix, bool hex, PdfStandardSecurityHandler securityHandler)
{
if (bytes == null || bytes.Length == 0)
return hex ? new byte[] { (byte)'<', (byte)'>' } : new byte[] { (byte)'(', (byte)')' };
Debug.Assert(!unicode || bytes.Length % 2 == 0, "Odd number of bytes in Unicode string.");
byte[] originalBytes = null;
bool encrypted = false;
if (securityHandler != null && !hex)
{
originalBytes = bytes;
bytes = (byte[])bytes.Clone();
bytes = securityHandler.EncryptBytes(bytes);
encrypted = true;
}
int count = bytes.Length;
StringBuilder pdf = new StringBuilder();
if (!unicode)
{
if (!hex)
{
pdf.Append("(");
for (int idx = 0; idx < count; idx++)
{
char ch = (char)bytes[idx];
if (ch < 32)
{
switch (ch)
{
case '\n':
pdf.Append("\\n");
break;
case '\r':
pdf.Append("\\r");
break;
case '\t':
pdf.Append("\\t");
break;
case '\b':
pdf.Append("\\b");
break;
// Corrupts encrypted text.
//case '\f':
// pdf.Append("\\f");
// break;
default:
// Don't escape characters less than 32 if the string is encrypted, because it is
// unreadable anyway.
encrypted = true;
if (!encrypted)
{
pdf.Append("\\0");
pdf.Append((char)(ch % 8 + '0'));
pdf.Append((char)(ch / 8 + '0'));
}
else
pdf.Append(ch);
break;
}
}
else
{
switch (ch)
{
case '(':
pdf.Append("\\(");
break;
case ')':
pdf.Append("\\)");
break;
case '\\':
pdf.Append("\\\\");
break;
default:
pdf.Append(ch);
break;
}
}
}
pdf.Append(')');
}
else
{
pdf.Append('<');
for (int idx = 0; idx < count; idx++)
pdf.AppendFormat("{0:X2}", bytes[idx]);
pdf.Append('>');
}
}
else
{
//Hex:
if (hex)
{
if (securityHandler != null && prefix)
{
// TODO Reduce redundancy.
// Encrypt data after padding BOM.
var bytes2 = new byte[bytes.Length + 2];
// Add BOM.
bytes2[0] = 0xfe;
bytes2[1] = 0xff;
// Copy bytes.
Array.Copy(bytes, 0, bytes2, 2, bytes.Length);
// Encyption.
bytes2 = securityHandler.EncryptBytes(bytes2);
encrypted = true;
pdf.Append("<");
var count2 = bytes2.Length;
for (int idx = 0; idx < count2; idx += 2)
{
pdf.AppendFormat("{0:X2}{1:X2}", bytes2[idx], bytes2[idx + 1]);
if (idx != 0 && (idx % 48) == 0)
pdf.Append("\n");
}
pdf.Append(">");
}
else
{
// No prefix or no encryption.
pdf.Append(prefix ? "<FEFF" : "<");
for (int idx = 0; idx < count; idx += 2)
{
pdf.AppendFormat("{0:X2}{1:X2}", bytes[idx], bytes[idx + 1]);
if (idx != 0 && (idx % 48) == 0)
pdf.Append("\n");
}
pdf.Append(">");
}
}
else
{
// TODO non hex literals... not sure how to treat linefeeds, '(', '\' etc.
if (encrypted)
{
// Hack: Call self with hex := true.
return FormatStringLiteral(originalBytes, unicode, prefix, true, securityHandler);
}
else
{
// Hack: Call self with hex := true.
return FormatStringLiteral(bytes, true, prefix, true, null);
}
}
}
return RawEncoding.GetBytes(pdf.ToString());
}
/// <summary>
/// Converts WinAnsi to DocEncode characters. Incomplete, just maps € and some other characters.
/// </summary>
static byte[] docencode_______ = new byte[256]
{
// TODO:
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0xA0, 0x7F, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x8A, 0x8C, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
};
//public static string DocEncode(string text, bool unicode)//, PdfStandardSecurityHandler securityHandler)
//{
// if (text == null || text == "")
// return "()";
//
// int length = text.Length;
// StringBuilder encoded = new StringBuilder(2 * length);
// if (!unicode)
// {
// byte[] bytes = WinAnsiEncoding.GetBytes(text);
// encoded.Append('(');
// for (int idx = 0; idx < length; idx++)
// {
// char ch = (char)bytes[idx];
// if (ch > 255)
// {
// //TODO unicode?
// encoded.Append(InvalidChar);
// //encoded.Append(ch);
// continue;
// }
// ch = (char)docencode[(int)ch];
// if (ch < 32)
// {
// switch (ch)
// {
// case '\n':
// encoded.Append("\\n");
// break;
//
// case '\r':
// encoded.Append("\\r");
// break;
//
// case '\t':
// encoded.Append("\\t");
// break;
//
// case '\f':
// encoded.Append("\\f");
// break;
//
// default:
// encoded.Append(InvalidChar); // TODO
// break;
// }
// }
// else
// {
// switch (ch)
// {
// case '(':
// encoded.Append("\\(");
// break;
//
// case ')':
// encoded.Append("\\)");
// break;
//
// case '\\':
// encoded.Append("\\\\");
// break;
//
// default:
// encoded.Append(ch);
// break;
// }
// }
// }
// encoded.Append(')');
// }
// else
// {
// encoded.Append("<FEFF");
// for (int idx = 0; idx < length; idx++)
// encoded.AppendFormat("{0:X4}", (int)text[idx]);
// encoded.Append('>');
// }
// return encoded.ToString();
//}
//public static string DocEncode(string text)
//{
// return DocEncode(text, false);
//}
///// <summary>
///// Encodes a hexadecimal doc-encoded string literal.
///// </summary>
//public static string DocEncodeHex(string text, bool unicode)
//{
// if (text == null || text == "")
// return "<>";
//
// int length = text.Length;
// StringBuilder encoded = new StringBuilder(3 * length);
// if (!unicode)
// {
// byte[] bytes = WinAnsiEncoding.GetBytes(text);
// encoded.Append('<');
// for (int idx = 0; idx < length; idx++)
// encoded.AppendFormat("{0:X2}", docencode[bytes[idx]]);
// encoded.Append('>');
// }
// else
// {
// encoded.Append("<FEFF");
// for (int idx = 0; idx < length; idx++)
// {
// encoded.AppendFormat("{0:X4}", (int)text[idx]);
// }
// encoded.Append('>');
// }
// return encoded.ToString();
//}
//public static string DocEncodeHex(string text)
//{
// return DocEncodeHex(text, false);
//}
/// <summary>
/// ...because I always forget CultureInfo.InvariantCulture and wonder why Acrobat
/// cannot understand my German decimal separator...
/// </summary>
public static string Format(string format, params object[] args)
{
return String.Format(CultureInfo.InvariantCulture, format, args);
}
/// <summary>
/// Converts a float into a string with up to 3 decimal digits and a decimal point.
/// </summary>
public static string ToString(double val)
{
return val.ToString(Config.SignificantFigures3, CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts an XColor into a string with up to 3 decimal digits and a decimal point.
/// </summary>
public static string ToString(XColor color, PdfColorMode colorMode)
{
const string format = Config.SignificantFigures3;
// If not defined let color decide
if (colorMode == PdfColorMode.Undefined)
colorMode = color.ColorSpace == XColorSpace.Cmyk ? PdfColorMode.Cmyk : PdfColorMode.Rgb;
switch (colorMode)
{
case PdfColorMode.Cmyk:
return String.Format(CultureInfo.InvariantCulture, "{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "}",
color.C, color.M, color.Y, color.K);
default:
return String.Format(CultureInfo.InvariantCulture, "{0:" + format + "} {1:" + format + "} {2:" + format + "}",
color.R / 255.0, color.G / 255.0, color.B / 255.0);
}
}
/// <summary>
/// Converts an XMatrix into a string with up to 4 decimal digits and a decimal point.
/// </summary>
public static string ToString(XMatrix matrix)
{
const string format = Config.SignificantFigures4;
return String.Format(CultureInfo.InvariantCulture,
"{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "}",
matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.OffsetX, matrix.OffsetY);
}
}
}