mirror of
https://github.com/wmjordan/PDFPatcher.git
synced 2026-10-18 21:52:34 +08:00
228 lines
7.2 KiB
C#
228 lines
7.2 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
|
||
namespace PDFPatcher.Processor
|
||
{
|
||
sealed class SetCaseProcessor : IPdfInfoXmlProcessor
|
||
{
|
||
static readonly System.Globalization.TextInfo __currentTextInfo = System.Globalization.CultureInfo.CurrentCulture.TextInfo;
|
||
static readonly char[] FullWidthNumbers = "01234567890".ToCharArray();
|
||
static readonly char[] HalfWidthNumbers = "01234567890".ToCharArray();
|
||
static readonly char[] ChineseNumbers = "○一二三四五六七八九〇".ToCharArray();
|
||
static readonly char[] TraditionalChineseNumbers = "零壹贰叁肆伍陆柒捌玖零".ToCharArray();
|
||
|
||
internal const string FullWidthPunctuations = "!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~";
|
||
internal const string HalfWidthPunctuations = "!\"#$%&'()*+,-./;:<=>?@[\\]^_`{|}~";
|
||
|
||
internal static string[] CaseNames = new string[]{
|
||
"首字母大写", "英文大写", "英文小写",
|
||
"全角数字", "全角字母", "全角标点",
|
||
"半角数字", "半角字母", "半角标点",
|
||
"中文数字", "大写中文数字",
|
||
"繁体汉字转简体", "简体汉字转繁体"
|
||
};
|
||
|
||
public enum LetterCase
|
||
{
|
||
Title, Upper, Lower,
|
||
FullWidthNumber, FullWidthAlphabetic, FullWidthPunctuation,
|
||
HalfWidthNumber, HalfWidthAlphabetic, HalfWidthPunctuation,
|
||
ChineseNumber, TraditionalChineseNumbers,
|
||
TraditionalToSimplifiedCjk, SimplifiedToTraditionalCjk
|
||
}
|
||
|
||
public LetterCase Case { get; }
|
||
|
||
public SetCaseProcessor(LetterCase letterCase) {
|
||
Case = letterCase;
|
||
}
|
||
|
||
#region IInfoDocProcessor 成员
|
||
|
||
public string Name => "设置书签文本为" + CaseNames[(int)Case];
|
||
|
||
public IUndoAction Process(System.Xml.XmlElement item) {
|
||
var a = item.GetAttributeNode(Constants.BookmarkAttributes.Title);
|
||
if (a == null) {
|
||
return null;
|
||
}
|
||
var source = a.Value;
|
||
var value = ConvertCase(source, Case);
|
||
if (source == value) {
|
||
return null;
|
||
}
|
||
return UndoAttributeAction.GetUndoAction(item, Constants.BookmarkAttributes.Title, value);
|
||
}
|
||
|
||
#endregion
|
||
|
||
static string ConvertCase(string source, LetterCase targetCase) {
|
||
string value;
|
||
switch (targetCase) {
|
||
case LetterCase.Lower:
|
||
return source.ToLowerInvariant();
|
||
case LetterCase.Upper:
|
||
return source.ToUpperInvariant();
|
||
case LetterCase.Title:
|
||
return __currentTextInfo.ToTitleCase(source.ToLowerInvariant());
|
||
case LetterCase.FullWidthAlphabetic:
|
||
return HWL2FWL.Convert(source);
|
||
case LetterCase.FullWidthNumber:
|
||
return HWN2FWN.Convert(source);
|
||
case LetterCase.FullWidthPunctuation:
|
||
return HWP2FWP.Convert(source);
|
||
case LetterCase.HalfWidthAlphabetic:
|
||
return FWL2HWL.Convert(source);
|
||
case LetterCase.HalfWidthNumber:
|
||
return FWN2HWN.Convert(source);
|
||
case LetterCase.HalfWidthPunctuation:
|
||
return FWP2HWP.Convert(source);
|
||
case LetterCase.ChineseNumber:
|
||
value = Translate(source, FullWidthNumbers, ChineseNumbers);
|
||
return Translate(value, HalfWidthNumbers, ChineseNumbers);
|
||
case LetterCase.TraditionalChineseNumbers:
|
||
value = Translate(source, FullWidthNumbers, TraditionalChineseNumbers);
|
||
value = Translate(value, HalfWidthNumbers, TraditionalChineseNumbers);
|
||
return Translate(value, ChineseNumbers, TraditionalChineseNumbers);
|
||
case LetterCase.SimplifiedToTraditionalCjk:
|
||
return SC2TC.Convert(source);
|
||
case LetterCase.TraditionalToSimplifiedCjk:
|
||
return TC2SC.Convert(source);
|
||
default: throw new ArgumentOutOfRangeException("LetterCase");
|
||
}
|
||
}
|
||
|
||
// todo 优化转换效率:避免 ToCharArray 和无条件创建新字符串
|
||
static string Translate(string s, char[] source, char[] target) {
|
||
var cs = s.ToCharArray();
|
||
for (int i = 0; i < cs.Length; i++) {
|
||
ref char c = ref cs[i];
|
||
var p = Array.IndexOf(source, c);
|
||
if (p != -1) {
|
||
c = target[p];
|
||
}
|
||
}
|
||
return new string(cs);
|
||
}
|
||
|
||
static char FullWidthLetterToHalfWidth(char ch) {
|
||
return ch >= 'a' && ch <= 'z' ? (char)(ch - 'a' + 'a')
|
||
: ch >= 'A' && ch <= 'Z' ? (char)(ch - 'A' + 'A')
|
||
: ch;
|
||
}
|
||
|
||
static char HalfWidthLetterToFullWidth(char ch) {
|
||
return ch >= 'a' && ch <= 'z' ? (char)(ch + 'a' - 'a')
|
||
: ch >= 'A' && ch <= 'Z' ? (char)(ch + 'A' - 'A')
|
||
: ch;
|
||
}
|
||
static char FullWidthNumberToHalfWidth(char ch) {
|
||
return ch >= '0' && ch <= '9' ? (char)(ch - '0' + '0') : ch;
|
||
}
|
||
|
||
static char HalfWidthNumberToFullWidth(char ch) {
|
||
return ch >= '0' && ch <= '9' ? (char)(ch + '0' - '0') : ch;
|
||
}
|
||
|
||
static class SC2TC
|
||
{
|
||
public static readonly Converter<string, string> Convert = new StringMapper(new CharacterMapper(Constants.Chinese.Simplified, Constants.Chinese.Traditional).Map).Convert;
|
||
}
|
||
static class TC2SC
|
||
{
|
||
public static readonly Converter<string, string> Convert = new StringMapper(new CharacterMapper(Constants.Chinese.Traditional, Constants.Chinese.Simplified).Map).Convert;
|
||
}
|
||
static class FWP2HWP
|
||
{
|
||
public static readonly Converter<string, string> Convert = new StringMapper(new CharacterMapper(FullWidthPunctuations, HalfWidthPunctuations).Map).Convert;
|
||
}
|
||
static class HWP2FWP
|
||
{
|
||
public static readonly Converter<string, string> Convert = new StringMapper(new CharacterMapper(HalfWidthPunctuations, FullWidthPunctuations).Map).Convert;
|
||
}
|
||
static class FWL2HWL
|
||
{
|
||
public static readonly Converter<string, string> Convert = new StringMapper(FullWidthLetterToHalfWidth).Convert;
|
||
}
|
||
static class HWL2FWL
|
||
{
|
||
public static readonly Converter<string, string> Convert = new StringMapper(HalfWidthLetterToFullWidth).Convert;
|
||
}
|
||
static class FWN2HWN
|
||
{
|
||
public static readonly Converter<string, string> Convert = new StringMapper(FullWidthNumberToHalfWidth).Convert;
|
||
}
|
||
static class HWN2FWN
|
||
{
|
||
public static readonly Converter<string, string> Convert = new StringMapper(HalfWidthNumberToFullWidth).Convert;
|
||
}
|
||
|
||
sealed class StringMapper
|
||
{
|
||
readonly Converter<char, char> Converter;
|
||
public StringMapper(Converter<char, char> converter) {
|
||
Converter = converter;
|
||
}
|
||
|
||
public unsafe string Convert(string value) {
|
||
if (String.IsNullOrEmpty(value)) {
|
||
return value;
|
||
}
|
||
int i = 0;
|
||
foreach (var ch in value) {
|
||
if (ch != Converter(ch)) {
|
||
break;
|
||
}
|
||
++i;
|
||
}
|
||
if (i == value.Length) {
|
||
return value;
|
||
}
|
||
var r = String.Copy(value);
|
||
fixed (char* s = r) {
|
||
char* c = s + i;
|
||
char* end = c + value.Length;
|
||
do {
|
||
*c = Converter(*c);
|
||
} while (++c < end);
|
||
}
|
||
return r;
|
||
}
|
||
}
|
||
|
||
sealed class CharacterMapper
|
||
{
|
||
readonly Dictionary<char, char> _Mapper;
|
||
|
||
public CharacterMapper(string from, string to) {
|
||
var i = 0;
|
||
_Mapper = new Dictionary<char, char>(from.Length);
|
||
foreach (var item in from) {
|
||
_Mapper[item] = to[i++];
|
||
}
|
||
}
|
||
|
||
public char Map(char value) {
|
||
return _Mapper.TryGetValue(value, out var r) ? r : value;
|
||
}
|
||
}
|
||
|
||
sealed class SequentialCharacterMapper
|
||
{
|
||
readonly char _From, _To;
|
||
readonly int _Count;
|
||
|
||
public SequentialCharacterMapper(char from, char to, int count) {
|
||
_From = from;
|
||
_To = to;
|
||
_Count = count;
|
||
}
|
||
|
||
public char Map(char value) {
|
||
var d = value - _From;
|
||
return d >= 0 && d < _Count ? (char)(_To + d) : value;
|
||
}
|
||
}
|
||
}
|
||
}
|